# HG changeset patch # User Stefan Anzinger # Date 1430760891 -7200 # Node ID bfb6e742ad0aef19fc1da28283cb316d275a0dd2 # Parent 0028ab94d268ebca7edb4dc6b0416853c0760018# Parent fbe449ca9707620dfac6b005e2ebc068033fc0e0 Merge diff -r 0028ab94d268 -r bfb6e742ad0a CHANGELOG.md --- a/CHANGELOG.md Mon May 04 19:12:50 2015 +0200 +++ b/CHANGELOG.md Mon May 04 19:34:51 2015 +0200 @@ -4,15 +4,18 @@ ## `tip` +... + +## Version 0.7 +29-Apr-2015, [Repository Revision](http://hg.openjdk.java.net/graal/graal/shortlog/graal-0.7) ### Graal * By default the Graal code is now only compiled by C1 which should improve application start-up. * Merged with jdk8u40-b25. * The Graal class loader now loads all lib/graal/graal*.jar jars. -* Fast Graal services (see com.oracle.graal.api.runtime.Service) are now looked up using service files in lib/graal/services +* Fast Graal services (see com.oracle.graal.api.runtime.Service) are now looked up using service files in lib/graal/services. * Add utilities ModifiersProvider#isConcrete, ResolvedJavaMethod#hasBytecodes, ResolvedJavaMethod#hasReceiver to Graal API. * Add `GraalDirectives` API, containing methods to influence compiler behavior for unittests and microbenchmarks. * Introduce `LIRSuites`, an extensible configuration for the low-level compiler pipeline. -* ... ### Truffle * New, faster partial evaluation (no more TruffleCache). diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/LIRKind.java --- a/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/LIRKind.java Mon May 04 19:12:50 2015 +0200 +++ b/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/LIRKind.java Mon May 04 19:34:51 2015 +0200 @@ -124,6 +124,27 @@ } /** + * Merge the types of the inputs. The result will have the {@link PlatformKind} of one of the + * inputs. If all inputs are values (references), the result is a value (reference). Otherwise, + * the result is a derived reference. + * + * This method should be used to construct the result {@link LIRKind} of merge operation that do + * not modify values (e.g. phis). + */ + public static LIRKind merge(Value... inputs) { + assert inputs.length > 0; + for (Value input : inputs) { + LIRKind kind = input.getLIRKind(); + if (kind.isDerivedReference()) { + return kind; + } + } + + // all inputs are values or references, just return one of them + return inputs[0].getLIRKind(); + } + + /** * Create a new {@link LIRKind} with the same reference information and a new * {@linkplain #getPlatformKind platform kind}. If the new kind is a longer vector than this, * the new elements are marked as untracked values. diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64LIRGenerator.java --- a/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64LIRGenerator.java Mon May 04 19:12:50 2015 +0200 +++ b/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64LIRGenerator.java Mon May 04 19:34:51 2015 +0200 @@ -32,6 +32,8 @@ import static com.oracle.graal.lir.amd64.AMD64Arithmetic.*; import static com.oracle.graal.lir.amd64.AMD64MathIntrinsicOp.IntrinsicOpcode.*; +import java.util.*; + import com.oracle.graal.amd64.*; import com.oracle.graal.api.code.*; import com.oracle.graal.api.meta.*; @@ -61,12 +63,14 @@ import com.oracle.graal.lir.amd64.AMD64ControlFlow.ReturnOp; import com.oracle.graal.lir.amd64.AMD64ControlFlow.StrategySwitchOp; import com.oracle.graal.lir.amd64.AMD64ControlFlow.TableSwitchOp; +import com.oracle.graal.lir.amd64.AMD64Move.AMD64StackMove; import com.oracle.graal.lir.amd64.AMD64Move.LeaDataOp; import com.oracle.graal.lir.amd64.AMD64Move.LeaOp; import com.oracle.graal.lir.amd64.AMD64Move.MembarOp; import com.oracle.graal.lir.amd64.AMD64Move.MoveFromRegOp; import com.oracle.graal.lir.amd64.AMD64Move.MoveToRegOp; import com.oracle.graal.lir.amd64.AMD64Move.StackLeaOp; +import com.oracle.graal.lir.framemap.*; import com.oracle.graal.lir.gen.*; import com.oracle.graal.phases.util.*; @@ -76,13 +80,56 @@ public abstract class AMD64LIRGenerator extends LIRGenerator implements AMD64ArithmeticLIRGenerator { private static final RegisterValue RCX_I = AMD64.rcx.asValue(LIRKind.value(Kind.Int)); + private AMD64SpillMoveFactory moveFactory; + + private static class RegisterBackupPair { + public final Register register; + public final StackSlotValue backupSlot; + + RegisterBackupPair(Register register, StackSlotValue backupSlot) { + this.register = register; + this.backupSlot = backupSlot; + } + } private class AMD64SpillMoveFactory implements LIRGeneratorTool.SpillMoveFactory { + private Map categorized; @Override public LIRInstruction createMove(AllocatableValue result, Value input) { return AMD64LIRGenerator.this.createMove(result, input); } + + @Override + public LIRInstruction createStackMove(AllocatableValue result, Value input) { + RegisterBackupPair backup = getScratchRegister(input.getPlatformKind()); + return new AMD64StackMove(result, input, backup.register, backup.backupSlot); + } + + private RegisterBackupPair getScratchRegister(PlatformKind kind) { + PlatformKind.Key key = kind.getKey(); + if (categorized == null) { + categorized = new HashMap<>(); + } else if (categorized.containsKey(key)) { + return categorized.get(key); + } + + FrameMapBuilder frameMapBuilder = getResult().getFrameMapBuilder(); + RegisterConfig registerConfig = frameMapBuilder.getRegisterConfig(); + + Register[] availableRegister = registerConfig.filterAllocatableRegisters(kind, registerConfig.getAllocatableRegisters()); + assert availableRegister != null && availableRegister.length > 1; + Register scratchRegister = availableRegister[0]; + + Architecture arch = frameMapBuilder.getCodeCache().getTarget().arch; + LIRKind largestKind = LIRKind.value(arch.getLargestStorableKind(scratchRegister.getRegisterCategory())); + VirtualStackSlot backupSlot = frameMapBuilder.allocateSpillSlot(largestKind); + + RegisterBackupPair value = new RegisterBackupPair(scratchRegister, backupSlot); + categorized.put(key, value); + + return value; + } } public AMD64LIRGenerator(LIRKindTool lirKindTool, Providers providers, CallingConvention cc, LIRGenerationResult lirGenRes) { @@ -90,7 +137,10 @@ } public SpillMoveFactory getSpillMoveFactory() { - return new AMD64SpillMoveFactory(); + if (moveFactory == null) { + moveFactory = new AMD64SpillMoveFactory(); + } + return moveFactory; } @Override diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java --- a/graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java Mon May 04 19:12:50 2015 +0200 +++ b/graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java Mon May 04 19:34:51 2015 +0200 @@ -70,6 +70,7 @@ public abstract class SPARCLIRGenerator extends LIRGenerator { private StackSlotValue tmpStackSlot; + private SPARCSpillMoveFactory moveFactory; private class SPARCSpillMoveFactory implements LIRGeneratorTool.SpillMoveFactory { @@ -84,7 +85,10 @@ } public SpillMoveFactory getSpillMoveFactory() { - return new SPARCSpillMoveFactory(); + if (moveFactory == null) { + moveFactory = new SPARCSpillMoveFactory(); + } + return moveFactory; } @Override diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java Mon May 04 19:12:50 2015 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java Mon May 04 19:34:51 2015 +0200 @@ -127,6 +127,10 @@ protected static void breakpoint() { } + @SuppressWarnings("unused") + protected static void breakpoint(int arg0) { + } + protected Suites createSuites() { Suites ret = backend.getSuites().createSuites(); ListIterator> iter = ret.getHighTier().findPhase(CleanTypeProfileProxyPhase.class); @@ -852,6 +856,12 @@ return true; } }, GraalCompilerTest.class, "breakpoint"); + invocationPlugins.register(new InvocationPlugin() { + public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg0) { + b.add(new BreakpointNode(arg0)); + return true; + } + }, GraalCompilerTest.class, "breakpoint", int.class); return conf; } diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GuardEliminationCornerCasesTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GuardEliminationCornerCasesTest.java Mon May 04 19:12:50 2015 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GuardEliminationCornerCasesTest.java Mon May 04 19:34:51 2015 +0200 @@ -62,11 +62,11 @@ if (b instanceof C) { return 1; } else { - GraalDirectives.deoptimize(); + GraalDirectives.deoptimizeAndInvalidate(); } } } else { - GraalDirectives.deoptimize(); + GraalDirectives.deoptimizeAndInvalidate(); } } return 0; diff -r 0028ab94d268 -r bfb6e742ad0a 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 Mon May 04 19:12:50 2015 +0200 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java Mon May 04 19:34:51 2015 +0200 @@ -293,7 +293,11 @@ // LIR generation LIRGenerationContext context = new LIRGenerationContext(lirGen, nodeLirGen, graph, schedule); - new LIRGenerationPhase().apply(target, lirGenRes, codeEmittingOrder, linearScanOrder, context); + try (Scope s = Debug.scope("LIRGeneration", nodeLirGen, lir)) { + new LIRGenerationPhase().apply(target, lirGenRes, codeEmittingOrder, linearScanOrder, context); + } catch (Throwable e) { + throw Debug.handle(e); + } try (Scope s = Debug.scope("LIRStages", nodeLirGen, lir)) { return emitLowLevel(target, codeEmittingOrder, linearScanOrder, lirGenRes, lirGen, lirSuites); diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/NodeLIRBuilder.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/NodeLIRBuilder.java Mon May 04 19:12:50 2015 +0200 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/NodeLIRBuilder.java Mon May 04 19:34:51 2015 +0200 @@ -45,6 +45,7 @@ import com.oracle.graal.lir.debug.*; import com.oracle.graal.lir.gen.*; import com.oracle.graal.lir.gen.LIRGenerator.Options; +import com.oracle.graal.lir.gen.LIRGeneratorTool.BlockScope; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.calc.*; import com.oracle.graal.nodes.cfg.*; @@ -192,71 +193,71 @@ } public void doBlock(Block block, StructuredGraph graph, BlockMap> blockMap) { - gen.doBlockStart(block); + try (BlockScope blockScope = gen.getBlockScope(block)) { - if (block == gen.getResult().getLIR().getControlFlowGraph().getStartBlock()) { - assert block.getPredecessorCount() == 0; - emitPrologue(graph); - } else { - assert block.getPredecessorCount() > 0; - } + if (block == gen.getResult().getLIR().getControlFlowGraph().getStartBlock()) { + assert block.getPredecessorCount() == 0; + emitPrologue(graph); + } else { + assert block.getPredecessorCount() > 0; + } - List nodes = blockMap.get(block); + List nodes = blockMap.get(block); - // Allow NodeLIRBuilder subclass to specialize code generation of any interesting groups - // of instructions - matchComplexExpressions(nodes); + // Allow NodeLIRBuilder subclass to specialize code generation of any interesting groups + // of instructions + matchComplexExpressions(nodes); - for (int i = 0; i < nodes.size(); i++) { - Node node = nodes.get(i); - if (node instanceof ValueNode) { - ValueNode valueNode = (ValueNode) node; - if (Options.TraceLIRGeneratorLevel.getValue() >= 3) { - TTY.println("LIRGen for " + valueNode); - } - Value operand = getOperand(valueNode); - if (operand == null) { - if (!peephole(valueNode)) { - try { - doRoot(valueNode); - } catch (GraalInternalError e) { - throw GraalGraphInternalError.transformAndAddContext(e, valueNode); - } catch (Throwable e) { - throw new GraalGraphInternalError(e).addContext(valueNode); + for (int i = 0; i < nodes.size(); i++) { + Node node = nodes.get(i); + if (node instanceof ValueNode) { + ValueNode valueNode = (ValueNode) node; + if (Options.TraceLIRGeneratorLevel.getValue() >= 3) { + TTY.println("LIRGen for " + valueNode); + } + Value operand = getOperand(valueNode); + if (operand == null) { + if (!peephole(valueNode)) { + try { + doRoot(valueNode); + } catch (GraalInternalError e) { + throw GraalGraphInternalError.transformAndAddContext(e, valueNode); + } catch (Throwable e) { + throw new GraalGraphInternalError(e).addContext(valueNode); + } } + } else if (ComplexMatchValue.INTERIOR_MATCH.equals(operand)) { + // Doesn't need to be evaluated + Debug.log("interior match for %s", valueNode); + } else if (operand instanceof ComplexMatchValue) { + Debug.log("complex match for %s", valueNode); + ComplexMatchValue match = (ComplexMatchValue) operand; + operand = match.evaluate(this); + if (operand != null) { + setResult(valueNode, operand); + } + } else { + // There can be cases in which the result of an instruction is already set + // before by other instructions. } - } else if (ComplexMatchValue.INTERIOR_MATCH.equals(operand)) { - // Doesn't need to be evaluated - Debug.log("interior match for %s", valueNode); - } else if (operand instanceof ComplexMatchValue) { - Debug.log("complex match for %s", valueNode); - ComplexMatchValue match = (ComplexMatchValue) operand; - operand = match.evaluate(this); - if (operand != null) { - setResult(valueNode, operand); - } - } else { - // There can be cases in which the result of an instruction is already set - // before by other instructions. } } - } - if (!gen.hasBlockEnd(block)) { - NodeClassIterable successors = block.getEndNode().successors(); - assert successors.count() == block.getSuccessorCount(); - if (block.getSuccessorCount() != 1) { - /* - * If we have more than one successor, we cannot just use the first one. Since - * successors are unordered, this would be a random choice. - */ - throw new GraalInternalError("Block without BlockEndOp: " + block.getEndNode()); + if (!gen.hasBlockEnd(block)) { + NodeClassIterable successors = block.getEndNode().successors(); + assert successors.count() == block.getSuccessorCount(); + if (block.getSuccessorCount() != 1) { + /* + * If we have more than one successor, we cannot just use the first one. Since + * successors are unordered, this would be a random choice. + */ + throw new GraalInternalError("Block without BlockEndOp: " + block.getEndNode()); + } + gen.emitJump(getLIRBlock((FixedNode) successors.first())); } - gen.emitJump(getLIRBlock((FixedNode) successors.first())); + + assert verifyBlock(gen.getResult().getLIR(), block); } - - assert verifyBlock(gen.getResult().getLIR(), block); - gen.doBlockEnd(block); } protected void matchComplexExpressions(List nodes) { @@ -345,7 +346,9 @@ @Override public void visitEndNode(AbstractEndNode end) { - moveToPhi(end.merge(), end); + AbstractMergeNode merge = end.merge(); + moveToPhi(merge, end); + append(newJumpOp(getLIRBlock(merge))); } /** @@ -359,7 +362,7 @@ if (Options.TraceLIRGeneratorLevel.getValue() >= 1) { TTY.println("MOVE TO PHI from " + pred + " to " + merge); } - PhiResolver resolver = new PhiResolver(gen); + PhiResolver resolver = PhiResolver.create(gen); for (PhiNode phi : merge.phis()) { if (phi instanceof ValuePhiNode) { ValueNode curVal = phi.valueAt(pred); @@ -367,8 +370,6 @@ } } resolver.dispose(); - - append(newJumpOp(getLIRBlock(merge))); } protected JumpOp newJumpOp(LabelRef ref) { diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/PhiResolver.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/PhiResolver.java Mon May 04 19:12:50 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,242 +0,0 @@ -/* - * Copyright (c) 2009, 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.gen; - -import static com.oracle.graal.api.code.ValueUtil.*; -import static com.oracle.graal.api.meta.Value.*; -import static com.oracle.graal.lir.LIRValueUtil.*; - -import java.util.*; - -import com.oracle.graal.api.meta.*; -import com.oracle.graal.compiler.common.*; -import com.oracle.graal.lir.gen.*; -import com.oracle.graal.nodes.*; - -/** - * Converts {@link ValuePhiNode} instructions into moves. - * - * Resolves cycles: - * - *
- *
- *  r1 := r2  becomes  temp := r1
- *  r2 := r1           r1 := r2
- *                     r2 := temp
- * 
- * - * and orders moves: - * - *
- *  r2 := r3  becomes  r1 := r2
- *  r1 := r2           r2 := r3
- * 
- */ -public class PhiResolver { - - /** - * Tracks a data flow dependency between a source operand and any number of the destination - * operands. - */ - static class PhiResolverNode { - - /** - * A source operand whose value flows into the {@linkplain #destinations destination} - * operands. - */ - final Value operand; - - /** - * The operands whose values are defined by the {@linkplain #operand source} operand. - */ - final ArrayList destinations; - - /** - * Denotes if a move instruction has already been emitted to initialize the value of - * {@link #operand}. - */ - boolean assigned; - - /** - * Specifies if this operand been visited for the purpose of emitting a move instruction. - */ - boolean visited; - - /** - * Specifies if this is the initial definition in data flow path for a given value. - */ - boolean startNode; - - PhiResolverNode(Value operand) { - this.operand = operand; - destinations = new ArrayList<>(4); - } - - @Override - public String toString() { - StringBuilder buf = new StringBuilder(operand.toString()); - if (!destinations.isEmpty()) { - buf.append(" ->"); - for (PhiResolverNode node : destinations) { - buf.append(' ').append(node.operand); - } - } - return buf.toString(); - } - } - - private final LIRGeneratorTool gen; - - /** - * The operand loop header phi for the operand currently being process in {@link #dispose()}. - */ - private PhiResolverNode loop; - - private Value temp; - - private final ArrayList variableOperands = new ArrayList<>(3); - private final ArrayList otherOperands = new ArrayList<>(3); - - /** - * Maps operands to nodes. - */ - private final HashMap operandToNodeMap = CollectionsFactory.newMap(); - - public PhiResolver(LIRGeneratorTool gen) { - this.gen = gen; - temp = ILLEGAL; - } - - public void dispose() { - // resolve any cycles in moves from and to variables - for (int i = variableOperands.size() - 1; i >= 0; i--) { - PhiResolverNode node = variableOperands.get(i); - if (!node.visited) { - loop = null; - move(node, null); - node.startNode = true; - assert isIllegal(temp) : "moveTempTo() call missing"; - } - } - - // generate move for move from non variable to arbitrary destination - for (int i = otherOperands.size() - 1; i >= 0; i--) { - PhiResolverNode node = otherOperands.get(i); - for (int j = node.destinations.size() - 1; j >= 0; j--) { - emitMove(node.destinations.get(j).operand, node.operand); - } - } - } - - public void move(Value dest, Value src) { - assert isVariable(dest) : "destination must be virtual"; - // tty.print("move "); src.print(); tty.print(" to "); dest.print(); tty.cr(); - assert isLegal(src) : "source for phi move is illegal"; - assert isLegal(dest) : "destination for phi move is illegal"; - PhiResolverNode srcNode = sourceNode(src); - PhiResolverNode destNode = destinationNode(dest); - srcNode.destinations.add(destNode); - } - - private PhiResolverNode createNode(Value operand, boolean source) { - PhiResolverNode node; - if (isVariable(operand)) { - node = operandToNodeMap.get(operand); - assert node == null || node.operand.equals(operand); - if (node == null) { - node = new PhiResolverNode(operand); - operandToNodeMap.put(operand, node); - } - // Make sure that all variables show up in the list when - // they are used as the source of a move. - if (source) { - if (!variableOperands.contains(node)) { - variableOperands.add(node); - } - } - } else { - assert source; - node = new PhiResolverNode(operand); - otherOperands.add(node); - } - return node; - } - - private PhiResolverNode destinationNode(Value opr) { - return createNode(opr, false); - } - - private void emitMove(Value dest, Value src) { - assert isLegal(src); - assert isLegal(dest); - gen.emitMove((AllocatableValue) dest, src); - } - - // Traverse assignment graph in depth first order and generate moves in post order - // ie. two assignments: b := c, a := b start with node c: - // Call graph: move(c, NULL) -> move(b, c) -> move(a, b) - // Generates moves in this order: move b to a and move c to b - // ie. cycle a := b, b := a start with node a - // Call graph: move(a, NULL) -> move(b, a) -> move(a, b) - // Generates moves in this order: move b to temp, move a to b, move temp to a - private void move(PhiResolverNode dest, PhiResolverNode src) { - if (!dest.visited) { - dest.visited = true; - for (int i = dest.destinations.size() - 1; i >= 0; i--) { - move(dest.destinations.get(i), dest); - } - } else if (!dest.startNode) { - // cycle in graph detected - assert loop == null : "only one loop valid!"; - loop = dest; - moveToTemp(src.operand); - return; - } // else dest is a start node - - if (!dest.assigned) { - if (loop == dest) { - moveTempTo(dest.operand); - dest.assigned = true; - } else if (src != null) { - emitMove(dest.operand, src.operand); - dest.assigned = true; - } - } - } - - private void moveTempTo(Value dest) { - assert isLegal(temp); - emitMove(dest, temp); - temp = ILLEGAL; - } - - private void moveToTemp(Value src) { - assert isIllegal(temp); - temp = gen.newVariable(src.getLIRKind()); - emitMove(temp, src); - } - - private PhiResolverNode sourceNode(Value opr) { - return createNode(opr, true); - } -} diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotHostForeignCallsProvider.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotHostForeignCallsProvider.java Mon May 04 19:12:50 2015 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotHostForeignCallsProvider.java Mon May 04 19:34:51 2015 +0200 @@ -194,14 +194,16 @@ linkForeignCall(providers, LOG_PRINTF, c.logPrintfAddress, PREPEND_THREAD, LEAF, REEXECUTABLE, NO_LOCATIONS); linkForeignCall(providers, LOG_OBJECT, c.logObjectAddress, PREPEND_THREAD, LEAF, REEXECUTABLE, NO_LOCATIONS); linkForeignCall(providers, LOG_PRIMITIVE, c.logPrimitiveAddress, PREPEND_THREAD, LEAF, REEXECUTABLE, NO_LOCATIONS); - linkForeignCall(providers, THREAD_IS_INTERRUPTED, c.threadIsInterruptedAddress, PREPEND_THREAD, NOT_LEAF, NOT_REEXECUTABLE, any()); linkForeignCall(providers, VM_ERROR, c.vmErrorAddress, PREPEND_THREAD, LEAF_NOFP, REEXECUTABLE, NO_LOCATIONS); linkForeignCall(providers, OSR_MIGRATION_END, c.osrMigrationEndAddress, DONT_PREPEND_THREAD, LEAF_NOFP, NOT_REEXECUTABLE, NO_LOCATIONS); linkForeignCall(providers, G1WBPRECALL, c.writeBarrierPreAddress, PREPEND_THREAD, LEAF_NOFP, REEXECUTABLE, NO_LOCATIONS); linkForeignCall(providers, G1WBPOSTCALL, c.writeBarrierPostAddress, PREPEND_THREAD, LEAF_NOFP, REEXECUTABLE, NO_LOCATIONS); linkForeignCall(providers, VALIDATE_OBJECT, c.validateObject, PREPEND_THREAD, LEAF_NOFP, REEXECUTABLE, NO_LOCATIONS); - linkForeignCall(providers, TEST_DEOPTIMIZE_CALL_INT, c.testDeoptimizeCallInt, PREPEND_THREAD, NOT_LEAF, NOT_REEXECUTABLE, any()); + // Cannot be a leaf as VM acquires Thread_lock which requires thread_in_vm state + linkForeignCall(providers, THREAD_IS_INTERRUPTED, c.threadIsInterruptedAddress, PREPEND_THREAD, NOT_LEAF, NOT_REEXECUTABLE, any()); + + linkForeignCall(providers, TEST_DEOPTIMIZE_CALL_INT, c.testDeoptimizeCallInt, PREPEND_THREAD, NOT_LEAF, REEXECUTABLE, any()); // sometimes the same function is used for different kinds of arraycopy so check for // duplicates using a map. diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/bytecode/BC_instanceof01.java --- a/graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/bytecode/BC_instanceof01.java Mon May 04 19:12:50 2015 +0200 +++ b/graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/bytecode/BC_instanceof01.java Mon May 04 19:34:51 2015 +0200 @@ -24,14 +24,10 @@ import org.junit.*; -import com.oracle.graal.api.code.*; -import com.oracle.graal.api.meta.*; -import com.oracle.graal.compiler.common.*; import com.oracle.graal.compiler.phases.*; import com.oracle.graal.jtt.*; -import com.oracle.graal.nodes.*; import com.oracle.graal.options.*; -import com.oracle.graal.options.OptionValue.*; +import com.oracle.graal.options.OptionValue.OverrideScope; import com.oracle.graal.phases.tiers.*; /** diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/loop/Loop09_2.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/loop/Loop09_2.java Mon May 04 19:34:51 2015 +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.jtt.loop; + +import org.junit.*; + +import com.oracle.graal.jtt.*; + +/* + */ +public class Loop09_2 extends JTTTest { + + private static int cnt; + + public static int test(int arg) { + cnt = 0; + int count = arg; + for (int i = 0; i < arg; i++) { + count++; + foo(); + } + return count - cnt; + } + + static void foo() { + cnt++; + } + + @Test + public void run0() throws Throwable { + runTest("test", 0); + } + + @Test + public void run1() throws Throwable { + runTest("test", 10); + } + + @Test + public void run2() throws Throwable { + runTest("test", 25); + } + +} diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Move.java --- a/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Move.java Mon May 04 19:12:50 2015 +0200 +++ b/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Move.java Mon May 04 19:34:51 2015 +0200 @@ -108,6 +108,47 @@ } } + @Opcode("STACKMOVE") + public static final class AMD64StackMove extends AMD64LIRInstruction implements MoveOp { + public static final LIRInstructionClass TYPE = LIRInstructionClass.create(AMD64StackMove.class); + + @Def({STACK}) protected AllocatableValue result; + @Use({STACK, HINT}) protected Value input; + @Alive({OperandFlag.STACK, OperandFlag.UNINITIALIZED}) private StackSlotValue backupSlot; + + private Register scratch; + + public AMD64StackMove(AllocatableValue result, Value input, Register scratch, StackSlotValue backupSlot) { + super(TYPE); + this.result = result; + this.input = input; + this.backupSlot = backupSlot; + this.scratch = scratch; + } + + @Override + public Value getInput() { + return input; + } + + @Override + public AllocatableValue getResult() { + return result; + } + + @Override + public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) { + // backup scratch register + move(backupSlot.getKind(), crb, masm, backupSlot, scratch.asValue(backupSlot.getLIRKind())); + // move stack slot + move(getInput().getKind(), crb, masm, scratch.asValue(getInput().getLIRKind()), getInput()); + move(getResult().getKind(), crb, masm, getResult(), scratch.asValue(getResult().getLIRKind())); + // restore scratch register + move(backupSlot.getKind(), crb, masm, scratch.asValue(backupSlot.getLIRKind()), backupSlot); + + } + } + public static final class LeaOp extends AMD64LIRInstruction { public static final LIRInstructionClass TYPE = LIRInstructionClass.create(LeaOp.class); diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.graal.lir/src/com/oracle/graal/lir/StandardOp.java --- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/StandardOp.java Mon May 04 19:12:50 2015 +0200 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/StandardOp.java Mon May 04 19:34:51 2015 +0200 @@ -235,4 +235,33 @@ // do nothing, just keep value alive until at least here } } + + public static final class StackMove extends LIRInstruction implements MoveOp { + public static final LIRInstructionClass TYPE = LIRInstructionClass.create(StackMove.class); + + @Def({STACK, HINT}) protected AllocatableValue result; + @Use({STACK}) protected Value input; + + public StackMove(AllocatableValue result, Value input) { + super(TYPE); + this.result = result; + this.input = input; + } + + @Override + public void emitCode(CompilationResultBuilder crb) { + throw new GraalInternalError(this + " should have been removed"); + } + + @Override + public Value getInput() { + return input; + } + + @Override + public AllocatableValue getResult() { + return result; + } + } + } diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScan.java --- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScan.java Mon May 04 19:12:50 2015 +0200 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScan.java Mon May 04 19:34:51 2015 +0200 @@ -200,6 +200,10 @@ return spillMoveFactory; } + protected MoveResolver createMoveResolver() { + return new MoveResolver(this); + } + public static boolean isVariableOrRegister(Value value) { return isVariable(value) || isRegister(value); } @@ -554,9 +558,7 @@ // move target is a stack slot that is always correct, so eliminate // instruction if (Debug.isLogEnabled()) { - if (Debug.isLogEnabled()) { - Debug.log("eliminating move from interval %d to %d", operandNumber(move.getInput()), operandNumber(move.getResult())); - } + Debug.log("eliminating move from interval %d to %d", operandNumber(move.getInput()), operandNumber(move.getResult())); } // null-instructions are deleted by assignRegNum instructions.set(j, null); @@ -688,7 +690,7 @@ // iterate all blocks for (final AbstractBlockBase block : sortedBlocks) { - try (Indent indent = Debug.logAndIndent("compute local live sets for block %d", block.getId())) { + try (Indent indent = Debug.logAndIndent("compute local live sets for block %s", block)) { final BitSet liveGen = new BitSet(liveSize); final BitSet liveKill = new BitSet(liveSize); @@ -702,7 +704,7 @@ if (!liveKill.get(operandNum)) { liveGen.set(operandNum); if (Debug.isLogEnabled()) { - Debug.log("liveGen for operand %d", operandNum); + Debug.log("liveGen for operand %d(%s)", operandNum, operand); } } if (block.getLoop() != null) { @@ -720,7 +722,7 @@ if (!liveKill.get(operandNum)) { liveGen.set(operandNum); if (Debug.isLogEnabled()) { - Debug.log("liveGen in state for operand %d", operandNum); + Debug.log("liveGen in state for operand %d(%s)", operandNum, operand); } } } @@ -730,7 +732,7 @@ int varNum = operandNumber(operand); liveKill.set(varNum); if (Debug.isLogEnabled()) { - Debug.log("liveKill for operand %d", varNum); + Debug.log("liveKill for operand %d(%s)", varNum, operand); } if (block.getLoop() != null) { intervalInLoop.setBit(varNum, block.getLoop().getIndex()); @@ -1521,7 +1523,7 @@ try (Indent indent = Debug.logAndIndent("resolve data flow")) { int numBlocks = blockCount(); - MoveResolver moveResolver = new MoveResolver(this); + MoveResolver moveResolver = createMoveResolver(); BitSet blockCompleted = new BitSet(numBlocks); BitSet alreadyResolved = new BitSet(numBlocks); @@ -1613,14 +1615,17 @@ if (DetailedAsserts.getValue()) { AbstractBlockBase block = blockForId(opId); if (block.getSuccessorCount() <= 1 && opId == getLastLirInstructionId(block)) { - // check if spill moves could have been appended at the end of this block, but - // before the branch instruction. So the split child information for this branch - // would - // be incorrect. + /* + * Check if spill moves could have been appended at the end of this block, but + * before the branch instruction. So the split child information for this branch + * would be incorrect. + */ LIRInstruction instr = ir.getLIRforBlock(block).get(ir.getLIRforBlock(block).size() - 1); if (instr instanceof StandardOp.JumpOp) { if (blockData.get(block).liveOut.get(operandNumber(operand))) { - assert false : "can't get split child for the last branch of a block because the information would be incorrect (moves are inserted before the branch in resolveDataFlow)"; + assert false : String.format( + "can't get split child for the last branch of a block because the information would be incorrect (moves are inserted before the branch in resolveDataFlow) block=%s, instruction=%s, operand=%s", + block, instr, operand); } } } diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanWalker.java --- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanWalker.java Mon May 04 19:12:50 2015 +0200 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanWalker.java Mon May 04 19:34:51 2015 +0200 @@ -74,7 +74,7 @@ LinearScanWalker(LinearScan allocator, Interval unhandledFixedFirst, Interval unhandledAnyFirst) { super(allocator, unhandledFixedFirst, unhandledAnyFirst); - moveResolver = new MoveResolver(allocator); + moveResolver = allocator.createMoveResolver(); spillIntervals = Util.uncheckedCast(new List[allocator.registers.length]); for (int i = 0; i < allocator.registers.length; i++) { spillIntervals[i] = EMPTY_LIST; diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/MoveResolver.java --- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/MoveResolver.java Mon May 04 19:12:50 2015 +0200 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/MoveResolver.java Mon May 04 19:34:51 2015 +0200 @@ -29,6 +29,7 @@ import com.oracle.graal.api.code.*; import com.oracle.graal.api.meta.*; +import com.oracle.graal.compiler.common.*; import com.oracle.graal.debug.*; import com.oracle.graal.lir.*; @@ -47,13 +48,18 @@ private boolean multipleReadsAllowed; private final int[] registerBlocked; - private int registerBlocked(int reg) { - return registerBlocked[reg]; + private void setValueBlocked(Value location, int direction) { + assert direction == 1 || direction == -1 : "out of bounds"; + if (isRegister(location)) { + registerBlocked[asRegister(location).number] += direction; + } } - private void setRegisterBlocked(int reg, int direction) { - assert direction == 1 || direction == -1 : "out of bounds"; - registerBlocked[reg] += direction; + private int valueBlocked(Value location) { + if (isRegister(location)) { + return registerBlocked[asRegister(location).number]; + } + throw GraalInternalError.shouldNotReachHere("unhandled value " + location); } void setMultipleReadsAllowed() { @@ -80,7 +86,7 @@ boolean checkEmpty() { assert mappingFrom.size() == 0 && mappingFromOpr.size() == 0 && mappingTo.size() == 0 : "list must be empty before and after processing"; for (int i = 0; i < allocator.registers.length; i++) { - assert registerBlocked(i) == 0 : "register map must be empty before and after processing"; + assert registerBlocked[i] == 0 : "register map must be empty before and after processing"; } assert !multipleReadsAllowed : "must have default value"; return true; @@ -149,9 +155,9 @@ private void blockRegisters(Interval interval) { Value location = interval.location(); if (isRegister(location)) { - int reg = asRegister(location).number; - assert multipleReadsAllowed || registerBlocked(reg) == 0 : "register already marked as used"; - setRegisterBlocked(reg, 1); + assert multipleReadsAllowed || valueBlocked(location) == 0 : "register already marked as used"; + int direction = 1; + setValueBlocked(location, direction); } } @@ -159,9 +165,8 @@ private void unblockRegisters(Interval interval) { Value location = interval.location(); if (isRegister(location)) { - int reg = asRegister(location).number; - assert registerBlocked(reg) > 0 : "register already marked as unused"; - setRegisterBlocked(reg, -1); + assert valueBlocked(location) > 0 : "register already marked as unused"; + setValueBlocked(location, -1); } } @@ -174,7 +179,7 @@ Value reg = to.location(); if (isRegister(reg)) { - if (registerBlocked(asRegister(reg).number) > 1 || (registerBlocked(asRegister(reg).number) == 1 && !reg.equals(fromReg))) { + if (valueBlocked(reg) > 1 || (valueBlocked(reg) == 1 && !reg.equals(fromReg))) { return false; } } @@ -350,7 +355,8 @@ } assert !fromInterval.operand.equals(toInterval.operand) : "from and to interval equal: " + fromInterval; - assert fromInterval.kind().equals(toInterval.kind()); + assert fromInterval.kind().equals(toInterval.kind()) || (fromInterval.kind().getPlatformKind().equals(toInterval.kind().getPlatformKind()) && toInterval.kind().isDerivedReference()) : String.format( + "Kind mismatch: %s vs. %s, from=%s, to=%s", fromInterval.kind(), toInterval.kind(), fromInterval, toInterval); mappingFrom.add(fromInterval); mappingFromOpr.add(Value.ILLEGAL); mappingTo.add(toInterval); diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/LIRGenerator.java --- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/LIRGenerator.java Mon May 04 19:12:50 2015 +0200 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/LIRGenerator.java Mon May 04 19:34:51 2015 +0200 @@ -183,7 +183,7 @@ TTY.println(); } assert LIRVerifier.verify(op); - res.getLIR().getLIRforBlock(currentBlock).add(op); + res.getLIR().getLIRforBlock(getCurrentBlock()).add(op); return op; } @@ -195,35 +195,55 @@ return ops.get(ops.size() - 1) instanceof BlockEndOp; } - public final void doBlockStart(AbstractBlockBase block) { - if (Options.PrintIRWithLIR.getValue()) { - TTY.print(block.toString()); + private final class BlockScopeImpl extends BlockScope { + + private BlockScopeImpl(AbstractBlockBase block) { + currentBlock = block; + } + + private void doBlockStart() { + if (Options.PrintIRWithLIR.getValue()) { + TTY.print(currentBlock.toString()); + } + + // set up the list of LIR instructions + assert res.getLIR().getLIRforBlock(currentBlock) == null : "LIR list already computed for this block"; + res.getLIR().setLIRforBlock(currentBlock, new ArrayList()); + + append(new LabelOp(new Label(currentBlock.getId()), currentBlock.isAligned())); + + if (Options.TraceLIRGeneratorLevel.getValue() >= 1) { + TTY.println("BEGIN Generating LIR for block B" + currentBlock.getId()); + } } - currentBlock = block; + private void doBlockEnd() { + if (Options.TraceLIRGeneratorLevel.getValue() >= 1) { + TTY.println("END Generating LIR for block B" + currentBlock.getId()); + } - // set up the list of LIR instructions - assert res.getLIR().getLIRforBlock(block) == null : "LIR list already computed for this block"; - res.getLIR().setLIRforBlock(block, new ArrayList()); + if (Options.PrintIRWithLIR.getValue()) { + TTY.println(); + } + currentBlock = null; + } - append(new LabelOp(new Label(block.getId()), block.isAligned())); + @Override + public AbstractBlockBase getCurrentBlock() { + return currentBlock; + } - if (Options.TraceLIRGeneratorLevel.getValue() >= 1) { - TTY.println("BEGIN Generating LIR for block B" + block.getId()); + @Override + public void close() { + doBlockEnd(); } + } - public final void doBlockEnd(AbstractBlockBase block) { - - if (Options.TraceLIRGeneratorLevel.getValue() >= 1) { - TTY.println("END Generating LIR for block B" + block.getId()); - } - - currentBlock = null; - - if (Options.PrintIRWithLIR.getValue()) { - TTY.println(); - } + public final BlockScope getBlockScope(AbstractBlockBase block) { + BlockScopeImpl blockScope = new BlockScopeImpl(block); + blockScope.doBlockStart(); + return blockScope; } public void emitIncomingValues(Value[] params) { diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/LIRGeneratorTool.java --- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/LIRGeneratorTool.java Mon May 04 19:12:50 2015 +0200 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/LIRGeneratorTool.java Mon May 04 19:34:51 2015 +0200 @@ -29,12 +29,25 @@ import com.oracle.graal.compiler.common.cfg.*; import com.oracle.graal.compiler.common.spi.*; import com.oracle.graal.lir.*; +import com.oracle.graal.lir.StandardOp.StackMove; public interface LIRGeneratorTool extends ArithmeticLIRGenerator, BenchmarkCounterFactory { public interface SpillMoveFactory { LIRInstruction createMove(AllocatableValue result, Value input); + + default LIRInstruction createStackMove(AllocatableValue result, Value input) { + return new StackMove(result, input); + } + } + + public abstract class BlockScope implements AutoCloseable { + + public abstract AbstractBlockBase getCurrentBlock(); + + public abstract void close(); + } CodeGenProviders getProviders(); @@ -55,9 +68,7 @@ SpillMoveFactory getSpillMoveFactory(); - void doBlockStart(AbstractBlockBase block); - - void doBlockEnd(AbstractBlockBase block); + BlockScope getBlockScope(AbstractBlockBase block); Value emitLoadConstant(LIRKind kind, Constant constant); diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/PhiResolver.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/PhiResolver.java Mon May 04 19:34:51 2015 +0200 @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2009, 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.lir.gen; + +import static com.oracle.graal.api.code.ValueUtil.*; +import static com.oracle.graal.api.meta.Value.*; +import static com.oracle.graal.lir.LIRValueUtil.*; + +import java.util.*; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.compiler.common.*; +import com.oracle.graal.compiler.common.cfg.*; +import com.oracle.graal.lir.*; +import com.oracle.graal.lir.gen.LIRGeneratorTool.*; + +/** + * Converts phi instructions into moves. + * + * Resolves cycles: + * + *
+ *
+ *  r1 := r2  becomes  temp := r1
+ *  r2 := r1           r1 := r2
+ *                     r2 := temp
+ * 
+ * + * and orders moves: + * + *
+ *  r2 := r3  becomes  r1 := r2
+ *  r1 := r2           r2 := r3
+ * 
+ */ +public class PhiResolver { + + /** + * Tracks a data flow dependency between a source operand and any number of the destination + * operands. + */ + static class PhiResolverNode { + + /** + * A source operand whose value flows into the {@linkplain #destinations destination} + * operands. + */ + final Value operand; + + /** + * The operands whose values are defined by the {@linkplain #operand source} operand. + */ + final ArrayList destinations; + + /** + * Denotes if a move instruction has already been emitted to initialize the value of + * {@link #operand}. + */ + boolean assigned; + + /** + * Specifies if this operand been visited for the purpose of emitting a move instruction. + */ + boolean visited; + + /** + * Specifies if this is the initial definition in data flow path for a given value. + */ + boolean startNode; + + PhiResolverNode(Value operand) { + this.operand = operand; + destinations = new ArrayList<>(4); + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(operand.toString()); + if (!destinations.isEmpty()) { + buf.append(" ->"); + for (PhiResolverNode node : destinations) { + buf.append(' ').append(node.operand); + } + } + return buf.toString(); + } + } + + private final LIRGeneratorTool gen; + private final SpillMoveFactory moveFactory; + private final LIRInsertionBuffer buffer; + private final int insertBefore; + + /** + * The operand loop header phi for the operand currently being process in {@link #dispose()}. + */ + private PhiResolverNode loop; + + private Value temp; + + private final ArrayList variableOperands = new ArrayList<>(3); + private final ArrayList otherOperands = new ArrayList<>(3); + + /** + * Maps operands to nodes. + */ + private final HashMap operandToNodeMap = CollectionsFactory.newMap(); + + public static PhiResolver create(LIRGeneratorTool gen) { + AbstractBlockBase block = gen.getCurrentBlock(); + assert block != null; + List instructions = gen.getResult().getLIR().getLIRforBlock(block); + + return new PhiResolver(gen, new LIRInsertionBuffer(), instructions, instructions.size()); + } + + public static PhiResolver create(LIRGeneratorTool gen, LIRInsertionBuffer buffer, List instructions, int insertBefore) { + return new PhiResolver(gen, buffer, instructions, insertBefore); + } + + protected PhiResolver(LIRGeneratorTool gen, LIRInsertionBuffer buffer, List instructions, int insertBefore) { + this.gen = gen; + moveFactory = gen.getSpillMoveFactory(); + temp = ILLEGAL; + + this.buffer = buffer; + this.buffer.init(instructions); + this.insertBefore = insertBefore; + + } + + public void dispose() { + // resolve any cycles in moves from and to variables + for (int i = variableOperands.size() - 1; i >= 0; i--) { + PhiResolverNode node = variableOperands.get(i); + if (!node.visited) { + loop = null; + move(node, null); + node.startNode = true; + assert isIllegal(temp) : "moveTempTo() call missing"; + } + } + + // generate move for move from non variable to arbitrary destination + for (int i = otherOperands.size() - 1; i >= 0; i--) { + PhiResolverNode node = otherOperands.get(i); + for (int j = node.destinations.size() - 1; j >= 0; j--) { + emitMove(node.destinations.get(j).operand, node.operand); + } + } + buffer.finish(); + } + + public void move(Value dest, Value src) { + assert isVariable(dest) : "destination must be virtual"; + // tty.print("move "); src.print(); tty.print(" to "); dest.print(); tty.cr(); + assert isLegal(src) : "source for phi move is illegal"; + assert isLegal(dest) : "destination for phi move is illegal"; + PhiResolverNode srcNode = sourceNode(src); + PhiResolverNode destNode = destinationNode(dest); + srcNode.destinations.add(destNode); + } + + private PhiResolverNode createNode(Value operand, boolean source) { + PhiResolverNode node; + if (isVariable(operand)) { + node = operandToNodeMap.get(operand); + assert node == null || node.operand.equals(operand); + if (node == null) { + node = new PhiResolverNode(operand); + operandToNodeMap.put(operand, node); + } + // Make sure that all variables show up in the list when + // they are used as the source of a move. + if (source) { + if (!variableOperands.contains(node)) { + variableOperands.add(node); + } + } + } else { + assert source; + node = new PhiResolverNode(operand); + otherOperands.add(node); + } + return node; + } + + private PhiResolverNode destinationNode(Value opr) { + return createNode(opr, false); + } + + private void emitMove(Value dest, Value src) { + assert isLegal(src); + assert isLegal(dest); + LIRInstruction move = moveFactory.createMove((AllocatableValue) dest, src); + buffer.append(insertBefore, move); + } + + // Traverse assignment graph in depth first order and generate moves in post order + // ie. two assignments: b := c, a := b start with node c: + // Call graph: move(c, NULL) -> move(b, c) -> move(a, b) + // Generates moves in this order: move b to a and move c to b + // ie. cycle a := b, b := a start with node a + // Call graph: move(a, NULL) -> move(b, a) -> move(a, b) + // Generates moves in this order: move b to temp, move a to b, move temp to a + private void move(PhiResolverNode dest, PhiResolverNode src) { + if (!dest.visited) { + dest.visited = true; + for (int i = dest.destinations.size() - 1; i >= 0; i--) { + move(dest.destinations.get(i), dest); + } + } else if (!dest.startNode) { + // cycle in graph detected + assert loop == null : "only one loop valid!"; + loop = dest; + moveToTemp(src.operand); + return; + } // else dest is a start node + + if (!dest.assigned) { + if (loop == dest) { + moveTempTo(dest.operand); + dest.assigned = true; + } else if (src != null) { + emitMove(dest.operand, src.operand); + dest.assigned = true; + } + } + } + + private void moveTempTo(Value dest) { + assert isLegal(temp); + emitMove(dest, temp); + temp = ILLEGAL; + } + + private void moveToTemp(Value src) { + assert isIllegal(temp); + temp = gen.newVariable(src.getLIRKind()); + emitMove(temp, src); + } + + private PhiResolverNode sourceNode(Value opr) { + return createNode(opr, true); + } +} diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.graal.lir/src/com/oracle/graal/lir/stackslotalloc/FixPointIntervalBuilder.java --- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/stackslotalloc/FixPointIntervalBuilder.java Mon May 04 19:12:50 2015 +0200 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/stackslotalloc/FixPointIntervalBuilder.java Mon May 04 19:34:51 2015 +0200 @@ -189,6 +189,7 @@ if (isVirtualStackSlot(operand)) { VirtualStackSlot vslot = asVirtualStackSlot(operand); addUse(vslot, inst, flags); + addRegisterHint(inst, vslot, mode, flags, false); usePos.add(inst); Debug.log("set operand: %s", operand); currentSet.set(vslot.getId()); @@ -201,6 +202,7 @@ if (isVirtualStackSlot(operand)) { VirtualStackSlot vslot = asVirtualStackSlot(operand); addDef(vslot, inst); + addRegisterHint(inst, vslot, mode, flags, true); usePos.add(inst); Debug.log("clear operand: %s", operand); currentSet.clear(vslot.getId()); @@ -229,6 +231,32 @@ interval.addFrom(inst.id()); } + void addRegisterHint(final LIRInstruction op, VirtualStackSlot targetValue, OperandMode mode, EnumSet flags, final boolean hintAtDef) { + if (flags.contains(OperandFlag.HINT)) { + + op.forEachRegisterHint(targetValue, mode, (registerHint, valueMode, valueFlags) -> { + if (isStackSlotValue(registerHint)) { + assert isVirtualStackSlot(registerHint) : "Hint is not a VirtualStackSlot: " + registerHint; + StackInterval from = getOrCreateInterval((VirtualStackSlot) registerHint); + StackInterval to = getOrCreateInterval(targetValue); + + /* hints always point from def to use */ + if (hintAtDef) { + to.setLocationHint(from); + } else { + from.setLocationHint(to); + } + if (Debug.isLogEnabled()) { + Debug.log("operation %s at opId %d: added hint from interval %d to %d", op, op.id(), from, to); + } + + return registerHint; + } + return null; + }); + } + } + } private StackInterval get(VirtualStackSlot stackSlot) { diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.graal.lir/src/com/oracle/graal/lir/stackslotalloc/StackInterval.java --- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/stackslotalloc/StackInterval.java Mon May 04 19:12:50 2015 +0200 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/stackslotalloc/StackInterval.java Mon May 04 19:34:51 2015 +0200 @@ -30,6 +30,7 @@ private static final int INVALID_START = Integer.MAX_VALUE; private static final int INVALID_END = Integer.MIN_VALUE; private final VirtualStackSlot operand; + private StackInterval hint; private final LIRKind kind; private int from = INVALID_START; private int to = INVALID_END; @@ -98,7 +99,15 @@ @Override public String toString() { - return String.format("SI[%d-%d] k=%s o=%s l=%s", from, to, kind, operand, location); + return String.format("SI[%d-%d] k=%s o=%s l=%s h=%s", from, to, kind, operand, location, hint.getOperand()); + } + + public void setLocationHint(StackInterval locationHint) { + hint = locationHint; + } + + public StackInterval locationHint() { + return hint; } } diff -r 0028ab94d268 -r bfb6e742ad0a 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 Mon May 04 19:12:50 2015 +0200 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/ConvertDeoptimizeToGuardPhase.java Mon May 04 19:34:51 2015 +0200 @@ -134,6 +134,9 @@ } private void visitDeoptBegin(AbstractBeginNode deoptBegin, DeoptimizationAction deoptAction, DeoptimizationReason deoptReason, StructuredGraph graph) { + if (!deoptAction.doesInvalidateCompilation()) { + return; + } if (deoptBegin instanceof AbstractMergeNode) { AbstractMergeNode mergeNode = (AbstractMergeNode) deoptBegin; Debug.log("Visiting %s", mergeNode); diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.graal.printer/src/com/oracle/graal/printer/CFGPrinter.java --- a/graal/com.oracle.graal.printer/src/com/oracle/graal/printer/CFGPrinter.java Mon May 04 19:12:50 2015 +0200 +++ b/graal/com.oracle.graal.printer/src/com/oracle/graal/printer/CFGPrinter.java Mon May 04 19:34:51 2015 +0200 @@ -576,7 +576,8 @@ out.printf("\"[%s|%c]\"", interval.getOperand(), interval.getOperand().getKind().getTypeChar()); } - out.printf("%s -1 ", interval.getOperand()); + StackInterval hint = interval.locationHint(); + out.printf("%s %s ", interval.getOperand(), hint != null ? hint.getOperand() : -1); out.printf("[%d, %d[", interval.from(), interval.to()); diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/InstrumentationPartialEvaluationTest.java --- a/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/InstrumentationPartialEvaluationTest.java Mon May 04 19:12:50 2015 +0200 +++ b/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/InstrumentationPartialEvaluationTest.java Mon May 04 19:34:51 2015 +0200 @@ -205,46 +205,53 @@ } @Test - public void constantValueInertSpliceInstrumentListener() { + public void constantValueInertToolEvalNodeFactory() { FrameDescriptor fd = new FrameDescriptor(); AbstractTestNode result = new ConstantTestNode(42); RootTestNode root = new RootTestNode(fd, "constantValue", result); root.adoptChildren(); - Probe probe = result.probe(); - // A listener that could insert a SplicedNode into the AST, but which never does. - Instrument instrument = Instrument.create(new SpliceInstrumentListener() { + Probe testProbe = result.probe(); + // A factory that could insert a ToolEvalNode into the AST, but which never does. + Instrument instrument = Instrument.create(new ToolEvalNodeFactory() { - public SplicedNode getSpliceNode(Probe p) { + public ToolEvalNode createToolEvalNode(Probe probe, Node node) { return null; } - }, null); - probe.attach(instrument); + testProbe.attach(instrument); // It all gets compiled away assertPartialEvalEquals("constant42", root); } @Test - public void constantValueInertSplicedNode() { + public void constantValueInertToolEvalNode() { FrameDescriptor fd = new FrameDescriptor(); - AbstractTestNode result = new ConstantTestNode(42); - RootTestNode root = new RootTestNode(fd, "constantValue", result); - root.adoptChildren(); - Probe probe = result.probe(); - // A listener that inserts a SplicedNode with empty methods into the AST. - Instrument instrument = Instrument.create(new SpliceInstrumentListener() { + AbstractTestNode resultTestNode = new ConstantTestNode(42); + RootTestNode rootTestNode = new RootTestNode(fd, "constantValue", resultTestNode); + rootTestNode.adoptChildren(); + Probe testProbe = resultTestNode.probe(); + // A factory that inserts a ToolEvalNode with empty methods into the instrumentation chain. + Instrument instrument = Instrument.create(new ToolEvalNodeFactory() { - public SplicedNode getSpliceNode(Probe p) { - return new SplicedNode() { + public ToolEvalNode createToolEvalNode(Probe probe, Node node) { + return new ToolEvalNode() { + + public String instrumentationInfo() { + return null; + } + + @Override + public Object executeToolEvalNode(Node n, VirtualFrame frame) { + return null; + } }; } - }, null); - probe.attach(instrument); + testProbe.attach(instrument); // It all gets compiled away. - assertPartialEvalEquals("constant42", root); + assertPartialEvalEquals("constant42", rootTestNode); } @Test diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/FrameWithBoxing.java --- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/FrameWithBoxing.java Mon May 04 19:12:50 2015 +0200 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/FrameWithBoxing.java Mon May 04 19:34:51 2015 +0200 @@ -22,11 +22,8 @@ */ package com.oracle.graal.truffle; -import java.lang.reflect.*; import java.util.*; -import sun.misc.*; - import com.oracle.truffle.api.*; import com.oracle.truffle.api.frame.*; @@ -236,70 +233,4 @@ static T unsafeCast(Object value, Class type, boolean condition, boolean nonNull) { return (T) value; } - - @SuppressWarnings("unused") - static int unsafeGetInt(Object receiver, long offset, boolean condition, Object locationIdentity) { - return UNSAFE.getInt(receiver, offset); - } - - @SuppressWarnings("unused") - static long unsafeGetLong(Object receiver, long offset, boolean condition, Object locationIdentity) { - return UNSAFE.getLong(receiver, offset); - } - - @SuppressWarnings("unused") - static float unsafeGetFloat(Object receiver, long offset, boolean condition, Object locationIdentity) { - return UNSAFE.getFloat(receiver, offset); - } - - @SuppressWarnings("unused") - static double unsafeGetDouble(Object receiver, long offset, boolean condition, Object locationIdentity) { - return UNSAFE.getDouble(receiver, offset); - } - - @SuppressWarnings("unused") - static Object unsafeGetObject(Object receiver, long offset, boolean condition, Object locationIdentity) { - return UNSAFE.getObject(receiver, offset); - } - - @SuppressWarnings("unused") - static void unsafePutInt(Object receiver, long offset, int value, Object locationIdentity) { - UNSAFE.putInt(receiver, offset, value); - } - - @SuppressWarnings("unused") - static void unsafePutLong(Object receiver, long offset, long value, Object locationIdentity) { - UNSAFE.putLong(receiver, offset, value); - } - - @SuppressWarnings("unused") - static void unsafePutFloat(Object receiver, long offset, float value, Object locationIdentity) { - UNSAFE.putFloat(receiver, offset, value); - } - - @SuppressWarnings("unused") - static void unsafePutDouble(Object receiver, long offset, double value, Object locationIdentity) { - UNSAFE.putDouble(receiver, offset, value); - } - - @SuppressWarnings("unused") - static void unsafePutObject(Object receiver, long offset, Object value, Object locationIdentity) { - UNSAFE.putObject(receiver, offset, value); - } - - private static final Unsafe UNSAFE = getUnsafe(); - - private static Unsafe getUnsafe() { - try { - return Unsafe.getUnsafe(); - } catch (SecurityException e) { - } - try { - Field theUnsafeInstance = Unsafe.class.getDeclaredField("theUnsafe"); - theUnsafeInstance.setAccessible(true); - return (Unsafe) theUnsafeInstance.get(Unsafe.class); - } catch (Exception e) { - throw new RuntimeException("exception while trying to get Unsafe.theUnsafe via reflection:", e); - } - } } diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/FrameWithoutBoxing.java --- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/FrameWithoutBoxing.java Mon May 04 19:12:50 2015 +0200 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/FrameWithoutBoxing.java Mon May 04 19:34:51 2015 +0200 @@ -104,7 +104,8 @@ } private Object getObjectUnsafe(int slotIndex, FrameSlot slot) { - return unsafeGetObject(getLocals(), Unsafe.ARRAY_OBJECT_BASE_OFFSET + slotIndex * (long) Unsafe.ARRAY_OBJECT_INDEX_SCALE, this.getTags()[slotIndex] == FrameSlotKind.Object.tag, slot); + boolean condition = this.getTags()[slotIndex] == OBJECT_TAG; + return unsafeGetObject(getLocals(), Unsafe.ARRAY_OBJECT_BASE_OFFSET + slotIndex * (long) Unsafe.ARRAY_OBJECT_INDEX_SCALE, condition, slot); } @Override @@ -127,7 +128,7 @@ private byte getByteUnsafe(int slotIndex, FrameSlot slot) { long offset = getPrimitiveOffset(slotIndex); - boolean condition = this.getTags()[slotIndex] == FrameSlotKind.Byte.tag; + boolean condition = this.getTags()[slotIndex] == BYTE_TAG; return (byte) unsafeGetInt(getPrimitiveLocals(), offset, condition, slot); } @@ -152,7 +153,7 @@ private boolean getBooleanUnsafe(int slotIndex, FrameSlot slot) { long offset = getPrimitiveOffset(slotIndex); - boolean condition = this.getTags()[slotIndex] == FrameSlotKind.Boolean.tag; + boolean condition = this.getTags()[slotIndex] == BOOLEAN_TAG; return unsafeGetInt(getPrimitiveLocals(), offset, condition, slot) != 0; } @@ -177,7 +178,7 @@ private float getFloatUnsafe(int slotIndex, FrameSlot slot) { long offset = getPrimitiveOffset(slotIndex); - boolean condition = this.getTags()[slotIndex] == FrameSlotKind.Float.tag; + boolean condition = this.getTags()[slotIndex] == FLOAT_TAG; return unsafeGetFloat(getPrimitiveLocals(), offset, condition, slot); } @@ -202,7 +203,7 @@ private long getLongUnsafe(int slotIndex, FrameSlot slot) { long offset = getPrimitiveOffset(slotIndex); - boolean condition = this.getTags()[slotIndex] == FrameSlotKind.Long.tag; + boolean condition = this.getTags()[slotIndex] == LONG_TAG; return unsafeGetLong(getPrimitiveLocals(), offset, condition, slot); } @@ -227,7 +228,7 @@ private int getIntUnsafe(int slotIndex, FrameSlot slot) { long offset = getPrimitiveOffset(slotIndex); - boolean condition = this.getTags()[slot.getIndex()] == FrameSlotKind.Int.tag; + boolean condition = this.getTags()[slot.getIndex()] == INT_TAG; return unsafeGetInt(getPrimitiveLocals(), offset, condition, slot); } @@ -252,7 +253,7 @@ private double getDoubleUnsafe(int slotIndex, FrameSlot slot) { long offset = getPrimitiveOffset(slotIndex); - boolean condition = this.getTags()[slotIndex] == FrameSlotKind.Double.tag; + boolean condition = this.getTags()[slotIndex] == DOUBLE_TAG; return unsafeGetDouble(getPrimitiveLocals(), offset, condition, slot); } @@ -306,20 +307,20 @@ resize(); } byte tag = getTags()[slotIndex]; - if (tag == FrameSlotKind.Boolean.tag) { + if (tag == BOOLEAN_TAG) { return getBooleanUnsafe(slotIndex, slot); - } else if (tag == FrameSlotKind.Byte.tag) { + } else if (tag == BYTE_TAG) { return getByteUnsafe(slotIndex, slot); - } else if (tag == FrameSlotKind.Int.tag) { + } else if (tag == INT_TAG) { return getIntUnsafe(slotIndex, slot); - } else if (tag == FrameSlotKind.Double.tag) { + } else if (tag == DOUBLE_TAG) { return getDoubleUnsafe(slotIndex, slot); - } else if (tag == FrameSlotKind.Long.tag) { + } else if (tag == LONG_TAG) { return getLongUnsafe(slotIndex, slot); - } else if (tag == FrameSlotKind.Float.tag) { + } else if (tag == FLOAT_TAG) { return getFloatUnsafe(slotIndex, slot); } else { - assert tag == FrameSlotKind.Object.tag; + assert tag == OBJECT_TAG; return getObjectUnsafe(slotIndex, slot); } } @@ -348,37 +349,37 @@ @Override public boolean isObject(FrameSlot slot) { - return getTag(slot) == FrameSlotKind.Object.tag; + return getTag(slot) == OBJECT_TAG; } @Override public boolean isByte(FrameSlot slot) { - return getTag(slot) == FrameSlotKind.Byte.tag; + return getTag(slot) == BYTE_TAG; } @Override public boolean isBoolean(FrameSlot slot) { - return getTag(slot) == FrameSlotKind.Boolean.tag; + return getTag(slot) == BOOLEAN_TAG; } @Override public boolean isInt(FrameSlot slot) { - return getTag(slot) == FrameSlotKind.Int.tag; + return getTag(slot) == INT_TAG; } @Override public boolean isLong(FrameSlot slot) { - return getTag(slot) == FrameSlotKind.Long.tag; + return getTag(slot) == LONG_TAG; } @Override public boolean isFloat(FrameSlot slot) { - return getTag(slot) == FrameSlotKind.Float.tag; + return getTag(slot) == FLOAT_TAG; } @Override public boolean isDouble(FrameSlot slot) { - return getTag(slot) == FrameSlotKind.Double.tag; + return getTag(slot) == DOUBLE_TAG; } @SuppressWarnings({"unchecked", "unused"}) diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java --- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java Mon May 04 19:12:50 2015 +0200 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java Mon May 04 19:34:51 2015 +0200 @@ -458,7 +458,7 @@ } @Override - public void nodeReplaced(Node oldNode, Node newNode, CharSequence reason) { + public boolean nodeReplaced(Node oldNode, Node newNode, CharSequence reason) { CompilerAsserts.neverPartOfCompilation(); if (isValid()) { invalidate(newNode, reason); @@ -470,6 +470,7 @@ if (cancelInstalledTask(newNode, reason)) { compilationProfile.reportInvalidated(); } + return false; } public void accept(NodeVisitor visitor, boolean includeInlinedNodes) { diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedOSRLoopNode.java --- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedOSRLoopNode.java Mon May 04 19:12:50 2015 +0200 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedOSRLoopNode.java Mon May 04 19:34:51 2015 +0200 @@ -157,8 +157,9 @@ } } - public void nodeReplaced(Node oldNode, Node newNode, CharSequence reason) { + public boolean nodeReplaced(Node oldNode, Node newNode, CharSequence reason) { invalidate(newNode, reason); + return false; } private void invalidate(Object source, CharSequence reason) { diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestNodes.java --- a/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestNodes.java Mon May 04 19:12:50 2015 +0200 +++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestNodes.java Mon May 04 19:34:51 2015 +0200 @@ -167,17 +167,22 @@ } } - static class TestSplicedCounterNode extends SplicedNode { + static class TestToolEvalCounterNode extends ToolEvalNode { private long count; @Override - public void enter(Node node, VirtualFrame vFrame) { + public Object executeToolEvalNode(Node node, VirtualFrame vFrame) { count++; + return null; } public long getCount() { return count; } + + public String instrumentationInfo() { + return null; + } } } diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/SpliceInstrumentTest.java --- a/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/SpliceInstrumentTest.java Mon May 04 19:12:50 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2015, 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.test.instrument; - -import static org.junit.Assert.*; - -import org.junit.*; - -import com.oracle.truffle.api.*; -import com.oracle.truffle.api.instrument.*; -import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestAdditionNode; -import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestRootNode; -import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestValueNode; -import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestSplicedCounterNode; - -/** - * Tests the kind of instrumentation where a client can provide an AST fragment to be - * spliced directly into the AST. - */ -public class SpliceInstrumentTest { - - @Test - public void testSpliceInstrumentListener() { - // Create a simple addition AST - final TruffleRuntime runtime = Truffle.getRuntime(); - final TestValueNode leftValueNode = new TestValueNode(6); - final TestValueNode rightValueNode = new TestValueNode(7); - final TestAdditionNode addNode = new TestAdditionNode(leftValueNode, rightValueNode); - final TestRootNode rootNode = new TestRootNode(addNode); - final CallTarget callTarget1 = runtime.createCallTarget(rootNode); - - // Ensure it executes correctly - assertEquals(13, callTarget1.call()); - - // Probe the addition node - final Probe probe = addNode.probe(); - - assertEquals(13, callTarget1.call()); - - // Attach a null listener; it never actually attaches a node. - final Instrument instrument = Instrument.create(new SpliceInstrumentListener() { - - public SplicedNode getSpliceNode(Probe p) { - return null; - } - - }, null); - probe.attach(instrument); - - assertEquals(13, callTarget1.call()); - - final TestSplicedCounterNode counter = new TestSplicedCounterNode(); - - // Attach a listener that splices an execution counter into the AST. - probe.attach(Instrument.create(new SpliceInstrumentListener() { - - public SplicedNode getSpliceNode(Probe p) { - return counter; - } - - }, null)); - assertEquals(0, counter.getCount()); - - assertEquals(13, callTarget1.call()); - - assertEquals(1, counter.getCount()); - - } - -} diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/ToolEvalInstrumentTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/ToolEvalInstrumentTest.java Mon May 04 19:34:51 2015 +0200 @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2015, 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.test.instrument; + +import static org.junit.Assert.*; + +import org.junit.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.instrument.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestAdditionNode; +import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestRootNode; +import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestValueNode; +import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestToolEvalCounterNode; + +/** + * Tests the kind of instrumentation where a client can provide an AST fragment to be + * spliced directly into the AST. + */ +public class ToolEvalInstrumentTest { + + @Test + public void testSpliceInstrumentListener() { + // Create a simple addition AST + final TruffleRuntime runtime = Truffle.getRuntime(); + final TestValueNode leftValueNode = new TestValueNode(6); + final TestValueNode rightValueNode = new TestValueNode(7); + final TestAdditionNode addNode = new TestAdditionNode(leftValueNode, rightValueNode); + final TestRootNode rootNode = new TestRootNode(addNode); + final CallTarget callTarget1 = runtime.createCallTarget(rootNode); + + // Ensure it executes correctly + assertEquals(13, callTarget1.call()); + + // Probe the addition node + final Probe probe = addNode.probe(); + + assertEquals(13, callTarget1.call()); + + // Attach a null listener; it never actually attaches a node. + final Instrument instrument = Instrument.create(new ToolEvalNodeFactory() { + + public ToolEvalNode createToolEvalNode(Probe p, Node n) { + return null; + } + }, null); + probe.attach(instrument); + + assertEquals(13, callTarget1.call()); + + final TestToolEvalCounterNode counter = new TestToolEvalCounterNode(); + + // Attach a listener that splices an execution counter into the AST. + probe.attach(Instrument.create(new ToolEvalNodeFactory() { + + public ToolEvalNode createToolEvalNode(Probe p, Node n) { + return counter; + } + }, null)); + assertEquals(0, counter.getCount()); + + assertEquals(13, callTarget1.call()); + + assertEquals(1, counter.getCount()); + + } + +} diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ReplaceObserver.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ReplaceObserver.java Mon May 04 19:12:50 2015 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ReplaceObserver.java Mon May 04 19:34:51 2015 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2015, 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 @@ -31,5 +31,10 @@ */ public interface ReplaceObserver { - void nodeReplaced(Node oldNode, Node newNode, CharSequence reason); + /** + * Returns true if the event is consumed and no parent nodes should be notified by + * for replaces. Returns false if the parent {@link Node} or {@link CallTarget} + * should get notified. + */ + boolean nodeReplaced(Node oldNode, Node newNode, CharSequence reason); } diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.truffle.api/src/com/oracle/truffle/api/frame/Frame.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/frame/Frame.java Mon May 04 19:12:50 2015 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/frame/Frame.java Mon May 04 19:34:51 2015 +0200 @@ -167,8 +167,7 @@ /** * Materializes this frame, which allows it to be stored in a field or cast to - * {@link java.lang.Object}. The frame however looses the ability to be packed or to access the - * caller frame. + * {@link java.lang.Object}. * * @return the new materialized frame */ diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.truffle.api/src/com/oracle/truffle/api/frame/MaterializedFrame.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/frame/MaterializedFrame.java Mon May 04 19:12:50 2015 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/frame/MaterializedFrame.java Mon May 04 19:34:51 2015 +0200 @@ -27,9 +27,7 @@ /** * Represents a materialized frame containing values of local variables of the guest language. It * can be created using the {@link VirtualFrame#materialize()} method. Instances of this type are - * the only frame instances that may be stored in fields or cast to {@link java.lang.Object}. In - * contrast to a {@link VirtualFrame}, a {@link MaterializedFrame} can no longer be packed and it - * also does not provide access to the caller frame. + * the only frame instances that may be stored in fields or cast to {@link java.lang.Object}. */ public interface MaterializedFrame extends Frame { diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java Mon May 04 19:12:50 2015 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java Mon May 04 19:34:51 2015 +0200 @@ -103,10 +103,11 @@ public abstract class Instrument { /** - * Creates an instrument that will route execution events to a listener. + * Creates a Simple Instrument: this Instrument routes execution events to a + * client-provided listener. * - * @param listener a minimal listener for event generated by the instrument. - * @param instrumentInfo optional description of the instrument's role, useful for debugging. + * @param listener a listener for execution events + * @param instrumentInfo optional description of the instrument's role, intended for debugging. * @return a new instrument, ready for attachment at a probe */ public static Instrument create(SimpleInstrumentListener listener, String instrumentInfo) { @@ -114,12 +115,11 @@ } /** - * Creates an instrument that will route execution events to a listener, along with access to - * internal execution state. + * Creates a Standard Instrument: this Instrument routes execution events, together + * with access to Truffle execution state, to a client-provided listener. * - * @param standardListener a listener for event generated by the instrument that provides access - * to internal execution state - * @param instrumentInfo optional description of the instrument's role, useful for debugging. + * @param standardListener a listener for execution events and execution state + * @param instrumentInfo optional description of the instrument's role, intended for debugging. * @return a new instrument, ready for attachment at a probe */ public static Instrument create(StandardInstrumentListener standardListener, String instrumentInfo) { @@ -127,17 +127,16 @@ } /** - * Creates an instrument that, when executed the first time in any particular AST location, - * invites the tool to provide an AST fragment for splicing directly into the running - * AST. + * Creates a Tool Eval Instrument: this Instrument executes efficiently, subject to + * full Truffle optimization, a client-provided AST fragment every time the Probed node is + * entered. * - * @param spliceListener a callback to the client that requests an AST node to be splice. - * @param instrumentInfo instrumentInfo optional description of the instrument's role, useful - * for debugging. - * @return a new instrument, ready for attachment at a probe. + * @param toolEvalNodeFactory provider of AST fragments on behalf of the client + * @param instrumentInfo optional description of the instrument's role, intended for debugging. + * @return a new instrument, ready for attachment at a probe */ - public static Instrument create(SpliceInstrumentListener spliceListener, String instrumentInfo) { - return new SpliceInstrument(spliceListener, instrumentInfo); + public static Instrument create(ToolEvalNodeFactory toolEvalNodeFactory, String instrumentInfo) { + return new ToolEvalInstrument(toolEvalNodeFactory, instrumentInfo); } // TODO (mlvdv) experimental @@ -165,7 +164,16 @@ } /** - * Removes this instrument from the probe to which it attached and renders the instrument inert. + * Gets the {@link Probe} to which this Instrument is currently attached: {@code null} if not + * yet attached to a Probe or if this Instrument has been {@linkplain #dispose() disposed}. + */ + public Probe getProbe() { + return probe; + } + + /** + * Removes this Instrument from the Probe to which it attached and renders this Instrument + * inert. * * @throws IllegalStateException if this instrument has already been disposed */ @@ -176,14 +184,14 @@ if (probe != null) { // It's attached probe.disposeInstrument(this); + probe = null; } this.isDisposed = true; } - Probe getProbe() { - return probe; - } - + /** + * For internal implementation only. + */ void setAttachedTo(Probe probe) { this.probe = probe; } @@ -239,6 +247,9 @@ return instrumentNode; } + /** + * Node that implements a {@link SimpleInstrument} in a particular AST. + */ @NodeInfo(cost = NodeCost.NONE) private final class SimpleInstrumentNode extends AbstractInstrumentNode { @@ -318,6 +329,9 @@ return instrumentNode; } + /** + * Node that implements a {@link StandardInstrument} in a particular AST. + */ @NodeInfo(cost = NodeCost.NONE) private final class StandardInstrumentNode extends AbstractInstrumentNode { @@ -360,26 +374,27 @@ } } - // TODO (mlvdv) EXPERIMENTAL- UNDER DEVELOPMENT /** - * An instrument that allows clients to "splice" an AST fragment directly into a Probe's - * instrumentation chain, and thus directly into the executing Truffle AST. + * An instrument that allows clients to provide an AST fragment to be executed directly from + * within a Probe's instrumentation chain, and thus directly in the executing Truffle + * AST with potential for full optimization. */ - private static final class SpliceInstrument extends Instrument { + private static final class ToolEvalInstrument extends Instrument { /** - * Tool-supplied listener for AST events. + * Client-provided supplier of new node instances to attach. */ - private final SpliceInstrumentListener spliceListener; + private final ToolEvalNodeFactory toolEvalNodeFactory; - private SpliceInstrument(SpliceInstrumentListener spliceListener, String instrumentInfo) { + private ToolEvalInstrument(ToolEvalNodeFactory toolEvalNodeFactory, String instrumentInfo) { super(instrumentInfo); - this.spliceListener = spliceListener; + this.toolEvalNodeFactory = toolEvalNodeFactory; + } @Override AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode) { - return new SpliceInstrumentNode(nextNode); + return new ToolEvalNodeInstrumentNode(nextNode); } @Override @@ -391,7 +406,7 @@ return instrumentNode.nextInstrumentNode; } // Match not at the head of the chain; remove it. - found = instrumentNode.removeFromChain(SpliceInstrument.this); + found = instrumentNode.removeFromChain(ToolEvalInstrument.this); } if (!found) { throw new IllegalStateException("Couldn't find instrument node to remove: " + this); @@ -399,26 +414,30 @@ return instrumentNode; } + /** + * Node that implements a {@link ToolEvalInstrument} in a particular AST. + */ @NodeInfo(cost = NodeCost.NONE) - private final class SpliceInstrumentNode extends AbstractInstrumentNode { + private final class ToolEvalNodeInstrumentNode extends AbstractInstrumentNode { - @Child SplicedNode splicedNode; + @Child ToolEvalNode toolEvalNode; - private SpliceInstrumentNode(AbstractInstrumentNode nextNode) { + private ToolEvalNodeInstrumentNode(AbstractInstrumentNode nextNode) { super(nextNode); } public void enter(Node node, VirtualFrame vFrame) { - if (splicedNode == null) { - final SplicedNode newSplicedNode = SpliceInstrument.this.spliceListener.getSpliceNode(SpliceInstrument.this.probe); - if (newSplicedNode != null) { - splicedNode = newSplicedNode; + if (toolEvalNode == null) { + final ToolEvalNode newToolEvalNodeNode = ToolEvalInstrument.this.toolEvalNodeFactory.createToolEvalNode(ToolEvalInstrument.this.probe, node); + if (newToolEvalNodeNode != null) { + toolEvalNode = newToolEvalNodeNode; adoptChildren(); - SpliceInstrument.this.probe.invalidateProbeUnchanged(); + ToolEvalInstrument.this.probe.invalidateProbeUnchanged(); } } - if (splicedNode != null) { - splicedNode.enter(node, vFrame); + if (toolEvalNode != null) { + // TODO (mlvdv) should report exception ; non-trivial architectural change + toolEvalNode.executeToolEvalNode(node, vFrame); } if (nextInstrumentNode != null) { nextInstrumentNode.enter(node, vFrame); @@ -426,27 +445,18 @@ } public void returnVoid(Node node, VirtualFrame vFrame) { - if (splicedNode != null) { - splicedNode.returnVoid(node, vFrame); - } if (nextInstrumentNode != null) { nextInstrumentNode.returnVoid(node, vFrame); } } public void returnValue(Node node, VirtualFrame vFrame, Object result) { - if (splicedNode != null) { - splicedNode.returnValue(node, vFrame, result); - } if (nextInstrumentNode != null) { nextInstrumentNode.returnValue(node, vFrame, result); } } public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) { - if (splicedNode != null) { - splicedNode.returnExceptional(node, vFrame, exception); - } if (nextInstrumentNode != null) { nextInstrumentNode.returnExceptional(node, vFrame, exception); } @@ -454,7 +464,7 @@ public String instrumentationInfo() { final String info = getInstrumentInfo(); - return info != null ? info : spliceListener.getClass().getSimpleName(); + return info != null ? info : toolEvalNodeFactory.getClass().getSimpleName(); } } } diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentationException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentationException.java Mon May 04 19:34:51 2015 +0200 @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2015, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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.instrument; + +public class InstrumentationException extends Exception { + + public InstrumentationException(RuntimeException ex) { + super(ex); + } + + private static final long serialVersionUID = 447857066220935502L; + +} diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeNode.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeNode.java Mon May 04 19:12:50 2015 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeNode.java Mon May 04 19:34:51 2015 +0200 @@ -200,6 +200,13 @@ } /** + * Gets the guest-language AST node to which this Probe is attached. + */ + Node getProbedNode() { + return ((WrapperNode) this.getParent()).getChild(); + } + + /** * Adds an {@link AbstractInstrumentNode} to this chain. */ @TruffleBoundary diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SpliceInstrumentListener.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SpliceInstrumentListener.java Mon May 04 19:12:50 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2015, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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.instrument; - -/** - * Instrument listener for a tool that works by providing an AST to be spliced directly - * into the AST. - */ -public interface SpliceInstrumentListener { - - /** - * Receive notification that a probed AST node to which the {@link Instrument} is attached is - * about to be executed for the first time. This is a lazy opportunity for the tool to - * optionally add the root of a newly created AST fragment that will be spliced - * directly into the executing AST. The new AST fragment will immediately begin receiving - * {@link InstrumentationNode.TruffleEvents}, beginning with the current execution event. - *

- * AST fragments must be written to Truffle conventions. Some of these conventions are - * especially important if the fragment is to be fully optimized along with it's new parent AST. - *

- * If this method returns {@code null} then it will be called again the next time the probed - * node is about to be executed. - *

- * In some situations, this method will be called more than once for a particular Probe, and a - * new instance must be supplied each time. Each instance will be attached at the equivalent - * location in clones of the AST, and so should be behave as if equivalent for most purposes. - *

- * In some situations the AST fragment supplied by this method maybe cloned for attachment to - * equivalent locations in cloned AST, so care should be taken about any state local to each - * instance of the AST fragment. - * - * @see Instrument - */ - SplicedNode getSpliceNode(Probe probe); - -} diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SplicedNode.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SplicedNode.java Mon May 04 19:12:50 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2015, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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.instrument; - -import com.oracle.truffle.api.frame.*; -import com.oracle.truffle.api.nodes.*; - -/** - * Root of a client-provided AST fragment that can be spliced directly into an executing AST via - * {@link Instrument#create(SpliceInstrumentListener, String)}. - *

- * Note: Instances of this class will in some situations be cloned by the - * instrumentation platform for attachment at equivalent locations in cloned ASTs. - */ -public abstract class SplicedNode extends Node implements InstrumentationNode.TruffleEvents, InstrumentationNode { - - public void enter(Node node, VirtualFrame vFrame) { - } - - public void returnVoid(Node node, VirtualFrame vFrame) { - } - - public void returnValue(Node node, VirtualFrame vFrame, Object result) { - } - - public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) { - } - - public String instrumentationInfo() { - return null; - } - -} diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ToolEvalNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ToolEvalNode.java Mon May 04 19:34:51 2015 +0200 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2015, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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.instrument; + +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; + +/** + * Root of a client-provided AST fragment that can be executed efficiently, subject to full Truffle + * optimization, by a {@linkplain Instrument#create(ToolEvalNodeFactory, String) Tool Eval + * Instrument}. + * + * @see Instrument + * @see ToolEvalNodeFactory + * @see ToolEvalResultListener + */ +public abstract class ToolEvalNode extends Node implements InstrumentationNode { + + /** + * Executes this AST fragment on behalf of a client {@link Instrument}, just before the + * guest-language AST node to which the {@link Probe} holding the Instrument is executed. + * + * @param node the guest-language AST node to which the host Instrument's Probe is attached + * @param vFrame execution frame at the guest-language AST node + * @return the result of this AST fragment's execution + */ + public abstract Object executeToolEvalNode(Node node, VirtualFrame vFrame); + +} diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ToolEvalNodeFactory.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ToolEvalNodeFactory.java Mon May 04 19:34:51 2015 +0200 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2015, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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.instrument; + +import com.oracle.truffle.api.nodes.*; + +/** + * Creator of {@linkplain ToolEvalNode AST fragments} suitable for efficient execution, subject to + * full Truffle optimization, by a {@linkplain Instrument#create(ToolEvalNodeFactory, String) Tool + * Eval Instrument}. + * + * @see Instrument + * @see ToolEvalNode + */ +public interface ToolEvalNodeFactory { + + /** + * Provider of {@linkplain ToolEvalNode AST fragment} instances for efficient execution via + * instrumentation, subject to full Truffle optimization, at a {@linkplain Probe Probed} site in + * a guest-language AST. + * + * @param probe the Probe to which the Instrument requesting the AST fragment is attached + * @param node the guest-language AST location that is the context in which the requesting + * Instrument must execute the AST fragment. + * @return a newly created AST fragment suitable for execution, via instrumentation, in the + * execution context of the specified guest-language AST site. + */ + ToolEvalNode createToolEvalNode(Probe probe, Node node); +} diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ToolEvalResultListener.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ToolEvalResultListener.java Mon May 04 19:34:51 2015 +0200 @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2015, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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.instrument; + +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; + +/** + * Listener for receiving the result a client-provided {@linkplain ToolEvalNode AST fragment}, when + * executed by a {@linkplain Instrument#create(ToolEvalNodeFactory, String) Tool Eval Instrument}. + * + * @see Instrument + * @see ToolEvalNode + * @see ToolEvalNodeFactory + */ +public interface ToolEvalResultListener { + + /** + * Notifies listener that a client-provided {@linkplain ToolEvalNode AST fragment} has been + * executed by a {@linkplain Instrument#create(ToolEvalNodeFactory, String) Tool Eval + * Instrument} with the specified result, possibly {@code null}. + * + * @param node the guest-language AST node to which the host Instrument's {@link Probe} is + * attached + * @param vFrame execution frame at the guest-language AST node + * @param result the result of this AST fragment's execution + */ + void notifyToolEvalResult(Node node, VirtualFrame vFrame, Object result); + + /** + * Notifies listener that execution of client-provided {@linkplain ToolEvalNode AST fragment} + * filed during execution by a @linkplain Instrument#create(ToolEvalNodeFactory, String) Tool + * Eval Instrument}. + * + * @param node the guest-language AST node to which the host Instrument's {@link Probe} is + * attached + * @param vFrame execution frame at the guest-language AST node + * @param ex the exception + */ + void notifyToolEvalFailure(Node node, VirtualFrame vFrame, RuntimeException ex); + +} diff -r 0028ab94d268 -r bfb6e742ad0a 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 Mon May 04 19:12:50 2015 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java Mon May 04 19:34:51 2015 +0200 @@ -299,14 +299,18 @@ private void reportReplace(Node oldNode, Node newNode, CharSequence reason) { Node node = this; while (node != null) { + boolean consumed = false; if (node instanceof ReplaceObserver) { - ((ReplaceObserver) node).nodeReplaced(oldNode, newNode, reason); + consumed = ((ReplaceObserver) node).nodeReplaced(oldNode, newNode, reason); } else if (node instanceof RootNode) { CallTarget target = ((RootNode) node).getCallTarget(); if (target instanceof ReplaceObserver) { - ((ReplaceObserver) target).nodeReplaced(oldNode, newNode, reason); + consumed = ((ReplaceObserver) target).nodeReplaced(oldNode, newNode, reason); } } + if (consumed) { + break; + } node = node.getParent(); } if (TruffleOptions.TraceRewrites) { diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.truffle.interop/src/com/oracle/truffle/interop/ForeignAccessArguments.java --- a/graal/com.oracle.truffle.interop/src/com/oracle/truffle/interop/ForeignAccessArguments.java Mon May 04 19:12:50 2015 +0200 +++ b/graal/com.oracle.truffle.interop/src/com/oracle/truffle/interop/ForeignAccessArguments.java Mon May 04 19:34:51 2015 +0200 @@ -54,6 +54,10 @@ return copyOfRange(arguments, RUNTIME_ARGUMENT_COUNT, arguments.length); } + public static Object[] extractUserArguments(int skip, Object[] arguments) { + return copyOfRange(arguments, RUNTIME_ARGUMENT_COUNT + skip, arguments.length); + } + public static int getUserArgumentCount(Object[] arguments) { return arguments.length - RUNTIME_ARGUMENT_COUNT; } diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.truffle.interop/src/com/oracle/truffle/interop/TruffleGlobalScope.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.interop/src/com/oracle/truffle/interop/TruffleGlobalScope.java Mon May 04 19:34:51 2015 +0200 @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2015, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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.interop; + +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.interop.*; + +public interface TruffleGlobalScope { + void exportTruffleObject(Object identifier, TruffleObject object); + + FrameSlot getFrameSlot(Object identifier); + + TruffleObject getTruffleObject(FrameSlot slot); + + boolean contains(Object identifier); +} diff -r 0028ab94d268 -r bfb6e742ad0a graal/com.oracle.truffle.interop/src/com/oracle/truffle/interop/TruffleLanguage.java --- a/graal/com.oracle.truffle.interop/src/com/oracle/truffle/interop/TruffleLanguage.java Mon May 04 19:12:50 2015 +0200 +++ b/graal/com.oracle.truffle.interop/src/com/oracle/truffle/interop/TruffleLanguage.java Mon May 04 19:34:51 2015 +0200 @@ -29,12 +29,12 @@ public interface TruffleLanguage { String getLanguageName(); - TruffleObject getLanguageContext(); - - void setGlobalContext(TruffleObject context); - - boolean hasGlobalProperty(String name); - Object run(String[] arguments); + void setTruffleGlobalScope(TruffleGlobalScope globalScope); + + TruffleObject getLanguageGlobal(); + + boolean isObjectOfLanguage(TruffleObject object); + } diff -r 0028ab94d268 -r bfb6e742ad0a mx/mx_graal.py --- a/mx/mx_graal.py Mon May 04 19:12:50 2015 +0200 +++ b/mx/mx_graal.py Mon May 04 19:34:51 2015 +0200 @@ -118,7 +118,7 @@ return _vm vm = mx.get_env('DEFAULT_VM') if vm is None: - if not sys.stdout.isatty(): + if not mx.is_interactive(): mx.abort('Need to specify VM with --vm option or DEFAULT_VM environment variable') envPath = join(_graal_home, 'mx', 'env') mx.log('Please select the VM to be executed from the following: ') @@ -392,7 +392,7 @@ if not vm: vm = _get_vm() mx.log('The ' + bld + ' ' + vm + ' VM has not been created') - if sys.stdout.isatty(): + if mx.is_interactive(): if mx.ask_yes_no('Build it now', 'y'): with VM(vm, bld): build([]) @@ -1714,9 +1714,8 @@ with Task('CleanAndBuildIdealGraphVisualizer', tasks) as t: if t and platform.processor() != 'sparc': - env = _igvFallbackJDK(os.environ) buildxml = mx._cygpathU2W(join(_graal_home, 'src', 'share', 'tools', 'IdealGraphVisualizer', 'build.xml')) - mx.run(['ant', '-f', buildxml, '-q', 'clean', 'build'], env=env) + mx.run(['ant', '-f', buildxml, '-q', 'clean', 'build'], env=_igvBuildEnv()) # Prevent Graal modifications from breaking the standard builds if args.buildNonGraal: @@ -1758,6 +1757,11 @@ if args.task_filter: Task.filters = args.task_filter.split(',') + # Force + if not mx._opts.strict_compliance: + mx.log("[gate] foring strict compliance") + mx._opts.strict_compliance = True + tasks = [] total = Task('Gate') try: @@ -1861,38 +1865,34 @@ dacapo(['100', 'eclipse', '-esa']) -def _igvFallbackJDK(env): +def _igvJdk(): v8u20 = mx.VersionSpec("1.8.0_20") v8u40 = mx.VersionSpec("1.8.0_40") v8 = mx.VersionSpec("1.8") - igvHomes = [h for h in mx._java_homes if h.version >= v8 and (h.version < v8u20 or h.version >= v8u40)] - defaultJava = mx.java() - if defaultJava not in igvHomes: - if not igvHomes: - mx.abort("No JDK available for building IGV. Must have JDK >= 1.8 and < 1.8.0u20 or >= 1.8.0u40 in JAVA_HOME or EXTRA_JAVA_HOMES") - env = dict(env) - fallbackJDK = igvHomes[0] - mx.logv("1.8.0_20 has a known javac bug (JDK-8043926), thus falling back to " + str(fallbackJDK.version)) - env['JAVA_HOME'] = str(fallbackJDK.jdk) + def _igvJdkVersionCheck(version): + return version >= v8 and (version < v8u20 or version >= v8u40) + return mx.java_version(_igvJdkVersionCheck, versionDescription='>= 1.8 and < 1.8.0u20 or >= 1.8.0u40').jdk + +def _igvBuildEnv(): + # When the http_proxy environment variable is set, convert it to the proxy settings that ant needs + env = dict(os.environ) + proxy = os.environ.get('http_proxy') + if not (proxy is None) and len(proxy) > 0: + if '://' in proxy: + # Remove the http:// prefix (or any other protocol prefix) + proxy = proxy.split('://', 1)[1] + # Separate proxy server name and port number + proxyName, proxyPort = proxy.split(':', 1) + proxyEnv = '-DproxyHost="' + proxyName + '" -DproxyPort=' + proxyPort + env['ANT_OPTS'] = proxyEnv + + env['JAVA_HOME'] = _igvJdk() return env def igv(args): """run the Ideal Graph Visualizer""" logFile = '.ideal_graph_visualizer.log' with open(join(_graal_home, logFile), 'w') as fp: - # When the http_proxy environment variable is set, convert it to the proxy settings that ant needs - env = dict(os.environ) - proxy = os.environ.get('http_proxy') - if not (proxy is None) and len(proxy) > 0: - if '://' in proxy: - # Remove the http:// prefix (or any other protocol prefix) - proxy = proxy.split('://', 1)[1] - # Separate proxy server name and port number - proxyName, proxyPort = proxy.split(':', 1) - proxyEnv = '-DproxyHost="' + proxyName + '" -DproxyPort=' + proxyPort - env['ANT_OPTS'] = proxyEnv - env = _igvFallbackJDK(env) - mx.logv('[Ideal Graph Visualizer log is in ' + fp.name + ']') nbplatform = join(_graal_home, 'src', 'share', 'tools', 'IdealGraphVisualizer', 'nbplatform') @@ -1914,6 +1914,8 @@ if not exists(nbplatform): mx.logv('[This execution may take a while as the NetBeans platform needs to be downloaded]') + + env = _igvBuildEnv() # make the jar for Batik 1.7 available. env['IGV_BATIK_JAR'] = mx.library('BATIK').get_path(True) if mx.run(['ant', '-f', mx._cygpathU2W(join(_graal_home, 'src', 'share', 'tools', 'IdealGraphVisualizer', 'build.xml')), '-l', mx._cygpathU2W(fp.name), 'run'], env=env, nonZeroIsFatal=False): @@ -2571,7 +2573,7 @@ findbugsResults = join(_graal_home, 'findbugs.results') cmd = ['-jar', mx._cygpathU2W(findbugsJar), '-textui', '-low', '-maxRank', '15'] - if sys.stdout.isatty(): + if mx.is_interactive(): cmd.append('-progress') cmd = cmd + ['-auxclasspath', mx._separatedCygpathU2W(mx.classpath([d.name for d in _jdkDeployedDists] + [p.name for p in nonTestProjects])), '-output', mx._cygpathU2W(findbugsResults), '-exitcode'] + args + outputDirs exitcode = mx.run_java(cmd, nonZeroIsFatal=False, javaConfig=mx.java(javaCompliance)) diff -r 0028ab94d268 -r bfb6e742ad0a mxtool/.pylintrc --- a/mxtool/.pylintrc Mon May 04 19:12:50 2015 +0200 +++ b/mxtool/.pylintrc Mon May 04 19:34:51 2015 +0200 @@ -44,7 +44,8 @@ too-many-lines,missing-docstring,no-init,no-self-use,too-many-statements, too-many-locals,too-few-public-methods,too-many-instance-attributes, too-many-arguments,too-many-branches,too-many-public-methods, - multiple-statements,abstract-method,F0401,no-member,non-parent-init-called + multiple-statements,abstract-method,F0401,no-member,non-parent-init-called, + maybe-no-member # F0401: http://www.logilab.org/ticket/9386 diff -r 0028ab94d268 -r bfb6e742ad0a mxtool/mx.py --- a/mxtool/mx.py Mon May 04 19:12:50 2015 +0200 +++ b/mxtool/mx.py Mon May 04 19:34:51 2015 +0200 @@ -47,6 +47,14 @@ from argparse import ArgumentParser, REMAINDER from os.path import join, basename, dirname, exists, getmtime, isabs, expandvars, isdir, isfile +try: + # needed to work around https://bugs.python.org/issue1927 + import readline + #then make pylint happy.. + readline.get_line_buffer() +except ImportError: + pass + # Support for Python 2.6 def check_output(*popenargs, **kwargs): process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs) @@ -94,7 +102,8 @@ _primary_suite_path = None _primary_suite = None _opts = None -_java_homes = None +_extra_java_homes = [] +_default_java_home = None _warn = False """ @@ -213,10 +222,6 @@ if p.javaCompliance > self.javaCompliance: abort("Compliance level doesn't match: Distribution {0} requires {1}, but {2} is {3}.".format(self.name, self.javaCompliance, p.name, p.javaCompliance)) - # skip a Java project if its Java compliance level is "higher" than the configured JDK - jdk = java(p.javaCompliance) - assert jdk - logv('[' + self.path + ': adding project ' + p.name + ']') outputDir = p.output_dir() for root, _, files in os.walk(outputDir): @@ -306,14 +311,6 @@ self.definedAnnotationProcessors = None self.definedAnnotationProcessorsDist = None - - # Verify that a JDK exists for this project if its compliance level is - # less than the compliance level of the default JDK - jdk = java(self.javaCompliance) - if jdk is None and self.javaCompliance < java().javaCompliance: - abort('Cannot find ' + str(self.javaCompliance) + ' JDK required by ' + name + '. ' + - 'Specify it with --extra-java-homes option or EXTRA_JAVA_HOMES environment variable.') - # Create directories for projects that don't yet exist if not exists(d): os.mkdir(d) @@ -755,6 +752,7 @@ sha1path = path + '.sha1' includedInJDK = getattr(self, 'includedInJDK', None) + # TODO since we don't know which JDK will be used, this check is dubious if includedInJDK and java().javaCompliance >= JavaCompliance(includedInJDK): return None @@ -1230,7 +1228,7 @@ del _libs[d.name] self.libs.remove(d) elif d.isProject(): - if java(d.javaCompliance) is None: + if java(d.javaCompliance, cancel='some projects will be omitted which may result in errrors') is None: logv('[omitting project {0} as Java compliance {1} cannot be satisfied by configured JDKs]'.format(d, d.javaCompliance)) del _projects[d.name] self.projects.remove(d) @@ -1656,68 +1654,6 @@ p.all_deps(deps, includeLibs=includeLibs, includeJreLibs=includeJreLibs, includeAnnotationProcessors=includeAnnotationProcessors) return deps -def _handle_lookup_java_home(jdk): - return _handle_lookup_jdk(jdk, 'JAVA_HOME', '--java-home', False) - -def _handle_lookup_extra_java_homes(jdk): - return _handle_lookup_jdk(jdk, 'EXTRA_JAVA_HOMES', '--extra-java-homes', True) - -def _handle_lookup_jdk(jdk, varName, flagName, allowMultiple): - if jdk != None and jdk != '': - return jdk - jdk = os.environ.get(varName) - if jdk != None and jdk != '': - return jdk - - if not sys.stdout.isatty(): - abort('Could not find bootstrap {0}. Use {1} option or ensure {2} environment variable is set.'.format(varName, flagName, varName)) - - candidateJdks = [] - if get_os() == 'darwin': - base = '/Library/Java/JavaVirtualMachines' - if exists(base): - candidateJdks = [join(base, n, 'Contents/Home') for n in os.listdir(base) if exists(join(base, n, 'Contents/Home'))] - elif get_os() == 'linux': - base = '/usr/lib/jvm' - if exists(base): - candidateJdks = [join(base, n) for n in os.listdir(base) if exists(join(base, n, 'jre/lib/rt.jar'))] - base = '/usr/java' - if exists(base): - candidateJdks += [join(base, n) for n in os.listdir(base) if exists(join(base, n, 'jre/lib/rt.jar'))] - elif get_os() == 'solaris': - base = '/usr/jdk/instances' - if exists(base): - candidateJdks = [join(base, n) for n in os.listdir(base) if exists(join(base, n, 'jre/lib/rt.jar'))] - elif get_os() == 'windows': - base = r'C:\Program Files\Java' - if exists(base): - candidateJdks = [join(base, n) for n in os.listdir(base) if exists(join(base, n, r'jre\lib\rt.jar'))] - - javaHome = None - if len(candidateJdks) != 0: - log('Missing value for {0}.'.format(varName)) - javaHome = select_items(candidateJdks + [''], allowMultiple=allowMultiple) - if javaHome == '': - javaHome = None - if javaHome != None and allowMultiple: - javaHome = os.pathsep.join(javaHome) - - while javaHome is None: - javaHome = raw_input('Enter path of JDK for {0}: '.format(varName)) - rtJarPath = join(javaHome, 'jre', 'lib', 'rt.jar') - if not exists(rtJarPath): - log('Does not appear to be a valid JDK as ' + rtJarPath + ' does not exist') - javaHome = None - else: - break - - envPath = join(_primary_suite.mxDir, 'env') - if ask_yes_no('Persist this setting by adding "{0}={1}" to {2}'.format(varName, javaHome, envPath), 'y'): - with open(envPath, 'a') as fp: - print >> fp, varName + '=' + javaHome - - return javaHome - class ArgParser(ArgumentParser): # Override parent to append the list of available commands def format_help(self): @@ -1744,6 +1680,7 @@ self.add_argument('--user-home', help='users home directory', metavar='', default=os.path.expanduser('~')) self.add_argument('--java-home', help='primary JDK directory (must be JDK 7 or later)', metavar='') self.add_argument('--extra-java-homes', help='secondary JDK directories separated by "' + os.pathsep + '"', metavar='') + self.add_argument('--strict-compliance', action='store_true', dest='strict_compliance', help='Projects of a certain compliance will only be built with a JDK of this exact compliance', default=False) self.add_argument('--ignore-project', action='append', dest='ignored_projects', help='name of project to ignore', metavar='', default=[]) self.add_argument('--kill-with-sigquit', action='store_true', dest='killwithsigquit', help='send sigquit first before killing child processes') if get_os() != 'windows': @@ -1769,15 +1706,16 @@ if opts.very_verbose: opts.verbose = True - opts.java_home = _handle_lookup_java_home(opts.java_home) - opts.extra_java_homes = _handle_lookup_extra_java_homes(opts.extra_java_homes) - if opts.user_home is None or opts.user_home == '': abort('Could not find user home. Use --user-home option or ensure HOME environment variable is set.') - os.environ['JAVA_HOME'] = opts.java_home + if opts.java_home: + os.environ['JAVA_HOME'] = opts.java_home os.environ['HOME'] = opts.user_home + if os.environ.get('STRICT_COMPLIANCE'): + _opts.strict_compliance = True + opts.ignored_projects = opts.ignored_projects + os.environ.get('IGNORED_PROJECTS', '').split(',') commandAndArgs = opts.__dict__.pop('commandAndArgs') @@ -1796,19 +1734,231 @@ msg += ' {0:<20} {1}\n'.format(cmd, doc.split('\n', 1)[0]) return msg + '\n' -def java(requiredCompliance=None): +_canceled_java_requests = set() + +def java(requiredCompliance=None, purpose=None, cancel=None): """ Get a JavaConfig object containing Java commands launch details. If requiredCompliance is None, the compliance level specified by --java-home/JAVA_HOME is returned. Otherwise, the JavaConfig exactly matching requiredCompliance is returned or None if there is no exact match. """ - assert _java_homes + + global _default_java_home + if cancel and (requiredCompliance, purpose) in _canceled_java_requests: + return None + if not requiredCompliance: - return _java_homes[0] - for java in _java_homes: - if java.javaCompliance == requiredCompliance: + if not _default_java_home: + _default_java_home = _find_jdk(purpose=purpose, cancel=cancel) + if not _default_java_home: + assert cancel + _canceled_java_requests.add((requiredCompliance, purpose)) + return _default_java_home + + if _opts.strict_compliance: + complianceCheck = requiredCompliance.exactMatch + desc = str(requiredCompliance) + else: + compVersion = VersionSpec(str(requiredCompliance)) + complianceCheck = lambda version: version >= compVersion + desc = '>=' + str(requiredCompliance) + + for java in _extra_java_homes: + if complianceCheck(java.version): + return java + + jdk = _find_jdk(versionCheck=complianceCheck, versionDescription=desc, purpose=purpose, cancel=cancel) + if jdk: + assert jdk not in _extra_java_homes + _extra_java_homes.append(jdk) + else: + assert cancel + _canceled_java_requests.add((requiredCompliance, purpose)) + return jdk + +def java_version(versionCheck, versionDescription=None, purpose=None): + if _default_java_home and versionCheck(_default_java_home.version): + return _default_java_home + for java in _extra_java_homes: + if versionCheck(java.version): return java + jdk = _find_jdk(versionCheck, versionDescription, purpose) + assert jdk not in _extra_java_homes + _extra_java_homes.append(jdk) + return jdk + +def _find_jdk(versionCheck=None, versionDescription=None, purpose=None, cancel=None): + if not versionCheck: + versionCheck = lambda v: True + assert not versionDescription or versionCheck + if not versionCheck and not purpose: + isDefaultJdk = True + else: + isDefaultJdk = False + + candidateJdks = [] + source = '' + if _opts.java_home: + candidateJdks.append(_opts.java_home) + source = '--java-home' + elif os.environ.get('JAVA_HOME'): + candidateJdks.append(os.environ.get('JAVA_HOME')) + source = 'JAVA_HOME' + + result = _find_jdk_in_candidates(candidateJdks, versionCheck, warn=True, source=source) + if result: + return result + + candidateJdks = [] + + if _opts.extra_java_homes: + candidateJdks += _opts.extra_java_homes.split(os.pathsep) + source = '--extra-java-homes' + elif os.environ.get('EXTRA_JAVA_HOMES'): + candidateJdks += os.environ.get('EXTRA_JAVA_HOMES').split(os.pathsep) + source = 'EXTRA_JAVA_HOMES' + + result = _find_jdk_in_candidates(candidateJdks, versionCheck, warn=True, source=source) + if not result: + candidateJdks = [] + source = '' + + if get_os() == 'darwin': + base = '/Library/Java/JavaVirtualMachines' + if exists(base): + candidateJdks = [join(base, n, 'Contents/Home') for n in os.listdir(base)] + elif get_os() == 'linux': + base = '/usr/lib/jvm' + if exists(base): + candidateJdks = [join(base, n) for n in os.listdir(base)] + base = '/usr/java' + if exists(base): + candidateJdks += [join(base, n) for n in os.listdir(base)] + elif get_os() == 'solaris': + base = '/usr/jdk/instances' + if exists(base): + candidateJdks = [join(base, n) for n in os.listdir(base)] + elif get_os() == 'windows': + base = r'C:\Program Files\Java' + if exists(base): + candidateJdks = [join(base, n) for n in os.listdir(base)] + + configs = _filtered_jdk_configs(candidateJdks, versionCheck) + else: + if not isDefaultJdk: + return result + configs = [result] + + if len(configs) > 1: + if not is_interactive(): + msg = "Multiple possible choices for a JDK" + if purpose: + msg += ' for' + purpose + msg += ': ' + if versionDescription: + msg += '(' + versionDescription + ')' + selected = configs[0] + msg += ". Selecting " + str(selected) + log(msg) + else: + msg = 'Please select a ' + if isDefaultJdk: + msg += 'default ' + msg += 'JDK' + if purpose: + msg += ' for' + purpose + msg += ': ' + if versionDescription: + msg += '(' + versionDescription + ')' + log(msg) + choices = configs + [''] + if cancel: + choices.append('Cancel (' + cancel + ')') + selected = select_items(choices, allowMultiple=False) + if isinstance(selected, types.StringTypes) and selected == '': + selected = None + if isinstance(selected, types.StringTypes) and selected == 'Cancel (' + cancel + ')': + return None + elif len(configs) == 1: + selected = configs[0] + msg = 'Selected ' + str(selected) + ' as ' + if isDefaultJdk: + msg += 'default' + msg += 'JDK' + if versionDescription: + msg = msg + ' ' + versionDescription + if purpose: + msg += ' for' + purpose + log(msg) + else: + msg = 'Could not find any JDK' + if purpose: + msg += ' for' + purpose + msg += ' ' + if versionDescription: + msg = msg + '(' + versionDescription + ')' + log(msg) + selected = None + + while not selected: + jdkLocation = raw_input('Enter path of JDK: ') + selected = _find_jdk_in_candidates([jdkLocation], versionCheck, warn=True) + + varName = 'JAVA_HOME' if isDefaultJdk else 'EXTRA_JAVA_HOMES' + allowMultiple = not isDefaultJdk + envPath = join(_primary_suite.mxDir, 'env') + if is_interactive() and ask_yes_no('Persist this setting by adding "{0}={1}" to {2}'.format(varName, selected.jdk, envPath), 'y'): + envLines = [] + with open(envPath) as fp: + append = True + for line in fp: + if line.rstrip().startswith(varName): + _, currentValue = line.split('=', 1) + currentValue = currentValue.strip() + if not allowMultiple and currentValue: + if not ask_yes_no('{0} is already set to {1}, overwrite with {2}?'.format(varName, currentValue, selected.jdk), 'n'): + return selected + else: + line = varName + '=' + selected.jdk + os.linesep + else: + line = line.rstrip() + if currentValue: + line += os.pathsep + line += selected.jdk + os.linesep + append = False + envLines.append(line) + if append: + envLines.append(varName + '=' + selected.jdk) + + with open(envPath, 'w') as fp: + for line in envLines: + fp.write(line) + + if varName == 'JAVA_HOME': + os.environ['JAVA_HOME'] = selected.jdk + + return selected + +def is_interactive(): + return sys.__stdin__.isatty() + +def _filtered_jdk_configs(candidates, versionCheck, warn=False, source=None): + filtered = [] + for candidate in candidates: + try: + config = JavaConfig(candidate) + if versionCheck(config.version): + filtered.append(config) + except JavaConfigException as e: + if warn: + log('Path in ' + source + "' is not pointing to a JDK (" + e.message + ")") + return filtered + +def _find_jdk_in_candidates(candidates, versionCheck, warn=False, source=None): + filtered = _filtered_jdk_configs(candidates, versionCheck, warn, source) + if filtered: + return filtered[0] return None @@ -2079,6 +2229,10 @@ def __hash__(self): return self.value.__hash__() + def exactMatch(self, version): + assert isinstance(version, VersionSpec) + return len(version.parts) > 1 and version.parts[0] == 1 and version.parts[1] == self.value + """ A version specification as defined in JSR-56 """ @@ -2102,13 +2256,16 @@ return os.pathsep.join([path for path in _separatedCygpathW2U(paths).split(os.pathsep) if exists(path)]) return None +class JavaConfigException(Exception): + def __init__(self, value): + Exception.__init__(self, value) + """ A JavaConfig object encapsulates info on how Java commands are run. """ class JavaConfig: - def __init__(self, java_home, java_dbg_port): + def __init__(self, java_home): self.jdk = java_home - self.debug_port = java_dbg_port self.jar = exe_suffix(join(self.jdk, 'bin', 'jar')) self.java = exe_suffix(join(self.jdk, 'bin', 'java')) self.javac = exe_suffix(join(self.jdk, 'bin', 'javac')) @@ -2122,7 +2279,7 @@ self._endorseddirs = None if not exists(self.java): - abort('Java launcher does not exist: ' + self.java) + raise JavaConfigException('Java launcher does not exist: ' + self.java) def delAtAndSplit(s): return shlex.split(s.lstrip('@')) @@ -2139,8 +2296,7 @@ try: output = subprocess.check_output([self.java, '-version'], stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: - print e.output - abort(e.returncode) + raise JavaConfigException(e.returncode + " :" + e.output) def _checkOutput(out): return 'version' in out @@ -2156,8 +2312,8 @@ self.version = VersionSpec(version.split()[2].strip('"')) self.javaCompliance = JavaCompliance(self.version.versionString) - if self.debug_port is not None: - self.java_args += ['-Xdebug', '-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=' + str(self.debug_port)] + if _opts.java_dbg_port is not None: + self.java_args += ['-Xdebug', '-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=' + str(_opts.java_dbg_port)] def _init_classpaths(self): if not self._classpaths_initialized: @@ -2180,7 +2336,7 @@ self._classpaths_initialized = True def __repr__(self): - return "JavaConfig(" + str(self.jdk) + ", " + str(self.debug_port) + ")" + return "JavaConfig(" + str(self.jdk) + ")" def __str__(self): return "Java " + str(self.version) + " (" + str(self.javaCompliance) + ") from " + str(self.jdk) @@ -2475,8 +2631,8 @@ self.logCompilation('javac' if not args.alt_javac else args.alt_javac) javacCmd = [javac, '-g', '-J-Xmx1g', '-source', compliance, '-target', compliance, '-classpath', cp, '-d', outputDir] jdk.javacLibOptions(javacCmd) - if jdk.debug_port is not None: - javacCmd += ['-J-Xdebug', '-J-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=' + str(jdk.debug_port)] + if _opts.java_dbg_port is not None: + javacCmd += ['-J-Xdebug', '-J-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=' + str(_opts.java_dbg_port)] javacCmd += processorArgs javacCmd += ['@' + _cygpathU2W(argfile.name)] @@ -2636,7 +2792,6 @@ # skip building this Java project if its Java compliance level is "higher" than the configured JDK requiredCompliance = p.javaCompliance if p.javaCompliance else JavaCompliance(args.compliance) if args.compliance else None jdk = java(requiredCompliance) - assert jdk outputDir = p.output_dir() @@ -2940,16 +3095,33 @@ def __init__(self, settingsDir, javaCompliance): self.path = join(settingsDir, 'org.eclipse.jdt.core.prefs') self.javaCompliance = javaCompliance - self.javafiles = list() with open(join(settingsDir, 'org.eclipse.jdt.ui.prefs')) as fp: jdtUiPrefs = fp.read() self.removeTrailingWhitespace = 'sp_cleanup.remove_trailing_whitespaces_all=true' in jdtUiPrefs if self.removeTrailingWhitespace: assert 'sp_cleanup.remove_trailing_whitespaces=true' in jdtUiPrefs and 'sp_cleanup.remove_trailing_whitespaces_ignore_empty=false' in jdtUiPrefs - - def settings(self): + self.cachedHash = None + + def __hash__(self): + if not self.cachedHash: + with open(self.path) as fp: + self.cachedHash = (fp.read(), self.javaCompliance, self.removeTrailingWhitespace).__hash__() + return self.cachedHash + + def __eq__(self, other): + if not isinstance(other, Batch): + return False + if self.removeTrailingWhitespace != other.removeTrailingWhitespace: + return False + if self.javaCompliance != other.javaCompliance: + return False + if self.path == other.path: + return True with open(self.path) as fp: - return fp.read() + java(self.javaCompliance).java + str(self.removeTrailingWhitespace) + with open(other.path) as ofp: + if fp.read() != ofp.read(): + return False + return True class FileInfo: def __init__(self, path): @@ -2992,22 +3164,22 @@ if _opts.verbose: log('[no Eclipse Code Formatter preferences at {0} - skipping]'.format(batch.path)) continue - + javafiles = [] for sourceDir in sourceDirs: for root, _, files in os.walk(sourceDir): for f in [join(root, name) for name in files if name.endswith('.java')]: - batch.javafiles.append(FileInfo(f)) - if len(batch.javafiles) == 0: + javafiles.append(FileInfo(f)) + if len(javafiles) == 0: logv('[no Java sources in {0} - skipping]'.format(p.name)) continue - res = batches.setdefault(batch.settings(), batch) - if res is not batch: - res.javafiles = res.javafiles + batch.javafiles + res = batches.setdefault(batch, javafiles) + if res is not javafiles: + res.extend(javafiles) log("we have: " + str(len(batches)) + " batches") - for batch in batches.itervalues(): - for chunk in _chunk_files_for_command_line(batch.javafiles, pathFunction=lambda f: f.path): + for batch, javafiles in batches.iteritems(): + for chunk in _chunk_files_for_command_line(javafiles, pathFunction=lambda f: f.path): run([args.eclipse_exe, '-nosplash', '-application', @@ -4721,7 +4893,7 @@ def fsckprojects(args): """find directories corresponding to deleted Java projects and delete them""" - if not sys.stdout.isatty(): + if not is_interactive(): log('fsckprojects command must be run in an interactive shell') return hg = HgConfig() @@ -4746,7 +4918,7 @@ indicatorsInHg = hg.locate(suite.dir, indicators) # Only proceed if there are indicator files that are not under HG if len(indicators) > len(indicatorsInHg): - if not sys.stdout.isatty() or ask_yes_no(dirpath + ' looks like a removed project -- delete it', 'n'): + if not is_interactive() or ask_yes_no(dirpath + ' looks like a removed project -- delete it', 'n'): shutil.rmtree(dirpath) log('Deleted ' + dirpath) @@ -4865,6 +5037,7 @@ # Once https://bugs.openjdk.java.net/browse/JDK-8041628 is fixed, # this should be reverted to: # javadocExe = java().javadoc + # we can then also respect _opts.relatex_compliance javadocExe = projectJava.javadoc run([javadocExe, memory, @@ -5085,15 +5258,17 @@ if len(items) <= 1: return items else: + assert is_interactive() + numlen = str(len(str(len(items)))) if allowMultiple: - log('[0] ') + log(('[{0:>' + numlen + '}] ').format(0)) for i in range(0, len(items)): if descriptions is None: - log('[{0}] {1}'.format(i + 1, items[i])) + log(('[{0:>' + numlen + '}] {1}').format(i + 1, items[i])) else: assert len(items) == len(descriptions) wrapper = textwrap.TextWrapper(subsequent_indent=' ') - log('\n'.join(wrapper.wrap('[{0}] {1} - {2}'.format(i + 1, items[i], descriptions[i])))) + log('\n'.join(wrapper.wrap(('[{0:>' + numlen + '}] {1} - {2}').format(i + 1, items[i], descriptions[i])))) while True: if allowMultiple: s = raw_input('Enter number(s) of selection (separate multiple choices with spaces): ').split() @@ -5265,7 +5440,7 @@ def ask_yes_no(question, default=None): """""" assert not default or default == 'y' or default == 'n' - if not sys.stdout.isatty(): + if not is_interactive(): if default: return default else: @@ -5395,16 +5570,6 @@ opts, commandAndArgs = _argParser._parse_cmd_line() assert _opts == opts - global _java_homes - defaultJdk = JavaConfig(opts.java_home, opts.java_dbg_port) - _java_homes = [defaultJdk] - if opts.extra_java_homes: - for java_home in opts.extra_java_homes.split(os.pathsep): - extraJdk = JavaConfig(java_home, opts.java_dbg_port) - if extraJdk.javaCompliance > defaultJdk.javaCompliance: - abort('Secondary JDK ' + extraJdk.jdk + ' has higher compliance level than default JDK ' + defaultJdk.jdk) - _java_homes.append(extraJdk) - for s in suites(): s._post_init(opts) diff -r 0028ab94d268 -r bfb6e742ad0a src/share/tools/IdealGraphVisualizer/Coordinator/src/com/sun/hotspot/igv/coordinator/OutlineTopComponent.java --- a/src/share/tools/IdealGraphVisualizer/Coordinator/src/com/sun/hotspot/igv/coordinator/OutlineTopComponent.java Mon May 04 19:12:50 2015 +0200 +++ b/src/share/tools/IdealGraphVisualizer/Coordinator/src/com/sun/hotspot/igv/coordinator/OutlineTopComponent.java Mon May 04 19:34:51 2015 +0200 @@ -124,6 +124,8 @@ public void clear() { document.clear(); + root = new FolderNode(document); + manager.setRootContext(root); } @Override diff -r 0028ab94d268 -r bfb6e742ad0a src/share/tools/IdealGraphVisualizer/Coordinator/src/com/sun/hotspot/igv/coordinator/actions/ImportAction.java --- a/src/share/tools/IdealGraphVisualizer/Coordinator/src/com/sun/hotspot/igv/coordinator/actions/ImportAction.java Mon May 04 19:12:50 2015 +0200 +++ b/src/share/tools/IdealGraphVisualizer/Coordinator/src/com/sun/hotspot/igv/coordinator/actions/ImportAction.java Mon May 04 19:34:51 2015 +0200 @@ -31,31 +31,47 @@ import com.sun.hotspot.igv.data.serialization.ParseMonitor; import com.sun.hotspot.igv.data.serialization.Parser; import com.sun.hotspot.igv.settings.Settings; -import java.awt.event.InputEvent; -import java.awt.event.KeyEvent; +import java.awt.event.ActionEvent; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.nio.channels.FileChannel; import java.nio.file.StandardOpenOption; -import javax.swing.Action; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.swing.JFileChooser; -import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.filechooser.FileFilter; import org.netbeans.api.progress.ProgressHandle; import org.netbeans.api.progress.ProgressHandleFactory; import org.openide.util.Exceptions; +import org.openide.util.RequestProcessor; +import org.openide.awt.ActionID; +import org.openide.awt.ActionReference; +import org.openide.awt.ActionReferences; +import org.openide.awt.ActionRegistration; import org.openide.util.HelpCtx; import org.openide.util.NbBundle; -import org.openide.util.RequestProcessor; -import org.openide.util.actions.CallableSystemAction; +import org.openide.util.actions.SystemAction; /** * * @author Thomas Wuerthinger */ -public final class ImportAction extends CallableSystemAction { + +@ActionID( + category = "File", + id = "com.sun.hotspot.igv.coordinator.actions.ImportAction" +) +@ActionRegistration( + iconBase = "com/sun/hotspot/igv/coordinator/images/import.png", + displayName = "#CTL_ImportAction" +) +@ActionReferences({ + @ActionReference(path = "Menu/File", position = 0), + @ActionReference(path = "Shortcuts", name = "C-O") +}) +public final class ImportAction extends SystemAction { private static final int WORKUNITS = 10000; public static FileFilter getFileFilter() { @@ -74,14 +90,13 @@ } @Override - public void performAction() { - + public void actionPerformed(ActionEvent e) { JFileChooser fc = new JFileChooser(); fc.setFileFilter(ImportAction.getFileFilter()); fc.setCurrentDirectory(new File(Settings.get().get(Settings.DIRECTORY, Settings.DIRECTORY_DEFAULT))); if (fc.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) { - File file = fc.getSelectedFile(); + final File file = fc.getSelectedFile(); File dir = file; if (!dir.isDirectory()) { @@ -93,6 +108,7 @@ final FileChannel channel = FileChannel.open(file.toPath(), StandardOpenOption.READ); final ProgressHandle handle = ProgressHandleFactory.createHandle("Opening file " + file.getName()); handle.start(WORKUNITS); + final long startTime = System.currentTimeMillis(); final long start = channel.size(); ParseMonitor monitor = new ParseMonitor() { @Override @@ -136,6 +152,8 @@ Exceptions.printStackTrace(ex); } handle.finish(); + long stop = System.currentTimeMillis(); + Logger.getLogger(getClass().getName()).log(Level.INFO, "Loaded in " + file + " in " + ((stop - startTime) / 1000.0) + " seconds"); } }); } catch (FileNotFoundException ex) { @@ -151,23 +169,13 @@ return NbBundle.getMessage(ImportAction.class, "CTL_ImportAction"); } - public ImportAction() { - putValue(Action.SHORT_DESCRIPTION, "Open XML graph document..."); - putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_O, InputEvent.CTRL_MASK)); - } - @Override protected String iconResource() { return "com/sun/hotspot/igv/coordinator/images/import.png"; } - + @Override public HelpCtx getHelpCtx() { return HelpCtx.DEFAULT_HELP; } - - @Override - protected boolean asynchronous() { - return false; - } } diff -r 0028ab94d268 -r bfb6e742ad0a src/share/tools/IdealGraphVisualizer/Coordinator/src/com/sun/hotspot/igv/coordinator/actions/SaveAllAction.java --- a/src/share/tools/IdealGraphVisualizer/Coordinator/src/com/sun/hotspot/igv/coordinator/actions/SaveAllAction.java Mon May 04 19:12:50 2015 +0200 +++ b/src/share/tools/IdealGraphVisualizer/Coordinator/src/com/sun/hotspot/igv/coordinator/actions/SaveAllAction.java Mon May 04 19:34:51 2015 +0200 @@ -29,14 +29,30 @@ import java.awt.event.KeyEvent; import javax.swing.Action; import javax.swing.KeyStroke; +import org.openide.awt.ActionID; +import org.openide.awt.ActionReference; +import org.openide.awt.ActionReferences; +import org.openide.awt.ActionRegistration; import org.openide.util.HelpCtx; import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; import org.openide.util.actions.CallableSystemAction; /** * * @author Thomas Wuerthinger */ +@ActionID( + category = "File", + id = "com.sun.hotspot.igv.coordinator.actions.SaveAllAction" +) +@ActionRegistration( + displayName = "#CTL_SaveAllAction" +) +@ActionReferences({ + @ActionReference(path = "Menu/File", position = 0), + @ActionReference(path = "Shortcuts", name = "C-S") +}) public final class SaveAllAction extends CallableSystemAction { @Override diff -r 0028ab94d268 -r bfb6e742ad0a src/share/tools/IdealGraphVisualizer/Coordinator/src/com/sun/hotspot/igv/coordinator/layer.xml --- a/src/share/tools/IdealGraphVisualizer/Coordinator/src/com/sun/hotspot/igv/coordinator/layer.xml Mon May 04 19:12:50 2015 +0200 +++ b/src/share/tools/IdealGraphVisualizer/Coordinator/src/com/sun/hotspot/igv/coordinator/layer.xml Mon May 04 19:34:51 2015 +0200 @@ -18,6 +18,8 @@ + + @@ -43,11 +45,11 @@ - + - + diff -r 0028ab94d268 -r bfb6e742ad0a src/share/tools/IdealGraphVisualizer/Data/src/com/sun/hotspot/igv/data/GraphDocument.java --- a/src/share/tools/IdealGraphVisualizer/Data/src/com/sun/hotspot/igv/data/GraphDocument.java Mon May 04 19:12:50 2015 +0200 +++ b/src/share/tools/IdealGraphVisualizer/Data/src/com/sun/hotspot/igv/data/GraphDocument.java Mon May 04 19:34:51 2015 +0200 @@ -51,11 +51,13 @@ } public void addGraphDocument(GraphDocument document) { - for (FolderElement e : document.elements) { - e.setParent(this); - this.addElement(e); + if (document != this) { + for (FolderElement e : document.elements) { + e.setParent(this); + this.addElement(e); + } + document.clear(); } - document.clear(); getChangedEvent().fire(); } diff -r 0028ab94d268 -r bfb6e742ad0a src/share/tools/IdealGraphVisualizer/Data/src/com/sun/hotspot/igv/data/InputBlock.java --- a/src/share/tools/IdealGraphVisualizer/Data/src/com/sun/hotspot/igv/data/InputBlock.java Mon May 04 19:12:50 2015 +0200 +++ b/src/share/tools/IdealGraphVisualizer/Data/src/com/sun/hotspot/igv/data/InputBlock.java Mon May 04 19:34:51 2015 +0200 @@ -90,7 +90,9 @@ public void addNode(int id) { InputNode node = graph.getNode(id); assert node != null; - assert !nodes.contains(node) : "duplicate : " + node; + // nodes.contains(node) is too expensive for large graphs so + // just make sure the Graph doesn't know it yet. + assert graph.getBlock(id) == null : "duplicate : " + node; graph.setBlock(node, this); nodes.add(node); } diff -r 0028ab94d268 -r bfb6e742ad0a src/share/tools/IdealGraphVisualizer/Data/src/com/sun/hotspot/igv/data/Properties.java --- a/src/share/tools/IdealGraphVisualizer/Data/src/com/sun/hotspot/igv/data/Properties.java Mon May 04 19:12:50 2015 +0200 +++ b/src/share/tools/IdealGraphVisualizer/Data/src/com/sun/hotspot/igv/data/Properties.java Mon May 04 19:34:51 2015 +0200 @@ -24,7 +24,9 @@ package com.sun.hotspot.igv.data; import java.io.Serializable; +import java.lang.ref.WeakReference; import java.util.*; +import java.util.Map.Entry; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; @@ -36,7 +38,7 @@ public class Properties implements Serializable, Iterable { public static final long serialVersionUID = 1L; - private String[] map = new String[4]; + protected String[] map = new String[4]; public Properties() { } @@ -101,6 +103,59 @@ map = new String[p.map.length]; System.arraycopy(p.map, 0, map, 0, p.map.length); } + + protected Properties(String[] map) { + this.map = map; + } + + static class SharedProperties extends Properties { + int hashCode; + + SharedProperties(String[] map) { + super(map); + this.hashCode = Arrays.hashCode(map); + } + + @Override + protected void setPropertyInternal(String name, String value) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (!(other instanceof SharedProperties)) { + return super.equals(other); + } + SharedProperties props2 = (SharedProperties) other; + return Arrays.equals(map, props2.map); + } + + @Override + public int hashCode() { + return hashCode; + } + } + + private static class PropertyCache { + static WeakHashMap> immutableCache = new WeakHashMap<>(); + + static synchronized SharedProperties intern(Properties properties) { + String[] map = properties.map; + SharedProperties key = new SharedProperties(map); + WeakReference entry = immutableCache.get(key); + if (entry != null) { + SharedProperties props = entry.get(); + if (props != null) { + return props; + } + } + immutableCache.put(key, new WeakReference<>(key)); + return key; + } + } public static class Entity implements Provider { @@ -118,6 +173,10 @@ public Properties getProperties() { return properties; } + + public void internProperties() { + properties = PropertyCache.intern(properties); + } } public interface PropertyMatcher { @@ -322,8 +381,8 @@ public void setProperty(String name, String value) { setPropertyInternal(name.intern(), value != null ? value.intern() : null); } - private void setPropertyInternal(String name, String value) { + protected void setPropertyInternal(String name, String value) { for (int i = 0; i < map.length; i += 2) { if (map[i] != null && map[i].equals(name)) { String p = map[i + 1]; diff -r 0028ab94d268 -r bfb6e742ad0a src/share/tools/IdealGraphVisualizer/Data/src/com/sun/hotspot/igv/data/serialization/BinaryParser.java --- a/src/share/tools/IdealGraphVisualizer/Data/src/com/sun/hotspot/igv/data/serialization/BinaryParser.java Mon May 04 19:12:50 2015 +0200 +++ b/src/share/tools/IdealGraphVisualizer/Data/src/com/sun/hotspot/igv/data/serialization/BinaryParser.java Mon May 04 19:34:51 2015 +0200 @@ -275,28 +275,33 @@ hashStack = new LinkedList<>(); this.monitor = monitor; try { - this.digest = MessageDigest.getInstance("SHA-256"); + this.digest = MessageDigest.getInstance("SHA-1"); } catch (NoSuchAlgorithmException e) { } } private void fill() throws IOException { + // All the data between lastPosition and position has been + // used so add it to the digest. + int position = buffer.position(); + buffer.position(lastPosition); + byte[] remaining = new byte[position - buffer.position()]; + buffer.get(remaining); + digest.update(remaining); + assert position == buffer.position(); + buffer.compact(); if (channel.read(buffer) < 0) { throw new EOFException(); } buffer.flip(); + lastPosition = buffer.position(); } private void ensureAvailable(int i) throws IOException { while (buffer.remaining() < i) { fill(); } - buffer.mark(); - byte[] result = new byte[i]; - buffer.get(result); - digest.update(result); - buffer.reset(); } private int readByte() throws IOException { @@ -650,6 +655,8 @@ } return group; } + + int lastPosition = 0; private InputGraph parseGraph() throws IOException { if (monitor != null) { @@ -657,7 +664,17 @@ } String title = readPoolObject(String.class); digest.reset(); + lastPosition = buffer.position(); InputGraph graph = parseGraph(title); + + int position = buffer.position(); + buffer.position(lastPosition); + byte[] remaining = new byte[position - buffer.position()]; + buffer.get(remaining); + digest.update(remaining); + assert position == buffer.position(); + lastPosition = buffer.position(); + byte[] d = digest.digest(); byte[] hash = hashStack.peek(); if (hash != null && Arrays.equals(hash, d)) { @@ -674,6 +691,9 @@ parseNodes(graph); parseBlocks(graph); graph.ensureNodesInBlocks(); + for (InputNode node : graph.getNodes()) { + node.internProperties(); + } return graph; } @@ -822,9 +842,10 @@ } } + static final Pattern templatePattern = Pattern.compile("\\{(p|i)#([a-zA-Z0-9$_]+)(/(l|m|s))?\\}"); + private String createName(List edges, Map properties, String template) { - Pattern p = Pattern.compile("\\{(p|i)#([a-zA-Z0-9$_]+)(/(l|m|s))?\\}"); - Matcher m = p.matcher(template); + Matcher m = templatePattern.matcher(template); StringBuffer sb = new StringBuffer(); while (m.find()) { String name = m.group(2); diff -r 0028ab94d268 -r bfb6e742ad0a src/share/tools/IdealGraphVisualizer/nbproject/project.properties --- a/src/share/tools/IdealGraphVisualizer/nbproject/project.properties Mon May 04 19:12:50 2015 +0200 +++ b/src/share/tools/IdealGraphVisualizer/nbproject/project.properties Mon May 04 19:34:51 2015 +0200 @@ -48,5 +48,5 @@ # Disable assertions for RequestProcessor to prevent annoying messages in case # of multiple SceneAnimator update tasks in the default RequestProcessor. -run.args.extra = -J-server -J-da:org.openide.util.RequestProcessor -J-Xms2g -J-Xmx8g -J-Djava.lang.Integer.IntegerCache.high=20000 +run.args.extra = -J-server -J-da:org.openide.util.RequestProcessor -J-Xms2g -J-Xmx8g -J-Djava.lang.Integer.IntegerCache.high=200000 debug.args.extra = -J-server -J-da:org.openide.util.RequestProcessor diff -r 0028ab94d268 -r bfb6e742ad0a src/share/vm/code/nmethod.cpp --- a/src/share/vm/code/nmethod.cpp Mon May 04 19:12:50 2015 +0200 +++ b/src/share/vm/code/nmethod.cpp Mon May 04 19:34:51 2015 +0200 @@ -3429,6 +3429,7 @@ } } } + st->print(" {reexecute=%d rethrow=%d return_oop=%d}", sd->should_reexecute(), sd->rethrow_exception(), sd->return_oop()); } // Print all scopes diff -r 0028ab94d268 -r bfb6e742ad0a src/share/vm/graal/graalRuntime.cpp --- a/src/share/vm/graal/graalRuntime.cpp Mon May 04 19:12:50 2015 +0200 +++ b/src/share/vm/graal/graalRuntime.cpp Mon May 04 19:34:51 2015 +0200 @@ -600,7 +600,8 @@ JRT_END JRT_ENTRY(jboolean, GraalRuntime::thread_is_interrupted(JavaThread* thread, oopDesc* receiver, jboolean clear_interrupted)) - // Ensure that the C++ Thread and OSThread structures aren't freed before we operate + // Ensure that the C++ Thread and OSThread structures aren't freed before we operate. + // This locking requires thread_in_vm which is why this method cannot be JRT_LEAF. Handle receiverHandle(thread, receiver); MutexLockerEx ml(thread->threadObj() == (void*)receiver ? NULL : Threads_lock); JavaThread* receiverThread = java_lang_Thread::thread(receiverHandle());