# HG changeset patch # User Thomas Wuerthinger # Date 1399146395 -7200 # Node ID ff5cacf47b68317e23b495eda21b1f3f6fed11eb # Parent 5ecbed00da232caa1add3b89e9a909986badbb71# Parent d370d87e528fe6fe31ddb53249eb0409dd670e0a Merge. diff -r 5ecbed00da23 -r ff5cacf47b68 CHANGELOG.md --- a/CHANGELOG.md Fri May 02 02:45:26 2014 +0200 +++ b/CHANGELOG.md Sat May 03 21:46:35 2014 +0200 @@ -12,13 +12,14 @@ * ... ### Truffle -* The method CallTarget#call takes now a variable number of Object arguments. +* The method `CallTarget#call` takes now a variable number of Object arguments. * Support for collecting stack traces and for accessing the current frame in slow paths. -* Renamed CallNode to DirectCallNode. -* Renamed TruffleRuntime#createCallNode to TruffleRuntime#createDirectCallNode. -* Added IndirectCallNode for calls with a changing CallTarget. -* Added TruffleRuntime#createIndirectCallNode to create an IndirectCallNode. -* DirectCallNode#inline was renamed to DirectCallNode#forceInlining(). +* Renamed `CallNode` to `DirectCallNode`. +* Renamed `TruffleRuntime#createCallNode` to `TruffleRuntime#createDirectCallNode`. +* Added `IndirectCallNode` for calls with a changing `CallTarget`. +* Added `TruffleRuntime#createIndirectCallNode` to create an `IndirectCallNode`. +* `DirectCallNode#inline` was renamed to `DirectCallNode#forceInlining()`. +* Removed deprecated `Node#adoptChild`. * ... ## Version 0.2 @@ -45,7 +46,7 @@ * New API to declare the cost of a Node for use in runtime environment specific heuristics. See `NodeCost`, `Node#getCost` and `NodeInfo#cost`. * Removed old API for `NodeInfo#Kind` and `NodeInfo#kind`. As a replacement the new `NodeCost` API can be used. * Changed `Node#replace` reason parameter type to `CharSequence` (to enable lazy string building) -* Deprecated `Node#adoptChild` and `Node#adoptChild`, no longer needed in node constructor +* Deprecated `Node#adoptChild` and `Node#adoptChildren`, no longer needed in node constructor * New `Node#insert` method for inserting new nodes into the tree (formerly `adoptChild`) * New `Node#adoptChildren` helper method that adopts all (direct and indirect) children of a node * New API `Node#atomic` for atomic tree operations diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.alloc/src/com/oracle/graal/alloc/ComputeBlockOrder.java --- a/graal/com.oracle.graal.alloc/src/com/oracle/graal/alloc/ComputeBlockOrder.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.alloc/src/com/oracle/graal/alloc/ComputeBlockOrder.java Sat May 03 21:46:35 2014 +0200 @@ -26,7 +26,6 @@ import java.util.*; import com.oracle.graal.compiler.common.cfg.*; -import com.oracle.graal.nodes.cfg.*; /** * Computes an ordering of the block that can be used by the linear scan register allocator and the @@ -67,11 +66,11 @@ * * @return sorted list of blocks */ - public static > List computeLinearScanOrder(int blockCount, T startBlock, BlocksToDoubles blockProbabilities) { + public static > List computeLinearScanOrder(int blockCount, T startBlock) { List order = new ArrayList<>(); BitSet visitedBlocks = new BitSet(blockCount); - PriorityQueue worklist = initializeWorklist(startBlock, visitedBlocks, blockProbabilities); - computeLinearScanOrder(order, worklist, visitedBlocks, blockProbabilities); + PriorityQueue worklist = initializeWorklist(startBlock, visitedBlocks); + computeLinearScanOrder(order, worklist, visitedBlocks); assert checkOrder(order, blockCount); return order; } @@ -81,11 +80,11 @@ * * @return sorted list of blocks */ - public static > List computeCodeEmittingOrder(int blockCount, T startBlock, BlocksToDoubles blockProbabilities) { + public static > List computeCodeEmittingOrder(int blockCount, T startBlock) { List order = new ArrayList<>(); BitSet visitedBlocks = new BitSet(blockCount); - PriorityQueue worklist = initializeWorklist(startBlock, visitedBlocks, blockProbabilities); - computeCodeEmittingOrder(order, worklist, visitedBlocks, blockProbabilities); + PriorityQueue worklist = initializeWorklist(startBlock, visitedBlocks); + computeCodeEmittingOrder(order, worklist, visitedBlocks); assert checkOrder(order, blockCount); return order; } @@ -93,28 +92,28 @@ /** * Iteratively adds paths to the code emission block order. */ - private static > void computeCodeEmittingOrder(List order, PriorityQueue worklist, BitSet visitedBlocks, BlocksToDoubles blockProbabilities) { + private static > void computeCodeEmittingOrder(List order, PriorityQueue worklist, BitSet visitedBlocks) { while (!worklist.isEmpty()) { T nextImportantPath = worklist.poll(); - addPathToCodeEmittingOrder(nextImportantPath, order, worklist, visitedBlocks, blockProbabilities); + addPathToCodeEmittingOrder(nextImportantPath, order, worklist, visitedBlocks); } } /** * Iteratively adds paths to the linear scan block order. */ - private static > void computeLinearScanOrder(List order, PriorityQueue worklist, BitSet visitedBlocks, BlocksToDoubles blockProbabilities) { + private static > void computeLinearScanOrder(List order, PriorityQueue worklist, BitSet visitedBlocks) { while (!worklist.isEmpty()) { T nextImportantPath = worklist.poll(); - addPathToLinearScanOrder(nextImportantPath, order, worklist, visitedBlocks, blockProbabilities); + addPathToLinearScanOrder(nextImportantPath, order, worklist, visitedBlocks); } } /** * Initializes the priority queue used for the work list of blocks and adds the start block. */ - private static > PriorityQueue initializeWorklist(T startBlock, BitSet visitedBlocks, BlocksToDoubles blockProbabilities) { - PriorityQueue result = new PriorityQueue<>(INITIAL_WORKLIST_CAPACITY, new BlockOrderComparator(blockProbabilities)); + private static > PriorityQueue initializeWorklist(T startBlock, BitSet visitedBlocks) { + PriorityQueue result = new PriorityQueue<>(INITIAL_WORKLIST_CAPACITY, new BlockOrderComparator<>()); result.add(startBlock); visitedBlocks.set(startBlock.getId()); return result; @@ -123,10 +122,10 @@ /** * Add a linear path to the linear scan order greedily following the most likely successor. */ - private static > void addPathToLinearScanOrder(T block, List order, PriorityQueue worklist, BitSet visitedBlocks, BlocksToDoubles blockProbabilities) { + private static > void addPathToLinearScanOrder(T block, List order, PriorityQueue worklist, BitSet visitedBlocks) { block.setLinearScanNumber(order.size()); order.add(block); - T mostLikelySuccessor = findAndMarkMostLikelySuccessor(block, visitedBlocks, blockProbabilities); + T mostLikelySuccessor = findAndMarkMostLikelySuccessor(block, visitedBlocks); enqueueSuccessors(block, worklist, visitedBlocks); if (mostLikelySuccessor != null) { if (!mostLikelySuccessor.isLoopHeader() && mostLikelySuccessor.getPredecessorCount() > 1) { @@ -135,24 +134,24 @@ double unscheduledSum = 0.0; for (T pred : mostLikelySuccessor.getPredecessors()) { if (pred.getLinearScanNumber() == -1) { - unscheduledSum += blockProbabilities.get(pred); + unscheduledSum += pred.probability(); } } - if (unscheduledSum > blockProbabilities.get(block) / PENALTY_VERSUS_UNSCHEDULED) { + if (unscheduledSum > block.probability() / PENALTY_VERSUS_UNSCHEDULED) { // Add this merge only after at least one additional predecessor gets scheduled. visitedBlocks.clear(mostLikelySuccessor.getId()); return; } } - addPathToLinearScanOrder(mostLikelySuccessor, order, worklist, visitedBlocks, blockProbabilities); + addPathToLinearScanOrder(mostLikelySuccessor, order, worklist, visitedBlocks); } } /** * Add a linear path to the code emission order greedily following the most likely successor. */ - private static > void addPathToCodeEmittingOrder(T initialBlock, List order, PriorityQueue worklist, BitSet visitedBlocks, BlocksToDoubles blockProbabilities) { + private static > void addPathToCodeEmittingOrder(T initialBlock, List order, PriorityQueue worklist, BitSet visitedBlocks) { T block = initialBlock; while (block != null) { // Skip loop headers if there is only a single loop end block to @@ -183,7 +182,7 @@ } } - T mostLikelySuccessor = findAndMarkMostLikelySuccessor(block, visitedBlocks, blockProbabilities); + T mostLikelySuccessor = findAndMarkMostLikelySuccessor(block, visitedBlocks); enqueueSuccessors(block, worklist, visitedBlocks); block = mostLikelySuccessor; } @@ -200,11 +199,11 @@ /** * Find the highest likely unvisited successor block of a given block. */ - private static > T findAndMarkMostLikelySuccessor(T block, BitSet visitedBlocks, BlocksToDoubles blockProbabilities) { + private static > T findAndMarkMostLikelySuccessor(T block, BitSet visitedBlocks) { T result = null; for (T successor : block.getSuccessors()) { - assert blockProbabilities.get(successor) >= 0.0 : "Probabilities must be positive"; - if (!visitedBlocks.get(successor.getId()) && successor.getLoopDepth() >= block.getLoopDepth() && (result == null || blockProbabilities.get(successor) >= blockProbabilities.get(result))) { + assert successor.probability() >= 0.0 : "Probabilities must be positive"; + if (!visitedBlocks.get(successor.getId()) && successor.getLoopDepth() >= block.getLoopDepth() && (result == null || successor.probability() >= result.probability())) { result = successor; } } @@ -247,12 +246,6 @@ */ private static class BlockOrderComparator> implements Comparator { - private final BlocksToDoubles probabilities; - - public BlockOrderComparator(BlocksToDoubles probabilities) { - this.probabilities = probabilities; - } - @Override public int compare(T a, T b) { // Loop blocks before any loop exit block. @@ -262,7 +255,7 @@ } // Blocks with high probability before blocks with low probability. - if (probabilities.get(a) > probabilities.get(b)) { + if (a.probability() > b.probability()) { return -1; } else { return 1; diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.api.replacements/src/com/oracle/graal/api/replacements/MethodHandleAccessProvider.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.api.replacements/src/com/oracle/graal/api/replacements/MethodHandleAccessProvider.java Sat May 03 21:46:35 2014 +0200 @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2014, 2014, 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.graal.api.replacements; + +import java.lang.invoke.*; + +import com.oracle.graal.api.meta.*; + +/** + * Interface to access the internals of the {@link MethodHandle} implementation of the VM. An + * implementation of this interface is usually required to access non-public classes, methods, and + * fields of {@link MethodHandle}, i.e., data that is not standardized by the Java specification. + */ +public interface MethodHandleAccessProvider { + + /** + * Identification for methods defined on the class {@link MethodHandle} that are processed by + * the {@link MethodHandleAccessProvider}. + */ + public enum IntrinsicMethod { + /** The method {@code MethodHandle.invokeBasic}. */ + INVOKE_BASIC, + /** The method {@code MethodHandle.linkToStatic}. */ + LINK_TO_STATIC, + /** The method {@code MethodHandle.linkToSpecial}. */ + LINK_TO_SPECIAL, + /** The method {@code MethodHandle.linkToVirtual}. */ + LINK_TO_VIRTUAL, + /** The method {@code MethodHandle.linkToInterface}. */ + LINK_TO_INTERFACE + } + + /** + * Returns the method handle method intrinsic identifier for the provided method, or + * {@code null} if the method is not an intrinsic processed by this interface. + */ + IntrinsicMethod lookupMethodHandleIntrinsic(ResolvedJavaMethod method); + + /** + * Resolves the invocation target for an invocation of {@link IntrinsicMethod#INVOKE_BASIC + * MethodHandle.invokeBasic} with the given constant receiver {@link MethodHandle}. Returns + * {@code null} if the invocation target is not available at this time. + *

+ * The first invocations of a method handle can use an interpreter to lookup the actual invoked + * method; frequently executed method handles can use Java bytecode generation to avoid the + * interpreter overhead. If the parameter forceBytecodeGeneration is set to true, the VM should + * try to generate bytecodes before this method returns. + */ + ResolvedJavaMethod resolveInvokeBasicTarget(Constant methodHandle, boolean forceBytecodeGeneration); + + /** + * Resolves the invocation target for an invocation of a {@code MethodHandle.linkTo*} method + * with the given constant member name. The member name is the last parameter of the + * {@code linkTo*} method. Returns {@code null} if the invocation target is not available at + * this time. + */ + ResolvedJavaMethod resolveLinkToTarget(Constant memberName); +} diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.asm.hsail/src/com/oracle/graal/asm/hsail/HSAILAssembler.java --- a/graal/com.oracle.graal.asm.hsail/src/com/oracle/graal/asm/hsail/HSAILAssembler.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.asm.hsail/src/com/oracle/graal/asm/hsail/HSAILAssembler.java Sat May 03 21:46:35 2014 +0200 @@ -190,16 +190,9 @@ emitString("st_spill_" + getArgTypeFromKind(kind) + " " + HSAIL.mapRegister(src) + ", " + mapStackSlot(dest, getArgSizeFromKind(kind)) + ";"); } - /** - * The mapping to stack slots is always relative to the beginning of the spillseg. - * HSAIL.getStackOffset returns the positive version of the originally negative offset. Then we - * back up from that by the argSize in bytes. This ensures that slots of different size do not - * overlap, even though we have converted from negative to positive offsets. - */ public static String mapStackSlot(Value reg, int argSize) { - long offset = HSAIL.getStackOffset(reg); - int argSizeBytes = argSize / 8; - return "[%spillseg]" + "[" + (offset - argSizeBytes) + "]"; + long startOffset = HSAIL.getStackOffsetStart(reg, argSize); + return "[%spillseg]" + "[" + startOffset + "]"; } public void cbr(String target1) { diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.baseline/src/com/oracle/graal/baseline/BaselineBytecodeParser.java --- a/graal/com.oracle.graal.baseline/src/com/oracle/graal/baseline/BaselineBytecodeParser.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.baseline/src/com/oracle/graal/baseline/BaselineBytecodeParser.java Sat May 03 21:46:35 2014 +0200 @@ -45,7 +45,6 @@ import com.oracle.graal.lir.*; import com.oracle.graal.lir.StandardOp.BlockEndOp; import com.oracle.graal.lir.gen.*; -import com.oracle.graal.nodes.cfg.*; import com.oracle.graal.phases.*; public class BaselineBytecodeParser extends AbstractBytecodeParser implements BytecodeParserTool { @@ -123,14 +122,9 @@ // create the control flow graph BaselineControlFlowGraph cfg = new BaselineControlFlowGraph(blockMap); - BlocksToDoubles blockProbabilities = new BlocksToDoubles(blockMap.blocks.size()); - for (BciBlock b : blockMap.blocks) { - blockProbabilities.put(b, 1); - } - // create the LIR - List> linearScanOrder = ComputeBlockOrder.computeLinearScanOrder(blockMap.blocks.size(), blockMap.startBlock, blockProbabilities); - List> codeEmittingOrder = ComputeBlockOrder.computeCodeEmittingOrder(blockMap.blocks.size(), blockMap.startBlock, blockProbabilities); + List> linearScanOrder = ComputeBlockOrder.computeLinearScanOrder(blockMap.blocks.size(), blockMap.startBlock); + List> codeEmittingOrder = ComputeBlockOrder.computeCodeEmittingOrder(blockMap.blocks.size(), blockMap.startBlock); LIR lir = new LIR(cfg, linearScanOrder, codeEmittingOrder); FrameMap frameMap = backend.newFrameMap(null); diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/cfg/AbstractBlock.java --- a/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/cfg/AbstractBlock.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/cfg/AbstractBlock.java Sat May 03 21:46:35 2014 +0200 @@ -55,4 +55,6 @@ void setAlign(boolean align); T getDominator(); + + double probability(); } diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.compiler.hsail.test.infra/src/com/oracle/graal/compiler/hsail/test/infra/ForceDeoptSubstitutions.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler.hsail.test.infra/src/com/oracle/graal/compiler/hsail/test/infra/ForceDeoptSubstitutions.java Sat May 03 21:46:35 2014 +0200 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2014, 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.graal.compiler.hsail.test.infra; + +import com.oracle.graal.api.replacements.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.api.meta.*; + +@ClassSubstitution(GraalKernelTester.class) +class ForceDeoptSubstitutions { + + /** + * Allows us to force a non-exception throwing deopt from java code. + */ + @MethodSubstitution + public static int forceDeopt(int x) { + DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.RuntimeConstraint); + // will never get here but keep the compiler happy + return x * x; + } + + @MethodSubstitution + public static double forceDeopt(double x) { + DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.RuntimeConstraint); + // will never get here but keep the compiler happy + return x * x; + } +} diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.compiler.hsail.test.infra/src/com/oracle/graal/compiler/hsail/test/infra/GraalKernelTester.java --- a/graal/com.oracle.graal.compiler.hsail.test.infra/src/com/oracle/graal/compiler/hsail/test/infra/GraalKernelTester.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.compiler.hsail.test.infra/src/com/oracle/graal/compiler/hsail/test/infra/GraalKernelTester.java Sat May 03 21:46:35 2014 +0200 @@ -50,8 +50,18 @@ public abstract class GraalKernelTester extends KernelTester { + private static boolean substitutionsInstalled; + + private static synchronized void installSubstitutions() { + if (!substitutionsInstalled) { + getHSAILBackend().getProviders().getReplacements().registerSubstitutions(ForceDeoptSubstitutions.class); + substitutionsInstalled = true; + } + } + public GraalKernelTester() { super(getHSAILBackend().isDeviceInitialized()); + installSubstitutions(); } protected static HSAILHotSpotBackend getHSAILBackend() { @@ -193,4 +203,14 @@ super.testGeneratedHsailUsingLambdaMethod(); } } + + // used for forcing a deoptimization + public static int forceDeopt(int x) { + return x * x; + } + + public static double forceDeopt(double x) { + return x * x; + } + } diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/DVec3.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/DVec3.java Sat May 03 21:46:35 2014 +0200 @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2009, 2012, 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.graal.compiler.hsail.test; + +/** + * A simple 3 element Vector object used in some junit tests. + */ +public class DVec3 { + + public DVec3(double x, double y, double z) { + this.x = x; + this.y = y; + this.z = z; + } + + public double x; + public double y; + public double z; + + public static DVec3 add(DVec3 a, DVec3 b) { + return new DVec3(a.x + b.x, a.y + b.y, a.z + b.z); + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof DVec3)) { + return false; + } + DVec3 oth = (DVec3) other; + return (oth.x == x && oth.y == y && oth.z == z); + } + + @Override + public String toString() { + return ("DVec3[" + x + ", " + y + ", " + z + "]"); + } + + @Override + public int hashCode() { + return (int) (x + y + z); + } + +} diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/ObjSpillDeoptBase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/ObjSpillDeoptBase.java Sat May 03 21:46:35 2014 +0200 @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2009, 2012, 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.graal.compiler.hsail.test; + +import java.util.*; + +import com.oracle.graal.compiler.hsail.test.infra.*; + +/** + * Base class for testing deopt when objects are in stack slots. + */ +public abstract class ObjSpillDeoptBase extends GraalKernelTester { + + abstract int getSize(); + + int loopcount = 5; + int objcount = 20; + @Result double[] out = new double[getSize()]; + @Result double[] aux = new double[getSize()]; + DVec3[] in = new DVec3[objcount]; + + public void doCompute(int gid, boolean causeDeopt) { + int idx = gid * 2 + 7; + DVec3 v0 = in[(idx++) % objcount]; + DVec3 v1 = in[(idx++) % objcount]; + DVec3 v2 = in[(idx++) % objcount]; + DVec3 v3 = in[(idx++) % objcount]; + DVec3 v4 = in[(idx++) % objcount]; + DVec3 v5 = in[(idx++) % objcount]; + DVec3 v6 = in[(idx++) % objcount]; + DVec3 v7 = in[(idx++) % objcount]; + DVec3 v8 = in[(idx++) % objcount]; + DVec3 v9 = in[(idx++) % objcount]; + idx += gid; + DVec3 v10 = in[(idx++) % objcount]; + DVec3 v11 = in[(idx++) % objcount]; + DVec3 v12 = in[(idx++) % objcount]; + DVec3 v13 = in[(idx++) % objcount]; + DVec3 v14 = in[(idx++) % objcount]; + DVec3 v15 = in[(idx++) % objcount]; + DVec3 v16 = in[(idx++) % objcount]; + DVec3 v17 = in[(idx++) % objcount]; + DVec3 v18 = in[(idx++) % objcount]; + DVec3 v19 = in[(idx++) % objcount]; + double sum = 0.0; + double sum1 = 0.0; + double sum2 = 0.0; + for (int i = 0; i < loopcount; i++) { + sum1 += v0.x + v1.y + v2.z + v3.x + v4.y + v5.z + v6.x + v7.y + v8.z + v9.x + i; + sum2 += v10.y + v11.z + v12.x + v13.y + v14.z + v15.x + v16.y + v17.z + v18.x + v19.y - i; + sum += sum1 - sum2 + i; + aux[gid] += sum1 + 1.2345; + } + if (causeDeopt) { + aux[gid] += forceDeopt(sum1); + } + out[gid] += sum; + } + + @Override + public void runTest() { + Arrays.fill(out, -1.0); + for (int i = 0; i < objcount; i++) { + in[i] = new DVec3(i / 10f, (i + 1) / 10f, (i + 2) / 10f); + } + dispatchMethodKernel(getSize()); + } + +} diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/ObjSpillDeoptMany20000Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/ObjSpillDeoptMany20000Test.java Sat May 03 21:46:35 2014 +0200 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2009, 2012, 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.graal.compiler.hsail.test; + +import org.junit.*; + +/** + * Tests deopt with objects in stack slots, with many items deopting. + */ +public class ObjSpillDeoptMany20000Test extends ObjSpillDeoptManyBase { + + @Override + int getSize() { + return 20000; + } + + @Test + public void test() { + testGeneratedHsail(); + } +} diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/ObjSpillDeoptMany5000Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/ObjSpillDeoptMany5000Test.java Sat May 03 21:46:35 2014 +0200 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2009, 2012, 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.graal.compiler.hsail.test; + +import org.junit.*; + +/** + * Tests deopt with objects in stack slots, with many items deopting. + */ +public class ObjSpillDeoptMany5000Test extends ObjSpillDeoptManyBase { + + @Override + int getSize() { + return 5000; + } + + @Test + public void test() { + testGeneratedHsail(); + } +} diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/ObjSpillDeoptMany99999Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/ObjSpillDeoptMany99999Test.java Sat May 03 21:46:35 2014 +0200 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2009, 2012, 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.graal.compiler.hsail.test; + +import org.junit.*; + +/** + * Tests deopt with objects in stack slots, with many items deopting. + */ +public class ObjSpillDeoptMany99999Test extends ObjSpillDeoptManyBase { + + @Override + int getSize() { + return 99999; + } + + @Test + public void test() { + testGeneratedHsail(); + } +} diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/ObjSpillDeoptManyBase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/ObjSpillDeoptManyBase.java Sat May 03 21:46:35 2014 +0200 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2009, 2012, 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.graal.compiler.hsail.test; + +/** + * Base class for testing deopt with objects in stack slots, with many items deopting. + */ +public abstract class ObjSpillDeoptManyBase extends ObjSpillDeoptBase { + + public void run(int gid) { + boolean causeDeopt = (gid < 4096 && gid % 512 == 1); + doCompute(gid, causeDeopt); + } +} diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/ObjSpillDeoptMost20000Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/ObjSpillDeoptMost20000Test.java Sat May 03 21:46:35 2014 +0200 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2009, 2012, 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.graal.compiler.hsail.test; + +import org.junit.*; + +/** + * Tests deopt with objects in stack slots, with most items deopting. + */ +public class ObjSpillDeoptMost20000Test extends ObjSpillDeoptMostBase { + + @Override + int getSize() { + return 20000; + } + + @Test + public void test() { + testGeneratedHsail(); + } +} diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/ObjSpillDeoptMost5000Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/ObjSpillDeoptMost5000Test.java Sat May 03 21:46:35 2014 +0200 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2009, 2012, 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.graal.compiler.hsail.test; + +import org.junit.*; + +/** + * Tests deopt with objects in stack slots, with most items deopting. + */ +public class ObjSpillDeoptMost5000Test extends ObjSpillDeoptMostBase { + + @Override + int getSize() { + return 5000; + } + + @Test + public void test() { + testGeneratedHsail(); + } +} diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/ObjSpillDeoptMost99999Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/ObjSpillDeoptMost99999Test.java Sat May 03 21:46:35 2014 +0200 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2009, 2012, 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.graal.compiler.hsail.test; + +import org.junit.*; + +/** + * Tests deopt with objects in stack slots, with most items deopting. + */ +public class ObjSpillDeoptMost99999Test extends ObjSpillDeoptMostBase { + + @Override + int getSize() { + return 99999; + } + + @Test + public void test() { + testGeneratedHsail(); + } +} diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/ObjSpillDeoptMostBase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/ObjSpillDeoptMostBase.java Sat May 03 21:46:35 2014 +0200 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2009, 2012, 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.graal.compiler.hsail.test; + +/** + * Base class for testing deopt with objects in stack slots. + */ +public abstract class ObjSpillDeoptMostBase extends ObjSpillDeoptBase { + + public void run(int gid) { + boolean causeDeopt = (gid % 500 != 1); + doCompute(gid, causeDeopt); + } +} diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/ObjSpillDeoptSingle100Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/ObjSpillDeoptSingle100Test.java Sat May 03 21:46:35 2014 +0200 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2009, 2012, 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.graal.compiler.hsail.test; + +import org.junit.*; + +/** + * Tests deopt with objects in stack slots, one item deopting. + */ +public class ObjSpillDeoptSingle100Test extends ObjSpillDeoptBase { + + final int size = 100; + + @Override + int getSize() { + return size; + } + + public void run(int gid) { + boolean causeDeopt = (gid == size / 2); + doCompute(gid, causeDeopt); + } + + @Test + public void test() { + testGeneratedHsail(); + } +} diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/ObjSpillDeoptSingle20000Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/ObjSpillDeoptSingle20000Test.java Sat May 03 21:46:35 2014 +0200 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2009, 2012, 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.graal.compiler.hsail.test; + +import org.junit.*; + +/** + * Tests deopt with objects in stack slots, one item deopting. + */ +public class ObjSpillDeoptSingle20000Test extends ObjSpillDeoptBase { + + final int size = 20000; + + @Override + int getSize() { + return size; + } + + public void run(int gid) { + boolean causeDeopt = (gid == size / 2); + doCompute(gid, causeDeopt); + } + + @Test + public void test() { + testGeneratedHsail(); + } +} diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/StaticDoubleSpillBoundsCatchOneTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/StaticDoubleSpillBoundsCatchOneTest.java Sat May 03 21:46:35 2014 +0200 @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2009, 2012, 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.graal.compiler.hsail.test; + +import java.util.*; + +import org.junit.*; + +import com.oracle.graal.compiler.hsail.test.infra.*; + +/** + * Tests the spilling of double variables into memory with deopt + */ +public class StaticDoubleSpillBoundsCatchOneTest extends GraalKernelTester { + + static final int size = 100; + private double[] in = new double[size * 400]; + @Result public double[] out = new double[size * 400]; + @Result public double[] aux = new double[2]; + + public static void run(double[] out, double[] in, double[] aux, int gid) { + int id = gid; + int step = 20; + double sum0; + double sum1; + double sum2; + double sum3; + double sum4; + double sum5; + double sum6; + double sum7; + double sum8; + double sum9; + double sum10; + double sum11; + double sum12; + double sum13; + double sum14; + double sum15; + double sum16; + double sum17; + double sum18; + double sum19; + sum0 = sum1 = sum2 = sum3 = sum4 = sum5 = sum6 = sum7 = sum8 = sum9 = 0; + sum10 = sum11 = sum12 = sum13 = sum14 = sum15 = sum16 = sum17 = sum18 = sum19 = 0; + try { + for (int i = 0; i < size; i += step) { + sum0 += in[i + 0]; + sum1 += in[i + 1]; + sum2 += in[i + 2]; + sum3 += in[i + 3]; + sum4 += in[i + 4]; + sum5 += in[i + 5]; + sum6 += in[i + 6]; + sum7 += in[i + 7]; + sum8 += in[i + 8]; + sum9 += in[i + 9]; + sum10 += in[i + 0]; + sum11 += in[i + 1]; + sum12 += in[i + 2]; + sum13 += in[i + 3]; + sum14 += in[i + 4]; + sum15 += in[i + 5]; + sum16 += in[i + 6]; + sum17 += in[i + 7]; + sum18 += in[i + 8]; + sum19 += in[i + 9]; + + if (id == size / 2) { + aux[id] = sum1 + sum2 + sum3 + sum4 + sum5 + sum6 + sum7 + sum8 + sum9 + sum10 + sum11 + sum12 + sum13 + sum14 + sum15 + sum16; + } + } + } catch (ArrayIndexOutOfBoundsException e) { + aux[0] += sum1 + sum2; + } + + out[id * step + 0] = sum0; + out[id * step + 1] = sum1; + out[id * step + 2] = sum2; + out[id * step + 3] = sum3; + out[id * step + 4] = sum4; + out[id * step + 5] = sum5; + out[id * step + 6] = sum6; + out[id * step + 7] = sum7; + out[id * step + 8] = sum8; + out[id * step + 9] = sum9; + out[id * step + 10] = sum10; + out[id * step + 11] = sum11; + out[id * step + 12] = sum12; + out[id * step + 13] = sum13; + out[id * step + 14] = sum14; + out[id * step + 15] = sum15; + out[id * step + 16] = sum16; + out[id * step + 17] = sum17; + out[id * step + 18] = sum18; + out[id * step + 19] = sum19; + } + + @Override + public void runTest() { + /** + * Call it for a range, specifying testmethod args (but not the fields it uses or the gid + * argument). + * + */ + Arrays.fill(out, -1f); + for (int i = 0; i < size; i++) { + in[i] = i + 1; + } + dispatchMethodKernel(size, out, in, aux); + } + + @Test + public void test() { + testGeneratedHsail(); + } + +} diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/StaticDoubleSpillBoundsCatchTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/StaticDoubleSpillBoundsCatchTest.java Sat May 03 21:46:35 2014 +0200 @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2009, 2012, 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.graal.compiler.hsail.test; + +import java.util.*; + +import org.junit.*; + +import com.oracle.graal.compiler.hsail.test.infra.*; + +/** + * Tests the spilling of double variables into memory with deopt + */ +public class StaticDoubleSpillBoundsCatchTest extends GraalKernelTester { + + static final int size = 100; + private double[] in = new double[size * 400]; + @Result public double[] out = new double[size * 400]; + @Result public double[] aux = new double[size]; + + public static void run(double[] out, double[] in, double[] aux, int gid) { + int id = gid; + int step = 20; + double sum0; + double sum1; + double sum2; + double sum3; + double sum4; + double sum5; + double sum6; + double sum7; + double sum8; + double sum9; + double sum10; + double sum11; + double sum12; + double sum13; + double sum14; + double sum15; + double sum16; + double sum17; + double sum18; + double sum19; + sum0 = sum1 = sum2 = sum3 = sum4 = sum5 = sum6 = sum7 = sum8 = sum9 = 0; + sum10 = sum11 = sum12 = sum13 = sum14 = sum15 = sum16 = sum17 = sum18 = sum19 = 0; + try { + for (int i = 0; i < size; i += step) { + sum0 += in[i + 0]; + sum1 += in[i + 1]; + sum2 += in[i + 2]; + sum3 += in[i + 3]; + sum4 += in[i + 4]; + sum5 += in[i + 5]; + sum6 += in[i + 6]; + sum7 += in[i + 7]; + sum8 += in[i + 8]; + sum9 += in[i + 9]; + sum10 += in[i + 0]; + sum11 += in[i + 1]; + sum12 += in[i + 2]; + sum13 += in[i + 3]; + sum14 += in[i + 4]; + sum15 += in[i + 5]; + sum16 += in[i + 6]; + sum17 += in[i + 7]; + sum18 += in[i + 8]; + sum19 += in[i + 9]; + + if (id > size / 2) { + aux[id + 10] += sum1 + sum2; + // + sum3 + sum4 + sum5 + sum6 + sum7 + sum8 + sum9 + sum10 + sum11 + sum12 + + // sum13 + sum14 + sum15 + sum16; + } + } + } catch (ArrayIndexOutOfBoundsException e) { + aux[id] += sum1 + sum2; + } + + out[id * step + 0] = sum0; + out[id * step + 1] = sum1; + out[id * step + 2] = sum2; + out[id * step + 3] = sum3; + out[id * step + 4] = sum4; + out[id * step + 5] = sum5; + out[id * step + 6] = sum6; + out[id * step + 7] = sum7; + out[id * step + 8] = sum8; + out[id * step + 9] = sum9; + out[id * step + 10] = sum10; + out[id * step + 11] = sum11; + out[id * step + 12] = sum12; + out[id * step + 13] = sum13; + out[id * step + 14] = sum14; + out[id * step + 15] = sum15; + out[id * step + 16] = sum16; + out[id * step + 17] = sum17; + out[id * step + 18] = sum18; + out[id * step + 19] = sum19; + } + + @Override + public void runTest() { + /** + * Call it for a range, specifying testmethod args (but not the fields it uses or the gid + * argument). + * + */ + Arrays.fill(out, -1f); + Arrays.fill(aux, 0f); + for (int i = 0; i < size; i++) { + in[i] = i + 1; + } + dispatchMethodKernel(size, out, in, aux); + } + + @Test + public void test() { + testGeneratedHsail(); + } + +} diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/StaticDoubleSpillTest.java --- a/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/StaticDoubleSpillTest.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/StaticDoubleSpillTest.java Sat May 03 21:46:35 2014 +0200 @@ -113,13 +113,14 @@ * argument). * */ - Arrays.fill(out, 0f); - Arrays.fill(in, 0f); + Arrays.fill(out, -1f); + for (int i = 0; i < size; i++) { + in[i] = i + 1; + } dispatchMethodKernel(size, out, in); } @Test - @Ignore("until stack slots are supported in deopt") public void test() { testGeneratedHsail(); } diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/StaticIntSpillTest.java --- a/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/StaticIntSpillTest.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/StaticIntSpillTest.java Sat May 03 21:46:35 2014 +0200 @@ -88,7 +88,6 @@ } @Test - @Ignore("until stack slots are supported in deopt") public void test() { testGeneratedHsail(); } diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/lambda/ArrayListGetTest.java --- a/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/lambda/ArrayListGetTest.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/lambda/ArrayListGetTest.java Sat May 03 21:46:35 2014 +0200 @@ -25,12 +25,12 @@ import static com.oracle.graal.debug.Debug.*; -import com.oracle.graal.compiler.hsail.test.infra.GraalKernelTester; -import com.oracle.graal.debug.*; +import java.util.*; -import org.junit.Test; +import org.junit.*; -import java.util.ArrayList; +import com.oracle.graal.compiler.hsail.test.infra.*; +import com.oracle.graal.debug.*; /** * Tests calling ArrayList.get(). @@ -60,6 +60,7 @@ // NYI emitForeignCall charAlignedDisjointArraycopy @Test(expected = com.oracle.graal.compiler.common.GraalInternalError.class) + @Ignore public void testUsingLambdaMethod() { try (DebugConfigScope s = disableIntercept()) { testGeneratedHsailUsingLambdaMethod(); diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.compiler.hsail/src/com/oracle/graal/compiler/hsail/HSAILLIRGenerator.java --- a/graal/com.oracle.graal.compiler.hsail/src/com/oracle/graal/compiler/hsail/HSAILLIRGenerator.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.compiler.hsail/src/com/oracle/graal/compiler/hsail/HSAILLIRGenerator.java Sat May 03 21:46:35 2014 +0200 @@ -37,20 +37,10 @@ import com.oracle.graal.lir.StandardOp.JumpOp; import com.oracle.graal.lir.gen.*; import com.oracle.graal.lir.hsail.*; -import com.oracle.graal.lir.hsail.HSAILArithmetic.ConvertOp; -import com.oracle.graal.lir.hsail.HSAILArithmetic.Op1Stack; +import com.oracle.graal.lir.hsail.HSAILArithmetic.Op1Reg; import com.oracle.graal.lir.hsail.HSAILArithmetic.Op2Reg; -import com.oracle.graal.lir.hsail.HSAILArithmetic.Op2Stack; -import com.oracle.graal.lir.hsail.HSAILArithmetic.ShiftOp; -import com.oracle.graal.lir.hsail.HSAILControlFlow.CompareBranchOp; -import com.oracle.graal.lir.hsail.HSAILControlFlow.CondMoveOp; -import com.oracle.graal.lir.hsail.HSAILControlFlow.FloatCondMoveOp; -import com.oracle.graal.lir.hsail.HSAILControlFlow.ReturnOp; -import com.oracle.graal.lir.hsail.HSAILControlFlow.StrategySwitchOp; -import com.oracle.graal.lir.hsail.HSAILMove.LeaOp; -import com.oracle.graal.lir.hsail.HSAILMove.MembarOp; -import com.oracle.graal.lir.hsail.HSAILMove.MoveFromRegOp; -import com.oracle.graal.lir.hsail.HSAILMove.MoveToRegOp; +import com.oracle.graal.lir.hsail.HSAILControlFlow.*; +import com.oracle.graal.lir.hsail.HSAILMove.*; import com.oracle.graal.phases.util.*; /** @@ -262,16 +252,16 @@ case Int: // Note: The Int case also handles the negation of shorts, bytes, and chars because // Java treats these types as ints at the bytecode level. - append(new Op1Stack(INEG, result, input)); + append(new Op1Reg(INEG, result, input)); break; case Long: - append(new Op1Stack(LNEG, result, input)); + append(new Op1Reg(LNEG, result, input)); break; case Double: - append(new Op1Stack(DNEG, result, input)); + append(new Op1Reg(DNEG, result, input)); break; case Float: - append(new Op1Stack(FNEG, result, input)); + append(new Op1Reg(FNEG, result, input)); break; default: throw GraalInternalError.shouldNotReachHere(); @@ -293,10 +283,10 @@ case Int: // Note: The Int case also covers other primitive integral types smaller than an int // (char, byte, short) because Java treats these types as ints. - append(new Op1Stack(INOT, result, input)); + append(new Op1Reg(INOT, result, input)); break; case Long: - append(new Op1Stack(LNOT, result, input)); + append(new Op1Reg(LNOT, result, input)); break; default: throw GraalInternalError.shouldNotReachHere(); @@ -309,16 +299,16 @@ Variable result = newVariable(a.getKind()); switch (a.getKind()) { case Int: - append(new Op2Stack(IADD, result, a, loadNonConst(b))); + append(new Op2Reg(IADD, result, a, loadNonConst(b))); break; case Long: - append(new Op2Stack(LADD, result, a, loadNonConst(b))); + append(new Op2Reg(LADD, result, a, loadNonConst(b))); break; case Float: - append(new Op2Stack(FADD, result, a, loadNonConst(b))); + append(new Op2Reg(FADD, result, a, loadNonConst(b))); break; case Double: - append(new Op2Stack(DADD, result, a, loadNonConst(b))); + append(new Op2Reg(DADD, result, a, loadNonConst(b))); break; case Object: throw GraalInternalError.shouldNotReachHere(); @@ -334,19 +324,19 @@ Variable result = newVariable(a.getKind()); switch (a.getKind()) { case Int: - append(new Op2Stack(IADD, result, a, loadNonConst(b))); + append(new Op2Reg(IADD, result, a, loadNonConst(b))); break; case Long: - append(new Op2Stack(LADD, result, a, loadNonConst(b))); + append(new Op2Reg(LADD, result, a, loadNonConst(b))); break; case Float: - append(new Op2Stack(FADD, result, a, loadNonConst(b))); + append(new Op2Reg(FADD, result, a, loadNonConst(b))); break; case Double: - append(new Op2Stack(DADD, result, a, loadNonConst(b))); + append(new Op2Reg(DADD, result, a, loadNonConst(b))); break; case Object: - append(new Op2Stack(OADD, result, a, loadNonConst(b))); + append(new Op2Reg(OADD, result, a, loadNonConst(b))); break; default: throw GraalInternalError.shouldNotReachHere(); @@ -359,16 +349,16 @@ Variable result = newVariable(a.getKind()); switch (a.getKind()) { case Int: - append(new Op2Stack(ISUB, result, a, loadNonConst(b))); + append(new Op2Reg(ISUB, result, a, loadNonConst(b))); break; case Float: - append(new Op2Stack(FSUB, result, a, loadNonConst(b))); + append(new Op2Reg(FSUB, result, a, loadNonConst(b))); break; case Long: - append(new Op2Stack(LSUB, result, a, loadNonConst(b))); + append(new Op2Reg(LSUB, result, a, loadNonConst(b))); break; case Double: - append(new Op2Stack(DSUB, result, a, loadNonConst(b))); + append(new Op2Reg(DSUB, result, a, loadNonConst(b))); break; default: throw GraalInternalError.shouldNotReachHere(); @@ -418,16 +408,16 @@ Variable result = newVariable(a.getKind()); switch (a.getKind()) { case Int: - append(new Op2Stack(IDIV, result, a, loadNonConst(b))); + append(new Op2Reg(IDIV, result, a, loadNonConst(b))); break; case Long: - append(new Op2Stack(LDIV, result, a, loadNonConst(b))); + append(new Op2Reg(LDIV, result, a, loadNonConst(b))); break; case Float: - append(new Op2Stack(FDIV, result, a, loadNonConst(b))); + append(new Op2Reg(FDIV, result, a, loadNonConst(b))); break; case Double: - append(new Op2Stack(DDIV, result, a, loadNonConst(b))); + append(new Op2Reg(DDIV, result, a, loadNonConst(b))); break; default: throw GraalInternalError.shouldNotReachHere(); @@ -441,16 +431,16 @@ Variable result = newVariable(a.getKind()); switch (a.getKind()) { case Int: - append(new Op2Stack(IREM, result, a, loadNonConst(b))); + append(new Op2Reg(IREM, result, a, loadNonConst(b))); break; case Long: - append(new Op2Stack(LREM, result, a, loadNonConst(b))); + append(new Op2Reg(LREM, result, a, loadNonConst(b))); break; case Float: - append(new Op2Stack(FREM, result, a, loadNonConst(b))); + append(new Op2Reg(FREM, result, a, loadNonConst(b))); break; case Double: - append(new Op2Stack(DREM, result, a, loadNonConst(b))); + append(new Op2Reg(DREM, result, a, loadNonConst(b))); break; default: throw GraalInternalError.shouldNotReachHere(); @@ -473,10 +463,10 @@ Variable result = newVariable(a.getKind()); switch (a.getKind()) { case Int: - append(new Op2Stack(IAND, result, a, loadNonConst(b))); + append(new Op2Reg(IAND, result, a, loadNonConst(b))); break; case Long: - append(new Op2Stack(LAND, result, a, loadNonConst(b))); + append(new Op2Reg(LAND, result, a, loadNonConst(b))); break; default: throw GraalInternalError.shouldNotReachHere(); @@ -489,10 +479,10 @@ Variable result = newVariable(a.getKind()); switch (a.getKind()) { case Int: - append(new Op2Stack(IOR, result, a, loadNonConst(b))); + append(new Op2Reg(IOR, result, a, loadNonConst(b))); break; case Long: - append(new Op2Stack(LOR, result, a, loadNonConst(b))); + append(new Op2Reg(LOR, result, a, loadNonConst(b))); break; default: throw GraalInternalError.shouldNotReachHere(); @@ -505,10 +495,10 @@ Variable result = newVariable(a.getKind()); switch (a.getKind()) { case Int: - append(new Op2Stack(IXOR, result, a, loadNonConst(b))); + append(new Op2Reg(IXOR, result, a, loadNonConst(b))); break; case Long: - append(new Op2Stack(LXOR, result, a, loadNonConst(b))); + append(new Op2Reg(LXOR, result, a, loadNonConst(b))); break; default: throw GraalInternalError.shouldNotReachHere(); @@ -720,7 +710,7 @@ @Override public Value emitMathAbs(Value input) { Variable result = newVariable(input.getPlatformKind()); - append(new Op1Stack(ABS, result, input)); + append(new Op1Reg(ABS, result, input)); return result; } @@ -732,7 +722,7 @@ */ public Value emitMathCeil(Value input) { Variable result = newVariable(input.getPlatformKind()); - append(new Op1Stack(CEIL, result, input)); + append(new Op1Reg(CEIL, result, input)); return result; } @@ -744,7 +734,7 @@ */ public Value emitMathFloor(Value input) { Variable result = newVariable(input.getPlatformKind()); - append(new Op1Stack(FLOOR, result, input)); + append(new Op1Reg(FLOOR, result, input)); return result; } @@ -756,7 +746,7 @@ */ public Value emitMathRint(Value input) { Variable result = newVariable(input.getPlatformKind()); - append(new Op1Stack(RINT, result, input)); + append(new Op1Reg(RINT, result, input)); return result; } @@ -769,7 +759,7 @@ @Override public Value emitMathSqrt(Value input) { Variable result = newVariable(input.getPlatformKind()); - append(new Op1Stack(SQRT, result, input)); + append(new Op1Reg(SQRT, result, input)); return result; } diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/BoxingEliminationTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/BoxingEliminationTest.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/BoxingEliminationTest.java Sat May 03 21:46:35 2014 +0200 @@ -29,6 +29,7 @@ import com.oracle.graal.nodes.*; import com.oracle.graal.phases.*; import com.oracle.graal.phases.common.*; +import com.oracle.graal.phases.common.inlining.*; import com.oracle.graal.phases.tiers.*; import com.oracle.graal.virtual.phases.ea.*; diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/DegeneratedLoopsTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/DegeneratedLoopsTest.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/DegeneratedLoopsTest.java Sat May 03 21:46:35 2014 +0200 @@ -30,6 +30,7 @@ import com.oracle.graal.nodes.*; import com.oracle.graal.phases.*; import com.oracle.graal.phases.common.*; +import com.oracle.graal.phases.common.inlining.*; import com.oracle.graal.phases.tiers.*; /** diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/FinalizableSubclassTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/FinalizableSubclassTest.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/FinalizableSubclassTest.java Sat May 03 21:46:35 2014 +0200 @@ -38,6 +38,7 @@ import com.oracle.graal.nodes.java.*; import com.oracle.graal.phases.*; import com.oracle.graal.phases.common.*; +import com.oracle.graal.phases.common.inlining.*; import com.oracle.graal.phases.tiers.*; public class FinalizableSubclassTest extends GraalCompilerTest { diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/InvokeExceptionTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/InvokeExceptionTest.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/InvokeExceptionTest.java Sat May 03 21:46:35 2014 +0200 @@ -30,6 +30,7 @@ import com.oracle.graal.nodes.*; import com.oracle.graal.phases.*; import com.oracle.graal.phases.common.*; +import com.oracle.graal.phases.common.inlining.*; import com.oracle.graal.phases.tiers.*; public class InvokeExceptionTest extends GraalCompilerTest { diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/InvokeHintsTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/InvokeHintsTest.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/InvokeHintsTest.java Sat May 03 21:46:35 2014 +0200 @@ -30,6 +30,7 @@ import com.oracle.graal.nodes.*; import com.oracle.graal.phases.*; import com.oracle.graal.phases.common.*; +import com.oracle.graal.phases.common.inlining.*; import com.oracle.graal.phases.tiers.*; public class InvokeHintsTest extends GraalCompilerTest { diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/LockEliminationTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/LockEliminationTest.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/LockEliminationTest.java Sat May 03 21:46:35 2014 +0200 @@ -32,6 +32,7 @@ import com.oracle.graal.nodes.spi.*; import com.oracle.graal.phases.*; import com.oracle.graal.phases.common.*; +import com.oracle.graal.phases.common.inlining.*; import com.oracle.graal.phases.tiers.*; public class LockEliminationTest extends GraalCompilerTest { diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/MemoryScheduleTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/MemoryScheduleTest.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/MemoryScheduleTest.java Sat May 03 21:46:35 2014 +0200 @@ -24,7 +24,6 @@ import static com.oracle.graal.compiler.common.GraalOptions.*; import static org.junit.Assert.*; - import java.util.*; import org.junit.*; @@ -43,6 +42,7 @@ import com.oracle.graal.options.OptionValue.OverrideScope; import com.oracle.graal.phases.*; import com.oracle.graal.phases.common.*; +import com.oracle.graal.phases.common.inlining.*; import com.oracle.graal.phases.schedule.*; import com.oracle.graal.phases.schedule.SchedulePhase.MemoryScheduling; import com.oracle.graal.phases.schedule.SchedulePhase.SchedulingStrategy; diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/MonitorGraphTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/MonitorGraphTest.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/MonitorGraphTest.java Sat May 03 21:46:35 2014 +0200 @@ -23,7 +23,6 @@ package com.oracle.graal.compiler.test; import static com.oracle.graal.graph.iterators.NodePredicates.*; - import java.util.*; import org.junit.*; @@ -35,6 +34,7 @@ import com.oracle.graal.nodes.java.*; import com.oracle.graal.phases.*; import com.oracle.graal.phases.common.*; +import com.oracle.graal.phases.common.inlining.*; import com.oracle.graal.phases.tiers.*; /** diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EATestBase.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EATestBase.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EATestBase.java Sat May 03 21:46:35 2014 +0200 @@ -37,6 +37,7 @@ import com.oracle.graal.nodes.virtual.*; import com.oracle.graal.phases.*; import com.oracle.graal.phases.common.*; +import com.oracle.graal.phases.common.inlining.*; import com.oracle.graal.phases.tiers.*; import com.oracle.graal.virtual.phases.ea.*; @@ -126,7 +127,7 @@ /** * Runs Escape Analysis on the given snippet and makes sure that no allocations remain in the * graph. - * + * * @param snippet the name of the method whose graph should be processed * @param expectedConstantResult if this is non-null, the resulting graph needs to have the * given constant return value @@ -156,7 +157,7 @@ new InliningPhase(new CanonicalizerPhase(true)).apply(graph, context); new DeadCodeEliminationPhase().apply(graph); new CanonicalizerPhase(true).apply(graph, context); - new PartialEscapePhase(iterativeEscapeAnalysis, false, new CanonicalizerPhase(true)).apply(graph, context); + new PartialEscapePhase(iterativeEscapeAnalysis, false, new CanonicalizerPhase(true), null).apply(graph, context); returnNodes = graph.getNodes(ReturnNode.class).snapshot(); } catch (Throwable e) { throw Debug.handle(e); diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EarlyReadEliminationTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EarlyReadEliminationTest.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EarlyReadEliminationTest.java Sat May 03 21:46:35 2014 +0200 @@ -27,6 +27,7 @@ import com.oracle.graal.api.code.*; import com.oracle.graal.phases.*; import com.oracle.graal.phases.common.*; +import com.oracle.graal.phases.common.inlining.*; import com.oracle.graal.phases.tiers.*; import com.oracle.graal.virtual.phases.ea.*; diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PEAReadEliminationTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PEAReadEliminationTest.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PEAReadEliminationTest.java Sat May 03 21:46:35 2014 +0200 @@ -23,7 +23,6 @@ package com.oracle.graal.compiler.test.ea; import static org.junit.Assert.*; - import java.util.*; import org.junit.*; @@ -34,6 +33,7 @@ import com.oracle.graal.nodes.java.*; import com.oracle.graal.phases.*; import com.oracle.graal.phases.common.*; +import com.oracle.graal.phases.common.inlining.*; import com.oracle.graal.phases.tiers.*; import com.oracle.graal.virtual.phases.ea.*; @@ -247,6 +247,6 @@ Assumptions assumptions = new Assumptions(false); HighTierContext context = new HighTierContext(getProviders(), assumptions, null, getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL); new InliningPhase(new CanonicalizerPhase(true)).apply(graph, context); - new PartialEscapePhase(false, true, new CanonicalizerPhase(true)).apply(graph, context); + new PartialEscapePhase(false, true, new CanonicalizerPhase(true), null).apply(graph, context); } } diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PartialEscapeAnalysisTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PartialEscapeAnalysisTest.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PartialEscapeAnalysisTest.java Sat May 03 21:46:35 2014 +0200 @@ -23,6 +23,7 @@ package com.oracle.graal.compiler.test.ea; import java.lang.ref.*; +import java.util.function.*; import org.junit.*; @@ -30,7 +31,6 @@ import com.oracle.graal.graph.*; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.java.*; -import com.oracle.graal.nodes.util.*; import com.oracle.graal.nodes.virtual.*; import com.oracle.graal.phases.common.*; import com.oracle.graal.phases.graph.*; @@ -186,11 +186,11 @@ Assert.assertTrue("partial escape analysis should have removed all NewInstanceNode allocations", graph.getNodes().filter(NewInstanceNode.class).isEmpty()); Assert.assertTrue("partial escape analysis should have removed all NewArrayNode allocations", graph.getNodes().filter(NewArrayNode.class).isEmpty()); - NodesToDoubles nodeProbabilities = new ComputeProbabilityClosure(graph).apply(); + ToDoubleFunction nodeProbabilities = new FixedNodeProbabilityCache(); double probabilitySum = 0; int materializeCount = 0; for (CommitAllocationNode materialize : graph.getNodes().filter(CommitAllocationNode.class)) { - probabilitySum += nodeProbabilities.get(materialize) * materialize.getVirtualObjects().size(); + probabilitySum += nodeProbabilities.applyAsDouble(materialize) * materialize.getVirtualObjects().size(); materializeCount += materialize.getVirtualObjects().size(); } Assert.assertEquals("unexpected number of MaterializeObjectNodes", expectedCount, materializeCount); diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PoorMansEATest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PoorMansEATest.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PoorMansEATest.java Sat May 03 21:46:35 2014 +0200 @@ -34,6 +34,7 @@ import com.oracle.graal.nodes.spi.*; import com.oracle.graal.phases.*; import com.oracle.graal.phases.common.*; +import com.oracle.graal.phases.common.inlining.*; import com.oracle.graal.phases.tiers.*; /** diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/inlining/InliningTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/inlining/InliningTest.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/inlining/InliningTest.java Sat May 03 21:46:35 2014 +0200 @@ -23,7 +23,6 @@ package com.oracle.graal.compiler.test.inlining; import static org.junit.Assert.*; - import java.lang.reflect.*; import org.junit.*; @@ -37,6 +36,7 @@ import com.oracle.graal.nodes.*; import com.oracle.graal.phases.*; import com.oracle.graal.phases.common.*; +import com.oracle.graal.phases.common.inlining.*; import com.oracle.graal.phases.tiers.*; public class InliningTest extends GraalCompilerTest { diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java Sat May 03 21:46:35 2014 +0200 @@ -45,11 +45,9 @@ import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.cfg.*; import com.oracle.graal.nodes.spi.*; -import com.oracle.graal.nodes.util.*; import com.oracle.graal.options.*; import com.oracle.graal.phases.*; import com.oracle.graal.phases.common.*; -import com.oracle.graal.phases.graph.*; import com.oracle.graal.phases.schedule.*; import com.oracle.graal.phases.tiers.*; import com.oracle.graal.phases.util.*; @@ -230,10 +228,8 @@ List linearScanOrder = null; try (Scope ds = Debug.scope("MidEnd")) { try (Scope s = Debug.scope("ComputeLinearScanOrder")) { - NodesToDoubles nodeProbabilities = new ComputeProbabilityClosure(graph).apply(); - BlocksToDoubles blockProbabilities = BlocksToDoubles.createFromNodeProbability(nodeProbabilities, schedule.getCFG()); - codeEmittingOrder = ComputeBlockOrder.computeCodeEmittingOrder(blocks.length, startBlock, blockProbabilities); - linearScanOrder = ComputeBlockOrder.computeLinearScanOrder(blocks.length, startBlock, blockProbabilities); + codeEmittingOrder = ComputeBlockOrder.computeCodeEmittingOrder(blocks.length, startBlock); + linearScanOrder = ComputeBlockOrder.computeLinearScanOrder(blocks.length, startBlock); lir = new LIR(schedule.getCFG(), linearScanOrder, codeEmittingOrder); Debug.dump(lir, "After linear scan order"); diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/phases/HighTier.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/phases/HighTier.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/phases/HighTier.java Sat May 03 21:46:35 2014 +0200 @@ -24,13 +24,13 @@ import static com.oracle.graal.compiler.common.GraalOptions.*; import static com.oracle.graal.compiler.phases.HighTier.Options.*; - import com.oracle.graal.loop.phases.*; import com.oracle.graal.nodes.spi.*; import com.oracle.graal.options.*; import com.oracle.graal.phases.*; import com.oracle.graal.phases.common.*; import com.oracle.graal.phases.common.cfs.IterativeFlowSensitiveReductionPhase; +import com.oracle.graal.phases.common.inlining.*; import com.oracle.graal.phases.tiers.*; import com.oracle.graal.virtual.phases.ea.*; diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.gpu/src/com/oracle/graal/gpu/ExternalCompilationResult.java --- a/graal/com.oracle.graal.gpu/src/com/oracle/graal/gpu/ExternalCompilationResult.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.gpu/src/com/oracle/graal/gpu/ExternalCompilationResult.java Sat May 03 21:46:35 2014 +0200 @@ -43,6 +43,8 @@ private long entryPoint; private StructuredGraph hostGraph; + private int[] oopMapArray; + /** * Set the address for the point of entry to the external compilation result. * @@ -75,4 +77,13 @@ public StructuredGraph getHostGraph() { return hostGraph; } + + public void setOopMapArray(int[] arr) { + oopMapArray = arr; + } + + public int[] getOopMapArray() { + return oopMapArray; + } + } diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackendFactory.java --- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackendFactory.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackendFactory.java Sat May 03 21:46:35 2014 +0200 @@ -113,7 +113,9 @@ Replacements replacements = createReplacements(runtime, assumptions, p, snippetReflection); HotSpotDisassemblerProvider disassembler = createDisassembler(runtime); HotSpotSuitesProvider suites = createSuites(runtime); - HotSpotProviders providers = new HotSpotProviders(metaAccess, codeCache, constantReflection, foreignCalls, lowerer, replacements, disassembler, suites, registers, snippetReflection); + HotSpotMethodHandleAccessProvider methodHandleAccess = new HotSpotMethodHandleAccessProvider(); + HotSpotProviders providers = new HotSpotProviders(metaAccess, codeCache, constantReflection, foreignCalls, lowerer, replacements, disassembler, suites, registers, snippetReflection, + methodHandleAccess); return createBackend(runtime, providers); } @@ -189,15 +191,15 @@ } else { /* * System V Application Binary Interface, AMD64 Architecture Processor Supplement - * + * * Draft Version 0.96 - * + * * http://www.uclibc.org/docs/psABI-x86_64.pdf - * + * * 3.2.1 - * + * * ... - * + * * This subsection discusses usage of each register. Registers %rbp, %rbx and %r12 * through %r15 "belong" to the calling function and the called function is required to * preserve their values. In other words, a called function must preserve these diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotBackend.java --- a/graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotBackend.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotBackend.java Sat May 03 21:46:35 2014 +0200 @@ -66,7 +66,6 @@ import com.oracle.graal.lir.hsail.HSAILMove.AtomicReadAndAddOp; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.StructuredGraph.GuardsStage; -import com.oracle.graal.nodes.calc.*; import com.oracle.graal.nodes.extended.*; import com.oracle.graal.nodes.java.*; import com.oracle.graal.nodes.spi.*; @@ -145,7 +144,21 @@ */ public HotSpotNmethod compileAndInstallKernel(Method method) { ResolvedJavaMethod javaMethod = getProviders().getMetaAccess().lookupJavaMethod(method); - return installKernel(javaMethod, compileKernel(javaMethod, true)); + HotSpotNmethod nm = installKernel(javaMethod, compileKernel(javaMethod, true)); + try (Scope s = Debug.scope("HostCodeGen")) { + if (Debug.isLogEnabled()) { + DisassemblerProvider dis = getRuntime().getHostBackend().getDisassembler(); + if (dis != null) { + String disasm = dis.disassemble(nm); + Debug.log("host code generated for %s%n%s", javaMethod, disasm); + } else { + Debug.log("host code disassembler is null"); + } + } + } catch (Throwable e) { + throw Debug.handle(e); + } + return nm; } /** @@ -274,7 +287,8 @@ } } - HotSpotNmethod code = new HotSpotNmethod(javaMethod, hsailCode.getName(), false, true); + HSAILHotSpotNmethod code = new HSAILHotSpotNmethod(javaMethod, hsailCode.getName(), false, true); + code.setOopMapArray(hsailCode.getOopMapArray()); HotSpotCompiledNmethod compiled = new HotSpotCompiledNmethod(getTarget(), javaMethod, compilationResult); CodeInstallResult result = getRuntime().getCompilerToVM().installCode(compiled, code, null); if (result != CodeInstallResult.OK) { @@ -359,11 +373,16 @@ if (!deviceInitialized) { throw new GraalInternalError("Cannot execute GPU kernel if device is not initialized"); } - Object[] oopsSaveArea = new Object[maxDeoptIndex * 16]; - return executeKernel0(kernel, jobSize, args, oopsSaveArea, donorThreadPool.get().getThreads(), HsailAllocBytesPerWorkitem.getValue()); + int[] oopMapArray = ((HSAILHotSpotNmethod) kernel).getOopMapArray(); + int saveAreaCounts = OopMapArrayBuilder.getSaveAreaCounts(oopMapArray); + int numDRegs = (saveAreaCounts >> 8) & 0xff; + int numStackSlots = (saveAreaCounts >> 16); + // pessimistically assume that any of the DRegs or stackslots could be oops + Object[] oopsSaveArea = new Object[maxDeoptIndex * (numDRegs + numStackSlots)]; + return executeKernel0(kernel, jobSize, args, oopsSaveArea, donorThreadPool.get().getThreads(), HsailAllocBytesPerWorkitem.getValue(), oopMapArray); } - private static native boolean executeKernel0(HotSpotInstalledCode kernel, int jobSize, Object[] args, Object[] oopsSave, Thread[] donorThreads, int allocBytesPerWorkitem) + private static native boolean executeKernel0(HotSpotInstalledCode kernel, int jobSize, Object[] args, Object[] oopsSave, Thread[] donorThreads, int allocBytesPerWorkitem, int[] oopMapArray) throws InvalidInstalledCodeException; /** @@ -419,6 +438,22 @@ } } + static class HSAILHotSpotNmethod extends HotSpotNmethod { + private int[] oopMapArray; + + HSAILHotSpotNmethod(HotSpotResolvedJavaMethod method, String name, boolean isDefault, boolean isExternal) { + super(method, name, isDefault, isExternal); + } + + void setOopMapArray(int[] array) { + oopMapArray = array; + } + + int[] getOopMapArray() { + return oopMapArray; + } + } + @Override protected Assembler createAssembler(FrameMap frameMap) { return new HSAILHotSpotAssembler(getTarget()); @@ -686,10 +721,54 @@ asm.emitString(spillsegStringFinal, spillsegDeclarationPosition); // Emit the epilogue. - // TODO: keep track of whether we need it + int numSRegs = 0; + int numDRegs = 0; + int numStackSlotBytes = 0; if (useHSAILDeoptimization) { + // get the union of registers and stack slots needed to be saved at the infopoints + // while doing this compute the highest register in each category + HSAILHotSpotRegisterConfig hsailRegConfig = (HSAILHotSpotRegisterConfig) regConfig; + Set infoUsedRegs = new TreeSet<>(); + Set infoUsedStackSlots = new HashSet<>(); + List infoList = crb.compilationResult.getInfopoints(); + for (Infopoint info : infoList) { + BytecodeFrame frame = info.debugInfo.frame(); + while (frame != null) { + for (int i = 0; i < frame.numLocals + frame.numStack; i++) { + Value val = frame.values[i]; + if (isLegal(val)) { + if (isRegister(val)) { + Register reg = asRegister(val); + infoUsedRegs.add(reg); + if (hsailRegConfig.isAllocatableSReg(reg)) { + numSRegs = Math.max(numSRegs, reg.encoding + 1); + } else if (hsailRegConfig.isAllocatableDReg(reg)) { + numDRegs = Math.max(numDRegs, reg.encoding + 1); + } + } else if (isStackSlot(val)) { + StackSlot slot = asStackSlot(val); + Kind slotKind = slot.getKind(); + int slotSizeBytes = (slotKind.isObject() ? 8 : slotKind.getByteCount()); + int slotOffsetMax = HSAIL.getStackOffsetStart(slot, slotSizeBytes * 8) + slotSizeBytes; + numStackSlotBytes = Math.max(numStackSlotBytes, slotOffsetMax); + infoUsedStackSlots.add(slot); + } + } + } + frame = frame.caller(); + } + } + + // round up numSRegs to even number so dregs start on aligned boundary + numSRegs += (numSRegs & 1); + + // numStackSlots is the number of 8-byte locations used for stack variables + int numStackSlots = (numStackSlotBytes + 7) / 8; + final int offsetToDeoptSaveStates = config.hsailSaveStatesOffset0; - final int sizeofKernelDeopt = config.hsailSaveStatesOffset1 - config.hsailSaveStatesOffset0; + final int sizeofKernelDeoptHeader = config.hsailKernelDeoptimizationHeaderSize; + final int bytesPerSaveArea = 4 * numSRegs + 8 * numDRegs + 8 * numStackSlots; + final int sizeofKernelDeopt = sizeofKernelDeoptHeader + bytesPerSaveArea; final int offsetToNeverRanArray = config.hsailNeverRanArrayOffset; final int offsetToDeoptNextIndex = config.hsailDeoptNextIndexOffset; final int offsetToDeoptimizationWorkItem = config.hsailDeoptimizationWorkItem; @@ -708,7 +787,6 @@ AllocatableValue codeBufferOffsetReg = HSAIL.codeBufferOffsetReg.asValue(Kind.Int); AllocatableValue scratch32 = HSAIL.s34.asValue(Kind.Int); AllocatableValue workidreg = HSAIL.s35.asValue(Kind.Int); - AllocatableValue dregOopMapReg = HSAIL.dregOopMapReg.asValue(Kind.Int); HSAILAddress deoptNextIndexAddr = new HSAILAddressValue(Kind.Int, scratch64, offsetToDeoptNextIndex).toAddress(); HSAILAddress neverRanArrayAddr = new HSAILAddressValue(Kind.Int, scratch64, offsetToNeverRanArray).toAddress(); @@ -769,8 +847,6 @@ // Now scratch64 is the _deopt_info._first_frame HSAILAddress pcStoreAddr = new HSAILAddressValue(Kind.Int, waveMathScratch1, offsetToFramePc).toAddress(); HSAILAddress regCountsAddr = new HSAILAddressValue(Kind.Int, waveMathScratch1, offsetToNumSaves).toAddress(); - HSAILAddress dregOopMapAddr = new HSAILAddressValue(Kind.Int, waveMathScratch1, offsetToNumSaves + 2).toAddress(); - asm.emitComment("// store deopting workitem"); asm.emitWorkItemAbsId(scratch32); asm.emitStore(Kind.Int, scratch32, workItemAddr); @@ -778,58 +854,61 @@ asm.emitStore(Kind.Int, actionAndReasonReg, actionReasonStoreAddr); asm.emitComment("// store PC"); asm.emitStore(Kind.Int, codeBufferOffsetReg, pcStoreAddr); - asm.emitComment("// store regCounts"); - asm.emitStore(Kind.Short, Constant.forInt(32 + (16 << 8) + (0 << 16)), regCountsAddr); - asm.emitComment("// store dreg ref map bits"); - asm.emitStore(Kind.Short, dregOopMapReg, dregOopMapAddr); + + asm.emitComment("// store regCounts (" + numSRegs + " $s registers and " + numDRegs + " $d registers" + numStackSlots + " stack slots)"); + asm.emitStore(Kind.Int, Constant.forInt(numSRegs + (numDRegs << 8) + (numStackSlots << 16)), regCountsAddr); - // get the union of registers needed to be saved at the infopoints - boolean[] infoUsedRegs = new boolean[HSAIL.threadRegister.number + 1]; - List infoList = crb.compilationResult.getInfopoints(); - for (Infopoint info : infoList) { - BytecodeFrame frame = info.debugInfo.frame(); - for (int i = 0; i < frame.numLocals + frame.numStack; i++) { - Value val = frame.values[i]; - if (isLegal(val) && isRegister(val)) { - Register reg = asRegister(val); - infoUsedRegs[reg.number] = true; - } + // loop thru the usedValues storing each of the registers that are used. + // we always store in a fixed location, even if some registers are skipped + asm.emitComment("// store used regs"); + for (Register reg : infoUsedRegs) { + if (hsailRegConfig.isAllocatableSReg(reg)) { + // 32 bit registers + Kind kind = Kind.Int; + int ofst = offsetToSaveArea + reg.encoding * 4; + HSAILAddress addr = new HSAILAddressValue(kind, waveMathScratch1, ofst).toAddress(); + AllocatableValue regValue = reg.asValue(kind); + asm.emitStore(kind, regValue, addr); + } else if (hsailRegConfig.isAllocatableDReg(reg)) { + // 64 bit registers + Kind kind = Kind.Long; + // d reg ofst starts past the 32 sregs + int ofst = offsetToSaveArea + (numSRegs * 4) + reg.encoding * 8; + HSAILAddress addr = new HSAILAddressValue(kind, waveMathScratch1, ofst).toAddress(); + AllocatableValue regValue = reg.asValue(kind); + asm.emitStore(kind, regValue, addr); + } else { + throw GraalInternalError.unimplemented(); } } - // loop storing each of the 32 s registers that are used by infopoints - // we always store in a fixed location, even if some registers are not stored - asm.emitComment("// store used s regs"); - int ofst = offsetToSaveArea; - for (Register sreg : HSAIL.sRegisters) { - if (infoUsedRegs[sreg.number]) { - Kind kind = Kind.Int; + // loop thru the usedStackSlots creating instructions to save in the save area + if (numStackSlotBytes > 0) { + asm.emitComment("// store stack slots (uses " + numStackSlotBytes + " bytes)"); + for (StackSlot slot : infoUsedStackSlots) { + asm.emitComment("// store " + slot); + Kind kind = slot.getKind(); + int sizeInBits = (kind.isObject() || kind.getByteCount() == 8 ? 64 : 32); + int ofst = offsetToSaveArea + (numSRegs * 4) + (numDRegs * 8) + HSAIL.getStackOffsetStart(slot, sizeInBits); HSAILAddress addr = new HSAILAddressValue(kind, waveMathScratch1, ofst).toAddress(); - AllocatableValue sregValue = sreg.asValue(kind); - asm.emitStore(kind, sregValue, addr); + if (sizeInBits == 64) { + asm.emitSpillLoad(kind, scratch64, slot); + asm.emitStore(kind, scratch64, addr); + } else { + asm.emitSpillLoad(kind, scratch32, slot); + asm.emitStore(kind, scratch32, addr); + } } - ofst += 4; } - // loop storing each of the 16 d registers that are used by infopoints - asm.emitComment("// store used d regs"); - for (Register dreg : HSAIL.dRegisters) { - if (infoUsedRegs[dreg.number]) { - Kind kind = Kind.Long; - HSAILAddress addr = new HSAILAddressValue(kind, waveMathScratch1, ofst).toAddress(); - AllocatableValue dregValue = dreg.asValue(kind); - asm.emitStore(kind, dregValue, addr); - } - ofst += 8; - } - - // for now, ignore saving the spill variables but that would come here - asm.emitString0(labelExit + ":\n"); // and emit the return crb.frameContext.leave(crb); asm.exit(); + // build the oopMap Array + int[] oopMapArray = new OopMapArrayBuilder().build(infoList, numSRegs, numDRegs, numStackSlots, hsailRegConfig); + ((ExternalCompilationResult) crb.compilationResult).setOopMapArray(oopMapArray); } else { // Deoptimization is explicitly off, so emit simple return asm.emitString0(asm.getDeoptLabelName() + ":\n"); @@ -841,10 +920,115 @@ ExternalCompilationResult compilationResult = (ExternalCompilationResult) crb.compilationResult; HSAILHotSpotLIRGenerationResult lirGenRes = ((HSAILCompilationResultBuilder) crb).lirGenRes; - compilationResult.setHostGraph(prepareHostGraph(method, lirGenRes.getDeopts(), getProviders(), config)); + compilationResult.setHostGraph(prepareHostGraph(method, lirGenRes.getDeopts(), getProviders(), config, numSRegs, numDRegs)); } - private static StructuredGraph prepareHostGraph(ResolvedJavaMethod method, List deopts, HotSpotProviders providers, HotSpotVMConfig config) { + private static class OopMapArrayBuilder { + // oopMapArray struct + // int bytesPerSaveArea; (not strictly part of oopsmap but convenient to put here) + // int intsPerInfopoint; + static final int SAVEAREACOUNTS_OFST = 0; + static final int INTSPERINFOPOINT_OFST = 1; + static final int HEADERSIZE = 2; + // for each infopoint: + // int deoptId + // one or more ints of bits for the oopmap + + private int[] array; + private int intsPerInfopoint; + + int[] build(List infoList, int numSRegs, int numDRegs, int numStackSlots, HSAILHotSpotRegisterConfig hsailRegConfig) { + // we are told that infoList is always sorted + // each infoPoint can have a different oopMap + + // since numStackSlots is the number of 8-byte stack slots used, it is an upper limit on + // the number of oop stack slots + int bitsPerInfopoint = numDRegs + numStackSlots; + int intsForBits = (bitsPerInfopoint + 31) / 32; + int numInfopoints = infoList.size(); + intsPerInfopoint = intsForBits + 1; // +1 for the pcoffset + int arraySize = HEADERSIZE + (numInfopoints * intsPerInfopoint); + array = new int[arraySize]; + array[INTSPERINFOPOINT_OFST] = intsPerInfopoint; + // compute saveAreaCounts + int saveAreaCounts = (numSRegs & 0xff) + (numDRegs << 8) + (numStackSlots << 16); + array[SAVEAREACOUNTS_OFST] = saveAreaCounts; + + // loop thru the infoList + int infoIndex = 0; + for (Infopoint info : infoList) { + setOopMapPcOffset(infoIndex, info.pcOffset); + BytecodeFrame frame = info.debugInfo.frame(); + while (frame != null) { + for (int i = 0; i < frame.numLocals + frame.numStack; i++) { + Value val = frame.values[i]; + if (isLegal(val)) { + if (isRegister(val)) { + Register reg = asRegister(val); + if (val.getKind().isObject()) { + assert (hsailRegConfig.isAllocatableDReg(reg)); + int bitIndex = reg.encoding(); + setOopMapBit(infoIndex, bitIndex); + } + } else if (isStackSlot(val)) { + StackSlot slot = asStackSlot(val); + if (val.getKind().isObject()) { + assert (HSAIL.getStackOffsetStart(slot, 64) % 8 == 0); + int bitIndex = numDRegs + HSAIL.getStackOffsetStart(slot, 64) / 8; + setOopMapBit(infoIndex, bitIndex); + } + } + } + } + frame = frame.caller(); + } + infoIndex++; + } + try (Scope s = Debug.scope("CodeGen")) { + if (Debug.isLogEnabled()) { + Debug.log("numSRegs=%d, numDRegs=%d, numStackSlots=%d", numSRegs, numDRegs, numStackSlots); + // show infopoint oopmap details + for (infoIndex = 0; infoIndex < infoList.size(); infoIndex++) { + String infoString = "Infopoint " + infoIndex + ", pcOffset=" + getOopMapPcOffset(infoIndex) + ", oopmap="; + for (int i = 0; i < intsForBits; i++) { + infoString += (i != 0 ? ", " : "") + Integer.toHexString(getOopMapBitsAsInt(infoIndex, i)); + } + Debug.log(infoString); + } + } + } catch (Throwable e) { + throw Debug.handle(e); + } + + return array; + } + + private void setOopMapPcOffset(int infoIndex, int pcOffset) { + int arrIndex = HEADERSIZE + infoIndex * intsPerInfopoint; + array[arrIndex] = pcOffset; + } + + private int getOopMapPcOffset(int infoIndex) { + int arrIndex = HEADERSIZE + infoIndex * intsPerInfopoint; + return array[arrIndex]; + } + + private void setOopMapBit(int infoIndex, int bitIndex) { + int arrIndex = HEADERSIZE + infoIndex * intsPerInfopoint + 1 + bitIndex / 32; + array[arrIndex] |= (1 << (bitIndex % 32)); + } + + private int getOopMapBitsAsInt(int infoIndex, int intIndex) { + int arrIndex = HEADERSIZE + infoIndex * intsPerInfopoint + 1 + intIndex; + return array[arrIndex]; + } + + public static int getSaveAreaCounts(int[] array) { + return array[SAVEAREACOUNTS_OFST]; + } + } + + private static StructuredGraph prepareHostGraph(ResolvedJavaMethod method, List deopts, HotSpotProviders providers, HotSpotVMConfig config, int numSRegs, int numDRegs) { if (deopts.isEmpty()) { return null; } @@ -868,7 +1052,7 @@ keyProbabilities[i] = 1.0 / deopts.size(); keys[i] = deopt.getCodeBufferPos(); assert keys[i] >= 0; - branches[i] = createHostDeoptBranch(deopt, hsailFrame, reasonAndAction, speculation, providers, config); + branches[i] = createHostDeoptBranch(deopt, hsailFrame, reasonAndAction, speculation, providers, config, numSRegs, numDRegs); i++; } @@ -892,34 +1076,35 @@ return BeginNode.begin(vmError); } - private static BeginNode createHostDeoptBranch(DeoptimizingOp deopt, ParameterNode hsailFrame, ValueNode reasonAndAction, ValueNode speculation, HotSpotProviders providers, HotSpotVMConfig config) { + private static BeginNode createHostDeoptBranch(DeoptimizingOp deopt, ParameterNode hsailFrame, ValueNode reasonAndAction, ValueNode speculation, HotSpotProviders providers, + HotSpotVMConfig config, int numSRegs, int numDRegs) { BeginNode branch = hsailFrame.graph().add(new BeginNode()); DynamicDeoptimizeNode deoptimization = hsailFrame.graph().add(new DynamicDeoptimizeNode(reasonAndAction, speculation)); - deoptimization.setStateBefore(createFrameState(deopt.getFrameState().topFrame, hsailFrame, providers, config)); + deoptimization.setStateBefore(createFrameState(deopt.getFrameState().topFrame, hsailFrame, providers, config, numSRegs, numDRegs)); branch.setNext(deoptimization); return branch; } - private static FrameState createFrameState(BytecodeFrame lowLevelFrame, ParameterNode hsailFrame, HotSpotProviders providers, HotSpotVMConfig config) { + private static FrameState createFrameState(BytecodeFrame lowLevelFrame, ParameterNode hsailFrame, HotSpotProviders providers, HotSpotVMConfig config, int numSRegs, int numDRegs) { StructuredGraph hostGraph = hsailFrame.graph(); ValueNode[] locals = new ValueNode[lowLevelFrame.numLocals]; for (int i = 0; i < lowLevelFrame.numLocals; i++) { - locals[i] = getNodeForValueFromFrame(lowLevelFrame.getLocalValue(i), hsailFrame, hostGraph, providers, config); + locals[i] = getNodeForValueFromFrame(lowLevelFrame.getLocalValue(i), hsailFrame, hostGraph, providers, config, numSRegs, numDRegs); } List stack = new ArrayList<>(lowLevelFrame.numStack); for (int i = 0; i < lowLevelFrame.numStack; i++) { - stack.add(getNodeForValueFromFrame(lowLevelFrame.getStackValue(i), hsailFrame, hostGraph, providers, config)); + stack.add(getNodeForValueFromFrame(lowLevelFrame.getStackValue(i), hsailFrame, hostGraph, providers, config, numSRegs, numDRegs)); } ValueNode[] locks = new ValueNode[lowLevelFrame.numLocks]; MonitorIdNode[] monitorIds = new MonitorIdNode[lowLevelFrame.numLocks]; for (int i = 0; i < lowLevelFrame.numLocks; i++) { HotSpotMonitorValue lockValue = (HotSpotMonitorValue) lowLevelFrame.getLockValue(i); - locks[i] = getNodeForValueFromFrame(lockValue, hsailFrame, hostGraph, providers, config); + locks[i] = getNodeForValueFromFrame(lockValue, hsailFrame, hostGraph, providers, config, numSRegs, numDRegs); monitorIds[i] = getMonitorIdForHotSpotMonitorValueFromFrame(lockValue, hsailFrame, hostGraph); } FrameState frameState = hostGraph.add(new FrameState(lowLevelFrame.getMethod(), lowLevelFrame.getBCI(), locals, stack, locks, monitorIds, lowLevelFrame.rethrowException, false)); if (lowLevelFrame.caller() != null) { - frameState.setOuterFrameState(createFrameState(lowLevelFrame.caller(), hsailFrame, providers, config)); + frameState.setOuterFrameState(createFrameState(lowLevelFrame.caller(), hsailFrame, providers, config, numSRegs, numDRegs)); } return frameState; } @@ -932,21 +1117,23 @@ throw GraalInternalError.unimplemented(); } - private static ValueNode getNodeForValueFromFrame(Value localValue, ParameterNode hsailFrame, StructuredGraph hostGraph, HotSpotProviders providers, HotSpotVMConfig config) { + private static ValueNode getNodeForValueFromFrame(Value localValue, ParameterNode hsailFrame, StructuredGraph hostGraph, HotSpotProviders providers, HotSpotVMConfig config, int numSRegs, + int numDRegs) { ValueNode valueNode; if (localValue instanceof Constant) { valueNode = ConstantNode.forConstant((Constant) localValue, providers.getMetaAccess(), hostGraph); } else if (localValue instanceof VirtualObject) { throw GraalInternalError.unimplemented(); } else if (localValue instanceof StackSlot) { - throw GraalInternalError.unimplemented(); + StackSlot slot = (StackSlot) localValue; + valueNode = getNodeForStackSlotFromFrame(slot, localValue.getKind(), hsailFrame, hostGraph, providers, config, numSRegs, numDRegs); } else if (localValue instanceof HotSpotMonitorValue) { HotSpotMonitorValue hotSpotMonitorValue = (HotSpotMonitorValue) localValue; - return getNodeForValueFromFrame(hotSpotMonitorValue.getOwner(), hsailFrame, hostGraph, providers, config); + return getNodeForValueFromFrame(hotSpotMonitorValue.getOwner(), hsailFrame, hostGraph, providers, config, numSRegs, numDRegs); } else if (localValue instanceof RegisterValue) { RegisterValue registerValue = (RegisterValue) localValue; int regNumber = registerValue.getRegister().number; - valueNode = getNodeForRegisterFromFrame(regNumber, localValue.getKind(), hsailFrame, hostGraph, providers, config); + valueNode = getNodeForRegisterFromFrame(regNumber, localValue.getKind(), hsailFrame, hostGraph, providers, config, numSRegs); } else if (Value.ILLEGAL.equals(localValue)) { valueNode = null; } else { @@ -955,20 +1142,18 @@ return valueNode; } - private static ValueNode getNodeForRegisterFromFrame(int regNumber, Kind valueKind, ParameterNode hsailFrame, StructuredGraph hostGraph, HotSpotProviders providers, HotSpotVMConfig config) { + private static ValueNode getNodeForRegisterFromFrame(int regNumber, Kind valueKind, ParameterNode hsailFrame, StructuredGraph hostGraph, HotSpotProviders providers, HotSpotVMConfig config, + int numSRegs) { ValueNode valueNode; LocationNode location; + int longSize = providers.getCodeCache().getTarget().arch.getSizeInBytes(Kind.Long); + int intSize = providers.getCodeCache().getTarget().arch.getSizeInBytes(Kind.Int); if (regNumber >= HSAIL.s0.number && regNumber <= HSAIL.s31.number) { - int intSize = providers.getCodeCache().getTarget().arch.getSizeInBytes(Kind.Int); long offset = config.hsailFrameSaveAreaOffset + intSize * (regNumber - HSAIL.s0.number); location = ConstantLocationNode.create(FINAL_LOCATION, valueKind, offset, hostGraph); } else if (regNumber >= HSAIL.d0.number && regNumber <= HSAIL.d15.number) { - int longSize = providers.getCodeCache().getTarget().arch.getSizeInBytes(Kind.Long); - long offset = config.hsailFrameSaveAreaOffset + longSize * (regNumber - HSAIL.d0.number); - LocationNode numSRegsLocation = ConstantLocationNode.create(FINAL_LOCATION, Kind.Byte, config.hsailFrameNumSRegOffset, hostGraph); - ValueNode numSRegs = hostGraph.unique(new FloatingReadNode(hsailFrame, numSRegsLocation, null, StampFactory.forInteger(8))); - numSRegs = SignExtendNode.convert(numSRegs, StampFactory.forKind(Kind.Byte)); - location = IndexedLocationNode.create(FINAL_LOCATION, valueKind, offset, numSRegs, hostGraph, 4); + long offset = config.hsailFrameSaveAreaOffset + intSize * numSRegs + longSize * (regNumber - HSAIL.d0.number); + location = ConstantLocationNode.create(FINAL_LOCATION, valueKind, offset, hostGraph); } else { throw GraalInternalError.shouldNotReachHere("unknown hsail register: " + regNumber); } @@ -976,4 +1161,18 @@ return valueNode; } + private static ValueNode getNodeForStackSlotFromFrame(StackSlot slot, Kind valueKind, ParameterNode hsailFrame, StructuredGraph hostGraph, HotSpotProviders providers, HotSpotVMConfig config, + int numSRegs, int numDRegs) { + int slotSizeInBits = (valueKind == Kind.Object ? 64 : valueKind.getByteCount() * 8); + if ((slotSizeInBits == 32) || (slotSizeInBits == 64)) { + int longSize = providers.getCodeCache().getTarget().arch.getSizeInBytes(Kind.Long); + int intSize = providers.getCodeCache().getTarget().arch.getSizeInBytes(Kind.Int); + long offset = config.hsailFrameSaveAreaOffset + (intSize * numSRegs) + (longSize * numDRegs) + HSAIL.getStackOffsetStart(slot, slotSizeInBits); + LocationNode location = ConstantLocationNode.create(FINAL_LOCATION, valueKind, offset, hostGraph); + ValueNode valueNode = hostGraph.unique(new FloatingReadNode(hsailFrame, location, null, StampFactory.forKind(valueKind))); + return valueNode; + } else { + throw GraalInternalError.shouldNotReachHere("unsupported stack slot kind: " + valueKind); + } + } } diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotBackendFactory.java --- a/graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotBackendFactory.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotBackendFactory.java Sat May 03 21:46:35 2014 +0200 @@ -57,7 +57,8 @@ Replacements replacements = new HSAILHotSpotReplacementsImpl(p, host.getSnippetReflection(), assumptions, codeCache.getTarget(), host.getReplacements()); HotSpotDisassemblerProvider disassembler = host.getDisassembler(); SuitesProvider suites = new HotSpotSuitesProvider(runtime); - HotSpotProviders providers = new HotSpotProviders(metaAccess, codeCache, constantReflection, foreignCalls, lowerer, replacements, disassembler, suites, registers, host.getSnippetReflection()); + HotSpotProviders providers = new HotSpotProviders(metaAccess, codeCache, constantReflection, foreignCalls, lowerer, replacements, disassembler, suites, registers, host.getSnippetReflection(), + host.getMethodHandleAccess()); // pass registers info down to ReplacementsUtil (maybe a better way to do this?) HSAILHotSpotReplacementsUtil.initialize(providers.getRegisters()); diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotRegisterConfig.java --- a/graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotRegisterConfig.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotRegisterConfig.java Sat May 03 21:46:35 2014 +0200 @@ -194,6 +194,14 @@ throw new UnsupportedOperationException(); } + public boolean isAllocatableSReg(Register reg) { + return (reg.number >= HSAIL.s0.number && reg.number <= HSAIL.s31.number); + } + + public boolean isAllocatableDReg(Register reg) { + return (reg.number >= HSAIL.d0.number && reg.number <= HSAIL.d15.number); + } + public HSAILHotSpotRegisterConfig() { } diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotSafepointOp.java --- a/graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotSafepointOp.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotSafepointOp.java Sat May 03 21:46:35 2014 +0200 @@ -22,8 +22,6 @@ */ package com.oracle.graal.hotspot.hsail; -import static com.oracle.graal.api.code.ValueUtil.*; - import com.oracle.graal.api.code.*; import com.oracle.graal.api.meta.*; import com.oracle.graal.asm.hsail.*; @@ -42,7 +40,6 @@ private Constant actionAndReason; @State protected LIRFrameState frameState; protected int codeBufferPos = -1; - protected int dregOopMap = 0; final int offsetToNoticeSafepoints; public HSAILHotSpotSafepointOp(LIRFrameState state, HotSpotVMConfig config, NodeLIRBuilderTool tool) { @@ -77,23 +74,10 @@ masm.emitCompare(Kind.Int, scratch32, Constant.forInt(0), "eq", false, false); masm.cbr(afterSafepointLabel); - BytecodeFrame frame = frameState.debugInfo().frame(); - for (int i = 0; i < frame.numLocals + frame.numStack; i++) { - Value val = frame.values[i]; - if (isLegal(val) && isRegister(val)) { - Register reg = asRegister(val); - if (val.getKind() == Kind.Object) { - dregOopMap |= 1 << (reg.encoding()); - } - } - } - AllocatableValue actionAndReasonReg = HSAIL.actionAndReasonReg.asValue(Kind.Int); AllocatableValue codeBufferOffsetReg = HSAIL.codeBufferOffsetReg.asValue(Kind.Int); - AllocatableValue dregOopMapReg = HSAIL.dregOopMapReg.asValue(Kind.Int); masm.emitMov(Kind.Int, actionAndReasonReg, actionAndReason); masm.emitMov(Kind.Int, codeBufferOffsetReg, Constant.forInt(codeBufferPos)); - masm.emitMov(Kind.Int, dregOopMapReg, Constant.forInt(dregOopMap)); masm.emitJumpToLabelName(masm.getDeoptLabelName()); masm.emitString0(afterSafepointLabel + ":\n"); diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.hotspot.ptx/src/com/oracle/graal/hotspot/ptx/PTXHotSpotBackendFactory.java --- a/graal/com.oracle.graal.hotspot.ptx/src/com/oracle/graal/hotspot/ptx/PTXHotSpotBackendFactory.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.hotspot.ptx/src/com/oracle/graal/hotspot/ptx/PTXHotSpotBackendFactory.java Sat May 03 21:46:35 2014 +0200 @@ -47,7 +47,8 @@ HotSpotDisassemblerProvider disassembler = host.getDisassembler(); SuitesProvider suites = new DefaultSuitesProvider(); HotSpotRegistersProvider registers = new HotSpotRegisters(PTX.tid, Register.None, Register.None); - HotSpotProviders providers = new HotSpotProviders(metaAccess, codeCache, constantReflection, foreignCalls, lowerer, replacements, disassembler, suites, registers, host.getSnippetReflection()); + HotSpotProviders providers = new HotSpotProviders(metaAccess, codeCache, constantReflection, foreignCalls, lowerer, replacements, disassembler, suites, registers, host.getSnippetReflection(), + host.getMethodHandleAccess()); return new PTXHotSpotBackend(runtime, providers); } diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotBackendFactory.java --- a/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotBackendFactory.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotBackendFactory.java Sat May 03 21:46:35 2014 +0200 @@ -64,7 +64,9 @@ HotSpotReplacementsImpl replacements = new HotSpotReplacementsImpl(p, snippetReflection, runtime.getConfig(), assumptions, target); HotSpotDisassemblerProvider disassembler = new HotSpotDisassemblerProvider(runtime); HotSpotSuitesProvider suites = new HotSpotSuitesProvider(runtime); - HotSpotProviders providers = new HotSpotProviders(metaAccess, codeCache, constantReflection, foreignCalls, lowerer, replacements, disassembler, suites, registers, snippetReflection); + HotSpotMethodHandleAccessProvider methodHandleAccess = new HotSpotMethodHandleAccessProvider(); + HotSpotProviders providers = new HotSpotProviders(metaAccess, codeCache, constantReflection, foreignCalls, lowerer, replacements, disassembler, suites, registers, snippetReflection, + methodHandleAccess); return new SPARCHotSpotBackend(runtime, providers); } diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/WriteBarrierAdditionTest.java --- a/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/WriteBarrierAdditionTest.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/WriteBarrierAdditionTest.java Sat May 03 21:46:35 2014 +0200 @@ -23,7 +23,6 @@ package com.oracle.graal.hotspot.test; import static com.oracle.graal.hotspot.replacements.HotSpotReplacementsUtil.*; - import java.lang.ref.*; import java.lang.reflect.*; @@ -44,6 +43,7 @@ import com.oracle.graal.nodes.spi.*; import com.oracle.graal.phases.*; import com.oracle.graal.phases.common.*; +import com.oracle.graal.phases.common.inlining.*; import com.oracle.graal.phases.tiers.*; import com.oracle.graal.runtime.*; diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/WriteBarrierVerificationTest.java --- a/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/WriteBarrierVerificationTest.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/WriteBarrierVerificationTest.java Sat May 03 21:46:35 2014 +0200 @@ -40,6 +40,7 @@ import com.oracle.graal.nodes.spi.*; import com.oracle.graal.phases.*; import com.oracle.graal.phases.common.*; +import com.oracle.graal.phases.common.inlining.*; import com.oracle.graal.phases.graph.*; import com.oracle.graal.phases.graph.ReentrantNodeIterator.NodeIteratorClosure; import com.oracle.graal.phases.tiers.*; @@ -693,7 +694,7 @@ DebugConfig debugConfig = DebugScope.getConfig(); DebugConfig fixedConfig = Debug.fixedConfig(false, false, false, false, debugConfig.dumpHandlers(), debugConfig.output()); try (DebugConfigScope s = Debug.setConfig(fixedConfig)) { - ReentrantNodeIterator.apply(closure, graph.start(), false, null); + ReentrantNodeIterator.apply(closure, graph.start(), false); new WriteBarrierVerificationPhase().apply(graph); } catch (AssertionError error) { /* diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompilationTask.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompilationTask.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompilationTask.java Sat May 03 21:46:35 2014 +0200 @@ -29,7 +29,7 @@ import static com.oracle.graal.compiler.common.UnsafeAccess.*; import static com.oracle.graal.hotspot.bridge.VMToCompilerImpl.*; import static com.oracle.graal.nodes.StructuredGraph.*; -import static com.oracle.graal.phases.common.InliningUtil.*; +import static com.oracle.graal.phases.common.inlining.InliningUtil.*; import java.io.*; import java.lang.management.*; diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotGraalRuntime.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotGraalRuntime.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotGraalRuntime.java Sat May 03 21:46:35 2014 +0200 @@ -368,6 +368,8 @@ return (T) this; } else if (clazz == SnippetReflectionProvider.class) { return (T) getHostProviders().getSnippetReflection(); + } else if (clazz == MethodHandleAccessProvider.class) { + return (T) getHostProviders().getMethodHandleAccess(); } return null; } diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotOptions.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotOptions.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotOptions.java Sat May 03 21:46:35 2014 +0200 @@ -37,7 +37,7 @@ import com.oracle.graal.debug.*; import com.oracle.graal.hotspot.logging.*; import com.oracle.graal.options.*; -import com.oracle.graal.phases.common.*; +import com.oracle.graal.phases.common.inlining.*; /** * Called from {@code graalCompiler.cpp} to parse any Graal specific options. Such options are @@ -113,7 +113,7 @@ /** * Parses a given option value specification. - * + * * @param option the specification of an option and its value * @param setter the object to notify of the parsed option and value. If null, the * {@link OptionValue#setValue(Object)} method of the specified option is called @@ -213,7 +213,7 @@ *

* This method verifies that the named field exists and is of an expected type. However, it does * not verify that the timer or metric created has the same name of the field. - * + * * @param c the class in which the field is declared * @param name the name of the field */ @@ -239,7 +239,7 @@ /** * Called from VM code once all Graal command line options have been processed by * {@link #setOption(String)}. - * + * * @param timeCompilations true if the CITime or CITimeEach HotSpot VM options are set */ public static void finalizeOptions(boolean timeCompilations) { @@ -255,7 +255,7 @@ /** * Wraps some given text to one or more lines of a given maximum width. - * + * * @param text text to wrap * @param width maximum width of an output line, exception for words in {@code text} longer than * this value @@ -314,7 +314,7 @@ /** * Compute string similarity based on Dice's coefficient. - * + * * Ported from str_similar() in globals.cpp. */ static float stringSimiliarity(String str1, String str2) { diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotReplacementsImpl.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotReplacementsImpl.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotReplacementsImpl.java Sat May 03 21:46:35 2014 +0200 @@ -86,16 +86,14 @@ HotSpotResolvedJavaMethod hsMethod = (HotSpotResolvedJavaMethod) method; int intrinsicId = hsMethod.intrinsicId(); if (intrinsicId != 0) { - if (intrinsicId == config.vmIntrinsicInvokeBasic) { - return MethodHandleInvokeBasicNode.class; - } else if (intrinsicId == config.vmIntrinsicLinkToInterface) { - return MethodHandleLinkToInterfaceNode.class; - } else if (intrinsicId == config.vmIntrinsicLinkToSpecial) { - return MethodHandleLinkToSpecialNode.class; - } else if (intrinsicId == config.vmIntrinsicLinkToStatic) { - return MethodHandleLinkToStaticNode.class; - } else if (intrinsicId == config.vmIntrinsicLinkToVirtual) { - return MethodHandleLinkToVirtualNode.class; + /* + * The methods of MethodHandle that need substitution are signature-polymorphic, i.e., + * the VM replicates them for every signature that they are actually used for. + * Therefore, we cannot use the usual annotation-driven mechanism to define the + * substitution. + */ + if (MethodHandleNode.lookupMethodHandleIntrinsic(method) != null) { + return MethodHandleNode.class; } } return super.getMacroSubstitution(method); diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java Sat May 03 21:46:35 2014 +0200 @@ -1022,8 +1022,6 @@ * exceptions from Hsail back to C++ runtime. */ @HotSpotVMField(name = "Hsail::HSAILDeoptimizationInfo::_notice_safepoints", type = "jint*", get = HotSpotVMField.Type.OFFSET) @Stable public int hsailNoticeSafepointsOffset; - @HotSpotVMField(name = "Hsail::HSAILDeoptimizationInfo::_deopt_save_states[0]", type = "Hsail::HSAILKernelDeoptimization", get = HotSpotVMField.Type.OFFSET) @Stable public int hsailSaveStatesOffset0; - @HotSpotVMField(name = "Hsail::HSAILDeoptimizationInfo::_deopt_save_states[1]", type = "Hsail::HSAILKernelDeoptimization", get = HotSpotVMField.Type.OFFSET) @Stable public int hsailSaveStatesOffset1; @HotSpotVMField(name = "Hsail::HSAILDeoptimizationInfo::_deopt_occurred", type = "jint", get = HotSpotVMField.Type.OFFSET) @Stable public int hsailDeoptOccurredOffset; @HotSpotVMField(name = "Hsail::HSAILDeoptimizationInfo::_never_ran_array", type = "jboolean *", get = HotSpotVMField.Type.OFFSET) @Stable public int hsailNeverRanArrayOffset; @HotSpotVMField(name = "Hsail::HSAILDeoptimizationInfo::_deopt_next_index", type = "jint", get = HotSpotVMField.Type.OFFSET) @Stable public int hsailDeoptNextIndexOffset; @@ -1035,7 +1033,10 @@ @HotSpotVMField(name = "HSAILFrame::_pc_offset", type = "jint", get = HotSpotVMField.Type.OFFSET) @Stable public int hsailFramePcOffset; @HotSpotVMField(name = "HSAILFrame::_num_s_regs", type = "jbyte", get = HotSpotVMField.Type.OFFSET) @Stable public int hsailFrameNumSRegOffset; - @HotSpotVMField(name = "HSAILFrame::_save_area[0]", type = "jlong", get = HotSpotVMField.Type.OFFSET) @Stable public int hsailFrameSaveAreaOffset; + @HotSpotVMField(name = "HSAILFrame::_num_d_regs", type = "jbyte", get = HotSpotVMField.Type.OFFSET) @Stable public int hsailFrameNumDRegOffset; + @HotSpotVMConstant(name = "sizeof(HSAILFrame)") @Stable public int hsailFrameSaveAreaOffset; + @HotSpotVMConstant(name = "sizeof(Hsail::HSAILKernelDeoptimization)") @Stable public int hsailKernelDeoptimizationHeaderSize; + @HotSpotVMField(name = "Hsail::HSAILDeoptimizationInfo::_deopt_save_states[0]", type = "Hsail::HSAILKernelDeoptimization", get = HotSpotVMField.Type.OFFSET) @Stable public int hsailSaveStatesOffset0; /** * Mark word right shift to get identity hash code. diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotMethodHandleAccessProvider.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotMethodHandleAccessProvider.java Sat May 03 21:46:35 2014 +0200 @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2014, 2014, 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.graal.hotspot.meta; + +import static com.oracle.graal.hotspot.HotSpotGraalRuntime.*; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.api.replacements.*; +import com.oracle.graal.compiler.common.*; +import com.oracle.graal.hotspot.*; + +public class HotSpotMethodHandleAccessProvider implements MethodHandleAccessProvider { + + /** + * Lazy initialization to break class initialization cycle. Field and method lookup is only + * possible after the {@link HotSpotGraalRuntime} is fully initialized. + */ + static class LazyInitialization { + static final ResolvedJavaField methodHandleFormField; + static final ResolvedJavaField lambdaFormVmentryField; + static final ResolvedJavaMethod lambdaFormCompileToBytecodeMethod; + static final ResolvedJavaField memberNameVmtargetField; + + /** + * Search for an instance field with the given name in a class. + * + * @param className name of the class to search in + * @param fieldName name of the field to be searched + * @return resolved java field + * @throws ClassNotFoundException + */ + private static ResolvedJavaField findFieldInClass(String className, String fieldName) throws ClassNotFoundException { + Class clazz = Class.forName(className); + ResolvedJavaType type = HotSpotResolvedObjectType.fromClass(clazz); + ResolvedJavaField[] fields = type.getInstanceFields(false); + for (ResolvedJavaField field : fields) { + if (field.getName().equals(fieldName)) { + return field; + } + } + return null; + } + + private static ResolvedJavaMethod findMethodInClass(String className, String methodName) throws ClassNotFoundException { + Class clazz = Class.forName(className); + ResolvedJavaType type = HotSpotResolvedObjectType.fromClass(clazz); + ResolvedJavaMethod result = null; + for (ResolvedJavaMethod method : type.getDeclaredMethods()) { + if (method.getName().equals(methodName)) { + assert result == null : "more than one method found: " + className + "." + methodName; + result = method; + } + } + assert result != null : "method not found: " + className + "." + methodName; + return result; + } + + static { + try { + methodHandleFormField = findFieldInClass("java.lang.invoke.MethodHandle", "form"); + lambdaFormVmentryField = findFieldInClass("java.lang.invoke.LambdaForm", "vmentry"); + lambdaFormCompileToBytecodeMethod = findMethodInClass("java.lang.invoke.LambdaForm", "compileToBytecode"); + memberNameVmtargetField = findFieldInClass("java.lang.invoke.MemberName", "vmtarget"); + } catch (Throwable ex) { + throw GraalInternalError.shouldNotReachHere(); + } + } + } + + @Override + public IntrinsicMethod lookupMethodHandleIntrinsic(ResolvedJavaMethod method) { + int intrinsicId = ((HotSpotResolvedJavaMethod) method).intrinsicId(); + if (intrinsicId != 0) { + HotSpotVMConfig config = runtime().getConfig(); + if (intrinsicId == config.vmIntrinsicInvokeBasic) { + return IntrinsicMethod.INVOKE_BASIC; + } else if (intrinsicId == config.vmIntrinsicLinkToInterface) { + return IntrinsicMethod.LINK_TO_INTERFACE; + } else if (intrinsicId == config.vmIntrinsicLinkToSpecial) { + return IntrinsicMethod.LINK_TO_SPECIAL; + } else if (intrinsicId == config.vmIntrinsicLinkToStatic) { + return IntrinsicMethod.LINK_TO_STATIC; + } else if (intrinsicId == config.vmIntrinsicLinkToVirtual) { + return IntrinsicMethod.LINK_TO_VIRTUAL; + } + } + return null; + } + + @Override + public ResolvedJavaMethod resolveInvokeBasicTarget(Constant methodHandle, boolean forceBytecodeGeneration) { + if (methodHandle.isNull()) { + return null; + } + + /* Load non-public field: LambdaForm MethodHandle.form */ + Constant lambdaForm = LazyInitialization.methodHandleFormField.readValue(methodHandle); + if (lambdaForm.isNull()) { + return null; + } + + Constant memberName; + if (forceBytecodeGeneration) { + /* Invoke non-public method: MemberName LambdaForm.compileToBytecode() */ + memberName = LazyInitialization.lambdaFormCompileToBytecodeMethod.invoke(lambdaForm, new Constant[0]); + } else { + /* Load non-public field: MemberName LambdaForm.vmentry */ + memberName = LazyInitialization.lambdaFormVmentryField.readValue(lambdaForm); + } + return getTargetMethod(memberName); + } + + @Override + public ResolvedJavaMethod resolveLinkToTarget(Constant memberName) { + return getTargetMethod(memberName); + } + + /** + * Returns the {@link ResolvedJavaMethod} for the vmtarget of a java.lang.invoke.MemberName. + */ + private static ResolvedJavaMethod getTargetMethod(Constant memberName) { + if (memberName.isNull()) { + return null; + } + + /* Load injected field: JVM_Method* MemberName.vmtarget */ + Constant vmtarget = LazyInitialization.memberNameVmtargetField.readValue(memberName); + /* Create a method from the vmtarget method pointer. */ + return HotSpotResolvedJavaMethod.fromMetaspace(vmtarget.asLong()); + } +} diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotNmethod.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotNmethod.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotNmethod.java Sat May 03 21:46:35 2014 +0200 @@ -36,7 +36,7 @@ * instance to be anything but weak. This is due to the fact that HotSpot does not treat nmethods as * strong GC roots. */ -public final class HotSpotNmethod extends HotSpotInstalledCode { +public class HotSpotNmethod extends HotSpotInstalledCode { /** * This (indirect) Method* reference is safe since class redefinition preserves all methods diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotProviders.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotProviders.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotProviders.java Sat May 03 21:46:35 2014 +0200 @@ -38,15 +38,17 @@ private final SuitesProvider suites; private final HotSpotRegistersProvider registers; private final SnippetReflectionProvider snippetReflection; + private final HotSpotMethodHandleAccessProvider methodHandleAccess; public HotSpotProviders(HotSpotMetaAccessProvider metaAccess, HotSpotCodeCacheProvider codeCache, ConstantReflectionProvider constantReflection, HotSpotForeignCallsProvider foreignCalls, LoweringProvider lowerer, Replacements replacements, HotSpotDisassemblerProvider disassembler, SuitesProvider suites, HotSpotRegistersProvider registers, - SnippetReflectionProvider snippetReflection) { + SnippetReflectionProvider snippetReflection, HotSpotMethodHandleAccessProvider methodHandleAccess) { super(metaAccess, codeCache, constantReflection, foreignCalls, lowerer, replacements); this.disassembler = disassembler; this.suites = suites; this.registers = registers; this.snippetReflection = snippetReflection; + this.methodHandleAccess = methodHandleAccess; } @Override @@ -79,4 +81,8 @@ public SnippetReflectionProvider getSnippetReflection() { return snippetReflection; } + + public HotSpotMethodHandleAccessProvider getMethodHandleAccess() { + return methodHandleAccess; + } } diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/phases/AheadOfTimeVerificationPhase.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/phases/AheadOfTimeVerificationPhase.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/phases/AheadOfTimeVerificationPhase.java Sat May 03 21:46:35 2014 +0200 @@ -46,7 +46,7 @@ protected boolean verify(StructuredGraph graph, PhaseContext context) { for (ConstantNode node : getConstantNodes(graph)) { if (node.recordsUsages() || !node.gatherUsages(graph).isEmpty()) { - if (isObject(node) && !isNullReference(node) && !isInternedString(node) && !isDirectMethodHandle(node)) { + if (isObject(node) && !isNullReference(node) && !isInternedString(node) && !isDirectMethodHandle(node) && !isBoundMethodHandle(node)) { throw new VerificationError("illegal object constant: " + node); } } @@ -69,6 +69,13 @@ return "Ljava/lang/invoke/DirectMethodHandle;".equals(StampTool.typeOrNull(node).getName()); } + private static boolean isBoundMethodHandle(ConstantNode node) { + if (!isObject(node)) { + return false; + } + return StampTool.typeOrNull(node).getName().startsWith("Ljava/lang/invoke/BoundMethodHandle"); + } + @SuppressFBWarnings(value = "ES_COMPARING_STRINGS_WITH_EQ", justification = "reference equality is what we want") private static boolean isInternedString(ConstantNode node) { if (!isObject(node)) { diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/AbstractMethodHandleNode.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/AbstractMethodHandleNode.java Fri May 02 02:45:26 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,283 +0,0 @@ -/* - * Copyright (c) 2013, 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.graal.hotspot.replacements; - -import java.util.*; - -import com.oracle.graal.api.meta.*; -import com.oracle.graal.compiler.common.*; -import com.oracle.graal.compiler.common.type.*; -import com.oracle.graal.graph.*; -import com.oracle.graal.graph.spi.*; -import com.oracle.graal.hotspot.meta.*; -import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.java.*; -import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind; -import com.oracle.graal.nodes.type.*; -import com.oracle.graal.replacements.nodes.*; - -/** - * Common base class for method handle invoke nodes. - */ -public abstract class AbstractMethodHandleNode extends MacroNode implements Canonicalizable { - - private static final ResolvedJavaField methodHandleFormField; - private static final ResolvedJavaField lambdaFormVmentryField; - private static final ResolvedJavaField memberNameClazzField; - private static final ResolvedJavaField memberNameVmtargetField; - - // Replacement method data - private ResolvedJavaMethod replacementTargetMethod; - private JavaType replacementReturnType; - @Input private final NodeInputList replacementArguments; - - /** - * Search for an instance field with the given name in a class. - * - * @param className name of the class to search in - * @param fieldName name of the field to be searched - * @return resolved java field - * @throws ClassNotFoundException - */ - private static ResolvedJavaField findFieldInClass(String className, String fieldName) throws ClassNotFoundException { - Class clazz = Class.forName(className); - ResolvedJavaType type = HotSpotResolvedObjectType.fromClass(clazz); - ResolvedJavaField[] fields = type.getInstanceFields(false); - for (ResolvedJavaField field : fields) { - if (field.getName().equals(fieldName)) { - return field; - } - } - return null; - } - - static { - try { - methodHandleFormField = findFieldInClass("java.lang.invoke.MethodHandle", "form"); - lambdaFormVmentryField = findFieldInClass("java.lang.invoke.LambdaForm", "vmentry"); - memberNameClazzField = findFieldInClass("java.lang.invoke.MemberName", "clazz"); - memberNameVmtargetField = findFieldInClass("java.lang.invoke.MemberName", "vmtarget"); - } catch (ClassNotFoundException | SecurityException ex) { - throw GraalInternalError.shouldNotReachHere(); - } - } - - public AbstractMethodHandleNode(Invoke invoke) { - super(invoke); - - // See if we need to save some replacement method data. - CallTargetNode callTarget = invoke.callTarget(); - if (callTarget instanceof SelfReplacingMethodCallTargetNode) { - SelfReplacingMethodCallTargetNode selfReplacingMethodCallTargetNode = (SelfReplacingMethodCallTargetNode) callTarget; - replacementTargetMethod = selfReplacingMethodCallTargetNode.replacementTargetMethod(); - replacementReturnType = selfReplacingMethodCallTargetNode.replacementReturnType(); - replacementArguments = selfReplacingMethodCallTargetNode.replacementArguments(); - } else { - // NodeInputList can't be null. - replacementArguments = new NodeInputList<>(this); - } - } - - /** - * Get the receiver of a MethodHandle.invokeBasic call. - * - * @return the receiver argument node - */ - private ValueNode getReceiver() { - return arguments.first(); - } - - /** - * Get the MemberName argument of a MethodHandle.linkTo* call. - * - * @return the MemberName argument node (which is the last argument) - */ - private ValueNode getMemberName() { - return arguments.last(); - } - - /** - * Used from {@link MethodHandleInvokeBasicNode} to get the target {@link InvokeNode} if the - * method handle receiver is constant. - * - * @return invoke node for the {@link java.lang.invoke.MethodHandle} target - */ - protected InvokeNode getInvokeBasicTarget() { - ValueNode methodHandleNode = getReceiver(); - if (methodHandleNode.isConstant() && !methodHandleNode.isNullConstant()) { - // Get the data we need from MethodHandle.LambdaForm.MemberName - Constant methodHandle = methodHandleNode.asConstant(); - Constant lambdaForm = methodHandleFormField.readValue(methodHandle); - Constant memberName = lambdaFormVmentryField.readValue(lambdaForm); - return getTargetInvokeNode(memberName); - } - return null; - } - - /** - * Used from {@link MethodHandleLinkToStaticNode}, {@link MethodHandleLinkToSpecialNode}, - * {@link MethodHandleLinkToVirtualNode}, and {@link MethodHandleLinkToInterfaceNode} to get the - * target {@link InvokeNode} if the member name argument is constant. - * - * @return invoke node for the member name target - */ - protected InvokeNode getLinkToTarget() { - ValueNode memberNameNode = getMemberName(); - if (memberNameNode.isConstant() && !memberNameNode.isNullConstant()) { - Constant memberName = memberNameNode.asConstant(); - return getTargetInvokeNode(memberName); - } - return null; - } - - /** - * Helper function to get the {@link InvokeNode} for the vmtarget of a - * java.lang.invoke.MemberName. - * - * @param memberName constant member name node - * @return invoke node for the member name target - */ - private InvokeNode getTargetInvokeNode(Constant memberName) { - // Get the data we need from MemberName - Constant clazz = memberNameClazzField.readValue(memberName); - Constant vmtarget = memberNameVmtargetField.readValue(memberName); - - // Create a method from the vmtarget pointer - Class c = (Class) HotSpotObjectConstant.asObject(clazz); - HotSpotResolvedObjectType holderClass = (HotSpotResolvedObjectType) HotSpotResolvedObjectType.fromClass(c); - HotSpotResolvedJavaMethod targetMethod = HotSpotResolvedJavaMethod.fromMetaspace(vmtarget.asLong()); - - // In lambda forms we erase signature types to avoid resolving issues - // involving class loaders. When we optimize a method handle invoke - // to a direct call we must cast the receiver and arguments to its - // actual types. - HotSpotSignature signature = targetMethod.getSignature(); - final boolean isStatic = targetMethod.isStatic(); - final int receiverSkip = isStatic ? 0 : 1; - - // Cast receiver to its type. - if (!isStatic) { - JavaType receiverType = holderClass; - maybeCastArgument(0, receiverType); - } - - // Cast reference arguments to its type. - for (int index = 0; index < signature.getParameterCount(false); index++) { - JavaType parameterType = signature.getParameterType(index, holderClass); - maybeCastArgument(receiverSkip + index, parameterType); - } - - // Try to get the most accurate receiver type - if (this instanceof MethodHandleLinkToVirtualNode || this instanceof MethodHandleLinkToInterfaceNode) { - ResolvedJavaType receiverType = StampTool.typeOrNull(getReceiver().stamp()); - if (receiverType != null) { - ResolvedJavaMethod concreteMethod = receiverType.findUniqueConcreteMethod(targetMethod); - if (concreteMethod != null) { - return createTargetInvokeNode(concreteMethod); - } - } - } - - if (targetMethod.canBeStaticallyBound()) { - return createTargetInvokeNode(targetMethod); - } - - ResolvedJavaMethod concreteMethod = targetMethod.uniqueConcreteMethod(); - if (concreteMethod != null) { - return createTargetInvokeNode(concreteMethod); - } - - return null; - } - - /** - * Inserts a node to cast the argument at index to the given type if the given type is more - * concrete than the argument type. - * - * @param index of the argument to be cast - * @param type the type the argument should be cast to - */ - private void maybeCastArgument(int index, JavaType type) { - if (type instanceof ResolvedJavaType) { - ResolvedJavaType targetType = (ResolvedJavaType) type; - if (!targetType.isPrimitive()) { - ValueNode argument = arguments.get(index); - ResolvedJavaType argumentType = StampTool.typeOrNull(argument.stamp()); - if (argumentType == null || (argumentType.isAssignableFrom(targetType) && !argumentType.equals(targetType))) { - PiNode piNode = graph().unique(new PiNode(argument, StampFactory.declared(targetType))); - arguments.set(index, piNode); - } - } - } - } - - /** - * Creates an {@link InvokeNode} for the given target method. The {@link CallTargetNode} passed - * to the InvokeNode is in fact a {@link SelfReplacingMethodCallTargetNode}. - * - * @param targetMethod the method the be called - * @return invoke node for the member name target - */ - private InvokeNode createTargetInvokeNode(ResolvedJavaMethod targetMethod) { - InvokeKind invokeKind = targetMethod.isStatic() ? InvokeKind.Static : InvokeKind.Special; - JavaType returnType = targetMethod.getSignature().getReturnType(null); - - // MethodHandleLinkTo* nodes have a trailing MemberName argument which - // needs to be popped. - ValueNode[] originalArguments = arguments.toArray(new ValueNode[arguments.size()]); - ValueNode[] targetArguments; - if (this instanceof MethodHandleInvokeBasicNode) { - targetArguments = originalArguments; - } else { - assert this instanceof MethodHandleLinkToStaticNode || this instanceof MethodHandleLinkToSpecialNode || this instanceof MethodHandleLinkToVirtualNode || - this instanceof MethodHandleLinkToInterfaceNode : this; - targetArguments = Arrays.copyOfRange(originalArguments, 0, arguments.size() - 1); - } - - // If there is already replacement information, use that instead. - MethodCallTargetNode callTarget; - if (replacementTargetMethod == null) { - callTarget = new SelfReplacingMethodCallTargetNode(invokeKind, targetMethod, targetArguments, returnType, getTargetMethod(), originalArguments, getReturnType()); - } else { - ValueNode[] args = replacementArguments.toArray(new ValueNode[replacementArguments.size()]); - callTarget = new SelfReplacingMethodCallTargetNode(invokeKind, targetMethod, targetArguments, returnType, replacementTargetMethod, args, replacementReturnType); - } - graph().add(callTarget); - - // The call target can have a different return type than the invoker, - // e.g. the target returns an Object but the invoker void. In this case - // we need to use the stamp of the invoker. Note: always using the - // invoker's stamp would be wrong because it's a less concrete type - // (usually java.lang.Object). - InvokeNode invoke; - if (stamp() == StampFactory.forVoid()) { - invoke = new InvokeNode(callTarget, getBci(), stamp()); - } else { - invoke = new InvokeNode(callTarget, getBci()); - } - graph().add(invoke); - invoke.setStateAfter(stateAfter()); - return invoke; - } - -} diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleInvokeBasicNode.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleInvokeBasicNode.java Fri May 02 02:45:26 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2013, 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.graal.hotspot.replacements; - -import java.lang.invoke.*; - -import com.oracle.graal.graph.*; -import com.oracle.graal.graph.spi.*; -import com.oracle.graal.nodes.*; - -/** - * Macro node for {@link MethodHandle}{@code .invokeBasic(Object...)}. - */ -public class MethodHandleInvokeBasicNode extends AbstractMethodHandleNode { - - public MethodHandleInvokeBasicNode(Invoke invoke) { - super(invoke); - } - - @Override - public Node canonical(CanonicalizerTool tool) { - InvokeNode invoke = getInvokeBasicTarget(); - if (invoke != null) { - return invoke; - } - return this; - } - -} diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleLinkToInterfaceNode.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleLinkToInterfaceNode.java Fri May 02 02:45:26 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2013, 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.graal.hotspot.replacements; - -import java.lang.invoke.*; - -import com.oracle.graal.graph.*; -import com.oracle.graal.graph.spi.*; -import com.oracle.graal.nodes.*; - -/** - * Macro node for {@link MethodHandle}{@code .linkToInterface(Object...)}. - */ -public class MethodHandleLinkToInterfaceNode extends AbstractMethodHandleNode { - - public MethodHandleLinkToInterfaceNode(Invoke invoke) { - super(invoke); - } - - @Override - public Node canonical(CanonicalizerTool tool) { - InvokeNode invoke = getLinkToTarget(); - if (invoke != null) { - return invoke; - } - return this; - } - -} diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleLinkToSpecialNode.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleLinkToSpecialNode.java Fri May 02 02:45:26 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2013, 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.graal.hotspot.replacements; - -import java.lang.invoke.*; - -import com.oracle.graal.graph.*; -import com.oracle.graal.graph.spi.*; -import com.oracle.graal.nodes.*; - -/** - * Macro node for {@link MethodHandle}{@code .linkToSpecial(Object...)}. - */ -public class MethodHandleLinkToSpecialNode extends AbstractMethodHandleNode { - - public MethodHandleLinkToSpecialNode(Invoke invoke) { - super(invoke); - } - - @Override - public Node canonical(CanonicalizerTool tool) { - InvokeNode invoke = getLinkToTarget(); - if (invoke != null) { - return invoke; - } - return this; - } - -} diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleLinkToStaticNode.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleLinkToStaticNode.java Fri May 02 02:45:26 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2013, 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.graal.hotspot.replacements; - -import java.lang.invoke.*; - -import com.oracle.graal.graph.*; -import com.oracle.graal.graph.spi.*; -import com.oracle.graal.nodes.*; - -/** - * Macro node for {@link MethodHandle}{@code .linkToStatic(Object...)}. - */ -public class MethodHandleLinkToStaticNode extends AbstractMethodHandleNode { - - public MethodHandleLinkToStaticNode(Invoke invoke) { - super(invoke); - } - - @Override - public Node canonical(CanonicalizerTool tool) { - InvokeNode invoke = getLinkToTarget(); - if (invoke != null) { - return invoke; - } - return this; - } - -} diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleLinkToVirtualNode.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleLinkToVirtualNode.java Fri May 02 02:45:26 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2013, 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.graal.hotspot.replacements; - -import java.lang.invoke.*; - -import com.oracle.graal.graph.*; -import com.oracle.graal.graph.spi.*; -import com.oracle.graal.nodes.*; - -/** - * Macro node for {@link MethodHandle}{@code .linkToVirtual(Object...)}. - */ -public class MethodHandleLinkToVirtualNode extends AbstractMethodHandleNode { - - public MethodHandleLinkToVirtualNode(Invoke invoke) { - super(invoke); - } - - @Override - public Node canonical(CanonicalizerTool tool) { - InvokeNode invoke = getLinkToTarget(); - if (invoke != null) { - return invoke; - } - return this; - } - -} diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleNode.java Sat May 03 21:46:35 2014 +0200 @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2013, 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.graal.hotspot.replacements; + +import static com.oracle.graal.hotspot.HotSpotGraalRuntime.*; + +import java.lang.invoke.*; +import java.util.*; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.api.replacements.*; +import com.oracle.graal.api.replacements.MethodHandleAccessProvider.IntrinsicMethod; +import com.oracle.graal.compiler.common.*; +import com.oracle.graal.compiler.common.type.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.graph.spi.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.java.*; +import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind; +import com.oracle.graal.nodes.type.*; +import com.oracle.graal.replacements.nodes.*; + +/** + * Node for invocation methods defined on the class {@link MethodHandle}. + */ +public class MethodHandleNode extends MacroNode implements Canonicalizable { + + /** The method that this node is representing. */ + private final IntrinsicMethod intrinsicMethod; + + // Replacement method data + private ResolvedJavaMethod replacementTargetMethod; + private JavaType replacementReturnType; + @Input private final NodeInputList replacementArguments; + + public MethodHandleNode(Invoke invoke) { + super(invoke); + + MethodCallTargetNode callTarget = (MethodCallTargetNode) invoke.callTarget(); + intrinsicMethod = lookupMethodHandleIntrinsic(callTarget.targetMethod()); + assert intrinsicMethod != null; + + // See if we need to save some replacement method data. + if (callTarget instanceof SelfReplacingMethodCallTargetNode) { + SelfReplacingMethodCallTargetNode selfReplacingMethodCallTargetNode = (SelfReplacingMethodCallTargetNode) callTarget; + replacementTargetMethod = selfReplacingMethodCallTargetNode.replacementTargetMethod(); + replacementReturnType = selfReplacingMethodCallTargetNode.replacementReturnType(); + replacementArguments = selfReplacingMethodCallTargetNode.replacementArguments(); + } else { + // NodeInputList can't be null. + replacementArguments = new NodeInputList<>(this); + } + } + + /** + * Returns the method handle method intrinsic identifier for the provided method, or + * {@code null} if the method is not a method that can be handled by this class. + */ + public static IntrinsicMethod lookupMethodHandleIntrinsic(ResolvedJavaMethod method) { + return methodHandleAccess().lookupMethodHandleIntrinsic(method); + } + + @Override + public Node canonical(CanonicalizerTool tool) { + InvokeNode invoke; + switch (intrinsicMethod) { + case INVOKE_BASIC: + invoke = getInvokeBasicTarget(); + break; + case LINK_TO_STATIC: + case LINK_TO_SPECIAL: + case LINK_TO_VIRTUAL: + case LINK_TO_INTERFACE: + invoke = getLinkToTarget(); + break; + default: + throw GraalInternalError.shouldNotReachHere(); + } + if (invoke != null) { + return invoke; + } + return this; + } + + /** + * Get the receiver of a MethodHandle.invokeBasic call. + * + * @return the receiver argument node + */ + private ValueNode getReceiver() { + return arguments.first(); + } + + /** + * Get the MemberName argument of a MethodHandle.linkTo* call. + * + * @return the MemberName argument node (which is the last argument) + */ + private ValueNode getMemberName() { + return arguments.last(); + } + + /** + * Returns the {@link MethodHandleAccessProvider} that provides introspection of internal + * {@link MethodHandle} data. + */ + private static MethodHandleAccessProvider methodHandleAccess() { + return runtime().getHostProviders().getMethodHandleAccess(); + } + + /** + * Used for the MethodHandle.invokeBasic method (the {@link IntrinsicMethod#INVOKE_BASIC } + * method) to get the target {@link InvokeNode} if the method handle receiver is constant. + * + * @return invoke node for the {@link java.lang.invoke.MethodHandle} target + */ + protected InvokeNode getInvokeBasicTarget() { + ValueNode methodHandleNode = getReceiver(); + if (methodHandleNode.isConstant()) { + return getTargetInvokeNode(methodHandleAccess().resolveInvokeBasicTarget(methodHandleNode.asConstant(), false)); + } + return null; + } + + /** + * Used for the MethodHandle.linkTo* methods (the {@link IntrinsicMethod#LINK_TO_STATIC}, + * {@link IntrinsicMethod#LINK_TO_SPECIAL}, {@link IntrinsicMethod#LINK_TO_VIRTUAL}, and + * {@link IntrinsicMethod#LINK_TO_INTERFACE} methods) to get the target {@link InvokeNode} if + * the member name argument is constant. + * + * @return invoke node for the member name target + */ + protected InvokeNode getLinkToTarget() { + ValueNode memberNameNode = getMemberName(); + if (memberNameNode.isConstant()) { + return getTargetInvokeNode(methodHandleAccess().resolveLinkToTarget(memberNameNode.asConstant())); + } + return null; + } + + /** + * Helper function to get the {@link InvokeNode} for the targetMethod of a + * java.lang.invoke.MemberName. + * + * @param targetMethod the target, already loaded from the member name node + * @return invoke node for the member name target + */ + private InvokeNode getTargetInvokeNode(ResolvedJavaMethod targetMethod) { + if (targetMethod == null) { + return null; + } + + // In lambda forms we erase signature types to avoid resolving issues + // involving class loaders. When we optimize a method handle invoke + // to a direct call we must cast the receiver and arguments to its + // actual types. + Signature signature = targetMethod.getSignature(); + final boolean isStatic = targetMethod.isStatic(); + final int receiverSkip = isStatic ? 0 : 1; + + // Cast receiver to its type. + if (!isStatic) { + JavaType receiverType = targetMethod.getDeclaringClass(); + maybeCastArgument(0, receiverType); + } + + // Cast reference arguments to its type. + for (int index = 0; index < signature.getParameterCount(false); index++) { + JavaType parameterType = signature.getParameterType(index, targetMethod.getDeclaringClass()); + maybeCastArgument(receiverSkip + index, parameterType); + } + + // Try to get the most accurate receiver type + if (intrinsicMethod == IntrinsicMethod.LINK_TO_VIRTUAL || intrinsicMethod == IntrinsicMethod.LINK_TO_INTERFACE) { + ResolvedJavaType receiverType = StampTool.typeOrNull(getReceiver().stamp()); + if (receiverType != null) { + ResolvedJavaMethod concreteMethod = receiverType.findUniqueConcreteMethod(targetMethod); + if (concreteMethod != null) { + return createTargetInvokeNode(concreteMethod); + } + } + } + + if (targetMethod.canBeStaticallyBound()) { + return createTargetInvokeNode(targetMethod); + } + + ResolvedJavaMethod concreteMethod = targetMethod.getDeclaringClass().findUniqueConcreteMethod(targetMethod); + if (concreteMethod != null) { + return createTargetInvokeNode(concreteMethod); + } + + return null; + } + + /** + * Inserts a node to cast the argument at index to the given type if the given type is more + * concrete than the argument type. + * + * @param index of the argument to be cast + * @param type the type the argument should be cast to + */ + private void maybeCastArgument(int index, JavaType type) { + if (type instanceof ResolvedJavaType) { + ResolvedJavaType targetType = (ResolvedJavaType) type; + if (!targetType.isPrimitive()) { + ValueNode argument = arguments.get(index); + ResolvedJavaType argumentType = StampTool.typeOrNull(argument.stamp()); + if (argumentType == null || (argumentType.isAssignableFrom(targetType) && !argumentType.equals(targetType))) { + PiNode piNode = graph().unique(new PiNode(argument, StampFactory.declared(targetType))); + arguments.set(index, piNode); + } + } + } + } + + /** + * Creates an {@link InvokeNode} for the given target method. The {@link CallTargetNode} passed + * to the InvokeNode is in fact a {@link SelfReplacingMethodCallTargetNode}. + * + * @param targetMethod the method the be called + * @return invoke node for the member name target + */ + private InvokeNode createTargetInvokeNode(ResolvedJavaMethod targetMethod) { + InvokeKind invokeKind = targetMethod.isStatic() ? InvokeKind.Static : InvokeKind.Special; + JavaType returnType = targetMethod.getSignature().getReturnType(null); + + // MethodHandleLinkTo* nodes have a trailing MemberName argument which + // needs to be popped. + ValueNode[] originalArguments = arguments.toArray(new ValueNode[arguments.size()]); + ValueNode[] targetArguments; + switch (intrinsicMethod) { + case INVOKE_BASIC: + targetArguments = originalArguments; + break; + case LINK_TO_STATIC: + case LINK_TO_SPECIAL: + case LINK_TO_VIRTUAL: + case LINK_TO_INTERFACE: + targetArguments = Arrays.copyOfRange(originalArguments, 0, arguments.size() - 1); + break; + default: + throw GraalInternalError.shouldNotReachHere(); + } + + // If there is already replacement information, use that instead. + MethodCallTargetNode callTarget; + if (replacementTargetMethod == null) { + callTarget = new SelfReplacingMethodCallTargetNode(invokeKind, targetMethod, targetArguments, returnType, getTargetMethod(), originalArguments, getReturnType()); + } else { + ValueNode[] args = replacementArguments.toArray(new ValueNode[replacementArguments.size()]); + callTarget = new SelfReplacingMethodCallTargetNode(invokeKind, targetMethod, targetArguments, returnType, replacementTargetMethod, args, replacementReturnType); + } + graph().add(callTarget); + + // The call target can have a different return type than the invoker, + // e.g. the target returns an Object but the invoker void. In this case + // we need to use the stamp of the invoker. Note: always using the + // invoker's stamp would be wrong because it's a less concrete type + // (usually java.lang.Object). + InvokeNode invoke; + if (stamp() == StampFactory.forVoid()) { + invoke = new InvokeNode(callTarget, getBci(), stamp()); + } else { + invoke = new InvokeNode(callTarget, getBci()); + } + graph().add(invoke); + invoke.setStateAfter(stateAfter()); + return invoke; + } + +} diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MonitorSnippets.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MonitorSnippets.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MonitorSnippets.java Sat May 03 21:46:35 2014 +0200 @@ -47,7 +47,7 @@ import com.oracle.graal.nodes.spi.*; import com.oracle.graal.nodes.type.*; import com.oracle.graal.options.*; -import com.oracle.graal.phases.common.*; +import com.oracle.graal.phases.common.inlining.*; import com.oracle.graal.replacements.*; import com.oracle.graal.replacements.Snippet.ConstantParameter; import com.oracle.graal.replacements.Snippet.Fold; diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.hsail/src/com/oracle/graal/hsail/HSAIL.java --- a/graal/com.oracle.graal.hsail/src/com/oracle/graal/hsail/HSAIL.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.hsail/src/com/oracle/graal/hsail/HSAIL.java Sat May 03 21:46:35 2014 +0200 @@ -136,7 +136,6 @@ public static final Register threadRegister = d20; public static final Register actionAndReasonReg = s32; public static final Register codeBufferOffsetReg = s33; - public static final Register dregOopMapReg = s39; // @formatter:off public static final Register[] cRegisters = { @@ -178,10 +177,16 @@ return -(((StackSlot) reg).getRawOffset()); } - public static String mapStackSlot(Value reg) { - StackSlot s = (StackSlot) reg; - long offset = -s.getRawOffset(); - return "[%spillseg]" + "[" + offset + "]"; + /** + * The mapping to stack slots is always relative to the beginning of the spillseg. + * {@link #getStackOffset(Value)} returns the positive version of the originally negative + * offset. Then we back up from that by {@code argSize} in bytes. This ensures that slots of + * different size do not overlap, even though we have converted from negative to positive + * offsets. + */ + public static int getStackOffsetStart(Value reg, int argSize) { + int argSizeInBytes = argSize / 8; + return getStackOffset(reg) - argSizeInBytes; } public static String mapRegister(Value arg) { diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.java/src/com/oracle/graal/java/BciBlockMapping.java --- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/BciBlockMapping.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/BciBlockMapping.java Sat May 03 21:46:35 2014 +0200 @@ -177,6 +177,10 @@ public BciBlock getPredecessor(int index) { return predecessors.get(index); } + + public double probability() { + return 1D; + } } public static class ExceptionDispatchBlock extends BciBlock { diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.java/src/com/oracle/graal/java/ComputeLoopFrequenciesClosure.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/ComputeLoopFrequenciesClosure.java Sat May 03 21:46:35 2014 +0200 @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2011, 2014, 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.graal.java; + +import java.util.*; +import java.util.stream.*; + +import com.oracle.graal.nodes.*; +import com.oracle.graal.phases.graph.*; + +public class ComputeLoopFrequenciesClosure extends ReentrantNodeIterator.NodeIteratorClosure { + + private static final double EPSILON = Double.MIN_NORMAL; + private static final ComputeLoopFrequenciesClosure INSTANCE = new ComputeLoopFrequenciesClosure(); + + private ComputeLoopFrequenciesClosure() { + // nothing to do + } + + @Override + protected Double processNode(FixedNode node, Double currentState) { + // normal nodes never change the probability of a path + return currentState; + } + + @Override + protected Double merge(MergeNode merge, List states) { + // a merge has the sum of all predecessor probabilities + return states.stream().collect(Collectors.summingDouble(d -> d)); + } + + @Override + protected Double afterSplit(BeginNode node, Double oldState) { + // a control split splits up the probability + ControlSplitNode split = (ControlSplitNode) node.predecessor(); + return oldState * split.probability(node); + } + + @Override + protected Map processLoop(LoopBeginNode loop, Double initialState) { + Map exitStates = ReentrantNodeIterator.processLoop(this, loop, 1D).exitStates; + + double exitProbability = exitStates.values().stream().mapToDouble(d -> d).sum(); + assert exitProbability <= 1D && exitProbability >= 0D; + if (exitProbability < EPSILON) { + exitProbability = EPSILON; + } + double loopFrequency = 1D / exitProbability; + loop.setLoopFrequency(loopFrequency); + + double adjustmentFactor = initialState * loopFrequency; + exitStates.replaceAll((exitNode, probability) -> multiplySaturate(probability, adjustmentFactor)); + + return exitStates; + } + + /** + * Multiplies a and b and saturates the result to 1/{@link Double#MIN_NORMAL}. + * + * @return a times b saturated to 1/{@link Double#MIN_NORMAL} + */ + public static double multiplySaturate(double a, double b) { + double r = a * b; + if (r > 1 / Double.MIN_NORMAL) { + return 1 / Double.MIN_NORMAL; + } + return r; + } + + /** + * Computes the frequencies of all loops in the given graph. This is done by performing a + * reverse postorder iteration and computing the probability of all fixed nodes. The combined + * probability of all exits of a loop can be used to compute the loop's expected frequency. + */ + public static void compute(StructuredGraph graph) { + if (graph.hasLoops()) { + ReentrantNodeIterator.apply(INSTANCE, graph.start(), 1D); + } + } + +} diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java --- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java Sat May 03 21:46:35 2014 +0200 @@ -26,6 +26,7 @@ import static com.oracle.graal.api.meta.DeoptimizationReason.*; import static com.oracle.graal.bytecode.Bytecodes.*; import static com.oracle.graal.compiler.common.GraalOptions.*; + import java.util.*; import com.oracle.graal.api.code.*; @@ -158,6 +159,8 @@ filter.remove(); } parser = null; + + ComputeLoopFrequenciesClosure.compute(graph); } @Override diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILArithmetic.java --- a/graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILArithmetic.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILArithmetic.java Sat May 03 21:46:35 2014 +0200 @@ -108,7 +108,7 @@ private final String from; private final String to; @Def({REG}) protected AllocatableValue result; - @Use({REG, STACK}) protected AllocatableValue x; + @Use({REG}) protected AllocatableValue x; public ConvertOp(AllocatableValue result, AllocatableValue x, String to, String from) { this.from = from; @@ -123,52 +123,10 @@ } } - public static class Op1Stack extends HSAILLIRInstruction { - @Opcode private final HSAILArithmetic opcode; - @Def({REG, HINT}) protected Value result; - @Use({REG, STACK, CONST}) protected Value x; - - public Op1Stack(HSAILArithmetic opcode, Value result, Value x) { - this.opcode = opcode; - this.result = result; - this.x = x; - } - - @Override - public void emitCode(CompilationResultBuilder crb, HSAILAssembler masm) { - emit(crb, masm, opcode, result, x, null); - } - } - - public static class Op2Stack extends HSAILLIRInstruction { + public static class Op1Reg extends HSAILLIRInstruction { @Opcode private final HSAILArithmetic opcode; @Def({REG, HINT}) protected Value result; @Use({REG, CONST}) protected Value x; - @Alive({REG, CONST}) protected Value y; - - public Op2Stack(HSAILArithmetic opcode, Value result, Value x, Value y) { - this.opcode = opcode; - this.result = result; - this.x = x; - this.y = y; - } - - @Override - public void emitCode(CompilationResultBuilder crb, HSAILAssembler masm) { - emit(crb, masm, opcode, result, x, y, null); - } - - @Override - public void verify() { - super.verify(); - verifyKind(opcode, result, x, y); - } - } - - public static class Op1Reg extends HSAILLIRInstruction { - @Opcode private final HSAILArithmetic opcode; - @Def({REG, HINT}) protected Value result; - @Use({REG}) protected Value x; public Op1Reg(HSAILArithmetic opcode, Value result, Value x) { this.opcode = opcode; @@ -185,7 +143,7 @@ public static class Op2Reg extends HSAILLIRInstruction { @Opcode private final HSAILArithmetic opcode; @Def({REG, HINT}) protected Value result; - @Use({REG, STACK, CONST}) protected Value x; + @Use({REG, CONST}) protected Value x; @Alive({REG, CONST}) protected Value y; public Op2Reg(HSAILArithmetic opcode, Value result, Value x, Value y) { @@ -207,35 +165,10 @@ } } - public static class Op2RegCommutative extends HSAILLIRInstruction { - @Opcode private final HSAILArithmetic opcode; - @Def({REG, HINT}) protected Value result; - @Use({REG, STACK, CONST}) protected Value x; - @Use({REG, CONST}) protected Value y; - - public Op2RegCommutative(HSAILArithmetic opcode, Value result, Value x, Value y) { - this.opcode = opcode; - this.result = result; - this.x = x; - this.y = y; - } - - @Override - public void emitCode(CompilationResultBuilder crb, HSAILAssembler masm) { - throw GraalInternalError.shouldNotReachHere(); - } - - @Override - protected void verify() { - super.verify(); - verifyKind(opcode, result, x, y); - } - } - public static class ShiftOp extends HSAILLIRInstruction { @Opcode private final HSAILArithmetic opcode; @Def({REG, HINT}) protected Value result; - @Use({REG, STACK, CONST}) protected Value x; + @Use({REG, CONST}) protected Value x; @Alive({REG, CONST}) protected Value y; public ShiftOp(HSAILArithmetic opcode, Value result, Value x, Value y) { @@ -295,7 +228,7 @@ /** * Emits the HSAIL code for an arithmetic operation taking one input parameter. - * + * * @param crb the CompilationResultBuilder * @param masm the HSAIL assembler * @param opcode the opcode of the arithmetic operation diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILCompare.java --- a/graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILCompare.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILCompare.java Sat May 03 21:46:35 2014 +0200 @@ -50,8 +50,8 @@ public static class CompareOp extends HSAILLIRInstruction { @Opcode private final HSAILCompare opcode; - @Use({REG, STACK, CONST}) protected Value x; - @Use({REG, STACK, CONST}) protected Value y; + @Use({REG, CONST}) protected Value x; + @Use({REG, CONST}) protected Value y; @Def({REG}) protected Value z; private final Condition condition; public boolean unordered = false; diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILControlFlow.java --- a/graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILControlFlow.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILControlFlow.java Sat May 03 21:46:35 2014 +0200 @@ -146,7 +146,6 @@ protected MetaAccessProvider metaAccessProvider; protected String emitName; protected int codeBufferPos = -1; - protected int dregOopMap = 0; public DeoptimizeOp(Value actionAndReason, LIRFrameState frameState, String emitName, MetaAccessProvider metaAccessProvider) { super(Value.ILLEGAL); // return with no ret value @@ -181,20 +180,10 @@ masm.emitComment("/* HSAIL Deoptimization pos=" + codeBufferPos + ", bci=" + frameState.debugInfo().getBytecodePosition().getBCI() + ", frameState=" + frameState + " */"); - // get the bitmap of $d regs that contain references - ReferenceMap referenceMap = frameState.debugInfo().getReferenceMap(); - for (int dreg = HSAIL.d0.number; dreg <= HSAIL.d15.number; dreg++) { - if (referenceMap.getRegister(dreg) == Kind.Object) { - dregOopMap |= 1 << (dreg - HSAIL.d0.number); - } - } - AllocatableValue actionAndReasonReg = HSAIL.actionAndReasonReg.asValue(Kind.Int); AllocatableValue codeBufferOffsetReg = HSAIL.codeBufferOffsetReg.asValue(Kind.Int); - AllocatableValue dregOopMapReg = HSAIL.dregOopMapReg.asValue(Kind.Int); masm.emitMov(Kind.Int, actionAndReasonReg, actionAndReason); masm.emitMov(Kind.Int, codeBufferOffsetReg, Constant.forInt(codeBufferPos)); - masm.emitMov(Kind.Int, dregOopMapReg, Constant.forInt(dregOopMap)); masm.emitJumpToLabelName(masm.getDeoptLabelName()); // now record the debuginfo @@ -305,10 +294,10 @@ @Opcode protected final HSAILCompare opcode; @Def({REG, HINT}) protected Value result; - @Use({REG, STACK, CONST}) protected Value trueValue; - @Use({REG, STACK, CONST}) protected Value falseValue; - @Use({REG, STACK, CONST}) protected Value left; - @Use({REG, STACK, CONST}) protected Value right; + @Use({REG, CONST}) protected Value trueValue; + @Use({REG, CONST}) protected Value falseValue; + @Use({REG, CONST}) protected Value left; + @Use({REG, CONST}) protected Value right; protected final Condition condition; public CondMoveOp(HSAILCompare opcode, Variable left, Variable right, Variable result, Condition condition, Value trueValue, Value falseValue) { diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILMove.java --- a/graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILMove.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILMove.java Sat May 03 21:46:35 2014 +0200 @@ -63,29 +63,6 @@ } @Opcode("MOVE") - public static class SpillMoveOp extends AbstractMoveOp { - - @Def({REG, STACK}) protected AllocatableValue result; - @Use({REG, STACK, CONST}) protected Value input; - - public SpillMoveOp(Kind moveKind, AllocatableValue result, Value input) { - super(moveKind); - this.result = result; - this.input = input; - } - - @Override - public Value getInput() { - return input; - } - - @Override - public AllocatableValue getResult() { - return result; - } - } - - @Opcode("MOVE") public static class MoveToRegOp extends AbstractMoveOp { @Def({REG, HINT}) protected AllocatableValue result; diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.loop/src/com/oracle/graal/loop/LoopPolicies.java --- a/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/LoopPolicies.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/LoopPolicies.java Sat May 03 21:46:35 2014 +0200 @@ -24,11 +24,12 @@ import static com.oracle.graal.compiler.common.GraalOptions.*; +import java.util.function.*; + import com.oracle.graal.debug.*; import com.oracle.graal.graph.*; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.cfg.*; -import com.oracle.graal.nodes.util.*; public abstract class LoopPolicies { @@ -37,12 +38,12 @@ } // TODO (gd) change when inversion is available - public static boolean shouldPeel(LoopEx loop, NodesToDoubles probabilities) { + public static boolean shouldPeel(LoopEx loop, ToDoubleFunction probabilities) { if (loop.detectCounted()) { return false; } LoopBeginNode loopBegin = loop.loopBegin(); - double entryProbability = probabilities.get(loopBegin.forwardEnd()); + double entryProbability = probabilities.applyAsDouble(loopBegin.forwardEnd()); return entryProbability > MinimumPeelProbability.getValue() && loop.size() + loopBegin.graph().getNodeCount() < MaximumDesiredSize.getValue(); } @@ -70,10 +71,8 @@ double maxProbability = 0; for (Node successor : controlSplit.successors()) { BeginNode branch = (BeginNode) successor; - inBranchTotal += loop.nodesInLoopFrom(branch, postDom).cardinality(); // this may count - // twice because - // of fall-through - // in switches + // this may count twice because of fall-through in switches + inBranchTotal += loop.nodesInLoopFrom(branch, postDom).cardinality(); double probability = controlSplit.probability(branch); if (probability > maxProbability) { maxProbability = probability; diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.loop/src/com/oracle/graal/loop/phases/LoopTransformHighPhase.java --- a/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/phases/LoopTransformHighPhase.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/phases/LoopTransformHighPhase.java Sat May 03 21:46:35 2014 +0200 @@ -24,10 +24,11 @@ import static com.oracle.graal.compiler.common.GraalOptions.*; +import java.util.function.*; + import com.oracle.graal.debug.*; import com.oracle.graal.loop.*; import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.util.*; import com.oracle.graal.phases.*; import com.oracle.graal.phases.graph.*; @@ -37,7 +38,7 @@ protected void run(StructuredGraph graph) { if (graph.hasLoops()) { if (LoopPeeling.getValue()) { - NodesToDoubles probabilities = new ComputeProbabilityClosure(graph).apply(); + ToDoubleFunction probabilities = new FixedNodeProbabilityCache(); LoopsData data = new LoopsData(graph); for (LoopEx loop : data.outterFirst()) { if (LoopPolicies.shouldPeel(loop, probabilities)) { diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/BeginNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/BeginNode.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/BeginNode.java Sat May 03 21:46:35 2014 +0200 @@ -32,6 +32,7 @@ import com.oracle.graal.graph.spi.*; import com.oracle.graal.nodes.extended.*; import com.oracle.graal.nodes.spi.*; +import com.oracle.graal.nodes.util.*; @NodeInfo(allowedUsageTypes = {InputType.Guard, InputType.Anchor}) public class BeginNode extends FixedWithNextNode implements LIRLowerable, Simplifiable, GuardingNode, AnchoringNode, IterableNodeType { @@ -69,12 +70,8 @@ } public static BeginNode prevBegin(FixedNode from) { - Node prevBegin = from; - while (prevBegin != null) { - if (prevBegin instanceof BeginNode) { - return (BeginNode) prevBegin; - } - prevBegin = prevBegin.predecessor(); + for (BeginNode begin : GraphUtil.predecessorIterable(from).filter(BeginNode.class)) { + return begin; } return null; } diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/InvokeNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/InvokeNode.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/InvokeNode.java Sat May 03 21:46:35 2014 +0200 @@ -36,7 +36,7 @@ * The {@code InvokeNode} represents all kinds of method calls. */ @NodeInfo(nameTemplate = "Invoke#{p#targetMethod/s}", allowedUsageTypes = {InputType.Memory}) -public final class InvokeNode extends AbstractMemoryCheckpoint implements Invoke, LIRLowerable, MemoryCheckpoint.Single { +public final class InvokeNode extends AbstractMemoryCheckpoint implements Invoke, LIRLowerable, MemoryCheckpoint.Single, IterableNodeType { @Input(InputType.Extension) private CallTargetNode callTarget; @Input(InputType.State) private FrameState stateDuring; diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/MergeNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/MergeNode.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/MergeNode.java Sat May 03 21:46:35 2014 +0200 @@ -107,7 +107,7 @@ ends.clear(); } - public NodeIterable forwardEnds() { + public NodeInputList forwardEnds() { return ends; } diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/StructuredGraph.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/StructuredGraph.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/StructuredGraph.java Sat May 03 21:46:35 2014 +0200 @@ -231,7 +231,7 @@ } public boolean hasLoops() { - return getNodes(LoopBeginNode.class).isNotEmpty(); + return hasNode(LoopBeginNode.class); } public void removeFloating(FloatingNode node) { diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/CompareNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/CompareNode.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/CompareNode.java Sat May 03 21:46:35 2014 +0200 @@ -23,7 +23,7 @@ package com.oracle.graal.nodes.calc; import com.oracle.graal.api.meta.*; -import com.oracle.graal.api.meta.ProfilingInfo.*; +import com.oracle.graal.api.meta.ProfilingInfo.TriState; import com.oracle.graal.compiler.common.*; import com.oracle.graal.compiler.common.calc.*; import com.oracle.graal.graph.*; @@ -92,8 +92,8 @@ @Override public TriState evaluate(ConstantReflectionProvider constantReflection, ValueNode forX, ValueNode forY) { - if (x().isConstant() && y().isConstant()) { - return TriState.get(condition().foldCondition(x().asConstant(), y().asConstant(), constantReflection, unorderedIsTrue())); + if (forX.isConstant() && forY.isConstant()) { + return TriState.get(condition().foldCondition(forX.asConstant(), forY.asConstant(), constantReflection, unorderedIsTrue())); } return TriState.UNKNOWN; } diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/cfg/Block.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/cfg/Block.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/cfg/Block.java Sat May 03 21:46:35 2014 +0200 @@ -33,6 +33,8 @@ protected final BeginNode beginNode; protected FixedNode endNode; + + protected double probability; protected Loop loop; protected List dominated; @@ -175,4 +177,11 @@ return getDominator().isDominatedBy(block); } + public double probability() { + return probability; + } + + public void setProbability(double probability) { + this.probability = probability; + } } diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/cfg/BlocksToDoubles.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/cfg/BlocksToDoubles.java Fri May 02 02:45:26 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2013, 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.graal.nodes.cfg; - -import java.util.*; - -import com.oracle.graal.compiler.common.cfg.*; -import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.util.*; - -public class BlocksToDoubles { - - private final IdentityHashMap, Double> nodeProbabilities; - - public BlocksToDoubles(int numberOfNodes) { - this.nodeProbabilities = new IdentityHashMap<>(numberOfNodes); - } - - public void put(AbstractBlock n, double value) { - assert value >= 0.0 : value; - nodeProbabilities.put(n, value); - } - - public boolean contains(AbstractBlock n) { - return nodeProbabilities.containsKey(n); - } - - public double get(AbstractBlock n) { - Double value = nodeProbabilities.get(n); - assert value != null; - return value; - } - - public static BlocksToDoubles createFromNodeProbability(NodesToDoubles nodeProbabilities, ControlFlowGraph cfg) { - BlocksToDoubles blockProbabilities = new BlocksToDoubles(cfg.getBlocks().length); - for (Block block : cfg.getBlocks()) { - blockProbabilities.put(block, nodeProbabilities.get(block.getBeginNode())); - } - assert verify(nodeProbabilities, cfg, blockProbabilities) : "Probabilities differ for nodes in the same block."; - return blockProbabilities; - } - - private static boolean verify(NodesToDoubles nodeProbabilities, ControlFlowGraph cfg, BlocksToDoubles blockProbabilities) { - for (Block b : cfg.getBlocks()) { - double p = blockProbabilities.get(b); - for (FixedNode n : b.getNodes()) { - if (nodeProbabilities.get(n) != p) { - return false; - } - } - } - return true; - } -} diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/cfg/ControlFlowGraph.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/cfg/ControlFlowGraph.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/cfg/ControlFlowGraph.java Sat May 03 21:46:35 2014 +0200 @@ -200,14 +200,21 @@ private void connectBlocks() { for (Block block : reversePostOrder) { List predecessors = new ArrayList<>(4); + double probability = 0; for (Node predNode : block.getBeginNode().cfgPredecessors()) { Block predBlock = nodeToBlock.get(predNode); if (predBlock.getId() >= 0) { predecessors.add(predBlock); + probability += predBlock.probability; } } + if (predecessors.size() == 1 && predecessors.get(0).getEndNode() instanceof ControlSplitNode) { + probability *= ((ControlSplitNode) predecessors.get(0).getEndNode()).probability(block.getBeginNode()); + } if (block.getBeginNode() instanceof LoopBeginNode) { - for (LoopEndNode predNode : ((LoopBeginNode) block.getBeginNode()).orderedLoopEnds()) { + LoopBeginNode loopBegin = (LoopBeginNode) block.getBeginNode(); + probability *= loopBegin.loopFrequency(); + for (LoopEndNode predNode : loopBegin.orderedLoopEnds()) { Block predBlock = nodeToBlock.get(predNode); if (predBlock.getId() >= 0) { predecessors.add(predBlock); @@ -215,6 +222,7 @@ } } block.setPredecessors(predecessors); + block.setProbability(probability); List successors = new ArrayList<>(4); for (Node suxNode : block.getEndNode().cfgSuccessors()) { diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/util/GraphUtil.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/util/GraphUtil.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/util/GraphUtil.java Sat May 03 21:46:35 2014 +0200 @@ -73,7 +73,8 @@ if (merge != null) { merge.removeEnd(end); StructuredGraph graph = end.graph(); - if (merge instanceof LoopBeginNode && merge.forwardEndCount() == 0) { // dead loop + if (merge instanceof LoopBeginNode && merge.forwardEndCount() == 0) { + // dead loop for (PhiNode phi : merge.phis().snapshot()) { propagateKill(phi); } @@ -93,13 +94,13 @@ } else if (merge instanceof LoopBeginNode && ((LoopBeginNode) merge).loopEnds().isEmpty()) { // not a loop anymore if (tool != null) { - merge.phis().forEach(phi -> phi.usages().forEach(usage -> tool.addToWorkList(usage))); + merge.phis().forEach(phi -> phi.usages().forEach(tool::addToWorkList)); } graph.reduceDegenerateLoopBegin((LoopBeginNode) merge); } else if (merge.phiPredecessorCount() == 1) { // not a merge anymore if (tool != null) { - merge.phis().forEach(phi -> phi.usages().forEach(usage -> tool.addToWorkList(usage))); + merge.phis().forEach(phi -> phi.usages().forEach(tool::addToWorkList)); } graph.reduceTrivialMerge(merge); } @@ -408,4 +409,32 @@ return true; } } + + /** + * Returns an iterator that will return the given node followed by all its predecessors, up + * until the point where {@link Node#predecessor()} returns null; + * + * @param start the node at which to start iterating + */ + public static NodeIterable predecessorIterable(final FixedNode start) { + return new NodeIterable() { + public Iterator iterator() { + return new Iterator() { + public FixedNode current = start; + + public boolean hasNext() { + return current != null; + } + + public FixedNode next() { + try { + return current; + } finally { + current = (FixedNode) current.predecessor(); + } + } + }; + } + }; + } } diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/util/NodesToDoubles.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/util/NodesToDoubles.java Fri May 02 02:45:26 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2013, 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.graal.nodes.util; - -import java.util.*; - -import com.oracle.graal.nodes.*; - -public class NodesToDoubles { - - private final IdentityHashMap nodeProbabilities; - - public NodesToDoubles(int numberOfNodes) { - this.nodeProbabilities = new IdentityHashMap<>(numberOfNodes); - } - - public void put(FixedNode n, double value) { - assert value >= 0.0 : value; - nodeProbabilities.put(n, value); - } - - public boolean contains(FixedNode n) { - return nodeProbabilities.containsKey(n); - } - - public double get(FixedNode n) { - Double value = nodeProbabilities.get(n); - assert value != null; - return value; - } - - public int getCount() { - return nodeProbabilities.size(); - } -} diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/ConvertDeoptimizeToGuardPhase.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/ConvertDeoptimizeToGuardPhase.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/ConvertDeoptimizeToGuardPhase.java Sat May 03 21:46:35 2014 +0200 @@ -35,27 +35,20 @@ /** * This phase will find branches which always end with a {@link DeoptimizeNode} and replace their * {@link ControlSplitNode ControlSplitNodes} with {@link FixedGuardNode FixedGuardNodes}. - * + * * This is useful because {@link FixedGuardNode FixedGuardNodes} will be lowered to * {@link GuardNode GuardNodes} which can later be optimized more aggressively than control-flow * constructs. - * + * * This is currently only done for branches that start from a {@link IfNode}. If it encounters a * branch starting at an other kind of {@link ControlSplitNode}, it will only bring the * {@link DeoptimizeNode} as close to the {@link ControlSplitNode} as possible. - * + * */ public class ConvertDeoptimizeToGuardPhase extends Phase { private static BeginNode findBeginNode(FixedNode startNode) { - Node n = startNode; - while (true) { - if (n instanceof BeginNode) { - return (BeginNode) n; - } else { - n = n.predecessor(); - } - } + return GraphUtil.predecessorIterable(startNode).filter(BeginNode.class).first(); } @Override diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/FloatingReadPhase.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/FloatingReadPhase.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/FloatingReadPhase.java Sat May 03 21:46:35 2014 +0200 @@ -113,8 +113,8 @@ @Override protected void run(StructuredGraph graph) { Map> modifiedInLoops = new IdentityHashMap<>(); - ReentrantNodeIterator.apply(new CollectMemoryCheckpointsClosure(modifiedInLoops), graph.start(), new HashSet(), null); - ReentrantNodeIterator.apply(new FloatingReadClosure(modifiedInLoops, execmode), graph.start(), new MemoryMapImpl(graph.start()), null); + ReentrantNodeIterator.apply(new CollectMemoryCheckpointsClosure(modifiedInLoops), graph.start(), new HashSet()); + ReentrantNodeIterator.apply(new FloatingReadClosure(modifiedInLoops, execmode), graph.start(), new MemoryMapImpl(graph.start())); if (execmode == ExecutionMode.CREATE_FLOATING_READS) { assert !graph.isAfterFloatingReadPhase(); graph.setAfterFloatingReadPhase(true); diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/FrameStateAssignmentPhase.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/FrameStateAssignmentPhase.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/FrameStateAssignmentPhase.java Sat May 03 21:46:35 2014 +0200 @@ -107,7 +107,7 @@ protected void run(StructuredGraph graph) { assert graph.getGuardsStage().ordinal() >= GuardsStage.FIXED_DEOPTS.ordinal() && checkFixedDeopts(graph); if (graph.getGuardsStage().ordinal() < GuardsStage.AFTER_FSA.ordinal()) { - ReentrantNodeIterator.apply(new FrameStateAssignmentClosure(), graph.start(), null, null); + ReentrantNodeIterator.apply(new FrameStateAssignmentClosure(), graph.start(), null); graph.setGuardsStage(GuardsStage.AFTER_FSA); } } diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningPhase.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningPhase.java Fri May 02 02:45:26 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,858 +0,0 @@ -/* - * 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.graal.phases.common; - -import static com.oracle.graal.compiler.common.GraalOptions.*; -import static com.oracle.graal.phases.common.InliningPhase.Options.*; - -import java.util.*; - -import com.oracle.graal.api.code.*; -import com.oracle.graal.api.meta.*; -import com.oracle.graal.compiler.common.*; -import com.oracle.graal.compiler.common.type.*; -import com.oracle.graal.debug.*; -import com.oracle.graal.debug.Debug.Scope; -import com.oracle.graal.graph.*; -import com.oracle.graal.graph.Graph.Mark; -import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.java.*; -import com.oracle.graal.nodes.spi.*; -import com.oracle.graal.nodes.util.*; -import com.oracle.graal.options.*; -import com.oracle.graal.phases.common.InliningUtil.InlineInfo; -import com.oracle.graal.phases.common.InliningUtil.Inlineable; -import com.oracle.graal.phases.common.InliningUtil.InlineableGraph; -import com.oracle.graal.phases.common.InliningUtil.InlineableMacroNode; -import com.oracle.graal.phases.common.InliningUtil.InliningPolicy; -import com.oracle.graal.phases.graph.*; -import com.oracle.graal.phases.tiers.*; -import com.oracle.graal.phases.util.*; - -public class InliningPhase extends AbstractInliningPhase { - - static class Options { - - // @formatter:off - @Option(help = "Unconditionally inline intrinsics") - public static final OptionValue AlwaysInlineIntrinsics = new OptionValue<>(false); - // @formatter:on - } - - private final InliningPolicy inliningPolicy; - private final CanonicalizerPhase canonicalizer; - - private int inliningCount; - private int maxMethodPerInlining = Integer.MAX_VALUE; - - // Metrics - private static final DebugMetric metricInliningPerformed = Debug.metric("InliningPerformed"); - private static final DebugMetric metricInliningConsidered = Debug.metric("InliningConsidered"); - private static final DebugMetric metricInliningStoppedByMaxDesiredSize = Debug.metric("InliningStoppedByMaxDesiredSize"); - private static final DebugMetric metricInliningRuns = Debug.metric("InliningRuns"); - - public InliningPhase(CanonicalizerPhase canonicalizer) { - this(new GreedyInliningPolicy(null), canonicalizer); - } - - public InliningPhase(Map hints, CanonicalizerPhase canonicalizer) { - this(new GreedyInliningPolicy(hints), canonicalizer); - } - - public InliningPhase(InliningPolicy policy, CanonicalizerPhase canonicalizer) { - this.inliningPolicy = policy; - this.canonicalizer = canonicalizer; - } - - public void setMaxMethodsPerInlining(int max) { - maxMethodPerInlining = max; - } - - public int getInliningCount() { - return inliningCount; - } - - @Override - protected void run(final StructuredGraph graph, final HighTierContext context) { - final InliningData data = new InliningData(graph, context.getAssumptions()); - - while (data.hasUnprocessedGraphs()) { - final MethodInvocation currentInvocation = data.currentInvocation(); - GraphInfo graphInfo = data.currentGraph(); - if (!currentInvocation.isRoot() && - !inliningPolicy.isWorthInlining(context.getReplacements(), currentInvocation.callee(), data.inliningDepth(), currentInvocation.probability(), - currentInvocation.relevance(), false)) { - int remainingGraphs = currentInvocation.totalGraphs() - currentInvocation.processedGraphs(); - assert remainingGraphs > 0; - data.popGraphs(remainingGraphs); - data.popInvocation(); - } else if (graphInfo.hasRemainingInvokes() && inliningPolicy.continueInlining(graphInfo.graph())) { - processNextInvoke(data, graphInfo, context); - } else { - data.popGraph(); - if (!currentInvocation.isRoot()) { - assert currentInvocation.callee().invoke().asNode().isAlive(); - currentInvocation.incrementProcessedGraphs(); - if (currentInvocation.processedGraphs() == currentInvocation.totalGraphs()) { - data.popInvocation(); - final MethodInvocation parentInvoke = data.currentInvocation(); - try (Scope s = Debug.scope("Inlining", data.inliningContext())) { - tryToInline(data.currentGraph(), currentInvocation, parentInvoke, data.inliningDepth() + 1, context); - } catch (Throwable e) { - throw Debug.handle(e); - } - } - } - } - } - - assert data.inliningDepth() == 0; - assert data.graphCount() == 0; - } - - /** - * Process the next invoke and enqueue all its graphs for processing. - */ - private void processNextInvoke(InliningData data, GraphInfo graphInfo, HighTierContext context) { - Invoke invoke = graphInfo.popInvoke(); - MethodInvocation callerInvocation = data.currentInvocation(); - Assumptions parentAssumptions = callerInvocation.assumptions(); - InlineInfo info = InliningUtil.getInlineInfo(data, invoke, maxMethodPerInlining, context.getReplacements(), parentAssumptions, context.getOptimisticOptimizations()); - - if (info != null) { - double invokeProbability = graphInfo.invokeProbability(invoke); - double invokeRelevance = graphInfo.invokeRelevance(invoke); - MethodInvocation calleeInvocation = data.pushInvocation(info, parentAssumptions, invokeProbability, invokeRelevance); - - for (int i = 0; i < info.numberOfMethods(); i++) { - Inlineable elem = getInlineableElement(info.methodAt(i), info.invoke(), context.replaceAssumptions(calleeInvocation.assumptions())); - info.setInlinableElement(i, elem); - if (elem instanceof InlineableGraph) { - data.pushGraph(((InlineableGraph) elem).getGraph(), invokeProbability * info.probabilityAt(i), invokeRelevance * info.relevanceAt(i)); - } else { - assert elem instanceof InlineableMacroNode; - data.pushDummyGraph(); - } - } - } - } - - private void tryToInline(GraphInfo callerGraphInfo, MethodInvocation calleeInfo, MethodInvocation parentInvocation, int inliningDepth, HighTierContext context) { - InlineInfo callee = calleeInfo.callee(); - Assumptions callerAssumptions = parentInvocation.assumptions(); - - if (inliningPolicy.isWorthInlining(context.getReplacements(), callee, inliningDepth, calleeInfo.probability(), calleeInfo.relevance(), true)) { - doInline(callerGraphInfo, calleeInfo, callerAssumptions, context); - } else if (context.getOptimisticOptimizations().devirtualizeInvokes()) { - callee.tryToDevirtualizeInvoke(context.getMetaAccess(), callerAssumptions); - } - metricInliningConsidered.increment(); - } - - private void doInline(GraphInfo callerGraphInfo, MethodInvocation calleeInfo, Assumptions callerAssumptions, HighTierContext context) { - StructuredGraph callerGraph = callerGraphInfo.graph(); - Mark markBeforeInlining = callerGraph.getMark(); - InlineInfo callee = calleeInfo.callee(); - try { - try (Scope scope = Debug.scope("doInline", callerGraph)) { - List invokeUsages = callee.invoke().asNode().usages().snapshot(); - callee.inline(new Providers(context), callerAssumptions); - callerAssumptions.record(calleeInfo.assumptions()); - metricInliningRuns.increment(); - Debug.dump(callerGraph, "after %s", callee); - - if (OptCanonicalizer.getValue()) { - Mark markBeforeCanonicalization = callerGraph.getMark(); - canonicalizer.applyIncremental(callerGraph, context, invokeUsages, markBeforeInlining); - - // process invokes that are possibly created during canonicalization - for (Node newNode : callerGraph.getNewNodes(markBeforeCanonicalization)) { - if (newNode instanceof Invoke) { - callerGraphInfo.pushInvoke((Invoke) newNode); - } - } - } - - callerGraphInfo.computeProbabilities(); - - inliningCount++; - metricInliningPerformed.increment(); - } - } catch (BailoutException bailout) { - throw bailout; - } catch (AssertionError | RuntimeException e) { - throw new GraalInternalError(e).addContext(callee.toString()); - } catch (GraalInternalError e) { - throw e.addContext(callee.toString()); - } - } - - private Inlineable getInlineableElement(final ResolvedJavaMethod method, Invoke invoke, HighTierContext context) { - Class macroNodeClass = InliningUtil.getMacroNodeClass(context.getReplacements(), method); - if (macroNodeClass != null) { - return new InlineableMacroNode(macroNodeClass); - } else { - return new InlineableGraph(buildGraph(method, invoke, context)); - } - } - - private StructuredGraph buildGraph(final ResolvedJavaMethod method, final Invoke invoke, final HighTierContext context) { - final StructuredGraph newGraph; - final boolean parseBytecodes; - - // TODO (chaeubl): copying the graph is only necessary if it is modified or if it contains - // any invokes - StructuredGraph intrinsicGraph = InliningUtil.getIntrinsicGraph(context.getReplacements(), method); - if (intrinsicGraph != null) { - newGraph = intrinsicGraph.copy(); - parseBytecodes = false; - } else { - StructuredGraph cachedGraph = getCachedGraph(method, context); - if (cachedGraph != null) { - newGraph = cachedGraph.copy(); - parseBytecodes = false; - } else { - newGraph = new StructuredGraph(method); - parseBytecodes = true; - } - } - - try (Scope s = Debug.scope("InlineGraph", newGraph)) { - if (parseBytecodes) { - parseBytecodes(newGraph, context); - } - - boolean callerHasMoreInformationAboutArguments = false; - NodeInputList args = invoke.callTarget().arguments(); - for (ParameterNode param : newGraph.getNodes(ParameterNode.class).snapshot()) { - ValueNode arg = args.get(param.index()); - if (arg.isConstant()) { - Constant constant = arg.asConstant(); - newGraph.replaceFloating(param, ConstantNode.forConstant(constant, context.getMetaAccess(), newGraph)); - callerHasMoreInformationAboutArguments = true; - } else { - Stamp joinedStamp = param.stamp().join(arg.stamp()); - if (joinedStamp != null && !joinedStamp.equals(param.stamp())) { - param.setStamp(joinedStamp); - callerHasMoreInformationAboutArguments = true; - } - } - } - - if (!callerHasMoreInformationAboutArguments) { - // TODO (chaeubl): if args are not more concrete, inlining should be avoided - // in most cases or we could at least use the previous graph size + invoke - // probability to check the inlining - } - - if (OptCanonicalizer.getValue()) { - canonicalizer.apply(newGraph, context); - } - - return newGraph; - } catch (Throwable e) { - throw Debug.handle(e); - } - } - - private static StructuredGraph getCachedGraph(ResolvedJavaMethod method, HighTierContext context) { - if (context.getGraphCache() != null) { - StructuredGraph cachedGraph = context.getGraphCache().get(method); - if (cachedGraph != null) { - return cachedGraph; - } - } - return null; - } - - private StructuredGraph parseBytecodes(StructuredGraph newGraph, HighTierContext context) { - boolean hasMatureProfilingInfo = newGraph.method().getProfilingInfo().isMature(); - - if (context.getGraphBuilderSuite() != null) { - context.getGraphBuilderSuite().apply(newGraph, context); - } - assert newGraph.start().next() != null : "graph needs to be populated during PhasePosition.AFTER_PARSING"; - - new DeadCodeEliminationPhase().apply(newGraph); - - if (OptCanonicalizer.getValue()) { - canonicalizer.apply(newGraph, context); - } - - if (hasMatureProfilingInfo && context.getGraphCache() != null) { - context.getGraphCache().put(newGraph.method(), newGraph.copy()); - } - return newGraph; - } - - private abstract static class AbstractInliningPolicy implements InliningPolicy { - - protected final Map hints; - - public AbstractInliningPolicy(Map hints) { - this.hints = hints; - } - - protected double computeMaximumSize(double relevance, int configuredMaximum) { - double inlineRatio = Math.min(RelevanceCapForInlining.getValue(), relevance); - return configuredMaximum * inlineRatio; - } - - protected double getInliningBonus(InlineInfo info) { - if (hints != null && hints.containsKey(info.invoke())) { - return hints.get(info.invoke()); - } - return 1; - } - - protected boolean isIntrinsic(Replacements replacements, InlineInfo info) { - if (AlwaysInlineIntrinsics.getValue()) { - return onlyIntrinsics(replacements, info); - } else { - return onlyForcedIntrinsics(replacements, info); - } - } - - private static boolean onlyIntrinsics(Replacements replacements, InlineInfo info) { - for (int i = 0; i < info.numberOfMethods(); i++) { - if (!InliningUtil.canIntrinsify(replacements, info.methodAt(i))) { - return false; - } - } - return true; - } - - private static boolean onlyForcedIntrinsics(Replacements replacements, InlineInfo info) { - for (int i = 0; i < info.numberOfMethods(); i++) { - if (!InliningUtil.canIntrinsify(replacements, info.methodAt(i))) { - return false; - } - if (!replacements.isForcedSubstitution(info.methodAt(i))) { - return false; - } - } - return true; - } - - protected static int previousLowLevelGraphSize(InlineInfo info) { - int size = 0; - for (int i = 0; i < info.numberOfMethods(); i++) { - ResolvedJavaMethod m = info.methodAt(i); - ProfilingInfo profile = m.getProfilingInfo(); - int compiledGraphSize = profile.getCompilerIRSize(StructuredGraph.class); - if (compiledGraphSize > 0) { - size += compiledGraphSize; - } - } - return size; - } - - protected static int determineNodeCount(InlineInfo info) { - int nodes = 0; - for (int i = 0; i < info.numberOfMethods(); i++) { - Inlineable elem = info.inlineableElementAt(i); - if (elem != null) { - nodes += elem.getNodeCount(); - } - } - return nodes; - } - - protected static double determineInvokeProbability(InlineInfo info) { - double invokeProbability = 0; - for (int i = 0; i < info.numberOfMethods(); i++) { - Inlineable callee = info.inlineableElementAt(i); - Iterable invokes = callee.getInvokes(); - if (invokes.iterator().hasNext()) { - NodesToDoubles nodeProbabilities = new ComputeProbabilityClosure(((InlineableGraph) callee).getGraph()).apply(); - for (Invoke invoke : invokes) { - invokeProbability += nodeProbabilities.get(invoke.asNode()); - } - } - } - return invokeProbability; - } - } - - public static class GreedyInliningPolicy extends AbstractInliningPolicy { - - public GreedyInliningPolicy(Map hints) { - super(hints); - } - - public boolean continueInlining(StructuredGraph currentGraph) { - if (currentGraph.getNodeCount() >= MaximumDesiredSize.getValue()) { - InliningUtil.logInliningDecision("inlining is cut off by MaximumDesiredSize"); - metricInliningStoppedByMaxDesiredSize.increment(); - return false; - } - return true; - } - - @Override - public boolean isWorthInlining(Replacements replacements, InlineInfo info, int inliningDepth, double probability, double relevance, boolean fullyProcessed) { - if (InlineEverything.getValue()) { - return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "inline everything"); - } - - if (isIntrinsic(replacements, info)) { - return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "intrinsic"); - } - - if (info.shouldInline()) { - return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "forced inlining"); - } - - double inliningBonus = getInliningBonus(info); - int nodes = determineNodeCount(info); - int lowLevelGraphSize = previousLowLevelGraphSize(info); - - if (SmallCompiledLowLevelGraphSize.getValue() > 0 && lowLevelGraphSize > SmallCompiledLowLevelGraphSize.getValue() * inliningBonus) { - return InliningUtil.logNotInlinedMethod(info, inliningDepth, "too large previous low-level graph (low-level-nodes: %d, relevance=%f, probability=%f, bonus=%f, nodes=%d)", - lowLevelGraphSize, relevance, probability, inliningBonus, nodes); - } - - if (nodes < TrivialInliningSize.getValue() * inliningBonus) { - return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "trivial (relevance=%f, probability=%f, bonus=%f, nodes=%d)", relevance, probability, inliningBonus, nodes); - } - - /* - * TODO (chaeubl): invoked methods that are on important paths but not yet compiled -> - * will be compiled anyways and it is likely that we are the only caller... might be - * useful to inline those methods but increases bootstrap time (maybe those methods are - * also getting queued in the compilation queue concurrently) - */ - double invokes = determineInvokeProbability(info); - if (LimitInlinedInvokes.getValue() > 0 && fullyProcessed && invokes > LimitInlinedInvokes.getValue() * inliningBonus) { - return InliningUtil.logNotInlinedMethod(info, inliningDepth, "callee invoke probability is too high (invokeP=%f, relevance=%f, probability=%f, bonus=%f, nodes=%d)", invokes, - relevance, probability, inliningBonus, nodes); - } - - double maximumNodes = computeMaximumSize(relevance, (int) (MaximumInliningSize.getValue() * inliningBonus)); - if (nodes <= maximumNodes) { - return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "relevance-based (relevance=%f, probability=%f, bonus=%f, nodes=%d <= %f)", relevance, probability, - inliningBonus, nodes, maximumNodes); - } - - return InliningUtil.logNotInlinedMethod(info, inliningDepth, "relevance-based (relevance=%f, probability=%f, bonus=%f, nodes=%d > %f)", relevance, probability, inliningBonus, nodes, - maximumNodes); - } - } - - public static final class InlineEverythingPolicy implements InliningPolicy { - - public boolean continueInlining(StructuredGraph graph) { - if (graph.getNodeCount() >= MaximumDesiredSize.getValue()) { - throw new BailoutException("Inline all calls failed. The resulting graph is too large."); - } - return true; - } - - public boolean isWorthInlining(Replacements replacements, InlineInfo info, int inliningDepth, double probability, double relevance, boolean fullyProcessed) { - return true; - } - } - - private static class InliningIterator { - - private final FixedNode start; - private final Deque nodeQueue; - private final NodeBitMap queuedNodes; - - public InliningIterator(FixedNode start, NodeBitMap visitedFixedNodes) { - this.start = start; - this.nodeQueue = new ArrayDeque<>(); - this.queuedNodes = visitedFixedNodes; - assert start.isAlive(); - } - - public LinkedList apply() { - LinkedList invokes = new LinkedList<>(); - FixedNode current; - forcedQueue(start); - - while ((current = nextQueuedNode()) != null) { - assert current.isAlive(); - - if (current instanceof Invoke && ((Invoke) current).callTarget() instanceof MethodCallTargetNode) { - if (current != start) { - invokes.addLast((Invoke) current); - } - queueSuccessors(current); - } else if (current instanceof LoopBeginNode) { - queueSuccessors(current); - } else if (current instanceof LoopEndNode) { - // nothing todo - } else if (current instanceof MergeNode) { - queueSuccessors(current); - } else if (current instanceof FixedWithNextNode) { - queueSuccessors(current); - } else if (current instanceof EndNode) { - queueMerge((EndNode) current); - } else if (current instanceof ControlSinkNode) { - // nothing todo - } else if (current instanceof ControlSplitNode) { - queueSuccessors(current); - } else { - assert false : current; - } - } - - return invokes; - } - - private void queueSuccessors(FixedNode x) { - for (Node node : x.successors()) { - queue(node); - } - } - - private void queue(Node node) { - if (node != null && !queuedNodes.isMarked(node)) { - forcedQueue(node); - } - } - - private void forcedQueue(Node node) { - queuedNodes.mark(node); - nodeQueue.addFirst((FixedNode) node); - } - - private FixedNode nextQueuedNode() { - if (nodeQueue.isEmpty()) { - return null; - } - - FixedNode result = nodeQueue.removeFirst(); - assert queuedNodes.isMarked(result); - return result; - } - - private void queueMerge(AbstractEndNode end) { - MergeNode merge = end.merge(); - if (!queuedNodes.isMarked(merge) && visitedAllEnds(merge)) { - queuedNodes.mark(merge); - nodeQueue.add(merge); - } - } - - private boolean visitedAllEnds(MergeNode merge) { - for (int i = 0; i < merge.forwardEndCount(); i++) { - if (!queuedNodes.isMarked(merge.forwardEndAt(i))) { - return false; - } - } - return true; - } - } - - /** - * Holds the data for building the callee graphs recursively: graphs and invocations (each - * invocation can have multiple graphs). - */ - static class InliningData { - - private static final GraphInfo DummyGraphInfo = new GraphInfo(null, new LinkedList(), 1.0, 1.0); - - /** - * Call hierarchy from outer most call (i.e., compilation unit) to inner most callee. - */ - private final ArrayDeque graphQueue; - private final ArrayDeque invocationQueue; - - private int maxGraphs; - - public InliningData(StructuredGraph rootGraph, Assumptions rootAssumptions) { - this.graphQueue = new ArrayDeque<>(); - this.invocationQueue = new ArrayDeque<>(); - this.maxGraphs = 1; - - invocationQueue.push(new MethodInvocation(null, rootAssumptions, 1.0, 1.0)); - pushGraph(rootGraph, 1.0, 1.0); - } - - public int graphCount() { - return graphQueue.size(); - } - - public void pushGraph(StructuredGraph graph, double probability, double relevance) { - assert !contains(graph); - NodeBitMap visitedFixedNodes = graph.createNodeBitMap(); - LinkedList invokes = new InliningIterator(graph.start(), visitedFixedNodes).apply(); - assert invokes.size() == count(graph.getInvokes()); - graphQueue.push(new GraphInfo(graph, invokes, probability, relevance)); - assert graphQueue.size() <= maxGraphs; - } - - public void pushDummyGraph() { - graphQueue.push(DummyGraphInfo); - } - - public boolean hasUnprocessedGraphs() { - return !graphQueue.isEmpty(); - } - - public GraphInfo currentGraph() { - return graphQueue.peek(); - } - - public void popGraph() { - graphQueue.pop(); - assert graphQueue.size() <= maxGraphs; - } - - public void popGraphs(int count) { - assert count >= 0; - for (int i = 0; i < count; i++) { - graphQueue.pop(); - } - } - - private static final Object[] NO_CONTEXT = {}; - - /** - * Gets the call hierarchy of this inlining from outer most call to inner most callee. - */ - public Object[] inliningContext() { - if (!Debug.isDumpEnabled()) { - return NO_CONTEXT; - } - Object[] result = new Object[graphQueue.size()]; - int i = 0; - for (GraphInfo g : graphQueue) { - result[i++] = g.graph.method(); - } - return result; - } - - public MethodInvocation currentInvocation() { - return invocationQueue.peekFirst(); - } - - public MethodInvocation pushInvocation(InlineInfo info, Assumptions assumptions, double probability, double relevance) { - MethodInvocation methodInvocation = new MethodInvocation(info, new Assumptions(assumptions.useOptimisticAssumptions()), probability, relevance); - invocationQueue.addFirst(methodInvocation); - maxGraphs += info.numberOfMethods(); - assert graphQueue.size() <= maxGraphs; - return methodInvocation; - } - - public void popInvocation() { - maxGraphs -= invocationQueue.peekFirst().callee.numberOfMethods(); - assert graphQueue.size() <= maxGraphs; - invocationQueue.removeFirst(); - } - - public int countRecursiveInlining(ResolvedJavaMethod method) { - int count = 0; - for (GraphInfo graphInfo : graphQueue) { - if (method.equals(graphInfo.method())) { - count++; - } - } - return count; - } - - public int inliningDepth() { - assert invocationQueue.size() > 0; - return invocationQueue.size() - 1; - } - - @Override - public String toString() { - StringBuilder result = new StringBuilder("Invocations: "); - - for (MethodInvocation invocation : invocationQueue) { - if (invocation.callee() != null) { - result.append(invocation.callee().numberOfMethods()); - result.append("x "); - result.append(invocation.callee().invoke()); - result.append("; "); - } - } - - result.append("\nGraphs: "); - for (GraphInfo graph : graphQueue) { - result.append(graph.graph()); - result.append("; "); - } - - return result.toString(); - } - - private boolean contains(StructuredGraph graph) { - for (GraphInfo info : graphQueue) { - if (info.graph() == graph) { - return true; - } - } - return false; - } - - private static int count(Iterable invokes) { - int count = 0; - Iterator iterator = invokes.iterator(); - while (iterator.hasNext()) { - iterator.next(); - count++; - } - return count; - } - } - - private static class MethodInvocation { - - private final InlineInfo callee; - private final Assumptions assumptions; - private final double probability; - private final double relevance; - - private int processedGraphs; - - public MethodInvocation(InlineInfo info, Assumptions assumptions, double probability, double relevance) { - this.callee = info; - this.assumptions = assumptions; - this.probability = probability; - this.relevance = relevance; - } - - public void incrementProcessedGraphs() { - processedGraphs++; - assert processedGraphs <= callee.numberOfMethods(); - } - - public int processedGraphs() { - assert processedGraphs <= callee.numberOfMethods(); - return processedGraphs; - } - - public int totalGraphs() { - return callee.numberOfMethods(); - } - - public InlineInfo callee() { - return callee; - } - - public Assumptions assumptions() { - return assumptions; - } - - public double probability() { - return probability; - } - - public double relevance() { - return relevance; - } - - public boolean isRoot() { - return callee == null; - } - - @Override - public String toString() { - if (isRoot()) { - return ""; - } - CallTargetNode callTarget = callee.invoke().callTarget(); - if (callTarget instanceof MethodCallTargetNode) { - ResolvedJavaMethod calleeMethod = ((MethodCallTargetNode) callTarget).targetMethod(); - return MetaUtil.format("Invoke#%H.%n(%p)", calleeMethod); - } else { - return "Invoke#" + callTarget.targetName(); - } - } - } - - /** - * Information about a graph that will potentially be inlined. This includes tracking the - * invocations in graph that will subject to inlining themselves. - */ - private static class GraphInfo { - - private final StructuredGraph graph; - private final LinkedList remainingInvokes; - private final double probability; - private final double relevance; - - private NodesToDoubles nodeProbabilities; - private NodesToDoubles nodeRelevance; - - public GraphInfo(StructuredGraph graph, LinkedList invokes, double probability, double relevance) { - this.graph = graph; - this.remainingInvokes = invokes; - this.probability = probability; - this.relevance = relevance; - - if (graph != null) { - computeProbabilities(); - } - } - - /** - * Gets the method associated with the {@linkplain #graph() graph} represented by this - * object. - */ - public ResolvedJavaMethod method() { - return graph.method(); - } - - public boolean hasRemainingInvokes() { - return !remainingInvokes.isEmpty(); - } - - /** - * The graph about which this object contains inlining information. - */ - public StructuredGraph graph() { - return graph; - } - - public Invoke popInvoke() { - return remainingInvokes.removeFirst(); - } - - public void pushInvoke(Invoke invoke) { - remainingInvokes.push(invoke); - } - - public void computeProbabilities() { - nodeProbabilities = new ComputeProbabilityClosure(graph).apply(); - nodeRelevance = new ComputeInliningRelevanceClosure(graph, nodeProbabilities).apply(); - } - - public double invokeProbability(Invoke invoke) { - return probability * nodeProbabilities.get(invoke.asNode()); - } - - public double invokeRelevance(Invoke invoke) { - return Math.min(CapInheritedRelevance.getValue(), relevance) * nodeRelevance.get(invoke.asNode()); - } - - @Override - public String toString() { - return (graph != null ? MetaUtil.format("%H.%n(%p)", method()) : "") + remainingInvokes; - } - } -} diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningUtil.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningUtil.java Fri May 02 02:45:26 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1618 +0,0 @@ -/* - * Copyright (c) 2012, 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.graal.phases.common; - -import static com.oracle.graal.api.meta.DeoptimizationAction.*; -import static com.oracle.graal.api.meta.DeoptimizationReason.*; -import static com.oracle.graal.compiler.common.GraalOptions.*; -import static com.oracle.graal.compiler.common.type.StampFactory.*; -import java.util.*; - -import com.oracle.graal.api.code.*; -import com.oracle.graal.api.code.Assumptions.Assumption; -import com.oracle.graal.api.meta.*; -import com.oracle.graal.api.meta.JavaTypeProfile.ProfiledType; -import com.oracle.graal.api.meta.ResolvedJavaType.Representation; -import com.oracle.graal.compiler.common.*; -import com.oracle.graal.compiler.common.calc.*; -import com.oracle.graal.compiler.common.type.*; -import com.oracle.graal.debug.*; -import com.oracle.graal.debug.Debug.Scope; -import com.oracle.graal.graph.*; -import com.oracle.graal.graph.Graph.DuplicationReplacement; -import com.oracle.graal.graph.Node.Verbosity; -import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.calc.*; -import com.oracle.graal.nodes.extended.*; -import com.oracle.graal.nodes.java.*; -import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind; -import com.oracle.graal.nodes.spi.*; -import com.oracle.graal.nodes.type.*; -import com.oracle.graal.nodes.util.*; -import com.oracle.graal.phases.*; -import com.oracle.graal.phases.common.InliningPhase.InliningData; -import com.oracle.graal.phases.tiers.*; -import com.oracle.graal.phases.util.*; - -public class InliningUtil { - - private static final DebugMetric metricInliningTailDuplication = Debug.metric("InliningTailDuplication"); - private static final String inliningDecisionsScopeString = "InliningDecisions"; - /** - * Meters the size (in bytecodes) of all methods processed during compilation (i.e., top level - * and all inlined methods), irrespective of how many bytecodes in each method are actually - * parsed (which may be none for methods whose IR is retrieved from a cache). - */ - public static final DebugMetric InlinedBytecodes = Debug.metric("InlinedBytecodes"); - - public interface InliningPolicy { - - boolean continueInlining(StructuredGraph graph); - - boolean isWorthInlining(Replacements replacements, InlineInfo info, int inliningDepth, double probability, double relevance, boolean fullyProcessed); - } - - public interface Inlineable { - - int getNodeCount(); - - Iterable getInvokes(); - } - - public static class InlineableGraph implements Inlineable { - - private final StructuredGraph graph; - - public InlineableGraph(StructuredGraph graph) { - this.graph = graph; - } - - @Override - public int getNodeCount() { - return graph.getNodeCount(); - } - - @Override - public Iterable getInvokes() { - return graph.getInvokes(); - } - - public StructuredGraph getGraph() { - return graph; - } - } - - public static class InlineableMacroNode implements Inlineable { - - private final Class macroNodeClass; - - public InlineableMacroNode(Class macroNodeClass) { - this.macroNodeClass = macroNodeClass; - } - - @Override - public int getNodeCount() { - return 1; - } - - @Override - public Iterable getInvokes() { - return Collections.emptyList(); - } - - public Class getMacroNodeClass() { - return macroNodeClass; - } - } - - /** - * Print a HotSpot-style inlining message to the console. - */ - private static void printInlining(final InlineInfo info, final int inliningDepth, final boolean success, final String msg, final Object... args) { - printInlining(info.methodAt(0), info.invoke(), inliningDepth, success, msg, args); - } - - /** - * Print a HotSpot-style inlining message to the console. - */ - private static void printInlining(final ResolvedJavaMethod method, final Invoke invoke, final int inliningDepth, final boolean success, final String msg, final Object... args) { - if (HotSpotPrintInlining.getValue()) { - // 1234567 - TTY.print(" "); // print timestamp - // 1234 - TTY.print(" "); // print compilation number - // % s ! b n - TTY.print("%c%c%c%c%c ", ' ', method.isSynchronized() ? 's' : ' ', ' ', ' ', method.isNative() ? 'n' : ' '); - TTY.print(" "); // more indent - TTY.print(" "); // initial inlining indent - for (int i = 0; i < inliningDepth; i++) { - TTY.print(" "); - } - TTY.println(String.format("@ %d %s %s%s", invoke.bci(), methodName(method, null), success ? "" : "not inlining ", String.format(msg, args))); - } - } - - public static boolean logInlinedMethod(InlineInfo info, int inliningDepth, boolean allowLogging, String msg, Object... args) { - return logInliningDecision(info, inliningDepth, allowLogging, true, msg, args); - } - - public static boolean logNotInlinedMethod(InlineInfo info, int inliningDepth, String msg, Object... args) { - return logInliningDecision(info, inliningDepth, true, false, msg, args); - } - - public static boolean logInliningDecision(InlineInfo info, int inliningDepth, boolean allowLogging, boolean success, String msg, final Object... args) { - if (allowLogging) { - printInlining(info, inliningDepth, success, msg, args); - if (shouldLogInliningDecision()) { - logInliningDecision(methodName(info), success, msg, args); - } - } - return success; - } - - public static void logInliningDecision(final String msg, final Object... args) { - try (Scope s = Debug.scope(inliningDecisionsScopeString)) { - // Can't use log here since we are varargs - if (Debug.isLogEnabled()) { - Debug.logv(msg, args); - } - } - } - - private static boolean logNotInlinedMethod(Invoke invoke, String msg) { - if (shouldLogInliningDecision()) { - String methodString = invoke.toString() + (invoke.callTarget() == null ? " callTarget=null" : invoke.callTarget().targetName()); - logInliningDecision(methodString, false, msg, new Object[0]); - } - return false; - } - - private static InlineInfo logNotInlinedMethodAndReturnNull(Invoke invoke, int inliningDepth, ResolvedJavaMethod method, String msg) { - return logNotInlinedMethodAndReturnNull(invoke, inliningDepth, method, msg, new Object[0]); - } - - private static InlineInfo logNotInlinedMethodAndReturnNull(Invoke invoke, int inliningDepth, ResolvedJavaMethod method, String msg, Object... args) { - printInlining(method, invoke, inliningDepth, false, msg, args); - if (shouldLogInliningDecision()) { - String methodString = methodName(method, invoke); - logInliningDecision(methodString, false, msg, args); - } - return null; - } - - private static boolean logNotInlinedMethodAndReturnFalse(Invoke invoke, int inliningDepth, ResolvedJavaMethod method, String msg) { - printInlining(method, invoke, inliningDepth, false, msg, new Object[0]); - if (shouldLogInliningDecision()) { - String methodString = methodName(method, invoke); - logInliningDecision(methodString, false, msg, new Object[0]); - } - return false; - } - - private static void logInliningDecision(final String methodString, final boolean success, final String msg, final Object... args) { - String inliningMsg = "inlining " + methodString + ": " + msg; - if (!success) { - inliningMsg = "not " + inliningMsg; - } - logInliningDecision(inliningMsg, args); - } - - public static boolean shouldLogInliningDecision() { - try (Scope s = Debug.scope(inliningDecisionsScopeString)) { - return Debug.isLogEnabled(); - } - } - - private static String methodName(ResolvedJavaMethod method, Invoke invoke) { - if (invoke != null && invoke.stateAfter() != null) { - return methodName(invoke.stateAfter(), invoke.bci()) + ": " + MetaUtil.format("%H.%n(%p):%r", method) + " (" + method.getCodeSize() + " bytes)"; - } else { - return MetaUtil.format("%H.%n(%p):%r", method) + " (" + method.getCodeSize() + " bytes)"; - } - } - - private static String methodName(InlineInfo info) { - if (info == null) { - return "null"; - } else if (info.invoke() != null && info.invoke().stateAfter() != null) { - return methodName(info.invoke().stateAfter(), info.invoke().bci()) + ": " + info.toString(); - } else { - return info.toString(); - } - } - - private static String methodName(FrameState frameState, int bci) { - StringBuilder sb = new StringBuilder(); - if (frameState.outerFrameState() != null) { - sb.append(methodName(frameState.outerFrameState(), frameState.outerFrameState().bci)); - sb.append("->"); - } - sb.append(MetaUtil.format("%h.%n", frameState.method())); - sb.append("@").append(bci); - return sb.toString(); - } - - /** - * Represents an opportunity for inlining at a given invoke, with the given weight and level. - * The weight is the amortized weight of the additional code - so smaller is better. The level - * is the number of nested inlinings that lead to this invoke. - */ - public interface InlineInfo { - - /** - * The graph containing the {@link #invoke() invocation} that may be inlined. - */ - StructuredGraph graph(); - - /** - * The invocation that may be inlined. - */ - Invoke invoke(); - - /** - * Returns the number of methods that may be inlined by the {@link #invoke() invocation}. - * This may be more than one in the case of a invocation profile showing a number of "hot" - * concrete methods dispatched to by the invocation. - */ - int numberOfMethods(); - - ResolvedJavaMethod methodAt(int index); - - Inlineable inlineableElementAt(int index); - - double probabilityAt(int index); - - double relevanceAt(int index); - - void setInlinableElement(int index, Inlineable inlineableElement); - - /** - * Performs the inlining described by this object and returns the node that represents the - * return value of the inlined method (or null for void methods and methods that have no - * non-exceptional exit). - */ - void inline(Providers providers, Assumptions assumptions); - - /** - * Try to make the call static bindable to avoid interface and virtual method calls. - */ - void tryToDevirtualizeInvoke(MetaAccessProvider metaAccess, Assumptions assumptions); - - boolean shouldInline(); - } - - public abstract static class AbstractInlineInfo implements InlineInfo { - - protected final Invoke invoke; - - public AbstractInlineInfo(Invoke invoke) { - this.invoke = invoke; - } - - @Override - public StructuredGraph graph() { - return invoke.asNode().graph(); - } - - @Override - public Invoke invoke() { - return invoke; - } - - protected static void inline(Invoke invoke, ResolvedJavaMethod concrete, Inlineable inlineable, Assumptions assumptions, boolean receiverNullCheck) { - if (inlineable instanceof InlineableGraph) { - StructuredGraph calleeGraph = ((InlineableGraph) inlineable).getGraph(); - InliningUtil.inline(invoke, calleeGraph, receiverNullCheck); - } else { - assert inlineable instanceof InlineableMacroNode; - - Class macroNodeClass = ((InlineableMacroNode) inlineable).getMacroNodeClass(); - inlineMacroNode(invoke, concrete, macroNodeClass); - } - - InlinedBytecodes.add(concrete.getCodeSize()); - assumptions.recordMethodContents(concrete); - } - } - - public static void replaceInvokeCallTarget(Invoke invoke, StructuredGraph graph, InvokeKind invokeKind, ResolvedJavaMethod targetMethod) { - MethodCallTargetNode oldCallTarget = (MethodCallTargetNode) invoke.callTarget(); - MethodCallTargetNode newCallTarget = graph.add(new MethodCallTargetNode(invokeKind, targetMethod, oldCallTarget.arguments().toArray(new ValueNode[0]), oldCallTarget.returnType())); - invoke.asNode().replaceFirstInput(oldCallTarget, newCallTarget); - } - - /** - * Represents an inlining opportunity where the compiler can statically determine a monomorphic - * target method and therefore is able to determine the called method exactly. - */ - public static class ExactInlineInfo extends AbstractInlineInfo { - - protected final ResolvedJavaMethod concrete; - private Inlineable inlineableElement; - private boolean suppressNullCheck; - - public ExactInlineInfo(Invoke invoke, ResolvedJavaMethod concrete) { - super(invoke); - this.concrete = concrete; - assert concrete != null; - } - - public void suppressNullCheck() { - suppressNullCheck = true; - } - - @Override - public void inline(Providers providers, Assumptions assumptions) { - inline(invoke, concrete, inlineableElement, assumptions, !suppressNullCheck); - } - - @Override - public void tryToDevirtualizeInvoke(MetaAccessProvider metaAccess, Assumptions assumptions) { - // nothing todo, can already be bound statically - } - - @Override - public int numberOfMethods() { - return 1; - } - - @Override - public ResolvedJavaMethod methodAt(int index) { - assert index == 0; - return concrete; - } - - @Override - public double probabilityAt(int index) { - assert index == 0; - return 1.0; - } - - @Override - public double relevanceAt(int index) { - assert index == 0; - return 1.0; - } - - @Override - public String toString() { - return "exact " + MetaUtil.format("%H.%n(%p):%r", concrete); - } - - @Override - public Inlineable inlineableElementAt(int index) { - assert index == 0; - return inlineableElement; - } - - @Override - public void setInlinableElement(int index, Inlineable inlineableElement) { - assert index == 0; - this.inlineableElement = inlineableElement; - } - - public boolean shouldInline() { - return concrete.shouldBeInlined(); - } - } - - /** - * Represents an inlining opportunity for which profiling information suggests a monomorphic - * receiver, but for which the receiver type cannot be proven. A type check guard will be - * generated if this inlining is performed. - */ - private static class TypeGuardInlineInfo extends AbstractInlineInfo { - - private final ResolvedJavaMethod concrete; - private final ResolvedJavaType type; - private Inlineable inlineableElement; - - public TypeGuardInlineInfo(Invoke invoke, ResolvedJavaMethod concrete, ResolvedJavaType type) { - super(invoke); - this.concrete = concrete; - this.type = type; - assert type.isArray() || !type.isAbstract() : type; - } - - @Override - public int numberOfMethods() { - return 1; - } - - @Override - public ResolvedJavaMethod methodAt(int index) { - assert index == 0; - return concrete; - } - - @Override - public Inlineable inlineableElementAt(int index) { - assert index == 0; - return inlineableElement; - } - - @Override - public double probabilityAt(int index) { - assert index == 0; - return 1.0; - } - - @Override - public double relevanceAt(int index) { - assert index == 0; - return 1.0; - } - - @Override - public void setInlinableElement(int index, Inlineable inlineableElement) { - assert index == 0; - this.inlineableElement = inlineableElement; - } - - @Override - public void inline(Providers providers, Assumptions assumptions) { - createGuard(graph(), providers.getMetaAccess()); - inline(invoke, concrete, inlineableElement, assumptions, false); - } - - @Override - public void tryToDevirtualizeInvoke(MetaAccessProvider metaAccess, Assumptions assumptions) { - createGuard(graph(), metaAccess); - replaceInvokeCallTarget(invoke, graph(), InvokeKind.Special, concrete); - } - - private void createGuard(StructuredGraph graph, MetaAccessProvider metaAccess) { - ValueNode nonNullReceiver = InliningUtil.nonNullReceiver(invoke); - ConstantNode typeHub = ConstantNode.forConstant(type.getEncoding(Representation.ObjectHub), metaAccess, graph); - LoadHubNode receiverHub = graph.unique(new LoadHubNode(nonNullReceiver, typeHub.getKind())); - - CompareNode typeCheck = CompareNode.createCompareNode(graph, Condition.EQ, receiverHub, typeHub); - FixedGuardNode guard = graph.add(new FixedGuardNode(typeCheck, DeoptimizationReason.TypeCheckedInliningViolated, DeoptimizationAction.InvalidateReprofile)); - assert invoke.predecessor() != null; - - ValueNode anchoredReceiver = createAnchoredReceiver(graph, guard, type, nonNullReceiver, true); - invoke.callTarget().replaceFirstInput(nonNullReceiver, anchoredReceiver); - - graph.addBeforeFixed(invoke.asNode(), guard); - } - - @Override - public String toString() { - return "type-checked with type " + type.getName() + " and method " + MetaUtil.format("%H.%n(%p):%r", concrete); - } - - public boolean shouldInline() { - return concrete.shouldBeInlined(); - } - } - - /** - * Polymorphic inlining of m methods with n type checks (n ≥ m) in case that the profiling - * information suggests a reasonable amount of different receiver types and different methods. - * If an unknown type is encountered a deoptimization is triggered. - */ - private static class MultiTypeGuardInlineInfo extends AbstractInlineInfo { - - private final List concretes; - private final double[] methodProbabilities; - private final double maximumMethodProbability; - private final ArrayList typesToConcretes; - private final ArrayList ptypes; - private final ArrayList concretesProbabilities; - private final double notRecordedTypeProbability; - private final Inlineable[] inlineableElements; - - public MultiTypeGuardInlineInfo(Invoke invoke, ArrayList concretes, ArrayList concretesProbabilities, ArrayList ptypes, - ArrayList typesToConcretes, double notRecordedTypeProbability) { - super(invoke); - assert concretes.size() > 0 : "must have at least one method"; - assert ptypes.size() == typesToConcretes.size() : "array lengths must match"; - - this.concretesProbabilities = concretesProbabilities; - this.concretes = concretes; - this.ptypes = ptypes; - this.typesToConcretes = typesToConcretes; - this.notRecordedTypeProbability = notRecordedTypeProbability; - this.inlineableElements = new Inlineable[concretes.size()]; - this.methodProbabilities = computeMethodProbabilities(); - this.maximumMethodProbability = maximumMethodProbability(); - assert maximumMethodProbability > 0; - } - - private double[] computeMethodProbabilities() { - double[] result = new double[concretes.size()]; - for (int i = 0; i < typesToConcretes.size(); i++) { - int concrete = typesToConcretes.get(i); - double probability = ptypes.get(i).getProbability(); - result[concrete] += probability; - } - return result; - } - - private double maximumMethodProbability() { - double max = 0; - for (int i = 0; i < methodProbabilities.length; i++) { - max = Math.max(max, methodProbabilities[i]); - } - return max; - } - - @Override - public int numberOfMethods() { - return concretes.size(); - } - - @Override - public ResolvedJavaMethod methodAt(int index) { - assert index >= 0 && index < concretes.size(); - return concretes.get(index); - } - - @Override - public Inlineable inlineableElementAt(int index) { - assert index >= 0 && index < concretes.size(); - return inlineableElements[index]; - } - - @Override - public double probabilityAt(int index) { - return methodProbabilities[index]; - } - - @Override - public double relevanceAt(int index) { - return probabilityAt(index) / maximumMethodProbability; - } - - @Override - public void setInlinableElement(int index, Inlineable inlineableElement) { - assert index >= 0 && index < concretes.size(); - inlineableElements[index] = inlineableElement; - } - - @Override - public void inline(Providers providers, Assumptions assumptions) { - if (hasSingleMethod()) { - inlineSingleMethod(graph(), providers.getMetaAccess(), assumptions); - } else { - inlineMultipleMethods(graph(), providers, assumptions); - } - } - - public boolean shouldInline() { - for (ResolvedJavaMethod method : concretes) { - if (method.shouldBeInlined()) { - return true; - } - } - return false; - } - - private boolean hasSingleMethod() { - return concretes.size() == 1 && !shouldFallbackToInvoke(); - } - - private boolean shouldFallbackToInvoke() { - return notRecordedTypeProbability > 0; - } - - private void inlineMultipleMethods(StructuredGraph graph, Providers providers, Assumptions assumptions) { - int numberOfMethods = concretes.size(); - FixedNode continuation = invoke.next(); - - ValueNode originalReceiver = ((MethodCallTargetNode) invoke.callTarget()).receiver(); - // setup merge and phi nodes for results and exceptions - MergeNode returnMerge = graph.add(new MergeNode()); - returnMerge.setStateAfter(invoke.stateAfter()); - - PhiNode returnValuePhi = null; - if (invoke.asNode().getKind() != Kind.Void) { - returnValuePhi = graph.addWithoutUnique(new ValuePhiNode(invoke.asNode().stamp().unrestricted(), returnMerge)); - } - - MergeNode exceptionMerge = null; - PhiNode exceptionObjectPhi = null; - if (invoke instanceof InvokeWithExceptionNode) { - InvokeWithExceptionNode invokeWithException = (InvokeWithExceptionNode) invoke; - ExceptionObjectNode exceptionEdge = (ExceptionObjectNode) invokeWithException.exceptionEdge(); - - exceptionMerge = graph.add(new MergeNode()); - - FixedNode exceptionSux = exceptionEdge.next(); - graph.addBeforeFixed(exceptionSux, exceptionMerge); - exceptionObjectPhi = graph.addWithoutUnique(new ValuePhiNode(StampFactory.forKind(Kind.Object), exceptionMerge)); - exceptionMerge.setStateAfter(exceptionEdge.stateAfter().duplicateModified(invoke.stateAfter().bci, true, Kind.Object, exceptionObjectPhi)); - } - - // create one separate block for each invoked method - BeginNode[] successors = new BeginNode[numberOfMethods + 1]; - for (int i = 0; i < numberOfMethods; i++) { - successors[i] = createInvocationBlock(graph, invoke, returnMerge, returnValuePhi, exceptionMerge, exceptionObjectPhi, true); - } - - // create the successor for an unknown type - FixedNode unknownTypeSux; - if (shouldFallbackToInvoke()) { - unknownTypeSux = createInvocationBlock(graph, invoke, returnMerge, returnValuePhi, exceptionMerge, exceptionObjectPhi, false); - } else { - unknownTypeSux = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.TypeCheckedInliningViolated)); - } - successors[successors.length - 1] = BeginNode.begin(unknownTypeSux); - - // replace the invoke exception edge - if (invoke instanceof InvokeWithExceptionNode) { - InvokeWithExceptionNode invokeWithExceptionNode = (InvokeWithExceptionNode) invoke; - ExceptionObjectNode exceptionEdge = (ExceptionObjectNode) invokeWithExceptionNode.exceptionEdge(); - exceptionEdge.replaceAtUsages(exceptionObjectPhi); - exceptionEdge.setNext(null); - GraphUtil.killCFG(invokeWithExceptionNode.exceptionEdge()); - } - - assert invoke.asNode().isAlive(); - - // replace the invoke with a switch on the type of the actual receiver - boolean methodDispatch = createDispatchOnTypeBeforeInvoke(graph, successors, false, providers.getMetaAccess()); - - assert invoke.next() == continuation; - invoke.setNext(null); - returnMerge.setNext(continuation); - invoke.asNode().replaceAtUsages(returnValuePhi); - invoke.asNode().replaceAndDelete(null); - - ArrayList replacementNodes = new ArrayList<>(); - - // do the actual inlining for every invoke - for (int i = 0; i < numberOfMethods; i++) { - BeginNode node = successors[i]; - Invoke invokeForInlining = (Invoke) node.next(); - - ResolvedJavaType commonType; - if (methodDispatch) { - commonType = concretes.get(i).getDeclaringClass(); - } else { - commonType = getLeastCommonType(i); - } - - ValueNode receiver = ((MethodCallTargetNode) invokeForInlining.callTarget()).receiver(); - boolean exact = (getTypeCount(i) == 1 && !methodDispatch); - GuardedValueNode anchoredReceiver = createAnchoredReceiver(graph, node, commonType, receiver, exact); - invokeForInlining.callTarget().replaceFirstInput(receiver, anchoredReceiver); - - inline(invokeForInlining, methodAt(i), inlineableElementAt(i), assumptions, false); - - replacementNodes.add(anchoredReceiver); - } - if (shouldFallbackToInvoke()) { - replacementNodes.add(null); - } - - if (OptTailDuplication.getValue()) { - /* - * We might want to perform tail duplication at the merge after a type switch, if - * there are invokes that would benefit from the improvement in type information. - */ - FixedNode current = returnMerge; - int opportunities = 0; - do { - if (current instanceof InvokeNode && ((InvokeNode) current).callTarget() instanceof MethodCallTargetNode && - ((MethodCallTargetNode) ((InvokeNode) current).callTarget()).receiver() == originalReceiver) { - opportunities++; - } else if (current.inputs().contains(originalReceiver)) { - opportunities++; - } - current = ((FixedWithNextNode) current).next(); - } while (current instanceof FixedWithNextNode); - - if (opportunities > 0) { - metricInliningTailDuplication.increment(); - Debug.log("MultiTypeGuardInlineInfo starting tail duplication (%d opportunities)", opportunities); - PhaseContext phaseContext = new PhaseContext(providers, assumptions); - CanonicalizerPhase canonicalizer = new CanonicalizerPhase(!ImmutableCode.getValue()); - TailDuplicationPhase.tailDuplicate(returnMerge, TailDuplicationPhase.TRUE_DECISION, replacementNodes, phaseContext, canonicalizer); - } - } - } - - private int getTypeCount(int concreteMethodIndex) { - int count = 0; - for (int i = 0; i < typesToConcretes.size(); i++) { - if (typesToConcretes.get(i) == concreteMethodIndex) { - count++; - } - } - return count; - } - - private ResolvedJavaType getLeastCommonType(int concreteMethodIndex) { - ResolvedJavaType commonType = null; - for (int i = 0; i < typesToConcretes.size(); i++) { - if (typesToConcretes.get(i) == concreteMethodIndex) { - if (commonType == null) { - commonType = ptypes.get(i).getType(); - } else { - commonType = commonType.findLeastCommonAncestor(ptypes.get(i).getType()); - } - } - } - assert commonType != null; - return commonType; - } - - private ResolvedJavaType getLeastCommonType() { - ResolvedJavaType result = getLeastCommonType(0); - for (int i = 1; i < concretes.size(); i++) { - result = result.findLeastCommonAncestor(getLeastCommonType(i)); - } - return result; - } - - private void inlineSingleMethod(StructuredGraph graph, MetaAccessProvider metaAccess, Assumptions assumptions) { - assert concretes.size() == 1 && inlineableElements.length == 1 && ptypes.size() > 1 && !shouldFallbackToInvoke() && notRecordedTypeProbability == 0; - - BeginNode calleeEntryNode = graph.add(new BeginNode()); - - BeginNode unknownTypeSux = createUnknownTypeSuccessor(graph); - BeginNode[] successors = new BeginNode[]{calleeEntryNode, unknownTypeSux}; - createDispatchOnTypeBeforeInvoke(graph, successors, false, metaAccess); - - calleeEntryNode.setNext(invoke.asNode()); - - inline(invoke, methodAt(0), inlineableElementAt(0), assumptions, false); - } - - private boolean createDispatchOnTypeBeforeInvoke(StructuredGraph graph, BeginNode[] successors, boolean invokeIsOnlySuccessor, MetaAccessProvider metaAccess) { - assert ptypes.size() >= 1; - ValueNode nonNullReceiver = nonNullReceiver(invoke); - Kind hubKind = ((MethodCallTargetNode) invoke.callTarget()).targetMethod().getDeclaringClass().getEncoding(Representation.ObjectHub).getKind(); - LoadHubNode hub = graph.unique(new LoadHubNode(nonNullReceiver, hubKind)); - - if (!invokeIsOnlySuccessor && chooseMethodDispatch()) { - assert successors.length == concretes.size() + 1; - assert concretes.size() > 0; - Debug.log("Method check cascade with %d methods", concretes.size()); - - ValueNode[] constantMethods = new ValueNode[concretes.size()]; - double[] probability = new double[concretes.size()]; - for (int i = 0; i < concretes.size(); ++i) { - ResolvedJavaMethod firstMethod = concretes.get(i); - Constant firstMethodConstant = firstMethod.getEncoding(); - - ValueNode firstMethodConstantNode = ConstantNode.forConstant(firstMethodConstant, metaAccess, graph); - constantMethods[i] = firstMethodConstantNode; - double concretesProbability = concretesProbabilities.get(i); - assert concretesProbability >= 0.0; - probability[i] = concretesProbability; - if (i > 0) { - double prevProbability = probability[i - 1]; - if (prevProbability == 1.0) { - probability[i] = 1.0; - } else { - probability[i] = Math.min(1.0, Math.max(0.0, probability[i] / (1.0 - prevProbability))); - } - } - } - - FixedNode lastSucc = successors[concretes.size()]; - for (int i = concretes.size() - 1; i >= 0; --i) { - LoadMethodNode method = graph.add(new LoadMethodNode(concretes.get(i), hub, constantMethods[i].getKind())); - CompareNode methodCheck = CompareNode.createCompareNode(graph, Condition.EQ, method, constantMethods[i]); - IfNode ifNode = graph.add(new IfNode(methodCheck, successors[i], lastSucc, probability[i])); - method.setNext(ifNode); - lastSucc = method; - } - - FixedWithNextNode pred = (FixedWithNextNode) invoke.asNode().predecessor(); - pred.setNext(lastSucc); - return true; - } else { - Debug.log("Type switch with %d types", concretes.size()); - } - - ResolvedJavaType[] keys = new ResolvedJavaType[ptypes.size()]; - double[] keyProbabilities = new double[ptypes.size() + 1]; - int[] keySuccessors = new int[ptypes.size() + 1]; - for (int i = 0; i < ptypes.size(); i++) { - keys[i] = ptypes.get(i).getType(); - keyProbabilities[i] = ptypes.get(i).getProbability(); - keySuccessors[i] = invokeIsOnlySuccessor ? 0 : typesToConcretes.get(i); - assert keySuccessors[i] < successors.length - 1 : "last successor is the unknownTypeSux"; - } - keyProbabilities[keyProbabilities.length - 1] = notRecordedTypeProbability; - keySuccessors[keySuccessors.length - 1] = successors.length - 1; - - TypeSwitchNode typeSwitch = graph.add(new TypeSwitchNode(hub, successors, keys, keyProbabilities, keySuccessors)); - FixedWithNextNode pred = (FixedWithNextNode) invoke.asNode().predecessor(); - pred.setNext(typeSwitch); - return false; - } - - private boolean chooseMethodDispatch() { - for (ResolvedJavaMethod concrete : concretes) { - if (!concrete.isInVirtualMethodTable()) { - return false; - } - } - - if (concretes.size() == 1 && this.notRecordedTypeProbability > 0) { - // Always chose method dispatch if there is a single concrete method and the call - // site is megamorphic. - return true; - } - - if (concretes.size() == ptypes.size()) { - // Always prefer types over methods if the number of types is smaller than the - // number of methods. - return false; - } - - return chooseMethodDispatchCostBased(); - } - - private boolean chooseMethodDispatchCostBased() { - double remainder = 1.0 - this.notRecordedTypeProbability; - double costEstimateMethodDispatch = remainder; - for (int i = 0; i < concretes.size(); ++i) { - if (i != 0) { - costEstimateMethodDispatch += remainder; - } - remainder -= concretesProbabilities.get(i); - } - - double costEstimateTypeDispatch = 0.0; - remainder = 1.0; - for (int i = 0; i < ptypes.size(); ++i) { - if (i != 0) { - costEstimateTypeDispatch += remainder; - } - remainder -= ptypes.get(i).getProbability(); - } - costEstimateTypeDispatch += notRecordedTypeProbability; - return costEstimateMethodDispatch < costEstimateTypeDispatch; - } - - private static BeginNode createInvocationBlock(StructuredGraph graph, Invoke invoke, MergeNode returnMerge, PhiNode returnValuePhi, MergeNode exceptionMerge, PhiNode exceptionObjectPhi, - boolean useForInlining) { - Invoke duplicatedInvoke = duplicateInvokeForInlining(graph, invoke, exceptionMerge, exceptionObjectPhi, useForInlining); - BeginNode calleeEntryNode = graph.add(new BeginNode()); - calleeEntryNode.setNext(duplicatedInvoke.asNode()); - - AbstractEndNode endNode = graph.add(new EndNode()); - duplicatedInvoke.setNext(endNode); - returnMerge.addForwardEnd(endNode); - - if (returnValuePhi != null) { - returnValuePhi.addInput(duplicatedInvoke.asNode()); - } - return calleeEntryNode; - } - - private static Invoke duplicateInvokeForInlining(StructuredGraph graph, Invoke invoke, MergeNode exceptionMerge, PhiNode exceptionObjectPhi, boolean useForInlining) { - Invoke result = (Invoke) invoke.asNode().copyWithInputs(); - Node callTarget = result.callTarget().copyWithInputs(); - result.asNode().replaceFirstInput(result.callTarget(), callTarget); - result.setUseForInlining(useForInlining); - - Kind kind = invoke.asNode().getKind(); - if (kind != Kind.Void) { - FrameState stateAfter = invoke.stateAfter(); - stateAfter = stateAfter.duplicate(stateAfter.bci); - stateAfter.replaceFirstInput(invoke.asNode(), result.asNode()); - result.setStateAfter(stateAfter); - } - - if (invoke instanceof InvokeWithExceptionNode) { - assert exceptionMerge != null && exceptionObjectPhi != null; - - InvokeWithExceptionNode invokeWithException = (InvokeWithExceptionNode) invoke; - ExceptionObjectNode exceptionEdge = (ExceptionObjectNode) invokeWithException.exceptionEdge(); - FrameState stateAfterException = exceptionEdge.stateAfter(); - - ExceptionObjectNode newExceptionEdge = (ExceptionObjectNode) exceptionEdge.copyWithInputs(); - // set new state (pop old exception object, push new one) - newExceptionEdge.setStateAfter(stateAfterException.duplicateModified(stateAfterException.bci, stateAfterException.rethrowException(), Kind.Object, newExceptionEdge)); - - AbstractEndNode endNode = graph.add(new EndNode()); - newExceptionEdge.setNext(endNode); - exceptionMerge.addForwardEnd(endNode); - exceptionObjectPhi.addInput(newExceptionEdge); - - ((InvokeWithExceptionNode) result).setExceptionEdge(newExceptionEdge); - } - return result; - } - - @Override - public void tryToDevirtualizeInvoke(MetaAccessProvider metaAccess, Assumptions assumptions) { - if (hasSingleMethod()) { - devirtualizeWithTypeSwitch(graph(), InvokeKind.Special, concretes.get(0), metaAccess); - } else { - tryToDevirtualizeMultipleMethods(graph(), metaAccess); - } - } - - private void tryToDevirtualizeMultipleMethods(StructuredGraph graph, MetaAccessProvider metaAccess) { - MethodCallTargetNode methodCallTarget = (MethodCallTargetNode) invoke.callTarget(); - if (methodCallTarget.invokeKind() == InvokeKind.Interface) { - ResolvedJavaMethod targetMethod = methodCallTarget.targetMethod(); - ResolvedJavaType leastCommonType = getLeastCommonType(); - // check if we have a common base type that implements the interface -> in that case - // we have a vtable entry for the interface method and can use a less expensive - // virtual call - if (!leastCommonType.isInterface() && targetMethod.getDeclaringClass().isAssignableFrom(leastCommonType)) { - ResolvedJavaMethod baseClassTargetMethod = leastCommonType.resolveMethod(targetMethod); - if (baseClassTargetMethod != null) { - devirtualizeWithTypeSwitch(graph, InvokeKind.Virtual, leastCommonType.resolveMethod(targetMethod), metaAccess); - } - } - } - } - - private void devirtualizeWithTypeSwitch(StructuredGraph graph, InvokeKind kind, ResolvedJavaMethod target, MetaAccessProvider metaAccess) { - BeginNode invocationEntry = graph.add(new BeginNode()); - BeginNode unknownTypeSux = createUnknownTypeSuccessor(graph); - BeginNode[] successors = new BeginNode[]{invocationEntry, unknownTypeSux}; - createDispatchOnTypeBeforeInvoke(graph, successors, true, metaAccess); - - invocationEntry.setNext(invoke.asNode()); - ValueNode receiver = ((MethodCallTargetNode) invoke.callTarget()).receiver(); - GuardedValueNode anchoredReceiver = createAnchoredReceiver(graph, invocationEntry, target.getDeclaringClass(), receiver, false); - invoke.callTarget().replaceFirstInput(receiver, anchoredReceiver); - replaceInvokeCallTarget(invoke, graph, kind, target); - } - - private static BeginNode createUnknownTypeSuccessor(StructuredGraph graph) { - return BeginNode.begin(graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.TypeCheckedInliningViolated))); - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(shouldFallbackToInvoke() ? "megamorphic" : "polymorphic"); - builder.append(", "); - builder.append(concretes.size()); - builder.append(" methods [ "); - for (int i = 0; i < concretes.size(); i++) { - builder.append(MetaUtil.format(" %H.%n(%p):%r", concretes.get(i))); - } - builder.append(" ], "); - builder.append(ptypes.size()); - builder.append(" type checks [ "); - for (int i = 0; i < ptypes.size(); i++) { - builder.append(" "); - builder.append(ptypes.get(i).getType().getName()); - builder.append(ptypes.get(i).getProbability()); - } - builder.append(" ]"); - return builder.toString(); - } - } - - /** - * Represents an inlining opportunity where the current class hierarchy leads to a monomorphic - * target method, but for which an assumption has to be registered because of non-final classes. - */ - private static class AssumptionInlineInfo extends ExactInlineInfo { - - private final Assumption takenAssumption; - - public AssumptionInlineInfo(Invoke invoke, ResolvedJavaMethod concrete, Assumption takenAssumption) { - super(invoke, concrete); - this.takenAssumption = takenAssumption; - } - - @Override - public void inline(Providers providers, Assumptions assumptions) { - assumptions.record(takenAssumption); - super.inline(providers, assumptions); - } - - @Override - public void tryToDevirtualizeInvoke(MetaAccessProvider metaAccess, Assumptions assumptions) { - assumptions.record(takenAssumption); - replaceInvokeCallTarget(invoke, graph(), InvokeKind.Special, concrete); - } - - @Override - public String toString() { - return "assumption " + MetaUtil.format("%H.%n(%p):%r", concrete); - } - } - - /** - * Determines if inlining is possible at the given invoke node. - * - * @param invoke the invoke that should be inlined - * @return an instance of InlineInfo, or null if no inlining is possible at the given invoke - */ - public static InlineInfo getInlineInfo(InliningData data, Invoke invoke, int maxNumberOfMethods, Replacements replacements, Assumptions assumptions, OptimisticOptimizations optimisticOpts) { - if (!checkInvokeConditions(invoke)) { - return null; - } - MethodCallTargetNode callTarget = (MethodCallTargetNode) invoke.callTarget(); - ResolvedJavaMethod targetMethod = callTarget.targetMethod(); - - if (callTarget.invokeKind() == InvokeKind.Special || targetMethod.canBeStaticallyBound()) { - return getExactInlineInfo(data, invoke, replacements, optimisticOpts, targetMethod); - } - - assert callTarget.invokeKind() == InvokeKind.Virtual || callTarget.invokeKind() == InvokeKind.Interface; - - ResolvedJavaType holder = targetMethod.getDeclaringClass(); - if (!(callTarget.receiver().stamp() instanceof ObjectStamp)) { - return null; - } - ObjectStamp receiverStamp = (ObjectStamp) callTarget.receiver().stamp(); - if (receiverStamp.alwaysNull()) { - // Don't inline if receiver is known to be null - return null; - } - if (receiverStamp.type() != null) { - // the invoke target might be more specific than the holder (happens after inlining: - // parameters lose their declared type...) - ResolvedJavaType receiverType = receiverStamp.type(); - if (receiverType != null && holder.isAssignableFrom(receiverType)) { - holder = receiverType; - if (receiverStamp.isExactType()) { - assert targetMethod.getDeclaringClass().isAssignableFrom(holder) : holder + " subtype of " + targetMethod.getDeclaringClass() + " for " + targetMethod; - ResolvedJavaMethod resolvedMethod = holder.resolveMethod(targetMethod); - if (resolvedMethod != null) { - return getExactInlineInfo(data, invoke, replacements, optimisticOpts, resolvedMethod); - } - } - } - } - - if (holder.isArray()) { - // arrays can be treated as Objects - ResolvedJavaMethod resolvedMethod = holder.resolveMethod(targetMethod); - if (resolvedMethod != null) { - return getExactInlineInfo(data, invoke, replacements, optimisticOpts, resolvedMethod); - } - } - - if (assumptions.useOptimisticAssumptions()) { - ResolvedJavaType uniqueSubtype = holder.findUniqueConcreteSubtype(); - if (uniqueSubtype != null) { - ResolvedJavaMethod resolvedMethod = uniqueSubtype.resolveMethod(targetMethod); - if (resolvedMethod != null) { - return getAssumptionInlineInfo(data, invoke, replacements, optimisticOpts, resolvedMethod, new Assumptions.ConcreteSubtype(holder, uniqueSubtype)); - } - } - - ResolvedJavaMethod concrete = holder.findUniqueConcreteMethod(targetMethod); - if (concrete != null) { - return getAssumptionInlineInfo(data, invoke, replacements, optimisticOpts, concrete, new Assumptions.ConcreteMethod(targetMethod, holder, concrete)); - } - } - - // type check based inlining - return getTypeCheckedInlineInfo(data, invoke, maxNumberOfMethods, replacements, targetMethod, optimisticOpts); - } - - private static InlineInfo getAssumptionInlineInfo(InliningData data, Invoke invoke, Replacements replacements, OptimisticOptimizations optimisticOpts, ResolvedJavaMethod concrete, - Assumption takenAssumption) { - assert !concrete.isAbstract(); - if (!checkTargetConditions(data, replacements, invoke, concrete, optimisticOpts)) { - return null; - } - return new AssumptionInlineInfo(invoke, concrete, takenAssumption); - } - - private static InlineInfo getExactInlineInfo(InliningData data, Invoke invoke, Replacements replacements, OptimisticOptimizations optimisticOpts, ResolvedJavaMethod targetMethod) { - assert !targetMethod.isAbstract(); - if (!checkTargetConditions(data, replacements, invoke, targetMethod, optimisticOpts)) { - return null; - } - return new ExactInlineInfo(invoke, targetMethod); - } - - private static InlineInfo getTypeCheckedInlineInfo(InliningData data, Invoke invoke, int maxNumberOfMethods, Replacements replacements, ResolvedJavaMethod targetMethod, - OptimisticOptimizations optimisticOpts) { - JavaTypeProfile typeProfile; - ValueNode receiver = invoke.callTarget().arguments().get(0); - if (receiver instanceof TypeProfileProxyNode) { - TypeProfileProxyNode typeProfileProxyNode = (TypeProfileProxyNode) receiver; - typeProfile = typeProfileProxyNode.getProfile(); - } else { - return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "no type profile exists"); - } - - ProfiledType[] ptypes = typeProfile.getTypes(); - if (ptypes == null || ptypes.length <= 0) { - return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "no types in profile"); - } - - double notRecordedTypeProbability = typeProfile.getNotRecordedProbability(); - if (ptypes.length == 1 && notRecordedTypeProbability == 0) { - if (!optimisticOpts.inlineMonomorphicCalls()) { - return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "inlining monomorphic calls is disabled"); - } - - ResolvedJavaType type = ptypes[0].getType(); - assert type.isArray() || !type.isAbstract(); - ResolvedJavaMethod concrete = type.resolveMethod(targetMethod); - if (!checkTargetConditions(data, replacements, invoke, concrete, optimisticOpts)) { - return null; - } - return new TypeGuardInlineInfo(invoke, concrete, type); - } else { - invoke.setPolymorphic(true); - - if (!optimisticOpts.inlinePolymorphicCalls() && notRecordedTypeProbability == 0) { - return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "inlining polymorphic calls is disabled (%d types)", ptypes.length); - } - if (!optimisticOpts.inlineMegamorphicCalls() && notRecordedTypeProbability > 0) { - // due to filtering impossible types, notRecordedTypeProbability can be > 0 although - // the number of types is lower than what can be recorded in a type profile - return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "inlining megamorphic calls is disabled (%d types, %f %% not recorded types)", ptypes.length, - notRecordedTypeProbability * 100); - } - - // Find unique methods and their probabilities. - ArrayList concreteMethods = new ArrayList<>(); - ArrayList concreteMethodsProbabilities = new ArrayList<>(); - for (int i = 0; i < ptypes.length; i++) { - ResolvedJavaMethod concrete = ptypes[i].getType().resolveMethod(targetMethod); - if (concrete == null) { - return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "could not resolve method"); - } - int index = concreteMethods.indexOf(concrete); - double curProbability = ptypes[i].getProbability(); - if (index < 0) { - index = concreteMethods.size(); - concreteMethods.add(concrete); - concreteMethodsProbabilities.add(curProbability); - } else { - concreteMethodsProbabilities.set(index, concreteMethodsProbabilities.get(index) + curProbability); - } - } - - if (concreteMethods.size() > maxNumberOfMethods) { - return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "polymorphic call with more than %d target methods", maxNumberOfMethods); - } - - // Clear methods that fall below the threshold. - if (notRecordedTypeProbability > 0) { - ArrayList newConcreteMethods = new ArrayList<>(); - ArrayList newConcreteMethodsProbabilities = new ArrayList<>(); - for (int i = 0; i < concreteMethods.size(); ++i) { - if (concreteMethodsProbabilities.get(i) >= MegamorphicInliningMinMethodProbability.getValue()) { - newConcreteMethods.add(concreteMethods.get(i)); - newConcreteMethodsProbabilities.add(concreteMethodsProbabilities.get(i)); - } - } - - if (newConcreteMethods.size() == 0) { - // No method left that is worth inlining. - return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "no methods remaining after filtering less frequent methods (%d methods previously)", - concreteMethods.size()); - } - - concreteMethods = newConcreteMethods; - concreteMethodsProbabilities = newConcreteMethodsProbabilities; - } - - // Clean out types whose methods are no longer available. - ArrayList usedTypes = new ArrayList<>(); - ArrayList typesToConcretes = new ArrayList<>(); - for (ProfiledType type : ptypes) { - ResolvedJavaMethod concrete = type.getType().resolveMethod(targetMethod); - int index = concreteMethods.indexOf(concrete); - if (index == -1) { - notRecordedTypeProbability += type.getProbability(); - } else { - assert type.getType().isArray() || !type.getType().isAbstract() : type + " " + concrete; - usedTypes.add(type); - typesToConcretes.add(index); - } - } - - if (usedTypes.size() == 0) { - // No type left that is worth checking for. - return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "no types remaining after filtering less frequent types (%d types previously)", ptypes.length); - } - - for (ResolvedJavaMethod concrete : concreteMethods) { - if (!checkTargetConditions(data, replacements, invoke, concrete, optimisticOpts)) { - return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "it is a polymorphic method call and at least one invoked method cannot be inlined"); - } - } - return new MultiTypeGuardInlineInfo(invoke, concreteMethods, concreteMethodsProbabilities, usedTypes, typesToConcretes, notRecordedTypeProbability); - } - } - - private static GuardedValueNode createAnchoredReceiver(StructuredGraph graph, GuardingNode anchor, ResolvedJavaType commonType, ValueNode receiver, boolean exact) { - return createAnchoredReceiver(graph, anchor, receiver, exact ? StampFactory.exactNonNull(commonType) : StampFactory.declaredNonNull(commonType)); - } - - private static GuardedValueNode createAnchoredReceiver(StructuredGraph graph, GuardingNode anchor, ValueNode receiver, Stamp stamp) { - // to avoid that floating reads on receiver fields float above the type check - return graph.unique(new GuardedValueNode(receiver, anchor, stamp)); - } - - // TODO (chaeubl): cleanup this method - private static boolean checkInvokeConditions(Invoke invoke) { - if (invoke.predecessor() == null || !invoke.asNode().isAlive()) { - return logNotInlinedMethod(invoke, "the invoke is dead code"); - } else if (!(invoke.callTarget() instanceof MethodCallTargetNode)) { - return logNotInlinedMethod(invoke, "the invoke has already been lowered, or has been created as a low-level node"); - } else if (((MethodCallTargetNode) invoke.callTarget()).targetMethod() == null) { - return logNotInlinedMethod(invoke, "target method is null"); - } else if (invoke.stateAfter() == null) { - // TODO (chaeubl): why should an invoke not have a state after? - return logNotInlinedMethod(invoke, "the invoke has no after state"); - } else if (!invoke.useForInlining()) { - return logNotInlinedMethod(invoke, "the invoke is marked to be not used for inlining"); - } else if (((MethodCallTargetNode) invoke.callTarget()).receiver() != null && ((MethodCallTargetNode) invoke.callTarget()).receiver().isConstant() && - ((MethodCallTargetNode) invoke.callTarget()).receiver().asConstant().isNull()) { - return logNotInlinedMethod(invoke, "receiver is null"); - } else { - return true; - } - } - - private static boolean checkTargetConditions(InliningData data, Replacements replacements, Invoke invoke, ResolvedJavaMethod method, OptimisticOptimizations optimisticOpts) { - if (method == null) { - return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "the method is not resolved"); - } else if (method.isNative() && (!Intrinsify.getValue() || !InliningUtil.canIntrinsify(replacements, method))) { - return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "it is a non-intrinsic native method"); - } else if (method.isAbstract()) { - return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "it is an abstract method"); - } else if (!method.getDeclaringClass().isInitialized()) { - return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "the method's class is not initialized"); - } else if (!method.canBeInlined()) { - return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "it is marked non-inlinable"); - } else if (data.countRecursiveInlining(method) > MaximumRecursiveInlining.getValue()) { - return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "it exceeds the maximum recursive inlining depth"); - } else if (new OptimisticOptimizations(method.getProfilingInfo()).lessOptimisticThan(optimisticOpts)) { - return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "the callee uses less optimistic optimizations than caller"); - } else { - return true; - } - } - - static MonitorExitNode findPrecedingMonitorExit(UnwindNode unwind) { - Node pred = unwind.predecessor(); - while (pred != null) { - if (pred instanceof MonitorExitNode) { - return (MonitorExitNode) pred; - } - pred = pred.predecessor(); - } - return null; - } - - /** - * Performs an actual inlining, thereby replacing the given invoke with the given inlineGraph. - * - * @param invoke the invoke that will be replaced - * @param inlineGraph the graph that the invoke will be replaced with - * @param receiverNullCheck true if a null check needs to be generated for non-static inlinings, - * false if no such check is required - */ - public static Map inline(Invoke invoke, StructuredGraph inlineGraph, boolean receiverNullCheck) { - final NodeInputList parameters = invoke.callTarget().arguments(); - FixedNode invokeNode = invoke.asNode(); - StructuredGraph graph = invokeNode.graph(); - assert inlineGraph.getGuardsStage().ordinal() >= graph.getGuardsStage().ordinal(); - assert !invokeNode.graph().isAfterFloatingReadPhase() : "inline isn't handled correctly after floating reads phase"; - - FrameState stateAfter = invoke.stateAfter(); - assert stateAfter == null || stateAfter.isAlive(); - if (receiverNullCheck && !((MethodCallTargetNode) invoke.callTarget()).isStatic()) { - nonNullReceiver(invoke); - } - - ArrayList nodes = new ArrayList<>(inlineGraph.getNodes().count()); - ArrayList returnNodes = new ArrayList<>(4); - UnwindNode unwindNode = null; - final StartNode entryPointNode = inlineGraph.start(); - FixedNode firstCFGNode = entryPointNode.next(); - if (firstCFGNode == null) { - throw new IllegalStateException("Inlined graph is in invalid state"); - } - for (Node node : inlineGraph.getNodes()) { - if (node == entryPointNode || node == entryPointNode.stateAfter() || node instanceof ParameterNode) { - // Do nothing. - } else { - nodes.add(node); - if (node instanceof ReturnNode) { - returnNodes.add((ReturnNode) node); - } else if (node instanceof UnwindNode) { - assert unwindNode == null; - unwindNode = (UnwindNode) node; - } - } - } - - final BeginNode prevBegin = BeginNode.prevBegin(invokeNode); - DuplicationReplacement localReplacement = new DuplicationReplacement() { - - public Node replacement(Node node) { - if (node instanceof ParameterNode) { - return parameters.get(((ParameterNode) node).index()); - } else if (node == entryPointNode) { - return prevBegin; - } - return node; - } - }; - - assert invokeNode.successors().first() != null : invoke; - assert invokeNode.predecessor() != null; - - Map duplicates = graph.addDuplicates(nodes, inlineGraph, inlineGraph.getNodeCount(), localReplacement); - FixedNode firstCFGNodeDuplicate = (FixedNode) duplicates.get(firstCFGNode); - invokeNode.replaceAtPredecessor(firstCFGNodeDuplicate); - - FrameState stateAtExceptionEdge = null; - if (invoke instanceof InvokeWithExceptionNode) { - InvokeWithExceptionNode invokeWithException = ((InvokeWithExceptionNode) invoke); - if (unwindNode != null) { - assert unwindNode.predecessor() != null; - assert invokeWithException.exceptionEdge().successors().count() == 1; - ExceptionObjectNode obj = (ExceptionObjectNode) invokeWithException.exceptionEdge(); - stateAtExceptionEdge = obj.stateAfter(); - UnwindNode unwindDuplicate = (UnwindNode) duplicates.get(unwindNode); - obj.replaceAtUsages(unwindDuplicate.exception()); - unwindDuplicate.clearInputs(); - Node n = obj.next(); - obj.setNext(null); - unwindDuplicate.replaceAndDelete(n); - } else { - invokeWithException.killExceptionEdge(); - } - - // get rid of memory kill - BeginNode begin = invokeWithException.next(); - if (begin instanceof KillingBeginNode) { - BeginNode newBegin = new BeginNode(); - graph.addAfterFixed(begin, graph.add(newBegin)); - begin.replaceAtUsages(newBegin); - graph.removeFixed(begin); - } - } else { - if (unwindNode != null) { - UnwindNode unwindDuplicate = (UnwindNode) duplicates.get(unwindNode); - DeoptimizeNode deoptimizeNode = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.NotCompiledExceptionHandler)); - unwindDuplicate.replaceAndDelete(deoptimizeNode); - } - } - - if (stateAfter != null) { - processFrameStates(invoke, inlineGraph, duplicates, stateAtExceptionEdge); - int callerLockDepth = stateAfter.nestedLockDepth(); - if (callerLockDepth != 0) { - for (MonitorIdNode original : inlineGraph.getNodes(MonitorIdNode.class)) { - MonitorIdNode monitor = (MonitorIdNode) duplicates.get(original); - monitor.setLockDepth(monitor.getLockDepth() + callerLockDepth); - } - } - } else { - assert checkContainsOnlyInvalidOrAfterFrameState(duplicates); - } - if (!returnNodes.isEmpty()) { - FixedNode n = invoke.next(); - invoke.setNext(null); - if (returnNodes.size() == 1) { - ReturnNode returnNode = (ReturnNode) duplicates.get(returnNodes.get(0)); - Node returnValue = returnNode.result(); - invokeNode.replaceAtUsages(returnValue); - returnNode.clearInputs(); - returnNode.replaceAndDelete(n); - } else { - ArrayList returnDuplicates = new ArrayList<>(returnNodes.size()); - for (ReturnNode returnNode : returnNodes) { - returnDuplicates.add((ReturnNode) duplicates.get(returnNode)); - } - MergeNode merge = graph.add(new MergeNode()); - merge.setStateAfter(stateAfter); - ValueNode returnValue = mergeReturns(merge, returnDuplicates); - invokeNode.replaceAtUsages(returnValue); - merge.setNext(n); - } - } - - invokeNode.replaceAtUsages(null); - GraphUtil.killCFG(invokeNode); - - return duplicates; - } - - protected static void processFrameStates(Invoke invoke, StructuredGraph inlineGraph, Map duplicates, FrameState stateAtExceptionEdge) { - FrameState stateAtReturn = invoke.stateAfter(); - FrameState outerFrameState = null; - Kind invokeReturnKind = invoke.asNode().getKind(); - for (FrameState original : inlineGraph.getNodes(FrameState.class)) { - FrameState frameState = (FrameState) duplicates.get(original); - if (frameState != null && frameState.isAlive()) { - if (frameState.bci == BytecodeFrame.AFTER_BCI) { - /* - * pop return kind from invoke's stateAfter and replace with this frameState's - * return value (top of stack) - */ - FrameState stateAfterReturn = stateAtReturn; - if (invokeReturnKind != Kind.Void && frameState.stackSize() > 0 && stateAfterReturn.stackAt(0) != frameState.stackAt(0)) { - stateAfterReturn = stateAtReturn.duplicateModified(invokeReturnKind, frameState.stackAt(0)); - } - frameState.replaceAndDelete(stateAfterReturn); - } else if (stateAtExceptionEdge != null && isStateAfterException(frameState)) { - /* - * pop exception object from invoke's stateAfter and replace with this - * frameState's exception object (top of stack) - */ - FrameState stateAfterException = stateAtExceptionEdge; - if (frameState.stackSize() > 0 && stateAtExceptionEdge.stackAt(0) != frameState.stackAt(0)) { - stateAfterException = stateAtExceptionEdge.duplicateModified(Kind.Object, frameState.stackAt(0)); - } - frameState.replaceAndDelete(stateAfterException); - } else if (frameState.bci == BytecodeFrame.UNWIND_BCI || frameState.bci == BytecodeFrame.AFTER_EXCEPTION_BCI) { - handleMissingAfterExceptionFrameState(frameState); - } else { - // only handle the outermost frame states - if (frameState.outerFrameState() == null) { - assert frameState.bci != BytecodeFrame.BEFORE_BCI : frameState; - assert frameState.bci == BytecodeFrame.INVALID_FRAMESTATE_BCI || frameState.method().equals(inlineGraph.method()); - assert frameState.bci != BytecodeFrame.AFTER_EXCEPTION_BCI && frameState.bci != BytecodeFrame.BEFORE_BCI && frameState.bci != BytecodeFrame.AFTER_EXCEPTION_BCI && - frameState.bci != BytecodeFrame.UNWIND_BCI : frameState.bci; - if (outerFrameState == null) { - outerFrameState = stateAtReturn.duplicateModified(invoke.bci(), stateAtReturn.rethrowException(), invokeReturnKind); - outerFrameState.setDuringCall(true); - } - frameState.setOuterFrameState(outerFrameState); - } - } - } - } - } - - private static boolean isStateAfterException(FrameState frameState) { - return frameState.bci == BytecodeFrame.AFTER_EXCEPTION_BCI || (frameState.bci == BytecodeFrame.UNWIND_BCI && !frameState.method().isSynchronized()); - } - - protected static void handleMissingAfterExceptionFrameState(FrameState nonReplaceableFrameState) { - Graph graph = nonReplaceableFrameState.graph(); - NodeWorkList workList = graph.createNodeWorkList(); - workList.add(nonReplaceableFrameState); - for (Node node : workList) { - FrameState fs = (FrameState) node; - for (Node usage : fs.usages().snapshot()) { - if (!usage.isAlive()) { - continue; - } - if (usage instanceof FrameState) { - workList.add(usage); - } else { - StateSplit stateSplit = (StateSplit) usage; - FixedNode fixedStateSplit = stateSplit.asNode(); - if (fixedStateSplit instanceof MergeNode) { - MergeNode merge = (MergeNode) fixedStateSplit; - while (merge.isAlive()) { - AbstractEndNode end = merge.forwardEnds().first(); - DeoptimizeNode deoptimizeNode = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.NotCompiledExceptionHandler)); - end.replaceAtPredecessor(deoptimizeNode); - GraphUtil.killCFG(end); - } - } else { - FixedNode deoptimizeNode = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.NotCompiledExceptionHandler)); - if (fixedStateSplit instanceof BeginNode) { - deoptimizeNode = BeginNode.begin(deoptimizeNode); - } - fixedStateSplit.replaceAtPredecessor(deoptimizeNode); - GraphUtil.killCFG(fixedStateSplit); - } - } - } - } - } - - public static ValueNode mergeReturns(MergeNode merge, List returnNodes) { - PhiNode returnValuePhi = null; - - for (ReturnNode returnNode : returnNodes) { - // create and wire up a new EndNode - EndNode endNode = merge.graph().add(new EndNode()); - merge.addForwardEnd(endNode); - - if (returnNode.result() != null) { - if (returnValuePhi == null) { - returnValuePhi = merge.graph().addWithoutUnique(new ValuePhiNode(returnNode.result().stamp().unrestricted(), merge)); - } - returnValuePhi.addInput(returnNode.result()); - } - returnNode.clearInputs(); - returnNode.replaceAndDelete(endNode); - - } - return returnValuePhi; - } - - private static boolean checkContainsOnlyInvalidOrAfterFrameState(Map duplicates) { - for (Node node : duplicates.values()) { - if (node instanceof FrameState) { - FrameState frameState = (FrameState) node; - assert frameState.bci == BytecodeFrame.AFTER_BCI || frameState.bci == BytecodeFrame.INVALID_FRAMESTATE_BCI : node.toString(Verbosity.Debugger); - } - } - return true; - } - - /** - * Gets the receiver for an invoke, adding a guard if necessary to ensure it is non-null. - */ - public static ValueNode nonNullReceiver(Invoke invoke) { - MethodCallTargetNode callTarget = (MethodCallTargetNode) invoke.callTarget(); - assert !callTarget.isStatic() : callTarget.targetMethod(); - StructuredGraph graph = callTarget.graph(); - ValueNode firstParam = callTarget.arguments().get(0); - if (firstParam.getKind() == Kind.Object && !StampTool.isObjectNonNull(firstParam)) { - IsNullNode condition = graph.unique(new IsNullNode(firstParam)); - Stamp stamp = firstParam.stamp().join(objectNonNull()); - GuardingPiNode nonNullReceiver = graph.add(new GuardingPiNode(firstParam, condition, true, NullCheckException, InvalidateReprofile, stamp)); - graph.addBeforeFixed(invoke.asNode(), nonNullReceiver); - callTarget.replaceFirstInput(firstParam, nonNullReceiver); - return nonNullReceiver; - } - return firstParam; - } - - public static boolean canIntrinsify(Replacements replacements, ResolvedJavaMethod target) { - return getIntrinsicGraph(replacements, target) != null || getMacroNodeClass(replacements, target) != null; - } - - public static StructuredGraph getIntrinsicGraph(Replacements replacements, ResolvedJavaMethod target) { - return replacements.getMethodSubstitution(target); - } - - public static Class getMacroNodeClass(Replacements replacements, ResolvedJavaMethod target) { - return replacements.getMacroSubstitution(target); - } - - public static FixedWithNextNode inlineMacroNode(Invoke invoke, ResolvedJavaMethod concrete, Class macroNodeClass) throws GraalInternalError { - StructuredGraph graph = invoke.asNode().graph(); - if (!concrete.equals(((MethodCallTargetNode) invoke.callTarget()).targetMethod())) { - assert ((MethodCallTargetNode) invoke.callTarget()).invokeKind() != InvokeKind.Static; - InliningUtil.replaceInvokeCallTarget(invoke, graph, InvokeKind.Special, concrete); - } - - FixedWithNextNode macroNode = createMacroNodeInstance(macroNodeClass, invoke); - - CallTargetNode callTarget = invoke.callTarget(); - if (invoke instanceof InvokeNode) { - graph.replaceFixedWithFixed((InvokeNode) invoke, graph.add(macroNode)); - } else { - InvokeWithExceptionNode invokeWithException = (InvokeWithExceptionNode) invoke; - invokeWithException.killExceptionEdge(); - graph.replaceSplitWithFixed(invokeWithException, graph.add(macroNode), invokeWithException.next()); - } - GraphUtil.killWithUnusedFloatingInputs(callTarget); - return macroNode; - } - - private static FixedWithNextNode createMacroNodeInstance(Class macroNodeClass, Invoke invoke) throws GraalInternalError { - try { - return macroNodeClass.getConstructor(Invoke.class).newInstance(invoke); - } catch (ReflectiveOperationException | IllegalArgumentException | SecurityException e) { - throw new GraalGraphInternalError(e).addContext(invoke.asNode()).addContext("macroSubstitution", macroNodeClass); - } - } -} diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/ProfileCompiledMethodsPhase.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/ProfileCompiledMethodsPhase.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/ProfileCompiledMethodsPhase.java Sat May 03 21:46:35 2014 +0200 @@ -23,6 +23,7 @@ package com.oracle.graal.phases.common; import java.util.*; +import java.util.function.*; import com.oracle.graal.compiler.common.cfg.*; import com.oracle.graal.graph.*; @@ -33,7 +34,6 @@ import com.oracle.graal.nodes.extended.*; import com.oracle.graal.nodes.java.*; import com.oracle.graal.nodes.spi.*; -import com.oracle.graal.nodes.util.*; import com.oracle.graal.nodes.virtual.*; import com.oracle.graal.phases.*; import com.oracle.graal.phases.graph.*; @@ -63,14 +63,13 @@ @Override protected void run(StructuredGraph graph) { - ComputeProbabilityClosure closure = new ComputeProbabilityClosure(graph); - NodesToDoubles probabilities = closure.apply(); + ToDoubleFunction probabilities = new FixedNodeProbabilityCache(); SchedulePhase schedule = new SchedulePhase(); schedule.apply(graph, false); ControlFlowGraph cfg = ControlFlowGraph.compute(graph, true, true, true, true); for (Loop loop : cfg.getLoops()) { - double loopProbability = probabilities.get(loop.header.getBeginNode()); + double loopProbability = probabilities.applyAsDouble(loop.header.getBeginNode()); if (loopProbability > (1D / Integer.MAX_VALUE)) { addSectionCounters(loop.header.getBeginNode(), loop.blocks, loop.children, schedule, probabilities); } @@ -93,12 +92,13 @@ } } - private static void addSectionCounters(FixedWithNextNode start, Collection sectionBlocks, Collection> childLoops, SchedulePhase schedule, NodesToDoubles probabilities) { + private static void addSectionCounters(FixedWithNextNode start, Collection sectionBlocks, Collection> childLoops, SchedulePhase schedule, + ToDoubleFunction probabilities) { HashSet blocks = new HashSet<>(sectionBlocks); for (Loop loop : childLoops) { blocks.removeAll(loop.blocks); } - double weight = getSectionWeight(schedule, probabilities, blocks) / probabilities.get(start); + double weight = getSectionWeight(schedule, probabilities, blocks) / probabilities.applyAsDouble(start); DynamicCounterNode.addCounterBefore(GROUP_NAME, sectionHead(start), (long) weight, true, start.next()); if (WITH_INVOKE_FREE_SECTIONS && !hasInvoke(blocks)) { DynamicCounterNode.addCounterBefore(GROUP_NAME_WITHOUT, sectionHead(start), (long) weight, true, start.next()); @@ -113,10 +113,10 @@ } } - private static double getSectionWeight(SchedulePhase schedule, NodesToDoubles probabilities, Collection blocks) { + private static double getSectionWeight(SchedulePhase schedule, ToDoubleFunction probabilities, Collection blocks) { double count = 0; for (Block block : blocks) { - double blockProbability = probabilities.get(block.getBeginNode()); + double blockProbability = probabilities.applyAsDouble(block.getBeginNode()); for (ScheduledNode node : schedule.getBlockToNodesMap().get(block)) { count += blockProbability * getNodeWeight(node); } diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/TailDuplicationPhase.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/TailDuplicationPhase.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/TailDuplicationPhase.java Sat May 03 21:46:35 2014 +0200 @@ -25,6 +25,7 @@ import static com.oracle.graal.compiler.common.GraalOptions.*; import java.util.*; +import java.util.function.*; import com.oracle.graal.compiler.common.*; import com.oracle.graal.compiler.common.type.*; @@ -153,13 +154,15 @@ @Override protected void run(StructuredGraph graph, PhaseContext phaseContext) { - NodesToDoubles nodeProbabilities = new ComputeProbabilityClosure(graph).apply(); + if (graph.hasNode(MergeNode.class)) { + ToDoubleFunction nodeProbabilities = new FixedNodeProbabilityCache(); - // A snapshot is taken here, so that new MergeNode instances aren't considered for tail - // duplication. - for (MergeNode merge : graph.getNodes(MergeNode.class).snapshot()) { - if (!(merge instanceof LoopBeginNode) && nodeProbabilities.get(merge) >= TailDuplicationProbability.getValue()) { - tailDuplicate(merge, DEFAULT_DECISION, null, phaseContext, canonicalizer); + // A snapshot is taken here, so that new MergeNode instances aren't considered for tail + // duplication. + for (MergeNode merge : graph.getNodes(MergeNode.class).snapshot()) { + if (!(merge instanceof LoopBeginNode) && nodeProbabilities.applyAsDouble(merge) >= TailDuplicationProbability.getValue()) { + tailDuplicate(merge, DEFAULT_DECISION, null, phaseContext, canonicalizer); + } } } } diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/BaseReduction.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/BaseReduction.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/BaseReduction.java Sat May 03 21:46:35 2014 +0200 @@ -44,11 +44,16 @@ * disadvantages. *

* - * *

* This class makes available little more than a few fields and a few utility methods used * throughout the remaining components making up control-flow sensitive reductions. *

+ * + *

+ * The laundry-list of all flow-sensitive reductions is summarized in + * {@link com.oracle.graal.phases.common.cfs.FlowSensitiveReduction} + *

+ * */ public abstract class BaseReduction extends PostOrderNodeIterator { @@ -83,6 +88,12 @@ this.deoptReason = deoptReason; } + /* + * TODO Actually, we want to emit instructions to signal "should-not-reach-here". An + * imperfect substitute (as done here) is emitting FixedGuard(false). + * "should-not-reach-here" would be better for the runtime error it raises, thus pointing to + * a bug in FlowSensitiveReduction (the code was reachable, after all). + */ public void doRewrite(LogicNode falseConstant) { StructuredGraph graph = fixed.graph(); // have to insert a FixedNode other than a ControlSinkNode diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/CheckCastReduction.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/CheckCastReduction.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/CheckCastReduction.java Sat May 03 21:46:35 2014 +0200 @@ -41,6 +41,11 @@ * {@link com.oracle.graal.nodes.java.CheckCastNode}. *

* + *

+ * The laundry-list of all flow-sensitive reductions is summarized in + * {@link com.oracle.graal.phases.common.cfs.FlowSensitiveReduction} + *

+ * * @see #visitCheckCastNode(com.oracle.graal.nodes.java.CheckCastNode) */ public abstract class CheckCastReduction extends GuardingPiReduction { @@ -51,17 +56,37 @@ /** *

- * This phase is able to refine the types of reference-values at use sites provided a - * {@link com.oracle.graal.nodes.extended.GuardingNode GuardingNode} is available witnessing - * that fact. + * Upon visiting a {@link com.oracle.graal.nodes.java.CheckCastNode}, based on flow-sensitive + * conditions, we need to determine whether: + *

    + *
  • it is redundant (in which case it should be simplified), or
  • + *
  • flow-sensitive information can be gained from it. "Gain information from it" requires + * lowering the {@link com.oracle.graal.nodes.java.CheckCastNode} such that a + * {@link com.oracle.graal.nodes.extended.GuardingNode GuardingNode} becomes available.
  • + *
*

* *

- * This method turns non-redundant {@link com.oracle.graal.nodes.java.CheckCastNode}s into - * {@link com.oracle.graal.nodes.GuardingPiNode}s. Once such lowering has been performed (during - * run N of this phase) follow-up runs attempt to further simplify the resulting node, see - * {@link EquationalReasoner#downcastGuardingPiNode(com.oracle.graal.nodes.GuardingPiNode, Witness)} - * and {@link #visitGuardingPiNode(com.oracle.graal.nodes.GuardingPiNode)} + * This method realizes the above by testing first for situations that require less work: + *

    + *
  1. the stamp of the subject deems the check-cast redundant or unsatisfiable (ie, + * always-succeeds or always-fails). A previous round of canonicalization takes care of this + * situation, however it can also arise due to consecutive runs of + * {@link com.oracle.graal.phases.common.cfs.FlowSensitiveReductionPhase} without intervening + * {@link com.oracle.graal.phases.common.CanonicalizerPhase canonicalization}.
  2. + *
  3. + * flow-sensitive information reveals the subject to be null, trivially fulfilling the + * check-cast.
  4. + *
  5. + * flow-sensitive information reveals the subject to be narrower than it stamp says. If the + * narrower ("downcasted") value fulfills the check-cast, the check-cast is removed.
  6. + *
  7. + * otherwise the check-cast provides additional flow-sensitive information. For that, a + * {@link com.oracle.graal.nodes.FixedGuardNode} is needed, as described in + * {@link #lowerCheckCastAnchorFriendlyWay(com.oracle.graal.nodes.java.CheckCastNode, com.oracle.graal.nodes.ValueNode)} + * . Please notice this lowering is currently performed unconditionally: it might occur no + * flow-sensitive reduction is enabled down the road.
  8. + *
*

* *

@@ -155,19 +180,24 @@ * *

* Rather than tracking the CheckCastNode via {@link com.oracle.graal.phases.common.cfs.State - * State} (doing so woud add a special case because a + * State} (doing so would add a special case because a * {@link com.oracle.graal.nodes.java.CheckCastNode} isn't a - * {@link com.oracle.graal.nodes.extended.GuardingNode}) this method creates an anchor by - * lowering the CheckCastNode into a FixedGuardNode. Not the same way as done by - * {@link com.oracle.graal.nodes.java.CheckCastNode#lower(com.oracle.graal.nodes.spi.LoweringTool)} - * which lowers into a {@link com.oracle.graal.nodes.GuardingPiNode} (which is not a - * {@link com.oracle.graal.nodes.extended.GuardingNode}). + * {@link com.oracle.graal.nodes.extended.GuardingNode guarding node}) this method creates an + * anchor by lowering the CheckCastNode into a FixedGuardNode. Not the same as the + * {@link com.oracle.graal.nodes.java.CheckCastNode#lower(com.oracle.graal.nodes.spi.LoweringTool) + * lowering of a CheckCastNode} which results in a {@link com.oracle.graal.nodes.GuardingPiNode} + * (which is not a {@link com.oracle.graal.nodes.extended.GuardingNode guarding node}). *

* *

* With that, state tracking can proceed as usual. *

* + *

+ * TODO This lowering is currently performed unconditionally: it might occur no flow-sensitive + * reduction is enabled down the road + *

+ * * @see #visitCheckCastNode(com.oracle.graal.nodes.java.CheckCastNode) * */ diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/EquationalReasoner.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/EquationalReasoner.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/EquationalReasoner.java Sat May 03 21:46:35 2014 +0200 @@ -219,7 +219,7 @@ if (n == null) { return null; } - assert !(n instanceof GuardNode) : "This phase not yet ready to run during MidTier"; + assert !(n instanceof GuardNode) : "This phase not intended to run during MidTier"; if (!(n instanceof ValueNode)) { return n; } diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/FixedGuardReduction.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/FixedGuardReduction.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/FixedGuardReduction.java Sat May 03 21:46:35 2014 +0200 @@ -33,6 +33,11 @@ * This class implements control-flow sensitive reductions for * {@link com.oracle.graal.nodes.FixedGuardNode}. *

+ * + *

+ * The laundry-list of all flow-sensitive reductions is summarized in + * {@link com.oracle.graal.phases.common.cfs.FlowSensitiveReduction} + *

* * @see #visitFixedGuardNode(com.oracle.graal.nodes.FixedGuardNode) */ @@ -43,15 +48,33 @@ } /** - * In case the condition is constant, - * {@link com.oracle.graal.nodes.FixedGuardNode#simplify(com.oracle.graal.graph.spi.SimplifierTool) - * FixedGuardNode#simplify(SimplifierTool)} will eventually remove the - * {@link com.oracle.graal.nodes.FixedGuardNode} ("always succeeds") or kill the code that - * should be killed ("always fails"). + *

+ * Upon visiting a {@link com.oracle.graal.nodes.FixedGuardNode}, based on flow-sensitive + * conditions, we need to determine whether: + *

    + *
  • it is redundant (in which case it should be simplified), or
  • + *
  • flow-sensitive information can be gained from it.
  • + *
+ *

* *

- * The only thing we do here is tracking as true fact (from this program point onwards) the - * condition of the {@link com.oracle.graal.nodes.FixedGuardNode FixedGuardNode}. + * This method realizes the above by inspecting the + * {@link com.oracle.graal.nodes.FixedGuardNode}'s condition: + *

    + *
  1. a constant condition signals the node won't be reduced here
  2. + *
  3. the outcome of the condition can be predicted:
  4. + *
      + *
    • + * "always succeeds", after finding an equivalent (or stronger) + * {@link com.oracle.graal.nodes.extended.GuardingNode} in scope. The + * {@link com.oracle.graal.nodes.FixedGuardNode} is removed after replacing its usages with the + * existing guarding node
    • + *
    • + * "always fails", which warrants making that explicit by making the condition constant, see + * {@link #markFixedGuardNodeAlwaysFails(com.oracle.graal.nodes.FixedGuardNode)}
    • + *
    + *
  5. otherwise the condition is tracked flow-sensitively
  6. + *
*

* *

@@ -62,6 +85,8 @@ /* * A FixedGuardNode with LogicConstantNode condition is left untouched. + * `FixedGuardNode.simplify()` will eventually remove the FixedGuardNode (in case it + * "always succeeds") or kill code ("always fails"). */ if (f.condition() instanceof LogicConstantNode) { @@ -92,10 +117,10 @@ final boolean isTrue = !f.isNegated(); /* - * FixedGuardNode requires handling similar to that of GuardingPiNode, (ie the condition - * can't simply be deverbosified in place). A replacement anchor is needed, ie an anchor - * that amounts to the same combination of (negated, condition) for the FixedGuardNode at - * hand. + * A FixedGuardNode can only be removed provided a replacement anchor is found (so called + * "evidence"), ie an anchor that amounts to the same combination of (negated, condition) as + * for the FixedGuardNode at hand. Just deverbosifying the condition in place isn't + * semantics-preserving. */ // TODO what about isDependencyTainted @@ -202,7 +227,8 @@ * Porcelain method. * *

- * The `replacement` guard must be such that it implies the `old` guard. + * The `replacement` guard must be such that it implies the `old` guard. Moreover, rhe + * `replacement` guard must be in scope. *

*/ private void removeFixedGuardNode(FixedGuardNode old, GuardingNode replacement) { diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/FlowSensitiveReduction.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/FlowSensitiveReduction.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/FlowSensitiveReduction.java Sat May 03 21:46:35 2014 +0200 @@ -43,20 +43,37 @@ /** *

- * All control-flow-sensitive reductions follow the common pattern of + * In a nutshell, {@link com.oracle.graal.phases.common.cfs.FlowSensitiveReductionPhase} makes a + * single pass in dominator-based order over the graph: + *

    + *
  1. collecting properties of interest at control-splits; as well as for check-casts, + * guarding-pis, null-checks, and fixed-guards. Such flow-sensitive information is tracked via a + * dedicated {@link com.oracle.graal.phases.common.cfs.State state instance} for each control-flow + * path.
  2. + *
  3. performing rewritings that are safe at specific program-points. This comprises: *
      - *
    • Recognizing properties of interest (ie, LogicNode-s) at control-flow splits, as well as upon - * check-casts and fixed-guards.
    • - *
    • Using the information thus tracked to simplify - *
        - *
      • side-effects free expressions, via + *
      • simplification of side-effects free expressions, via * {@link com.oracle.graal.phases.common.cfs.EquationalReasoner#deverbosify(com.oracle.graal.graph.Node)} *
      • - *
      • control-flow, eg. by eliminating redundant fixed-guards and check-casts, ie which are known - * always to hold.
      • + *
      • simplification of control-flow: + *
          + *
        • + * by simplifying the input-condition to an {@link com.oracle.graal.nodes.IfNode}
        • + *
        • + * by eliminating redundant check-casts, guarding-pis, null-checks, and fixed-guards; where + * "redundancy" is determined using flow-sensitive information. In these cases, redundancy can be + * due to: + *
            + *
          • an equivalent, existing, guarding node is already in scope (thus, use it as replacement and + * remove the redundant one)
          • + *
          • "always fails" (thus, replace the node in question with FixedGuardNode(false))
          • *
          *
        • *
        + *
      • + *
      + *
    • + *
*

* * @see com.oracle.graal.phases.common.cfs.CheckCastReduction @@ -145,6 +162,9 @@ if (begin instanceof LoopExitNode) { state.clear(); + /* + * TODO return or not? (by not returning we agree it's ok to update the state as below) + */ } if (pred instanceof IfNode) { @@ -253,7 +273,7 @@ public boolean deverbosifyInputsInPlace(ValueNode parent) { boolean changed = false; for (ValueNode i : FlowUtil.distinctValueAndConditionInputs(parent)) { - assert !(i instanceof GuardNode) : "ConditionalElim shouldn't run in MidTier"; + assert !(i instanceof GuardNode) : "This phase not intended to run during MidTier"; ValueNode j = (ValueNode) reasoner.deverbosify(i); if (i != j) { changed = true; @@ -390,7 +410,7 @@ * Step 5: After special-case handling, we do our best for those FixedNode-s * where the effort to reduce their inputs might pay off. * - * Why is this useful? For example, by the time the AbstractBeginNode for an If-branch + * Why is this useful? For example, by the time the BeginNode for an If-branch * is visited (in general a ControlSplitNode), the If-condition will have gone already * through simplification (and thus potentially have been reduced to a * LogicConstantNode). @@ -407,8 +427,7 @@ paysOffToReduce = true; } - // TODO comb the remaining FixedWithNextNode subclasses, pick those with good changes of - // paying-off + // TODO comb remaining FixedWithNextNode subclasses, pick those with chances of paying-off // TODO UnsafeLoadNode takes a condition @@ -423,8 +442,7 @@ */ // TODO some nodes are GuardingNodes (eg, FixedAccessNode) we could use them to track state - // TODO others are additionally guarded (eg JavaReadNode), thus *their* guards could be - // simplified. + // TODO other nodes are guarded (eg JavaReadNode), thus *their* guards could be replaced. } @@ -498,11 +516,29 @@ } /** - * One or more arguments at `invoke` may have control-flow sensitive simplifications. In such - * case, a new {@link com.oracle.graal.nodes.java.MethodCallTargetNode MethodCallTargetNode} is - * prepared just for this callsite, consuming reduced arguments. This proves useful in - * connection with inlining, in order to specialize callees on the types of arguments other than - * the receiver (examples: multi-methods, the inlining problem, lambdas as arguments). + *

+ * For one or more `invoke` arguments, flow-sensitive information may suggest their narrowing or + * simplification. In those cases, a new + * {@link com.oracle.graal.nodes.java.MethodCallTargetNode MethodCallTargetNode} is prepared + * just for this callsite, consuming reduced arguments. + *

+ * + *

+ * Specializing the {@link com.oracle.graal.nodes.java.MethodCallTargetNode + * MethodCallTargetNode} as described above may enable two optimizations: + *

    + *
  • + * devirtualization of an + * {@link com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind#Interface} or + * {@link com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind#Virtual} callsite + * (devirtualization made possible after narrowing the type of the receiver)
  • + *
  • + * (future work) actual-argument-aware inlining, ie, to specialize callees on the types of + * arguments other than the receiver (examples: multi-methods, the inlining problem, lambdas as + * arguments).
  • + * + *
+ *

* *

* Precondition: inputs haven't been deverbosified yet. diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/FlowSensitiveReductionPhase.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/FlowSensitiveReductionPhase.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/FlowSensitiveReductionPhase.java Sat May 03 21:46:35 2014 +0200 @@ -43,6 +43,10 @@ @Override protected final void run(StructuredGraph graph, PhaseContext context) { try (Debug.Scope s = Debug.scope("FlowSensitiveReduction")) { + if (graph.isOSR()) { + Debug.log("Skipping OSR method %s", graph.method() == null ? "" : MetaUtil.format("%H.%n", graph.method())); + return; + } Debug.dump(graph, "FlowSensitiveReduction initial"); new FlowSensitiveReduction(graph.start(), new State(), context).apply(); Debug.dump(graph, "FlowSensitiveReduction done"); diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/FlowUtil.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/FlowUtil.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/FlowUtil.java Sat May 03 21:46:35 2014 +0200 @@ -23,10 +23,6 @@ package com.oracle.graal.phases.common.cfs; import com.oracle.graal.api.meta.ResolvedJavaType; -import com.oracle.graal.debug.Debug; -import com.oracle.graal.debug.DebugConfig; -import com.oracle.graal.debug.DebugConfigScope; -import com.oracle.graal.debug.internal.DebugScope; import com.oracle.graal.graph.InputType; import com.oracle.graal.graph.Node; import com.oracle.graal.graph.NodeClass; @@ -290,28 +286,4 @@ // `oldInput` if unused wil be removed in finished() } - public static StructuredGraph visualize(StructuredGraph graph, String title) { - DebugConfig debugConfig = DebugScope.getConfig(); - DebugConfig fixedConfig = Debug.fixedConfig(false, true, false, false, debugConfig.dumpHandlers(), debugConfig.output()); - try (DebugConfigScope s = Debug.setConfig(fixedConfig)) { - Debug.dump(graph, title); - - return graph; - } - } - - public static final String ANSI_RESET = "\u001B[0m"; - public static final String ANSI_BLACK = "\u001B[30m"; - public static final String ANSI_RED = "\u001B[31m"; - public static final String ANSI_GREEN = "\u001B[32m"; - public static final String ANSI_YELLOW = "\u001B[33m"; - public static final String ANSI_BLUE = "\u001B[34m"; - public static final String ANSI_PURPLE = "\u001B[35m"; - public static final String ANSI_CYAN = "\u001B[36m"; - public static final String ANSI_WHITE = "\u001B[37m"; - - public static void highlightInRed(String msg) { - System.out.println(ANSI_RED + msg + ANSI_RESET); - } - } diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/GuardingPiReduction.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/GuardingPiReduction.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/GuardingPiReduction.java Sat May 03 21:46:35 2014 +0200 @@ -37,7 +37,12 @@ * This class implements control-flow sensitive reductions for * {@link com.oracle.graal.nodes.GuardingPiNode}. *

- * + * + *

+ * The laundry-list of all flow-sensitive reductions is summarized in + * {@link com.oracle.graal.phases.common.cfs.FlowSensitiveReduction} + *

+ * * @see #visitGuardingPiNode(com.oracle.graal.nodes.GuardingPiNode) */ public abstract class GuardingPiReduction extends BaseReduction { @@ -139,6 +144,12 @@ FixedGuardNode fixedGuard = graph.add(new FixedGuardNode(envelope.condition(), envelope.getReason(), envelope.getAction(), envelope.isNegated())); graph.addBeforeFixed(envelope, fixedGuard); + /* + * + * TODO This lowering is currently performed unconditionally: it might occur no + * flow-sensitive reduction is enabled down the road + */ + if (!FlowUtil.lacksUsages(envelope)) { // not calling wrapInPiNode() because we don't want to rememberSubstitution() PiNode replacement = graph.unique(new PiNode(envelope.object(), envelope.stamp(), fixedGuard)); diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/Histogram.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/Histogram.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/Histogram.java Sat May 03 21:46:35 2014 +0200 @@ -54,7 +54,7 @@ int percentOut = (int) (numCases / casesTotal * 100); String msg = prefix + String.format("%d iters in %4d cases (%2d %%)", entry.getKey(), numCases, percentOut); if (entry.getKey() > 3) { - FlowUtil.highlightInRed(msg); + highlightInRed(msg); } else { System.out.println(msg); } @@ -67,4 +67,11 @@ return (Histogram) super.clone(); } + public static final String ANSI_RESET = "\u001B[0m"; + public static final String ANSI_RED = "\u001B[31m"; + + public static void highlightInRed(String msg) { + System.out.println(ANSI_RED + msg + ANSI_RESET); + } + } diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/ComputeInliningRelevance.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/ComputeInliningRelevance.java Sat May 03 21:46:35 2014 +0200 @@ -0,0 +1,322 @@ +/* + * Copyright (c) 2013, 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.graal.phases.common.inlining; + +import java.util.*; +import java.util.function.*; + +import com.oracle.graal.graph.*; +import com.oracle.graal.nodes.*; + +import edu.umd.cs.findbugs.annotations.*; + +public class ComputeInliningRelevance { + + private static final double EPSILON = 1d / Integer.MAX_VALUE; + private static final double UNINITIALIZED = -1D; + + private static final int EXPECTED_MIN_INVOKE_COUNT = 3; + private static final int EXPECTED_INVOKE_RATIO = 20; + private static final int EXPECTED_LOOP_COUNT = 3; + + private final StructuredGraph graph; + private final ToDoubleFunction nodeProbabilities; + + /** + * Node relevances are pre-computed for all invokes if the graph contains loops. If there are no + * loops, the computation happens lazily based on {@link #rootScope}. + */ + private IdentityHashMap nodeRelevances; + /** + * This scope is non-null if (and only if) there are no loops in the graph. In this case, the + * root scope is used to compute invoke relevances on the fly. + */ + private Scope rootScope; + + public ComputeInliningRelevance(StructuredGraph graph, ToDoubleFunction nodeProbabilities) { + this.graph = graph; + this.nodeProbabilities = nodeProbabilities; + } + + /** + * Initializes or updates the relevance computation. If there are no loops within the graph, + * most computation happens lazily. + */ + public void compute() { + rootScope = null; + if (!graph.hasLoops()) { + // fast path for the frequent case of no loops + rootScope = new Scope(graph.start(), null); + } else { + if (nodeRelevances == null) { + nodeRelevances = new IdentityHashMap<>(EXPECTED_MIN_INVOKE_COUNT + graph.getNodeCount() / EXPECTED_INVOKE_RATIO); + } + NodeWorkList workList = graph.createNodeWorkList(); + IdentityHashMap loops = new IdentityHashMap<>(EXPECTED_LOOP_COUNT); + + loops.put(null, new Scope(graph.start(), null)); + for (LoopBeginNode loopBegin : graph.getNodes(LoopBeginNode.class)) { + createLoopScope(loopBegin, loops); + } + + for (Scope scope : loops.values()) { + scope.process(workList); + } + } + } + + public double getRelevance(Invoke invoke) { + if (rootScope != null) { + return rootScope.computeInvokeRelevance(invoke); + } + assert nodeRelevances != null : "uninitialized relevance"; + return nodeRelevances.get(invoke); + } + + /** + * Determines the parent of the given loop and creates a {@link Scope} object for each one. This + * method will call itself recursively if no {@link Scope} for the parent loop exists. + */ + private Scope createLoopScope(LoopBeginNode loopBegin, IdentityHashMap loops) { + Scope scope = loops.get(loopBegin); + if (scope == null) { + final Scope parent; + // look for the parent scope + FixedNode current = loopBegin.forwardEnd(); + while (true) { + if (current.predecessor() == null) { + if (current instanceof LoopBeginNode) { + // if we reach a LoopBeginNode then we're within this loop + parent = createLoopScope((LoopBeginNode) current, loops); + break; + } else if (current instanceof StartNode) { + // we're within the outermost scope + parent = loops.get(null); + break; + } else { + assert current.getClass() == MergeNode.class : current; + // follow any path upwards - it doesn't matter which one + current = ((MergeNode) current).forwardEndAt(0); + } + } else if (current instanceof LoopExitNode) { + // if we reach a loop exit then we follow this loop and have the same parent + parent = createLoopScope(((LoopExitNode) current).loopBegin(), loops).parent; + break; + } else { + current = (FixedNode) current.predecessor(); + } + } + scope = new Scope(loopBegin, parent); + loops.put(loopBegin, scope); + } + return scope; + } + + /** + * A scope holds information for the contents of one loop or of the root of the method. It does + * not include child loops, i.e., the iteration in {@link #process(NodeWorkList)} explicitly + * excludes the nodes of child loops. + */ + private class Scope { + public final FixedNode start; + public final Scope parent; // can be null for the outermost scope + + /** + * The minimum probability along the most probable path in this scope. Computed lazily. + */ + private double fastPathMinProbability = UNINITIALIZED; + /** + * A measure of how important this scope is within its parent scope. Computed lazily. + */ + private double scopeRelevanceWithinParent = UNINITIALIZED; + + public Scope(FixedNode start, Scope parent) { + this.start = start; + this.parent = parent; + } + + @SuppressFBWarnings("FE_FLOATING_POINT_EQUALITY") + public double getFastPathMinProbability() { + if (fastPathMinProbability == UNINITIALIZED) { + fastPathMinProbability = Math.max(EPSILON, computeFastPathMinProbability(start)); + } + return fastPathMinProbability; + } + + /** + * Computes the ratio between the probabilities of the current scope's entry point and the + * parent scope's fastPathMinProbability. + */ + @SuppressFBWarnings("FE_FLOATING_POINT_EQUALITY") + public double getScopeRelevanceWithinParent() { + if (scopeRelevanceWithinParent == UNINITIALIZED) { + if (start instanceof LoopBeginNode) { + assert parent != null; + double scopeEntryProbability = nodeProbabilities.applyAsDouble(((LoopBeginNode) start).forwardEnd()); + + scopeRelevanceWithinParent = scopeEntryProbability / parent.getFastPathMinProbability(); + } else { + scopeRelevanceWithinParent = 1D; + } + } + return scopeRelevanceWithinParent; + } + + /** + * Processes all invokes in this scope by starting at the scope's start node and iterating + * all fixed nodes. Child loops are skipped by going from loop entries directly to the loop + * exits. Processing stops at loop exits of the current loop. + */ + public void process(NodeWorkList workList) { + assert !(start instanceof Invoke); + workList.addAll(start.successors()); + + for (Node current : workList) { + assert current.isAlive(); + + if (current instanceof Invoke) { + // process the invoke and queue its successors + nodeRelevances.put((FixedNode) current, computeInvokeRelevance((Invoke) current)); + workList.addAll(current.successors()); + } else if (current instanceof LoopBeginNode) { + // skip child loops by advancing over the loop exits + ((LoopBeginNode) current).loopExits().forEach(exit -> workList.add(exit.next())); + } else if (current instanceof LoopEndNode) { + // nothing to do + } else if (current instanceof LoopExitNode) { + // nothing to do + } else if (current instanceof FixedWithNextNode) { + workList.add(((FixedWithNextNode) current).next()); + } else if (current instanceof EndNode) { + workList.add(((EndNode) current).merge()); + } else if (current instanceof ControlSinkNode) { + // nothing to do + } else if (current instanceof ControlSplitNode) { + workList.addAll(current.successors()); + } else { + assert false : current; + } + } + } + + /** + * The relevance of an invoke is the ratio between the invoke's probability and the current + * scope's fastPathMinProbability, adjusted by scopeRelevanceWithinParent. + */ + public double computeInvokeRelevance(Invoke invoke) { + double invokeProbability = nodeProbabilities.applyAsDouble(invoke.asNode()); + assert !Double.isNaN(invokeProbability); + + double relevance = (invokeProbability / getFastPathMinProbability()) * Math.min(1.0, getScopeRelevanceWithinParent()); + assert !Double.isNaN(relevance); + return relevance; + } + } + + /** + * Computes the minimum probability along the most probable path within the scope. During + * iteration, the method returns immediately once a loop exit is discovered. + */ + private double computeFastPathMinProbability(FixedNode scopeStart) { + ArrayList pathBeginNodes = new ArrayList<>(); + pathBeginNodes.add(scopeStart); + double minPathProbability = nodeProbabilities.applyAsDouble(scopeStart); + boolean isLoopScope = scopeStart instanceof LoopBeginNode; + + do { + Node current = pathBeginNodes.remove(pathBeginNodes.size() - 1); + do { + if (isLoopScope && current instanceof LoopExitNode && ((LoopBeginNode) scopeStart).loopExits().contains((LoopExitNode) current)) { + return minPathProbability; + } else if (current instanceof LoopBeginNode && current != scopeStart) { + current = getMaxProbabilityLoopExit((LoopBeginNode) current, pathBeginNodes); + minPathProbability = getMinPathProbability((FixedNode) current, minPathProbability); + } else if (current instanceof ControlSplitNode) { + current = getMaxProbabilitySux((ControlSplitNode) current, pathBeginNodes); + minPathProbability = getMinPathProbability((FixedNode) current, minPathProbability); + } else { + assert current.successors().count() <= 1; + current = current.successors().first(); + } + } while (current != null); + } while (!pathBeginNodes.isEmpty()); + + return minPathProbability; + } + + private double getMinPathProbability(FixedNode current, double minPathProbability) { + return current == null ? minPathProbability : Math.min(minPathProbability, nodeProbabilities.applyAsDouble(current)); + } + + /** + * Returns the most probable successor. If multiple successors share the maximum probability, + * one is returned and the others are enqueued in pathBeginNodes. + */ + private static Node getMaxProbabilitySux(ControlSplitNode controlSplit, ArrayList pathBeginNodes) { + Node maxSux = null; + double maxProbability = 0.0; + int pathBeginCount = pathBeginNodes.size(); + + for (Node sux : controlSplit.successors()) { + double probability = controlSplit.probability((BeginNode) sux); + if (probability > maxProbability) { + maxProbability = probability; + maxSux = sux; + truncate(pathBeginNodes, pathBeginCount); + } else if (probability == maxProbability) { + pathBeginNodes.add((FixedNode) sux); + } + } + + return maxSux; + } + + /** + * Returns the most probable loop exit. If multiple successors share the maximum probability, + * one is returned and the others are enqueued in pathBeginNodes. + */ + private Node getMaxProbabilityLoopExit(LoopBeginNode loopBegin, ArrayList pathBeginNodes) { + Node maxSux = null; + double maxProbability = 0.0; + int pathBeginCount = pathBeginNodes.size(); + + for (LoopExitNode sux : loopBegin.loopExits()) { + double probability = nodeProbabilities.applyAsDouble(sux); + if (probability > maxProbability) { + maxProbability = probability; + maxSux = sux; + truncate(pathBeginNodes, pathBeginCount); + } else if (probability == maxProbability) { + pathBeginNodes.add(sux); + } + } + + return maxSux; + } + + private static void truncate(ArrayList pathBeginNodes, int pathBeginCount) { + for (int i = pathBeginNodes.size() - pathBeginCount; i > 0; i--) { + pathBeginNodes.remove(pathBeginNodes.size() - 1); + } + } +} diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningPhase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningPhase.java Sat May 03 21:46:35 2014 +0200 @@ -0,0 +1,866 @@ +/* + * 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.graal.phases.common.inlining; + +import static com.oracle.graal.compiler.common.GraalOptions.*; +import static com.oracle.graal.phases.common.inlining.InliningPhase.Options.*; + +import java.util.*; +import java.util.function.*; + +import com.oracle.graal.api.code.*; +import com.oracle.graal.api.meta.*; +import com.oracle.graal.compiler.common.*; +import com.oracle.graal.compiler.common.type.*; +import com.oracle.graal.debug.*; +import com.oracle.graal.debug.Debug.Scope; +import com.oracle.graal.graph.Graph.Mark; +import com.oracle.graal.graph.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.java.*; +import com.oracle.graal.nodes.spi.*; +import com.oracle.graal.options.*; +import com.oracle.graal.phases.common.*; +import com.oracle.graal.phases.common.inlining.InliningUtil.InlineInfo; +import com.oracle.graal.phases.common.inlining.InliningUtil.Inlineable; +import com.oracle.graal.phases.common.inlining.InliningUtil.InlineableGraph; +import com.oracle.graal.phases.common.inlining.InliningUtil.InlineableMacroNode; +import com.oracle.graal.phases.common.inlining.InliningUtil.InliningPolicy; +import com.oracle.graal.phases.graph.*; +import com.oracle.graal.phases.tiers.*; +import com.oracle.graal.phases.util.*; + +public class InliningPhase extends AbstractInliningPhase { + + static class Options { + + // @formatter:off + @Option(help = "Unconditionally inline intrinsics") + public static final OptionValue AlwaysInlineIntrinsics = new OptionValue<>(false); + // @formatter:on + } + + private final InliningPolicy inliningPolicy; + private final CanonicalizerPhase canonicalizer; + + private int inliningCount; + private int maxMethodPerInlining = Integer.MAX_VALUE; + + // Metrics + private static final DebugMetric metricInliningPerformed = Debug.metric("InliningPerformed"); + private static final DebugMetric metricInliningConsidered = Debug.metric("InliningConsidered"); + private static final DebugMetric metricInliningStoppedByMaxDesiredSize = Debug.metric("InliningStoppedByMaxDesiredSize"); + private static final DebugMetric metricInliningRuns = Debug.metric("InliningRuns"); + + public InliningPhase(CanonicalizerPhase canonicalizer) { + this(new GreedyInliningPolicy(null), canonicalizer); + } + + public InliningPhase(Map hints, CanonicalizerPhase canonicalizer) { + this(new GreedyInliningPolicy(hints), canonicalizer); + } + + public InliningPhase(InliningPolicy policy, CanonicalizerPhase canonicalizer) { + this.inliningPolicy = policy; + this.canonicalizer = canonicalizer; + } + + public void setMaxMethodsPerInlining(int max) { + maxMethodPerInlining = max; + } + + public int getInliningCount() { + return inliningCount; + } + + @Override + protected void run(final StructuredGraph graph, final HighTierContext context) { + final InliningData data = new InliningData(graph, context.getAssumptions()); + ToDoubleFunction probabilities = new FixedNodeProbabilityCache(); + + while (data.hasUnprocessedGraphs()) { + final MethodInvocation currentInvocation = data.currentInvocation(); + GraphInfo graphInfo = data.currentGraph(); + if (!currentInvocation.isRoot() && + !inliningPolicy.isWorthInlining(probabilities, context.getReplacements(), currentInvocation.callee(), data.inliningDepth(), currentInvocation.probability(), + currentInvocation.relevance(), false)) { + int remainingGraphs = currentInvocation.totalGraphs() - currentInvocation.processedGraphs(); + assert remainingGraphs > 0; + data.popGraphs(remainingGraphs); + data.popInvocation(); + } else if (graphInfo.hasRemainingInvokes() && inliningPolicy.continueInlining(graphInfo.graph())) { + processNextInvoke(data, graphInfo, context); + } else { + data.popGraph(); + if (!currentInvocation.isRoot()) { + assert currentInvocation.callee().invoke().asNode().isAlive(); + currentInvocation.incrementProcessedGraphs(); + if (currentInvocation.processedGraphs() == currentInvocation.totalGraphs()) { + data.popInvocation(); + final MethodInvocation parentInvoke = data.currentInvocation(); + try (Scope s = Debug.scope("Inlining", data.inliningContext())) { + tryToInline(probabilities, data.currentGraph(), currentInvocation, parentInvoke, data.inliningDepth() + 1, context); + } catch (Throwable e) { + throw Debug.handle(e); + } + } + } + } + } + + assert data.inliningDepth() == 0; + assert data.graphCount() == 0; + } + + /** + * Process the next invoke and enqueue all its graphs for processing. + */ + private void processNextInvoke(InliningData data, GraphInfo graphInfo, HighTierContext context) { + Invoke invoke = graphInfo.popInvoke(); + MethodInvocation callerInvocation = data.currentInvocation(); + Assumptions parentAssumptions = callerInvocation.assumptions(); + InlineInfo info = InliningUtil.getInlineInfo(data, invoke, maxMethodPerInlining, context.getReplacements(), parentAssumptions, context.getOptimisticOptimizations()); + + if (info != null) { + double invokeProbability = graphInfo.invokeProbability(invoke); + double invokeRelevance = graphInfo.invokeRelevance(invoke); + MethodInvocation calleeInvocation = data.pushInvocation(info, parentAssumptions, invokeProbability, invokeRelevance); + + for (int i = 0; i < info.numberOfMethods(); i++) { + Inlineable elem = getInlineableElement(info.methodAt(i), info.invoke(), context.replaceAssumptions(calleeInvocation.assumptions())); + info.setInlinableElement(i, elem); + if (elem instanceof InlineableGraph) { + data.pushGraph(((InlineableGraph) elem).getGraph(), invokeProbability * info.probabilityAt(i), invokeRelevance * info.relevanceAt(i)); + } else { + assert elem instanceof InlineableMacroNode; + data.pushDummyGraph(); + } + } + } + } + + private void tryToInline(ToDoubleFunction probabilities, GraphInfo callerGraphInfo, MethodInvocation calleeInfo, MethodInvocation parentInvocation, int inliningDepth, + HighTierContext context) { + InlineInfo callee = calleeInfo.callee(); + Assumptions callerAssumptions = parentInvocation.assumptions(); + + if (inliningPolicy.isWorthInlining(probabilities, context.getReplacements(), callee, inliningDepth, calleeInfo.probability(), calleeInfo.relevance(), true)) { + doInline(callerGraphInfo, calleeInfo, callerAssumptions, context); + } else if (context.getOptimisticOptimizations().devirtualizeInvokes()) { + callee.tryToDevirtualizeInvoke(context.getMetaAccess(), callerAssumptions); + } + metricInliningConsidered.increment(); + } + + private void doInline(GraphInfo callerGraphInfo, MethodInvocation calleeInfo, Assumptions callerAssumptions, HighTierContext context) { + StructuredGraph callerGraph = callerGraphInfo.graph(); + Mark markBeforeInlining = callerGraph.getMark(); + InlineInfo callee = calleeInfo.callee(); + try { + try (Scope scope = Debug.scope("doInline", callerGraph)) { + List invokeUsages = callee.invoke().asNode().usages().snapshot(); + callee.inline(new Providers(context), callerAssumptions); + callerAssumptions.record(calleeInfo.assumptions()); + metricInliningRuns.increment(); + Debug.dump(callerGraph, "after %s", callee); + + if (OptCanonicalizer.getValue()) { + Mark markBeforeCanonicalization = callerGraph.getMark(); + canonicalizer.applyIncremental(callerGraph, context, invokeUsages, markBeforeInlining); + + // process invokes that are possibly created during canonicalization + for (Node newNode : callerGraph.getNewNodes(markBeforeCanonicalization)) { + if (newNode instanceof Invoke) { + callerGraphInfo.pushInvoke((Invoke) newNode); + } + } + } + + callerGraphInfo.computeProbabilities(); + + inliningCount++; + metricInliningPerformed.increment(); + } + } catch (BailoutException bailout) { + throw bailout; + } catch (AssertionError | RuntimeException e) { + throw new GraalInternalError(e).addContext(callee.toString()); + } catch (GraalInternalError e) { + throw e.addContext(callee.toString()); + } + } + + private Inlineable getInlineableElement(final ResolvedJavaMethod method, Invoke invoke, HighTierContext context) { + Class macroNodeClass = InliningUtil.getMacroNodeClass(context.getReplacements(), method); + if (macroNodeClass != null) { + return new InlineableMacroNode(macroNodeClass); + } else { + return new InlineableGraph(buildGraph(method, invoke, context)); + } + } + + private StructuredGraph buildGraph(final ResolvedJavaMethod method, final Invoke invoke, final HighTierContext context) { + final StructuredGraph newGraph; + final boolean parseBytecodes; + + // TODO (chaeubl): copying the graph is only necessary if it is modified or if it contains + // any invokes + StructuredGraph intrinsicGraph = InliningUtil.getIntrinsicGraph(context.getReplacements(), method); + if (intrinsicGraph != null) { + newGraph = intrinsicGraph.copy(); + parseBytecodes = false; + } else { + StructuredGraph cachedGraph = getCachedGraph(method, context); + if (cachedGraph != null) { + newGraph = cachedGraph.copy(); + parseBytecodes = false; + } else { + newGraph = new StructuredGraph(method); + parseBytecodes = true; + } + } + + try (Scope s = Debug.scope("InlineGraph", newGraph)) { + if (parseBytecodes) { + parseBytecodes(newGraph, context); + } + + boolean callerHasMoreInformationAboutArguments = false; + NodeInputList args = invoke.callTarget().arguments(); + for (ParameterNode param : newGraph.getNodes(ParameterNode.class).snapshot()) { + ValueNode arg = args.get(param.index()); + if (arg.isConstant()) { + Constant constant = arg.asConstant(); + newGraph.replaceFloating(param, ConstantNode.forConstant(constant, context.getMetaAccess(), newGraph)); + callerHasMoreInformationAboutArguments = true; + } else { + Stamp joinedStamp = param.stamp().join(arg.stamp()); + if (joinedStamp != null && !joinedStamp.equals(param.stamp())) { + param.setStamp(joinedStamp); + callerHasMoreInformationAboutArguments = true; + } + } + } + + if (!callerHasMoreInformationAboutArguments) { + // TODO (chaeubl): if args are not more concrete, inlining should be avoided + // in most cases or we could at least use the previous graph size + invoke + // probability to check the inlining + } + + if (OptCanonicalizer.getValue()) { + canonicalizer.apply(newGraph, context); + } + + return newGraph; + } catch (Throwable e) { + throw Debug.handle(e); + } + } + + private static StructuredGraph getCachedGraph(ResolvedJavaMethod method, HighTierContext context) { + if (context.getGraphCache() != null) { + StructuredGraph cachedGraph = context.getGraphCache().get(method); + if (cachedGraph != null) { + return cachedGraph; + } + } + return null; + } + + private StructuredGraph parseBytecodes(StructuredGraph newGraph, HighTierContext context) { + boolean hasMatureProfilingInfo = newGraph.method().getProfilingInfo().isMature(); + + if (context.getGraphBuilderSuite() != null) { + context.getGraphBuilderSuite().apply(newGraph, context); + } + assert newGraph.start().next() != null : "graph needs to be populated during PhasePosition.AFTER_PARSING"; + + new DeadCodeEliminationPhase().apply(newGraph); + + if (OptCanonicalizer.getValue()) { + canonicalizer.apply(newGraph, context); + } + + if (hasMatureProfilingInfo && context.getGraphCache() != null) { + context.getGraphCache().put(newGraph.method(), newGraph.copy()); + } + return newGraph; + } + + private abstract static class AbstractInliningPolicy implements InliningPolicy { + + protected final Map hints; + + public AbstractInliningPolicy(Map hints) { + this.hints = hints; + } + + protected double computeMaximumSize(double relevance, int configuredMaximum) { + double inlineRatio = Math.min(RelevanceCapForInlining.getValue(), relevance); + return configuredMaximum * inlineRatio; + } + + protected double getInliningBonus(InlineInfo info) { + if (hints != null && hints.containsKey(info.invoke())) { + return hints.get(info.invoke()); + } + return 1; + } + + protected boolean isIntrinsic(Replacements replacements, InlineInfo info) { + if (AlwaysInlineIntrinsics.getValue()) { + return onlyIntrinsics(replacements, info); + } else { + return onlyForcedIntrinsics(replacements, info); + } + } + + private static boolean onlyIntrinsics(Replacements replacements, InlineInfo info) { + for (int i = 0; i < info.numberOfMethods(); i++) { + if (!InliningUtil.canIntrinsify(replacements, info.methodAt(i))) { + return false; + } + } + return true; + } + + private static boolean onlyForcedIntrinsics(Replacements replacements, InlineInfo info) { + for (int i = 0; i < info.numberOfMethods(); i++) { + if (!InliningUtil.canIntrinsify(replacements, info.methodAt(i))) { + return false; + } + if (!replacements.isForcedSubstitution(info.methodAt(i))) { + return false; + } + } + return true; + } + + protected static int previousLowLevelGraphSize(InlineInfo info) { + int size = 0; + for (int i = 0; i < info.numberOfMethods(); i++) { + ResolvedJavaMethod m = info.methodAt(i); + ProfilingInfo profile = m.getProfilingInfo(); + int compiledGraphSize = profile.getCompilerIRSize(StructuredGraph.class); + if (compiledGraphSize > 0) { + size += compiledGraphSize; + } + } + return size; + } + + protected static int determineNodeCount(InlineInfo info) { + int nodes = 0; + for (int i = 0; i < info.numberOfMethods(); i++) { + Inlineable elem = info.inlineableElementAt(i); + if (elem != null) { + nodes += elem.getNodeCount(); + } + } + return nodes; + } + + protected static double determineInvokeProbability(ToDoubleFunction probabilities, InlineInfo info) { + double invokeProbability = 0; + for (int i = 0; i < info.numberOfMethods(); i++) { + Inlineable callee = info.inlineableElementAt(i); + Iterable invokes = callee.getInvokes(); + if (invokes.iterator().hasNext()) { + for (Invoke invoke : invokes) { + invokeProbability += probabilities.applyAsDouble(invoke.asNode()); + } + } + } + return invokeProbability; + } + } + + public static class GreedyInliningPolicy extends AbstractInliningPolicy { + + public GreedyInliningPolicy(Map hints) { + super(hints); + } + + public boolean continueInlining(StructuredGraph currentGraph) { + if (currentGraph.getNodeCount() >= MaximumDesiredSize.getValue()) { + InliningUtil.logInliningDecision("inlining is cut off by MaximumDesiredSize"); + metricInliningStoppedByMaxDesiredSize.increment(); + return false; + } + return true; + } + + @Override + public boolean isWorthInlining(ToDoubleFunction probabilities, Replacements replacements, InlineInfo info, int inliningDepth, double probability, double relevance, + boolean fullyProcessed) { + if (InlineEverything.getValue()) { + return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "inline everything"); + } + + if (isIntrinsic(replacements, info)) { + return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "intrinsic"); + } + + if (info.shouldInline()) { + return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "forced inlining"); + } + + double inliningBonus = getInliningBonus(info); + int nodes = determineNodeCount(info); + int lowLevelGraphSize = previousLowLevelGraphSize(info); + + if (SmallCompiledLowLevelGraphSize.getValue() > 0 && lowLevelGraphSize > SmallCompiledLowLevelGraphSize.getValue() * inliningBonus) { + return InliningUtil.logNotInlinedMethod(info, inliningDepth, "too large previous low-level graph (low-level-nodes: %d, relevance=%f, probability=%f, bonus=%f, nodes=%d)", + lowLevelGraphSize, relevance, probability, inliningBonus, nodes); + } + + if (nodes < TrivialInliningSize.getValue() * inliningBonus) { + return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "trivial (relevance=%f, probability=%f, bonus=%f, nodes=%d)", relevance, probability, inliningBonus, nodes); + } + + /* + * TODO (chaeubl): invoked methods that are on important paths but not yet compiled -> + * will be compiled anyways and it is likely that we are the only caller... might be + * useful to inline those methods but increases bootstrap time (maybe those methods are + * also getting queued in the compilation queue concurrently) + */ + double invokes = determineInvokeProbability(probabilities, info); + if (LimitInlinedInvokes.getValue() > 0 && fullyProcessed && invokes > LimitInlinedInvokes.getValue() * inliningBonus) { + return InliningUtil.logNotInlinedMethod(info, inliningDepth, "callee invoke probability is too high (invokeP=%f, relevance=%f, probability=%f, bonus=%f, nodes=%d)", invokes, + relevance, probability, inliningBonus, nodes); + } + + double maximumNodes = computeMaximumSize(relevance, (int) (MaximumInliningSize.getValue() * inliningBonus)); + if (nodes <= maximumNodes) { + return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "relevance-based (relevance=%f, probability=%f, bonus=%f, nodes=%d <= %f)", relevance, probability, + inliningBonus, nodes, maximumNodes); + } + + return InliningUtil.logNotInlinedMethod(info, inliningDepth, "relevance-based (relevance=%f, probability=%f, bonus=%f, nodes=%d > %f)", relevance, probability, inliningBonus, nodes, + maximumNodes); + } + } + + public static final class InlineEverythingPolicy implements InliningPolicy { + + public boolean continueInlining(StructuredGraph graph) { + if (graph.getNodeCount() >= MaximumDesiredSize.getValue()) { + throw new BailoutException("Inline all calls failed. The resulting graph is too large."); + } + return true; + } + + public boolean isWorthInlining(ToDoubleFunction probabilities, Replacements replacements, InlineInfo info, int inliningDepth, double probability, double relevance, + boolean fullyProcessed) { + return true; + } + } + + private static class InliningIterator { + + private final FixedNode start; + private final Deque nodeQueue; + private final NodeBitMap queuedNodes; + + public InliningIterator(FixedNode start, NodeBitMap visitedFixedNodes) { + this.start = start; + this.nodeQueue = new ArrayDeque<>(); + this.queuedNodes = visitedFixedNodes; + assert start.isAlive(); + } + + public LinkedList apply() { + LinkedList invokes = new LinkedList<>(); + FixedNode current; + forcedQueue(start); + + while ((current = nextQueuedNode()) != null) { + assert current.isAlive(); + + if (current instanceof Invoke && ((Invoke) current).callTarget() instanceof MethodCallTargetNode) { + if (current != start) { + invokes.addLast((Invoke) current); + } + queueSuccessors(current); + } else if (current instanceof LoopBeginNode) { + queueSuccessors(current); + } else if (current instanceof LoopEndNode) { + // nothing todo + } else if (current instanceof MergeNode) { + queueSuccessors(current); + } else if (current instanceof FixedWithNextNode) { + queueSuccessors(current); + } else if (current instanceof EndNode) { + queueMerge((EndNode) current); + } else if (current instanceof ControlSinkNode) { + // nothing todo + } else if (current instanceof ControlSplitNode) { + queueSuccessors(current); + } else { + assert false : current; + } + } + + return invokes; + } + + private void queueSuccessors(FixedNode x) { + for (Node node : x.successors()) { + queue(node); + } + } + + private void queue(Node node) { + if (node != null && !queuedNodes.isMarked(node)) { + forcedQueue(node); + } + } + + private void forcedQueue(Node node) { + queuedNodes.mark(node); + nodeQueue.addFirst((FixedNode) node); + } + + private FixedNode nextQueuedNode() { + if (nodeQueue.isEmpty()) { + return null; + } + + FixedNode result = nodeQueue.removeFirst(); + assert queuedNodes.isMarked(result); + return result; + } + + private void queueMerge(AbstractEndNode end) { + MergeNode merge = end.merge(); + if (!queuedNodes.isMarked(merge) && visitedAllEnds(merge)) { + queuedNodes.mark(merge); + nodeQueue.add(merge); + } + } + + private boolean visitedAllEnds(MergeNode merge) { + for (int i = 0; i < merge.forwardEndCount(); i++) { + if (!queuedNodes.isMarked(merge.forwardEndAt(i))) { + return false; + } + } + return true; + } + } + + /** + * Holds the data for building the callee graphs recursively: graphs and invocations (each + * invocation can have multiple graphs). + */ + static class InliningData { + + private static final GraphInfo DummyGraphInfo = new GraphInfo(null, new LinkedList(), 1.0, 1.0); + + /** + * Call hierarchy from outer most call (i.e., compilation unit) to inner most callee. + */ + private final ArrayDeque graphQueue; + private final ArrayDeque invocationQueue; + + private int maxGraphs; + + public InliningData(StructuredGraph rootGraph, Assumptions rootAssumptions) { + this.graphQueue = new ArrayDeque<>(); + this.invocationQueue = new ArrayDeque<>(); + this.maxGraphs = 1; + + invocationQueue.push(new MethodInvocation(null, rootAssumptions, 1.0, 1.0)); + pushGraph(rootGraph, 1.0, 1.0); + } + + public int graphCount() { + return graphQueue.size(); + } + + public void pushGraph(StructuredGraph graph, double probability, double relevance) { + assert !contains(graph); + NodeBitMap visitedFixedNodes = graph.createNodeBitMap(); + LinkedList invokes = new InliningIterator(graph.start(), visitedFixedNodes).apply(); + assert invokes.size() == count(graph.getInvokes()); + graphQueue.push(new GraphInfo(graph, invokes, probability, relevance)); + assert graphQueue.size() <= maxGraphs; + } + + public void pushDummyGraph() { + graphQueue.push(DummyGraphInfo); + } + + public boolean hasUnprocessedGraphs() { + return !graphQueue.isEmpty(); + } + + public GraphInfo currentGraph() { + return graphQueue.peek(); + } + + public void popGraph() { + graphQueue.pop(); + assert graphQueue.size() <= maxGraphs; + } + + public void popGraphs(int count) { + assert count >= 0; + for (int i = 0; i < count; i++) { + graphQueue.pop(); + } + } + + private static final Object[] NO_CONTEXT = {}; + + /** + * Gets the call hierarchy of this inlining from outer most call to inner most callee. + */ + public Object[] inliningContext() { + if (!Debug.isDumpEnabled()) { + return NO_CONTEXT; + } + Object[] result = new Object[graphQueue.size()]; + int i = 0; + for (GraphInfo g : graphQueue) { + result[i++] = g.graph.method(); + } + return result; + } + + public MethodInvocation currentInvocation() { + return invocationQueue.peekFirst(); + } + + public MethodInvocation pushInvocation(InlineInfo info, Assumptions assumptions, double probability, double relevance) { + MethodInvocation methodInvocation = new MethodInvocation(info, new Assumptions(assumptions.useOptimisticAssumptions()), probability, relevance); + invocationQueue.addFirst(methodInvocation); + maxGraphs += info.numberOfMethods(); + assert graphQueue.size() <= maxGraphs; + return methodInvocation; + } + + public void popInvocation() { + maxGraphs -= invocationQueue.peekFirst().callee.numberOfMethods(); + assert graphQueue.size() <= maxGraphs; + invocationQueue.removeFirst(); + } + + public int countRecursiveInlining(ResolvedJavaMethod method) { + int count = 0; + for (GraphInfo graphInfo : graphQueue) { + if (method.equals(graphInfo.method())) { + count++; + } + } + return count; + } + + public int inliningDepth() { + assert invocationQueue.size() > 0; + return invocationQueue.size() - 1; + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder("Invocations: "); + + for (MethodInvocation invocation : invocationQueue) { + if (invocation.callee() != null) { + result.append(invocation.callee().numberOfMethods()); + result.append("x "); + result.append(invocation.callee().invoke()); + result.append("; "); + } + } + + result.append("\nGraphs: "); + for (GraphInfo graph : graphQueue) { + result.append(graph.graph()); + result.append("; "); + } + + return result.toString(); + } + + private boolean contains(StructuredGraph graph) { + for (GraphInfo info : graphQueue) { + if (info.graph() == graph) { + return true; + } + } + return false; + } + + private static int count(Iterable invokes) { + int count = 0; + Iterator iterator = invokes.iterator(); + while (iterator.hasNext()) { + iterator.next(); + count++; + } + return count; + } + } + + private static class MethodInvocation { + + private final InlineInfo callee; + private final Assumptions assumptions; + private final double probability; + private final double relevance; + + private int processedGraphs; + + public MethodInvocation(InlineInfo info, Assumptions assumptions, double probability, double relevance) { + this.callee = info; + this.assumptions = assumptions; + this.probability = probability; + this.relevance = relevance; + } + + public void incrementProcessedGraphs() { + processedGraphs++; + assert processedGraphs <= callee.numberOfMethods(); + } + + public int processedGraphs() { + assert processedGraphs <= callee.numberOfMethods(); + return processedGraphs; + } + + public int totalGraphs() { + return callee.numberOfMethods(); + } + + public InlineInfo callee() { + return callee; + } + + public Assumptions assumptions() { + return assumptions; + } + + public double probability() { + return probability; + } + + public double relevance() { + return relevance; + } + + public boolean isRoot() { + return callee == null; + } + + @Override + public String toString() { + if (isRoot()) { + return ""; + } + CallTargetNode callTarget = callee.invoke().callTarget(); + if (callTarget instanceof MethodCallTargetNode) { + ResolvedJavaMethod calleeMethod = ((MethodCallTargetNode) callTarget).targetMethod(); + return MetaUtil.format("Invoke#%H.%n(%p)", calleeMethod); + } else { + return "Invoke#" + callTarget.targetName(); + } + } + } + + /** + * Information about a graph that will potentially be inlined. This includes tracking the + * invocations in graph that will subject to inlining themselves. + */ + private static class GraphInfo { + + private final StructuredGraph graph; + private final LinkedList remainingInvokes; + private final double probability; + private final double relevance; + + private final ToDoubleFunction probabilities; + private final ComputeInliningRelevance computeInliningRelevance; + + public GraphInfo(StructuredGraph graph, LinkedList invokes, double probability, double relevance) { + this.graph = graph; + this.remainingInvokes = invokes; + this.probability = probability; + this.relevance = relevance; + + if (graph != null && (graph.hasNode(InvokeNode.class) || graph.hasNode(InvokeWithExceptionNode.class))) { + probabilities = new FixedNodeProbabilityCache(); + computeInliningRelevance = new ComputeInliningRelevance(graph, probabilities); + computeProbabilities(); + } else { + probabilities = null; + computeInliningRelevance = null; + } + } + + /** + * Gets the method associated with the {@linkplain #graph() graph} represented by this + * object. + */ + public ResolvedJavaMethod method() { + return graph.method(); + } + + public boolean hasRemainingInvokes() { + return !remainingInvokes.isEmpty(); + } + + /** + * The graph about which this object contains inlining information. + */ + public StructuredGraph graph() { + return graph; + } + + public Invoke popInvoke() { + return remainingInvokes.removeFirst(); + } + + public void pushInvoke(Invoke invoke) { + remainingInvokes.push(invoke); + } + + public void computeProbabilities() { + computeInliningRelevance.compute(); + } + + public double invokeProbability(Invoke invoke) { + return probability * probabilities.applyAsDouble(invoke.asNode()); + } + + public double invokeRelevance(Invoke invoke) { + return Math.min(CapInheritedRelevance.getValue(), relevance) * computeInliningRelevance.getRelevance(invoke); + } + + @Override + public String toString() { + return (graph != null ? MetaUtil.format("%H.%n(%p)", method()) : "") + remainingInvokes; + } + } +} diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningUtil.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningUtil.java Sat May 03 21:46:35 2014 +0200 @@ -0,0 +1,1610 @@ +/* + * Copyright (c) 2012, 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.graal.phases.common.inlining; + +import static com.oracle.graal.api.meta.DeoptimizationAction.*; +import static com.oracle.graal.api.meta.DeoptimizationReason.*; +import static com.oracle.graal.compiler.common.GraalOptions.*; +import static com.oracle.graal.compiler.common.type.StampFactory.*; + +import java.util.*; +import java.util.function.*; + +import com.oracle.graal.api.code.*; +import com.oracle.graal.api.code.Assumptions.Assumption; +import com.oracle.graal.api.meta.*; +import com.oracle.graal.api.meta.JavaTypeProfile.ProfiledType; +import com.oracle.graal.api.meta.ResolvedJavaType.Representation; +import com.oracle.graal.compiler.common.*; +import com.oracle.graal.compiler.common.calc.*; +import com.oracle.graal.compiler.common.type.*; +import com.oracle.graal.debug.*; +import com.oracle.graal.debug.Debug.Scope; +import com.oracle.graal.graph.*; +import com.oracle.graal.graph.Graph.DuplicationReplacement; +import com.oracle.graal.graph.Node.Verbosity; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.calc.*; +import com.oracle.graal.nodes.extended.*; +import com.oracle.graal.nodes.java.*; +import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind; +import com.oracle.graal.nodes.spi.*; +import com.oracle.graal.nodes.type.*; +import com.oracle.graal.nodes.util.*; +import com.oracle.graal.phases.*; +import com.oracle.graal.phases.common.*; +import com.oracle.graal.phases.common.inlining.InliningPhase.*; +import com.oracle.graal.phases.tiers.*; +import com.oracle.graal.phases.util.*; + +public class InliningUtil { + + private static final DebugMetric metricInliningTailDuplication = Debug.metric("InliningTailDuplication"); + private static final String inliningDecisionsScopeString = "InliningDecisions"; + /** + * Meters the size (in bytecodes) of all methods processed during compilation (i.e., top level + * and all inlined methods), irrespective of how many bytecodes in each method are actually + * parsed (which may be none for methods whose IR is retrieved from a cache). + */ + public static final DebugMetric InlinedBytecodes = Debug.metric("InlinedBytecodes"); + + public interface InliningPolicy { + + boolean continueInlining(StructuredGraph graph); + + boolean isWorthInlining(ToDoubleFunction probabilities, Replacements replacements, InlineInfo info, int inliningDepth, double probability, double relevance, boolean fullyProcessed); + } + + public interface Inlineable { + + int getNodeCount(); + + Iterable getInvokes(); + } + + public static class InlineableGraph implements Inlineable { + + private final StructuredGraph graph; + + public InlineableGraph(StructuredGraph graph) { + this.graph = graph; + } + + @Override + public int getNodeCount() { + return graph.getNodeCount(); + } + + @Override + public Iterable getInvokes() { + return graph.getInvokes(); + } + + public StructuredGraph getGraph() { + return graph; + } + } + + public static class InlineableMacroNode implements Inlineable { + + private final Class macroNodeClass; + + public InlineableMacroNode(Class macroNodeClass) { + this.macroNodeClass = macroNodeClass; + } + + @Override + public int getNodeCount() { + return 1; + } + + @Override + public Iterable getInvokes() { + return Collections.emptyList(); + } + + public Class getMacroNodeClass() { + return macroNodeClass; + } + } + + /** + * Print a HotSpot-style inlining message to the console. + */ + private static void printInlining(final InlineInfo info, final int inliningDepth, final boolean success, final String msg, final Object... args) { + printInlining(info.methodAt(0), info.invoke(), inliningDepth, success, msg, args); + } + + /** + * Print a HotSpot-style inlining message to the console. + */ + private static void printInlining(final ResolvedJavaMethod method, final Invoke invoke, final int inliningDepth, final boolean success, final String msg, final Object... args) { + if (HotSpotPrintInlining.getValue()) { + // 1234567 + TTY.print(" "); // print timestamp + // 1234 + TTY.print(" "); // print compilation number + // % s ! b n + TTY.print("%c%c%c%c%c ", ' ', method.isSynchronized() ? 's' : ' ', ' ', ' ', method.isNative() ? 'n' : ' '); + TTY.print(" "); // more indent + TTY.print(" "); // initial inlining indent + for (int i = 0; i < inliningDepth; i++) { + TTY.print(" "); + } + TTY.println(String.format("@ %d %s %s%s", invoke.bci(), methodName(method, null), success ? "" : "not inlining ", String.format(msg, args))); + } + } + + public static boolean logInlinedMethod(InlineInfo info, int inliningDepth, boolean allowLogging, String msg, Object... args) { + return logInliningDecision(info, inliningDepth, allowLogging, true, msg, args); + } + + public static boolean logNotInlinedMethod(InlineInfo info, int inliningDepth, String msg, Object... args) { + return logInliningDecision(info, inliningDepth, true, false, msg, args); + } + + public static boolean logInliningDecision(InlineInfo info, int inliningDepth, boolean allowLogging, boolean success, String msg, final Object... args) { + if (allowLogging) { + printInlining(info, inliningDepth, success, msg, args); + if (shouldLogInliningDecision()) { + logInliningDecision(methodName(info), success, msg, args); + } + } + return success; + } + + public static void logInliningDecision(final String msg, final Object... args) { + try (Scope s = Debug.scope(inliningDecisionsScopeString)) { + // Can't use log here since we are varargs + if (Debug.isLogEnabled()) { + Debug.logv(msg, args); + } + } + } + + private static boolean logNotInlinedMethod(Invoke invoke, String msg) { + if (shouldLogInliningDecision()) { + String methodString = invoke.toString() + (invoke.callTarget() == null ? " callTarget=null" : invoke.callTarget().targetName()); + logInliningDecision(methodString, false, msg, new Object[0]); + } + return false; + } + + private static InlineInfo logNotInlinedMethodAndReturnNull(Invoke invoke, int inliningDepth, ResolvedJavaMethod method, String msg) { + return logNotInlinedMethodAndReturnNull(invoke, inliningDepth, method, msg, new Object[0]); + } + + private static InlineInfo logNotInlinedMethodAndReturnNull(Invoke invoke, int inliningDepth, ResolvedJavaMethod method, String msg, Object... args) { + printInlining(method, invoke, inliningDepth, false, msg, args); + if (shouldLogInliningDecision()) { + String methodString = methodName(method, invoke); + logInliningDecision(methodString, false, msg, args); + } + return null; + } + + private static boolean logNotInlinedMethodAndReturnFalse(Invoke invoke, int inliningDepth, ResolvedJavaMethod method, String msg) { + printInlining(method, invoke, inliningDepth, false, msg, new Object[0]); + if (shouldLogInliningDecision()) { + String methodString = methodName(method, invoke); + logInliningDecision(methodString, false, msg, new Object[0]); + } + return false; + } + + private static void logInliningDecision(final String methodString, final boolean success, final String msg, final Object... args) { + String inliningMsg = "inlining " + methodString + ": " + msg; + if (!success) { + inliningMsg = "not " + inliningMsg; + } + logInliningDecision(inliningMsg, args); + } + + public static boolean shouldLogInliningDecision() { + try (Scope s = Debug.scope(inliningDecisionsScopeString)) { + return Debug.isLogEnabled(); + } + } + + private static String methodName(ResolvedJavaMethod method, Invoke invoke) { + if (invoke != null && invoke.stateAfter() != null) { + return methodName(invoke.stateAfter(), invoke.bci()) + ": " + MetaUtil.format("%H.%n(%p):%r", method) + " (" + method.getCodeSize() + " bytes)"; + } else { + return MetaUtil.format("%H.%n(%p):%r", method) + " (" + method.getCodeSize() + " bytes)"; + } + } + + private static String methodName(InlineInfo info) { + if (info == null) { + return "null"; + } else if (info.invoke() != null && info.invoke().stateAfter() != null) { + return methodName(info.invoke().stateAfter(), info.invoke().bci()) + ": " + info.toString(); + } else { + return info.toString(); + } + } + + private static String methodName(FrameState frameState, int bci) { + StringBuilder sb = new StringBuilder(); + if (frameState.outerFrameState() != null) { + sb.append(methodName(frameState.outerFrameState(), frameState.outerFrameState().bci)); + sb.append("->"); + } + sb.append(MetaUtil.format("%h.%n", frameState.method())); + sb.append("@").append(bci); + return sb.toString(); + } + + /** + * Represents an opportunity for inlining at a given invoke, with the given weight and level. + * The weight is the amortized weight of the additional code - so smaller is better. The level + * is the number of nested inlinings that lead to this invoke. + */ + public interface InlineInfo { + + /** + * The graph containing the {@link #invoke() invocation} that may be inlined. + */ + StructuredGraph graph(); + + /** + * The invocation that may be inlined. + */ + Invoke invoke(); + + /** + * Returns the number of methods that may be inlined by the {@link #invoke() invocation}. + * This may be more than one in the case of a invocation profile showing a number of "hot" + * concrete methods dispatched to by the invocation. + */ + int numberOfMethods(); + + ResolvedJavaMethod methodAt(int index); + + Inlineable inlineableElementAt(int index); + + double probabilityAt(int index); + + double relevanceAt(int index); + + void setInlinableElement(int index, Inlineable inlineableElement); + + /** + * Performs the inlining described by this object and returns the node that represents the + * return value of the inlined method (or null for void methods and methods that have no + * non-exceptional exit). + */ + void inline(Providers providers, Assumptions assumptions); + + /** + * Try to make the call static bindable to avoid interface and virtual method calls. + */ + void tryToDevirtualizeInvoke(MetaAccessProvider metaAccess, Assumptions assumptions); + + boolean shouldInline(); + } + + public abstract static class AbstractInlineInfo implements InlineInfo { + + protected final Invoke invoke; + + public AbstractInlineInfo(Invoke invoke) { + this.invoke = invoke; + } + + @Override + public StructuredGraph graph() { + return invoke.asNode().graph(); + } + + @Override + public Invoke invoke() { + return invoke; + } + + protected static void inline(Invoke invoke, ResolvedJavaMethod concrete, Inlineable inlineable, Assumptions assumptions, boolean receiverNullCheck) { + if (inlineable instanceof InlineableGraph) { + StructuredGraph calleeGraph = ((InlineableGraph) inlineable).getGraph(); + InliningUtil.inline(invoke, calleeGraph, receiverNullCheck); + } else { + assert inlineable instanceof InlineableMacroNode; + + Class macroNodeClass = ((InlineableMacroNode) inlineable).getMacroNodeClass(); + inlineMacroNode(invoke, concrete, macroNodeClass); + } + + InlinedBytecodes.add(concrete.getCodeSize()); + assumptions.recordMethodContents(concrete); + } + } + + public static void replaceInvokeCallTarget(Invoke invoke, StructuredGraph graph, InvokeKind invokeKind, ResolvedJavaMethod targetMethod) { + MethodCallTargetNode oldCallTarget = (MethodCallTargetNode) invoke.callTarget(); + MethodCallTargetNode newCallTarget = graph.add(new MethodCallTargetNode(invokeKind, targetMethod, oldCallTarget.arguments().toArray(new ValueNode[0]), oldCallTarget.returnType())); + invoke.asNode().replaceFirstInput(oldCallTarget, newCallTarget); + } + + /** + * Represents an inlining opportunity where the compiler can statically determine a monomorphic + * target method and therefore is able to determine the called method exactly. + */ + public static class ExactInlineInfo extends AbstractInlineInfo { + + protected final ResolvedJavaMethod concrete; + private Inlineable inlineableElement; + private boolean suppressNullCheck; + + public ExactInlineInfo(Invoke invoke, ResolvedJavaMethod concrete) { + super(invoke); + this.concrete = concrete; + assert concrete != null; + } + + public void suppressNullCheck() { + suppressNullCheck = true; + } + + @Override + public void inline(Providers providers, Assumptions assumptions) { + inline(invoke, concrete, inlineableElement, assumptions, !suppressNullCheck); + } + + @Override + public void tryToDevirtualizeInvoke(MetaAccessProvider metaAccess, Assumptions assumptions) { + // nothing todo, can already be bound statically + } + + @Override + public int numberOfMethods() { + return 1; + } + + @Override + public ResolvedJavaMethod methodAt(int index) { + assert index == 0; + return concrete; + } + + @Override + public double probabilityAt(int index) { + assert index == 0; + return 1.0; + } + + @Override + public double relevanceAt(int index) { + assert index == 0; + return 1.0; + } + + @Override + public String toString() { + return "exact " + MetaUtil.format("%H.%n(%p):%r", concrete); + } + + @Override + public Inlineable inlineableElementAt(int index) { + assert index == 0; + return inlineableElement; + } + + @Override + public void setInlinableElement(int index, Inlineable inlineableElement) { + assert index == 0; + this.inlineableElement = inlineableElement; + } + + public boolean shouldInline() { + return concrete.shouldBeInlined(); + } + } + + /** + * Represents an inlining opportunity for which profiling information suggests a monomorphic + * receiver, but for which the receiver type cannot be proven. A type check guard will be + * generated if this inlining is performed. + */ + private static class TypeGuardInlineInfo extends AbstractInlineInfo { + + private final ResolvedJavaMethod concrete; + private final ResolvedJavaType type; + private Inlineable inlineableElement; + + public TypeGuardInlineInfo(Invoke invoke, ResolvedJavaMethod concrete, ResolvedJavaType type) { + super(invoke); + this.concrete = concrete; + this.type = type; + assert type.isArray() || !type.isAbstract() : type; + } + + @Override + public int numberOfMethods() { + return 1; + } + + @Override + public ResolvedJavaMethod methodAt(int index) { + assert index == 0; + return concrete; + } + + @Override + public Inlineable inlineableElementAt(int index) { + assert index == 0; + return inlineableElement; + } + + @Override + public double probabilityAt(int index) { + assert index == 0; + return 1.0; + } + + @Override + public double relevanceAt(int index) { + assert index == 0; + return 1.0; + } + + @Override + public void setInlinableElement(int index, Inlineable inlineableElement) { + assert index == 0; + this.inlineableElement = inlineableElement; + } + + @Override + public void inline(Providers providers, Assumptions assumptions) { + createGuard(graph(), providers.getMetaAccess()); + inline(invoke, concrete, inlineableElement, assumptions, false); + } + + @Override + public void tryToDevirtualizeInvoke(MetaAccessProvider metaAccess, Assumptions assumptions) { + createGuard(graph(), metaAccess); + replaceInvokeCallTarget(invoke, graph(), InvokeKind.Special, concrete); + } + + private void createGuard(StructuredGraph graph, MetaAccessProvider metaAccess) { + ValueNode nonNullReceiver = InliningUtil.nonNullReceiver(invoke); + ConstantNode typeHub = ConstantNode.forConstant(type.getEncoding(Representation.ObjectHub), metaAccess, graph); + LoadHubNode receiverHub = graph.unique(new LoadHubNode(nonNullReceiver, typeHub.getKind())); + + CompareNode typeCheck = CompareNode.createCompareNode(graph, Condition.EQ, receiverHub, typeHub); + FixedGuardNode guard = graph.add(new FixedGuardNode(typeCheck, DeoptimizationReason.TypeCheckedInliningViolated, DeoptimizationAction.InvalidateReprofile)); + assert invoke.predecessor() != null; + + ValueNode anchoredReceiver = createAnchoredReceiver(graph, guard, type, nonNullReceiver, true); + invoke.callTarget().replaceFirstInput(nonNullReceiver, anchoredReceiver); + + graph.addBeforeFixed(invoke.asNode(), guard); + } + + @Override + public String toString() { + return "type-checked with type " + type.getName() + " and method " + MetaUtil.format("%H.%n(%p):%r", concrete); + } + + public boolean shouldInline() { + return concrete.shouldBeInlined(); + } + } + + /** + * Polymorphic inlining of m methods with n type checks (n ≥ m) in case that the profiling + * information suggests a reasonable amount of different receiver types and different methods. + * If an unknown type is encountered a deoptimization is triggered. + */ + private static class MultiTypeGuardInlineInfo extends AbstractInlineInfo { + + private final List concretes; + private final double[] methodProbabilities; + private final double maximumMethodProbability; + private final ArrayList typesToConcretes; + private final ArrayList ptypes; + private final ArrayList concretesProbabilities; + private final double notRecordedTypeProbability; + private final Inlineable[] inlineableElements; + + public MultiTypeGuardInlineInfo(Invoke invoke, ArrayList concretes, ArrayList concretesProbabilities, ArrayList ptypes, + ArrayList typesToConcretes, double notRecordedTypeProbability) { + super(invoke); + assert concretes.size() > 0 : "must have at least one method"; + assert ptypes.size() == typesToConcretes.size() : "array lengths must match"; + + this.concretesProbabilities = concretesProbabilities; + this.concretes = concretes; + this.ptypes = ptypes; + this.typesToConcretes = typesToConcretes; + this.notRecordedTypeProbability = notRecordedTypeProbability; + this.inlineableElements = new Inlineable[concretes.size()]; + this.methodProbabilities = computeMethodProbabilities(); + this.maximumMethodProbability = maximumMethodProbability(); + assert maximumMethodProbability > 0; + } + + private double[] computeMethodProbabilities() { + double[] result = new double[concretes.size()]; + for (int i = 0; i < typesToConcretes.size(); i++) { + int concrete = typesToConcretes.get(i); + double probability = ptypes.get(i).getProbability(); + result[concrete] += probability; + } + return result; + } + + private double maximumMethodProbability() { + double max = 0; + for (int i = 0; i < methodProbabilities.length; i++) { + max = Math.max(max, methodProbabilities[i]); + } + return max; + } + + @Override + public int numberOfMethods() { + return concretes.size(); + } + + @Override + public ResolvedJavaMethod methodAt(int index) { + assert index >= 0 && index < concretes.size(); + return concretes.get(index); + } + + @Override + public Inlineable inlineableElementAt(int index) { + assert index >= 0 && index < concretes.size(); + return inlineableElements[index]; + } + + @Override + public double probabilityAt(int index) { + return methodProbabilities[index]; + } + + @Override + public double relevanceAt(int index) { + return probabilityAt(index) / maximumMethodProbability; + } + + @Override + public void setInlinableElement(int index, Inlineable inlineableElement) { + assert index >= 0 && index < concretes.size(); + inlineableElements[index] = inlineableElement; + } + + @Override + public void inline(Providers providers, Assumptions assumptions) { + if (hasSingleMethod()) { + inlineSingleMethod(graph(), providers.getMetaAccess(), assumptions); + } else { + inlineMultipleMethods(graph(), providers, assumptions); + } + } + + public boolean shouldInline() { + for (ResolvedJavaMethod method : concretes) { + if (method.shouldBeInlined()) { + return true; + } + } + return false; + } + + private boolean hasSingleMethod() { + return concretes.size() == 1 && !shouldFallbackToInvoke(); + } + + private boolean shouldFallbackToInvoke() { + return notRecordedTypeProbability > 0; + } + + private void inlineMultipleMethods(StructuredGraph graph, Providers providers, Assumptions assumptions) { + int numberOfMethods = concretes.size(); + FixedNode continuation = invoke.next(); + + ValueNode originalReceiver = ((MethodCallTargetNode) invoke.callTarget()).receiver(); + // setup merge and phi nodes for results and exceptions + MergeNode returnMerge = graph.add(new MergeNode()); + returnMerge.setStateAfter(invoke.stateAfter()); + + PhiNode returnValuePhi = null; + if (invoke.asNode().getKind() != Kind.Void) { + returnValuePhi = graph.addWithoutUnique(new ValuePhiNode(invoke.asNode().stamp().unrestricted(), returnMerge)); + } + + MergeNode exceptionMerge = null; + PhiNode exceptionObjectPhi = null; + if (invoke instanceof InvokeWithExceptionNode) { + InvokeWithExceptionNode invokeWithException = (InvokeWithExceptionNode) invoke; + ExceptionObjectNode exceptionEdge = (ExceptionObjectNode) invokeWithException.exceptionEdge(); + + exceptionMerge = graph.add(new MergeNode()); + + FixedNode exceptionSux = exceptionEdge.next(); + graph.addBeforeFixed(exceptionSux, exceptionMerge); + exceptionObjectPhi = graph.addWithoutUnique(new ValuePhiNode(StampFactory.forKind(Kind.Object), exceptionMerge)); + exceptionMerge.setStateAfter(exceptionEdge.stateAfter().duplicateModified(invoke.stateAfter().bci, true, Kind.Object, exceptionObjectPhi)); + } + + // create one separate block for each invoked method + BeginNode[] successors = new BeginNode[numberOfMethods + 1]; + for (int i = 0; i < numberOfMethods; i++) { + successors[i] = createInvocationBlock(graph, invoke, returnMerge, returnValuePhi, exceptionMerge, exceptionObjectPhi, true); + } + + // create the successor for an unknown type + FixedNode unknownTypeSux; + if (shouldFallbackToInvoke()) { + unknownTypeSux = createInvocationBlock(graph, invoke, returnMerge, returnValuePhi, exceptionMerge, exceptionObjectPhi, false); + } else { + unknownTypeSux = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.TypeCheckedInliningViolated)); + } + successors[successors.length - 1] = BeginNode.begin(unknownTypeSux); + + // replace the invoke exception edge + if (invoke instanceof InvokeWithExceptionNode) { + InvokeWithExceptionNode invokeWithExceptionNode = (InvokeWithExceptionNode) invoke; + ExceptionObjectNode exceptionEdge = (ExceptionObjectNode) invokeWithExceptionNode.exceptionEdge(); + exceptionEdge.replaceAtUsages(exceptionObjectPhi); + exceptionEdge.setNext(null); + GraphUtil.killCFG(invokeWithExceptionNode.exceptionEdge()); + } + + assert invoke.asNode().isAlive(); + + // replace the invoke with a switch on the type of the actual receiver + boolean methodDispatch = createDispatchOnTypeBeforeInvoke(graph, successors, false, providers.getMetaAccess()); + + assert invoke.next() == continuation; + invoke.setNext(null); + returnMerge.setNext(continuation); + invoke.asNode().replaceAtUsages(returnValuePhi); + invoke.asNode().replaceAndDelete(null); + + ArrayList replacementNodes = new ArrayList<>(); + + // do the actual inlining for every invoke + for (int i = 0; i < numberOfMethods; i++) { + BeginNode node = successors[i]; + Invoke invokeForInlining = (Invoke) node.next(); + + ResolvedJavaType commonType; + if (methodDispatch) { + commonType = concretes.get(i).getDeclaringClass(); + } else { + commonType = getLeastCommonType(i); + } + + ValueNode receiver = ((MethodCallTargetNode) invokeForInlining.callTarget()).receiver(); + boolean exact = (getTypeCount(i) == 1 && !methodDispatch); + GuardedValueNode anchoredReceiver = createAnchoredReceiver(graph, node, commonType, receiver, exact); + invokeForInlining.callTarget().replaceFirstInput(receiver, anchoredReceiver); + + inline(invokeForInlining, methodAt(i), inlineableElementAt(i), assumptions, false); + + replacementNodes.add(anchoredReceiver); + } + if (shouldFallbackToInvoke()) { + replacementNodes.add(null); + } + + if (OptTailDuplication.getValue()) { + /* + * We might want to perform tail duplication at the merge after a type switch, if + * there are invokes that would benefit from the improvement in type information. + */ + FixedNode current = returnMerge; + int opportunities = 0; + do { + if (current instanceof InvokeNode && ((InvokeNode) current).callTarget() instanceof MethodCallTargetNode && + ((MethodCallTargetNode) ((InvokeNode) current).callTarget()).receiver() == originalReceiver) { + opportunities++; + } else if (current.inputs().contains(originalReceiver)) { + opportunities++; + } + current = ((FixedWithNextNode) current).next(); + } while (current instanceof FixedWithNextNode); + + if (opportunities > 0) { + metricInliningTailDuplication.increment(); + Debug.log("MultiTypeGuardInlineInfo starting tail duplication (%d opportunities)", opportunities); + PhaseContext phaseContext = new PhaseContext(providers, assumptions); + CanonicalizerPhase canonicalizer = new CanonicalizerPhase(!ImmutableCode.getValue()); + TailDuplicationPhase.tailDuplicate(returnMerge, TailDuplicationPhase.TRUE_DECISION, replacementNodes, phaseContext, canonicalizer); + } + } + } + + private int getTypeCount(int concreteMethodIndex) { + int count = 0; + for (int i = 0; i < typesToConcretes.size(); i++) { + if (typesToConcretes.get(i) == concreteMethodIndex) { + count++; + } + } + return count; + } + + private ResolvedJavaType getLeastCommonType(int concreteMethodIndex) { + ResolvedJavaType commonType = null; + for (int i = 0; i < typesToConcretes.size(); i++) { + if (typesToConcretes.get(i) == concreteMethodIndex) { + if (commonType == null) { + commonType = ptypes.get(i).getType(); + } else { + commonType = commonType.findLeastCommonAncestor(ptypes.get(i).getType()); + } + } + } + assert commonType != null; + return commonType; + } + + private ResolvedJavaType getLeastCommonType() { + ResolvedJavaType result = getLeastCommonType(0); + for (int i = 1; i < concretes.size(); i++) { + result = result.findLeastCommonAncestor(getLeastCommonType(i)); + } + return result; + } + + private void inlineSingleMethod(StructuredGraph graph, MetaAccessProvider metaAccess, Assumptions assumptions) { + assert concretes.size() == 1 && inlineableElements.length == 1 && ptypes.size() > 1 && !shouldFallbackToInvoke() && notRecordedTypeProbability == 0; + + BeginNode calleeEntryNode = graph.add(new BeginNode()); + + BeginNode unknownTypeSux = createUnknownTypeSuccessor(graph); + BeginNode[] successors = new BeginNode[]{calleeEntryNode, unknownTypeSux}; + createDispatchOnTypeBeforeInvoke(graph, successors, false, metaAccess); + + calleeEntryNode.setNext(invoke.asNode()); + + inline(invoke, methodAt(0), inlineableElementAt(0), assumptions, false); + } + + private boolean createDispatchOnTypeBeforeInvoke(StructuredGraph graph, BeginNode[] successors, boolean invokeIsOnlySuccessor, MetaAccessProvider metaAccess) { + assert ptypes.size() >= 1; + ValueNode nonNullReceiver = nonNullReceiver(invoke); + Kind hubKind = ((MethodCallTargetNode) invoke.callTarget()).targetMethod().getDeclaringClass().getEncoding(Representation.ObjectHub).getKind(); + LoadHubNode hub = graph.unique(new LoadHubNode(nonNullReceiver, hubKind)); + + if (!invokeIsOnlySuccessor && chooseMethodDispatch()) { + assert successors.length == concretes.size() + 1; + assert concretes.size() > 0; + Debug.log("Method check cascade with %d methods", concretes.size()); + + ValueNode[] constantMethods = new ValueNode[concretes.size()]; + double[] probability = new double[concretes.size()]; + for (int i = 0; i < concretes.size(); ++i) { + ResolvedJavaMethod firstMethod = concretes.get(i); + Constant firstMethodConstant = firstMethod.getEncoding(); + + ValueNode firstMethodConstantNode = ConstantNode.forConstant(firstMethodConstant, metaAccess, graph); + constantMethods[i] = firstMethodConstantNode; + double concretesProbability = concretesProbabilities.get(i); + assert concretesProbability >= 0.0; + probability[i] = concretesProbability; + if (i > 0) { + double prevProbability = probability[i - 1]; + if (prevProbability == 1.0) { + probability[i] = 1.0; + } else { + probability[i] = Math.min(1.0, Math.max(0.0, probability[i] / (1.0 - prevProbability))); + } + } + } + + FixedNode lastSucc = successors[concretes.size()]; + for (int i = concretes.size() - 1; i >= 0; --i) { + LoadMethodNode method = graph.add(new LoadMethodNode(concretes.get(i), hub, constantMethods[i].getKind())); + CompareNode methodCheck = CompareNode.createCompareNode(graph, Condition.EQ, method, constantMethods[i]); + IfNode ifNode = graph.add(new IfNode(methodCheck, successors[i], lastSucc, probability[i])); + method.setNext(ifNode); + lastSucc = method; + } + + FixedWithNextNode pred = (FixedWithNextNode) invoke.asNode().predecessor(); + pred.setNext(lastSucc); + return true; + } else { + Debug.log("Type switch with %d types", concretes.size()); + } + + ResolvedJavaType[] keys = new ResolvedJavaType[ptypes.size()]; + double[] keyProbabilities = new double[ptypes.size() + 1]; + int[] keySuccessors = new int[ptypes.size() + 1]; + for (int i = 0; i < ptypes.size(); i++) { + keys[i] = ptypes.get(i).getType(); + keyProbabilities[i] = ptypes.get(i).getProbability(); + keySuccessors[i] = invokeIsOnlySuccessor ? 0 : typesToConcretes.get(i); + assert keySuccessors[i] < successors.length - 1 : "last successor is the unknownTypeSux"; + } + keyProbabilities[keyProbabilities.length - 1] = notRecordedTypeProbability; + keySuccessors[keySuccessors.length - 1] = successors.length - 1; + + TypeSwitchNode typeSwitch = graph.add(new TypeSwitchNode(hub, successors, keys, keyProbabilities, keySuccessors)); + FixedWithNextNode pred = (FixedWithNextNode) invoke.asNode().predecessor(); + pred.setNext(typeSwitch); + return false; + } + + private boolean chooseMethodDispatch() { + for (ResolvedJavaMethod concrete : concretes) { + if (!concrete.isInVirtualMethodTable()) { + return false; + } + } + + if (concretes.size() == 1 && this.notRecordedTypeProbability > 0) { + // Always chose method dispatch if there is a single concrete method and the call + // site is megamorphic. + return true; + } + + if (concretes.size() == ptypes.size()) { + // Always prefer types over methods if the number of types is smaller than the + // number of methods. + return false; + } + + return chooseMethodDispatchCostBased(); + } + + private boolean chooseMethodDispatchCostBased() { + double remainder = 1.0 - this.notRecordedTypeProbability; + double costEstimateMethodDispatch = remainder; + for (int i = 0; i < concretes.size(); ++i) { + if (i != 0) { + costEstimateMethodDispatch += remainder; + } + remainder -= concretesProbabilities.get(i); + } + + double costEstimateTypeDispatch = 0.0; + remainder = 1.0; + for (int i = 0; i < ptypes.size(); ++i) { + if (i != 0) { + costEstimateTypeDispatch += remainder; + } + remainder -= ptypes.get(i).getProbability(); + } + costEstimateTypeDispatch += notRecordedTypeProbability; + return costEstimateMethodDispatch < costEstimateTypeDispatch; + } + + private static BeginNode createInvocationBlock(StructuredGraph graph, Invoke invoke, MergeNode returnMerge, PhiNode returnValuePhi, MergeNode exceptionMerge, PhiNode exceptionObjectPhi, + boolean useForInlining) { + Invoke duplicatedInvoke = duplicateInvokeForInlining(graph, invoke, exceptionMerge, exceptionObjectPhi, useForInlining); + BeginNode calleeEntryNode = graph.add(new BeginNode()); + calleeEntryNode.setNext(duplicatedInvoke.asNode()); + + AbstractEndNode endNode = graph.add(new EndNode()); + duplicatedInvoke.setNext(endNode); + returnMerge.addForwardEnd(endNode); + + if (returnValuePhi != null) { + returnValuePhi.addInput(duplicatedInvoke.asNode()); + } + return calleeEntryNode; + } + + private static Invoke duplicateInvokeForInlining(StructuredGraph graph, Invoke invoke, MergeNode exceptionMerge, PhiNode exceptionObjectPhi, boolean useForInlining) { + Invoke result = (Invoke) invoke.asNode().copyWithInputs(); + Node callTarget = result.callTarget().copyWithInputs(); + result.asNode().replaceFirstInput(result.callTarget(), callTarget); + result.setUseForInlining(useForInlining); + + Kind kind = invoke.asNode().getKind(); + if (kind != Kind.Void) { + FrameState stateAfter = invoke.stateAfter(); + stateAfter = stateAfter.duplicate(stateAfter.bci); + stateAfter.replaceFirstInput(invoke.asNode(), result.asNode()); + result.setStateAfter(stateAfter); + } + + if (invoke instanceof InvokeWithExceptionNode) { + assert exceptionMerge != null && exceptionObjectPhi != null; + + InvokeWithExceptionNode invokeWithException = (InvokeWithExceptionNode) invoke; + ExceptionObjectNode exceptionEdge = (ExceptionObjectNode) invokeWithException.exceptionEdge(); + FrameState stateAfterException = exceptionEdge.stateAfter(); + + ExceptionObjectNode newExceptionEdge = (ExceptionObjectNode) exceptionEdge.copyWithInputs(); + // set new state (pop old exception object, push new one) + newExceptionEdge.setStateAfter(stateAfterException.duplicateModified(stateAfterException.bci, stateAfterException.rethrowException(), Kind.Object, newExceptionEdge)); + + AbstractEndNode endNode = graph.add(new EndNode()); + newExceptionEdge.setNext(endNode); + exceptionMerge.addForwardEnd(endNode); + exceptionObjectPhi.addInput(newExceptionEdge); + + ((InvokeWithExceptionNode) result).setExceptionEdge(newExceptionEdge); + } + return result; + } + + @Override + public void tryToDevirtualizeInvoke(MetaAccessProvider metaAccess, Assumptions assumptions) { + if (hasSingleMethod()) { + devirtualizeWithTypeSwitch(graph(), InvokeKind.Special, concretes.get(0), metaAccess); + } else { + tryToDevirtualizeMultipleMethods(graph(), metaAccess); + } + } + + private void tryToDevirtualizeMultipleMethods(StructuredGraph graph, MetaAccessProvider metaAccess) { + MethodCallTargetNode methodCallTarget = (MethodCallTargetNode) invoke.callTarget(); + if (methodCallTarget.invokeKind() == InvokeKind.Interface) { + ResolvedJavaMethod targetMethod = methodCallTarget.targetMethod(); + ResolvedJavaType leastCommonType = getLeastCommonType(); + // check if we have a common base type that implements the interface -> in that case + // we have a vtable entry for the interface method and can use a less expensive + // virtual call + if (!leastCommonType.isInterface() && targetMethod.getDeclaringClass().isAssignableFrom(leastCommonType)) { + ResolvedJavaMethod baseClassTargetMethod = leastCommonType.resolveMethod(targetMethod); + if (baseClassTargetMethod != null) { + devirtualizeWithTypeSwitch(graph, InvokeKind.Virtual, leastCommonType.resolveMethod(targetMethod), metaAccess); + } + } + } + } + + private void devirtualizeWithTypeSwitch(StructuredGraph graph, InvokeKind kind, ResolvedJavaMethod target, MetaAccessProvider metaAccess) { + BeginNode invocationEntry = graph.add(new BeginNode()); + BeginNode unknownTypeSux = createUnknownTypeSuccessor(graph); + BeginNode[] successors = new BeginNode[]{invocationEntry, unknownTypeSux}; + createDispatchOnTypeBeforeInvoke(graph, successors, true, metaAccess); + + invocationEntry.setNext(invoke.asNode()); + ValueNode receiver = ((MethodCallTargetNode) invoke.callTarget()).receiver(); + GuardedValueNode anchoredReceiver = createAnchoredReceiver(graph, invocationEntry, target.getDeclaringClass(), receiver, false); + invoke.callTarget().replaceFirstInput(receiver, anchoredReceiver); + replaceInvokeCallTarget(invoke, graph, kind, target); + } + + private static BeginNode createUnknownTypeSuccessor(StructuredGraph graph) { + return BeginNode.begin(graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.TypeCheckedInliningViolated))); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(shouldFallbackToInvoke() ? "megamorphic" : "polymorphic"); + builder.append(", "); + builder.append(concretes.size()); + builder.append(" methods [ "); + for (int i = 0; i < concretes.size(); i++) { + builder.append(MetaUtil.format(" %H.%n(%p):%r", concretes.get(i))); + } + builder.append(" ], "); + builder.append(ptypes.size()); + builder.append(" type checks [ "); + for (int i = 0; i < ptypes.size(); i++) { + builder.append(" "); + builder.append(ptypes.get(i).getType().getName()); + builder.append(ptypes.get(i).getProbability()); + } + builder.append(" ]"); + return builder.toString(); + } + } + + /** + * Represents an inlining opportunity where the current class hierarchy leads to a monomorphic + * target method, but for which an assumption has to be registered because of non-final classes. + */ + private static class AssumptionInlineInfo extends ExactInlineInfo { + + private final Assumption takenAssumption; + + public AssumptionInlineInfo(Invoke invoke, ResolvedJavaMethod concrete, Assumption takenAssumption) { + super(invoke, concrete); + this.takenAssumption = takenAssumption; + } + + @Override + public void inline(Providers providers, Assumptions assumptions) { + assumptions.record(takenAssumption); + super.inline(providers, assumptions); + } + + @Override + public void tryToDevirtualizeInvoke(MetaAccessProvider metaAccess, Assumptions assumptions) { + assumptions.record(takenAssumption); + replaceInvokeCallTarget(invoke, graph(), InvokeKind.Special, concrete); + } + + @Override + public String toString() { + return "assumption " + MetaUtil.format("%H.%n(%p):%r", concrete); + } + } + + /** + * Determines if inlining is possible at the given invoke node. + * + * @param invoke the invoke that should be inlined + * @return an instance of InlineInfo, or null if no inlining is possible at the given invoke + */ + public static InlineInfo getInlineInfo(InliningData data, Invoke invoke, int maxNumberOfMethods, Replacements replacements, Assumptions assumptions, OptimisticOptimizations optimisticOpts) { + if (!checkInvokeConditions(invoke)) { + return null; + } + MethodCallTargetNode callTarget = (MethodCallTargetNode) invoke.callTarget(); + ResolvedJavaMethod targetMethod = callTarget.targetMethod(); + + if (callTarget.invokeKind() == InvokeKind.Special || targetMethod.canBeStaticallyBound()) { + return getExactInlineInfo(data, invoke, replacements, optimisticOpts, targetMethod); + } + + assert callTarget.invokeKind() == InvokeKind.Virtual || callTarget.invokeKind() == InvokeKind.Interface; + + ResolvedJavaType holder = targetMethod.getDeclaringClass(); + if (!(callTarget.receiver().stamp() instanceof ObjectStamp)) { + return null; + } + ObjectStamp receiverStamp = (ObjectStamp) callTarget.receiver().stamp(); + if (receiverStamp.alwaysNull()) { + // Don't inline if receiver is known to be null + return null; + } + if (receiverStamp.type() != null) { + // the invoke target might be more specific than the holder (happens after inlining: + // parameters lose their declared type...) + ResolvedJavaType receiverType = receiverStamp.type(); + if (receiverType != null && holder.isAssignableFrom(receiverType)) { + holder = receiverType; + if (receiverStamp.isExactType()) { + assert targetMethod.getDeclaringClass().isAssignableFrom(holder) : holder + " subtype of " + targetMethod.getDeclaringClass() + " for " + targetMethod; + ResolvedJavaMethod resolvedMethod = holder.resolveMethod(targetMethod); + if (resolvedMethod != null) { + return getExactInlineInfo(data, invoke, replacements, optimisticOpts, resolvedMethod); + } + } + } + } + + if (holder.isArray()) { + // arrays can be treated as Objects + ResolvedJavaMethod resolvedMethod = holder.resolveMethod(targetMethod); + if (resolvedMethod != null) { + return getExactInlineInfo(data, invoke, replacements, optimisticOpts, resolvedMethod); + } + } + + if (assumptions.useOptimisticAssumptions()) { + ResolvedJavaType uniqueSubtype = holder.findUniqueConcreteSubtype(); + if (uniqueSubtype != null) { + ResolvedJavaMethod resolvedMethod = uniqueSubtype.resolveMethod(targetMethod); + if (resolvedMethod != null) { + return getAssumptionInlineInfo(data, invoke, replacements, optimisticOpts, resolvedMethod, new Assumptions.ConcreteSubtype(holder, uniqueSubtype)); + } + } + + ResolvedJavaMethod concrete = holder.findUniqueConcreteMethod(targetMethod); + if (concrete != null) { + return getAssumptionInlineInfo(data, invoke, replacements, optimisticOpts, concrete, new Assumptions.ConcreteMethod(targetMethod, holder, concrete)); + } + } + + // type check based inlining + return getTypeCheckedInlineInfo(data, invoke, maxNumberOfMethods, replacements, targetMethod, optimisticOpts); + } + + private static InlineInfo getAssumptionInlineInfo(InliningData data, Invoke invoke, Replacements replacements, OptimisticOptimizations optimisticOpts, ResolvedJavaMethod concrete, + Assumption takenAssumption) { + assert !concrete.isAbstract(); + if (!checkTargetConditions(data, replacements, invoke, concrete, optimisticOpts)) { + return null; + } + return new AssumptionInlineInfo(invoke, concrete, takenAssumption); + } + + private static InlineInfo getExactInlineInfo(InliningData data, Invoke invoke, Replacements replacements, OptimisticOptimizations optimisticOpts, ResolvedJavaMethod targetMethod) { + assert !targetMethod.isAbstract(); + if (!checkTargetConditions(data, replacements, invoke, targetMethod, optimisticOpts)) { + return null; + } + return new ExactInlineInfo(invoke, targetMethod); + } + + private static InlineInfo getTypeCheckedInlineInfo(InliningData data, Invoke invoke, int maxNumberOfMethods, Replacements replacements, ResolvedJavaMethod targetMethod, + OptimisticOptimizations optimisticOpts) { + JavaTypeProfile typeProfile; + ValueNode receiver = invoke.callTarget().arguments().get(0); + if (receiver instanceof TypeProfileProxyNode) { + TypeProfileProxyNode typeProfileProxyNode = (TypeProfileProxyNode) receiver; + typeProfile = typeProfileProxyNode.getProfile(); + } else { + return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "no type profile exists"); + } + + ProfiledType[] ptypes = typeProfile.getTypes(); + if (ptypes == null || ptypes.length <= 0) { + return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "no types in profile"); + } + + double notRecordedTypeProbability = typeProfile.getNotRecordedProbability(); + if (ptypes.length == 1 && notRecordedTypeProbability == 0) { + if (!optimisticOpts.inlineMonomorphicCalls()) { + return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "inlining monomorphic calls is disabled"); + } + + ResolvedJavaType type = ptypes[0].getType(); + assert type.isArray() || !type.isAbstract(); + ResolvedJavaMethod concrete = type.resolveMethod(targetMethod); + if (!checkTargetConditions(data, replacements, invoke, concrete, optimisticOpts)) { + return null; + } + return new TypeGuardInlineInfo(invoke, concrete, type); + } else { + invoke.setPolymorphic(true); + + if (!optimisticOpts.inlinePolymorphicCalls() && notRecordedTypeProbability == 0) { + return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "inlining polymorphic calls is disabled (%d types)", ptypes.length); + } + if (!optimisticOpts.inlineMegamorphicCalls() && notRecordedTypeProbability > 0) { + // due to filtering impossible types, notRecordedTypeProbability can be > 0 although + // the number of types is lower than what can be recorded in a type profile + return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "inlining megamorphic calls is disabled (%d types, %f %% not recorded types)", ptypes.length, + notRecordedTypeProbability * 100); + } + + // Find unique methods and their probabilities. + ArrayList concreteMethods = new ArrayList<>(); + ArrayList concreteMethodsProbabilities = new ArrayList<>(); + for (int i = 0; i < ptypes.length; i++) { + ResolvedJavaMethod concrete = ptypes[i].getType().resolveMethod(targetMethod); + if (concrete == null) { + return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "could not resolve method"); + } + int index = concreteMethods.indexOf(concrete); + double curProbability = ptypes[i].getProbability(); + if (index < 0) { + index = concreteMethods.size(); + concreteMethods.add(concrete); + concreteMethodsProbabilities.add(curProbability); + } else { + concreteMethodsProbabilities.set(index, concreteMethodsProbabilities.get(index) + curProbability); + } + } + + if (concreteMethods.size() > maxNumberOfMethods) { + return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "polymorphic call with more than %d target methods", maxNumberOfMethods); + } + + // Clear methods that fall below the threshold. + if (notRecordedTypeProbability > 0) { + ArrayList newConcreteMethods = new ArrayList<>(); + ArrayList newConcreteMethodsProbabilities = new ArrayList<>(); + for (int i = 0; i < concreteMethods.size(); ++i) { + if (concreteMethodsProbabilities.get(i) >= MegamorphicInliningMinMethodProbability.getValue()) { + newConcreteMethods.add(concreteMethods.get(i)); + newConcreteMethodsProbabilities.add(concreteMethodsProbabilities.get(i)); + } + } + + if (newConcreteMethods.size() == 0) { + // No method left that is worth inlining. + return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "no methods remaining after filtering less frequent methods (%d methods previously)", + concreteMethods.size()); + } + + concreteMethods = newConcreteMethods; + concreteMethodsProbabilities = newConcreteMethodsProbabilities; + } + + // Clean out types whose methods are no longer available. + ArrayList usedTypes = new ArrayList<>(); + ArrayList typesToConcretes = new ArrayList<>(); + for (ProfiledType type : ptypes) { + ResolvedJavaMethod concrete = type.getType().resolveMethod(targetMethod); + int index = concreteMethods.indexOf(concrete); + if (index == -1) { + notRecordedTypeProbability += type.getProbability(); + } else { + assert type.getType().isArray() || !type.getType().isAbstract() : type + " " + concrete; + usedTypes.add(type); + typesToConcretes.add(index); + } + } + + if (usedTypes.size() == 0) { + // No type left that is worth checking for. + return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "no types remaining after filtering less frequent types (%d types previously)", ptypes.length); + } + + for (ResolvedJavaMethod concrete : concreteMethods) { + if (!checkTargetConditions(data, replacements, invoke, concrete, optimisticOpts)) { + return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "it is a polymorphic method call and at least one invoked method cannot be inlined"); + } + } + return new MultiTypeGuardInlineInfo(invoke, concreteMethods, concreteMethodsProbabilities, usedTypes, typesToConcretes, notRecordedTypeProbability); + } + } + + private static GuardedValueNode createAnchoredReceiver(StructuredGraph graph, GuardingNode anchor, ResolvedJavaType commonType, ValueNode receiver, boolean exact) { + return createAnchoredReceiver(graph, anchor, receiver, exact ? StampFactory.exactNonNull(commonType) : StampFactory.declaredNonNull(commonType)); + } + + private static GuardedValueNode createAnchoredReceiver(StructuredGraph graph, GuardingNode anchor, ValueNode receiver, Stamp stamp) { + // to avoid that floating reads on receiver fields float above the type check + return graph.unique(new GuardedValueNode(receiver, anchor, stamp)); + } + + // TODO (chaeubl): cleanup this method + private static boolean checkInvokeConditions(Invoke invoke) { + if (invoke.predecessor() == null || !invoke.asNode().isAlive()) { + return logNotInlinedMethod(invoke, "the invoke is dead code"); + } else if (!(invoke.callTarget() instanceof MethodCallTargetNode)) { + return logNotInlinedMethod(invoke, "the invoke has already been lowered, or has been created as a low-level node"); + } else if (((MethodCallTargetNode) invoke.callTarget()).targetMethod() == null) { + return logNotInlinedMethod(invoke, "target method is null"); + } else if (invoke.stateAfter() == null) { + // TODO (chaeubl): why should an invoke not have a state after? + return logNotInlinedMethod(invoke, "the invoke has no after state"); + } else if (!invoke.useForInlining()) { + return logNotInlinedMethod(invoke, "the invoke is marked to be not used for inlining"); + } else if (((MethodCallTargetNode) invoke.callTarget()).receiver() != null && ((MethodCallTargetNode) invoke.callTarget()).receiver().isConstant() && + ((MethodCallTargetNode) invoke.callTarget()).receiver().asConstant().isNull()) { + return logNotInlinedMethod(invoke, "receiver is null"); + } else { + return true; + } + } + + private static boolean checkTargetConditions(InliningData data, Replacements replacements, Invoke invoke, ResolvedJavaMethod method, OptimisticOptimizations optimisticOpts) { + if (method == null) { + return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "the method is not resolved"); + } else if (method.isNative() && (!Intrinsify.getValue() || !InliningUtil.canIntrinsify(replacements, method))) { + return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "it is a non-intrinsic native method"); + } else if (method.isAbstract()) { + return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "it is an abstract method"); + } else if (!method.getDeclaringClass().isInitialized()) { + return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "the method's class is not initialized"); + } else if (!method.canBeInlined()) { + return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "it is marked non-inlinable"); + } else if (data.countRecursiveInlining(method) > MaximumRecursiveInlining.getValue()) { + return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "it exceeds the maximum recursive inlining depth"); + } else if (new OptimisticOptimizations(method.getProfilingInfo()).lessOptimisticThan(optimisticOpts)) { + return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "the callee uses less optimistic optimizations than caller"); + } else { + return true; + } + } + + /** + * Performs an actual inlining, thereby replacing the given invoke with the given inlineGraph. + * + * @param invoke the invoke that will be replaced + * @param inlineGraph the graph that the invoke will be replaced with + * @param receiverNullCheck true if a null check needs to be generated for non-static inlinings, + * false if no such check is required + */ + public static Map inline(Invoke invoke, StructuredGraph inlineGraph, boolean receiverNullCheck) { + final NodeInputList parameters = invoke.callTarget().arguments(); + FixedNode invokeNode = invoke.asNode(); + StructuredGraph graph = invokeNode.graph(); + assert inlineGraph.getGuardsStage().ordinal() >= graph.getGuardsStage().ordinal(); + assert !invokeNode.graph().isAfterFloatingReadPhase() : "inline isn't handled correctly after floating reads phase"; + + FrameState stateAfter = invoke.stateAfter(); + assert stateAfter == null || stateAfter.isAlive(); + if (receiverNullCheck && !((MethodCallTargetNode) invoke.callTarget()).isStatic()) { + nonNullReceiver(invoke); + } + + ArrayList nodes = new ArrayList<>(inlineGraph.getNodes().count()); + ArrayList returnNodes = new ArrayList<>(4); + UnwindNode unwindNode = null; + final StartNode entryPointNode = inlineGraph.start(); + FixedNode firstCFGNode = entryPointNode.next(); + if (firstCFGNode == null) { + throw new IllegalStateException("Inlined graph is in invalid state"); + } + for (Node node : inlineGraph.getNodes()) { + if (node == entryPointNode || node == entryPointNode.stateAfter() || node instanceof ParameterNode) { + // Do nothing. + } else { + nodes.add(node); + if (node instanceof ReturnNode) { + returnNodes.add((ReturnNode) node); + } else if (node instanceof UnwindNode) { + assert unwindNode == null; + unwindNode = (UnwindNode) node; + } + } + } + + final BeginNode prevBegin = BeginNode.prevBegin(invokeNode); + DuplicationReplacement localReplacement = new DuplicationReplacement() { + + public Node replacement(Node node) { + if (node instanceof ParameterNode) { + return parameters.get(((ParameterNode) node).index()); + } else if (node == entryPointNode) { + return prevBegin; + } + return node; + } + }; + + assert invokeNode.successors().first() != null : invoke; + assert invokeNode.predecessor() != null; + + Map duplicates = graph.addDuplicates(nodes, inlineGraph, inlineGraph.getNodeCount(), localReplacement); + FixedNode firstCFGNodeDuplicate = (FixedNode) duplicates.get(firstCFGNode); + invokeNode.replaceAtPredecessor(firstCFGNodeDuplicate); + + FrameState stateAtExceptionEdge = null; + if (invoke instanceof InvokeWithExceptionNode) { + InvokeWithExceptionNode invokeWithException = ((InvokeWithExceptionNode) invoke); + if (unwindNode != null) { + assert unwindNode.predecessor() != null; + assert invokeWithException.exceptionEdge().successors().count() == 1; + ExceptionObjectNode obj = (ExceptionObjectNode) invokeWithException.exceptionEdge(); + stateAtExceptionEdge = obj.stateAfter(); + UnwindNode unwindDuplicate = (UnwindNode) duplicates.get(unwindNode); + obj.replaceAtUsages(unwindDuplicate.exception()); + unwindDuplicate.clearInputs(); + Node n = obj.next(); + obj.setNext(null); + unwindDuplicate.replaceAndDelete(n); + } else { + invokeWithException.killExceptionEdge(); + } + + // get rid of memory kill + BeginNode begin = invokeWithException.next(); + if (begin instanceof KillingBeginNode) { + BeginNode newBegin = new BeginNode(); + graph.addAfterFixed(begin, graph.add(newBegin)); + begin.replaceAtUsages(newBegin); + graph.removeFixed(begin); + } + } else { + if (unwindNode != null) { + UnwindNode unwindDuplicate = (UnwindNode) duplicates.get(unwindNode); + DeoptimizeNode deoptimizeNode = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.NotCompiledExceptionHandler)); + unwindDuplicate.replaceAndDelete(deoptimizeNode); + } + } + + if (stateAfter != null) { + processFrameStates(invoke, inlineGraph, duplicates, stateAtExceptionEdge); + int callerLockDepth = stateAfter.nestedLockDepth(); + if (callerLockDepth != 0) { + for (MonitorIdNode original : inlineGraph.getNodes(MonitorIdNode.class)) { + MonitorIdNode monitor = (MonitorIdNode) duplicates.get(original); + monitor.setLockDepth(monitor.getLockDepth() + callerLockDepth); + } + } + } else { + assert checkContainsOnlyInvalidOrAfterFrameState(duplicates); + } + if (!returnNodes.isEmpty()) { + FixedNode n = invoke.next(); + invoke.setNext(null); + if (returnNodes.size() == 1) { + ReturnNode returnNode = (ReturnNode) duplicates.get(returnNodes.get(0)); + Node returnValue = returnNode.result(); + invokeNode.replaceAtUsages(returnValue); + returnNode.clearInputs(); + returnNode.replaceAndDelete(n); + } else { + ArrayList returnDuplicates = new ArrayList<>(returnNodes.size()); + for (ReturnNode returnNode : returnNodes) { + returnDuplicates.add((ReturnNode) duplicates.get(returnNode)); + } + MergeNode merge = graph.add(new MergeNode()); + merge.setStateAfter(stateAfter); + ValueNode returnValue = mergeReturns(merge, returnDuplicates); + invokeNode.replaceAtUsages(returnValue); + merge.setNext(n); + } + } + + invokeNode.replaceAtUsages(null); + GraphUtil.killCFG(invokeNode); + + return duplicates; + } + + protected static void processFrameStates(Invoke invoke, StructuredGraph inlineGraph, Map duplicates, FrameState stateAtExceptionEdge) { + FrameState stateAtReturn = invoke.stateAfter(); + FrameState outerFrameState = null; + Kind invokeReturnKind = invoke.asNode().getKind(); + for (FrameState original : inlineGraph.getNodes(FrameState.class)) { + FrameState frameState = (FrameState) duplicates.get(original); + if (frameState != null && frameState.isAlive()) { + if (frameState.bci == BytecodeFrame.AFTER_BCI) { + /* + * pop return kind from invoke's stateAfter and replace with this frameState's + * return value (top of stack) + */ + FrameState stateAfterReturn = stateAtReturn; + if (invokeReturnKind != Kind.Void && frameState.stackSize() > 0 && stateAfterReturn.stackAt(0) != frameState.stackAt(0)) { + stateAfterReturn = stateAtReturn.duplicateModified(invokeReturnKind, frameState.stackAt(0)); + } + frameState.replaceAndDelete(stateAfterReturn); + } else if (stateAtExceptionEdge != null && isStateAfterException(frameState)) { + /* + * pop exception object from invoke's stateAfter and replace with this + * frameState's exception object (top of stack) + */ + FrameState stateAfterException = stateAtExceptionEdge; + if (frameState.stackSize() > 0 && stateAtExceptionEdge.stackAt(0) != frameState.stackAt(0)) { + stateAfterException = stateAtExceptionEdge.duplicateModified(Kind.Object, frameState.stackAt(0)); + } + frameState.replaceAndDelete(stateAfterException); + } else if (frameState.bci == BytecodeFrame.UNWIND_BCI || frameState.bci == BytecodeFrame.AFTER_EXCEPTION_BCI) { + handleMissingAfterExceptionFrameState(frameState); + } else { + // only handle the outermost frame states + if (frameState.outerFrameState() == null) { + assert frameState.bci != BytecodeFrame.BEFORE_BCI : frameState; + assert frameState.bci == BytecodeFrame.INVALID_FRAMESTATE_BCI || frameState.method().equals(inlineGraph.method()); + assert frameState.bci != BytecodeFrame.AFTER_EXCEPTION_BCI && frameState.bci != BytecodeFrame.BEFORE_BCI && frameState.bci != BytecodeFrame.AFTER_EXCEPTION_BCI && + frameState.bci != BytecodeFrame.UNWIND_BCI : frameState.bci; + if (outerFrameState == null) { + outerFrameState = stateAtReturn.duplicateModified(invoke.bci(), stateAtReturn.rethrowException(), invokeReturnKind); + outerFrameState.setDuringCall(true); + } + frameState.setOuterFrameState(outerFrameState); + } + } + } + } + } + + private static boolean isStateAfterException(FrameState frameState) { + return frameState.bci == BytecodeFrame.AFTER_EXCEPTION_BCI || (frameState.bci == BytecodeFrame.UNWIND_BCI && !frameState.method().isSynchronized()); + } + + protected static void handleMissingAfterExceptionFrameState(FrameState nonReplaceableFrameState) { + Graph graph = nonReplaceableFrameState.graph(); + NodeWorkList workList = graph.createNodeWorkList(); + workList.add(nonReplaceableFrameState); + for (Node node : workList) { + FrameState fs = (FrameState) node; + for (Node usage : fs.usages().snapshot()) { + if (!usage.isAlive()) { + continue; + } + if (usage instanceof FrameState) { + workList.add(usage); + } else { + StateSplit stateSplit = (StateSplit) usage; + FixedNode fixedStateSplit = stateSplit.asNode(); + if (fixedStateSplit instanceof MergeNode) { + MergeNode merge = (MergeNode) fixedStateSplit; + while (merge.isAlive()) { + AbstractEndNode end = merge.forwardEnds().first(); + DeoptimizeNode deoptimizeNode = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.NotCompiledExceptionHandler)); + end.replaceAtPredecessor(deoptimizeNode); + GraphUtil.killCFG(end); + } + } else { + FixedNode deoptimizeNode = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.NotCompiledExceptionHandler)); + if (fixedStateSplit instanceof BeginNode) { + deoptimizeNode = BeginNode.begin(deoptimizeNode); + } + fixedStateSplit.replaceAtPredecessor(deoptimizeNode); + GraphUtil.killCFG(fixedStateSplit); + } + } + } + } + } + + public static ValueNode mergeReturns(MergeNode merge, List returnNodes) { + PhiNode returnValuePhi = null; + + for (ReturnNode returnNode : returnNodes) { + // create and wire up a new EndNode + EndNode endNode = merge.graph().add(new EndNode()); + merge.addForwardEnd(endNode); + + if (returnNode.result() != null) { + if (returnValuePhi == null) { + returnValuePhi = merge.graph().addWithoutUnique(new ValuePhiNode(returnNode.result().stamp().unrestricted(), merge)); + } + returnValuePhi.addInput(returnNode.result()); + } + returnNode.clearInputs(); + returnNode.replaceAndDelete(endNode); + + } + return returnValuePhi; + } + + private static boolean checkContainsOnlyInvalidOrAfterFrameState(Map duplicates) { + for (Node node : duplicates.values()) { + if (node instanceof FrameState) { + FrameState frameState = (FrameState) node; + assert frameState.bci == BytecodeFrame.AFTER_BCI || frameState.bci == BytecodeFrame.INVALID_FRAMESTATE_BCI : node.toString(Verbosity.Debugger); + } + } + return true; + } + + /** + * Gets the receiver for an invoke, adding a guard if necessary to ensure it is non-null. + */ + public static ValueNode nonNullReceiver(Invoke invoke) { + MethodCallTargetNode callTarget = (MethodCallTargetNode) invoke.callTarget(); + assert !callTarget.isStatic() : callTarget.targetMethod(); + StructuredGraph graph = callTarget.graph(); + ValueNode firstParam = callTarget.arguments().get(0); + if (firstParam.getKind() == Kind.Object && !StampTool.isObjectNonNull(firstParam)) { + IsNullNode condition = graph.unique(new IsNullNode(firstParam)); + Stamp stamp = firstParam.stamp().join(objectNonNull()); + GuardingPiNode nonNullReceiver = graph.add(new GuardingPiNode(firstParam, condition, true, NullCheckException, InvalidateReprofile, stamp)); + graph.addBeforeFixed(invoke.asNode(), nonNullReceiver); + callTarget.replaceFirstInput(firstParam, nonNullReceiver); + return nonNullReceiver; + } + return firstParam; + } + + public static boolean canIntrinsify(Replacements replacements, ResolvedJavaMethod target) { + return getIntrinsicGraph(replacements, target) != null || getMacroNodeClass(replacements, target) != null; + } + + public static StructuredGraph getIntrinsicGraph(Replacements replacements, ResolvedJavaMethod target) { + return replacements.getMethodSubstitution(target); + } + + public static Class getMacroNodeClass(Replacements replacements, ResolvedJavaMethod target) { + return replacements.getMacroSubstitution(target); + } + + public static FixedWithNextNode inlineMacroNode(Invoke invoke, ResolvedJavaMethod concrete, Class macroNodeClass) throws GraalInternalError { + StructuredGraph graph = invoke.asNode().graph(); + if (!concrete.equals(((MethodCallTargetNode) invoke.callTarget()).targetMethod())) { + assert ((MethodCallTargetNode) invoke.callTarget()).invokeKind() != InvokeKind.Static; + InliningUtil.replaceInvokeCallTarget(invoke, graph, InvokeKind.Special, concrete); + } + + FixedWithNextNode macroNode = createMacroNodeInstance(macroNodeClass, invoke); + + CallTargetNode callTarget = invoke.callTarget(); + if (invoke instanceof InvokeNode) { + graph.replaceFixedWithFixed((InvokeNode) invoke, graph.add(macroNode)); + } else { + InvokeWithExceptionNode invokeWithException = (InvokeWithExceptionNode) invoke; + invokeWithException.killExceptionEdge(); + graph.replaceSplitWithFixed(invokeWithException, graph.add(macroNode), invokeWithException.next()); + } + GraphUtil.killWithUnusedFloatingInputs(callTarget); + return macroNode; + } + + private static FixedWithNextNode createMacroNodeInstance(Class macroNodeClass, Invoke invoke) throws GraalInternalError { + try { + return macroNodeClass.getConstructor(Invoke.class).newInstance(invoke); + } catch (ReflectiveOperationException | IllegalArgumentException | SecurityException e) { + throw new GraalGraphInternalError(e).addContext(invoke.asNode()).addContext("macroSubstitution", macroNodeClass); + } + } +} diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/graph/ComputeInliningRelevanceClosure.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/graph/ComputeInliningRelevanceClosure.java Fri May 02 02:45:26 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,224 +0,0 @@ -/* - * Copyright (c) 2013, 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.graal.phases.graph; - -import java.util.*; - -import com.oracle.graal.compiler.common.cfg.*; -import com.oracle.graal.graph.*; -import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.cfg.*; -import com.oracle.graal.nodes.util.*; - -public class ComputeInliningRelevanceClosure { - - private static final double EPSILON = 1d / Integer.MAX_VALUE; - - private final StructuredGraph graph; - private final NodesToDoubles nodeProbabilities; - private final NodesToDoubles nodeRelevances; - - public ComputeInliningRelevanceClosure(StructuredGraph graph, NodesToDoubles nodeProbabilities) { - this.graph = graph; - this.nodeProbabilities = nodeProbabilities; - this.nodeRelevances = new NodesToDoubles(graph.getNodeCount()); - } - - public NodesToDoubles apply() { - new ComputeInliningRelevanceIterator(graph).apply(); - return nodeRelevances; - } - - private class ComputeInliningRelevanceIterator extends ScopedPostOrderNodeIterator { - - private final HashMap scopes; - private double currentProbability; - private double parentRelevance; - - public ComputeInliningRelevanceIterator(StructuredGraph graph) { - super(graph); - this.scopes = computeScopesAndProbabilities(); - } - - @Override - protected void initializeScope() { - Scope scope = scopes.get(currentScopeStart); - parentRelevance = getParentScopeRelevance(scope); - currentProbability = scope.probability; - } - - private double getParentScopeRelevance(Scope scope) { - if (scope.start instanceof LoopBeginNode) { - assert scope.parent != null; - double parentProbability = 0; - for (AbstractEndNode end : ((LoopBeginNode) scope.start).forwardEnds()) { - parentProbability += nodeProbabilities.get(end); - } - return parentProbability / scope.parent.probability; - } else { - assert scope.parent == null; - return 1.0; - } - } - - @Override - protected void invoke(Invoke invoke) { - double probability = nodeProbabilities.get(invoke.asNode()); - assert !Double.isNaN(probability); - - double relevance = (probability / currentProbability) * Math.min(1.0, parentRelevance); - nodeRelevances.put(invoke.asNode(), relevance); - assert !Double.isNaN(relevance); - } - - private HashMap computeScopesAndProbabilities() { - HashMap result = new HashMap<>(); - - for (Scope scope : computeScopes()) { - double lowestPathProbability = computeLowestPathProbability(scope); - scope.probability = Math.max(EPSILON, lowestPathProbability); - result.put(scope.start, scope); - } - - return result; - } - - private Scope[] computeScopes() { - ControlFlowGraph cfg = ControlFlowGraph.compute(graph, true, true, false, false); - - List> loops = cfg.getLoops(); - HashMap, Scope> processedScopes = new HashMap<>(); - Scope[] result = new Scope[loops.size() + 1]; - Scope methodScope = new Scope(graph.start(), null); - processedScopes.put(null, methodScope); - - result[0] = methodScope; - for (int i = 0; i < loops.size(); i++) { - result[i + 1] = createScope(loops.get(i), processedScopes); - } - - return result; - } - - private Scope createScope(Loop loop, HashMap, Scope> processedLoops) { - Scope parent = processedLoops.get(loop.parent); - if (parent == null) { - parent = createScope(loop.parent, processedLoops); - } - Scope result = new Scope(loop.header.getBeginNode(), parent); - processedLoops.put(loop, result); - return result; - } - } - - private double computeLowestPathProbability(Scope scope) { - FixedNode scopeStart = scope.start; - ArrayList pathBeginNodes = new ArrayList<>(); - pathBeginNodes.add(scopeStart); - double minPathProbability = nodeProbabilities.get(scopeStart); - boolean isLoopScope = scopeStart instanceof LoopBeginNode; - - do { - Node current = pathBeginNodes.remove(pathBeginNodes.size() - 1); - do { - if (isLoopScope && current instanceof LoopExitNode && ((LoopBeginNode) scopeStart).loopExits().contains((LoopExitNode) current)) { - return minPathProbability; - } else if (current instanceof LoopBeginNode && current != scopeStart) { - current = getMaxProbabilityLoopExit((LoopBeginNode) current, pathBeginNodes); - minPathProbability = getMinPathProbability((FixedNode) current, minPathProbability); - } else if (current instanceof ControlSplitNode) { - current = getMaxProbabilitySux((ControlSplitNode) current, pathBeginNodes); - minPathProbability = getMinPathProbability((FixedNode) current, minPathProbability); - } else { - assert current.successors().count() <= 1; - current = current.successors().first(); - } - } while (current != null); - } while (!pathBeginNodes.isEmpty()); - - return minPathProbability; - } - - private double getMinPathProbability(FixedNode current, double minPathProbability) { - if (current != null && nodeProbabilities.get(current) < minPathProbability) { - return nodeProbabilities.get(current); - } - return minPathProbability; - } - - private static Node getMaxProbabilitySux(ControlSplitNode controlSplit, ArrayList pathBeginNodes) { - Node maxSux = null; - double maxProbability = 0.0; - int pathBeginCount = pathBeginNodes.size(); - - for (Node sux : controlSplit.successors()) { - double probability = controlSplit.probability((BeginNode) sux); - if (probability > maxProbability) { - maxProbability = probability; - maxSux = sux; - truncate(pathBeginNodes, pathBeginCount); - } else if (probability == maxProbability) { - pathBeginNodes.add((FixedNode) sux); - } - } - - return maxSux; - } - - private Node getMaxProbabilityLoopExit(LoopBeginNode loopBegin, ArrayList pathBeginNodes) { - Node maxSux = null; - double maxProbability = 0.0; - int pathBeginCount = pathBeginNodes.size(); - - for (LoopExitNode sux : loopBegin.loopExits()) { - double probability = nodeProbabilities.get(sux); - if (probability > maxProbability) { - maxProbability = probability; - maxSux = sux; - truncate(pathBeginNodes, pathBeginCount); - } else if (probability == maxProbability) { - pathBeginNodes.add(sux); - } - } - - return maxSux; - } - - private static void truncate(ArrayList pathBeginNodes, int pathBeginCount) { - for (int i = pathBeginNodes.size() - pathBeginCount; i > 0; i--) { - pathBeginNodes.remove(pathBeginNodes.size() - 1); - } - } - - private static class Scope { - - public final FixedNode start; - public final Scope parent; - public double probability; - - public Scope(FixedNode start, Scope parent) { - this.start = start; - this.parent = parent; - } - } -} diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/graph/ComputeProbabilityClosure.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/graph/ComputeProbabilityClosure.java Fri May 02 02:45:26 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,295 +0,0 @@ -/* - * 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.graal.phases.graph; - -import java.util.*; - -import com.oracle.graal.graph.*; -import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.util.*; -import com.oracle.graal.phases.util.*; - -/** - * Computes probabilities for nodes in a graph. - *

- * The computation of absolute probabilities works in three steps: - *

    - *
  1. {@link PropagateProbability} traverses the graph in post order (merges after their ends, ...) - * and keeps track of the "probability state". Whenever it encounters a {@link ControlSplitNode} it - * uses the split's probability information to divide the probability upon the successors. Whenever - * it encounters an {@link Invoke} it assumes that the exception edge is unlikely and propagates the - * whole probability to the normal successor. Whenever it encounters a {@link MergeNode} it sums up - * the probability of all predecessors. It also maintains a set of active loops (whose - * {@link LoopBeginNode} has been visited) and builds def/use information for step 2.
  2. - *
  3. - *
  4. {@link PropagateLoopFrequency} propagates the loop frequencies and multiplies each - * {@link FixedNode}'s probability with its loop frequency.
  5. - *
- */ -public class ComputeProbabilityClosure { - - private static final double EPSILON = Double.MIN_NORMAL; - - private final StructuredGraph graph; - private final NodesToDoubles nodeProbabilities; - private final Set loopInfos; - private final Map> mergeLoops; - - public ComputeProbabilityClosure(StructuredGraph graph) { - this.graph = graph; - this.nodeProbabilities = new NodesToDoubles(graph.getNodeCount()); - this.loopInfos = new ArraySet<>(); - this.mergeLoops = new IdentityHashMap<>(); - } - - public NodesToDoubles apply() { - // adjustControlSplitProbabilities(); - new PropagateProbability(graph.start()).apply(); - computeLoopFactors(); - new PropagateLoopFrequency(graph.start()).apply(); - // assert verifyProbabilities(); - return nodeProbabilities; - } - - private void computeLoopFactors() { - for (LoopInfo info : loopInfos) { - double frequency = info.loopFrequency(nodeProbabilities); - assert frequency != -1; - } - } - - public static class LoopInfo { - - public final LoopBeginNode loopBegin; - - public final NodeMap> requires; - - private double loopFrequency = -1.0; - public boolean ended = false; - - public LoopInfo(LoopBeginNode loopBegin) { - this.loopBegin = loopBegin; - this.requires = loopBegin.graph().createNodeMap(); - } - - public double loopFrequency(NodesToDoubles nodeProbabilities) { - // loopFrequency is initialized with -1.0 - if (loopFrequency < 0.0 && ended) { - double backEdgeProb = 0.0; - for (LoopEndNode le : loopBegin.loopEnds()) { - double factor = 1; - Set requireds = requires.get(le); - for (LoopInfo required : requireds) { - double t = required.loopFrequency(nodeProbabilities); - if (t == -1) { - return -1; - } - factor = multiplySaturate(factor, t); - } - backEdgeProb += nodeProbabilities.get(le) * factor; - } - double entryProb = nodeProbabilities.get(loopBegin); - double d = entryProb - backEdgeProb; - if (d <= EPSILON) { - d = EPSILON; - } - loopFrequency = entryProb / d; - loopBegin.setLoopFrequency(loopFrequency); - } - return loopFrequency; - } - } - - /** - * Multiplies a and b and saturates the result to 1/{@link Double#MIN_NORMAL}. - * - * @param a - * @param b - * @return a times b saturated to 1/{@link Double#MIN_NORMAL} - */ - public static double multiplySaturate(double a, double b) { - double r = a * b; - if (r > 1 / Double.MIN_NORMAL) { - return 1 / Double.MIN_NORMAL; - } - return r; - } - - private class Probability extends MergeableState implements Cloneable { - - public double probability; - public Set loops; - public LoopInfo loopInfo; - - public Probability(double probability, Set loops) { - assert probability >= 0.0; - this.probability = probability; - this.loops = new ArraySet<>(4); - if (loops != null) { - this.loops.addAll(loops); - } - } - - @Override - public Probability clone() { - return new Probability(probability, loops); - } - - @Override - public boolean merge(MergeNode merge, List withStates) { - if (merge.forwardEndCount() > 1) { - Set intersection = new ArraySet<>(loops); - for (Probability other : withStates) { - intersection.retainAll(other.loops); - } - for (LoopInfo info : loops) { - if (!intersection.contains(info)) { - double loopFrequency = info.loopFrequency(nodeProbabilities); - if (loopFrequency == -1) { - return false; - } - probability = multiplySaturate(probability, loopFrequency); - assert probability >= 0; - } - } - for (Probability other : withStates) { - double prob = other.probability; - for (LoopInfo info : other.loops) { - if (!intersection.contains(info)) { - double loopFrequency = info.loopFrequency(nodeProbabilities); - if (loopFrequency == -1) { - return false; - } - prob = multiplySaturate(prob, loopFrequency); - assert prob >= 0; - } - } - probability += prob; - assert probability >= 0; - } - loops = intersection; - mergeLoops.put(merge, new ArraySet<>(intersection)); - probability = Math.max(0.0, probability); - } - return true; - } - - @Override - public void loopBegin(LoopBeginNode loopBegin) { - loopInfo = new LoopInfo(loopBegin); - loopInfos.add(loopInfo); - loops.add(loopInfo); - } - - @Override - public void loopEnds(LoopBeginNode loopBegin, List loopEndStates) { - assert loopInfo != null; - List loopEnds = loopBegin.orderedLoopEnds(); - int i = 0; - for (Probability proba : loopEndStates) { - LoopEndNode loopEnd = loopEnds.get(i++); - Set requires = loopInfo.requires.get(loopEnd); - if (requires == null) { - requires = new HashSet<>(); - loopInfo.requires.set(loopEnd, requires); - } - for (LoopInfo innerLoop : proba.loops) { - if (innerLoop != loopInfo && !this.loops.contains(innerLoop)) { - requires.add(innerLoop); - } - } - } - loopInfo.ended = true; - } - - @Override - public void afterSplit(BeginNode node) { - assert node.predecessor() != null; - Node pred = node.predecessor(); - ControlSplitNode x = (ControlSplitNode) pred; - double nodeProbability = x.probability(node); - assert nodeProbability >= 0.0 : "Node " + x + " provided negative probability for begin " + node + ": " + nodeProbability; - probability *= nodeProbability; - assert probability >= 0.0; - } - } - - private class PropagateProbability extends PostOrderNodeIterator { - - public PropagateProbability(FixedNode start) { - super(start, new Probability(1d, null)); - } - - @Override - protected void node(FixedNode node) { - nodeProbabilities.put(node, state.probability); - } - } - - private class LoopCount extends MergeableState implements Cloneable { - - public double count; - - public LoopCount(double count) { - this.count = count; - } - - @Override - public LoopCount clone() { - return new LoopCount(count); - } - - @Override - public boolean merge(MergeNode merge, List withStates) { - assert merge.forwardEndCount() == withStates.size() + 1; - if (merge.forwardEndCount() > 1) { - Set loops = mergeLoops.get(merge); - assert loops != null; - double countProd = 1; - for (LoopInfo loop : loops) { - countProd = multiplySaturate(countProd, loop.loopFrequency(nodeProbabilities)); - } - count = countProd; - } - return true; - } - - @Override - public void loopBegin(LoopBeginNode loopBegin) { - count = multiplySaturate(count, loopBegin.loopFrequency()); - } - } - - private class PropagateLoopFrequency extends PostOrderNodeIterator { - - public PropagateLoopFrequency(FixedNode start) { - super(start, new LoopCount(1d)); - } - - @Override - protected void node(FixedNode node) { - nodeProbabilities.put(node, nodeProbabilities.get(node) * state.count); - } - - } -} diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/graph/FixedNodeProbabilityCache.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/graph/FixedNodeProbabilityCache.java Sat May 03 21:46:35 2014 +0200 @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2014, 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.graal.phases.graph; + +import java.util.*; +import java.util.function.*; + +import com.oracle.graal.debug.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.nodes.*; + +/** + * Compute probabilities for fixed nodes on the fly and cache at {@link BeginNode}s and + * {@link ControlSplitNode}s. + */ +public class FixedNodeProbabilityCache implements ToDoubleFunction { + + private static final DebugMetric metricComputeNodeProbability = Debug.metric("ComputeNodeProbability"); + + private final IdentityHashMap cache = new IdentityHashMap<>(); + + public double applyAsDouble(FixedNode node) { + metricComputeNodeProbability.increment(); + + FixedNode current = node; + while (true) { + Node predecessor = current.predecessor(); + if (current instanceof BeginNode) { + if (predecessor == null) { + break; + } else if (predecessor.successors().count() != 1) { + assert predecessor instanceof ControlSplitNode : "a FixedNode with multiple successors needs to be a ControlSplitNode: " + current + " / " + predecessor; + break; + } + } + current = (FixedNode) predecessor; + } + + Double cachedValue = cache.get(current); + if (cachedValue != null) { + return cachedValue; + } + + double probability; + if (current.predecessor() == null) { + if (current instanceof MergeNode) { + probability = ((MergeNode) current).forwardEnds().stream().mapToDouble(end -> applyAsDouble(end)).sum(); + if (current instanceof LoopBeginNode) { + probability *= ((LoopBeginNode) current).loopFrequency(); + } + } else if (current instanceof StartNode) { + probability = 1D; + } else { + // this should only appear for dead code + probability = 1D; + } + } else { + ControlSplitNode split = (ControlSplitNode) current.predecessor(); + probability = split.probability((BeginNode) current) * applyAsDouble(split); + } + cache.put(current, probability); + return probability; + } +} diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/graph/ReentrantNodeIterator.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/graph/ReentrantNodeIterator.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/graph/ReentrantNodeIterator.java Sat May 03 21:46:35 2014 +0200 @@ -47,7 +47,7 @@ /** * Determine whether iteration should continue in the current state. - * + * * @param currentState */ protected boolean continueIteration(StateT currentState) { @@ -60,11 +60,7 @@ } public static LoopInfo processLoop(NodeIteratorClosure closure, LoopBeginNode loop, StateT initialState) { - HashSet boundary = new HashSet<>(); - for (LoopExitNode exit : loop.loopExits()) { - boundary.add(exit); - } - Map blockEndStates = apply(closure, loop, initialState, boundary); + Map blockEndStates = apply(closure, loop, initialState, loop); LoopInfo info = new LoopInfo<>(); for (LoopEndNode end : loop.loopEnds()) { @@ -80,7 +76,11 @@ return info; } - public static Map apply(NodeIteratorClosure closure, FixedNode start, StateT initialState, Set boundary) { + public static Map apply(NodeIteratorClosure closure, FixedNode start, StateT initialState) { + return apply(closure, start, initialState, null); + } + + private static Map apply(NodeIteratorClosure closure, FixedNode start, StateT initialState, LoopBeginNode boundary) { Deque nodeQueue = new ArrayDeque<>(); IdentityHashMap blockEndStates = new IdentityHashMap<>(); @@ -88,7 +88,7 @@ FixedNode current = start; do { while (current instanceof FixedWithNextNode) { - if (boundary != null && boundary.contains(current)) { + if (boundary != null && current instanceof LoopExitNode && ((LoopExitNode) current).loopBegin() == boundary) { blockEndStates.put(current, state); current = null; } else { diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.printer/src/com/oracle/graal/printer/BinaryGraphPrinter.java --- a/graal/com.oracle.graal.printer/src/com/oracle/graal/printer/BinaryGraphPrinter.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.printer/src/com/oracle/graal/printer/BinaryGraphPrinter.java Sat May 03 21:46:35 2014 +0200 @@ -29,6 +29,7 @@ import java.nio.channels.*; import java.util.*; import java.util.Map.Entry; +import java.util.function.*; import com.oracle.graal.api.meta.*; import com.oracle.graal.compiler.common.cfg.*; @@ -36,7 +37,6 @@ import com.oracle.graal.graph.NodeClass.Position; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.cfg.*; -import com.oracle.graal.nodes.util.*; import com.oracle.graal.phases.graph.*; import com.oracle.graal.phases.schedule.*; @@ -394,10 +394,10 @@ } private void writeNodes(Graph graph) throws IOException { - NodesToDoubles probabilities = null; + ToDoubleFunction probabilities = null; if (PrintGraphProbabilities.getValue()) { try { - probabilities = new ComputeProbabilityClosure((StructuredGraph) graph).apply(); + probabilities = new FixedNodeProbabilityCache(); } catch (Throwable t) { } } @@ -408,8 +408,8 @@ for (Node node : graph.getNodes()) { NodeClass nodeClass = node.getNodeClass(); node.getDebugProperties(props); - if (probabilities != null && node instanceof FixedNode && probabilities.contains((FixedNode) node)) { - props.put("probability", probabilities.get((FixedNode) node)); + if (probabilities != null && node instanceof FixedNode) { + props.put("probability", probabilities.applyAsDouble((FixedNode) node)); } writeInt(getNodeId(node)); writePoolObject(nodeClass); diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/ArraysSubstitutionsTest.java --- a/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/ArraysSubstitutionsTest.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/ArraysSubstitutionsTest.java Sat May 03 21:46:35 2014 +0200 @@ -31,6 +31,7 @@ import com.oracle.graal.nodes.*; import com.oracle.graal.phases.*; import com.oracle.graal.phases.common.*; +import com.oracle.graal.phases.common.inlining.*; import com.oracle.graal.phases.tiers.*; import com.oracle.graal.replacements.*; import com.oracle.graal.replacements.nodes.*; diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/MethodSubstitutionTest.java --- a/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/MethodSubstitutionTest.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/MethodSubstitutionTest.java Sat May 03 21:46:35 2014 +0200 @@ -23,7 +23,6 @@ package com.oracle.graal.replacements.test; import static org.junit.Assert.*; - import com.oracle.graal.api.code.*; import com.oracle.graal.api.replacements.*; import com.oracle.graal.compiler.test.*; @@ -33,6 +32,7 @@ import com.oracle.graal.nodes.*; import com.oracle.graal.phases.*; import com.oracle.graal.phases.common.*; +import com.oracle.graal.phases.common.inlining.*; import com.oracle.graal.phases.tiers.*; /** diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/CollapseFrameForSingleSideEffectPhase.java --- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/CollapseFrameForSingleSideEffectPhase.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/CollapseFrameForSingleSideEffectPhase.java Sat May 03 21:46:35 2014 +0200 @@ -36,11 +36,11 @@ /** * This phase ensures that there's a single {@linkplain BytecodeFrame#AFTER_BCI collapsed frame * state} per path. - * + * * Removes other frame states from {@linkplain StateSplit#hasSideEffect() non-side-effecting} nodes * in the graph, and replaces them with {@linkplain BytecodeFrame#INVALID_FRAMESTATE_BCI invalid * frame states}. - * + * * The invalid frame states ensure that no deoptimization to a snippet frame state will happen. */ public class CollapseFrameForSingleSideEffectPhase extends Phase { @@ -112,7 +112,7 @@ @Override protected void run(StructuredGraph graph) { CollapseFrameForSingleSideEffectClosure closure = new CollapseFrameForSingleSideEffectClosure(); - ReentrantNodeIterator.apply(closure, graph.start(), new IterationState(), null); + ReentrantNodeIterator.apply(closure, graph.start(), new IterationState()); closure.finishProcessing(graph); } diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/GraphKit.java --- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/GraphKit.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/GraphKit.java Sat May 03 21:46:35 2014 +0200 @@ -35,6 +35,7 @@ import com.oracle.graal.nodes.java.*; import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind; import com.oracle.graal.phases.common.*; +import com.oracle.graal.phases.common.inlining.*; import com.oracle.graal.phases.util.*; import com.oracle.graal.replacements.ReplacementsImpl.FrameStateProcessing; import com.oracle.graal.word.phases.*; diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java --- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java Sat May 03 21:46:35 2014 +0200 @@ -49,6 +49,7 @@ import com.oracle.graal.nodes.spi.*; import com.oracle.graal.phases.*; import com.oracle.graal.phases.common.*; +import com.oracle.graal.phases.common.inlining.*; import com.oracle.graal.phases.tiers.*; import com.oracle.graal.phases.util.*; import com.oracle.graal.replacements.Snippet.DefaultSnippetInliningPolicy; diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/SnippetTemplate.java --- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/SnippetTemplate.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/SnippetTemplate.java Sat May 03 21:46:35 2014 +0200 @@ -23,7 +23,6 @@ package com.oracle.graal.replacements; import static com.oracle.graal.api.meta.LocationIdentity.*; - import static com.oracle.graal.api.meta.MetaUtil.*; import static com.oracle.graal.debug.Debug.*; import static java.util.FormattableFlags.*; @@ -54,6 +53,7 @@ import com.oracle.graal.nodes.util.*; import com.oracle.graal.phases.common.*; import com.oracle.graal.phases.common.FloatingReadPhase.MemoryMapImpl; +import com.oracle.graal.phases.common.inlining.*; import com.oracle.graal.phases.tiers.*; import com.oracle.graal.phases.util.*; import com.oracle.graal.replacements.Snippet.ConstantParameter; diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/MacroNode.java --- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/MacroNode.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/MacroNode.java Sat May 03 21:46:35 2014 +0200 @@ -35,6 +35,7 @@ import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind; import com.oracle.graal.nodes.spi.*; import com.oracle.graal.phases.common.*; +import com.oracle.graal.phases.common.inlining.*; import com.oracle.graal.phases.tiers.*; import com.oracle.graal.replacements.*; diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.truffle.hotspot/src/com/oracle/graal/truffle/hotspot/HotSpotTruffleRuntime.java --- a/graal/com.oracle.graal.truffle.hotspot/src/com/oracle/graal/truffle/hotspot/HotSpotTruffleRuntime.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.truffle.hotspot/src/com/oracle/graal/truffle/hotspot/HotSpotTruffleRuntime.java Sat May 03 21:46:35 2014 +0200 @@ -45,14 +45,14 @@ import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.spi.*; import com.oracle.graal.phases.*; -import com.oracle.graal.phases.common.*; +import com.oracle.graal.phases.common.inlining.*; import com.oracle.graal.phases.tiers.*; import com.oracle.graal.phases.util.*; import com.oracle.graal.printer.*; import com.oracle.graal.runtime.*; import com.oracle.graal.truffle.*; import com.oracle.truffle.api.*; -import com.oracle.truffle.api.CompilerDirectives.*; +import com.oracle.truffle.api.CompilerDirectives.SlowPath; import com.oracle.truffle.api.frame.*; import com.oracle.truffle.api.impl.*; import com.oracle.truffle.api.nodes.*; diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/PartialEvaluationTest.java --- a/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/PartialEvaluationTest.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/PartialEvaluationTest.java Sat May 03 21:46:35 2014 +0200 @@ -37,6 +37,7 @@ import com.oracle.graal.nodes.spi.*; import com.oracle.graal.phases.*; import com.oracle.graal.phases.common.*; +import com.oracle.graal.phases.common.inlining.*; import com.oracle.graal.phases.tiers.*; import com.oracle.graal.printer.*; import com.oracle.graal.truffle.*; diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java --- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java Sat May 03 21:46:35 2014 +0200 @@ -48,6 +48,7 @@ import com.oracle.graal.phases.*; import com.oracle.graal.phases.common.*; import com.oracle.graal.phases.common.CanonicalizerPhase.CustomCanonicalizer; +import com.oracle.graal.phases.common.inlining.*; import com.oracle.graal.phases.tiers.*; import com.oracle.graal.phases.util.*; import com.oracle.graal.truffle.nodes.asserts.*; diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCacheImpl.java --- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCacheImpl.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCacheImpl.java Sat May 03 21:46:35 2014 +0200 @@ -43,6 +43,7 @@ import com.oracle.graal.nodes.util.*; import com.oracle.graal.phases.*; import com.oracle.graal.phases.common.*; +import com.oracle.graal.phases.common.inlining.*; import com.oracle.graal.phases.tiers.*; import com.oracle.graal.phases.util.*; import com.oracle.graal.truffle.phases.*; diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/phases/ReplaceIntrinsicsPhase.java --- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/phases/ReplaceIntrinsicsPhase.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/phases/ReplaceIntrinsicsPhase.java Sat May 03 21:46:35 2014 +0200 @@ -28,7 +28,7 @@ import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind; import com.oracle.graal.nodes.spi.*; import com.oracle.graal.phases.*; -import com.oracle.graal.phases.common.*; +import com.oracle.graal.phases.common.inlining.*; /** * Compiler phase for intrinsifying the access to the Truffle virtual frame. diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/EffectsPhase.java --- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/EffectsPhase.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/EffectsPhase.java Sat May 03 21:46:35 2014 +0200 @@ -24,6 +24,8 @@ import static com.oracle.graal.debug.Debug.*; +import java.util.*; + import com.oracle.graal.debug.*; import com.oracle.graal.debug.Debug.Scope; import com.oracle.graal.graph.*; @@ -46,7 +48,7 @@ } private final int maxIterations; - private final CanonicalizerPhase canonicalizer; + protected final CanonicalizerPhase canonicalizer; public EffectsPhase(int maxIterations, CanonicalizerPhase canonicalizer) { this.maxIterations = maxIterations; @@ -85,19 +87,24 @@ new DeadCodeEliminationPhase().apply(graph); + Set changedNodes = listener.getChangedNodes(); for (Node node : graph.getNodes()) { if (node instanceof Simplifiable) { - listener.getChangedNodes().add(node); + changedNodes.add(node); } } - if (canonicalizer != null) { - canonicalizer.applyIncremental(graph, context, listener.getChangedNodes()); - } + postIteration(graph, context, changedNodes); } changed = true; } return changed; } + protected void postIteration(final StructuredGraph graph, final PhaseContextT context, Set changedNodes) { + if (canonicalizer != null) { + canonicalizer.applyIncremental(graph, context, changedNodes); + } + } + protected abstract Closure createEffectsClosure(PhaseContextT context, SchedulePhase schedule); } diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/IterativeInliningPhase.java --- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/IterativeInliningPhase.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/IterativeInliningPhase.java Sat May 03 21:46:35 2014 +0200 @@ -31,7 +31,8 @@ import com.oracle.graal.debug.Debug.Scope; import com.oracle.graal.nodes.*; import com.oracle.graal.phases.common.*; -import com.oracle.graal.phases.common.cfs.IterativeFlowSensitiveReductionPhase; +import com.oracle.graal.phases.common.cfs.*; +import com.oracle.graal.phases.common.inlining.*; import com.oracle.graal.phases.tiers.*; public class IterativeInliningPhase extends AbstractInliningPhase { diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapePhase.java --- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapePhase.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapePhase.java Sat May 03 21:46:35 2014 +0200 @@ -26,14 +26,15 @@ import static com.oracle.graal.virtual.phases.ea.PartialEscapePhase.Options.*; import java.util.*; +import java.util.function.*; import com.oracle.graal.graph.*; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.java.*; import com.oracle.graal.nodes.spi.*; -import com.oracle.graal.nodes.util.*; import com.oracle.graal.nodes.virtual.*; import com.oracle.graal.options.*; +import com.oracle.graal.phases.*; import com.oracle.graal.phases.common.*; import com.oracle.graal.phases.graph.*; import com.oracle.graal.phases.schedule.*; @@ -42,7 +43,6 @@ public class PartialEscapePhase extends EffectsPhase { static class Options { - //@formatter:off @Option(help = "") public static final OptionValue OptEarlyReadElimination = new OptionValue<>(true); @@ -50,14 +50,28 @@ } private final boolean readElimination; + private final BasePhase cleanupPhase; public PartialEscapePhase(boolean iterative, CanonicalizerPhase canonicalizer) { - this(iterative, OptEarlyReadElimination.getValue(), canonicalizer); + this(iterative, OptEarlyReadElimination.getValue(), canonicalizer, null); + } + + public PartialEscapePhase(boolean iterative, CanonicalizerPhase canonicalizer, BasePhase cleanupPhase) { + this(iterative, OptEarlyReadElimination.getValue(), canonicalizer, cleanupPhase); } - public PartialEscapePhase(boolean iterative, boolean readElimination, CanonicalizerPhase canonicalizer) { + public PartialEscapePhase(boolean iterative, boolean readElimination, CanonicalizerPhase canonicalizer, BasePhase cleanupPhase) { super(iterative ? EscapeAnalysisIterations.getValue() : 1, canonicalizer); this.readElimination = readElimination; + this.cleanupPhase = cleanupPhase; + } + + @Override + protected void postIteration(StructuredGraph graph, PhaseContext context, Set changedNodes) { + super.postIteration(graph, context, changedNodes); + if (cleanupPhase != null) { + cleanupPhase.apply(graph, context); + } } @Override @@ -79,7 +93,7 @@ } public static Map getHints(StructuredGraph graph) { - NodesToDoubles probabilities = new ComputeProbabilityClosure(graph).apply(); + ToDoubleFunction probabilities = new FixedNodeProbabilityCache(); Map hints = null; for (CommitAllocationNode commit : graph.getNodes().filter(CommitAllocationNode.class)) { double sum = 0; @@ -87,14 +101,14 @@ for (Node commitUsage : commit.usages()) { for (Node usage : commitUsage.usages()) { if (usage instanceof FixedNode) { - sum += probabilities.get((FixedNode) usage); + sum += probabilities.applyAsDouble((FixedNode) usage); } else { if (usage instanceof MethodCallTargetNode) { - invokeSum += probabilities.get(((MethodCallTargetNode) usage).invoke().asNode()); + invokeSum += probabilities.applyAsDouble(((MethodCallTargetNode) usage).invoke().asNode()); } for (Node secondLevelUage : usage.usages()) { if (secondLevelUage instanceof FixedNode) { - sum += probabilities.get(((FixedNode) secondLevelUage)); + sum += probabilities.applyAsDouble(((FixedNode) secondLevelUage)); } } } diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/LazyClassLoadingTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/LazyClassLoadingTest.java Sat May 03 21:46:35 2014 +0200 @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2014, 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.truffle.api.dsl.test; + +import java.lang.reflect.*; + +import org.junit.*; + +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.dsl.test.LazyClassLoadingTestFactory.TestNodeFactory; +import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode; + +public class LazyClassLoadingTest { + @Test + public void test() { + String testClassName = getClass().getName(); + String factoryClassName = testClassName + "Factory"; + String nodeFactoryClassName = factoryClassName + "$TestNodeFactory"; + + Assert.assertFalse(isLoaded(factoryClassName + "$TestNode")); + Assert.assertFalse(isLoaded(nodeFactoryClassName)); + + NodeFactory factory = TestNodeFactory.getInstance(); + + Assert.assertTrue(isLoaded(nodeFactoryClassName)); + Assert.assertFalse(isLoaded(nodeFactoryClassName + "$TestBaseNode")); + + TestHelper.createRoot(factory); + + Assert.assertTrue(isLoaded(nodeFactoryClassName + "$TestBaseNode")); + Assert.assertTrue(isLoaded(nodeFactoryClassName + "$TestUninitializedNode")); + Assert.assertFalse(isLoaded(nodeFactoryClassName + "$TestGenericNode")); + } + + private boolean isLoaded(String className) { + ClassLoader classLoader = getClass().getClassLoader(); + Method m; + try { + m = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class); + m.setAccessible(true); + return m.invoke(classLoader, className) != null; + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + @SuppressWarnings("unused") + @NodeChildren({@NodeChild("left"), @NodeChild("right")}) + abstract static class TestNode extends ValueNode { + @Specialization(order = 1) + int add(int left, int right) { + return 42; + } + + @Specialization(order = 2) + int add(boolean left, boolean right) { + return 21; + } + + @Specialization(order = 4) + String add(boolean left, int right) { + return "(boolean,int)"; + } + } +} diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ChildNodeTest.java --- a/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ChildNodeTest.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ChildNodeTest.java Sat May 03 21:46:35 2014 +0200 @@ -36,10 +36,8 @@ * *

* Child nodes are stored in the class of the parent node in fields that are marked with the - * {@link Child} annotation. Before such a field is assigned, {@link Node#adoptChild} must be - * called. This method automatically establishes a link from the child to the parent. The - * {@link Node#getParent()} method allows access to this field. Every node also provides the ability - * to iterate over its children using {@link Node#getChildren()}. + * {@link Child} annotation. The {@link Node#getParent()} method allows access to this field. Every + * node also provides the ability to iterate over its children using {@link Node#getChildren()}. *

* *

diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultVisualizer.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultVisualizer.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultVisualizer.java Sat May 03 21:46:35 2014 +0200 @@ -24,8 +24,6 @@ */ package com.oracle.truffle.api.instrument.impl; -import java.io.*; - import com.oracle.truffle.api.*; import com.oracle.truffle.api.frame.*; import com.oracle.truffle.api.instrument.*; @@ -53,16 +51,7 @@ section = node.getEncapsulatingSourceSection(); estimated = true; } - - String sourceString; - if (section == null || section.getSource() == null) { - sourceString = ""; - } else { - String sourceName = new File(section.getSource().getName()).getName(); - int startLine = section.getStartLine(); - sourceString = String.format("%s:%d%s", sourceName, startLine, estimated ? "~" : ""); - } - return sourceString; + return section.getShortDescription() + (estimated ? "~" : ""); } public String displayMethodName(Node node) { diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java Sat May 03 21:46:35 2014 +0200 @@ -134,31 +134,6 @@ * @param newChildren the array of new children whose parent should be updated * @return the array of new children */ - @SuppressWarnings("static-method") - @Deprecated - protected final T[] adoptChildren(final T[] newChildren) { - return newChildren; - } - - /** - * Method that updates the link to the parent in the specified new child node to this node. - * - * @param newChild the new child whose parent should be updated - * @return the new child - */ - @SuppressWarnings("static-method") - @Deprecated - protected final T adoptChild(final T newChild) { - return newChild; - } - - /** - * Method that updates the link to the parent in the array of specified new child nodes to this - * node. - * - * @param newChildren the array of new children whose parent should be updated - * @return the array of new children - */ protected final T[] insert(final T[] newChildren) { CompilerDirectives.transferToInterpreterAndInvalidate(); assert newChildren != null; diff -r 5ecbed00da23 -r ff5cacf47b68 graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeCodeGenerator.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeCodeGenerator.java Fri May 02 02:45:26 2014 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeCodeGenerator.java Sat May 03 21:46:35 2014 +0200 @@ -49,6 +49,8 @@ private static final String EXECUTE_POLYMORPHIC_NAME = "executePolymorphic0"; private static final String UPDATE_TYPES_NAME = "updateTypes"; + private static final String COPY_WITH_CONSTRUCTOR_NAME = "copyWithConstructor"; + private static final String CREATE_SPECIALIZATION_NAME = "createSpecialization"; public NodeCodeGenerator(ProcessorContext context) { super(context); @@ -834,7 +836,7 @@ if (node.getSpecializations().isEmpty()) { body.nullLiteral(); } else { - body.startNew(nodeSpecializationClassName(node.getSpecializations().get(0))); + body.startCall(nodeSpecializationClassName(node.getSpecializations().get(0)), CREATE_SPECIALIZATION_NAME); for (VariableElement var : method.getParameters()) { body.string(var.getSimpleName().toString()); } @@ -865,7 +867,7 @@ if (found == null) { body.startThrow().startNew(getContext().getType(UnsupportedOperationException.class)).end().end(); } else { - body.startReturn().startNew(nodeSpecializationClassName(found)).startGroup().cast(baseClassName(node)).string(THIS_NODE_LOCAL_VAR_NAME).end().end().end(); + body.startReturn().startCall(nodeSpecializationClassName(found), CREATE_SPECIALIZATION_NAME).startGroup().string(THIS_NODE_LOCAL_VAR_NAME).end().end().end(); } return method; } @@ -952,7 +954,6 @@ if (node.getGenericSpecialization() != null && node.getGenericSpecialization().isReachable()) { clazz.add(createGenericExecute(node, rootGroup)); } - } protected boolean needsInvokeCopyConstructorMethod() { @@ -960,7 +961,7 @@ } protected CodeExecutableElement createCopy(TypeMirror baseType, SpecializationData specialization) { - CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), baseType, "copyWithConstructor"); + CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), baseType, COPY_WITH_CONSTRUCTOR_NAME); if (specialization == null) { method.getModifiers().add(ABSTRACT); } else { @@ -968,7 +969,7 @@ builder.startReturn(); builder.startNew(getElement().asType()); builder.string("this"); - for (ActualParameter param : getImplicitTypeParamters(specialization)) { + for (ActualParameter param : getImplicitTypeParameters(specialization)) { builder.string(implicitTypeName(param)); } builder.end().end(); @@ -1738,7 +1739,7 @@ } else { replaceCall.startCall("replace"); } - replaceCall.startGroup().startNew(className).string(source); + replaceCall.startGroup().cast(baseClassName(current.getNode())).startCall(className, CREATE_SPECIALIZATION_NAME).string(source); for (ActualParameter param : current.getSignatureParameters()) { NodeChildData child = param.getSpecification().getExecution().getChild(); List types = child.getNodeData().getTypeSystem().lookupSourceTypes(param.getTypeSystemType()); @@ -1762,7 +1763,7 @@ String uninitializedName = nodeSpecializationClassName(node.getUninitializedSpecialization()); CodeTreeBuilder builder = parent.create(); - builder.declaration(getElement().asType(), "currentCopy", currentNode + ".copyWithConstructor()"); + builder.declaration(getElement().asType(), "currentCopy", currentNode + "." + COPY_WITH_CONSTRUCTOR_NAME + "()"); for (ActualParameter param : getModel().getSignatureParameters()) { NodeExecutionData execution = param.getSpecification().getExecution(); builder.startStatement().tree(createAccessChild(execution, "currentCopy")).string(" = ").nullLiteral().end(); @@ -1928,7 +1929,7 @@ } } - protected final List getImplicitTypeParamters(SpecializationData model) { + protected final List getImplicitTypeParameters(SpecializationData model) { List parameter = new ArrayList<>(); for (ActualParameter param : model.getSignatureParameters()) { NodeChildData child = param.getSpecification().getExecution().getChild(); @@ -2546,6 +2547,18 @@ if (needsInvokeCopyConstructorMethod()) { clazz.add(createCopy(nodeGen.asType(), specialization)); } + + if (!specialization.isUninitialized() && specialization.getNode().needsRewrites(context)) { + clazz.add(createCopyConstructorFactoryMethod(nodeGen.asType(), specialization)); + } else { + for (ExecutableElement constructor : ElementFilter.constructorsIn(clazz.getEnclosedElements())) { + if (constructor.getParameters().size() == 1 && ((CodeVariableElement) constructor.getParameters().get(0)).getType().equals(nodeGen.asType())) { + // skip copy constructor - not used + continue; + } + clazz.add(createConstructorFactoryMethod(specialization, constructor)); + } + } } protected void createConstructors(CodeTypeElement clazz) { @@ -2579,7 +2592,7 @@ } } if (superConstructor != null) { - for (ActualParameter param : getImplicitTypeParamters(getModel())) { + for (ActualParameter param : getImplicitTypeParameters(getModel())) { clazz.add(new CodeVariableElement(modifiers(PRIVATE, FINAL), getContext().getType(Class.class), implicitTypeName(param))); superConstructor.getParameters().add(new CodeVariableElement(getContext().getType(Class.class), implicitTypeName(param))); @@ -2911,6 +2924,40 @@ return builder.getRoot(); } + protected CodeExecutableElement createCopyConstructorFactoryMethod(TypeMirror baseType, SpecializationData specialization) { + List implicitTypeParams = getImplicitTypeParameters(specialization); + CodeVariableElement[] parameters = new CodeVariableElement[implicitTypeParams.size() + 1]; + int i = 0; + String baseName = "current"; + parameters[i++] = new CodeVariableElement(specialization.getNode().getNodeType(), baseName); + for (ActualParameter implicitTypeParam : implicitTypeParams) { + parameters[i++] = new CodeVariableElement(getContext().getType(Class.class), implicitTypeName(implicitTypeParam)); + } + CodeExecutableElement method = new CodeExecutableElement(modifiers(STATIC), specialization.getNode().getNodeType(), CREATE_SPECIALIZATION_NAME, parameters); + CodeTreeBuilder builder = method.createBuilder(); + builder.startReturn(); + builder.startNew(getElement().asType()); + builder.startGroup().cast(baseType, CodeTreeBuilder.singleString(baseName)).end(); + for (ActualParameter param : implicitTypeParams) { + builder.string(implicitTypeName(param)); + } + builder.end().end(); + return method; + } + + protected CodeExecutableElement createConstructorFactoryMethod(SpecializationData specialization, ExecutableElement constructor) { + List parameters = constructor.getParameters(); + CodeExecutableElement method = new CodeExecutableElement(modifiers(STATIC), specialization.getNode().getNodeType(), CREATE_SPECIALIZATION_NAME, + parameters.toArray(new CodeVariableElement[parameters.size()])); + CodeTreeBuilder builder = method.createBuilder(); + builder.startReturn(); + builder.startNew(getElement().asType()); + for (VariableElement param : parameters) { + builder.string(((CodeVariableElement) param).getName()); + } + builder.end().end(); + return method; + } } private interface CodeBlock { diff -r 5ecbed00da23 -r ff5cacf47b68 mx/projects --- a/mx/projects Fri May 02 02:45:26 2014 +0200 +++ b/mx/projects Sat May 03 21:46:35 2014 +0200 @@ -315,7 +315,7 @@ # graal.alloc project@com.oracle.graal.alloc@subDir=graal project@com.oracle.graal.alloc@sourceDirs=src -project@com.oracle.graal.alloc@dependencies=com.oracle.graal.nodes +project@com.oracle.graal.alloc@dependencies=com.oracle.graal.compiler.common project@com.oracle.graal.alloc@checkstyle=com.oracle.graal.graph project@com.oracle.graal.alloc@javaCompliance=1.8 project@com.oracle.graal.alloc@workingSets=Graal diff -r 5ecbed00da23 -r ff5cacf47b68 src/gpu/hsail/vm/gpu_hsail.cpp --- a/src/gpu/hsail/vm/gpu_hsail.cpp Fri May 02 02:45:26 2014 +0200 +++ b/src/gpu/hsail/vm/gpu_hsail.cpp Sat May 03 21:46:35 2014 +0200 @@ -67,7 +67,7 @@ JNINativeMethod Hsail::HSAIL_methods[] = { {CC"initialize", CC"()Z", FN_PTR(Hsail::initialize)}, {CC"generateKernel", CC"([B" STRING ")J", FN_PTR(Hsail::generate_kernel)}, - {CC"executeKernel0", CC"("HS_INSTALLED_CODE"I["OBJECT"["OBJECT"["JLTHREAD"I)Z", FN_PTR(Hsail::execute_kernel_void_1d)}, + {CC"executeKernel0", CC"("HS_INSTALLED_CODE"I["OBJECT"["OBJECT"["JLTHREAD"I[I)Z", FN_PTR(Hsail::execute_kernel_void_1d)}, }; void * Hsail::_device_context = NULL; @@ -147,7 +147,7 @@ } GPU_VMENTRY(jboolean, Hsail::execute_kernel_void_1d, (JNIEnv* env, jclass, jobject kernel_handle, jint dimX, jobject args, jobject oops_save, - jobject donor_threads, jint allocBytesPerWorkitem)) + jobject donor_threads, jint allocBytesPerWorkitem, jobject oop_map_array)) ResourceMark rm; jlong nmethodValue = InstalledCode::address(kernel_handle); @@ -163,7 +163,7 @@ SharedRuntime::throw_and_post_jvmti_exception(JavaThread::current(), vmSymbols::com_oracle_graal_api_code_InvalidInstalledCodeException(), NULL); } - return execute_kernel_void_1d_internal((address) kernel, dimX, args, mh, nm, oops_save, donor_threads, allocBytesPerWorkitem, CHECK_0); +return execute_kernel_void_1d_internal((address) kernel, dimX, args, mh, nm, oops_save, donor_threads, allocBytesPerWorkitem, oop_map_array, CHECK_0); GPU_END static void showRanges(jboolean *a, int len) { @@ -215,9 +215,141 @@ tty->print_cr("(%p, %p, %p), siz=%ld, free=%ld (%f%%)", start, top, end, tlabSize, tlabFree, freePct); } +class OopSaver : public StackObj { +private: + objArrayOop _oopsSaveArray; + typeArrayOop _oopMapArray; + jobject _oops_save; + jobject _oop_map_array; + int _last_pcoffset; + int _last_idx; + int _saveAreaCounts; -jboolean Hsail::execute_kernel_void_1d_internal(address kernel, int dimX, jobject args, methodHandle& mh, nmethod *nm, jobject oops_save, - jobject donor_threads, int allocBytesPerWorkitem, TRAPS) { + enum { + SAVEAREACOUNTS_OFST=0, + SPAN_OFST=1, + HEADERSIZE=2 + }; + int mapPcOffsetToIndex(int pcOffset) { + if (pcOffset == _last_pcoffset) { + return _last_idx; + } + int span = _oopMapArray->int_at(SPAN_OFST); + for (int idx = HEADERSIZE; idx < _oopMapArray->length(); idx += span) { + int ofst = _oopMapArray->int_at(idx); + if (ofst == pcOffset) { + _last_pcoffset = pcOffset; + _last_idx = idx + 1; + return _last_idx; + } + } + ShouldNotReachHere(); + return -1; + } + +public: + OopSaver(jobject oops_save, jobject oop_map_array) { + _oops_save = oops_save; + _oop_map_array = oop_map_array; + _last_pcoffset = -1; + _saveAreaCounts = getSaveAreaCounts(oop_map_array); + resolveArrays(); + } + + void resolveArrays() { + _oopsSaveArray = (objArrayOop) JNIHandles::resolve(_oops_save); + _oopMapArray = (typeArrayOop) JNIHandles::resolve(_oop_map_array); + } + + void * getOopForBit(HSAILFrame * hsailFrame, int bit) { + assert(isOop(hsailFrame, bit), ""); + void *oop; + if (bit < hsailFrame->num_d_regs()) { + // d register + oop = (void*) hsailFrame->get_d_reg(bit); + } else { + // stack slot + int stackOffset = (bit - hsailFrame->num_d_regs()) * 8; // 8 bytes per stack slot + oop = (void *) hsailFrame->get_stackslot64(stackOffset); + } + return oop; + } + + void putOopForBit(HSAILFrame * hsailFrame, int bit, void *oop) { + assert(isOop(hsailFrame, bit), ""); + if (bit < hsailFrame->num_d_regs()) { + // d register + hsailFrame->put_d_reg(bit, (jlong) oop); + } else { + // stack slot + int stackOffset = (bit - hsailFrame->num_d_regs()) * 8; // 8 bytes per stack slot + hsailFrame->put_stackslot64(stackOffset, (jlong) oop); + } + } + + void saveOopsFromFrame(HSAILFrame * hsailFrame, int deoptSlot){ + // as used, no need to resolve arrays on each call + int oopsPerDeopt = hsailFrame->num_d_regs() + hsailFrame->num_stack_slots(); + + // handle the dregister and stackSlot based oops + for (int bit = 0; bit < oopsPerDeopt; bit++) { + if (isOop(hsailFrame, bit)) { + void* saved_oop = getOopForBit(hsailFrame, bit); + int saveArrayIndex = deoptSlot * oopsPerDeopt + bit; + _oopsSaveArray->obj_at_put(saveArrayIndex, (oop) saved_oop); + } + } + } + + void restoreOopsToFrame(HSAILFrame * hsailFrame, int deoptSlot, int workitem){ + // need to re-resolve on each restore + resolveArrays(); + int oopsPerDeopt = hsailFrame->num_d_regs() + hsailFrame->num_stack_slots(); + + // handle the dregister and stackSlot based oops + for (int bit = 0; bit < oopsPerDeopt; bit++) { + if (isOop(hsailFrame, bit)) { + // the dregister or stack slot at this bit is an oop, retrieve it from array and put back in frame + int saveArrayIndex = deoptSlot * oopsPerDeopt + bit; + void * newValue = (void *) _oopsSaveArray->obj_at(saveArrayIndex); + void * oldValue = getOopForBit(hsailFrame, bit); + assert((oldValue != 0 ? newValue != 0 : newValue == 0), "bad dregValue retrieved"); + if (newValue != oldValue) { + if (TraceGPUInteraction) { + int numDRegs = hsailFrame->num_d_regs(); + const char *name = (bit < numDRegs ? "$d" : "stk"); + int num = (bit < numDRegs ? bit : bit - numDRegs); + tty->print_cr("oop moved for %s%d, workitem %d, slot %d, old=%p, new=%p", + name, num, workitem, deoptSlot, oldValue, newValue); + } + putOopForBit(hsailFrame, bit, newValue); + } + } + } + } + + bool isOop(HSAILFrame * hsailFrame, int bit){ + // re-resolve on each access + resolveArrays(); + if (bit > hsailFrame->num_d_regs() + hsailFrame->num_stack_slots()) { + return false; + } + int pcOffset = hsailFrame->pc_offset(); + int bits_int_idx = mapPcOffsetToIndex(pcOffset) + (bit / 32); + int bitpos = bit % 32; + int bits = _oopMapArray->int_at(bits_int_idx); + return ((bits & (1 << bitpos)) != 0); + } + + static int getSaveAreaCounts(jobject oopMapArrayObject) { + typeArrayOop oopMapArray = (typeArrayOop) JNIHandles::resolve(oopMapArrayObject); + return oopMapArray->int_at(SAVEAREACOUNTS_OFST); + } + +}; + +jboolean Hsail::execute_kernel_void_1d_internal(address kernel, int dimX, jobject args, methodHandle& mh, nmethod *nm, jobject oops_save, + jobject donor_threads, int allocBytesPerWorkitem, jobject oop_map_array, TRAPS) { ResourceMark rm(THREAD); objArrayOop argsArray = (objArrayOop) JNIHandles::resolve(args); @@ -260,9 +392,16 @@ // Reset the kernel arguments _okra_clearargs(kernel); + // get how many bytes per deopt save area are required + int saveAreaCounts = OopSaver::getSaveAreaCounts(oop_map_array); + int numSRegs = saveAreaCounts & 0xff; + int numDRegs = (saveAreaCounts >> 8) & 0xff; + int numStackSlots = (saveAreaCounts >> 16); + int bytesPerSaveArea = numSRegs * 4 + (numDRegs + numStackSlots) * 8; + HSAILDeoptimizationInfo* e; if (UseHSAILDeoptimization) { - e = new (ResourceObj::C_HEAP, mtInternal) HSAILDeoptimizationInfo(); + e = new (MAX_DEOPT_SLOTS, bytesPerSaveArea) HSAILDeoptimizationInfo(MAX_DEOPT_SLOTS, bytesPerSaveArea); e->set_never_ran_array(NEW_C_HEAP_ARRAY(jboolean, dimX, mtInternal)); memset(e->never_ran_array(), 0, dimX * sizeof(jboolean)); e->set_donor_threads(donorThreads); @@ -318,7 +457,6 @@ if (UseHSAILDeoptimization) { kernelStats.incDeopts(); // check if any workitem requested a deopt - // currently we only support at most one such workitem int deoptcode = e->deopt_occurred(); if (deoptcode != 1) { if (deoptcode == 0) { @@ -337,40 +475,30 @@ TraceTime t3("handle deoptimizing workitems", TraceGPUInteraction); if (TraceGPUInteraction) { tty->print_cr("deopt happened."); - HSAILKernelDeoptimization * pdeopt = &e->_deopt_save_states[0]; + HSAILKernelDeoptimization * pdeopt = e->get_deopt_save_state(0); tty->print_cr("first deopter was workitem %d", pdeopt->workitem()); } // Before handling any deopting workitems, save the pointers from // the hsail frames in oops_save so they get adjusted by any // GC. Need to do this before leaving thread_in_vm mode. + OopSaver oopSaver(oops_save, oop_map_array); // resolve handle only needed once here (not exiting vm mode) - objArrayOop oopsSaveArray = (objArrayOop) JNIHandles::resolve(oops_save); + oopSaver.resolveArrays(); // since slots are allocated from the beginning, we know how far to look - assert(e->num_deopts() < MAX_DEOPT_SAVE_STATES_SIZE, "deopt save state overflow"); + assert(e->num_deopts() < e->num_slots(), "deopt save state overflow"); for (int k = 0; k < e->num_deopts(); k++) { - HSAILKernelDeoptimization * pdeopt = &e->_deopt_save_states[k]; - jint workitem = pdeopt->workitem(); - if (workitem != -1) { - // this is a workitem that deopted - HSAILFrame *hsailFrame = pdeopt->first_frame(); - int dregOopMap = hsailFrame->dreg_oops_map(); - for (int bit = 0; bit < 16; bit++) { - if ((dregOopMap & (1 << bit)) != 0) { - // the dregister at this bit is an oop, save it in the array - int index = k * 16 + bit; - void* saved_oop = (void*) hsailFrame->get_d_reg(bit); - oopsSaveArray->obj_at_put(index, (oop) saved_oop); - } - } - } + HSAILKernelDeoptimization * pdeopt = e->get_deopt_save_state(k); + assert (pdeopt->workitem() >= 0, "bad workitem in deopt"); + // this is a workitem that deopted + oopSaver.saveOopsFromFrame(pdeopt->first_frame(), k); } // Handle any deopting workitems. int count_deoptimized = 0; for (int k = 0; k < e->num_deopts(); k++) { - HSAILKernelDeoptimization * pdeopt = &e->_deopt_save_states[k]; + HSAILKernelDeoptimization * pdeopt = e->get_deopt_save_state(k); jint workitem = pdeopt->workitem(); if (workitem != -1) { @@ -378,25 +506,8 @@ HSAILFrame *hsailFrame = pdeopt->first_frame(); // update the hsailFrame from the oopsSaveArray - // re-resolve the handle - oopsSaveArray = (objArrayOop) JNIHandles::resolve(oops_save); - - int dregOopMap = hsailFrame->dreg_oops_map(); - for (int bit = 0; bit < 16; bit++) { - if ((dregOopMap & (1 << bit)) != 0) { - // the dregister at this bit is an oop, retrieve it from array and put back in frame - int index = k * 16 + bit; - void * dregValue = (void *) oopsSaveArray->obj_at(index); - void * oldDregValue = (void *) hsailFrame->get_d_reg(bit); - assert((oldDregValue != 0 ? dregValue != 0 : dregValue == 0), "bad dregValue retrieved"); - if (TraceGPUInteraction) { - if (dregValue != oldDregValue) { - tty->print_cr("oop moved for $d%d, workitem %d, slot %d, old=%p, new=%p", bit, workitem, k, oldDregValue, dregValue); - } - } - hsailFrame->put_d_reg(bit, (jlong) dregValue); - } - } + // will re-resolve the handles each time + oopSaver.restoreOopsToFrame(hsailFrame, k, workitem); JavaValue result(T_VOID); JavaCallArguments javaArgs; @@ -410,12 +521,19 @@ javaArgs.push_int(myActionReason); javaArgs.push_oop((oop) NULL); if (TraceGPUInteraction) { - int dregOopMap = hsailFrame->dreg_oops_map(); - tty->print_cr("[HSAIL] Deoptimizing to host for workitem=%d (slot=%d) with deoptId=%d, frame=" INTPTR_FORMAT ", actionAndReason=%d, dregOopMap=%04x", workitem, k, deoptId, hsailFrame, myActionReason, dregOopMap); - // show the registers containing references - for (int bit = 0; bit < 16; bit++) { - if ((dregOopMap & (1 << bit)) != 0) { - tty->print_cr(" oop $d%d = %p", bit, hsailFrame->get_d_reg(bit)); + tty->print_cr("[HSAIL] Deoptimizing to host for workitem=%d (slot=%d) with deoptId=%d, frame=" INTPTR_FORMAT ", actionAndReason=%d", workitem, k, deoptId, hsailFrame, myActionReason); + // show the $d registers or stack slots containing references + int maxOopBits = hsailFrame->num_d_regs() + hsailFrame->num_stack_slots(); + for (int bit = 0; bit < maxOopBits; bit++) { + if (oopSaver.isOop(hsailFrame, bit)) { + if (bit < hsailFrame->num_d_regs()) { + // show $d reg oop + tty->print_cr(" oop $d%d = %p", bit, oopSaver.getOopForBit(hsailFrame, bit)); + } else { + // show stack slot oop + int stackOffset = (bit - hsailFrame->num_d_regs()) * 8; // 8 bytes per stack slot + tty->print_cr(" oop stk:%d = %p", stackOffset, oopSaver.getOopForBit(hsailFrame, bit)); + } } } } @@ -461,7 +579,7 @@ } } TraceGPUInteraction = savedTraceGPUInteraction; - if (TraceGPUInteraction) { + if (TraceGPUInteraction && (count_never_ran > 0)) { tty->print_cr("%d workitems never ran, have been run via JavaCall", count_never_ran); showRanges(never_ran_array, dimX); } diff -r 5ecbed00da23 -r ff5cacf47b68 src/gpu/hsail/vm/gpu_hsail.hpp --- a/src/gpu/hsail/vm/gpu_hsail.hpp Fri May 02 02:45:26 2014 +0200 +++ b/src/gpu/hsail/vm/gpu_hsail.hpp Sat May 03 21:46:35 2014 +0200 @@ -51,7 +51,8 @@ }; // 8 compute units * 40 waves per cu * wavesize 64 -#define MAX_DEOPT_SAVE_STATES_SIZE (8 * 40 * 64) +// TODO: query the device to get this number +#define MAX_DEOPT_SLOTS (8 * 40 * 64) class HSAILDeoptimizationInfo : public ResourceObj { friend class VMStructs; @@ -61,14 +62,23 @@ jint _deopt_next_index; JavaThread** _donor_threads; jboolean * _never_ran_array; + jint _num_slots; + jint _bytesPerSaveArea; + jint _deopt_span; public: - HSAILKernelDeoptimization _deopt_save_states[MAX_DEOPT_SAVE_STATES_SIZE]; + HSAILKernelDeoptimization _deopt_save_states[1]; // number and size of these can vary per kernel - inline HSAILDeoptimizationInfo() { + inline HSAILDeoptimizationInfo(int numSlots, int bytesPerSaveArea) { _notice_safepoints = &Hsail::_notice_safepoints; _deopt_occurred = 0; _deopt_next_index = 0; + _num_slots = numSlots; + _bytesPerSaveArea = bytesPerSaveArea; + _deopt_span = sizeof(HSAILKernelDeoptimization) + bytesPerSaveArea; + if (TraceGPUInteraction) { + tty->print_cr("HSAILDeoptimizationInfo allocated, %d slots of size %d, total size = 0x%lx bytes", _num_slots, _deopt_span, (_num_slots * _deopt_span + sizeof(HSAILDeoptimizationInfo))); + } } inline jint deopt_occurred() { @@ -78,6 +88,23 @@ inline jboolean *never_ran_array() { return _never_ran_array; } inline void set_never_ran_array(jboolean *p) { _never_ran_array = p; } inline void set_donor_threads(JavaThread **threads) { _donor_threads = threads; } + inline jint num_slots() {return _num_slots;} + + inline HSAILKernelDeoptimization * get_deopt_save_state(int slot) { + // use _deopt_span to index into _deopt_states + char *p = (char *) _deopt_save_states; + p += _deopt_span * slot; + return (HSAILKernelDeoptimization *) p; + } + + void * operator new (size_t hdrSize, int numSlots, int bytesPerSaveArea) { + size_t totalSizeBytes = hdrSize + numSlots * (sizeof(HSAILKernelDeoptimization) + bytesPerSaveArea); + return NEW_C_HEAP_ARRAY(char, totalSizeBytes, mtInternal); + } + + void operator delete (void *ptr) { + FREE_C_HEAP_ARRAY(char, ptr, mtInternal); + } }; @@ -93,7 +120,7 @@ // static native boolean executeKernel0(HotSpotInstalledCode kernel, int jobSize, Object[] args); JNIEXPORT static jboolean execute_kernel_void_1d(JNIEnv *env, jclass, jobject hotspotInstalledCode, jint dimX, jobject args, jobject oopsSave, - jobject donorThreads, int allocBytesPerWorkitem); + jobject donorThreads, int allocBytesPerWorkitem, jobject oop_map_array); // static native void getThreadPointers(Object[] donorThreads, long[] threadPointersOut); JNIEXPORT static void get_thread_pointers(JNIEnv *env, jclass, jobject donor_threads_handle, jobject thread_ptrs_handle); @@ -101,8 +128,8 @@ static void getNewTlabForDonorThread(ThreadLocalAllocBuffer* tlab, size_t tlabMinHsail); static jboolean execute_kernel_void_1d_internal(address kernel, int dimX, jobject args, methodHandle& mh, nmethod *nm, jobject oopsSave, - jobject donor_threads, int allocBytesPerWorkitem, TRAPS); - + jobject donorThreads, int allocBytesPerWorkitem, jobject oop_map_array, TRAPS); + static void register_heap(); static GraalEnv::CodeInstallResult install_code(Handle& compiled_code, CodeBlob*& cb, Handle installed_code, Handle triggered_deoptimizations); diff -r 5ecbed00da23 -r ff5cacf47b68 src/gpu/hsail/vm/gpu_hsail_Frame.hpp --- a/src/gpu/hsail/vm/gpu_hsail_Frame.hpp Fri May 02 02:45:26 2014 +0200 +++ b/src/gpu/hsail/vm/gpu_hsail_Frame.hpp Sat May 03 21:46:35 2014 +0200 @@ -5,39 +5,44 @@ #include "code/debugInfo.hpp" #include "code/location.hpp" -// maximum registers that could be saved for now -#define MAX_SREGS 32 -#define MAX_DREGS 16 - class HSAILFrame { friend class VMStructs; private: jint _pc_offset; // The HSAIL "pc_offset" where the exception happens jbyte _num_s_regs; jbyte _num_d_regs; - jshort _dreg_oops_map; // bits = 1 if that dreg is an oop - jlong _save_area[MAX_SREGS/2 + MAX_DREGS]; + jshort _num_stack_slots; + jbyte _save_area[0]; // save area size can vary per kernel compilation public: // Accessors jint pc_offset() { return _pc_offset; } jint num_s_regs() {return _num_s_regs; } jint num_d_regs() {return _num_d_regs; } - jint dreg_oops_map() {return _dreg_oops_map; } + jint num_stack_slots() {return _num_stack_slots; } jlong get_d_reg(int idx) { - char *p = (char *) _save_area; int ofst = num_s_regs() * 4 + idx * 8; - return(*(jlong *) (p + ofst)); + return(*(jlong *) (_save_area + ofst)); } jint get_s_reg(int idx) { - char *p = (char *) _save_area; int ofst = idx * 4; - return(*(jint *) (p + ofst)); + return(*(jint *) (_save_area + ofst)); } void put_d_reg(int idx, jlong val) { - char *p = (char *) _save_area; int ofst = num_s_regs() * 4 + idx * 8; - (*(jlong *) (p + ofst)) = val; + (*(jlong *) (_save_area + ofst)) = val; + } + jint get_stackslot32(int stackOffset) { + int ofst = num_s_regs() * 4 + num_d_regs() * 8 + stackOffset; + return(*(jint *) (_save_area + ofst)); + } + jlong get_stackslot64(int stackOffset) { + int ofst = num_s_regs() * 4 + num_d_regs() * 8 + stackOffset; + return(*(jlong *) (_save_area + ofst)); + } + void put_stackslot64(int stackOffset, jlong val) { + int ofst = num_s_regs() * 4 + num_d_regs() * 8 + stackOffset; + (*(jlong *) (_save_area + ofst)) = val; } }; diff -r 5ecbed00da23 -r ff5cacf47b68 src/gpu/hsail/vm/vmStructs_hsail.hpp --- a/src/gpu/hsail/vm/vmStructs_hsail.hpp Fri May 02 02:45:26 2014 +0200 +++ b/src/gpu/hsail/vm/vmStructs_hsail.hpp Sat May 03 21:46:35 2014 +0200 @@ -32,10 +32,11 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_GPU_HSAIL(nonstatic_field) \ +#define VM_STRUCTS_GPU_HSAIL(nonstatic_field) \ nonstatic_field(HSAILFrame, _pc_offset, jint) \ nonstatic_field(HSAILFrame, _num_s_regs, jbyte) \ - nonstatic_field(HSAILFrame, _save_area[0], jlong) \ + nonstatic_field(HSAILFrame, _num_d_regs, jbyte) \ + nonstatic_field(HSAILFrame, _num_stack_slots, jshort) \ \ nonstatic_field(Hsail::HSAILKernelDeoptimization, _workitemid, jint) \ nonstatic_field(Hsail::HSAILKernelDeoptimization, _actionAndReason, jint) \ @@ -47,7 +48,6 @@ nonstatic_field(Hsail::HSAILDeoptimizationInfo, _donor_threads, JavaThread**) \ nonstatic_field(Hsail::HSAILDeoptimizationInfo, _never_ran_array, jboolean *) \ nonstatic_field(Hsail::HSAILDeoptimizationInfo, _deopt_save_states[0], Hsail::HSAILKernelDeoptimization) \ - nonstatic_field(Hsail::HSAILDeoptimizationInfo, _deopt_save_states[1], Hsail::HSAILKernelDeoptimization) #define VM_TYPES_GPU_HSAIL(declare_type, declare_toplevel_type) \ declare_toplevel_type(HSAILFrame) \ @@ -55,4 +55,8 @@ declare_toplevel_type(Hsail::HSAILKernelDeoptimization) \ declare_toplevel_type(Hsail::HSAILDeoptimizationInfo) +#define VM_INT_CONSTANTS_GPU_HSAIL(declare_constant) \ + declare_constant(sizeof(HSAILFrame)) \ + declare_constant(sizeof(Hsail::HSAILKernelDeoptimization)) \ + #endif // GPU_HSAIL_VM_VMSTRUCTS_HSAIL_HPP diff -r 5ecbed00da23 -r ff5cacf47b68 src/share/vm/compiler/compileBroker.cpp --- a/src/share/vm/compiler/compileBroker.cpp Fri May 02 02:45:26 2014 +0200 +++ b/src/share/vm/compiler/compileBroker.cpp Sat May 03 21:46:35 2014 +0200 @@ -516,7 +516,8 @@ if (_osr_bci != CompileBroker::standard_entry_bci) { log->print(" osr_bci='%d'", _osr_bci); } - if (_comp_level != CompLevel_highest_tier) { + // Always print the level in tiered. + if (_comp_level != CompLevel_highest_tier || TieredCompilation) { log->print(" level='%d'", _comp_level); } if (_is_blocking) { diff -r 5ecbed00da23 -r ff5cacf47b68 src/share/vm/graal/graalCompiler.cpp --- a/src/share/vm/graal/graalCompiler.cpp Fri May 02 02:45:26 2014 +0200 +++ b/src/share/vm/graal/graalCompiler.cpp Sat May 03 21:46:35 2014 +0200 @@ -58,12 +58,18 @@ NOT_LP64(error("check TLAB allocation code for address space conflicts")); BufferBlob* buffer_blob = initialize_buffer_blob(); - if (buffer_blob == NULL) { - // If we are called from JNI_CreateJavaVM we cannot use set_state yet because it takes a lock. - // set_state(failed); - } else { - // set_state(initialized); +#ifdef COMPILERGRAAL + if (!UseGraalCompilationQueue) { + // This path is used for initialization both by the native queue and the graal queue + // but set_state acquired a lock which might not be safe during JVM_CreateJavaVM, so + // only update the state flag for the native queue. + if (buffer_blob == NULL) { + set_state(failed); + } else { + set_state(initialized); + } } +#endif JNIEnv *env = ((JavaThread *) Thread::current())->jni_environment(); jclass klass = env->FindClass("com/oracle/graal/hotspot/bridge/CompilerToVMImpl"); diff -r 5ecbed00da23 -r ff5cacf47b68 src/share/vm/graal/graalGlobals.hpp --- a/src/share/vm/graal/graalGlobals.hpp Fri May 02 02:45:26 2014 +0200 +++ b/src/share/vm/graal/graalGlobals.hpp Sat May 03 21:46:35 2014 +0200 @@ -52,7 +52,7 @@ COMPILERGRAAL_PRESENT(product(bool, BootstrapGraal, true, \ "Bootstrap Graal before running Java main method")) \ \ - COMPILERGRAAL_PRESENT(product(bool, UseGraalCompilationQueue, false, \ + COMPILERGRAAL_PRESENT(product(bool, UseGraalCompilationQueue, true, \ "Use non-native compilation queue for Graal")) \ \ product(bool, ForceGraalInitialization, false, \ diff -r 5ecbed00da23 -r ff5cacf47b68 src/share/vm/runtime/vmStructs.cpp --- a/src/share/vm/runtime/vmStructs.cpp Fri May 02 02:45:26 2014 +0200 +++ b/src/share/vm/runtime/vmStructs.cpp Sat May 03 21:46:35 2014 +0200 @@ -106,6 +106,7 @@ #include "utilities/macros.hpp" #ifdef GRAAL # include "graal/vmStructs_graal.hpp" +# include "hsail/vm/vmStructs_hsail.hpp" #endif #ifdef TARGET_ARCH_x86 # include "vmStructs_x86.hpp" @@ -153,8 +154,6 @@ # include "vmStructs_bsd_zero.hpp" #endif -#include "hsail/vm/vmStructs_hsail.hpp" - #if INCLUDE_ALL_GCS #include "gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp" #include "gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp" @@ -3042,7 +3041,9 @@ GENERATE_C1_UNCHECKED_STATIC_VM_STRUCT_ENTRY, GENERATE_C2_UNCHECKED_STATIC_VM_STRUCT_ENTRY) +#ifdef GRAAL VM_STRUCTS_GPU_HSAIL(GENERATE_NONSTATIC_VM_STRUCT_ENTRY) +#endif VM_STRUCTS_OS_CPU(GENERATE_NONSTATIC_VM_STRUCT_ENTRY, GENERATE_STATIC_VM_STRUCT_ENTRY, @@ -3094,8 +3095,10 @@ GENERATE_C2_VM_TYPE_ENTRY, GENERATE_C2_TOPLEVEL_VM_TYPE_ENTRY) +#ifdef GRAAL VM_TYPES_GPU_HSAIL(GENERATE_VM_TYPE_ENTRY, GENERATE_TOPLEVEL_VM_TYPE_ENTRY) +#endif VM_TYPES_OS_CPU(GENERATE_VM_TYPE_ENTRY, GENERATE_TOPLEVEL_VM_TYPE_ENTRY, @@ -3120,6 +3123,8 @@ #ifdef GRAAL VM_INT_CONSTANTS_GRAAL(GENERATE_VM_INT_CONSTANT_ENTRY, GENERATE_PREPROCESSOR_VM_INT_CONSTANT_ENTRY) + + VM_INT_CONSTANTS_GPU_HSAIL(GENERATE_VM_INT_CONSTANT_ENTRY) #endif #if INCLUDE_ALL_GCS @@ -3201,7 +3206,9 @@ CHECK_NO_OP, CHECK_NO_OP); - VM_STRUCTS_GPU_HSAIL(CHECK_NONSTATIC_VM_STRUCT_ENTRY); +#ifdef GRAAL + VM_STRUCTS_GPU_HSAIL(CHECK_NONSTATIC_VM_STRUCT_ENTRY) +#endif VM_STRUCTS_OS_CPU(CHECK_NONSTATIC_VM_STRUCT_ENTRY, CHECK_STATIC_VM_STRUCT_ENTRY, @@ -3243,8 +3250,10 @@ CHECK_C2_VM_TYPE_ENTRY, CHECK_C2_TOPLEVEL_VM_TYPE_ENTRY); +#ifdef GRAAL VM_TYPES_GPU_HSAIL(CHECK_VM_TYPE_ENTRY, CHECK_SINGLE_ARG_VM_TYPE_NO_OP); +#endif VM_TYPES_OS_CPU(CHECK_VM_TYPE_ENTRY, CHECK_SINGLE_ARG_VM_TYPE_NO_OP,