Mercurial > hg > graal-compiler
changeset 23146:5eb7281c661c
merge
author | Christian Wimmer <christian.wimmer@oracle.com> |
---|---|
date | Tue, 08 Dec 2015 12:30:15 -0800 |
parents | 49619bdbd262 (current diff) 043cb31c85cd (diff) |
children | 2d578cb15516 |
files | graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/ForeignCallPlugin.java graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/GraphBuilderConfiguration.java graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/GraphBuilderContext.java graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/GraphBuilderPlugin.java graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InlineInvokePlugin.java graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/IntrinsicContext.java graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InvocationPlugin.java graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InvocationPlugins.java graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/LoopExplosionPlugin.java graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/MethodSubstitutionPlugin.java graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/NodePlugin.java graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/ParameterPlugin.java graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/JVMCIErrorTest.java graal/com.oracle.graal.java/src/com/oracle/graal/java/BytecodeParser.java graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/FixedInterval.java graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/FixedRange.java graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/IntervalHint.java graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/RegisterVerifier.java graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/TraceInterval.java graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/TraceIntervalDumper.java graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/TraceIntervalWalker.java graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/TraceLinearScan.java graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/TraceLinearScanAllocationPhase.java graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/TraceLinearScanAssignLocationsPhase.java graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/TraceLinearScanEliminateSpillMovePhase.java graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/TraceLinearScanLifetimeAnalysisPhase.java graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/TraceLinearScanRegisterAllocationPhase.java graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/TraceLinearScanResolveDataFlowPhase.java graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/TraceLinearScanWalker.java graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/TraceLocalMoveResolver.java graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/UsePosList.java graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/NodeIntrinsificationPhase.java |
diffstat | 233 files changed, 11472 insertions(+), 10622 deletions(-) [+] |
line wrap: on
line diff
--- a/graal/com.oracle.graal.api.directives.test/src/com/oracle/graal/api/directives/test/ControlFlowAnchorDirectiveTest.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.api.directives.test/src/com/oracle/graal/api/directives/test/ControlFlowAnchorDirectiveTest.java Tue Dec 08 12:30:15 2015 -0800 @@ -167,6 +167,7 @@ while (ret > 1) { GraalDirectives.controlFlowAnchor(); if (ret % 2 == 0) { + GraalDirectives.controlFlowAnchor(); ret /= 2; } else { ret = 3 * ret + 1;
--- a/graal/com.oracle.graal.api.replacements/src/com/oracle/graal/api/replacements/SnippetReflectionProvider.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.api.replacements/src/com/oracle/graal/api/replacements/SnippetReflectionProvider.java Tue Dec 08 12:30:15 2015 -0800 @@ -182,5 +182,5 @@ * @return the value that should be bound to the parameter when invoking the constructor or null * if this provider cannot provide a value of the requested type */ - Object getInjectedNodeIntrinsicParameter(ResolvedJavaType type); + <T> T getInjectedNodeIntrinsicParameter(Class<T> type); }
--- a/graal/com.oracle.graal.asm.test/src/com/oracle/graal/asm/test/AssemblerTest.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.asm.test/src/com/oracle/graal/asm/test/AssemblerTest.java Tue Dec 08 12:30:15 2015 -0800 @@ -75,6 +75,7 @@ byte[] targetCode = test.generateCode(compResult, codeCache.getTarget(), registerConfig, cc); compResult.setTargetCode(targetCode, targetCode.length); compResult.setTotalFrameSize(0); + compResult.close(); InstalledCode code = codeCache.addCode(method, compResult, null, null);
--- a/graal/com.oracle.graal.code/src/com/oracle/graal/code/HexCodeFile.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.code/src/com/oracle/graal/code/HexCodeFile.java Tue Dec 08 12:30:15 2015 -0800 @@ -122,7 +122,7 @@ * Map from a machine code position to a comment for the operands of the instruction at the * position. */ - public final Map<Integer, String> operandComments = new TreeMap<>(); + public final Map<Integer, List<String>> operandComments = new TreeMap<>(); public final byte[] code; @@ -180,8 +180,10 @@ } } - for (Map.Entry<Integer, String> e : operandComments.entrySet()) { - ps.printf("OperandComment %d %s %s%n", e.getKey(), e.getValue(), SECTION_DELIM); + for (Map.Entry<Integer, List<String>> e : operandComments.entrySet()) { + for (String c : e.getValue()) { + ps.printf("OperandComment %d %s %s%n", e.getKey(), c, SECTION_DELIM); + } } ps.flush(); } @@ -218,12 +220,15 @@ } /** - * Sets an operand comment for a given position. - * - * @return the previous operand comment for {@code pos} + * Adds an operand comment for a given position. */ - public String addOperandComment(int pos, String comment) { - return operandComments.put(pos, encodeString(comment)); + public void addOperandComment(int pos, String comment) { + List<String> list = comments.get(pos); + if (list == null) { + list = new ArrayList<>(1); + comments.put(pos, list); + } + list.add(encodeString(comment)); } /**
--- a/graal/com.oracle.graal.code/src/com/oracle/graal/code/HexCodeFileDisassemblerProvider.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.code/src/com/oracle/graal/code/HexCodeFileDisassemblerProvider.java Tue Dec 08 12:30:15 2015 -0800 @@ -117,8 +117,7 @@ } private static void addOperandComment(HexCodeFile hcf, int pos, String comment) { - String oldValue = hcf.addOperandComment(pos, comment); - assert oldValue == null : "multiple comments for operand of instruction at " + pos + ": " + comment + ", " + oldValue; + hcf.addOperandComment(pos, comment); } /**
--- a/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64NodeMatchRules.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64NodeMatchRules.java Tue Dec 08 12:30:15 2015 -0800 @@ -126,7 +126,7 @@ if (value.isConstant()) { JavaConstant constant = value.asJavaConstant(); - if (constant != null && kind == AMD64Kind.QWORD && !NumUtil.isInt(constant.asLong())) { + if (constant != null && kind == AMD64Kind.QWORD && !constant.getJavaKind().isObject() && !NumUtil.isInt(constant.asLong())) { // Only imm32 as long return null; } @@ -146,14 +146,7 @@ LabelRef falseLabel = getLIRBlock(ifNode.falseSuccessor()); boolean unorderedIsTrue = compare.unorderedIsTrue(); double trueLabelProbability = ifNode.probability(ifNode.trueSuccessor()); - Value other; - JavaConstant constant = value.asJavaConstant(); - if (constant != null) { - other = gen.emitJavaConstant(constant); - } else { - other = operand(value); - } - + Value other = operand(value); AMD64AddressValue address = (AMD64AddressValue) operand(access.getAddress()); getLIRGeneratorTool().emitCompareBranchMemory(kind, other, address, getState(access), finalCondition, unorderedIsTrue, trueLabel, falseLabel, trueLabelProbability); return null; @@ -268,6 +261,8 @@ @MatchRule("(If (FloatLessThan=compare value FloatingRead=access))") @MatchRule("(If (PointerEquals=compare value Read=access))") @MatchRule("(If (PointerEquals=compare value FloatingRead=access))") + @MatchRule("(If (ObjectEquals=compare value Read=access))") + @MatchRule("(If (ObjectEquals=compare value FloatingRead=access))") public ComplexMatchResult ifCompareMemory(IfNode root, CompareNode compare, ValueNode value, Access access) { return emitCompareBranchMemory(root, compare, value, access); }
--- a/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64SuitesProvider.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64SuitesProvider.java Tue Dec 08 12:30:15 2015 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -24,10 +24,10 @@ import static com.oracle.graal.compiler.common.BackendOptions.ShouldOptimizeStackToStackMoves; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins; import com.oracle.graal.java.DefaultSuitesProvider; import com.oracle.graal.lir.amd64.phases.StackMoveOptimizationPhase; import com.oracle.graal.lir.phases.LIRSuites; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; import com.oracle.graal.phases.tiers.CompilerConfiguration; public class AMD64SuitesProvider extends DefaultSuitesProvider {
--- a/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/spi/ForeignCallsProvider.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/spi/ForeignCallsProvider.java Tue Dec 08 12:30:15 2015 -0800 @@ -48,6 +48,11 @@ boolean canDeoptimize(ForeignCallDescriptor descriptor); /** + * Identifies foreign calls which are guaranteed to include a safepoint check. + */ + boolean isGuaranteedSafepoint(ForeignCallDescriptor descriptor); + + /** * Gets the linkage for a foreign call. */ ForeignCallLinkage lookupForeignCall(ForeignCallDescriptor descriptor);
--- a/graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCSuitesProvider.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCSuitesProvider.java Tue Dec 08 12:30:15 2015 -0800 @@ -22,8 +22,8 @@ */ package com.oracle.graal.compiler.sparc; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins; import com.oracle.graal.java.DefaultSuitesProvider; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; import com.oracle.graal.phases.tiers.CompilerConfiguration; import com.oracle.graal.phases.tiers.Suites;
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/CheckGraalInvariants.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/CheckGraalInvariants.java Tue Dec 08 12:30:15 2015 -0800 @@ -68,14 +68,14 @@ import com.oracle.graal.debug.GraalDebugConfig; import com.oracle.graal.graph.Node; import com.oracle.graal.graph.NodeClass; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins; -import com.oracle.graal.graphbuilderconf.InvocationPlugins; import com.oracle.graal.java.GraphBuilderPhase; import com.oracle.graal.nodeinfo.NodeInfo; import com.oracle.graal.nodes.PhiNode; import com.oracle.graal.nodes.StructuredGraph; import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugins; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; import com.oracle.graal.phases.OptimisticOptimizations; import com.oracle.graal.phases.PhaseSuite; import com.oracle.graal.phases.VerifyPhase;
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/CountedLoopTest.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/CountedLoopTest.java Tue Dec 08 12:30:15 2015 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -22,22 +22,23 @@ */ package com.oracle.graal.compiler.test; +import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.ResolvedJavaMethod; import org.junit.Test; import com.oracle.graal.api.directives.GraalDirectives; import com.oracle.graal.graph.NodeClass; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins; -import com.oracle.graal.graphbuilderconf.GraphBuilderContext; -import com.oracle.graal.graphbuilderconf.InvocationPlugin; -import com.oracle.graal.graphbuilderconf.InvocationPlugins.Registration; import com.oracle.graal.loop.InductionVariable; import com.oracle.graal.loop.LoopsData; import com.oracle.graal.nodeinfo.NodeInfo; import com.oracle.graal.nodes.StructuredGraph; import com.oracle.graal.nodes.ValueNode; import com.oracle.graal.nodes.calc.FloatingNode; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderContext; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugin; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugins.Registration; import com.oracle.graal.nodes.spi.LIRLowerable; import com.oracle.graal.nodes.spi.NodeLIRBuilderTool; @@ -232,13 +233,6 @@ public void generate(NodeLIRBuilderTool gen) { gen.setResult(this, gen.operand(iv)); } - - @NodeIntrinsic - public static native int get(@ConstantNodeParameter IVProperty property, int iv); - } - - protected static int getIntrinsic(IVProperty property, int iv) { - return IVPropertyNode.get(property, iv); } @Override @@ -246,11 +240,18 @@ Plugins plugins = super.getDefaultGraphBuilderPlugins(); Registration r = new Registration(plugins.getInvocationPlugins(), CountedLoopTest.class); - ResolvedJavaMethod intrinsic = getResolvedJavaMethod("getIntrinsic"); r.register2("get", IVProperty.class, int.class, new InvocationPlugin() { public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg1, ValueNode arg2) { - b.intrinsify(targetMethod, intrinsic, new ValueNode[]{arg1, arg2}); - return true; + IVProperty property = null; + if (arg1.isConstant()) { + property = getSnippetReflection().asObject(IVProperty.class, arg1.asJavaConstant()); + } + if (property != null) { + b.addPush(JavaKind.Int, new IVPropertyNode(property, arg2)); + return true; + } else { + return false; + } } }); @@ -268,4 +269,37 @@ return true; } + public static Result incrementNeqSnippet(int limit) { + int i; + int posLimit = ((limit - 1) & 0xFFFF) + 1; // make sure limit is always strictly positive + Result ret = new Result(); + for (i = 0; i != posLimit; i++) { + GraalDirectives.controlFlowAnchor(); + ret.extremum = get(InductionVariable::extremumNode, i); + } + ret.exitValue = get(InductionVariable::exitValueNode, i); + return ret; + } + + @Test + public void decrementNeq() { + test("decrementNeqSnippet", 256); + } + + public static Result decrementNeqSnippet(int limit) { + int i; + int posLimit = ((limit - 1) & 0xFFFF) + 1; // make sure limit is always strictly positive + Result ret = new Result(); + for (i = posLimit; i != 0; i--) { + GraalDirectives.controlFlowAnchor(); + ret.extremum = get(InductionVariable::extremumNode, i); + } + ret.exitValue = get(InductionVariable::exitValueNode, i); + return ret; + } + + @Test + public void incrementNeq() { + test("incrementNeqSnippet", 256); + } }
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/FinalizableSubclassTest.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/FinalizableSubclassTest.java Tue Dec 08 12:30:15 2015 -0800 @@ -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 @@ -37,10 +37,10 @@ import org.junit.Test; import com.oracle.graal.debug.Debug; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration; import com.oracle.graal.java.GraphBuilderPhase; import com.oracle.graal.nodes.StructuredGraph; import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration; import com.oracle.graal.nodes.java.RegisterFinalizerNode; import com.oracle.graal.phases.OptimisticOptimizations; import com.oracle.graal.phases.common.CanonicalizerPhase;
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java Tue Dec 08 12:30:15 2015 -0800 @@ -80,11 +80,6 @@ import com.oracle.graal.debug.TTY; import com.oracle.graal.graph.Node; import com.oracle.graal.graph.NodeMap; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins; -import com.oracle.graal.graphbuilderconf.GraphBuilderContext; -import com.oracle.graal.graphbuilderconf.InvocationPlugin; -import com.oracle.graal.graphbuilderconf.InvocationPlugins; import com.oracle.graal.java.ComputeLoopFrequenciesClosure; import com.oracle.graal.java.GraphBuilderPhase; import com.oracle.graal.lir.asm.CompilationResultBuilderFactory; @@ -99,6 +94,11 @@ import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions; import com.oracle.graal.nodes.ValueNode; import com.oracle.graal.nodes.cfg.Block; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderContext; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugin; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugins; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; import com.oracle.graal.nodes.spi.LoweringProvider; import com.oracle.graal.nodes.spi.Replacements; import com.oracle.graal.nodes.virtual.VirtualObjectNode;
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/InfopointReasonTest.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/InfopointReasonTest.java Tue Dec 08 12:30:15 2015 -0800 @@ -37,11 +37,11 @@ import org.junit.Test; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration; import com.oracle.graal.lir.asm.CompilationResultBuilderFactory; import com.oracle.graal.nodes.FullInfopointNode; import com.oracle.graal.nodes.StructuredGraph; import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration; import com.oracle.graal.phases.OptimisticOptimizations; import com.oracle.graal.phases.PhaseSuite; import com.oracle.graal.phases.tiers.HighTierContext; @@ -83,7 +83,7 @@ final StructuredGraph graph = parseDebug(method, AllowAssumptions.from(OptAssumptions.getValue())); int graphLineSPs = 0; for (FullInfopointNode ipn : graph.getNodes().filter(FullInfopointNode.class)) { - if (ipn.getReason() == InfopointReason.LINE_NUMBER) { + if (ipn.getReason() == InfopointReason.BYTECODE_POSITION) { ++graphLineSPs; } } @@ -95,7 +95,7 @@ int lineSPs = 0; for (Infopoint sp : cr.getInfopoints()) { assertNotNull(sp.reason); - if (sp.reason == InfopointReason.LINE_NUMBER) { + if (sp.reason == InfopointReason.BYTECODE_POSITION) { ++lineSPs; } }
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/StaticInterfaceFieldTest.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/StaticInterfaceFieldTest.java Tue Dec 08 12:30:15 2015 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -36,12 +36,12 @@ import com.oracle.graal.debug.Debug; import com.oracle.graal.debug.DebugConfigScope; import com.oracle.graal.debug.DelegatingDebugConfig; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins; -import com.oracle.graal.graphbuilderconf.InvocationPlugins; import com.oracle.graal.java.GraphBuilderPhase; import com.oracle.graal.nodes.StructuredGraph; import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugins; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; import com.oracle.graal.phases.OptimisticOptimizations; import com.oracle.graal.phases.PhaseSuite; import com.oracle.graal.phases.VerifyPhase;
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/UnbalancedMonitorsTest.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/UnbalancedMonitorsTest.java Tue Dec 08 12:30:15 2015 -0800 @@ -31,12 +31,12 @@ import org.junit.Test; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins; -import com.oracle.graal.graphbuilderconf.InvocationPlugins; import com.oracle.graal.java.GraphBuilderPhase; import com.oracle.graal.nodes.StructuredGraph; import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugins; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; import com.oracle.graal.phases.OptimisticOptimizations; /**
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/inlining/InliningTest.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/inlining/InliningTest.java Tue Dec 08 12:30:15 2015 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -33,11 +33,11 @@ import com.oracle.graal.debug.Debug.Scope; import com.oracle.graal.debug.DebugDumpScope; import com.oracle.graal.graph.Node; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration; import com.oracle.graal.nodes.FullInfopointNode; import com.oracle.graal.nodes.Invoke; import com.oracle.graal.nodes.StructuredGraph; import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration; import com.oracle.graal.phases.OptimisticOptimizations; import com.oracle.graal.phases.PhaseSuite; import com.oracle.graal.phases.common.CanonicalizerPhase;
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/tutorial/StaticAnalysis.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/tutorial/StaticAnalysis.java Tue Dec 08 12:30:15 2015 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -42,9 +42,6 @@ import com.oracle.graal.debug.Debug.Scope; import com.oracle.graal.graph.Node; import com.oracle.graal.graph.NodeMap; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins; -import com.oracle.graal.graphbuilderconf.InvocationPlugins; import com.oracle.graal.java.GraphBuilderPhase; import com.oracle.graal.nodes.CallTargetNode.InvokeKind; import com.oracle.graal.nodes.ConstantNode; @@ -56,6 +53,9 @@ import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions; import com.oracle.graal.nodes.ValueNode; import com.oracle.graal.nodes.ValuePhiNode; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugins; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; import com.oracle.graal.nodes.java.LoadFieldNode; import com.oracle.graal.nodes.java.MethodCallTargetNode; import com.oracle.graal.nodes.java.NewArrayNode;
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java Tue Dec 08 12:30:15 2015 -0800 @@ -345,7 +345,6 @@ FrameMap frameMap = lirGenRes.getFrameMap(); CompilationResultBuilder crb = backend.newCompilationResultBuilder(lirGenRes, frameMap, compilationResult, factory); backend.emitCode(crb, lirGenRes.getLIR(), installedCodeOwner); - crb.finish(); if (assumptions != null && !assumptions.isEmpty()) { compilationResult.setAssumptions(assumptions.toArray()); } @@ -353,7 +352,7 @@ compilationResult.setMethods(rootMethod, inlinedMethods); compilationResult.setBytecodeSize(bytecodeSize); } - + crb.finish(); if (Debug.isMeterEnabled()) { List<DataPatch> ldp = compilationResult.getDataPatches(); JavaKind[] kindValues = JavaKind.values();
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/NodeLIRBuilder.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/NodeLIRBuilder.java Tue Dec 08 12:30:15 2015 -0800 @@ -451,7 +451,7 @@ BytecodePosition position = node.getNodeContext(BytecodePosition.class); if (position != null && (lastPosition == null || !lastPosition.equals(position))) { lastPosition = position; - recordSimpleInfopoint(InfopointReason.LINE_NUMBER, position); + recordSimpleInfopoint(InfopointReason.BYTECODE_POSITION, position); } } if (node instanceof LIRLowerable) {
--- a/graal/com.oracle.graal.debug/src/com/oracle/graal/debug/Debug.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.debug/src/com/oracle/graal/debug/Debug.java Tue Dec 08 12:30:15 2015 -0800 @@ -576,6 +576,26 @@ } } + public static void log(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8, Object arg9) { + log(DEFAULT_LOG_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); + } + + public static void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8, Object arg9) { + if (ENABLED) { + DebugScope.getInstance().log(logLevel, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); + } + } + + public static void log(String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8, Object arg9, Object arg10) { + log(DEFAULT_LOG_LEVEL, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10); + } + + public static void log(int logLevel, String format, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8, Object arg9, Object arg10) { + if (ENABLED) { + DebugScope.getInstance().log(logLevel, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10); + } + } + public static void logv(String format, Object... args) { logv(DEFAULT_LOG_LEVEL, format, args); }
--- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Node.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Node.java Tue Dec 08 12:30:15 2015 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -163,7 +163,11 @@ * annotated method can be replaced with an instance of the node class denoted by * {@link #value()}. For this reason, the signature of the annotated method must match the * signature (excluding a prefix of {@linkplain InjectedNodeParameter injected} parameters) of a - * factory method named {@code "create"} in the node class. + * constructor in the node class. + * <p> + * If the node class has a static method {@code intrinsify} with a matching signature plus a + * {@code GraphBuilderContext} as first argument, this method is called instead of creating the + * node. */ @java.lang.annotation.Retention(RetentionPolicy.RUNTIME) @java.lang.annotation.Target(ElementType.METHOD)
--- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeStack.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeStack.java Tue Dec 08 12:30:15 2015 -0800 @@ -57,4 +57,17 @@ public boolean isEmpty() { return tos == 0; } + + @Override + public String toString() { + if (tos == 0) { + return "NodeStack: []"; + } + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < tos; i++) { + sb.append(", "); + sb.append(values[i]); + } + return "NodeStack: [" + sb.substring(2) + "]"; + } }
--- a/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/ForeignCallPlugin.java Tue Dec 08 12:29:24 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +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.graal.graphbuilderconf; - -import jdk.vm.ci.meta.ResolvedJavaMethod; - -import com.oracle.graal.compiler.common.spi.ForeignCallDescriptor; -import com.oracle.graal.compiler.common.spi.ForeignCallsProvider; -import com.oracle.graal.nodes.ValueNode; -import com.oracle.graal.nodes.extended.ForeignCallNode; - -/** - * {@link InvocationPlugin} for converting a method call directly to a foreign call. - */ -public final class ForeignCallPlugin implements InvocationPlugin { - private final ForeignCallsProvider foreignCalls; - private final ForeignCallDescriptor descriptor; - - public ForeignCallPlugin(ForeignCallsProvider foreignCalls, ForeignCallDescriptor descriptor) { - this.foreignCalls = foreignCalls; - this.descriptor = descriptor; - } - - public boolean execute(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode[] args) { - ForeignCallNode foreignCall = new ForeignCallNode(foreignCalls, descriptor, args); - foreignCall.setBci(b.bci()); - b.addPush(targetMethod.getSignature().getReturnKind(), foreignCall); - return true; - } -}
--- a/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/GraphBuilderConfiguration.java Tue Dec 08 12:29:24 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,301 +0,0 @@ -/* - * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.graal.graphbuilderconf; - -import java.util.Arrays; - -import jdk.vm.ci.meta.ResolvedJavaType; - -import com.oracle.graal.compiler.common.GraalOptions; -import com.oracle.graal.nodes.FullInfopointNode; -import com.oracle.graal.nodes.SimpleInfopointNode; - -public class GraphBuilderConfiguration { - - public static class Plugins { - private final InvocationPlugins invocationPlugins; - private NodePlugin[] nodePlugins; - private ParameterPlugin[] parameterPlugins; - private InlineInvokePlugin[] inlineInvokePlugins; - private LoopExplosionPlugin loopExplosionPlugin; - - /** - * Creates a copy of a given set of plugins. The {@link InvocationPlugins} in - * {@code copyFrom} become the {@linkplain InvocationPlugins#getParent() default} - * {@linkplain #getInvocationPlugins() invocation plugins} in this object. - */ - public Plugins(Plugins copyFrom) { - this.invocationPlugins = new InvocationPlugins(copyFrom.invocationPlugins); - this.nodePlugins = copyFrom.nodePlugins; - this.parameterPlugins = copyFrom.parameterPlugins; - this.inlineInvokePlugins = copyFrom.inlineInvokePlugins; - this.loopExplosionPlugin = copyFrom.loopExplosionPlugin; - } - - /** - * Creates a new set of plugins. - * - * @param invocationPlugins the {@linkplain #getInvocationPlugins() invocation plugins} in - * this object - */ - public Plugins(InvocationPlugins invocationPlugins) { - this.invocationPlugins = invocationPlugins; - this.nodePlugins = new NodePlugin[0]; - this.parameterPlugins = new ParameterPlugin[0]; - this.inlineInvokePlugins = new InlineInvokePlugin[0]; - } - - public InvocationPlugins getInvocationPlugins() { - return invocationPlugins; - } - - public NodePlugin[] getNodePlugins() { - return nodePlugins; - } - - public void appendNodePlugin(NodePlugin plugin) { - nodePlugins = Arrays.copyOf(nodePlugins, nodePlugins.length + 1); - nodePlugins[nodePlugins.length - 1] = plugin; - } - - public void prependNodePlugin(NodePlugin plugin) { - NodePlugin[] newPlugins = new NodePlugin[nodePlugins.length + 1]; - System.arraycopy(nodePlugins, 0, newPlugins, 1, nodePlugins.length); - newPlugins[0] = plugin; - nodePlugins = newPlugins; - } - - public void clearNodePlugin() { - nodePlugins = new NodePlugin[0]; - } - - public ParameterPlugin[] getParameterPlugins() { - return parameterPlugins; - } - - public void appendParameterPlugin(ParameterPlugin plugin) { - parameterPlugins = Arrays.copyOf(parameterPlugins, parameterPlugins.length + 1); - parameterPlugins[parameterPlugins.length - 1] = plugin; - } - - public void prependParameterPlugin(ParameterPlugin plugin) { - ParameterPlugin[] newPlugins = new ParameterPlugin[parameterPlugins.length + 1]; - System.arraycopy(parameterPlugins, 0, newPlugins, 1, parameterPlugins.length); - newPlugins[0] = plugin; - parameterPlugins = newPlugins; - } - - public void clearParameterPlugin() { - parameterPlugins = new ParameterPlugin[0]; - } - - public InlineInvokePlugin[] getInlineInvokePlugins() { - return inlineInvokePlugins; - } - - public void appendInlineInvokePlugin(InlineInvokePlugin plugin) { - inlineInvokePlugins = Arrays.copyOf(inlineInvokePlugins, inlineInvokePlugins.length + 1); - inlineInvokePlugins[inlineInvokePlugins.length - 1] = plugin; - } - - public void prependInlineInvokePlugin(InlineInvokePlugin plugin) { - InlineInvokePlugin[] newPlugins = new InlineInvokePlugin[inlineInvokePlugins.length + 1]; - System.arraycopy(inlineInvokePlugins, 0, newPlugins, 1, inlineInvokePlugins.length); - newPlugins[0] = plugin; - inlineInvokePlugins = newPlugins; - } - - public void clearInlineInvokePlugins() { - inlineInvokePlugins = new InlineInvokePlugin[0]; - } - - public LoopExplosionPlugin getLoopExplosionPlugin() { - return loopExplosionPlugin; - } - - public void setLoopExplosionPlugin(LoopExplosionPlugin plugin) { - this.loopExplosionPlugin = plugin; - } - } - - private static final ResolvedJavaType[] EMPTY = new ResolvedJavaType[]{}; - - private final boolean eagerResolving; - private final boolean omitAllExceptionEdges; - private final boolean omitAssertions; - private final ResolvedJavaType[] skippedExceptionTypes; - private final DebugInfoMode debugInfoMode; - private final boolean clearNonLiveLocals; - private boolean useProfiling; - private final Plugins plugins; - - public static enum DebugInfoMode { - SafePointsOnly, - /** - * This mode inserts {@link SimpleInfopointNode}s in places where no safepoints would be - * inserted: inlining boundaries, and line number switches. - * <p> - * In this mode the infopoint only have a location (method and bytecode index) and no - * values. - * <p> - * This is useful to have better program counter to bci mapping and has no influence on the - * generated code. However it can increase the amount of metadata and does not allow access - * to accessing values at runtime. - */ - Simple, - /** - * In this mode, {@link FullInfopointNode}s are generated in the same locations as in - * {@link #Simple} mode but the infopoints have access to the runtime values. - * <p> - * This is relevant when code is to be generated for native, machine-code level debugging - * but can have a limit the amount of optimization applied to the code. - */ - Full, - } - - protected GraphBuilderConfiguration(boolean eagerResolving, boolean omitAllExceptionEdges, boolean omitAssertions, DebugInfoMode debugInfoMode, ResolvedJavaType[] skippedExceptionTypes, - boolean clearNonLiveLocals, Plugins plugins) { - this.eagerResolving = eagerResolving; - this.omitAllExceptionEdges = omitAllExceptionEdges; - this.omitAssertions = omitAssertions; - this.debugInfoMode = debugInfoMode; - this.skippedExceptionTypes = skippedExceptionTypes; - this.clearNonLiveLocals = clearNonLiveLocals; - this.useProfiling = true; - this.plugins = plugins; - } - - /** - * Creates a copy of this configuration with all its plugins. The {@link InvocationPlugins} in - * this configuration become the {@linkplain InvocationPlugins#getParent() parent} of the - * {@link InvocationPlugins} in the copy. - */ - public GraphBuilderConfiguration copy() { - Plugins newPlugins = new Plugins(plugins); - GraphBuilderConfiguration result = new GraphBuilderConfiguration(eagerResolving, omitAllExceptionEdges, omitAssertions, debugInfoMode, skippedExceptionTypes, clearNonLiveLocals, newPlugins); - result.useProfiling = useProfiling; - return result; - } - - public boolean getUseProfiling() { - return useProfiling; - } - - public void setUseProfiling(boolean b) { - this.useProfiling = b; - } - - public GraphBuilderConfiguration withEagerResolving(boolean newEagerResolving) { - return new GraphBuilderConfiguration(newEagerResolving, omitAllExceptionEdges, omitAssertions, debugInfoMode, skippedExceptionTypes, clearNonLiveLocals, plugins); - } - - public GraphBuilderConfiguration withSkippedExceptionTypes(ResolvedJavaType[] newSkippedExceptionTypes) { - return new GraphBuilderConfiguration(eagerResolving, omitAllExceptionEdges, omitAssertions, debugInfoMode, newSkippedExceptionTypes, clearNonLiveLocals, plugins); - } - - public GraphBuilderConfiguration withOmitAllExceptionEdges(boolean newOmitAllExceptionEdges) { - return new GraphBuilderConfiguration(eagerResolving, newOmitAllExceptionEdges, omitAssertions, debugInfoMode, skippedExceptionTypes, clearNonLiveLocals, plugins); - } - - public GraphBuilderConfiguration withOmitAssertions(boolean newOmitAssertions) { - return new GraphBuilderConfiguration(eagerResolving, omitAllExceptionEdges, newOmitAssertions, debugInfoMode, skippedExceptionTypes, clearNonLiveLocals, plugins); - } - - public GraphBuilderConfiguration withDebugInfoMode(DebugInfoMode newDebugInfoMode) { - ResolvedJavaType[] newSkippedExceptionTypes = skippedExceptionTypes == EMPTY ? EMPTY : Arrays.copyOf(skippedExceptionTypes, skippedExceptionTypes.length); - return new GraphBuilderConfiguration(eagerResolving, omitAllExceptionEdges, omitAssertions, newDebugInfoMode, newSkippedExceptionTypes, clearNonLiveLocals, plugins); - } - - public GraphBuilderConfiguration withClearNonLiveLocals(boolean newClearNonLiveLocals) { - return new GraphBuilderConfiguration(eagerResolving, omitAllExceptionEdges, omitAssertions, debugInfoMode, skippedExceptionTypes, newClearNonLiveLocals, plugins); - } - - public ResolvedJavaType[] getSkippedExceptionTypes() { - return skippedExceptionTypes; - } - - public boolean eagerResolving() { - return eagerResolving; - } - - public boolean omitAllExceptionEdges() { - return omitAllExceptionEdges; - } - - public boolean omitAssertions() { - return omitAssertions; - } - - public boolean insertNonSafepointDebugInfo() { - return debugInfoMode.ordinal() >= DebugInfoMode.Simple.ordinal(); - } - - public boolean insertFullDebugInfo() { - return debugInfoMode.ordinal() >= DebugInfoMode.Full.ordinal(); - } - - public boolean insertSimpleDebugInfo() { - return debugInfoMode == DebugInfoMode.Simple; - } - - public boolean clearNonLiveLocals() { - return clearNonLiveLocals; - } - - public static GraphBuilderConfiguration getDefault(Plugins plugins) { - return new GraphBuilderConfiguration(false, false, false, DebugInfoMode.SafePointsOnly, EMPTY, GraalOptions.OptClearNonLiveLocals.getValue(), plugins); - } - - public static GraphBuilderConfiguration getInfopointDefault(Plugins plugins) { - return new GraphBuilderConfiguration(true, false, false, DebugInfoMode.Simple, EMPTY, GraalOptions.OptClearNonLiveLocals.getValue(), plugins); - } - - public static GraphBuilderConfiguration getEagerDefault(Plugins plugins) { - return new GraphBuilderConfiguration(true, false, false, DebugInfoMode.SafePointsOnly, EMPTY, GraalOptions.OptClearNonLiveLocals.getValue(), plugins); - } - - public static GraphBuilderConfiguration getInfopointEagerDefault(Plugins plugins) { - return new GraphBuilderConfiguration(true, false, false, DebugInfoMode.Simple, EMPTY, GraalOptions.OptClearNonLiveLocals.getValue(), plugins); - } - - public static GraphBuilderConfiguration getSnippetDefault(Plugins plugins) { - return new GraphBuilderConfiguration(true, true, false, DebugInfoMode.SafePointsOnly, EMPTY, GraalOptions.OptClearNonLiveLocals.getValue(), plugins); - } - - public static GraphBuilderConfiguration getFullDebugDefault(Plugins plugins) { - return new GraphBuilderConfiguration(true, false, false, DebugInfoMode.Full, EMPTY, GraalOptions.OptClearNonLiveLocals.getValue(), plugins); - } - - /** - * Returns {@code true} if it is an error for a class/field/method resolution to fail. The - * default is the same result as returned by {@link #eagerResolving()}. However, it may be - * overridden to allow failure even when {@link #eagerResolving} is {@code true}. - */ - public boolean unresolvedIsError() { - return eagerResolving; - } - - public Plugins getPlugins() { - return plugins; - } -}
--- a/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/GraphBuilderContext.java Tue Dec 08 12:29:24 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,266 +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.graal.graphbuilderconf; - -import static com.oracle.graal.compiler.common.type.StampFactory.objectNonNull; -import static jdk.vm.ci.meta.DeoptimizationAction.InvalidateReprofile; -import static jdk.vm.ci.meta.DeoptimizationReason.NullCheckException; -import jdk.vm.ci.code.BailoutException; -import jdk.vm.ci.meta.Assumptions; -import jdk.vm.ci.meta.ConstantReflectionProvider; -import jdk.vm.ci.meta.JavaKind; -import jdk.vm.ci.meta.JavaType; -import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaMethod; -import jdk.vm.ci.meta.ResolvedJavaType; - -import com.oracle.graal.compiler.common.type.ObjectStamp; -import com.oracle.graal.compiler.common.type.Stamp; -import com.oracle.graal.compiler.common.type.StampFactory; -import com.oracle.graal.nodes.CallTargetNode.InvokeKind; -import com.oracle.graal.nodes.FixedGuardNode; -import com.oracle.graal.nodes.PiNode; -import com.oracle.graal.nodes.StateSplit; -import com.oracle.graal.nodes.StructuredGraph; -import com.oracle.graal.nodes.ValueNode; -import com.oracle.graal.nodes.calc.IsNullNode; -import com.oracle.graal.nodes.spi.StampProvider; -import com.oracle.graal.nodes.type.StampTool; - -/** - * Used by a {@link GraphBuilderPlugin} to interface with an object that parses the bytecode of a - * single {@linkplain #getMethod() method} as part of building a {@linkplain #getGraph() graph} . - */ -public interface GraphBuilderContext { - - /** - * Raw operation for adding a node to the graph when neither {@link #add} nor - * {@link #addPush(JavaKind, ValueNode)} can be used. - * - * @return either the node added or an equivalent node - */ - <T extends ValueNode> T append(T value); - - /** - * Adds the given node to the graph and also adds recursively all referenced inputs. - * - * @param value the node to be added to the graph - * @return either the node added or an equivalent node - */ - <T extends ValueNode> T recursiveAppend(T value); - - /** - * Pushes a given value to the frame state stack using an explicit kind. This should be used - * when {@code value.getJavaKind()} is different from the kind that the bytecode instruction - * currently being parsed pushes to the stack. - * - * @param kind the kind to use when type checking this operation - * @param value the value to push to the stack. The value must already have been - * {@linkplain #append(ValueNode) appended}. - */ - void push(JavaKind kind, ValueNode value); - - /** - * Adds a node to the graph. If the returned node is a {@link StateSplit} with a null - * {@linkplain StateSplit#stateAfter() frame state}, the frame state is initialized. - * - * @param value the value to add to the graph and push to the stack. The - * {@code value.getJavaKind()} kind is used when type checking this operation. - * @return a node equivalent to {@code value} in the graph - */ - default <T extends ValueNode> T add(T value) { - if (value.graph() != null) { - assert !(value instanceof StateSplit) || ((StateSplit) value).stateAfter() != null; - return value; - } - T equivalentValue = append(value); - if (equivalentValue instanceof StateSplit) { - StateSplit stateSplit = (StateSplit) equivalentValue; - if (stateSplit.stateAfter() == null && stateSplit.hasSideEffect()) { - setStateAfter(stateSplit); - } - } - return equivalentValue; - } - - /** - * Adds a node with a non-void kind to the graph, pushes it to the stack. If the returned node - * is a {@link StateSplit} with a null {@linkplain StateSplit#stateAfter() frame state}, the - * frame state is initialized. - * - * @param kind the kind to use when type checking this operation - * @param value the value to add to the graph and push to the stack - * @return a node equivalent to {@code value} in the graph - */ - default <T extends ValueNode> T addPush(JavaKind kind, T value) { - T equivalentValue = value.graph() != null ? value : append(value); - push(kind, equivalentValue); - if (equivalentValue instanceof StateSplit) { - StateSplit stateSplit = (StateSplit) equivalentValue; - if (stateSplit.stateAfter() == null && stateSplit.hasSideEffect()) { - setStateAfter(stateSplit); - } - } - return equivalentValue; - } - - /** - * Handles an invocation that a plugin determines can replace the original invocation (i.e., the - * one for which the plugin was applied). This applies all standard graph builder processing to - * the replaced invocation including applying any relevant plugins. - * - * @param invokeKind the kind of the replacement invocation - * @param targetMethod the target of the replacement invocation - * @param args the arguments to the replacement invocation - * @param forceInlineEverything specifies if all invocations encountered in the scope of - * handling the replaced invoke are to be force inlined - */ - void handleReplacedInvoke(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] args, boolean forceInlineEverything); - - /** - * Intrinsifies an invocation of a given method by inlining the bytecodes of a given - * substitution method. - * - * @param targetMethod the method being intrinsified - * @param substitute the intrinsic implementation - * @param args the arguments with which to inline the invocation - */ - void intrinsify(ResolvedJavaMethod targetMethod, ResolvedJavaMethod substitute, ValueNode[] args); - - StampProvider getStampProvider(); - - MetaAccessProvider getMetaAccess(); - - default Assumptions getAssumptions() { - return getGraph().getAssumptions(); - } - - ConstantReflectionProvider getConstantReflection(); - - /** - * Gets the graph being constructed. - */ - StructuredGraph getGraph(); - - /** - * Creates a snap shot of the current frame state with the BCI of the instruction after the one - * currently being parsed and assigns it to a given {@linkplain StateSplit#hasSideEffect() side - * effect} node. - * - * @param sideEffect a side effect node just appended to the graph - */ - void setStateAfter(StateSplit sideEffect); - - /** - * Gets the parsing context for the method that inlines the method being parsed by this context. - */ - GraphBuilderContext getParent(); - - /** - * Gets the first ancestor parsing context that is not parsing a - * {@linkplain #parsingIntrinsic() intrinsic}. - */ - default GraphBuilderContext getNonIntrinsicAncestor() { - GraphBuilderContext ancestor = getParent(); - while (ancestor != null && ancestor.parsingIntrinsic()) { - ancestor = ancestor.getParent(); - } - return ancestor; - } - - /** - * Gets the method being parsed by this context. - */ - ResolvedJavaMethod getMethod(); - - /** - * Gets the index of the bytecode instruction currently being parsed. - */ - int bci(); - - /** - * Gets the kind of invocation currently being parsed. - */ - InvokeKind getInvokeKind(); - - /** - * Gets the return type of the invocation currently being parsed. - */ - JavaType getInvokeReturnType(); - - default Stamp getInvokeReturnStamp() { - JavaType returnType = getInvokeReturnType(); - if (returnType.getJavaKind() == JavaKind.Object && returnType instanceof ResolvedJavaType) { - return StampFactory.declared((ResolvedJavaType) returnType); - } else { - return StampFactory.forKind(returnType.getJavaKind()); - } - } - - /** - * Gets the inline depth of this context. A return value of 0 implies that this is the context - * for the parse root. - */ - default int getDepth() { - GraphBuilderContext parent = getParent(); - return parent == null ? 0 : 1 + parent.getDepth(); - } - - /** - * Determines if this parsing context is within the bytecode of an intrinsic or a method inlined - * by an intrinsic. - */ - default boolean parsingIntrinsic() { - return getIntrinsic() != null; - } - - /** - * Gets the intrinsic of the current parsing context or {@code null} if not - * {@link #parsingIntrinsic() parsing an intrinsic}. - */ - IntrinsicContext getIntrinsic(); - - BailoutException bailout(String string); - - /** - * Gets a version of a given value that has a {@linkplain StampTool#isPointerNonNull(ValueNode) - * non-null} stamp. - */ - default ValueNode nullCheckedValue(ValueNode value) { - if (!StampTool.isPointerNonNull(value.stamp())) { - IsNullNode condition = getGraph().unique(new IsNullNode(value)); - ObjectStamp receiverStamp = (ObjectStamp) value.stamp(); - Stamp stamp = receiverStamp.join(objectNonNull()); - FixedGuardNode fixedGuard = append(new FixedGuardNode(condition, NullCheckException, InvalidateReprofile, true)); - PiNode nonNullReceiver = getGraph().unique(new PiNode(value, stamp)); - nonNullReceiver.setGuard(fixedGuard); - // TODO: Propogating the non-null into the frame state would - // remove subsequent null-checks on the same value. However, - // it currently causes an assertion failure when merging states. - // - // frameState.replace(value, nonNullReceiver); - return nonNullReceiver; - } - return value; - } -}
--- a/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/GraphBuilderPlugin.java Tue Dec 08 12:29:24 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,29 +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.graal.graphbuilderconf; - -/** - * Marker interface for graph builder plugins. - */ -public interface GraphBuilderPlugin { -}
--- a/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InlineInvokePlugin.java Tue Dec 08 12:29:24 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,130 +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.graal.graphbuilderconf; - -import jdk.vm.ci.meta.JavaType; -import jdk.vm.ci.meta.ResolvedJavaMethod; - -import com.oracle.graal.nodes.Invoke; -import com.oracle.graal.nodes.ValueNode; - -/** - * Plugin for specifying what is inlined during graph parsing. This plugin is also notified - * {@link #notifyBeforeInline before} and {@link #notifyAfterInline} the inlining, as well as of - * {@link #notifyNotInlined non-inlined} invocations (i.e., those for which an {@link Invoke} node - * is created). - */ -public interface InlineInvokePlugin extends GraphBuilderPlugin { - - /** - * Result of a {@link #shouldInlineInvoke inlining decision}. - */ - public static class InlineInfo { - - /** - * Denotes a call site that must not be inlined and should be implemented by a node that - * does not speculate on the call not raising an exception. - */ - public static final InlineInfo DO_NOT_INLINE_WITH_EXCEPTION = new InlineInfo(null, false); - - /** - * Denotes a call site must not be inlined and can be implemented by a node that speculates - * the call will not throw an exception. - */ - public static final InlineInfo DO_NOT_INLINE_NO_EXCEPTION = new InlineInfo(null, false); - - private final ResolvedJavaMethod methodToInline; - private final boolean isIntrinsic; - - public InlineInfo(ResolvedJavaMethod methodToInline, boolean isIntrinsic) { - this.methodToInline = methodToInline; - this.isIntrinsic = isIntrinsic; - } - - /** - * Returns the method to be inlined, or {@code null} if the call site must not be inlined. - */ - public ResolvedJavaMethod getMethodToInline() { - return methodToInline; - } - - /** - * Specifies if {@link #methodToInline} is an intrinsic for the original method (i.e., the - * {@code method} passed to {@link InlineInvokePlugin#shouldInlineInvoke}). - */ - public boolean isIntrinsic() { - return isIntrinsic; - } - } - - /** - * Determines whether a call to a given method is to be inlined. The return value is a - * tri-state: - * <p> - * Non-null return value with a non-null {@link InlineInfo#getMethodToInline method}: That - * {@link InlineInfo#getMethodToInline method} is inlined. Note that it can be a different - * method than the one specified here as the parameter, which allows method substitutions. - * <p> - * Non-null return value with a null {@link InlineInfo#getMethodToInline method}, e.g., - * {@link InlineInfo#DO_NOT_INLINE_WITH_EXCEPTION}: The method is not inlined, and other plugins - * with a lower priority cannot overwrite this decision. - * <p> - * Null return value: This plugin made no decision, other plugins with a lower priority are - * asked. - * - * @param b the context - * @param method the target method of an invoke - * @param args the arguments to the invoke - * @param returnType the return type derived from {@code method}'s signature - */ - default InlineInfo shouldInlineInvoke(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args, JavaType returnType) { - return null; - } - - /** - * Notification that a method is about to be inlined. - * - * @param methodToInline the inlined method - */ - default void notifyBeforeInline(ResolvedJavaMethod methodToInline) { - } - - /** - * Notification that a method was inlined. - * - * @param methodToInline the inlined method - */ - default void notifyAfterInline(ResolvedJavaMethod methodToInline) { - } - - /** - * Notifies this plugin of the {@link Invoke} node created for a method that was not inlined per - * {@link #shouldInlineInvoke}. - * - * @param b the context - * @param method the method that was not inlined - * @param invoke the invoke node created for the call to {@code method} - */ - default void notifyNotInlined(GraphBuilderContext b, ResolvedJavaMethod method, Invoke invoke) { - } -}
--- a/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/IntrinsicContext.java Tue Dec 08 12:29:24 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,170 +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.graal.graphbuilderconf; - -import static com.oracle.graal.graphbuilderconf.IntrinsicContext.CompilationContext.INLINE_AFTER_PARSING; -import static com.oracle.graal.graphbuilderconf.IntrinsicContext.CompilationContext.ROOT_COMPILATION; -import static jdk.vm.ci.code.BytecodeFrame.AFTER_BCI; -import static jdk.vm.ci.code.BytecodeFrame.BEFORE_BCI; -import static jdk.vm.ci.code.BytecodeFrame.INVALID_FRAMESTATE_BCI; -import jdk.vm.ci.meta.ResolvedJavaMethod; - -import com.oracle.graal.nodes.AbstractMergeNode; -import com.oracle.graal.nodes.FrameState; -import com.oracle.graal.nodes.Invoke; -import com.oracle.graal.nodes.StateSplit; -import com.oracle.graal.nodes.StructuredGraph; - -/** - * An intrinsic is a substitute implementation of a Java method (or a bytecode in the case of - * snippets) that is itself implemented in Java. This interface provides information about the - * intrinsic currently being processed by the graph builder. - * - * When in the scope of an intrinsic, the graph builder does not check the value kinds flowing - * through the JVM state since intrinsics can employ non-Java kinds to represent values such as raw - * machine words and pointers. - */ -public class IntrinsicContext { - - /** - * Gets the method being intrinsified. - */ - final ResolvedJavaMethod method; - - /** - * Gets the method providing the intrinsic implementation. - */ - final ResolvedJavaMethod intrinsic; - - public ResolvedJavaMethod getOriginalMethod() { - return method; - } - - public ResolvedJavaMethod getIntrinsicMethod() { - return intrinsic; - } - - /** - * Determines if a call within the compilation scope of this intrinsic represents a call to the - * {@linkplain #getOriginalMethod() original} method. This denotes the path where a partial - * intrinsification falls back to the original method. - */ - public boolean isCallToOriginal(ResolvedJavaMethod targetMethod) { - return method.equals(targetMethod) || intrinsic.equals(targetMethod); - } - - final CompilationContext compilationContext; - - public IntrinsicContext(ResolvedJavaMethod method, ResolvedJavaMethod intrinsic, CompilationContext compilationContext) { - this.method = method; - this.intrinsic = intrinsic; - this.compilationContext = compilationContext; - assert !isCompilationRoot() || method.hasBytecodes() : "Cannot root compile intrinsic for native or abstract method " + method.format("%H.%n(%p)"); - } - - public boolean isPostParseInlined() { - return compilationContext.equals(INLINE_AFTER_PARSING); - } - - public boolean isCompilationRoot() { - return compilationContext.equals(ROOT_COMPILATION); - } - - /** - * Denotes the compilation context in which an intrinsic is being parsed. - */ - public enum CompilationContext { - /** - * An intrinsic is being processed when parsing an invoke bytecode that calls the - * intrinsified method. - */ - INLINE_DURING_PARSING, - - /** - * An intrinsic is being processed when inlining an {@link Invoke} in an existing graph. - */ - INLINE_AFTER_PARSING, - - /** - * An intrinsic is the root of compilation. - */ - ROOT_COMPILATION - } - - /** - * Models the state of a graph in terms of {@link StateSplit#hasSideEffect() side effects} that - * are control flow predecessors of the current point in a graph. - */ - public interface SideEffectsState { - - /** - * Determines if the current program point is preceded by one or more side effects. - */ - boolean isAfterSideEffect(); - - /** - * Gets the side effects preceding the current program point. - */ - Iterable<StateSplit> sideEffects(); - - /** - * Records a side effect for the current program point. - */ - void addSideEffect(StateSplit sideEffect); - } - - public FrameState createFrameState(StructuredGraph graph, SideEffectsState sideEffects, StateSplit forStateSplit) { - assert forStateSplit != graph.start(); - if (forStateSplit.hasSideEffect()) { - if (sideEffects.isAfterSideEffect()) { - // Only the last side effect on any execution path in a replacement - // can inherit the stateAfter of the replaced node - FrameState invalid = graph.add(new FrameState(INVALID_FRAMESTATE_BCI)); - for (StateSplit lastSideEffect : sideEffects.sideEffects()) { - lastSideEffect.setStateAfter(invalid); - } - } - sideEffects.addSideEffect(forStateSplit); - return graph.add(new FrameState(AFTER_BCI)); - } else { - if (forStateSplit instanceof AbstractMergeNode) { - // Merge nodes always need a frame state - if (sideEffects.isAfterSideEffect()) { - // A merge after one or more side effects - return graph.add(new FrameState(AFTER_BCI)); - } else { - // A merge before any side effects - return graph.add(new FrameState(BEFORE_BCI)); - } - } else { - // Other non-side-effects do not need a state - return null; - } - } - } - - @Override - public String toString() { - return "Intrinsic{original: " + method.format("%H.%n(%p)") + ", intrinsic: " + intrinsic.format("%H.%n(%p)") + ", context: " + compilationContext + "}"; - } -}
--- a/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InvocationPlugin.java Tue Dec 08 12:29:24 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,208 +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.graal.graphbuilderconf; - -import java.lang.invoke.MethodHandle; -import java.lang.reflect.Method; - -import jdk.vm.ci.common.JVMCIError; -import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaMethod; - -import com.oracle.graal.nodes.Invoke; -import com.oracle.graal.nodes.ValueNode; -import com.oracle.graal.nodes.type.StampTool; - -/** - * Plugin for handling a specific method invocation. - */ -public interface InvocationPlugin extends GraphBuilderPlugin { - - /** - * The receiver in a non-static method. The class literal for this interface must be used with - * {@link InvocationPlugins#put(InvocationPlugin, boolean, boolean, boolean, Class, String, Class...)} - * to denote the receiver argument for such a non-static method. - */ - public interface Receiver { - /** - * Gets the receiver value, null checking it first if necessary. - * - * @return the receiver value with a {@linkplain StampTool#isPointerNonNull(ValueNode) - * non-null} stamp - */ - ValueNode get(); - - /** - * Determines if the receiver is constant. - */ - default boolean isConstant() { - return false; - } - } - - /** - * Determines if this plugin is for a method with a polymorphic signature (e.g. - * {@link MethodHandle#invokeExact(Object...)}). - */ - default boolean isSignaturePolymorphic() { - return false; - } - - /** - * Determines if this plugin can only be used when inlining the method is it associated with. - * That is, this plugin cannot be used when the associated method is the compilation root. - */ - default boolean inlineOnly() { - return isSignaturePolymorphic(); - } - - /** - * Handles invocation of a signature polymorphic method. - * - * @param receiver access to the receiver, {@code null} if {@code targetMethod} is static - * @param argsIncludingReceiver all arguments to the invocation include the raw receiver in - * position 0 if {@code targetMethod} is not static - * @see #execute - */ - default boolean applyPolymorphic(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode... argsIncludingReceiver) { - return defaultHandler(b, targetMethod, receiver, argsIncludingReceiver); - } - - /** - * @see #execute - */ - default boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver) { - return defaultHandler(b, targetMethod, receiver); - } - - /** - * @see #execute - */ - default boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode arg) { - return defaultHandler(b, targetMethod, receiver, arg); - } - - /** - * @see #execute - */ - default boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode arg1, ValueNode arg2) { - return defaultHandler(b, targetMethod, receiver, arg1, arg2); - } - - /** - * @see #execute - */ - default boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode arg1, ValueNode arg2, ValueNode arg3) { - return defaultHandler(b, targetMethod, receiver, arg1, arg2, arg3); - } - - /** - * @see #execute - */ - default boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode arg1, ValueNode arg2, ValueNode arg3, ValueNode arg4) { - return defaultHandler(b, targetMethod, receiver, arg1, arg2, arg3, arg4); - } - - /** - * @see #execute - */ - default boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode arg1, ValueNode arg2, ValueNode arg3, ValueNode arg4, ValueNode arg5) { - return defaultHandler(b, targetMethod, receiver, arg1, arg2, arg3, arg4, arg5); - } - - /** - * Executes this plugin against a set of invocation arguments. - * - * The default implementation in {@link InvocationPlugin} dispatches to the {@code apply(...)} - * method that matches the number of arguments or to {@link #applyPolymorphic} if {@code plugin} - * is {@linkplain #isSignaturePolymorphic() signature polymorphic}. - * - * @param targetMethod the method for which this plugin is being applied - * @param receiver access to the receiver, {@code null} if {@code targetMethod} is static - * @param argsIncludingReceiver all arguments to the invocation include the receiver in position - * 0 if {@code targetMethod} is not static - * @return {@code true} if this plugin handled the invocation of {@code targetMethod} - * {@code false} if the graph builder should process the invoke further (e.g., by - * inlining it or creating an {@link Invoke} node). A plugin that does not handle an - * invocation must not modify the graph being constructed. - */ - default boolean execute(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode[] argsIncludingReceiver) { - if (isSignaturePolymorphic()) { - return applyPolymorphic(b, targetMethod, receiver, argsIncludingReceiver); - } else if (receiver != null) { - assert !targetMethod.isStatic(); - assert argsIncludingReceiver.length > 0; - if (argsIncludingReceiver.length == 1) { - return apply(b, targetMethod, receiver); - } else if (argsIncludingReceiver.length == 2) { - return apply(b, targetMethod, receiver, argsIncludingReceiver[1]); - } else if (argsIncludingReceiver.length == 3) { - return apply(b, targetMethod, receiver, argsIncludingReceiver[1], argsIncludingReceiver[2]); - } else if (argsIncludingReceiver.length == 4) { - return apply(b, targetMethod, receiver, argsIncludingReceiver[1], argsIncludingReceiver[2], argsIncludingReceiver[3]); - } else if (argsIncludingReceiver.length == 5) { - return apply(b, targetMethod, receiver, argsIncludingReceiver[1], argsIncludingReceiver[2], argsIncludingReceiver[3], argsIncludingReceiver[4]); - } else { - return defaultHandler(b, targetMethod, receiver, argsIncludingReceiver); - } - } else { - assert targetMethod.isStatic(); - if (argsIncludingReceiver.length == 0) { - return apply(b, targetMethod, null); - } else if (argsIncludingReceiver.length == 1) { - return apply(b, targetMethod, null, argsIncludingReceiver[0]); - } else if (argsIncludingReceiver.length == 2) { - return apply(b, targetMethod, null, argsIncludingReceiver[0], argsIncludingReceiver[1]); - } else if (argsIncludingReceiver.length == 3) { - return apply(b, targetMethod, null, argsIncludingReceiver[0], argsIncludingReceiver[1], argsIncludingReceiver[2]); - } else if (argsIncludingReceiver.length == 4) { - return apply(b, targetMethod, null, argsIncludingReceiver[0], argsIncludingReceiver[1], argsIncludingReceiver[2], argsIncludingReceiver[3]); - } else if (argsIncludingReceiver.length == 5) { - return apply(b, targetMethod, null, argsIncludingReceiver[0], argsIncludingReceiver[1], argsIncludingReceiver[2], argsIncludingReceiver[3], argsIncludingReceiver[4]); - } else { - return defaultHandler(b, targetMethod, receiver, argsIncludingReceiver); - } - - } - } - - /** - * Handles an invocation when a specific {@code apply} method is not available. - */ - default boolean defaultHandler(@SuppressWarnings("unused") GraphBuilderContext b, ResolvedJavaMethod targetMethod, @SuppressWarnings("unused") InvocationPlugin.Receiver receiver, - ValueNode... args) { - throw new JVMCIError("Invocation plugin for %s does not handle invocations with %d arguments", targetMethod.format("%H.%n(%p)"), args.length); - } - - default StackTraceElement getApplySourceLocation(MetaAccessProvider metaAccess) { - Class<?> c = getClass(); - for (Method m : c.getDeclaredMethods()) { - if (m.getName().equals("apply")) { - return metaAccess.lookupJavaMethod(m).asStackTraceElement(0); - } else if (m.getName().equals("defaultHandler")) { - return metaAccess.lookupJavaMethod(m).asStackTraceElement(0); - } - } - throw new JVMCIError("could not find method named \"apply\" or \"defaultHandler\" in " + c.getName()); - } -}
--- a/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InvocationPlugins.java Tue Dec 08 12:29:24 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,604 +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.graal.graphbuilderconf; - -import static java.lang.String.format; - -import java.lang.reflect.Executable; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import jdk.vm.ci.common.JVMCIError; -import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaMethod; - -import com.oracle.graal.graph.Node; -import com.oracle.graal.graph.iterators.NodeIterable; -import com.oracle.graal.nodes.ValueNode; - -/** - * Manages a set of {@link InvocationPlugin}s. - */ -public class InvocationPlugins { - - public static class InvocationPluginReceiver implements InvocationPlugin.Receiver { - private final GraphBuilderContext parser; - private ValueNode[] args; - private ValueNode value; - - public InvocationPluginReceiver(GraphBuilderContext parser) { - this.parser = parser; - } - - @Override - public ValueNode get() { - assert args != null : "Cannot get the receiver of a static method"; - if (value == null) { - value = parser.nullCheckedValue(args[0]); - if (value != args[0]) { - args[0] = value; - } - } - return value; - } - - @Override - public boolean isConstant() { - return args[0].isConstant(); - } - - public InvocationPluginReceiver init(ResolvedJavaMethod targetMethod, ValueNode[] newArgs) { - if (!targetMethod.isStatic()) { - this.args = newArgs; - this.value = null; - return this; - } - return null; - } - } - - /** - * Utility for - * {@linkplain InvocationPlugins#register(InvocationPlugin, Class, String, Class...) - * registration} of invocation plugins. - */ - public static class Registration { - - private final InvocationPlugins plugins; - private final Class<?> declaringClass; - private boolean allowOverwrite; - - /** - * Creates an object for registering {@link InvocationPlugin}s for methods declared by a - * given class. - * - * @param plugins where to register the plugins - * @param declaringClass the class declaring the methods for which plugins will be - * registered via this object - */ - public Registration(InvocationPlugins plugins, Class<?> declaringClass) { - this.plugins = plugins; - this.declaringClass = declaringClass; - } - - /** - * Creates an object for registering {@link InvocationPlugin}s for methods declared by a - * given class. - * - * @param plugins where to register the plugins - * @param declaringClassName the name of the class class declaring the methods for which - * plugins will be registered via this object - */ - public Registration(InvocationPlugins plugins, String declaringClassName) { - this.plugins = plugins; - try { - this.declaringClass = Class.forName(declaringClassName); - } catch (ClassNotFoundException ex) { - throw JVMCIError.shouldNotReachHere(ex); - } - } - - /** - * Configures this registration to allow or disallow overwriting of invocation plugins. - */ - public Registration setAllowOverwrite(boolean allowOverwrite) { - this.allowOverwrite = allowOverwrite; - return this; - } - - /** - * Registers a plugin for a method with no arguments. - * - * @param name the name of the method - * @param plugin the plugin to be registered - */ - public void register0(String name, InvocationPlugin plugin) { - plugins.register(plugin, false, allowOverwrite, declaringClass, name); - } - - /** - * Registers a plugin for a method with 1 argument. - * - * @param name the name of the method - * @param plugin the plugin to be registered - */ - public void register1(String name, Class<?> arg, InvocationPlugin plugin) { - plugins.register(plugin, false, allowOverwrite, declaringClass, name, arg); - } - - /** - * Registers a plugin for a method with 2 arguments. - * - * @param name the name of the method - * @param plugin the plugin to be registered - */ - public void register2(String name, Class<?> arg1, Class<?> arg2, InvocationPlugin plugin) { - plugins.register(plugin, false, allowOverwrite, declaringClass, name, arg1, arg2); - } - - /** - * Registers a plugin for a method with 3 arguments. - * - * @param name the name of the method - * @param plugin the plugin to be registered - */ - public void register3(String name, Class<?> arg1, Class<?> arg2, Class<?> arg3, InvocationPlugin plugin) { - plugins.register(plugin, false, allowOverwrite, declaringClass, name, arg1, arg2, arg3); - } - - /** - * Registers a plugin for a method with 4 arguments. - * - * @param name the name of the method - * @param plugin the plugin to be registered - */ - public void register4(String name, Class<?> arg1, Class<?> arg2, Class<?> arg3, Class<?> arg4, InvocationPlugin plugin) { - plugins.register(plugin, false, allowOverwrite, declaringClass, name, arg1, arg2, arg3, arg4); - } - - /** - * Registers a plugin for a method with 5 arguments. - * - * @param name the name of the method - * @param plugin the plugin to be registered - */ - public void register5(String name, Class<?> arg1, Class<?> arg2, Class<?> arg3, Class<?> arg4, Class<?> arg5, InvocationPlugin plugin) { - plugins.register(plugin, false, allowOverwrite, declaringClass, name, arg1, arg2, arg3, arg4, arg5); - } - - /** - * Registers a plugin for an optional method with no arguments. - * - * @param name the name of the method - * @param plugin the plugin to be registered - */ - public void registerOptional0(String name, InvocationPlugin plugin) { - plugins.register(plugin, true, allowOverwrite, declaringClass, name); - } - - /** - * Registers a plugin for an optional method with 1 argument. - * - * @param name the name of the method - * @param plugin the plugin to be registered - */ - public void registerOptional1(String name, Class<?> arg, InvocationPlugin plugin) { - plugins.register(plugin, true, allowOverwrite, declaringClass, name, arg); - } - - /** - * Registers a plugin for an optional method with 2 arguments. - * - * @param name the name of the method - * @param plugin the plugin to be registered - */ - public void registerOptional2(String name, Class<?> arg1, Class<?> arg2, InvocationPlugin plugin) { - plugins.register(plugin, true, allowOverwrite, declaringClass, name, arg1, arg2); - } - - /** - * Registers a plugin for an optional method with 3 arguments. - * - * @param name the name of the method - * @param plugin the plugin to be registered - */ - public void registerOptional3(String name, Class<?> arg1, Class<?> arg2, Class<?> arg3, InvocationPlugin plugin) { - plugins.register(plugin, true, allowOverwrite, declaringClass, name, arg1, arg2, arg3); - } - - /** - * Registers a plugin for an optional method with 4 arguments. - * - * @param name the name of the method - * @param plugin the plugin to be registered - */ - public void registerOptional4(String name, Class<?> arg1, Class<?> arg2, Class<?> arg3, Class<?> arg4, InvocationPlugin plugin) { - plugins.register(plugin, true, allowOverwrite, declaringClass, name, arg1, arg2, arg3, arg4); - } - - /** - * Registers a plugin that implements a method based on the bytecode of a substitute method. - * - * @param substituteDeclaringClass the class declaring the substitute method - * @param name the name of both the original and substitute method - * @param argumentTypes the argument types of the method. Element 0 of this array must be - * the {@link Class} value for {@link InvocationPlugin.Receiver} iff the method - * is non-static. Upon returning, element 0 will have been rewritten to - * {@code declaringClass} - */ - public void registerMethodSubstitution(Class<?> substituteDeclaringClass, String name, Class<?>... argumentTypes) { - registerMethodSubstitution(substituteDeclaringClass, name, name, argumentTypes); - } - - /** - * Registers a plugin that implements a method based on the bytecode of a substitute method. - * - * @param substituteDeclaringClass the class declaring the substitute method - * @param name the name of both the original method - * @param substituteName the name of the substitute method - * @param argumentTypes the argument types of the method. Element 0 of this array must be - * the {@link Class} value for {@link InvocationPlugin.Receiver} iff the method - * is non-static. Upon returning, element 0 will have been rewritten to - * {@code declaringClass} - */ - public void registerMethodSubstitution(Class<?> substituteDeclaringClass, String name, String substituteName, Class<?>... argumentTypes) { - MethodSubstitutionPlugin plugin = new MethodSubstitutionPlugin(substituteDeclaringClass, substituteName, argumentTypes); - plugins.register(plugin, false, allowOverwrite, declaringClass, name, argumentTypes); - } - } - - /** - * Key for a method. - */ - static class MethodKey { - final boolean isStatic; - - /** - * This method is optional. This is used for new API methods not present in previous JDK - * versions. - */ - final boolean isOptional; - - final Class<?> declaringClass; - final String name; - final Class<?>[] argumentTypes; - final InvocationPlugin value; - - MethodKey(InvocationPlugin data, boolean isStatic, boolean isOptional, Class<?> declaringClass, String name, Class<?>... argumentTypes) { - assert isStatic || argumentTypes[0] == declaringClass; - this.value = data; - this.isStatic = isStatic; - this.isOptional = isOptional; - this.declaringClass = declaringClass; - this.name = name; - this.argumentTypes = argumentTypes; - assert isOptional || resolveJava() != null; - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof MethodKey) { - MethodKey that = (MethodKey) obj; - boolean res = this.name.equals(that.name) && this.declaringClass.equals(that.declaringClass) && Arrays.equals(this.argumentTypes, that.argumentTypes); - assert !res || this.isStatic == that.isStatic; - return res; - } - return false; - } - - public int getDeclaredParameterCount() { - return isStatic ? argumentTypes.length : argumentTypes.length - 1; - } - - @Override - public int hashCode() { - // Replay compilation mandates use of stable hash codes - return declaringClass.getName().hashCode() ^ name.hashCode(); - } - - private ResolvedJavaMethod resolve(MetaAccessProvider metaAccess) { - Executable method = resolveJava(); - if (method == null) { - return null; - } - return metaAccess.lookupJavaMethod(method); - } - - private Executable resolveJava() { - try { - Executable res; - Class<?>[] parameterTypes = isStatic ? argumentTypes : Arrays.copyOfRange(argumentTypes, 1, argumentTypes.length); - if (name.equals("<init>")) { - res = declaringClass.getDeclaredConstructor(parameterTypes); - } else { - res = declaringClass.getDeclaredMethod(name, parameterTypes); - } - assert Modifier.isStatic(res.getModifiers()) == isStatic; - return res; - } catch (NoSuchMethodException | SecurityException e) { - if (isOptional) { - return null; - } - throw new InternalError(e); - } - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(declaringClass.getName()).append('.').append(name).append('('); - for (Class<?> p : argumentTypes) { - if (sb.charAt(sb.length() - 1) != '(') { - sb.append(", "); - } - sb.append(p.getSimpleName()); - } - return sb.append(')').toString(); - } - } - - private final MetaAccessProvider metaAccess; - - /** - * Initial list of entries. - */ - private final List<MethodKey> registrations = new ArrayList<>(INITIAL_CAPACITY); - - /** - * Entry map that is initialized upon first call to {@link #get(ResolvedJavaMethod)}. - * - * Note: this must be volatile since double-checked locking is used to initialize it - */ - private volatile Map<ResolvedJavaMethod, InvocationPlugin> entries; - - private static final int INITIAL_CAPACITY = 64; - - /** - * Adds an entry to this map for a specified method. - * - * @param value value to be associated with the specified method - * @param isStatic specifies if the method is static - * @param isOptional specifies if the method is optional - * @param declaringClass the class declaring the method - * @param name the name of the method - * @param argumentTypes the argument types of the method. Element 0 of this array must be - * {@code declaringClass} iff the method is non-static. - * @return an object representing the method - */ - MethodKey put(InvocationPlugin value, boolean isStatic, boolean isOptional, boolean allowOverwrite, Class<?> declaringClass, String name, Class<?>... argumentTypes) { - assert isStatic || argumentTypes[0] == declaringClass; - MethodKey methodKey = new MethodKey(value, isStatic, isOptional, declaringClass, name, argumentTypes); - assert entries == null : "registration is closed"; - assert allowOverwrite || !registrations.contains(methodKey) : "a value is already registered for " + methodKey; - registrations.add(methodKey); - return methodKey; - } - - /** - * Determines if a method denoted by a given {@link MethodKey} is in this map. - */ - boolean containsKey(MethodKey key) { - return registrations.contains(key); - } - - InvocationPlugin get(ResolvedJavaMethod method) { - if (entries == null) { - initializeMap(); - } - - return entries.get(method); - } - - /** - * Disallows new registrations of new plugins, and creates the internal tables for method - * lookup. - */ - public void closeRegistration() { - if (entries == null) { - initializeMap(); - } - } - - void initializeMap() { - if (registrations.isEmpty()) { - entries = Collections.emptyMap(); - } else { - Map<ResolvedJavaMethod, InvocationPlugin> newEntries = new HashMap<>(); - for (MethodKey methodKey : registrations) { - ResolvedJavaMethod m = methodKey.resolve(metaAccess); - newEntries.put(m, methodKey.value); - } - entries = newEntries; - } - } - - public int size() { - return registrations.size(); - } - - /** - * The plugins {@linkplain #lookupInvocation(ResolvedJavaMethod) searched} before searching in - * this object. - */ - protected final InvocationPlugins parent; - - private InvocationPlugins(InvocationPlugins parent, MetaAccessProvider metaAccess) { - this.metaAccess = metaAccess; - InvocationPlugins p = parent; - this.parent = p; - } - - /** - * Creates a set of invocation plugins with a non-null {@linkplain #getParent() parent}. - */ - public InvocationPlugins(InvocationPlugins parent) { - this(parent, parent.getMetaAccess()); - } - - public MetaAccessProvider getMetaAccess() { - return metaAccess; - } - - public InvocationPlugins(MetaAccessProvider metaAccess) { - this(null, metaAccess); - } - - private void register(InvocationPlugin plugin, boolean isOptional, boolean allowOverwrite, Class<?> declaringClass, String name, Class<?>... argumentTypes) { - boolean isStatic = argumentTypes.length == 0 || argumentTypes[0] != InvocationPlugin.Receiver.class; - if (!isStatic) { - argumentTypes[0] = declaringClass; - } - MethodKey methodInfo = put(plugin, isStatic, isOptional, allowOverwrite, declaringClass, name, argumentTypes); - assert Checker.check(this, methodInfo, plugin); - } - - /** - * Registers an invocation plugin for a given method. There must be no plugin currently - * registered for {@code method}. - * - * @param argumentTypes the argument types of the method. Element 0 of this array must be the - * {@link Class} value for {@link InvocationPlugin.Receiver} iff the method is - * non-static. Upon returning, element 0 will have been rewritten to - * {@code declaringClass} - */ - public void register(InvocationPlugin plugin, Class<?> declaringClass, String name, Class<?>... argumentTypes) { - register(plugin, false, false, declaringClass, name, argumentTypes); - } - - /** - * Registers an invocation plugin for a given, optional method. There must be no plugin - * currently registered for {@code method}. - * - * @param argumentTypes the argument types of the method. Element 0 of this array must be the - * {@link Class} value for {@link InvocationPlugin.Receiver} iff the method is - * non-static. Upon returning, element 0 will have been rewritten to - * {@code declaringClass} - */ - public void registerOptional(InvocationPlugin plugin, Class<?> declaringClass, String name, Class<?>... argumentTypes) { - register(plugin, true, false, declaringClass, name, argumentTypes); - } - - /** - * Gets the plugin for a given method. - * - * @param method the method to lookup - * @return the plugin associated with {@code method} or {@code null} if none exists - */ - public InvocationPlugin lookupInvocation(ResolvedJavaMethod method) { - if (parent != null) { - InvocationPlugin plugin = parent.lookupInvocation(method); - if (plugin != null) { - return plugin; - } - } - return get(method); - } - - /** - * Gets the invocation plugins {@linkplain #lookupInvocation(ResolvedJavaMethod) searched} - * before searching in this object. - */ - public InvocationPlugins getParent() { - return parent; - } - - @Override - public String toString() { - return registrations.stream().map(MethodKey::toString).collect(Collectors.joining(", ")) + " / parent: " + this.parent; - } - - private static class Checker { - private static final int MAX_ARITY = 5; - /** - * The set of all {@link InvocationPlugin#apply} method signatures. - */ - static final Class<?>[][] SIGS; - - static { - ArrayList<Class<?>[]> sigs = new ArrayList<>(MAX_ARITY); - for (Method method : InvocationPlugin.class.getDeclaredMethods()) { - if (!Modifier.isStatic(method.getModifiers()) && method.getName().equals("apply")) { - Class<?>[] sig = method.getParameterTypes(); - assert sig[0] == GraphBuilderContext.class; - assert sig[1] == ResolvedJavaMethod.class; - assert sig[2] == InvocationPlugin.Receiver.class; - assert Arrays.asList(sig).subList(3, sig.length).stream().allMatch(c -> c == ValueNode.class); - while (sigs.size() < sig.length - 2) { - sigs.add(null); - } - sigs.set(sig.length - 3, sig); - } - } - assert sigs.indexOf(null) == -1 : format("need to add an apply() method to %s that takes %d %s arguments ", InvocationPlugin.class.getName(), sigs.indexOf(null), - ValueNode.class.getSimpleName()); - SIGS = sigs.toArray(new Class<?>[sigs.size()][]); - } - - public static boolean check(InvocationPlugins plugins, MethodKey method, InvocationPlugin plugin) { - InvocationPlugins p = plugins.parent; - while (p != null) { - assert !p.containsKey(method) : "a plugin is already registered for " + method; - p = p.parent; - } - if (plugin instanceof ForeignCallPlugin) { - return true; - } - if (plugin instanceof MethodSubstitutionPlugin) { - MethodSubstitutionPlugin msplugin = (MethodSubstitutionPlugin) plugin; - msplugin.getJavaSubstitute(); - return true; - } - int arguments = method.getDeclaredParameterCount(); - assert arguments < SIGS.length : format("need to extend %s to support method with %d arguments: %s", InvocationPlugin.class.getSimpleName(), arguments, method); - for (Method m : plugin.getClass().getDeclaredMethods()) { - if (m.getName().equals("apply")) { - Class<?>[] parameterTypes = m.getParameterTypes(); - if (Arrays.equals(SIGS[arguments], parameterTypes)) { - return true; - } - } - } - throw new AssertionError(format("graph builder plugin for %s not found", method)); - } - } - - /** - * Checks a set of nodes added to the graph by an {@link InvocationPlugin}. - * - * @param b the graph builder that applied the plugin - * @param plugin a plugin that was just applied - * @param newNodes the nodes added to the graph by {@code plugin} - * @throws AssertionError if any check fail - */ - public void checkNewNodes(GraphBuilderContext b, InvocationPlugin plugin, NodeIterable<Node> newNodes) { - if (parent != null) { - parent.checkNewNodes(b, plugin, newNodes); - } - } -}
--- a/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/LoopExplosionPlugin.java Tue Dec 08 12:29:24 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +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.graal.graphbuilderconf; - -import jdk.vm.ci.meta.ResolvedJavaMethod; - -public interface LoopExplosionPlugin extends GraphBuilderPlugin { - boolean shouldExplodeLoops(ResolvedJavaMethod method); - - boolean shouldMergeExplosions(ResolvedJavaMethod method); -}
--- a/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/MethodSubstitutionPlugin.java Tue Dec 08 12:29:24 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,206 +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.graal.graphbuilderconf; - -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.Arrays; -import java.util.stream.Collectors; - -import jdk.vm.ci.common.JVMCIError; -import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaMethod; -import sun.misc.Launcher; - -import com.oracle.graal.nodes.ValueNode; - -/** - * An {@link InvocationPlugin} for a method where the implementation of the method is provided by a - * {@linkplain #getSubstitute(MetaAccessProvider) substitute} method. A substitute method must be - * static even if the substituted method is not. - */ -public final class MethodSubstitutionPlugin implements InvocationPlugin { - - private ResolvedJavaMethod cachedSubstitute; - - /** - * The class in which the substitute method is declared. - */ - private final Class<?> declaringClass; - - /** - * The name of the original and substitute method. - */ - private final String name; - - /** - * The parameter types of the substitute method. - */ - private final Class<?>[] parameters; - - private final boolean originalIsStatic; - - /** - * Creates a method substitution plugin. - * - * @param declaringClass the class in which the substitute method is declared - * @param name the name of the substitute method - * @param parameters the parameter types of the substitute method. If the original method is not - * static, then {@code parameters[0]} must be the {@link Class} value denoting - * {@link InvocationPlugin.Receiver} - */ - public MethodSubstitutionPlugin(Class<?> declaringClass, String name, Class<?>... parameters) { - this.declaringClass = declaringClass; - this.name = name; - this.parameters = parameters; - this.originalIsStatic = parameters.length == 0 || parameters[0] != InvocationPlugin.Receiver.class; - } - - /** - * Creates a method substitution plugin. - * - * @param declaringClass the class in which the substitute method is declared - * @param name the name of the substitute method - * @param parameters the parameter types of the substitute method - */ - public MethodSubstitutionPlugin(boolean originalIsStatic, Class<?> declaringClass, String name, Class<?>... parameters) { - this.declaringClass = declaringClass; - this.name = name; - this.parameters = parameters; - this.originalIsStatic = originalIsStatic; - } - - public boolean inlineOnly() { - // Conservatively assume MacroNodes may be used in a substitution - return true; - } - - /** - * Gets the substitute method, resolving it first if necessary. - */ - public ResolvedJavaMethod getSubstitute(MetaAccessProvider metaAccess) { - if (cachedSubstitute == null) { - cachedSubstitute = metaAccess.lookupJavaMethod(getJavaSubstitute()); - } - return cachedSubstitute; - } - - /** - * Gets the reflection API version of the substitution method. - */ - Method getJavaSubstitute() throws JVMCIError { - Method substituteMethod = lookupSubstitute(); - int modifiers = substituteMethod.getModifiers(); - if (Modifier.isAbstract(modifiers) || Modifier.isNative(modifiers)) { - throw new JVMCIError("Substitution method must not be abstract or native: " + substituteMethod); - } - if (!Modifier.isStatic(modifiers)) { - throw new JVMCIError("Substitution method must be static: " + substituteMethod); - } - return substituteMethod; - } - - /** - * Determines if a given method is the substitute method of this plugin. - */ - private boolean isSubstitute(Method m) { - if (Modifier.isStatic(m.getModifiers()) && m.getName().equals(name)) { - if (parameters.length == m.getParameterCount()) { - Class<?>[] mparams = m.getParameterTypes(); - int start = 0; - if (!originalIsStatic) { - start = 1; - if (!mparams[0].isAssignableFrom(parameters[0])) { - return false; - } - } - for (int i = start; i < mparams.length; i++) { - if (mparams[i] != parameters[i]) { - return false; - } - } - } - return true; - } - return false; - } - - /** - * Gets the substitute method of this plugin. - */ - private Method lookupSubstitute() { - for (Method m : declaringClass.getDeclaredMethods()) { - if (isSubstitute(m)) { - return m; - } - } - throw new JVMCIError("No method found specified by %s", this); - } - - /** - * Resolves a name to a class. - * - * @param className the name of the class to resolve - * @param optional if true, resolution failure returns null - * @return the resolved class or null if resolution fails and {@code optional} is true - */ - public static Class<?> resolveClass(String className, boolean optional) { - try { - // Need to use launcher class path to handle classes - // that are not on the boot class path - ClassLoader cl = Launcher.getLauncher().getClassLoader(); - return Class.forName(className, false, cl); - } catch (ClassNotFoundException e) { - if (optional) { - return null; - } - throw new JVMCIError("Could not resolve type " + className); - } - } - - @Override - public boolean execute(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode[] argsIncludingReceiver) { - ResolvedJavaMethod subst = getSubstitute(b.getMetaAccess()); - if (receiver != null) { - receiver.get(); - } - b.intrinsify(targetMethod, subst, argsIncludingReceiver); - return true; - } - - public StackTraceElement getApplySourceLocation(MetaAccessProvider metaAccess) { - Class<?> c = getClass(); - for (Method m : c.getDeclaredMethods()) { - if (m.getName().equals("execute")) { - return metaAccess.lookupJavaMethod(m).asStackTraceElement(0); - } - } - throw new JVMCIError("could not find method named \"execute\" in " + c.getName()); - } - - @Override - public String toString() { - return String.format("%s[%s.%s(%s)]", getClass().getSimpleName(), declaringClass.getName(), name, - Arrays.asList(parameters).stream().map(c -> c.getSimpleName()).collect(Collectors.joining(", "))); - } -}
--- a/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/NodePlugin.java Tue Dec 08 12:29:24 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,213 +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.graal.graphbuilderconf; - -import jdk.vm.ci.meta.JavaKind; -import jdk.vm.ci.meta.JavaTypeProfile; -import jdk.vm.ci.meta.ResolvedJavaField; -import jdk.vm.ci.meta.ResolvedJavaMethod; -import jdk.vm.ci.meta.ResolvedJavaType; -import jdk.vm.ci.meta.Signature; - -import com.oracle.graal.nodes.ValueNode; - -public interface NodePlugin extends GraphBuilderPlugin { - /** - * Handle the parsing of a method invocation bytecode to a method that can be bound statically. - * If the method returns true, it must {@link GraphBuilderContext#push push} a value as the - * result of the method invocation using the {@link Signature#getReturnKind return kind} of the - * method. - * - * @param b the context - * @param method the statically bound, invoked method - * @param args the arguments of the method invocation - * @return true if the plugin handles the invocation, false otherwise - */ - default boolean handleInvoke(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args) { - return false; - } - - /** - * Handle the parsing of a GETFIELD bytecode. If the method returns true, it must - * {@link GraphBuilderContext#push push} a value using the - * {@link ResolvedJavaField#getJavaKind() kind} of the field. - * - * @param b the context - * @param object the receiver object for the field access - * @param field the accessed field - * @return true if the plugin handles the field access, false otherwise - */ - default boolean handleLoadField(GraphBuilderContext b, ValueNode object, ResolvedJavaField field) { - return false; - } - - /** - * Handle the parsing of a GETSTATIC bytecode. If the method returns true, it must - * {@link GraphBuilderContext#push push} a value using the - * {@link ResolvedJavaField#getJavaKind() kind} of the field. - * - * @param b the context - * @param field the accessed field - * @return true if the plugin handles the field access, false otherwise - */ - default boolean handleLoadStaticField(GraphBuilderContext b, ResolvedJavaField field) { - return false; - } - - /** - * Handle the parsing of a PUTFIELD bytecode. - * - * @param b the context - * @param object the receiver object for the field access - * @param field the accessed field - * @param value the value to be stored into the field - * @return true if the plugin handles the field access, false otherwise - */ - default boolean handleStoreField(GraphBuilderContext b, ValueNode object, ResolvedJavaField field, ValueNode value) { - return false; - } - - /** - * Handle the parsing of a PUTSTATIC bytecode. - * - * @param b the context - * @param field the accessed field - * @param value the value to be stored into the field - * @return true if the plugin handles the field access, false otherwise. - */ - default boolean handleStoreStaticField(GraphBuilderContext b, ResolvedJavaField field, ValueNode value) { - return false; - } - - /** - * Handle the parsing of an array load bytecode. If the method returns true, it must - * {@link GraphBuilderContext#push push} a value using the provided elementKind. - * - * @param b the context - * @param array the accessed array - * @param index the index for the array access - * @param elementKind the element kind of the accessed array - * @return true if the plugin handles the array access, false otherwise. - */ - default boolean handleLoadIndexed(GraphBuilderContext b, ValueNode array, ValueNode index, JavaKind elementKind) { - return false; - } - - /** - * Handle the parsing of an array store bytecode. - * - * @param b the context - * @param array the accessed array - * @param index the index for the array access - * @param elementKind the element kind of the accessed array - * @param value the value to be stored into the array - * @return true if the plugin handles the array access, false otherwise. - */ - default boolean handleStoreIndexed(GraphBuilderContext b, ValueNode array, ValueNode index, JavaKind elementKind, ValueNode value) { - return false; - } - - /** - * Handle the parsing of a CHECKCAST bytecode. If the method returns true, it must - * {@link GraphBuilderContext#push push} a value with the result of the cast using - * {@link JavaKind#Object}. - * - * @param b the context - * @param object the object to be type checked - * @param type the type that the object is checked against - * @param profile the profiling information for the type check, or null if no profiling - * information is available - * @return true if the plugin handles the cast, false otherwise - */ - default boolean handleCheckCast(GraphBuilderContext b, ValueNode object, ResolvedJavaType type, JavaTypeProfile profile) { - return false; - } - - /** - * Handle the parsing of a INSTANCEOF bytecode. If the method returns true, it must - * {@link GraphBuilderContext#push push} a value with the result of the instanceof using - * {@link JavaKind#Int}. - * - * @param b the context - * @param object the object to be type checked - * @param type the type that the object is checked against - * @param profile the profiling information for the type check, or null if no profiling - * information is available - * @return true if the plugin handles the instanceof, false otherwise - */ - default boolean handleInstanceOf(GraphBuilderContext b, ValueNode object, ResolvedJavaType type, JavaTypeProfile profile) { - return false; - } - - /** - * Handle the parsing of a NEW bytecode. If the method returns true, it must - * {@link GraphBuilderContext#push push} a value with the result of the allocation using - * {@link JavaKind#Object}. - * - * @param b the context - * @param type the type to be instantiated - * @return true if the plugin handles the bytecode, false otherwise - */ - default boolean handleNewInstance(GraphBuilderContext b, ResolvedJavaType type) { - return false; - } - - /** - * Handle the parsing of a NEWARRAY and ANEWARRAY bytecode. If the method returns true, it must - * {@link GraphBuilderContext#push push} a value with the result of the allocation using - * {@link JavaKind#Object}. - * - * @param b the context - * @param elementType the element type of the array to be instantiated - * @param length the length of the new array - * @return true if the plugin handles the bytecode, false otherwise - */ - default boolean handleNewArray(GraphBuilderContext b, ResolvedJavaType elementType, ValueNode length) { - return false; - } - - /** - * Handle the parsing of a MULTIANEWARRAY bytecode. If the method returns true, it must - * {@link GraphBuilderContext#push push} a value with the result of the allocation using - * {@link JavaKind#Object}. - * - * @param b the context - * @param type the type of the outermost array to be instantiated - * @param dimensions the array of lengths for all the dimensions to be instantiated - * @return true if the plugin handles the bytecode, false otherwise - */ - default boolean handleNewMultiArray(GraphBuilderContext b, ResolvedJavaType type, ValueNode[] dimensions) { - return false; - } - - /** - * If the plugin {@link GraphBuilderContext#push pushes} a value with a different - * {@link JavaKind} than specified by the bytecode, it must override this method and return - * {@code true}. This disables assertion checking for value kinds. - * - * @param b the context - */ - default boolean canChangeStackKind(GraphBuilderContext b) { - return false; - } -}
--- a/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/ParameterPlugin.java Tue Dec 08 12:29:24 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,30 +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.graal.graphbuilderconf; - -import com.oracle.graal.compiler.common.type.Stamp; -import com.oracle.graal.nodes.calc.FloatingNode; - -public interface ParameterPlugin extends GraphBuilderPlugin { - FloatingNode interceptParameter(GraphBuilderContext b, int index, Stamp stamp); -}
--- a/graal/com.oracle.graal.hotspot.amd64.test/src/com/oracle/graal/hotspot/amd64/test/DataPatchInConstantsTest.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot.amd64.test/src/com/oracle/graal/hotspot/amd64/test/DataPatchInConstantsTest.java Tue Dec 08 12:30:15 2015 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 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 @@ -29,18 +29,17 @@ import jdk.vm.ci.amd64.AMD64; import jdk.vm.ci.meta.AllocatableValue; import jdk.vm.ci.meta.Constant; +import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.ResolvedJavaMethod; import org.junit.Assume; import org.junit.Before; import org.junit.Test; -import com.oracle.graal.api.replacements.ClassSubstitution; -import com.oracle.graal.api.replacements.MethodSubstitution; import com.oracle.graal.asm.amd64.AMD64Address; import com.oracle.graal.asm.amd64.AMD64MacroAssembler; import com.oracle.graal.graph.NodeClass; import com.oracle.graal.hotspot.nodes.CompressionNode; -import com.oracle.graal.hotspot.nodes.CompressionNode.CompressionOp; import com.oracle.graal.hotspot.nodes.type.NarrowOopStamp; import com.oracle.graal.hotspot.test.HotSpotGraalCompilerTest; import com.oracle.graal.lir.LIRInstruction; @@ -51,6 +50,10 @@ import com.oracle.graal.nodeinfo.NodeInfo; import com.oracle.graal.nodes.FixedWithNextNode; import com.oracle.graal.nodes.ValueNode; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderContext; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugin; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugins.Registration; import com.oracle.graal.nodes.spi.LIRLowerable; import com.oracle.graal.nodes.spi.NodeLIRBuilderTool; @@ -135,30 +138,28 @@ test("compareSnippet"); } - private static boolean initReplacements = false; - - @Before - public void initReplacements() { - if (!initReplacements) { - getReplacements().registerSubstitutions(DataPatchInConstantsTest.class, DataPatchInConstantsTestSubstitutions.class); - initReplacements = true; - } - } + @Override + protected Plugins getDefaultGraphBuilderPlugins() { + Plugins plugins = super.getDefaultGraphBuilderPlugins(); + Registration r = new Registration(plugins.getInvocationPlugins(), DataPatchInConstantsTest.class); - @ClassSubstitution(DataPatchInConstantsTest.class) - private static class DataPatchInConstantsTestSubstitutions { + r.register1("loadThroughPatch", Object.class, new InvocationPlugin() { + public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg) { + b.addPush(JavaKind.Object, new LoadThroughPatchNode(arg)); + return true; + } + }); - @MethodSubstitution - public static Object loadThroughPatch(Object obj) { - return LoadThroughPatchNode.load(obj); - } + r.register1("loadThroughCompressedPatch", Object.class, new InvocationPlugin() { + public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg) { + ValueNode compressed = b.add(CompressionNode.compress(arg, config().getOopEncoding())); + ValueNode patch = b.add(new LoadThroughPatchNode(compressed)); + b.addPush(JavaKind.Object, CompressionNode.uncompress(patch, config().getOopEncoding())); + return true; + } + }); - @MethodSubstitution - public static Object loadThroughCompressedPatch(Object obj) { - Object compressed = CompressionNode.compression(CompressionOp.Compress, obj, config().getOopEncoding()); - Object patch = LoadThroughPatchNode.load(compressed); - return CompressionNode.compression(CompressionOp.Uncompress, patch, config().getOopEncoding()); - } + return plugins; } @NodeInfo @@ -181,9 +182,6 @@ gen.append(new LoadThroughPatchOp(input.asConstant(), stamp() instanceof NarrowOopStamp, ret)); generator.setResult(this, ret); } - - @NodeIntrinsic - public static native Object load(Object obj); } private static final class LoadThroughPatchOp extends LIRInstruction {
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackendFactory.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackendFactory.java Tue Dec 08 12:30:15 2015 -0800 @@ -45,7 +45,6 @@ import com.oracle.graal.api.replacements.SnippetReflectionProvider; import com.oracle.graal.compiler.amd64.AMD64SuitesProvider; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins; import com.oracle.graal.hotspot.DefaultHotSpotGraalCompilerFactory; import com.oracle.graal.hotspot.HotSpotBackend; import com.oracle.graal.hotspot.HotSpotBackendFactory; @@ -63,9 +62,11 @@ import com.oracle.graal.hotspot.meta.HotSpotStampProvider; import com.oracle.graal.hotspot.meta.HotSpotSuitesProvider; import com.oracle.graal.hotspot.word.HotSpotWordTypes; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; import com.oracle.graal.phases.tiers.CompilerConfiguration; import com.oracle.graal.phases.util.Providers; import com.oracle.graal.replacements.amd64.AMD64GraphBuilderPlugins; +import com.oracle.graal.word.WordTypes; @ServiceProvider(HotSpotBackendFactory.class) public class AMD64HotSpotBackendFactory implements HotSpotBackendFactory { @@ -112,15 +113,15 @@ HotSpotStampProvider stampProvider = new HotSpotStampProvider(); Providers p = new Providers(metaAccess, codeCache, constantReflection, foreignCalls, lowerer, null, stampProvider); + try (InitTimer rt = timer("create WordTypes")) { + wordTypes = new HotSpotWordTypes(metaAccess, target.wordJavaKind); + } try (InitTimer rt = timer("create SnippetReflection provider")) { - snippetReflection = createSnippetReflection(graalRuntime, constantReflection); + snippetReflection = createSnippetReflection(graalRuntime, constantReflection, wordTypes); } try (InitTimer rt = timer("create Replacements provider")) { replacements = createReplacements(config, p, snippetReflection); } - try (InitTimer rt = timer("create WordTypes")) { - wordTypes = new HotSpotWordTypes(metaAccess, target.wordJavaKind); - } try (InitTimer rt = timer("create GraphBuilderPhase plugins")) { plugins = createGraphBuilderPlugins(config, target, constantReflection, foreignCalls, metaAccess, snippetReflection, replacements, wordTypes, stampProvider); replacements.setGraphBuilderPlugins(plugins); @@ -166,8 +167,8 @@ registers.getHeapBaseRegister())); } - protected HotSpotSnippetReflectionProvider createSnippetReflection(HotSpotGraalRuntimeProvider runtime, HotSpotConstantReflectionProvider constantReflection) { - return new HotSpotSnippetReflectionProvider(runtime, constantReflection); + protected HotSpotSnippetReflectionProvider createSnippetReflection(HotSpotGraalRuntimeProvider runtime, HotSpotConstantReflectionProvider constantReflection, WordTypes wordTypes) { + return new HotSpotSnippetReflectionProvider(runtime, constantReflection, wordTypes); } protected HotSpotLoweringProvider createLowerer(HotSpotGraalRuntimeProvider runtime, HotSpotMetaAccessProvider metaAccess, HotSpotForeignCallsProvider foreignCalls,
--- a/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotBackend.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotBackend.java Tue Dec 08 12:30:15 2015 -0800 @@ -324,11 +324,9 @@ RegisterConfig regConfig = frameMap.getRegisterConfig(); HotSpotVMConfig config = config(); Label unverifiedStub = installedCodeOwner == null || installedCodeOwner.isStatic() ? null : new Label(); - boolean hasUnsafeAccess = crb.compilationResult.hasUnsafeAccess(); - int i = 0; - do { + for (int i = 0; i < 2; i++) { if (i > 0) { - crb.reset(); + crb.resetForEmittingCode(); lir.resetLabels(); resetDelayedControlTransfers(lir); } @@ -358,9 +356,7 @@ // Emit code for the LIR crb.emit(lir); - } while (i++ < 1); - // Restore the unsafeAccess flag - crb.compilationResult.setHasUnsafeAccess(hasUnsafeAccess); + } profileInstructions(lir, crb); HotSpotFrameContext frameContext = (HotSpotFrameContext) crb.frameContext;
--- a/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotBackendFactory.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotBackendFactory.java Tue Dec 08 12:30:15 2015 -0800 @@ -42,7 +42,6 @@ import com.oracle.graal.compiler.sparc.SPARCAddressLowering; import com.oracle.graal.compiler.sparc.SPARCSuitesProvider; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins; import com.oracle.graal.hotspot.DefaultHotSpotGraalCompilerFactory; import com.oracle.graal.hotspot.HotSpotBackend; import com.oracle.graal.hotspot.HotSpotBackendFactory; @@ -59,6 +58,7 @@ import com.oracle.graal.hotspot.meta.HotSpotStampProvider; import com.oracle.graal.hotspot.meta.HotSpotSuitesProvider; import com.oracle.graal.hotspot.word.HotSpotWordTypes; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; import com.oracle.graal.nodes.spi.LoweringProvider; import com.oracle.graal.phases.tiers.CompilerConfiguration; import com.oracle.graal.phases.util.Providers; @@ -88,9 +88,9 @@ LoweringProvider lowerer = createLowerer(runtime, metaAccess, foreignCalls, registers, constantReflection, target); HotSpotStampProvider stampProvider = new HotSpotStampProvider(); Providers p = new Providers(metaAccess, codeCache, constantReflection, foreignCalls, lowerer, null, stampProvider); - HotSpotSnippetReflectionProvider snippetReflection = new HotSpotSnippetReflectionProvider(runtime, constantReflection); + HotSpotWordTypes wordTypes = new HotSpotWordTypes(metaAccess, target.wordJavaKind); + HotSpotSnippetReflectionProvider snippetReflection = new HotSpotSnippetReflectionProvider(runtime, constantReflection, wordTypes); HotSpotReplacementsImpl replacements = new HotSpotReplacementsImpl(p, snippetReflection, config, target); - HotSpotWordTypes wordTypes = new HotSpotWordTypes(metaAccess, target.wordJavaKind); Plugins plugins = createGraphBuilderPlugins(config, metaAccess, constantReflection, foreignCalls, stampProvider, snippetReflection, replacements, wordTypes); replacements.setGraphBuilderPlugins(plugins); HotSpotSuitesProvider suites = createSuites(config, runtime, compilerConfiguration, plugins, codeCache);
--- a/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotForeignCallsProvider.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotForeignCallsProvider.java Tue Dec 08 12:30:15 2015 -0800 @@ -31,6 +31,8 @@ import static com.oracle.graal.hotspot.HotSpotForeignCallLinkage.Transition.LEAF_NOFP; import static com.oracle.graal.hotspot.HotSpotHostBackend.DEOPTIMIZATION_HANDLER; import static com.oracle.graal.hotspot.HotSpotHostBackend.UNCOMMON_TRAP_HANDLER; +import static com.oracle.graal.hotspot.replacements.CRC32Substitutions.UPDATE_BYTES_CRC32; +import static jdk.vm.ci.code.CallingConvention.Type.NativeCall; import static jdk.vm.ci.meta.LocationIdentity.any; import static jdk.vm.ci.meta.Value.ILLEGAL; import static jdk.vm.ci.sparc.SPARC.i0; @@ -87,6 +89,11 @@ link(new SPARCUncommonTrapStub(providers, target, registerStubCall(UNCOMMON_TRAP_HANDLER, REEXECUTABLE, LEAF, NO_LOCATIONS), config)); } + if (config.useCRC32Intrinsics) { + // This stub does callee saving + registerForeignCall(UPDATE_BYTES_CRC32, config.updateBytesCRC32Stub, NativeCall, PRESERVES_REGISTERS, LEAF_NOFP, NOT_REEXECUTABLE, any()); + } + super.initialize(providers); }
--- a/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/DataPatchTest.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/DataPatchTest.java Tue Dec 08 12:30:15 2015 -0800 @@ -30,17 +30,12 @@ import org.junit.Before; import org.junit.Test; +import com.oracle.graal.api.directives.GraalDirectives; import com.oracle.graal.api.replacements.ClassSubstitution; import com.oracle.graal.api.replacements.MethodSubstitution; import com.oracle.graal.compiler.test.GraalCompilerTest; -import com.oracle.graal.graph.NodeClass; import com.oracle.graal.hotspot.nodes.CompressionNode; import com.oracle.graal.hotspot.nodes.CompressionNode.CompressionOp; -import com.oracle.graal.nodeinfo.NodeInfo; -import com.oracle.graal.nodes.ValueNode; -import com.oracle.graal.nodes.calc.FloatingNode; -import com.oracle.graal.nodes.spi.LIRLowerable; -import com.oracle.graal.nodes.spi.NodeLIRBuilderTool; public class DataPatchTest extends GraalCompilerTest { @@ -93,27 +88,8 @@ @MethodSubstitution public static Object compressUncompress(Object obj) { Object compressed = CompressionNode.compression(CompressionOp.Compress, obj, config.getOopEncoding()); - Object proxy = ConstantFoldBarrier.wrap(compressed); + Object proxy = GraalDirectives.opaque(compressed); return CompressionNode.compression(CompressionOp.Uncompress, proxy, config.getOopEncoding()); } } - - @NodeInfo - private static final class ConstantFoldBarrier extends FloatingNode implements LIRLowerable { - - public static final NodeClass<ConstantFoldBarrier> TYPE = NodeClass.create(ConstantFoldBarrier.class); - @Input protected ValueNode input; - - public ConstantFoldBarrier(ValueNode input) { - super(TYPE, input.stamp()); - this.input = input; - } - - public void generate(NodeLIRBuilderTool generator) { - generator.setResult(this, generator.operand(input)); - } - - @NodeIntrinsic - public static native Object wrap(Object object); - } }
--- a/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/ForeignCallDeoptimizeTest.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/ForeignCallDeoptimizeTest.java Tue Dec 08 12:30:15 2015 -0800 @@ -22,50 +22,42 @@ */ package com.oracle.graal.hotspot.test; +import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.ResolvedJavaMethod; + import org.junit.Test; -import com.oracle.graal.api.replacements.ClassSubstitution; -import com.oracle.graal.api.replacements.MethodSubstitution; -import com.oracle.graal.api.test.Graal; -import com.oracle.graal.compiler.common.spi.ForeignCallDescriptor; +import com.oracle.graal.compiler.common.spi.ForeignCallsProvider; import com.oracle.graal.compiler.test.GraalCompilerTest; -import com.oracle.graal.graph.Node.ConstantNodeParameter; -import com.oracle.graal.graph.Node.NodeIntrinsic; import com.oracle.graal.hotspot.meta.HotSpotForeignCallsProviderImpl; +import com.oracle.graal.hotspot.meta.HotSpotProviders; +import com.oracle.graal.nodes.ValueNode; import com.oracle.graal.nodes.extended.ForeignCallNode; -import com.oracle.graal.nodes.spi.Replacements; -import com.oracle.graal.runtime.RuntimeProvider; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderContext; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugin; /** * Tests that deoptimization upon exception handling works. */ public class ForeignCallDeoptimizeTest extends GraalCompilerTest { - private static boolean substitutionsInstalled; + @Override + protected Plugins getDefaultGraphBuilderPlugins() { + ForeignCallsProvider foreignCalls = ((HotSpotProviders) getProviders()).getForeignCalls(); - public ForeignCallDeoptimizeTest() { - if (!substitutionsInstalled) { - Replacements replacements = Graal.getRequiredCapability(RuntimeProvider.class).getHostBackend().getProviders().getReplacements(); - replacements.registerSubstitutions(ForeignCallDeoptimizeTest.class, Substitutions.class); - substitutionsInstalled = true; - } - } + Plugins ret = super.getDefaultGraphBuilderPlugins(); + ret.getInvocationPlugins().register(new InvocationPlugin() { - @ClassSubstitution(ForeignCallDeoptimizeTest.class) - static class Substitutions { - - @MethodSubstitution(isStatic = true) - static int testCallInt(int value) { - return testDeoptimizeCallInt(HotSpotForeignCallsProviderImpl.TEST_DEOPTIMIZE_CALL_INT, value); - } + public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg) { + ForeignCallNode node = new ForeignCallNode(foreignCalls, HotSpotForeignCallsProviderImpl.TEST_DEOPTIMIZE_CALL_INT, arg); + b.addPush(JavaKind.Int, node); + return true; + } + }, ForeignCallDeoptimizeTest.class, "testCallInt", int.class); + return ret; } - /** - * Exercise deoptimization inside of a non leaf runtime call. - */ - @NodeIntrinsic(ForeignCallNode.class) - static native int testDeoptimizeCallInt(@ConstantNodeParameter ForeignCallDescriptor descriptor, int value); - public static int testCallInt(int value) { return value; }
--- a/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/HotSpotCryptoSubstitutionTest.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/HotSpotCryptoSubstitutionTest.java Tue Dec 08 12:30:15 2015 -0800 @@ -22,7 +22,7 @@ */ package com.oracle.graal.hotspot.test; -import static com.oracle.graal.graphbuilderconf.IntrinsicContext.CompilationContext.ROOT_COMPILATION; +import static com.oracle.graal.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.ROOT_COMPILATION; import static jdk.vm.ci.hotspot.HotSpotVMConfig.config; import java.io.ByteArrayOutputStream; @@ -44,14 +44,14 @@ import org.junit.Assert; import org.junit.Test; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins; -import com.oracle.graal.graphbuilderconf.IntrinsicContext; import com.oracle.graal.hotspot.meta.HotSpotGraphBuilderPlugins; import com.oracle.graal.hotspot.meta.HotSpotProviders; import com.oracle.graal.java.GraphBuilderPhase; import com.oracle.graal.nodes.StructuredGraph; import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration; +import com.oracle.graal.nodes.graphbuilderconf.IntrinsicContext; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; import com.oracle.graal.phases.OptimisticOptimizations; /**
--- a/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/JVMCIErrorTest.java Tue Dec 08 12:29:24 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,175 +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.graal.hotspot.test; - -import java.util.function.Consumer; - -import jdk.vm.ci.code.CompilationResult; -import jdk.vm.ci.code.CompilationResult.ConstantReference; -import jdk.vm.ci.code.CompilationResult.DataPatch; -import jdk.vm.ci.code.CompilationResult.DataSectionReference; -import jdk.vm.ci.code.CompilationResult.Infopoint; -import jdk.vm.ci.code.CompilationResult.Reference; -import jdk.vm.ci.code.DataSection.Data; -import jdk.vm.ci.code.DataSection.DataBuilder; -import jdk.vm.ci.code.InfopointReason; -import jdk.vm.ci.common.JVMCIError; -import jdk.vm.ci.hotspot.HotSpotConstant; -import jdk.vm.ci.meta.Assumptions.Assumption; -import jdk.vm.ci.meta.ResolvedJavaMethod; -import jdk.vm.ci.meta.VMConstant; - -import org.junit.Test; - -import com.oracle.graal.compiler.test.GraalCompilerTest; - -public class JVMCIErrorTest extends GraalCompilerTest { - - public static void testMethod() { - } - - private void test(Consumer<CompilationResult> modify) { - ResolvedJavaMethod method = getResolvedJavaMethod("testMethod"); - CompilationResult compResult = compile(method, null); - - modify.accept(compResult); - - getCodeCache().addCode(method, compResult, null, null); - } - - @Test(expected = JVMCIError.class) - public void testInvalidAssumption() { - test(r -> r.setAssumptions(new Assumption[]{new InvalidAssumption()})); - } - - private static class InvalidAssumption extends Assumption { - } - - @Test(expected = JVMCIError.class) - public void testInvalidAlignment() { - test(r -> r.getDataSection().insertData(new Data(7, 1, DataBuilder.zero(1)))); - } - - @Test(expected = JVMCIError.class) - public void testInvalidDataSectionReference() { - test(r -> { - DataSectionReference ref = r.getDataSection().insertData(new Data(1, 1, DataBuilder.zero(1))); - Data data = new Data(1, 1, (buffer, patch) -> { - patch.accept(new DataPatch(buffer.position(), ref)); - buffer.put((byte) 0); - }); - r.getDataSection().insertData(data); - }); - } - - @Test(expected = JVMCIError.class) - public void testInvalidNarrowMethodInDataSection() { - test(r -> { - ResolvedJavaMethod method = getResolvedJavaMethod("testMethod"); - HotSpotConstant c = (HotSpotConstant) method.getEncoding(); - Data data = new Data(4, 4, (buffer, patch) -> { - patch.accept(new DataPatch(buffer.position(), new ConstantReference((VMConstant) c.compress()))); - buffer.putInt(0); - }); - r.getDataSection().insertData(data); - }); - } - - @Test(expected = JVMCIError.class) - public void testInvalidConstantInDataSection() { - test(r -> { - Data data = new Data(1, 1, (buffer, patch) -> { - patch.accept(new DataPatch(buffer.position(), new ConstantReference(new InvalidVMConstant()))); - }); - r.getDataSection().insertData(data); - }); - } - - @Test(expected = JVMCIError.class) - public void testInvalidConstantInCode() { - test(r -> r.recordDataPatch(0, new ConstantReference(new InvalidVMConstant()))); - } - - private static class InvalidVMConstant implements VMConstant { - - public boolean isDefaultForKind() { - return false; - } - - public String toValueString() { - return null; - } - } - - @Test(expected = JVMCIError.class) - public void testInvalidReference() { - test(r -> r.recordDataPatch(0, new InvalidReference())); - } - - private static class InvalidReference extends Reference { - - @Override - public int hashCode() { - return 0; - } - - @Override - public boolean equals(Object obj) { - return false; - } - } - - @Test(expected = JVMCIError.class) - public void testOutOfBoundsDataSectionReference() { - test(r -> { - DataSectionReference ref = new DataSectionReference(); - ref.setOffset(0x1000); - r.recordDataPatch(0, ref); - }); - } - - @Test(expected = JVMCIError.class) - public void testInvalidMark() { - test(r -> r.recordMark(0, new Object())); - } - - @Test(expected = JVMCIError.class) - public void testInvalidMarkInt() { - test(r -> r.recordMark(0, -1)); - } - - @Test(expected = JVMCIError.class) - public void testUnknownInfopointReason() { - test(r -> r.addInfopoint(new Infopoint(0, null, InfopointReason.UNKNOWN))); - } - - @Test(expected = JVMCIError.class) - public void testInfopointMissingDebugInfo() { - test(r -> r.addInfopoint(new Infopoint(0, null, InfopointReason.METHOD_START))); - } - - @Test(expected = JVMCIError.class) - public void testSafepointMissingDebugInfo() { - test(r -> r.addInfopoint(new Infopoint(0, null, InfopointReason.SAFEPOINT))); - } -}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotForeignCallLinkage.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotForeignCallLinkage.java Tue Dec 08 12:30:15 2015 -0800 @@ -77,7 +77,7 @@ * in the called function. That is, {@code JavaFrameAnchor} management code around the call * is required. */ - NOT_LEAF; + SAFEPOINT, } /** @@ -115,4 +115,9 @@ * Gets the VM symbol associated with the target {@linkplain #getAddress() address} of the call. */ String getSymbol(); + + /** + * Identifies foreign calls which are guaranteed to include a safepoint check. + */ + boolean isGuaranteedSafepoint(); }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotForeignCallLinkageImpl.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotForeignCallLinkageImpl.java Tue Dec 08 12:30:15 2015 -0800 @@ -172,6 +172,10 @@ return reexecutable; } + public boolean isGuaranteedSafepoint() { + return transition == Transition.SAFEPOINT; + } + public LocationIdentity[] getKilledLocations() { return killedLocations; } @@ -241,7 +245,7 @@ @Override public boolean needsDebugInfo() { - return transition == Transition.NOT_LEAF; + return transition == Transition.SAFEPOINT; } public boolean mayContainFP() { @@ -249,7 +253,7 @@ } public boolean needsJavaFrameAnchor() { - if (transition == Transition.NOT_LEAF || transition == Transition.STACK_INSPECTABLE_LEAF) { + if (transition == Transition.SAFEPOINT || transition == Transition.STACK_INSPECTABLE_LEAF) { if (stub != null) { // The stub will do the JavaFrameAnchor management // around the runtime call(s) it makes
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotGraalCompiler.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotGraalCompiler.java Tue Dec 08 12:30:15 2015 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -23,7 +23,7 @@ package com.oracle.graal.hotspot; import static com.oracle.graal.compiler.common.GraalOptions.OptAssumptions; -import static com.oracle.graal.graphbuilderconf.IntrinsicContext.CompilationContext.ROOT_COMPILATION; +import static com.oracle.graal.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.ROOT_COMPILATION; import static jdk.vm.ci.code.CallingConvention.Type.JavaCallee; import static jdk.vm.ci.code.CodeUtil.getCallingConvention; import jdk.vm.ci.code.CallingConvention; @@ -47,9 +47,6 @@ import com.oracle.graal.debug.TTY; import com.oracle.graal.debug.TopLevelDebugConfig; import com.oracle.graal.debug.internal.DebugScope; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins; -import com.oracle.graal.graphbuilderconf.IntrinsicContext; import com.oracle.graal.hotspot.meta.HotSpotProviders; import com.oracle.graal.hotspot.meta.HotSpotSuitesProvider; import com.oracle.graal.hotspot.phases.OnStackReplacementPhase; @@ -58,6 +55,9 @@ import com.oracle.graal.lir.phases.LIRSuites; import com.oracle.graal.nodes.StructuredGraph; import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration; +import com.oracle.graal.nodes.graphbuilderconf.IntrinsicContext; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; import com.oracle.graal.nodes.spi.Replacements; import com.oracle.graal.phases.OptimisticOptimizations; import com.oracle.graal.phases.OptimisticOptimizations.Optimization; @@ -137,10 +137,10 @@ // all code after the OSR loop is never executed. optimisticOpts.remove(Optimization.RemoveNeverExecutedCode); } - CompilationResult result = GraalCompiler.compileGraph(graph, cc, method, providers, backend, getGraphBuilderSuite(providers, isOSR), optimisticOpts, profilingInfo, suites, lirSuites, - new CompilationResult(), CompilationResultBuilderFactory.Default); - + CompilationResult result = new CompilationResult(); result.setEntryBCI(entryBCI); + GraalCompiler.compileGraph(graph, cc, method, providers, backend, getGraphBuilderSuite(providers, isOSR), optimisticOpts, profilingInfo, suites, lirSuites, result, + CompilationResultBuilderFactory.Default); if (!isOSR) { ProfilingInfo profile = method.getProfilingInfo();
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotInstructionProfiling.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotInstructionProfiling.java Tue Dec 08 12:30:15 2015 -0800 @@ -54,7 +54,8 @@ @Override protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, - BenchmarkCounterFactory counterFactory) { + PostAllocationOptimizationContext context) { + BenchmarkCounterFactory counterFactory = context.counterFactory; new Analyzer(target, lirGenRes.getCompilationUnitName(), lirGenRes.getLIR(), counterFactory).run(); }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/DefaultHotSpotLoweringProvider.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/DefaultHotSpotLoweringProvider.java Tue Dec 08 12:30:15 2015 -0800 @@ -63,6 +63,7 @@ import com.oracle.graal.graph.NodeInputList; import com.oracle.graal.hotspot.HotSpotGraalRuntimeProvider; import com.oracle.graal.hotspot.nodes.CompressionNode; +import com.oracle.graal.hotspot.nodes.CompressionNode.CompressionOp; import com.oracle.graal.hotspot.nodes.ComputeObjectAddressNode; import com.oracle.graal.hotspot.nodes.G1ArrayRangePostWriteBarrier; import com.oracle.graal.hotspot.nodes.G1ArrayRangePreWriteBarrier; @@ -101,6 +102,7 @@ import com.oracle.graal.nodes.Invoke; import com.oracle.graal.nodes.LoweredCallTargetNode; import com.oracle.graal.nodes.ParameterNode; +import com.oracle.graal.nodes.PiNode; import com.oracle.graal.nodes.SafepointNode; import com.oracle.graal.nodes.StartNode; import com.oracle.graal.nodes.StructuredGraph; @@ -362,10 +364,11 @@ MethodCallTargetNode callTarget = (MethodCallTargetNode) invoke.callTarget(); NodeInputList<ValueNode> parameters = callTarget.arguments(); ValueNode receiver = parameters.size() <= 0 ? null : parameters.get(0); - GuardingNode receiverNullCheck = null; if (!callTarget.isStatic() && receiver.stamp() instanceof ObjectStamp && !StampTool.isPointerNonNull(receiver)) { - receiverNullCheck = createNullCheck(receiver, invoke.asNode(), tool); - invoke.setGuard(receiverNullCheck); + GuardingNode receiverNullCheck = createNullCheck(receiver, invoke.asNode(), tool); + PiNode nonNullReceiver = graph.unique(new PiNode(receiver, ((ObjectStamp) receiver.stamp()).join(StampFactory.objectNonNull()), (ValueNode) receiverNullCheck)); + parameters.set(0, nonNullReceiver); + receiver = nonNullReceiver; } JavaType[] signature = callTarget.targetMethod().getSignature().toParameterTypes(callTarget.isStatic() ? null : callTarget.targetMethod().getDeclaringClass()); @@ -375,7 +378,7 @@ ResolvedJavaType receiverType = invoke.getReceiverType(); if (hsMethod.isInVirtualMethodTable(receiverType)) { JavaKind wordKind = runtime.getTarget().wordJavaKind; - ValueNode hub = createReadHub(graph, receiver, receiverNullCheck, tool); + ValueNode hub = createReadHub(graph, receiver, tool); ReadNode metaspaceMethod = createReadVirtualMethod(graph, hub, hsMethod, receiverType); // We use LocationNode.ANY_LOCATION for the reads that access the @@ -410,11 +413,11 @@ } @Override - protected ValueNode implicitLoadConvert(StructuredGraph graph, JavaKind kind, ValueNode value, boolean compressible) { + protected ValueNode implicitLoadConvert(JavaKind kind, ValueNode value, boolean compressible) { if (kind == JavaKind.Object && compressible && config().useCompressedOops) { - return CompressionNode.uncompress(value, config().getOopEncoding()); + return new CompressionNode(CompressionOp.Uncompress, value, config().getOopEncoding()); } - return super.implicitLoadConvert(graph, kind, value, compressible); + return super.implicitLoadConvert(kind, value, compressible); } @Override @@ -425,11 +428,11 @@ } @Override - protected ValueNode implicitStoreConvert(StructuredGraph graph, JavaKind kind, ValueNode value, boolean compressible) { + protected ValueNode implicitStoreConvert(JavaKind kind, ValueNode value, boolean compressible) { if (kind == JavaKind.Object && compressible && config().useCompressedOops) { - return CompressionNode.compress(value, config().getOopEncoding()); + return new CompressionNode(CompressionOp.Compress, value, config().getOopEncoding()); } - return super.implicitStoreConvert(graph, kind, value, compressible); + return super.implicitStoreConvert(kind, value, compressible); } @Override @@ -584,9 +587,9 @@ } @Override - protected ValueNode createReadHub(StructuredGraph graph, ValueNode object, GuardingNode guard, LoweringTool tool) { + protected ValueNode createReadHub(StructuredGraph graph, ValueNode object, LoweringTool tool) { if (tool.getLoweringStage() != LoweringTool.StandardLoweringStage.LOW_TIER) { - return graph.unique(new LoadHubNode(tool.getStampProvider(), object, guard != null ? guard.asNode() : null)); + return graph.unique(new LoadHubNode(tool.getStampProvider(), object)); } assert !object.isConstant() || object.isNullConstant(); @@ -596,7 +599,7 @@ } AddressNode address = createOffsetAddress(graph, object, config().hubOffset); - FloatingReadNode memoryRead = graph.unique(new FloatingReadNode(address, HUB_LOCATION, null, hubStamp, guard, BarrierType.NONE)); + FloatingReadNode memoryRead = graph.unique(new FloatingReadNode(address, HUB_LOCATION, null, hubStamp, null, BarrierType.NONE)); if (config().useCompressedClassPointers) { return CompressionNode.uncompress(memoryRead, config().getKlassEncoding()); } else {
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotForeignCallsProviderImpl.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotForeignCallsProviderImpl.java Tue Dec 08 12:30:15 2015 -0800 @@ -23,7 +23,7 @@ package com.oracle.graal.hotspot.meta; import static com.oracle.graal.hotspot.HotSpotForeignCallLinkage.RegisterEffect.PRESERVES_REGISTERS; -import static com.oracle.graal.hotspot.HotSpotForeignCallLinkage.Transition.NOT_LEAF; +import static com.oracle.graal.hotspot.HotSpotForeignCallLinkage.Transition.SAFEPOINT; import static jdk.vm.ci.code.CallingConvention.Type.JavaCall; import static jdk.vm.ci.code.CallingConvention.Type.JavaCallee; @@ -113,7 +113,7 @@ boolean reexecutable, LocationIdentity... killedLocations) { Class<?> resultType = descriptor.getResultType(); assert address != 0; - assert transition != NOT_LEAF || resultType.isPrimitive() || Word.class.isAssignableFrom(resultType) : "non-leaf foreign calls must return objects in thread local storage: " + descriptor; + assert transition != SAFEPOINT || resultType.isPrimitive() || Word.class.isAssignableFrom(resultType) : "non-leaf foreign calls must return objects in thread local storage: " + descriptor; return register(HotSpotForeignCallLinkageImpl.create(metaAccess, codeCache, this, descriptor, address, effect, outgoingCcType, null, transition, reexecutable, killedLocations)); } @@ -166,6 +166,11 @@ return foreignCalls.get(descriptor).needsDebugInfo(); } + public boolean isGuaranteedSafepoint(ForeignCallDescriptor descriptor) { + assert foreignCalls.containsKey(descriptor) : "unknown foreign call: " + descriptor; + return foreignCalls.get(descriptor).isGuaranteedSafepoint(); + } + public LocationIdentity[] getKilledLocations(ForeignCallDescriptor descriptor) { assert foreignCalls.containsKey(descriptor) : "unknown foreign call: " + descriptor; return foreignCalls.get(descriptor).getKilledLocations();
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotGraphBuilderPlugins.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotGraphBuilderPlugins.java Tue Dec 08 12:30:15 2015 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -39,18 +39,11 @@ import jdk.vm.ci.meta.MetaAccessProvider; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.options.StableOptionValue; +import jdk.vm.ci.service.Services; import sun.reflect.Reflection; import com.oracle.graal.api.replacements.SnippetReflectionProvider; import com.oracle.graal.compiler.common.spi.ForeignCallsProvider; -import com.oracle.graal.graphbuilderconf.ForeignCallPlugin; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins; -import com.oracle.graal.graphbuilderconf.GraphBuilderContext; -import com.oracle.graal.graphbuilderconf.InvocationPlugin; -import com.oracle.graal.graphbuilderconf.InvocationPlugin.Receiver; -import com.oracle.graal.graphbuilderconf.InvocationPlugins; -import com.oracle.graal.graphbuilderconf.InvocationPlugins.Registration; -import com.oracle.graal.graphbuilderconf.MethodSubstitutionPlugin; import com.oracle.graal.hotspot.nodes.ClassCastNode; import com.oracle.graal.hotspot.nodes.CurrentJavaThreadNode; import com.oracle.graal.hotspot.replacements.AESCryptSubstitutions; @@ -68,6 +61,15 @@ import com.oracle.graal.nodes.ConstantNode; import com.oracle.graal.nodes.PiNode; import com.oracle.graal.nodes.ValueNode; +import com.oracle.graal.nodes.graphbuilderconf.ForeignCallPlugin; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderContext; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugin; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugin.Receiver; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugins; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugins.Registration; +import com.oracle.graal.nodes.graphbuilderconf.MethodSubstitutionPlugin; +import com.oracle.graal.nodes.graphbuilderconf.NodeIntrinsicPluginFactory; import com.oracle.graal.nodes.memory.HeapAccess.BarrierType; import com.oracle.graal.nodes.memory.address.AddressNode; import com.oracle.graal.nodes.memory.address.OffsetAddressNode; @@ -75,7 +77,7 @@ import com.oracle.graal.nodes.util.GraphUtil; import com.oracle.graal.replacements.InlineDuringParsingPlugin; import com.oracle.graal.replacements.MethodHandlePlugin; -import com.oracle.graal.replacements.NodeIntrinsificationPhase; +import com.oracle.graal.replacements.NodeIntrinsificationProvider; import com.oracle.graal.replacements.NodeIntrinsificationPlugin; import com.oracle.graal.replacements.ReplacementsImpl; import com.oracle.graal.replacements.StandardGraphBuilderPlugins; @@ -100,8 +102,8 @@ InvocationPlugins invocationPlugins = new HotSpotInvocationPlugins(config, metaAccess); Plugins plugins = new Plugins(invocationPlugins); - NodeIntrinsificationPhase nodeIntrinsificationPhase = new NodeIntrinsificationPhase(metaAccess, constantReflection, snippetReflection, foreignCalls, stampProvider); - NodeIntrinsificationPlugin nodeIntrinsificationPlugin = new NodeIntrinsificationPlugin(metaAccess, nodeIntrinsificationPhase, wordTypes, false); + NodeIntrinsificationProvider nodeIntrinsificationProvider = new NodeIntrinsificationProvider(metaAccess, snippetReflection, foreignCalls, wordTypes); + NodeIntrinsificationPlugin nodeIntrinsificationPlugin = new NodeIntrinsificationPlugin(); HotSpotWordOperationPlugin wordOperationPlugin = new HotSpotWordOperationPlugin(snippetReflection, wordTypes); HotSpotNodePlugin nodePlugin = new HotSpotNodePlugin(wordOperationPlugin, nodeIntrinsificationPlugin); @@ -114,17 +116,26 @@ plugins.appendInlineInvokePlugin(new InlineDuringParsingPlugin()); } - registerObjectPlugins(invocationPlugins); - registerClassPlugins(plugins); - registerSystemPlugins(invocationPlugins, foreignCalls); - registerThreadPlugins(invocationPlugins, metaAccess, wordTypes, config); - registerCallSitePlugins(invocationPlugins); - registerReflectionPlugins(invocationPlugins); - registerStableOptionPlugins(invocationPlugins, snippetReflection); - registerAESPlugins(invocationPlugins, config); - registerCRC32Plugins(invocationPlugins, config); - StandardGraphBuilderPlugins.registerInvocationPlugins(metaAccess, invocationPlugins, true); + invocationPlugins.defer(new Runnable() { + public void run() { + registerObjectPlugins(invocationPlugins); + registerClassPlugins(plugins); + registerSystemPlugins(invocationPlugins, foreignCalls); + registerThreadPlugins(invocationPlugins, metaAccess, wordTypes, config); + registerCallSitePlugins(invocationPlugins); + registerReflectionPlugins(invocationPlugins); + registerStableOptionPlugins(invocationPlugins, snippetReflection); + registerAESPlugins(invocationPlugins, config); + registerCRC32Plugins(invocationPlugins, config); + StandardGraphBuilderPlugins.registerInvocationPlugins(metaAccess, invocationPlugins, true); + + for (NodeIntrinsicPluginFactory factory : Services.load(NodeIntrinsicPluginFactory.class)) { + factory.registerPlugin(invocationPlugins, nodeIntrinsificationProvider); + } + + } + }); return plugins; }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotHostForeignCallsProvider.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotHostForeignCallsProvider.java Tue Dec 08 12:30:15 2015 -0800 @@ -50,7 +50,7 @@ import static com.oracle.graal.hotspot.HotSpotForeignCallLinkage.RegisterEffect.PRESERVES_REGISTERS; import static com.oracle.graal.hotspot.HotSpotForeignCallLinkage.Transition.LEAF; import static com.oracle.graal.hotspot.HotSpotForeignCallLinkage.Transition.LEAF_NOFP; -import static com.oracle.graal.hotspot.HotSpotForeignCallLinkage.Transition.NOT_LEAF; +import static com.oracle.graal.hotspot.HotSpotForeignCallLinkage.Transition.SAFEPOINT; import static com.oracle.graal.hotspot.HotSpotForeignCallLinkage.Transition.STACK_INSPECTABLE_LEAF; import static com.oracle.graal.hotspot.HotSpotHostBackend.DEOPTIMIZATION_HANDLER; import static com.oracle.graal.hotspot.HotSpotHostBackend.UNCOMMON_TRAP_HANDLER; @@ -234,12 +234,12 @@ registerForeignCall(ARITHMETIC_POW, c.arithmeticPowAddress, NativeCall, DESTROYS_REGISTERS, LEAF, REEXECUTABLE, NO_LOCATIONS); registerForeignCall(LOAD_AND_CLEAR_EXCEPTION, c.loadAndClearExceptionAddress, NativeCall, DESTROYS_REGISTERS, LEAF_NOFP, NOT_REEXECUTABLE, any()); - registerForeignCall(EXCEPTION_HANDLER_FOR_PC, c.exceptionHandlerForPcAddress, NativeCall, DESTROYS_REGISTERS, NOT_LEAF, REEXECUTABLE, any()); - registerForeignCall(EXCEPTION_HANDLER_FOR_RETURN_ADDRESS, c.exceptionHandlerForReturnAddressAddress, NativeCall, DESTROYS_REGISTERS, NOT_LEAF, REEXECUTABLE, any()); - registerForeignCall(FETCH_UNROLL_INFO, c.deoptimizationFetchUnrollInfo, NativeCall, DESTROYS_REGISTERS, NOT_LEAF, REEXECUTABLE, any()); - registerForeignCall(NEW_ARRAY_C, c.newArrayAddress, NativeCall, DESTROYS_REGISTERS, NOT_LEAF, REEXECUTABLE, any()); - registerForeignCall(NEW_INSTANCE_C, c.newInstanceAddress, NativeCall, DESTROYS_REGISTERS, NOT_LEAF, REEXECUTABLE, any()); - registerForeignCall(UNCOMMON_TRAP, c.deoptimizationUncommonTrap, NativeCall, DESTROYS_REGISTERS, NOT_LEAF, NOT_REEXECUTABLE, any()); + registerForeignCall(EXCEPTION_HANDLER_FOR_PC, c.exceptionHandlerForPcAddress, NativeCall, DESTROYS_REGISTERS, SAFEPOINT, REEXECUTABLE, any()); + registerForeignCall(EXCEPTION_HANDLER_FOR_RETURN_ADDRESS, c.exceptionHandlerForReturnAddressAddress, NativeCall, DESTROYS_REGISTERS, SAFEPOINT, REEXECUTABLE, any()); + registerForeignCall(FETCH_UNROLL_INFO, c.deoptimizationFetchUnrollInfo, NativeCall, DESTROYS_REGISTERS, SAFEPOINT, REEXECUTABLE, any()); + registerForeignCall(NEW_ARRAY_C, c.newArrayAddress, NativeCall, DESTROYS_REGISTERS, SAFEPOINT, REEXECUTABLE, any()); + registerForeignCall(NEW_INSTANCE_C, c.newInstanceAddress, NativeCall, DESTROYS_REGISTERS, SAFEPOINT, REEXECUTABLE, any()); + registerForeignCall(UNCOMMON_TRAP, c.deoptimizationUncommonTrap, NativeCall, DESTROYS_REGISTERS, SAFEPOINT, NOT_REEXECUTABLE, any()); /* * We cannot use LEAF_SP here because on some architectures we have to align the stack @@ -252,24 +252,24 @@ * This message call is registered twice, where the second one must only be used for calls * that do not return, i.e., that exit the VM. */ - registerForeignCall(VM_MESSAGE_C, c.vmMessageAddress, NativeCall, DESTROYS_REGISTERS, NOT_LEAF, REEXECUTABLE, NO_LOCATIONS); + registerForeignCall(VM_MESSAGE_C, c.vmMessageAddress, NativeCall, DESTROYS_REGISTERS, SAFEPOINT, REEXECUTABLE, NO_LOCATIONS); registerForeignCall(ASSERTION_VM_MESSAGE_C, c.vmMessageAddress, NativeCall, PRESERVES_REGISTERS, LEAF, REEXECUTABLE, NO_LOCATIONS); - link(new NewInstanceStub(providers, registerStubCall(NEW_INSTANCE, REEXECUTABLE, NOT_LEAF, INIT_LOCATION, TLAB_TOP_LOCATION, TLAB_END_LOCATION))); - link(new NewArrayStub(providers, registerStubCall(NEW_ARRAY, REEXECUTABLE, NOT_LEAF, INIT_LOCATION, TLAB_TOP_LOCATION, TLAB_END_LOCATION))); + link(new NewInstanceStub(providers, registerStubCall(NEW_INSTANCE, REEXECUTABLE, SAFEPOINT, INIT_LOCATION, TLAB_TOP_LOCATION, TLAB_END_LOCATION))); + link(new NewArrayStub(providers, registerStubCall(NEW_ARRAY, REEXECUTABLE, SAFEPOINT, INIT_LOCATION, TLAB_TOP_LOCATION, TLAB_END_LOCATION))); link(new ExceptionHandlerStub(providers, foreignCalls.get(EXCEPTION_HANDLER))); - link(new UnwindExceptionToCallerStub(providers, registerStubCall(UNWIND_EXCEPTION_TO_CALLER, NOT_REEXECUTABLE, NOT_LEAF, any()))); + link(new UnwindExceptionToCallerStub(providers, registerStubCall(UNWIND_EXCEPTION_TO_CALLER, NOT_REEXECUTABLE, SAFEPOINT, any()))); link(new VerifyOopStub(providers, registerStubCall(VERIFY_OOP, REEXECUTABLE, LEAF_NOFP, NO_LOCATIONS))); - linkForeignCall(providers, IDENTITY_HASHCODE, c.identityHashCodeAddress, PREPEND_THREAD, NOT_LEAF, NOT_REEXECUTABLE, MARK_WORD_LOCATION); - linkForeignCall(providers, REGISTER_FINALIZER, c.registerFinalizerAddress, PREPEND_THREAD, NOT_LEAF, NOT_REEXECUTABLE, any()); - linkForeignCall(providers, CREATE_NULL_POINTER_EXCEPTION, c.createNullPointerExceptionAddress, PREPEND_THREAD, NOT_LEAF, REEXECUTABLE, any()); - linkForeignCall(providers, CREATE_OUT_OF_BOUNDS_EXCEPTION, c.createOutOfBoundsExceptionAddress, PREPEND_THREAD, NOT_LEAF, REEXECUTABLE, any()); - linkForeignCall(providers, MONITORENTER, c.monitorenterAddress, PREPEND_THREAD, NOT_LEAF, NOT_REEXECUTABLE, any()); + linkForeignCall(providers, IDENTITY_HASHCODE, c.identityHashCodeAddress, PREPEND_THREAD, SAFEPOINT, NOT_REEXECUTABLE, MARK_WORD_LOCATION); + linkForeignCall(providers, REGISTER_FINALIZER, c.registerFinalizerAddress, PREPEND_THREAD, SAFEPOINT, NOT_REEXECUTABLE, any()); + linkForeignCall(providers, CREATE_NULL_POINTER_EXCEPTION, c.createNullPointerExceptionAddress, PREPEND_THREAD, SAFEPOINT, REEXECUTABLE, any()); + linkForeignCall(providers, CREATE_OUT_OF_BOUNDS_EXCEPTION, c.createOutOfBoundsExceptionAddress, PREPEND_THREAD, SAFEPOINT, REEXECUTABLE, any()); + linkForeignCall(providers, MONITORENTER, c.monitorenterAddress, PREPEND_THREAD, SAFEPOINT, NOT_REEXECUTABLE, any()); linkForeignCall(providers, MONITOREXIT, c.monitorexitAddress, PREPEND_THREAD, STACK_INSPECTABLE_LEAF, NOT_REEXECUTABLE, any()); - linkForeignCall(providers, NEW_MULTI_ARRAY, c.newMultiArrayAddress, PREPEND_THREAD, NOT_LEAF, REEXECUTABLE, INIT_LOCATION, TLAB_TOP_LOCATION, TLAB_END_LOCATION); - linkForeignCall(providers, DYNAMIC_NEW_ARRAY, c.dynamicNewArrayAddress, PREPEND_THREAD, NOT_LEAF, REEXECUTABLE, INIT_LOCATION); - linkForeignCall(providers, DYNAMIC_NEW_INSTANCE, c.dynamicNewInstanceAddress, PREPEND_THREAD, NOT_LEAF, REEXECUTABLE, INIT_LOCATION); + linkForeignCall(providers, NEW_MULTI_ARRAY, c.newMultiArrayAddress, PREPEND_THREAD, SAFEPOINT, REEXECUTABLE, INIT_LOCATION, TLAB_TOP_LOCATION, TLAB_END_LOCATION); + linkForeignCall(providers, DYNAMIC_NEW_ARRAY, c.dynamicNewArrayAddress, PREPEND_THREAD, SAFEPOINT, REEXECUTABLE, INIT_LOCATION); + linkForeignCall(providers, DYNAMIC_NEW_INSTANCE, c.dynamicNewInstanceAddress, PREPEND_THREAD, SAFEPOINT, REEXECUTABLE, INIT_LOCATION); 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); @@ -280,9 +280,9 @@ linkForeignCall(providers, VALIDATE_OBJECT, c.validateObject, PREPEND_THREAD, LEAF_NOFP, REEXECUTABLE, NO_LOCATIONS); // 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, THREAD_IS_INTERRUPTED, c.threadIsInterruptedAddress, PREPEND_THREAD, SAFEPOINT, NOT_REEXECUTABLE, any()); - linkForeignCall(providers, TEST_DEOPTIMIZE_CALL_INT, c.testDeoptimizeCallInt, PREPEND_THREAD, NOT_LEAF, REEXECUTABLE, any()); + linkForeignCall(providers, TEST_DEOPTIMIZE_CALL_INT, c.testDeoptimizeCallInt, PREPEND_THREAD, SAFEPOINT, REEXECUTABLE, any()); registerArrayCopy(JavaKind.Byte, c.jbyteArraycopy, c.jbyteAlignedArraycopy, c.jbyteDisjointArraycopy, c.jbyteAlignedDisjointArraycopy); registerArrayCopy(JavaKind.Boolean, c.jbyteArraycopy, c.jbyteAlignedArraycopy, c.jbyteDisjointArraycopy, c.jbyteAlignedDisjointArraycopy);
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotInvocationPlugins.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotInvocationPlugins.java Tue Dec 08 12:30:15 2015 -0800 @@ -30,12 +30,12 @@ import com.oracle.graal.compiler.common.GraalOptions; import com.oracle.graal.graph.Node; import com.oracle.graal.graph.iterators.NodeIterable; -import com.oracle.graal.graphbuilderconf.GraphBuilderContext; -import com.oracle.graal.graphbuilderconf.InvocationPlugin; -import com.oracle.graal.graphbuilderconf.InvocationPlugins; import com.oracle.graal.hotspot.phases.AheadOfTimeVerificationPhase; import com.oracle.graal.nodes.ConstantNode; import com.oracle.graal.nodes.FrameState; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderContext; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugin; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugins; import com.oracle.graal.nodes.type.StampTool; import com.oracle.graal.replacements.nodes.MacroNode;
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotNodePlugin.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotNodePlugin.java Tue Dec 08 12:30:15 2015 -0800 @@ -34,13 +34,13 @@ import com.oracle.graal.api.replacements.Fold; import com.oracle.graal.compiler.common.type.Stamp; import com.oracle.graal.graph.Node.NodeIntrinsic; -import com.oracle.graal.graphbuilderconf.GraphBuilderContext; -import com.oracle.graal.graphbuilderconf.InlineInvokePlugin; -import com.oracle.graal.graphbuilderconf.NodePlugin; -import com.oracle.graal.graphbuilderconf.ParameterPlugin; import com.oracle.graal.nodes.ConstantNode; import com.oracle.graal.nodes.ValueNode; import com.oracle.graal.nodes.calc.FloatingNode; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderContext; +import com.oracle.graal.nodes.graphbuilderconf.InlineInvokePlugin; +import com.oracle.graal.nodes.graphbuilderconf.NodePlugin; +import com.oracle.graal.nodes.graphbuilderconf.ParameterPlugin; import com.oracle.graal.replacements.NodeIntrinsificationPlugin; import com.oracle.graal.replacements.WordOperationPlugin; import com.oracle.graal.word.Word;
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotProviders.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotProviders.java Tue Dec 08 12:30:15 2015 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014, 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 @@ -27,8 +27,8 @@ import jdk.vm.ci.meta.MetaAccessProvider; import com.oracle.graal.api.replacements.SnippetReflectionProvider; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins; import com.oracle.graal.hotspot.word.HotSpotWordTypes; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; import com.oracle.graal.nodes.spi.LoweringProvider; import com.oracle.graal.nodes.spi.Replacements; import com.oracle.graal.phases.tiers.SuitesProvider;
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotSnippetReflectionProvider.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotSnippetReflectionProvider.java Tue Dec 08 12:30:15 2015 -0800 @@ -28,20 +28,22 @@ import jdk.vm.ci.hotspot.HotSpotVMConfig; import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; -import jdk.vm.ci.meta.MetaAccessProvider; import jdk.vm.ci.meta.ResolvedJavaType; import com.oracle.graal.api.replacements.SnippetReflectionProvider; import com.oracle.graal.hotspot.HotSpotGraalRuntimeProvider; +import com.oracle.graal.word.WordTypes; public class HotSpotSnippetReflectionProvider implements SnippetReflectionProvider { private final HotSpotGraalRuntimeProvider runtime; private final HotSpotConstantReflectionProvider constantReflection; + private final WordTypes wordTypes; - public HotSpotSnippetReflectionProvider(HotSpotGraalRuntimeProvider runtime, HotSpotConstantReflectionProvider constantReflection) { + public HotSpotSnippetReflectionProvider(HotSpotGraalRuntimeProvider runtime, HotSpotConstantReflectionProvider constantReflection, WordTypes wordTypes) { this.runtime = runtime; this.constantReflection = constantReflection; + this.wordTypes = wordTypes; } @Override @@ -88,29 +90,28 @@ } // Lazily initialized - private ResolvedJavaType wordTypesType; - private ResolvedJavaType runtimeType; - private ResolvedJavaType configType; + private Class<?> wordTypesType; + private Class<?> runtimeType; + private Class<?> configType; - public Object getInjectedNodeIntrinsicParameter(ResolvedJavaType type) { + public <T> T getInjectedNodeIntrinsicParameter(Class<T> type) { // Need to test all fields since there no guarantee under the JMM // about the order in which these fields are written. HotSpotVMConfig config = config(); if (configType == null || wordTypesType == null || configType == null) { - MetaAccessProvider metaAccess = runtime.getHostProviders().getMetaAccess(); - wordTypesType = metaAccess.lookupJavaType(runtime.getHostProviders().getWordTypes().getClass()); - runtimeType = metaAccess.lookupJavaType(runtime.getClass()); - configType = metaAccess.lookupJavaType(config.getClass()); + wordTypesType = wordTypes.getClass(); + runtimeType = runtime.getClass(); + configType = config.getClass(); } if (type.isAssignableFrom(wordTypesType)) { - return runtime.getHostProviders().getWordTypes(); + return type.cast(wordTypes); } if (type.isAssignableFrom(runtimeType)) { - return runtime; + return type.cast(runtime); } if (type.isAssignableFrom(configType)) { - return config; + return type.cast(config); } return null; }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotSuitesProvider.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotSuitesProvider.java Tue Dec 08 12:30:15 2015 -0800 @@ -28,8 +28,6 @@ import jdk.vm.ci.options.DerivedOptionValue; import jdk.vm.ci.options.DerivedOptionValue.OptionSupplier; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.DebugInfoMode; import com.oracle.graal.hotspot.HotSpotBackend; import com.oracle.graal.hotspot.HotSpotGraalRuntimeProvider; import com.oracle.graal.hotspot.HotSpotInstructionProfiling; @@ -44,6 +42,8 @@ import com.oracle.graal.nodes.SimplifyingGraphDecoder; import com.oracle.graal.nodes.StructuredGraph; import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration.DebugInfoMode; import com.oracle.graal.phases.BasePhase; import com.oracle.graal.phases.PhaseSuite; import com.oracle.graal.phases.common.AddressLoweringPhase;
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotWordOperationPlugin.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotWordOperationPlugin.java Tue Dec 08 12:30:15 2015 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -35,7 +35,6 @@ import com.oracle.graal.api.replacements.SnippetReflectionProvider; import com.oracle.graal.compiler.common.type.Stamp; import com.oracle.graal.compiler.common.type.StampFactory; -import com.oracle.graal.graphbuilderconf.GraphBuilderContext; import com.oracle.graal.hotspot.nodes.LoadIndexedPointerNode; import com.oracle.graal.hotspot.nodes.type.KlassPointerStamp; import com.oracle.graal.hotspot.nodes.type.MetaspacePointerStamp; @@ -48,6 +47,7 @@ import com.oracle.graal.nodes.calc.ConditionalNode; import com.oracle.graal.nodes.calc.IsNullNode; import com.oracle.graal.nodes.calc.PointerEqualsNode; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderContext; import com.oracle.graal.nodes.java.LoadIndexedNode; import com.oracle.graal.nodes.memory.HeapAccess.BarrierType; import com.oracle.graal.nodes.memory.ReadNode;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/AcquiredCASLockNode.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,58 @@ +/* + * 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.graal.hotspot.nodes; + +import com.oracle.graal.compiler.common.type.StampFactory; +import com.oracle.graal.graph.NodeClass; +import com.oracle.graal.nodeinfo.NodeInfo; +import com.oracle.graal.nodes.FixedWithNextNode; +import com.oracle.graal.nodes.ValueNode; +import com.oracle.graal.nodes.spi.LIRLowerable; +import com.oracle.graal.nodes.spi.NodeLIRBuilderTool; + +/** + * Marks the control flow path where an object acquired a lightweight lock based on an atomic + * compare-and-swap (CAS) of the mark word in the object's header. + */ +@NodeInfo +public final class AcquiredCASLockNode extends FixedWithNextNode implements LIRLowerable { + public static final NodeClass<AcquiredCASLockNode> TYPE = NodeClass.create(AcquiredCASLockNode.class); + + @Input ValueNode object; + + public AcquiredCASLockNode(ValueNode object) { + super(TYPE, StampFactory.forVoid()); + this.object = object; + } + + public ValueNode object() { + return object; + } + + public void generate(NodeLIRBuilderTool generator) { + // This is just a marker node so it generates nothing + } + + @NodeIntrinsic + public static native void mark(Object object); +}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/CRC32Substitutions.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/CRC32Substitutions.java Tue Dec 08 12:30:15 2015 -0800 @@ -48,7 +48,7 @@ * Gets the address of {@code StubRoutines::x86::_crc_table} in {@code stubRoutines_x86.hpp}. */ @Fold - private static long crcTableAddress() { + static long crcTableAddress() { return config().crcTableAddress; }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/CheckCastDynamicSnippets.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/CheckCastDynamicSnippets.java Tue Dec 08 12:30:15 2015 -0800 @@ -41,6 +41,7 @@ import com.oracle.graal.hotspot.nodes.SnippetAnchorNode; import com.oracle.graal.hotspot.word.KlassPointer; import com.oracle.graal.nodes.DeoptimizeNode; +import com.oracle.graal.nodes.PiNode; import com.oracle.graal.nodes.StructuredGraph; import com.oracle.graal.nodes.ValueNode; import com.oracle.graal.nodes.extended.GuardingNode; @@ -64,7 +65,7 @@ isNull.inc(); } else { GuardingNode anchorNode = SnippetAnchorNode.anchor(); - KlassPointer objectHub = loadHubIntrinsic(object, anchorNode); + KlassPointer objectHub = loadHubIntrinsic(PiNode.piCastNonNull(object, anchorNode)); if (!checkUnknownSubType(hub, objectHub)) { DeoptimizeNode.deopt(InvalidateReprofile, ClassCastException); }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/CipherBlockChainingSubstitutions.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/CipherBlockChainingSubstitutions.java Tue Dec 08 12:30:15 2015 -0800 @@ -71,7 +71,7 @@ } @Fold - private static Class<?> getAESCryptClass() { + static Class<?> getAESCryptClass() { return AESCryptSubstitutions.AESCryptClass; } @@ -79,7 +79,7 @@ Object realReceiver = PiNode.piCastNonNull(rcvr, cipherBlockChainingClass); Object embeddedCipher = UnsafeLoadNode.load(realReceiver, embeddedCipherOffset, JavaKind.Object, LocationIdentity.any()); if (getAESCryptClass().isInstance(embeddedCipher)) { - Object aesCipher = PiNode.piCastNonNull(embeddedCipher, AESCryptSubstitutions.AESCryptClass); + Object aesCipher = getAESCryptClass().cast(embeddedCipher); crypt(realReceiver, in, inOffset, inLength, out, outOffset, aesCipher, true, false); return inLength; } else { @@ -91,7 +91,7 @@ Object realReceiver = PiNode.piCastNonNull(rcvr, cipherBlockChainingClass); Object embeddedCipher = UnsafeLoadNode.load(realReceiver, embeddedCipherOffset, JavaKind.Object, LocationIdentity.any()); if (getAESCryptClass().isInstance(embeddedCipher)) { - Object aesCipher = PiNode.piCastNonNull(embeddedCipher, AESCryptSubstitutions.AESCryptClass); + Object aesCipher = getAESCryptClass().cast(embeddedCipher); crypt(realReceiver, in, inOffset, inLength, out, outOffset, aesCipher, true, false); return inLength; } else { @@ -103,7 +103,7 @@ Object realReceiver = PiNode.piCastNonNull(rcvr, cipherBlockChainingClass); Object embeddedCipher = UnsafeLoadNode.load(realReceiver, embeddedCipherOffset, JavaKind.Object, LocationIdentity.any()); if (in != out && getAESCryptClass().isInstance(embeddedCipher)) { - Object aesCipher = PiNode.piCastNonNull(embeddedCipher, AESCryptSubstitutions.AESCryptClass); + Object aesCipher = getAESCryptClass().cast(embeddedCipher); crypt(realReceiver, in, inOffset, inLength, out, outOffset, aesCipher, false, false); return inLength; } else { @@ -115,7 +115,7 @@ Object realReceiver = PiNode.piCastNonNull(rcvr, cipherBlockChainingClass); Object embeddedCipher = UnsafeLoadNode.load(realReceiver, embeddedCipherOffset, JavaKind.Object, LocationIdentity.any()); if (in != out && getAESCryptClass().isInstance(embeddedCipher)) { - Object aesCipher = PiNode.piCastNonNull(embeddedCipher, AESCryptSubstitutions.AESCryptClass); + Object aesCipher = getAESCryptClass().cast(embeddedCipher); crypt(realReceiver, in, inOffset, inLength, out, outOffset, aesCipher, false, false); return inLength; } else { @@ -131,7 +131,7 @@ Object realReceiver = PiNode.piCastNonNull(rcvr, cipherBlockChainingClass); Object embeddedCipher = UnsafeLoadNode.load(realReceiver, embeddedCipherOffset, JavaKind.Object, LocationIdentity.any()); if (in != out && getAESCryptClass().isInstance(embeddedCipher)) { - Object aesCipher = PiNode.piCastNonNull(embeddedCipher, AESCryptSubstitutions.AESCryptClass); + Object aesCipher = getAESCryptClass().cast(embeddedCipher); crypt(realReceiver, in, inOffset, inLength, out, outOffset, aesCipher, false, true); return inLength; } else { @@ -146,7 +146,7 @@ Object realReceiver = PiNode.piCastNonNull(rcvr, cipherBlockChainingClass); Object embeddedCipher = UnsafeLoadNode.load(realReceiver, embeddedCipherOffset, JavaKind.Object, LocationIdentity.any()); if (in != out && getAESCryptClass().isInstance(embeddedCipher)) { - Object aesCipher = PiNode.piCastNonNull(embeddedCipher, AESCryptSubstitutions.AESCryptClass); + Object aesCipher = getAESCryptClass().cast(embeddedCipher); crypt(realReceiver, in, inOffset, inLength, out, outOffset, aesCipher, false, true); return inLength; } else { @@ -157,7 +157,7 @@ private static void crypt(Object rcvr, byte[] in, int inOffset, int inLength, byte[] out, int outOffset, Object embeddedCipher, boolean encrypt, boolean withOriginalKey) { AESCryptSubstitutions.checkArgs(in, inOffset, out, outOffset); Object realReceiver = PiNode.piCastNonNull(rcvr, cipherBlockChainingClass); - Object aesCipher = PiNode.piCastNonNull(embeddedCipher, AESCryptSubstitutions.AESCryptClass); + Object aesCipher = getAESCryptClass().cast(embeddedCipher); Object kObject = UnsafeLoadNode.load(aesCipher, AESCryptSubstitutions.kOffset, JavaKind.Object, LocationIdentity.any()); Object rObject = UnsafeLoadNode.load(realReceiver, rOffset, JavaKind.Object, LocationIdentity.any()); Pointer kAddr = Word.objectToTrackedPointer(kObject).add(getArrayBaseOffset(JavaKind.Int));
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/ClassGetHubNode.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/ClassGetHubNode.java Tue Dec 08 12:30:15 2015 -0800 @@ -91,7 +91,7 @@ } if (clazz instanceof GetClassNode) { GetClassNode getClass = (GetClassNode) clazz; - return new LoadHubNode(KlassPointerStamp.klassNonNull(), getClass.getObject(), null); + return new LoadHubNode(KlassPointerStamp.klassNonNull(), getClass.getObject()); } if (clazz instanceof HubGetClassNode) { // replace _klass._java_mirror._klass -> _klass
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/HotSpotReplacementsUtil.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/HotSpotReplacementsUtil.java Tue Dec 08 12:30:15 2015 -0800 @@ -195,14 +195,14 @@ public static final LocationIdentity TLAB_END_LOCATION = NamedLocationIdentity.mutable("TlabEnd"); @Fold - private static int threadTlabEndOffset() { + static int threadTlabEndOffset() { return config().threadTlabEndOffset(); } public static final LocationIdentity TLAB_START_LOCATION = NamedLocationIdentity.mutable("TlabStart"); @Fold - private static int threadTlabStartOffset() { + static int threadTlabStartOffset() { return config().threadTlabStartOffset(); } @@ -212,7 +212,7 @@ * @see HotSpotVMConfig#pendingExceptionOffset */ @Fold - private static int threadPendingExceptionOffset() { + static int threadPendingExceptionOffset() { return config().pendingExceptionOffset; } @@ -222,14 +222,14 @@ * @see HotSpotVMConfig#pendingDeoptimizationOffset */ @Fold - private static int threadPendingDeoptimizationOffset() { + static int threadPendingDeoptimizationOffset() { return config().pendingDeoptimizationOffset; } public static final LocationIdentity OBJECT_RESULT_LOCATION = NamedLocationIdentity.mutable("ObjectResult"); @Fold - private static int objectResultOffset() { + static int objectResultOffset() { return config().threadObjectResultOffset; } @@ -503,7 +503,7 @@ }; @Fold - private static int hubOffset() { + static int hubOffset() { return config().hubOffset; } @@ -750,9 +750,6 @@ private static native KlassPointer loadKlassFromObjectIntrinsic(Object object, long offset, @ConstantNodeParameter JavaKind wordKind, @ConstantNodeParameter LocationIdentity locationIdentity); @NodeIntrinsic(value = LoadHubNode.class) - public static native KlassPointer loadHubIntrinsic(Object object, GuardingNode anchor); - - @NodeIntrinsic(value = LoadHubNode.class) public static native KlassPointer loadHubIntrinsic(Object object); @Fold
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/InstanceOfSnippets.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/InstanceOfSnippets.java Tue Dec 08 12:30:15 2015 -0800 @@ -60,6 +60,7 @@ import com.oracle.graal.hotspot.word.KlassPointer; import com.oracle.graal.nodes.ConstantNode; import com.oracle.graal.nodes.DeoptimizeNode; +import com.oracle.graal.nodes.PiNode; import com.oracle.graal.nodes.StructuredGraph; import com.oracle.graal.nodes.TypeCheckHints; import com.oracle.graal.nodes.ValueNode; @@ -106,7 +107,7 @@ return falseValue; } GuardingNode anchorNode = SnippetAnchorNode.anchor(); - KlassPointer objectHub = loadHubIntrinsic(object, anchorNode); + KlassPointer objectHub = loadHubIntrinsic(PiNode.piCastNonNull(object, anchorNode)); // if we get an exact match: succeed immediately ExplodeLoopNode.explodeLoop(); for (int i = 0; i < hints.length; i++) { @@ -136,7 +137,7 @@ return falseValue; } GuardingNode anchorNode = SnippetAnchorNode.anchor(); - KlassPointer objectHub = loadHubIntrinsic(object, anchorNode); + KlassPointer objectHub = loadHubIntrinsic(PiNode.piCastNonNull(object, anchorNode)); if (probability(LIKELY_PROBABILITY, objectHub.notEqual(exactHub))) { exactMiss.inc(); return falseValue; @@ -155,7 +156,7 @@ return falseValue; } GuardingNode anchorNode = SnippetAnchorNode.anchor(); - KlassPointer objectHub = loadHubIntrinsic(object, anchorNode); + KlassPointer objectHub = loadHubIntrinsic(PiNode.piCastNonNull(object, anchorNode)); if (probability(NOT_LIKELY_PROBABILITY, objectHub.readKlassPointer(superCheckOffset, PRIMARY_SUPERS_LOCATION).notEqual(hub))) { displayMiss.inc(); return falseValue; @@ -174,7 +175,7 @@ return falseValue; } GuardingNode anchorNode = SnippetAnchorNode.anchor(); - KlassPointer objectHub = loadHubIntrinsic(object, anchorNode); + KlassPointer objectHub = loadHubIntrinsic(PiNode.piCastNonNull(object, anchorNode)); // if we get an exact match: succeed immediately ExplodeLoopNode.explodeLoop(); for (int i = 0; i < hints.length; i++) { @@ -203,7 +204,7 @@ } GuardingNode anchorNode = SnippetAnchorNode.anchor(); KlassPointer hub = ClassGetHubNode.readClass(mirror, anchorNode); - KlassPointer objectHub = loadHubIntrinsic(object, anchorNode); + KlassPointer objectHub = loadHubIntrinsic(PiNode.piCastNonNull(object, anchorNode)); if (hub.isNull() || !checkUnknownSubType(hub, objectHub)) { return falseValue; }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MonitorSnippets.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MonitorSnippets.java Tue Dec 08 12:30:15 2015 -0800 @@ -46,7 +46,6 @@ import static com.oracle.graal.hotspot.replacements.HotSpotReplacementsUtil.verifyOop; import static com.oracle.graal.hotspot.replacements.HotSpotReplacementsUtil.wordSize; import static com.oracle.graal.nodes.extended.BranchProbabilityNode.FREQUENT_PROBABILITY; -import static com.oracle.graal.nodes.extended.BranchProbabilityNode.NOT_FREQUENT_PROBABILITY; import static com.oracle.graal.nodes.extended.BranchProbabilityNode.VERY_FAST_PATH_PROBABILITY; import static com.oracle.graal.nodes.extended.BranchProbabilityNode.VERY_SLOW_PATH_PROBABILITY; import static com.oracle.graal.nodes.extended.BranchProbabilityNode.probability; @@ -75,6 +74,7 @@ import com.oracle.graal.graph.iterators.NodeIterable; import com.oracle.graal.hotspot.meta.HotSpotProviders; import com.oracle.graal.hotspot.meta.HotSpotRegistersProvider; +import com.oracle.graal.hotspot.nodes.AcquiredCASLockNode; import com.oracle.graal.hotspot.nodes.CurrentLockNode; import com.oracle.graal.hotspot.nodes.DirectCompareAndSwapNode; import com.oracle.graal.hotspot.nodes.FastAcquireBiasedLockNode; @@ -91,6 +91,7 @@ import com.oracle.graal.nodes.StructuredGraph; import com.oracle.graal.nodes.ValueNode; import com.oracle.graal.nodes.debug.DynamicCounterNode; +import com.oracle.graal.nodes.extended.BranchProbabilityNode; import com.oracle.graal.nodes.extended.ForeignCallNode; import com.oracle.graal.nodes.java.MethodCallTargetNode; import com.oracle.graal.nodes.java.MonitorExitNode; @@ -128,25 +129,25 @@ * JavaThread*:23 epoch:2 age:4 biased_lock:1 lock:2 (biased object) * size:32 ------------------------------------------>| (CMS free block) * PromotedObject*:29 ---------->| promo_bits:3 ----->| (CMS promoted object) - * + * * 64 bits: * -------- * unused:25 hash:31 -->| unused:1 age:4 biased_lock:1 lock:2 (normal object) * JavaThread*:54 epoch:2 unused:1 age:4 biased_lock:1 lock:2 (biased object) * PromotedObject*:61 --------------------->| promo_bits:3 ----->| (CMS promoted object) * size:64 ----------------------------------------------------->| (CMS free block) - * + * * unused:25 hash:31 -->| cms_free:1 age:4 biased_lock:1 lock:2 (COOPs && normal object) * JavaThread*:54 epoch:2 cms_free:1 age:4 biased_lock:1 lock:2 (COOPs && biased object) * narrowOop:32 unused:24 cms_free:1 unused:4 promo_bits:3 ----->| (COOPs && CMS promoted object) * unused:21 size:35 -->| cms_free:1 unused:7 ------------------>| (COOPs && CMS free block) - * + * * - hash contains the identity hash value: largest value is * 31 bits, see os::random(). Also, 64-bit vm's require * a hash value no bigger than 32 bits because they will not * properly generate a mask larger than that: see library_call.cpp * and c1_CodePatterns_sparc.cpp. - * + * * - the biased lock pattern is used to bias a lock toward a given * thread. When this pattern is set in the low three bits, the lock * is either biased toward a given thread or "anonymously" biased, @@ -155,12 +156,12 @@ * be performed by that thread without using atomic operations. * When a lock's bias is revoked, it reverts back to the normal * locking scheme described below. - * + * * Note that we are overloading the meaning of the "unlocked" state * of the header. Because we steal a bit from the age we can * guarantee that the bias pattern will never be seen for a truly * unlocked object. - * + * * Note also that the biased state contains the age bits normally * contained in the object header. Large increases in scavenge * times were seen when these bits were absent and an arbitrary age @@ -171,18 +172,18 @@ * a very large value (currently 128 bytes (32bVM) or 256 bytes (64bVM)) * to make room for the age bits & the epoch bits (used in support of * biased locking), and for the CMS "freeness" bit in the 64bVM (+COOPs). - * + * * [JavaThread* | epoch | age | 1 | 01] lock is biased toward given thread * [0 | epoch | age | 1 | 01] lock is anonymously biased - * + * * - the two lock bits are used to describe three states: locked/unlocked and monitor. - * + * * [ptr | 00] locked ptr points to real header on stack * [header | 0 | 01] unlocked regular object header * [ptr | 10] monitor inflated lock (header is wapped out) * [ptr | 11] marked used by markSweep to mark an object * not valid at any other time - * + * * We assume that stack/thread pointers have the lowest two bits cleared. * </pre> * @@ -202,7 +203,7 @@ private static final boolean PROFILE_CONTEXT = false; @Fold - private static boolean doProfile() { + static boolean doProfile() { return Options.ProfileMonitors.getValue(); } @@ -250,7 +251,7 @@ trace(trace, "prototypeMarkWord: 0x%016lx\n", prototypeMarkWord); trace(trace, " thread: 0x%016lx\n", thread); trace(trace, " tmp: 0x%016lx\n", tmp); - if (probability(FREQUENT_PROBABILITY, tmp.equal(0))) { + if (probability(BranchProbabilityNode.NOT_LIKELY_PROBABILITY, tmp.equal(0))) { // Object is already biased to current thread -> done traceObject(trace, "+lock{bias:existing}", object, true); lockBiasExisting.inc(); @@ -259,7 +260,7 @@ } // Now check to see whether biasing is enabled for this object - if (probability(NOT_FREQUENT_PROBABILITY, biasableLockBits.notEqual(Word.unsigned(biasedLockPattern())))) { + if (probability(BranchProbabilityNode.FAST_PATH_PROBABILITY, biasableLockBits.notEqual(Word.unsigned(biasedLockPattern())))) { // Biasing not enabled -> fall through to lightweight locking unbiasable.inc(); } else { @@ -360,7 +361,7 @@ // Test if the object's mark word is unlocked, and if so, store the // (address of) the lock slot into the object's mark word. Word currentMark = compareAndSwap(OffsetAddressNode.address(object, markOffset()), unlockedMark, lock, MARK_WORD_LOCATION); - if (currentMark.notEqual(unlockedMark)) { + if (probability(BranchProbabilityNode.SLOW_PATH_PROBABILITY, currentMark.notEqual(unlockedMark))) { trace(trace, " currentMark: 0x%016lx\n", currentMark); // The mark word in the object header was not the same. // Either the object is locked by another thread or is already locked @@ -394,6 +395,7 @@ } else { traceObject(trace, "+lock{cas}", object, true); lockCas.inc(); + AcquiredCASLockNode.mark(object); } } @@ -426,7 +428,7 @@ // the bias bit would be clear. final Word mark = loadWordFromObject(object, markOffset()); trace(trace, " mark: 0x%016lx\n", mark); - if (probability(FREQUENT_PROBABILITY, mark.and(biasedLockMaskInPlace()).equal(Word.unsigned(biasedLockPattern())))) { + if (probability(BranchProbabilityNode.NOT_LIKELY_PROBABILITY, mark.and(biasedLockMaskInPlace()).equal(Word.unsigned(biasedLockPattern())))) { endLockScope(); decCounter(); traceObject(trace, "-lock{bias}", object, false); @@ -441,7 +443,7 @@ final Word displacedMark = lock.readWord(lockDisplacedMarkOffset(), DISPLACED_MARK_WORD_LOCATION); trace(trace, " displacedMark: 0x%016lx\n", displacedMark); - if (displacedMark.equal(0)) { + if (probability(BranchProbabilityNode.NOT_LIKELY_PROBABILITY, displacedMark.equal(0))) { // Recursive locking => done traceObject(trace, "-lock{recursive}", object, false); unlockCasRecursive.inc(); @@ -669,7 +671,7 @@ private static native void monitorenterStubC(@ConstantNodeParameter ForeignCallDescriptor descriptor, Object object, Word lock); @NodeIntrinsic(ForeignCallNode.class) - private static native void monitorexitStubC(@ConstantNodeParameter ForeignCallDescriptor descriptor, Object object, Word lock); + public static native void monitorexitStubC(@ConstantNodeParameter ForeignCallDescriptor descriptor, Object object, Word lock); /** * Counters for the various paths for acquiring a lock. The counters whose names start with
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/NewObjectSnippets.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/NewObjectSnippets.java Tue Dec 08 12:30:15 2015 -0800 @@ -138,7 +138,7 @@ public static final ProfileMode PROFILE_MODE = ProfileMode.AllocatedTypes; @Fold - private static String createName(String path, String typeContext) { + static String createName(String path, String typeContext) { switch (PROFILE_MODE) { case AllocatingMethods: return ""; @@ -155,7 +155,7 @@ } @Fold - private static boolean doProfile() { + static boolean doProfile() { return ProfileAllocations.getValue(); }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/RuntimeStringSnippets.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/RuntimeStringSnippets.java Tue Dec 08 12:30:15 2015 -0800 @@ -49,7 +49,7 @@ public class RuntimeStringSnippets implements Snippets { @Fold - private static long valueOffset() { + static long valueOffset() { try { return UNSAFE.objectFieldOffset(String.class.getDeclaredField("value")); } catch (Exception e) { @@ -58,7 +58,7 @@ } @Fold - private static long hashOffset() { + static long hashOffset() { try { return UNSAFE.objectFieldOffset(String.class.getDeclaredField("hash")); } catch (Exception e) { @@ -67,7 +67,7 @@ } @Fold - private static long arrayBaseOffset() { + static long arrayBaseOffset() { return UNSAFE.arrayBaseOffset(char[].class); }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/arraycopy/ArrayCopySnippets.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/arraycopy/ArrayCopySnippets.java Tue Dec 08 12:30:15 2015 -0800 @@ -62,6 +62,7 @@ import com.oracle.graal.nodes.Invoke; import com.oracle.graal.nodes.InvokeNode; import com.oracle.graal.nodes.NamedLocationIdentity; +import com.oracle.graal.nodes.PiNode; import com.oracle.graal.nodes.StructuredGraph; import com.oracle.graal.nodes.ValueNode; import com.oracle.graal.nodes.extended.UnsafeLoadNode; @@ -173,8 +174,8 @@ public static void arraycopyPredictedObjectWork(Object nonNullSrc, int srcPos, Object nonNullDest, int destPos, int length, KlassPointer objectArrayKlass, @ConstantParameter SnippetCounter counter, @ConstantParameter SnippetCounter copiedCounter) { if (length > 0) { - KlassPointer srcHub = loadHub(nonNullSrc); - KlassPointer destHub = loadHub(nonNullDest); + KlassPointer srcHub = loadHub(PiNode.asNonNullObject(nonNullSrc)); + KlassPointer destHub = loadHub(PiNode.asNonNullObject(nonNullDest)); if (probability(FAST_PATH_PROBABILITY, srcHub == destHub || destHub == objectArrayKlass)) { counter.inc(); copiedCounter.add(length); @@ -284,7 +285,7 @@ } @Fold - private static LocationIdentity getArrayLocation(JavaKind kind) { + static LocationIdentity getArrayLocation(JavaKind kind) { return NamedLocationIdentity.getArrayLocation(kind); }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/arraycopy/UnsafeArrayCopySnippets.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/arraycopy/UnsafeArrayCopySnippets.java Tue Dec 08 12:30:15 2015 -0800 @@ -144,7 +144,7 @@ } @Fold - private static LocationIdentity getArrayLocation(JavaKind kind) { + static LocationIdentity getArrayLocation(JavaKind kind) { return NamedLocationIdentity.getArrayLocation(kind); }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/DeoptimizationStub.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/DeoptimizationStub.java Tue Dec 08 12:30:15 2015 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014, 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 @@ -154,7 +154,7 @@ * Stack bang to make sure there's enough room for the interpreter frames. Bang stack for * total size of the interpreter frames plus shadow page size. Bang one page at a time * because large sizes can bang beyond yellow and red zones. - * + * * @deprecated This code should go away as soon as JDK-8032410 hits the Graal repository. */ final int totalFrameSizes = unrollBlock.readInt(deoptimizationUnrollBlockTotalFrameSizesOffset()); @@ -228,7 +228,7 @@ } @Fold - private static int stackShadowPages() { + static int stackShadowPages() { return config().useStackBanging ? config().stackShadowPages : 0; } @@ -241,57 +241,57 @@ */ @Deprecated @Fold - private static int stackBias() { + static int stackBias() { return config().stackBias; } @Fold - private static int deoptimizationUnrollBlockSizeOfDeoptimizedFrameOffset() { + static int deoptimizationUnrollBlockSizeOfDeoptimizedFrameOffset() { return config().deoptimizationUnrollBlockSizeOfDeoptimizedFrameOffset; } @Fold - private static int deoptimizationUnrollBlockCallerAdjustmentOffset() { + static int deoptimizationUnrollBlockCallerAdjustmentOffset() { return config().deoptimizationUnrollBlockCallerAdjustmentOffset; } @Fold - private static int deoptimizationUnrollBlockNumberOfFramesOffset() { + static int deoptimizationUnrollBlockNumberOfFramesOffset() { return config().deoptimizationUnrollBlockNumberOfFramesOffset; } @Fold - private static int deoptimizationUnrollBlockTotalFrameSizesOffset() { + static int deoptimizationUnrollBlockTotalFrameSizesOffset() { return config().deoptimizationUnrollBlockTotalFrameSizesOffset; } @Fold - private static int deoptimizationUnrollBlockUnpackKindOffset() { + static int deoptimizationUnrollBlockUnpackKindOffset() { return config().deoptimizationUnrollBlockUnpackKindOffset; } @Fold - private static int deoptimizationUnrollBlockFrameSizesOffset() { + static int deoptimizationUnrollBlockFrameSizesOffset() { return config().deoptimizationUnrollBlockFrameSizesOffset; } @Fold - private static int deoptimizationUnrollBlockFramePcsOffset() { + static int deoptimizationUnrollBlockFramePcsOffset() { return config().deoptimizationUnrollBlockFramePcsOffset; } @Fold - private static int deoptimizationUnrollBlockInitialInfoOffset() { + static int deoptimizationUnrollBlockInitialInfoOffset() { return config().deoptimizationUnrollBlockInitialInfoOffset; } @Fold - private static int deoptimizationUnpackDeopt() { + static int deoptimizationUnpackDeopt() { return config().deoptimizationUnpackDeopt; } @Fold - private static int deoptimizationUnpackUncommonTrap() { + static int deoptimizationUnpackUncommonTrap() { return config().deoptimizationUnpackUncommonTrap; }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/ExceptionHandlerStub.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/ExceptionHandlerStub.java Tue Dec 08 12:30:15 2015 -0800 @@ -133,7 +133,7 @@ } @Fold - private static boolean logging() { + static boolean logging() { return Boolean.getBoolean("graal.logExceptionHandlerStub"); } @@ -146,7 +146,7 @@ */ @Fold @SuppressWarnings("all") - private static boolean assertionsEnabled() { + static boolean assertionsEnabled() { boolean enabled = false; assert enabled = true; return enabled || cAssertionsEnabled();
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/NewArrayStub.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/NewArrayStub.java Tue Dec 08 12:30:15 2015 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -84,7 +84,7 @@ } @Fold - private static boolean logging() { + static boolean logging() { return Boolean.getBoolean("graal.logNewArrayStub"); }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/NewInstanceStub.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/NewInstanceStub.java Tue Dec 08 12:30:15 2015 -0800 @@ -126,7 +126,7 @@ } @Fold - private static boolean logging() { + static boolean logging() { return Boolean.getBoolean("graal.logNewInstanceStub"); } @@ -294,7 +294,7 @@ } @Fold - private static boolean forceSlowPath() { + static boolean forceSlowPath() { return Boolean.getBoolean("graal.newInstanceStub.forceSlowPath"); }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/SnippetStub.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/SnippetStub.java Tue Dec 08 12:30:15 2015 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -22,7 +22,7 @@ */ package com.oracle.graal.hotspot.stubs; -import static com.oracle.graal.graphbuilderconf.IntrinsicContext.CompilationContext.INLINE_AFTER_PARSING; +import static com.oracle.graal.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.INLINE_AFTER_PARSING; import java.lang.reflect.Method; @@ -34,15 +34,15 @@ import com.oracle.graal.debug.Debug; import com.oracle.graal.debug.Debug.Scope; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins; -import com.oracle.graal.graphbuilderconf.IntrinsicContext; import com.oracle.graal.hotspot.HotSpotForeignCallLinkage; import com.oracle.graal.hotspot.meta.HotSpotProviders; import com.oracle.graal.java.GraphBuilderPhase; import com.oracle.graal.nodes.StructuredGraph; import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions; import com.oracle.graal.nodes.StructuredGraph.GuardsStage; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration; +import com.oracle.graal.nodes.graphbuilderconf.IntrinsicContext; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; import com.oracle.graal.nodes.spi.LoweringTool; import com.oracle.graal.phases.OptimisticOptimizations; import com.oracle.graal.phases.common.CanonicalizerPhase;
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/StubUtil.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/StubUtil.java Tue Dec 08 12:30:15 2015 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014, 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 @@ -47,6 +47,7 @@ import com.oracle.graal.hotspot.nodes.StubForeignCallNode; import com.oracle.graal.hotspot.nodes.VMErrorNode; import com.oracle.graal.hotspot.word.KlassPointer; +import com.oracle.graal.nodes.PiNode; import com.oracle.graal.nodes.extended.GuardingNode; import com.oracle.graal.replacements.Log; import com.oracle.graal.word.Pointer; @@ -241,7 +242,7 @@ fatal("oop not in heap: %p", oop.rawValue()); } - KlassPointer klass = loadHubIntrinsic(object, anchorNode); + KlassPointer klass = loadHubIntrinsic(PiNode.piCastNonNull(object, anchorNode)); if (klass.isNull()) { fatal("klass for oop %p is null", oop.rawValue()); } @@ -251,22 +252,22 @@ } @Fold - private static long verifyOopCounterAddress() { + static long verifyOopCounterAddress() { return config().verifyOopCounterAddress; } @Fold - private static long verifyOopMask() { + static long verifyOopMask() { return config().verifyOopMask; } @Fold - private static long verifyOopBits() { + static long verifyOopBits() { return config().verifyOopBits; } @Fold - private static int hubOffset() { + static int hubOffset() { return config().hubOffset; } }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/UncommonTrapStub.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/UncommonTrapStub.java Tue Dec 08 12:30:15 2015 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014, 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 @@ -156,7 +156,7 @@ } @Fold - private static int stackShadowPages() { + static int stackShadowPages() { return config().useStackBanging ? config().stackShadowPages : 0; } @@ -169,52 +169,52 @@ */ @Deprecated @Fold - private static int stackBias() { + static int stackBias() { return config().stackBias; } @Fold - private static int deoptimizationUnrollBlockSizeOfDeoptimizedFrameOffset() { + static int deoptimizationUnrollBlockSizeOfDeoptimizedFrameOffset() { return config().deoptimizationUnrollBlockSizeOfDeoptimizedFrameOffset; } @Fold - private static int deoptimizationUnrollBlockCallerAdjustmentOffset() { + static int deoptimizationUnrollBlockCallerAdjustmentOffset() { return config().deoptimizationUnrollBlockCallerAdjustmentOffset; } @Fold - private static int deoptimizationUnrollBlockNumberOfFramesOffset() { + static int deoptimizationUnrollBlockNumberOfFramesOffset() { return config().deoptimizationUnrollBlockNumberOfFramesOffset; } @Fold - private static int deoptimizationUnrollBlockTotalFrameSizesOffset() { + static int deoptimizationUnrollBlockTotalFrameSizesOffset() { return config().deoptimizationUnrollBlockTotalFrameSizesOffset; } @Fold - private static int deoptimizationUnrollBlockFrameSizesOffset() { + static int deoptimizationUnrollBlockFrameSizesOffset() { return config().deoptimizationUnrollBlockFrameSizesOffset; } @Fold - private static int deoptimizationUnrollBlockFramePcsOffset() { + static int deoptimizationUnrollBlockFramePcsOffset() { return config().deoptimizationUnrollBlockFramePcsOffset; } @Fold - private static int deoptimizationUnrollBlockInitialInfoOffset() { + static int deoptimizationUnrollBlockInitialInfoOffset() { return config().deoptimizationUnrollBlockInitialInfoOffset; } @Fold - private static int deoptimizationUnpackDeopt() { + static int deoptimizationUnpackDeopt() { return config().deoptimizationUnpackDeopt; } @Fold - private static int deoptimizationUnpackUncommonTrap() { + static int deoptimizationUnpackUncommonTrap() { return config().deoptimizationUnpackUncommonTrap; }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/UnwindExceptionToCallerStub.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/UnwindExceptionToCallerStub.java Tue Dec 08 12:30:15 2015 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -96,7 +96,7 @@ } @Fold - private static boolean logging() { + static boolean logging() { return Boolean.getBoolean("graal.logUnwindExceptionToCallerStub"); } @@ -109,7 +109,7 @@ */ @Fold @SuppressWarnings("all") - private static boolean assertionsEnabled() { + static boolean assertionsEnabled() { boolean enabled = false; assert enabled = true; return enabled || cAssertionsEnabled();
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/BytecodeParser.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/BytecodeParser.java Tue Dec 08 12:30:15 2015 -0800 @@ -231,12 +231,12 @@ import static com.oracle.graal.compiler.common.GraalOptions.ResolveClassBeforeStaticInvoke; import static com.oracle.graal.compiler.common.GraalOptions.StressInvokeWithExceptionNode; import static com.oracle.graal.compiler.common.type.StampFactory.objectNonNull; -import static com.oracle.graal.graphbuilderconf.IntrinsicContext.CompilationContext.INLINE_DURING_PARSING; import static com.oracle.graal.java.BytecodeParserOptions.DumpDuringGraphBuilding; import static com.oracle.graal.java.BytecodeParserOptions.FailedLoopExplosionIsFatal; import static com.oracle.graal.java.BytecodeParserOptions.MaximumLoopExplosionCount; import static com.oracle.graal.java.BytecodeParserOptions.TraceInlineDuringParsing; import static com.oracle.graal.java.BytecodeParserOptions.TraceParserPlugins; +import static com.oracle.graal.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.INLINE_DURING_PARSING; import static com.oracle.graal.nodes.type.StampTool.isPointerNonNull; import static java.lang.String.format; import static jdk.vm.ci.common.JVMCIError.guarantee; @@ -312,15 +312,6 @@ import com.oracle.graal.graph.Node.ValueNumberable; import com.oracle.graal.graph.NodeBitMap; import com.oracle.graal.graph.iterators.NodeIterable; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration; -import com.oracle.graal.graphbuilderconf.GraphBuilderContext; -import com.oracle.graal.graphbuilderconf.InlineInvokePlugin; -import com.oracle.graal.graphbuilderconf.InlineInvokePlugin.InlineInfo; -import com.oracle.graal.graphbuilderconf.IntrinsicContext; -import com.oracle.graal.graphbuilderconf.InvocationPlugin; -import com.oracle.graal.graphbuilderconf.InvocationPlugins.InvocationPluginReceiver; -import com.oracle.graal.graphbuilderconf.LoopExplosionPlugin; -import com.oracle.graal.graphbuilderconf.NodePlugin; import com.oracle.graal.java.BciBlockMapping.BciBlock; import com.oracle.graal.java.BciBlockMapping.ExceptionDispatchBlock; import com.oracle.graal.nodeinfo.InputType; @@ -393,6 +384,15 @@ import com.oracle.graal.nodes.extended.GuardedNode; import com.oracle.graal.nodes.extended.GuardingNode; import com.oracle.graal.nodes.extended.IntegerSwitchNode; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderContext; +import com.oracle.graal.nodes.graphbuilderconf.InlineInvokePlugin; +import com.oracle.graal.nodes.graphbuilderconf.IntrinsicContext; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugin; +import com.oracle.graal.nodes.graphbuilderconf.LoopExplosionPlugin; +import com.oracle.graal.nodes.graphbuilderconf.NodePlugin; +import com.oracle.graal.nodes.graphbuilderconf.InlineInvokePlugin.InlineInfo; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugins.InvocationPluginReceiver; import com.oracle.graal.nodes.java.ArrayLengthNode; import com.oracle.graal.nodes.java.CheckCastNode; import com.oracle.graal.nodes.java.ExceptionObjectNode; @@ -1387,7 +1387,7 @@ protected void genThrow() { if (GraalOptions.OldInfopoints.getValue() && graphBuilderConfig.insertNonSafepointDebugInfo() && !parsingIntrinsic()) { - genInfoPointNode(InfopointReason.LINE_NUMBER, null); + genInfoPointNode(InfopointReason.BYTECODE_POSITION, null); } ValueNode exception = frameState.pop(JavaKind.Object); @@ -2642,7 +2642,7 @@ if (GraalOptions.OldInfopoints.getValue() && graphBuilderConfig.insertNonSafepointDebugInfo() && !parsingIntrinsic()) { currentLineNumber = lnt != null ? lnt.getLineNumber(bci) : (graphBuilderConfig.insertFullDebugInfo() ? -1 : bci); if (currentLineNumber != previousLineNumber) { - genInfoPointNode(InfopointReason.LINE_NUMBER, null); + genInfoPointNode(InfopointReason.BYTECODE_POSITION, null); previousLineNumber = currentLineNumber; } } @@ -3434,7 +3434,7 @@ if (skippedExceptionTypes != null) { for (ResolvedJavaType exceptionType : skippedExceptionTypes) { if (exceptionType.isAssignableFrom(resolvedType)) { - append(new DeoptimizeNode(DeoptimizationAction.None, TransferToInterpreter)); + append(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, TransferToInterpreter)); return; } }
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/DefaultSuitesProvider.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/DefaultSuitesProvider.java Tue Dec 08 12:30:15 2015 -0800 @@ -25,9 +25,9 @@ import jdk.vm.ci.options.DerivedOptionValue; import jdk.vm.ci.options.DerivedOptionValue.OptionSupplier; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins; import com.oracle.graal.lir.phases.LIRSuites; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; import com.oracle.graal.phases.PhaseSuite; import com.oracle.graal.phases.tiers.CompilerConfiguration; import com.oracle.graal.phases.tiers.HighTierContext;
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/FrameStateBuilder.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/FrameStateBuilder.java Tue Dec 08 12:30:15 2015 -0800 @@ -54,8 +54,6 @@ import com.oracle.graal.compiler.common.type.Stamp; import com.oracle.graal.compiler.common.type.StampFactory; import com.oracle.graal.debug.Debug; -import com.oracle.graal.graphbuilderconf.IntrinsicContext.SideEffectsState; -import com.oracle.graal.graphbuilderconf.ParameterPlugin; import com.oracle.graal.java.BciBlockMapping.BciBlock; import com.oracle.graal.nodeinfo.Verbosity; import com.oracle.graal.nodes.AbstractMergeNode; @@ -71,6 +69,8 @@ import com.oracle.graal.nodes.ValuePhiNode; import com.oracle.graal.nodes.ValueProxyNode; import com.oracle.graal.nodes.calc.FloatingNode; +import com.oracle.graal.nodes.graphbuilderconf.ParameterPlugin; +import com.oracle.graal.nodes.graphbuilderconf.IntrinsicContext.SideEffectsState; import com.oracle.graal.nodes.java.MonitorIdNode; import com.oracle.graal.nodes.util.GraphUtil;
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java Tue Dec 08 12:30:15 2015 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 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 @@ -26,9 +26,9 @@ import jdk.vm.ci.meta.MetaAccessProvider; import jdk.vm.ci.meta.ResolvedJavaMethod; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration; -import com.oracle.graal.graphbuilderconf.IntrinsicContext; import com.oracle.graal.nodes.StructuredGraph; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration; +import com.oracle.graal.nodes.graphbuilderconf.IntrinsicContext; import com.oracle.graal.nodes.spi.StampProvider; import com.oracle.graal.phases.BasePhase; import com.oracle.graal.phases.OptimisticOptimizations;
--- a/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/phases/StackMoveOptimizationPhase.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/phases/StackMoveOptimizationPhase.java Tue Dec 08 12:30:15 2015 -0800 @@ -44,7 +44,6 @@ import com.oracle.graal.lir.RedundantMoveElimination; import com.oracle.graal.lir.amd64.AMD64Move.AMD64MultiStackMove; import com.oracle.graal.lir.amd64.AMD64Move.AMD64StackMove; -import com.oracle.graal.lir.gen.BenchmarkCounterFactory; import com.oracle.graal.lir.gen.LIRGenerationResult; import com.oracle.graal.lir.phases.PostAllocationOptimizationPhase; @@ -67,7 +66,7 @@ @Override protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, - BenchmarkCounterFactory counterFactory) { + PostAllocationOptimizationContext context) { LIR lir = lirGenRes.getLIR(); for (AbstractBlockBase<?> block : lir.getControlFlowGraph().getBlocks()) { List<LIRInstruction> instructions = lir.getLIRforBlock(block);
--- a/graal/com.oracle.graal.lir.jtt/src/com/oracle/graal/lir/jtt/LIRTest.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.lir.jtt/src/com/oracle/graal/lir/jtt/LIRTest.java Tue Dec 08 12:30:15 2015 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -38,14 +38,14 @@ import com.oracle.graal.compiler.common.type.StampFactory; import com.oracle.graal.graph.NodeClass; import com.oracle.graal.graph.NodeInputList; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration; -import com.oracle.graal.graphbuilderconf.GraphBuilderContext; -import com.oracle.graal.graphbuilderconf.InvocationPlugin; -import com.oracle.graal.graphbuilderconf.InvocationPlugins; import com.oracle.graal.jtt.JTTTest; import com.oracle.graal.nodeinfo.NodeInfo; import com.oracle.graal.nodes.FixedWithNextNode; import com.oracle.graal.nodes.ValueNode; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderContext; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugin; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugins; import com.oracle.graal.nodes.spi.LIRLowerable; import com.oracle.graal.nodes.spi.NodeLIRBuilderTool;
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/ControlFlowOptimizer.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/ControlFlowOptimizer.java Tue Dec 08 12:30:15 2015 -0800 @@ -33,7 +33,6 @@ import com.oracle.graal.compiler.common.cfg.AbstractBlockBase; import com.oracle.graal.debug.Debug; import com.oracle.graal.debug.DebugMetric; -import com.oracle.graal.lir.gen.BenchmarkCounterFactory; import com.oracle.graal.lir.gen.LIRGenerationResult; import com.oracle.graal.lir.phases.PostAllocationOptimizationPhase; @@ -47,7 +46,7 @@ */ @Override protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, - BenchmarkCounterFactory counterFactory) { + PostAllocationOptimizationContext context) { LIR lir = lirGenRes.getLIR(); new Optimizer<B>(lir).deleteEmptyBlocks(codeEmittingOrder); }
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/EdgeMoveOptimizer.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/EdgeMoveOptimizer.java Tue Dec 08 12:30:15 2015 -0800 @@ -31,7 +31,6 @@ import com.oracle.graal.lir.StandardOp.LoadConstantOp; import com.oracle.graal.lir.StandardOp.MoveOp; import com.oracle.graal.lir.StandardOp.ValueMoveOp; -import com.oracle.graal.lir.gen.BenchmarkCounterFactory; import com.oracle.graal.lir.gen.LIRGenerationResult; import com.oracle.graal.lir.phases.PostAllocationOptimizationPhase; @@ -57,7 +56,7 @@ @Override protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, - BenchmarkCounterFactory counterFactory) { + PostAllocationOptimizationContext context) { LIR ir = lirGenRes.getLIR(); Optimizer optimizer = new Optimizer(ir);
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/NullCheckOptimizer.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/NullCheckOptimizer.java Tue Dec 08 12:30:15 2015 -0800 @@ -29,7 +29,6 @@ import com.oracle.graal.compiler.common.cfg.AbstractBlockBase; import com.oracle.graal.lir.StandardOp.ImplicitNullCheck; import com.oracle.graal.lir.StandardOp.NullCheck; -import com.oracle.graal.lir.gen.BenchmarkCounterFactory; import com.oracle.graal.lir.gen.LIRGenerationResult; import com.oracle.graal.lir.phases.PostAllocationOptimizationPhase; @@ -37,7 +36,7 @@ @Override protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, - BenchmarkCounterFactory counterFactory) { + PostAllocationOptimizationContext context) { LIR ir = lirGenRes.getLIR(); List<? extends AbstractBlockBase<?>> blocks = ir.codeEmittingOrder(); NullCheckOptimizer.foldNullChecks(ir, blocks, target.implicitNullCheckLimit);
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/RedundantMoveElimination.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/RedundantMoveElimination.java Tue Dec 08 12:30:15 2015 -0800 @@ -46,7 +46,6 @@ import com.oracle.graal.lir.StandardOp.MoveOp; import com.oracle.graal.lir.StandardOp.ValueMoveOp; import com.oracle.graal.lir.framemap.FrameMap; -import com.oracle.graal.lir.gen.BenchmarkCounterFactory; import com.oracle.graal.lir.gen.LIRGenerationResult; import com.oracle.graal.lir.phases.PostAllocationOptimizationPhase; @@ -57,7 +56,7 @@ @Override protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, - BenchmarkCounterFactory counterFactory) { + PostAllocationOptimizationContext context) { Optimization redundantMoveElimination = new Optimization(lirGenRes.getFrameMap()); redundantMoveElimination.doOptimize(lirGenRes.getLIR()); }
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/AllocationStageVerifier.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/AllocationStageVerifier.java Tue Dec 08 12:30:15 2015 -0800 @@ -31,14 +31,12 @@ import jdk.vm.ci.code.TargetDescription; import jdk.vm.ci.meta.Value; -import com.oracle.graal.compiler.common.alloc.RegisterAllocationConfig; import com.oracle.graal.compiler.common.cfg.AbstractBlockBase; import com.oracle.graal.lir.LIR; import com.oracle.graal.lir.LIRInstruction; import com.oracle.graal.lir.LIRInstruction.OperandFlag; import com.oracle.graal.lir.LIRInstruction.OperandMode; import com.oracle.graal.lir.gen.LIRGenerationResult; -import com.oracle.graal.lir.gen.LIRGeneratorTool.MoveFactory; import com.oracle.graal.lir.phases.AllocationPhase; /** @@ -47,8 +45,7 @@ public class AllocationStageVerifier extends AllocationPhase { @Override - protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, MoveFactory spillMoveFactory, - RegisterAllocationConfig registerAllocationConfig) { + protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, AllocationContext context) { verifyLIR(lirGenRes.getLIR()); }
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanAssignLocationsPhase.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanAssignLocationsPhase.java Tue Dec 08 12:30:15 2015 -0800 @@ -37,7 +37,6 @@ import jdk.vm.ci.meta.AllocatableValue; import jdk.vm.ci.meta.Value; -import com.oracle.graal.compiler.common.alloc.RegisterAllocationConfig; import com.oracle.graal.compiler.common.cfg.AbstractBlockBase; import com.oracle.graal.debug.Debug; import com.oracle.graal.debug.Indent; @@ -52,7 +51,6 @@ import com.oracle.graal.lir.StandardOp.ValueMoveOp; import com.oracle.graal.lir.Variable; import com.oracle.graal.lir.gen.LIRGenerationResult; -import com.oracle.graal.lir.gen.LIRGeneratorTool.MoveFactory; import com.oracle.graal.lir.phases.AllocationPhase; /** @@ -67,8 +65,7 @@ } @Override - protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, MoveFactory spillMoveFactory, - RegisterAllocationConfig registerAllocationConfig) { + protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, AllocationContext context) { assignLocations(); }
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanEliminateSpillMovePhase.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanEliminateSpillMovePhase.java Tue Dec 08 12:30:15 2015 -0800 @@ -37,7 +37,6 @@ import jdk.vm.ci.options.OptionType; import jdk.vm.ci.options.OptionValue; -import com.oracle.graal.compiler.common.alloc.RegisterAllocationConfig; import com.oracle.graal.compiler.common.cfg.AbstractBlockBase; import com.oracle.graal.debug.Debug; import com.oracle.graal.debug.Indent; @@ -49,7 +48,6 @@ import com.oracle.graal.lir.alloc.lsra.Interval.SpillState; import com.oracle.graal.lir.alloc.lsra.LinearScan.IntervalPredicate; import com.oracle.graal.lir.gen.LIRGenerationResult; -import com.oracle.graal.lir.gen.LIRGeneratorTool.MoveFactory; import com.oracle.graal.lir.phases.AllocationPhase; public class LinearScanEliminateSpillMovePhase extends AllocationPhase { @@ -76,8 +74,7 @@ } @Override - protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, MoveFactory spillMoveFactory, - RegisterAllocationConfig registerAllocationConfig) { + protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, AllocationContext context) { eliminateSpillMoves(); }
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanLifetimeAnalysisPhase.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanLifetimeAnalysisPhase.java Tue Dec 08 12:30:15 2015 -0800 @@ -49,7 +49,6 @@ import jdk.vm.ci.meta.Value; import com.oracle.graal.compiler.common.alloc.ComputeBlockOrder; -import com.oracle.graal.compiler.common.alloc.RegisterAllocationConfig; import com.oracle.graal.compiler.common.cfg.AbstractBlockBase; import com.oracle.graal.compiler.common.util.BitMap2D; import com.oracle.graal.debug.Debug; @@ -66,7 +65,6 @@ import com.oracle.graal.lir.alloc.lsra.Interval.SpillState; import com.oracle.graal.lir.alloc.lsra.LinearScan.BlockData; import com.oracle.graal.lir.gen.LIRGenerationResult; -import com.oracle.graal.lir.gen.LIRGeneratorTool.MoveFactory; import com.oracle.graal.lir.phases.AllocationPhase; public class LinearScanLifetimeAnalysisPhase extends AllocationPhase { @@ -81,8 +79,7 @@ } @Override - protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, MoveFactory spillMoveFactory, - RegisterAllocationConfig registerAllocationConfig) { + protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, AllocationContext context) { numberInstructions(); allocator.printLir("Before register allocation", true); computeLocalLiveSets();
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanOptimizeSpillPositionPhase.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanOptimizeSpillPositionPhase.java Tue Dec 08 12:30:15 2015 -0800 @@ -32,7 +32,6 @@ import jdk.vm.ci.code.TargetDescription; import jdk.vm.ci.meta.AllocatableValue; -import com.oracle.graal.compiler.common.alloc.RegisterAllocationConfig; import com.oracle.graal.compiler.common.cfg.AbstractBlockBase; import com.oracle.graal.debug.Debug; import com.oracle.graal.debug.DebugMetric; @@ -42,7 +41,6 @@ import com.oracle.graal.lir.LIRInstruction.OperandMode; import com.oracle.graal.lir.alloc.lsra.Interval.SpillState; import com.oracle.graal.lir.gen.LIRGenerationResult; -import com.oracle.graal.lir.gen.LIRGeneratorTool.MoveFactory; import com.oracle.graal.lir.phases.AllocationPhase; public final class LinearScanOptimizeSpillPositionPhase extends AllocationPhase { @@ -57,8 +55,7 @@ } @Override - protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, MoveFactory spillMoveFactory, - RegisterAllocationConfig registerAllocationConfig) { + protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, AllocationContext context) { optimizeSpillPosition(); allocator.printIntervals("After optimize spill position"); } @@ -125,7 +122,7 @@ /* * The spill block is the begin of the first split child (aka the value is on the * stack). - * + * * The problem is that if spill block has more than one predecessor, the values at the * end of the predecessors might differ. Therefore, we would need a spill move in all * predecessors. To avoid this we spill in the dominator.
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanPhase.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanPhase.java Tue Dec 08 12:30:15 2015 -0800 @@ -46,8 +46,9 @@ } @Override - protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, MoveFactory spillMoveFactory, - RegisterAllocationConfig registerAllocationConfig) { + protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, AllocationContext context) { + MoveFactory spillMoveFactory = context.spillMoveFactory; + RegisterAllocationConfig registerAllocationConfig = context.registerAllocationConfig; final LinearScan allocator; switch (LinearScanVariant.getValue()) { case SSI_LSRA:
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanRegisterAllocationPhase.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanRegisterAllocationPhase.java Tue Dec 08 12:30:15 2015 -0800 @@ -26,12 +26,10 @@ import jdk.vm.ci.code.TargetDescription; -import com.oracle.graal.compiler.common.alloc.RegisterAllocationConfig; import com.oracle.graal.compiler.common.cfg.AbstractBlockBase; import com.oracle.graal.debug.Debug; import com.oracle.graal.debug.Indent; import com.oracle.graal.lir.gen.LIRGenerationResult; -import com.oracle.graal.lir.gen.LIRGeneratorTool.MoveFactory; import com.oracle.graal.lir.phases.AllocationPhase; public final class LinearScanRegisterAllocationPhase extends AllocationPhase { @@ -43,8 +41,7 @@ } @Override - protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, MoveFactory spillMoveFactory, - RegisterAllocationConfig registerAllocationConfig) { + protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, AllocationContext context) { allocator.printIntervals("Before register allocation"); allocateRegisters(); allocator.printIntervals("After register allocation");
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanResolveDataFlowPhase.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanResolveDataFlowPhase.java Tue Dec 08 12:30:15 2015 -0800 @@ -29,14 +29,12 @@ import jdk.vm.ci.code.TargetDescription; -import com.oracle.graal.compiler.common.alloc.RegisterAllocationConfig; import com.oracle.graal.compiler.common.cfg.AbstractBlockBase; import com.oracle.graal.debug.Debug; import com.oracle.graal.debug.Indent; import com.oracle.graal.lir.LIRInstruction; import com.oracle.graal.lir.StandardOp; import com.oracle.graal.lir.gen.LIRGenerationResult; -import com.oracle.graal.lir.gen.LIRGeneratorTool.MoveFactory; import com.oracle.graal.lir.phases.AllocationPhase; /** @@ -53,8 +51,7 @@ } @Override - protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, MoveFactory spillMoveFactory, - RegisterAllocationConfig registerAllocationConfig) { + protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, AllocationContext context) { resolveDataFlow(); }
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/FixedInterval.java Tue Dec 08 12:29:24 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,310 +0,0 @@ -/* - * Copyright (c) 2015, 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.graal.lir.alloc.trace; - -import static jdk.vm.ci.code.ValueUtil.asRegister; -import static jdk.vm.ci.code.ValueUtil.isRegister; -import jdk.vm.ci.meta.AllocatableValue; -import jdk.vm.ci.meta.Value; - -import com.oracle.graal.lir.LIRInstruction; - -/** - * Represents a fixed interval. - */ -public final class FixedInterval extends IntervalHint { - - static final class FixedList { - - public FixedInterval fixed; - - public FixedList(FixedInterval fixed) { - this.fixed = fixed; - } - - /** - * Gets the fixed list. - */ - public FixedInterval getFixed() { - return fixed; - } - - /** - * Sets the fixed list. - */ - public void setFixed(FixedInterval list) { - fixed = list; - } - - /** - * Adds an interval to a list sorted by {@linkplain FixedInterval#currentFrom() current - * from} positions. - * - * @param interval the interval to add - */ - public void addToListSortedByCurrentFromPositions(FixedInterval interval) { - FixedInterval list = getFixed(); - FixedInterval prev = null; - FixedInterval cur = list; - while (cur.currentFrom() < interval.currentFrom()) { - prev = cur; - cur = cur.next; - } - FixedInterval result = list; - if (prev == null) { - // add to head of list - result = interval; - } else { - // add before 'cur' - prev.next = interval; - } - interval.next = cur; - setFixed(result); - } - - } - - /** - * The fixed operand of this interval. - */ - public final AllocatableValue operand; - - /** - * The head of the list of ranges describing this interval. This list is sorted by - * {@linkplain LIRInstruction#id instruction ids}. - */ - private FixedRange first; - - /** - * Iterator used to traverse the ranges of an interval. - */ - private FixedRange current; - - /** - * Link to next interval in a sorted list of intervals that ends with {@link #EndMarker}. - */ - FixedInterval next; - - private int cachedTo; // cached value: to of last range (-1: not cached) - - public FixedRange first() { - return first; - } - - @Override - public int from() { - return first.from; - } - - public int to() { - if (cachedTo == -1) { - cachedTo = calcTo(); - } - assert cachedTo == calcTo() : "invalid cached value"; - return cachedTo; - } - - // test intersection - boolean intersects(TraceInterval i) { - return first.intersects(i); - } - - int intersectsAt(TraceInterval i) { - return first.intersectsAt(i); - } - - // range iteration - void rewindRange() { - current = first; - } - - void nextRange() { - assert this != EndMarker : "not allowed on sentinel"; - current = current.next; - } - - int currentFrom() { - return current.from; - } - - int currentTo() { - return current.to; - } - - boolean currentAtEnd() { - return current == FixedRange.EndMarker; - } - - boolean currentIntersects(TraceInterval it) { - return current.intersects(it); - } - - int currentIntersectsAt(TraceInterval it) { - return current.intersectsAt(it); - } - - // range creation - public void setFrom(int from) { - assert !isEmpty(); - first().from = from; - } - - private boolean isEmpty() { - return first() == FixedRange.EndMarker; - } - - public void addRange(int from, int to) { - if (isEmpty()) { - first = new FixedRange(from, to, first()); - return; - } - if (to <= to() && from >= from()) { - return; - } - if (from() == to) { - first().from = from; - } else { - first = new FixedRange(from, to, first()); - } - } - - @Override - public AllocatableValue location() { - return operand; - } - - /** - * Sentinel interval to denote the end of an interval list. - */ - static final FixedInterval EndMarker = new FixedInterval(Value.ILLEGAL); - - FixedInterval(AllocatableValue operand) { - assert operand != null; - this.operand = operand; - this.first = FixedRange.EndMarker; - this.current = FixedRange.EndMarker; - this.next = FixedInterval.EndMarker; - this.cachedTo = -1; - } - - int calcTo() { - assert first != FixedRange.EndMarker : "interval has no range"; - - FixedRange r = first; - while (r.next != FixedRange.EndMarker) { - r = r.next; - } - return r.to; - } - - // returns true if the opId is inside the interval - boolean covers(int opId, LIRInstruction.OperandMode mode) { - FixedRange cur = first; - - while (cur != FixedRange.EndMarker && cur.to < opId) { - cur = cur.next; - } - if (cur != FixedRange.EndMarker) { - assert cur.to != cur.next.from : "ranges not separated"; - - if (mode == LIRInstruction.OperandMode.DEF) { - return cur.from <= opId && opId < cur.to; - } else { - return cur.from <= opId && opId <= cur.to; - } - } - return false; - } - - // returns true if the interval has any hole between holeFrom and holeTo - // (even if the hole has only the length 1) - boolean hasHoleBetween(int holeFrom, int holeTo) { - assert holeFrom < holeTo : "check"; - assert from() <= holeFrom && holeTo <= to() : "index out of interval"; - - FixedRange cur = first; - while (cur != FixedRange.EndMarker) { - assert cur.to < cur.next.from : "no space between ranges"; - - // hole-range starts before this range . hole - if (holeFrom < cur.from) { - return true; - - // hole-range completely inside this range . no hole - } else { - if (holeTo <= cur.to) { - return false; - - // overlapping of hole-range with this range . hole - } else { - if (holeFrom <= cur.to) { - return true; - } - } - } - - cur = cur.next; - } - - return false; - } - - @Override - public String toString() { - String from = "?"; - String to = "?"; - if (first != null && first != FixedRange.EndMarker) { - from = String.valueOf(from()); - // to() may cache a computed value, modifying the current object, which is a bad idea - // for a printing function. Compute it directly instead. - to = String.valueOf(calcTo()); - } - String locationString = "@" + this.operand; - return asRegister(operand).number + ":" + operand + (isRegister(operand) ? "" : locationString) + "[" + from + "," + to + "]"; - } - - /** - * Gets a single line string for logging the details of this interval to a log stream. - */ - @Override - public String logString(TraceLinearScan allocator) { - StringBuilder buf = new StringBuilder(100); - buf.append("fix ").append(asRegister(operand).number).append(':').append(operand).append(' '); - - buf.append(" ranges{"); - - // print ranges - FixedRange cur = first; - while (cur != FixedRange.EndMarker) { - if (cur != first) { - buf.append(", "); - } - buf.append(cur); - cur = cur.next; - assert cur != null : "range list not closed with range sentinel"; - } - buf.append("}"); - return buf.toString(); - } - -}
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/FixedRange.java Tue Dec 08 12:29:24 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,109 +0,0 @@ -/* - * Copyright (c) 2015, 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.graal.lir.alloc.trace; - -/** - * Represents a range of integers from a start (inclusive) to an end (exclusive). - */ -public final class FixedRange { - - public static final FixedRange EndMarker = new FixedRange(Integer.MAX_VALUE, Integer.MAX_VALUE, null); - - /** - * The start of the range, inclusive. - */ - public int from; - - /** - * The end of the range, exclusive. - */ - public int to; - - /** - * A link to allow the range to be put into a singly linked list. - */ - public FixedRange next; - - boolean intersects(TraceInterval i) { - return intersectsAt(i) != -1; - } - - /** - * Creates a new range. - * - * @param from the start of the range, inclusive - * @param to the end of the range, exclusive - * @param next link to the next range in a linked list - */ - FixedRange(int from, int to, FixedRange next) { - this.from = from; - this.to = to; - this.next = next; - } - - int intersectsAt(TraceInterval other) { - FixedRange range = this; - assert other != null : "null ranges not allowed"; - assert range != EndMarker && other != TraceInterval.EndMarker : "empty ranges not allowed"; - int intervalFrom = other.from(); - int intervalTo = other.to(); - - do { - if (range.from < intervalFrom) { - if (range.to <= intervalFrom) { - range = range.next; - if (range == EndMarker) { - return -1; - } - } else { - return intervalFrom; - } - } else { - if (intervalFrom < range.from) { - if (intervalTo <= range.from) { - return -1; - } - return range.from; - } else { - assert range.from == intervalFrom; - if (range.from == range.to) { - range = range.next; - if (range == EndMarker) { - return -1; - } - } else { - if (intervalFrom == intervalTo) { - return -1; - } - return range.from; - } - } - } - } while (true); - } - - @Override - public String toString() { - return "[" + from + ", " + to + "]"; - } -}
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/IntervalHint.java Tue Dec 08 12:29:24 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2015, 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.graal.lir.alloc.trace; - -import jdk.vm.ci.meta.AllocatableValue; - -/** - * An interval that is a hint for an {@code TraceInterval interval}. - */ -abstract class IntervalHint { - - public abstract AllocatableValue location(); - - public abstract int from(); - - public abstract String logString(TraceLinearScan allocator); -}
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/RegisterVerifier.java Tue Dec 08 12:29:24 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,264 +0,0 @@ -/* - * 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.lir.alloc.trace; - -import static jdk.vm.ci.code.ValueUtil.asRegister; -import static jdk.vm.ci.code.ValueUtil.isRegister; - -import java.util.ArrayList; -import java.util.EnumSet; -import java.util.List; - -import jdk.vm.ci.code.Register; -import jdk.vm.ci.common.JVMCIError; -import jdk.vm.ci.meta.Value; - -import com.oracle.graal.compiler.common.cfg.AbstractBlockBase; -import com.oracle.graal.compiler.common.util.ArrayMap; -import com.oracle.graal.debug.Debug; -import com.oracle.graal.debug.Debug.Scope; -import com.oracle.graal.debug.Indent; -import com.oracle.graal.lir.InstructionValueConsumer; -import com.oracle.graal.lir.LIRInstruction; -import com.oracle.graal.lir.LIRInstruction.OperandFlag; -import com.oracle.graal.lir.LIRInstruction.OperandMode; - -/** - */ -final class RegisterVerifier { - - TraceLinearScan allocator; - List<AbstractBlockBase<?>> workList; // all blocks that must be processed - ArrayMap<TraceInterval[]> savedStates; // saved information of previous check - - // simplified access to methods of LinearScan - TraceInterval intervalAt(Value operand) { - return allocator.intervalFor(operand); - } - - // currently, only registers are processed - int stateSize() { - return allocator.numRegisters(); - } - - // accessors - TraceInterval[] stateForBlock(AbstractBlockBase<?> block) { - return savedStates.get(block.getId()); - } - - void setStateForBlock(AbstractBlockBase<?> block, TraceInterval[] savedState) { - savedStates.put(block.getId(), savedState); - } - - void addToWorkList(AbstractBlockBase<?> block) { - if (!workList.contains(block)) { - workList.add(block); - } - } - - RegisterVerifier(TraceLinearScan allocator) { - this.allocator = allocator; - workList = new ArrayList<>(16); - this.savedStates = new ArrayMap<>(); - - } - - @SuppressWarnings("try") - void verify(AbstractBlockBase<?> start) { - try (Scope s = Debug.scope("RegisterVerifier")) { - // setup input registers (method arguments) for first block - TraceInterval[] inputState = new TraceInterval[stateSize()]; - setStateForBlock(start, inputState); - addToWorkList(start); - - // main loop for verification - do { - AbstractBlockBase<?> block = workList.get(0); - workList.remove(0); - - processBlock(block); - } while (!workList.isEmpty()); - } - } - - @SuppressWarnings("try") - private void processBlock(AbstractBlockBase<?> block) { - try (Indent indent = Debug.logAndIndent("processBlock B%d", block.getId())) { - // must copy state because it is modified - TraceInterval[] inputState = copy(stateForBlock(block)); - - try (Indent indent2 = Debug.logAndIndent("Input-State of intervals:")) { - printState(inputState); - } - - // process all operations of the block - processOperations(block, inputState); - - try (Indent indent2 = Debug.logAndIndent("Output-State of intervals:")) { - printState(inputState); - } - - // iterate all successors - for (AbstractBlockBase<?> succ : block.getSuccessors()) { - processSuccessor(succ, inputState); - } - } - } - - protected void printState(TraceInterval[] inputState) { - for (int i = 0; i < stateSize(); i++) { - Register reg = allocator.getRegisters()[i]; - assert reg.number == i; - if (inputState[i] != null) { - Debug.log(" %6s %4d -- %s", reg, inputState[i].operandNumber, inputState[i]); - } else { - Debug.log(" %6s __", reg); - } - } - } - - private void processSuccessor(AbstractBlockBase<?> block, TraceInterval[] inputState) { - TraceInterval[] savedState = stateForBlock(block); - - if (savedState != null) { - // this block was already processed before. - // check if new inputState is consistent with savedState - - boolean savedStateCorrect = true; - for (int i = 0; i < stateSize(); i++) { - if (inputState[i] != savedState[i]) { - // current inputState and previous savedState assume a different - // interval in this register . assume that this register is invalid - if (savedState[i] != null) { - // invalidate old calculation only if it assumed that - // register was valid. when the register was already invalid, - // then the old calculation was correct. - savedStateCorrect = false; - savedState[i] = null; - - Debug.log("processSuccessor B%d: invalidating slot %d", block.getId(), i); - } - } - } - - if (savedStateCorrect) { - // already processed block with correct inputState - Debug.log("processSuccessor B%d: previous visit already correct", block.getId()); - } else { - // must re-visit this block - Debug.log("processSuccessor B%d: must re-visit because input state changed", block.getId()); - addToWorkList(block); - } - - } else { - // block was not processed before, so set initial inputState - Debug.log("processSuccessor B%d: initial visit", block.getId()); - - setStateForBlock(block, copy(inputState)); - addToWorkList(block); - } - } - - static TraceInterval[] copy(TraceInterval[] inputState) { - return inputState.clone(); - } - - static void statePut(TraceInterval[] inputState, Value location, TraceInterval interval) { - if (location != null && isRegister(location)) { - Register reg = asRegister(location); - int regNum = reg.number; - if (interval != null) { - Debug.log("%s = %s", reg, interval.operand); - } else if (inputState[regNum] != null) { - Debug.log("%s = null", reg); - } - - inputState[regNum] = interval; - } - } - - static boolean checkState(AbstractBlockBase<?> block, LIRInstruction op, TraceInterval[] inputState, Value operand, Value reg, TraceInterval interval) { - if (reg != null && isRegister(reg)) { - if (inputState[asRegister(reg).number] != interval) { - throw new JVMCIError( - "Error in register allocation: operation (%s) in block %s expected register %s (operand %s) to contain the value of interval %s but data-flow says it contains interval %s", - op, block, reg, operand, interval, inputState[asRegister(reg).number]); - } - } - return true; - } - - void processOperations(AbstractBlockBase<?> block, final TraceInterval[] inputState) { - List<LIRInstruction> ops = allocator.getLIR().getLIRforBlock(block); - InstructionValueConsumer useConsumer = new InstructionValueConsumer() { - - @Override - public void visitValue(LIRInstruction op, Value operand, OperandMode mode, EnumSet<OperandFlag> flags) { - // we skip spill moves inserted by the spill position optimization - if (TraceLinearScan.isVariableOrRegister(operand) && allocator.isProcessed(operand) && op.id() != TraceLinearScan.DOMINATOR_SPILL_MOVE_ID) { - TraceInterval interval = intervalAt(operand); - if (op.id() != -1) { - interval = interval.getSplitChildAtOpId(op.id(), mode, allocator); - } - - assert checkState(block, op, inputState, interval.operand, interval.location(), interval.splitParent()); - } - } - }; - - InstructionValueConsumer defConsumer = (op, operand, mode, flags) -> { - if (TraceLinearScan.isVariableOrRegister(operand) && allocator.isProcessed(operand)) { - TraceInterval interval = intervalAt(operand); - if (op.id() != -1) { - interval = interval.getSplitChildAtOpId(op.id(), mode, allocator); - } - - statePut(inputState, interval.location(), interval.splitParent()); - } - }; - - // visit all instructions of the block - for (int i = 0; i < ops.size(); i++) { - final LIRInstruction op = ops.get(i); - - if (Debug.isLogEnabled()) { - Debug.log("%s", op.toStringWithIdPrefix()); - } - - // check if input operands are correct - op.visitEachInput(useConsumer); - // invalidate all caller save registers at calls - if (op.destroysCallerSavedRegisters()) { - for (Register r : allocator.getRegisterAllocationConfig().getRegisterConfig().getCallerSaveRegisters()) { - statePut(inputState, r.asValue(), null); - } - } - op.visitEachAlive(useConsumer); - // set temp operands (some operations use temp operands also as output operands, so - // can't set them null) - op.visitEachTemp(defConsumer); - // set output operands - op.visitEachOutput(defConsumer); - } - } -}
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/TraceAllocationPhase.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/TraceAllocationPhase.java Tue Dec 08 12:30:15 2015 -0800 @@ -22,35 +22,23 @@ */ package com.oracle.graal.lir.alloc.trace; -import java.util.List; - -import jdk.vm.ci.code.TargetDescription; - import com.oracle.graal.compiler.common.alloc.RegisterAllocationConfig; -import com.oracle.graal.compiler.common.cfg.AbstractBlockBase; -import com.oracle.graal.lir.gen.LIRGenerationResult; +import com.oracle.graal.compiler.common.alloc.TraceBuilder.TraceBuilderResult; import com.oracle.graal.lir.gen.LIRGeneratorTool.MoveFactory; import com.oracle.graal.lir.phases.LIRPhase; public abstract class TraceAllocationPhase extends LIRPhase<TraceAllocationPhase.TraceAllocationContext> { public static final class TraceAllocationContext { - private final MoveFactory spillMoveFactory; - private final RegisterAllocationConfig registerAllocationConfig; + public final MoveFactory spillMoveFactory; + public final RegisterAllocationConfig registerAllocationConfig; + public final TraceBuilderResult<?> resultTraces; - public TraceAllocationContext(MoveFactory spillMoveFactory, RegisterAllocationConfig registerAllocationConfig) { + public TraceAllocationContext(MoveFactory spillMoveFactory, RegisterAllocationConfig registerAllocationConfig, TraceBuilderResult<?> resultTraces) { this.spillMoveFactory = spillMoveFactory; this.registerAllocationConfig = registerAllocationConfig; + this.resultTraces = resultTraces; } } - @Override - protected final <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, - TraceAllocationContext context) { - run(target, lirGenRes, codeEmittingOrder, linearScanOrder, context.spillMoveFactory, context.registerAllocationConfig); - } - - protected abstract <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, - MoveFactory spillMoveFactory, RegisterAllocationConfig registerAllocationConfig); - }
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/TraceGlobalMoveResolutionPhase.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/TraceGlobalMoveResolutionPhase.java Tue Dec 08 12:30:15 2015 -0800 @@ -37,7 +37,6 @@ import jdk.vm.ci.meta.AllocatableValue; import jdk.vm.ci.meta.Value; -import com.oracle.graal.compiler.common.alloc.RegisterAllocationConfig; import com.oracle.graal.compiler.common.alloc.TraceBuilder.TraceBuilderResult; import com.oracle.graal.compiler.common.cfg.AbstractBlockBase; import com.oracle.graal.debug.Debug; @@ -58,16 +57,10 @@ public abstract void addMapping(Value src, AllocatableValue dst); } - private final TraceBuilderResult<?> resultTraces; - - public TraceGlobalMoveResolutionPhase(TraceBuilderResult<?> resultTraces) { - this.resultTraces = resultTraces; - } - @Override - protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, MoveFactory spillMoveFactory, - RegisterAllocationConfig registerAllocationConfig) { - resolveGlobalDataFlow(resultTraces, lirGenRes, spillMoveFactory, target.arch); + protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, TraceAllocationContext context) { + MoveFactory spillMoveFactory = context.spillMoveFactory; + resolveGlobalDataFlow(context.resultTraces, lirGenRes, spillMoveFactory, target.arch); } @SuppressWarnings("try")
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/TraceGlobalMoveResolver.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/TraceGlobalMoveResolver.java Tue Dec 08 12:30:15 2015 -0800 @@ -229,8 +229,7 @@ } /** - * Checks if the {@linkplain TraceInterval#location() location} of {@code to} is not blocked or - * is only blocked by {@code from}. + * Checks if {@code to} is not blocked or is only blocked by {@code from}. */ private boolean safeToProcessMove(Value fromLocation, Value toLocation) { if (mightBeBlocked(toLocation)) { @@ -318,8 +317,8 @@ } /** - * @param fromOpr {@link TraceInterval#operand operand} of the {@code from} interval - * @param toOpr {@link TraceInterval#operand operand} of the {@code to} interval + * @param fromOpr Operand of the {@code from} interval + * @param toOpr Operand of the {@code to} interval */ private LIRInstruction createMove(Value fromOpr, AllocatableValue toOpr) { if (isStackSlotValue(toOpr) && isStackSlotValue(fromOpr)) {
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/TraceInterval.java Tue Dec 08 12:29:24 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1100 +0,0 @@ -/* - * Copyright (c) 2009, 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.graal.lir.alloc.trace; - -import static com.oracle.graal.compiler.common.GraalOptions.DetailedAsserts; -import static com.oracle.graal.lir.LIRValueUtil.isStackSlotValue; -import static com.oracle.graal.lir.LIRValueUtil.isVariable; -import static com.oracle.graal.lir.LIRValueUtil.isVirtualStackSlot; -import static jdk.vm.ci.code.ValueUtil.asRegister; -import static jdk.vm.ci.code.ValueUtil.isIllegal; -import static jdk.vm.ci.code.ValueUtil.isRegister; -import static jdk.vm.ci.code.ValueUtil.isStackSlot; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.EnumSet; -import java.util.List; - -import jdk.vm.ci.code.BailoutException; -import jdk.vm.ci.code.RegisterValue; -import jdk.vm.ci.code.StackSlot; -import jdk.vm.ci.common.JVMCIError; -import jdk.vm.ci.meta.AllocatableValue; -import jdk.vm.ci.meta.JavaConstant; -import jdk.vm.ci.meta.LIRKind; -import jdk.vm.ci.meta.Value; - -import com.oracle.graal.compiler.common.util.Util; -import com.oracle.graal.debug.TTY; -import com.oracle.graal.lir.LIRInstruction; -import com.oracle.graal.lir.Variable; - -/** - * Represents an interval in the {@linkplain TraceLinearScan linear scan register allocator}. - */ -final class TraceInterval extends IntervalHint { - - static final class AnyList { - - /** - * List of intervals whose binding is currently {@link RegisterBinding#Any}. - */ - public TraceInterval any; - - public AnyList(TraceInterval any) { - this.any = any; - } - - /** - * Gets the any list. - */ - public TraceInterval getAny() { - return any; - } - - /** - * Sets the any list. - */ - public void setAny(TraceInterval list) { - any = list; - } - - /** - * Adds an interval to a list sorted by {@linkplain TraceInterval#from() current from} - * positions. - * - * @param interval the interval to add - */ - public void addToListSortedByFromPositions(TraceInterval interval) { - TraceInterval list = getAny(); - TraceInterval prev = null; - TraceInterval cur = list; - while (cur.from() < interval.from()) { - prev = cur; - cur = cur.next; - } - TraceInterval result = list; - if (prev == null) { - // add to head of list - result = interval; - } else { - // add before 'cur' - prev.next = interval; - } - interval.next = cur; - setAny(result); - } - - /** - * Adds an interval to a list sorted by {@linkplain TraceInterval#from() start} positions - * and {@linkplain TraceInterval#firstUsage(RegisterPriority) first usage} positions. - * - * @param interval the interval to add - */ - public void addToListSortedByStartAndUsePositions(TraceInterval interval) { - TraceInterval list = getAny(); - TraceInterval prev = null; - TraceInterval cur = list; - while (cur.from() < interval.from() || (cur.from() == interval.from() && cur.firstUsage(RegisterPriority.None) < interval.firstUsage(RegisterPriority.None))) { - prev = cur; - cur = cur.next; - } - if (prev == null) { - list = interval; - } else { - prev.next = interval; - } - interval.next = cur; - setAny(list); - } - - /** - * Removes an interval from a list. - * - * @param i the interval to remove - */ - public void removeAny(TraceInterval i) { - TraceInterval list = getAny(); - TraceInterval prev = null; - TraceInterval cur = list; - while (cur != i) { - assert cur != null && cur != TraceInterval.EndMarker : "interval has not been found in list: " + i; - prev = cur; - cur = cur.next; - } - if (prev == null) { - setAny(cur.next); - } else { - prev.next = cur.next; - } - } - } - - /** - * Constants denoting the register usage priority for an interval. The constants are declared in - * increasing order of priority are are used to optimize spilling when multiple overlapping - * intervals compete for limited registers. - */ - public enum RegisterPriority { - /** - * No special reason for an interval to be allocated a register. - */ - None, - - /** - * Priority level for intervals live at the end of a loop. - */ - LiveAtLoopEnd, - - /** - * Priority level for intervals that should be allocated to a register. - */ - ShouldHaveRegister, - - /** - * Priority level for intervals that must be allocated to a register. - */ - MustHaveRegister; - - public static final RegisterPriority[] VALUES = values(); - - /** - * Determines if this priority is higher than or equal to a given priority. - */ - public boolean greaterEqual(RegisterPriority other) { - return ordinal() >= other.ordinal(); - } - - /** - * Determines if this priority is lower than a given priority. - */ - public boolean lessThan(RegisterPriority other) { - return ordinal() < other.ordinal(); - } - - public CharSequence shortName() { - return name().subSequence(0, 1); - } - } - - /** - * Constants denoting whether an interval is bound to a specific register. This models platform - * dependencies on register usage for certain instructions. - */ - enum RegisterBinding { - /** - * Interval is bound to a specific register as required by the platform. - */ - Fixed, - - /** - * Interval has no specific register requirements. - */ - Any, - - /** - * Interval is bound to a stack slot. - */ - Stack; - - public static final RegisterBinding[] VALUES = values(); - } - - /** - * Constants denoting the linear-scan states an interval may be in with respect to the - * {@linkplain TraceInterval#from() start} {@code position} of the interval being processed. - */ - enum State { - /** - * An interval that starts after {@code position}. - */ - Unhandled, - - /** - * An interval that {@linkplain TraceInterval#covers covers} {@code position} and has an - * assigned register. - */ - Active, - - /** - * An interval that starts before and ends after {@code position} but does not - * {@linkplain TraceInterval#covers cover} it due to a lifetime hole. - */ - Inactive, - - /** - * An interval that ends before {@code position} or is spilled to memory. - */ - Handled; - } - - /** - * Constants used in optimization of spilling of an interval. - */ - public enum SpillState { - /** - * Starting state of calculation: no definition found yet. - */ - NoDefinitionFound, - - /** - * One definition has already been found. Two consecutive definitions are treated as one - * (e.g. a consecutive move and add because of two-operand LIR form). The position of this - * definition is given by {@link TraceInterval#spillDefinitionPos()}. - */ - NoSpillStore, - - /** - * One spill move has already been inserted. - */ - OneSpillStore, - - /** - * The interval is spilled multiple times or is spilled in a loop. Place the store somewhere - * on the dominator path between the definition and the usages. - */ - SpillInDominator, - - /** - * The interval should be stored immediately after its definition to prevent multiple - * redundant stores. - */ - StoreAtDefinition, - - /** - * The interval starts in memory (e.g. method parameter), so a store is never necessary. - */ - StartInMemory, - - /** - * The interval has more than one definition (e.g. resulting from phi moves), so stores to - * memory are not optimized. - */ - NoOptimization; - - public static final EnumSet<SpillState> ALWAYS_IN_MEMORY = EnumSet.of(SpillInDominator, StoreAtDefinition, StartInMemory); - } - - /** - * The {@linkplain RegisterValue register} or {@linkplain Variable variable} for this interval - * prior to register allocation. - */ - public final AllocatableValue operand; - - /** - * The operand number for this interval's {@linkplain #operand operand}. - */ - public final int operandNumber; - - /** - * The {@linkplain RegisterValue register} or {@linkplain StackSlot spill slot} assigned to this - * interval. In case of a spilled interval which is re-materialized this is - * {@link Value#ILLEGAL}. - */ - private AllocatableValue location; - - /** - * The stack slot to which all splits of this interval are spilled if necessary. - */ - private AllocatableValue spillSlot; - - /** - * The kind of this interval. - */ - private LIRKind kind; - - /** - * The start of the range, inclusive. - */ - public int intFrom; - - /** - * The end of the range, exclusive. - */ - public int intTo; - - /** - * List of (use-positions, register-priorities) pairs, sorted by use-positions. - */ - private UsePosList usePosList; - - /** - * Link to next interval in a sorted list of intervals that ends with {@link #EndMarker}. - */ - TraceInterval next; - - /** - * The linear-scan state of this interval. - */ - State state; - - /** - * The interval from which this one is derived. If this is a {@linkplain #isSplitParent() split - * parent}, it points to itself. - */ - private TraceInterval splitParent; - - /** - * List of all intervals that are split off from this interval. This is only used if this is a - * {@linkplain #isSplitParent() split parent}. - */ - private List<TraceInterval> splitChildren = Collections.emptyList(); - - /** - * Current split child that has been active or inactive last (always stored in split parents). - */ - private TraceInterval currentSplitChild; - - /** - * Specifies if move is inserted between currentSplitChild and this interval when interval gets - * active the first time. - */ - private boolean insertMoveWhenActivated; - - /** - * For spill move optimization. - */ - private SpillState spillState; - - /** - * Position where this interval is defined (if defined only once). - */ - private int spillDefinitionPos; - - /** - * This interval should be assigned the same location as the hint interval. - */ - private IntervalHint locationHint; - - /** - * The value with which a spilled child interval can be re-materialized. Currently this must be - * a Constant. - */ - private JavaConstant materializedValue; - - /** - * The number of times {@link #addMaterializationValue(JavaConstant)} is called. - */ - private int numMaterializationValuesAdded; - - void assignLocation(AllocatableValue newLocation) { - if (isRegister(newLocation)) { - assert this.location == null : "cannot re-assign location for " + this; - if (newLocation.getLIRKind().equals(LIRKind.Illegal) && !kind.equals(LIRKind.Illegal)) { - this.location = asRegister(newLocation).asValue(kind); - return; - } - } else if (isIllegal(newLocation)) { - assert canMaterialize(); - } else { - assert this.location == null || isRegister(this.location) || (isVirtualStackSlot(this.location) && isStackSlot(newLocation)) : "cannot re-assign location for " + this; - assert isStackSlotValue(newLocation); - assert !newLocation.getLIRKind().equals(LIRKind.Illegal); - assert newLocation.getLIRKind().equals(this.kind); - } - this.location = newLocation; - } - - /** - * Gets the {@linkplain RegisterValue register} or {@linkplain StackSlot spill slot} assigned to - * this interval. - */ - @Override - public AllocatableValue location() { - return location; - } - - public LIRKind kind() { - assert !isRegister(operand) : "cannot access type for fixed interval"; - return kind; - } - - public void setKind(LIRKind kind) { - assert isRegister(operand) || this.kind().equals(LIRKind.Illegal) || this.kind().equals(kind) : "overwriting existing type"; - this.kind = kind; - } - - public boolean isEmpty() { - return intFrom == Integer.MAX_VALUE && intTo == Integer.MAX_VALUE; - } - - public void setTo(int pos) { - assert intFrom == Integer.MAX_VALUE || intFrom < pos; - intTo = pos; - } - - public void setFrom(int pos) { - assert intTo == Integer.MAX_VALUE || pos < intTo; - intFrom = pos; - } - - @Override - public int from() { - return intFrom; - } - - int to() { - return intTo; - } - - int numUsePositions() { - return usePosList.size(); - } - - public void setLocationHint(IntervalHint interval) { - locationHint = interval; - } - - public boolean isSplitParent() { - return splitParent == this; - } - - boolean isSplitChild() { - return splitParent != this; - } - - /** - * Gets the split parent for this interval. - */ - public TraceInterval splitParent() { - assert splitParent.isSplitParent() : "not a split parent: " + this; - return splitParent; - } - - /** - * Gets the canonical spill slot for this interval. - */ - public AllocatableValue spillSlot() { - return splitParent().spillSlot; - } - - public void setSpillSlot(AllocatableValue slot) { - assert isStackSlotValue(slot); - assert splitParent().spillSlot == null || (isVirtualStackSlot(splitParent().spillSlot) && isStackSlot(slot)) : "connot overwrite existing spill slot"; - splitParent().spillSlot = slot; - } - - TraceInterval currentSplitChild() { - return splitParent().currentSplitChild; - } - - void makeCurrentSplitChild() { - splitParent().currentSplitChild = this; - } - - boolean insertMoveWhenActivated() { - return insertMoveWhenActivated; - } - - void setInsertMoveWhenActivated(boolean b) { - insertMoveWhenActivated = b; - } - - // for spill optimization - public SpillState spillState() { - return splitParent().spillState; - } - - public int spillDefinitionPos() { - return splitParent().spillDefinitionPos; - } - - public void setSpillState(SpillState state) { - assert state.ordinal() >= spillState().ordinal() : "state cannot decrease"; - splitParent().spillState = state; - } - - public void setSpillDefinitionPos(int pos) { - assert spillState() == SpillState.SpillInDominator || spillState() == SpillState.NoDefinitionFound || spillDefinitionPos() == -1 : "cannot set the position twice"; - splitParent().spillDefinitionPos = pos; - } - - /** - * Returns true if this interval has a shadow copy on the stack that is correct after - * {@code opId}. - */ - public boolean inMemoryAt(int opId) { - return SpillState.ALWAYS_IN_MEMORY.contains(spillState()) && !canMaterialize() && opId > spillDefinitionPos(); - } - - void removeFirstUsePos() { - usePosList.removeLowestUsePos(); - } - - // test intersection - boolean intersects(TraceInterval i) { - return intersectsAt(i) != -1; - } - - int intersectsAt(TraceInterval i) { - TraceInterval i1; - TraceInterval i2; - if (i.from() < this.from()) { - i1 = i; - i2 = this; - } else { - i1 = this; - i2 = i; - } - assert i1.from() <= i2.from(); - - if (i1.to() <= i2.from()) { - return -1; - } - return i2.from(); - } - - /** - * Sentinel interval to denote the end of an interval list. - */ - static final TraceInterval EndMarker = new TraceInterval(Value.ILLEGAL, -1); - - TraceInterval(AllocatableValue operand, int operandNumber) { - assert operand != null; - this.operand = operand; - this.operandNumber = operandNumber; - if (isRegister(operand)) { - location = operand; - } else { - assert isIllegal(operand) || isVariable(operand); - } - this.kind = LIRKind.Illegal; - this.intFrom = Integer.MAX_VALUE; - this.intTo = Integer.MAX_VALUE; - this.usePosList = new UsePosList(4); - this.next = EndMarker; - this.spillState = SpillState.NoDefinitionFound; - this.spillDefinitionPos = -1; - splitParent = this; - currentSplitChild = this; - } - - /** - * Sets the value which is used for re-materialization. - */ - public void addMaterializationValue(JavaConstant value) { - if (numMaterializationValuesAdded == 0) { - materializedValue = value; - } else { - // Interval is defined on multiple places -> no materialization is possible. - materializedValue = null; - } - numMaterializationValuesAdded++; - } - - /** - * Returns true if this interval can be re-materialized when spilled. This means that no - * spill-moves are needed. Instead of restore-moves the {@link #materializedValue} is restored. - */ - public boolean canMaterialize() { - return getMaterializedValue() != null; - } - - /** - * Returns a value which can be moved to a register instead of a restore-move from stack. - */ - public JavaConstant getMaterializedValue() { - return splitParent().materializedValue; - } - - // consistency check of split-children - boolean checkSplitChildren() { - if (!splitChildren.isEmpty()) { - assert isSplitParent() : "only split parents can have children"; - - for (int i = 0; i < splitChildren.size(); i++) { - TraceInterval i1 = splitChildren.get(i); - - assert i1.splitParent() == this : "not a split child of this interval"; - assert i1.kind().equals(kind()) : "must be equal for all split children"; - assert (i1.spillSlot() == null && spillSlot == null) || i1.spillSlot().equals(spillSlot()) : "must be equal for all split children"; - - for (int j = i + 1; j < splitChildren.size(); j++) { - TraceInterval i2 = splitChildren.get(j); - - assert !i1.operand.equals(i2.operand) : "same register number"; - - if (i1.from() < i2.from()) { - assert i1.to() <= i2.from() && i1.to() < i2.to() : "intervals overlapping"; - } else { - assert i2.from() < i1.from() : "intervals start at same opId"; - assert i2.to() <= i1.from() && i2.to() < i1.to() : "intervals overlapping"; - } - } - } - } - - return true; - } - - public IntervalHint locationHint(boolean searchSplitChild) { - if (!searchSplitChild) { - return locationHint; - } - - if (locationHint != null) { - assert !(locationHint instanceof TraceInterval) || ((TraceInterval) locationHint).isSplitParent() : "ony split parents are valid hint registers"; - - if (locationHint.location() != null && isRegister(locationHint.location())) { - return locationHint; - } else if (locationHint instanceof TraceInterval) { - TraceInterval hint = (TraceInterval) locationHint; - if (!hint.splitChildren.isEmpty()) { - // search the first split child that has a register assigned - int len = hint.splitChildren.size(); - for (int i = 0; i < len; i++) { - TraceInterval interval = hint.splitChildren.get(i); - if (interval.location != null && isRegister(interval.location)) { - return interval; - } - } - } - } - } - - // no hint interval found that has a register assigned - return null; - } - - TraceInterval getSplitChildAtOpId(int opId, LIRInstruction.OperandMode mode, TraceLinearScan allocator) { - assert isSplitParent() : "can only be called for split parents"; - assert opId >= 0 : "invalid opId (method cannot be called for spill moves)"; - - if (splitChildren.isEmpty()) { - assert this.covers(opId, mode) : this + " does not cover " + opId; - return this; - } else { - TraceInterval result = null; - int len = splitChildren.size(); - - // in outputMode, the end of the interval (opId == cur.to()) is not valid - int toOffset = (mode == LIRInstruction.OperandMode.DEF ? 0 : 1); - - int i; - for (i = 0; i < len; i++) { - TraceInterval cur = splitChildren.get(i); - if (cur.from() <= opId && opId < cur.to() + toOffset) { - if (i > 0) { - // exchange current split child to start of list (faster access for next - // call) - Util.atPutGrow(splitChildren, i, splitChildren.get(0), null); - Util.atPutGrow(splitChildren, 0, cur, null); - } - - // interval found - result = cur; - break; - } - } - - assert checkSplitChild(result, opId, allocator, toOffset, mode); - return result; - } - } - - private boolean checkSplitChild(TraceInterval result, int opId, TraceLinearScan allocator, int toOffset, LIRInstruction.OperandMode mode) { - if (result == null) { - // this is an error - StringBuilder msg = new StringBuilder(this.toString()).append(" has no child at ").append(opId); - if (!splitChildren.isEmpty()) { - TraceInterval firstChild = splitChildren.get(0); - TraceInterval lastChild = splitChildren.get(splitChildren.size() - 1); - msg.append(" (first = ").append(firstChild).append(", last = ").append(lastChild).append(")"); - } - throw new JVMCIError("Linear Scan Error: %s", msg); - } - - if (!splitChildren.isEmpty()) { - for (TraceInterval interval : splitChildren) { - if (interval != result && interval.from() <= opId && opId < interval.to() + toOffset) { - TTY.println(String.format("two valid result intervals found for opId %d: %d and %d", opId, result.operandNumber, interval.operandNumber)); - TTY.println(result.logString(allocator)); - TTY.println(interval.logString(allocator)); - throw new BailoutException("two valid result intervals found"); - } - } - } - assert result.covers(opId, mode) : "opId not covered by interval"; - return true; - } - - // returns the interval that covers the given opId or null if there is none - TraceInterval getIntervalCoveringOpId(int opId) { - assert opId >= 0 : "invalid opId"; - assert opId < to() : "can only look into the past"; - - if (opId >= from()) { - return this; - } - - TraceInterval parent = splitParent(); - TraceInterval result = null; - - assert !parent.splitChildren.isEmpty() : "no split children available"; - int len = parent.splitChildren.size(); - - for (int i = len - 1; i >= 0; i--) { - TraceInterval cur = parent.splitChildren.get(i); - if (cur.from() <= opId && opId < cur.to()) { - assert result == null : "covered by multiple split children " + result + " and " + cur; - result = cur; - } - } - - return result; - } - - // returns the last split child that ends before the given opId - TraceInterval getSplitChildBeforeOpId(int opId) { - assert opId >= 0 : "invalid opId"; - - TraceInterval parent = splitParent(); - TraceInterval result = null; - - assert !parent.splitChildren.isEmpty() : "no split children available"; - int len = parent.splitChildren.size(); - - for (int i = len - 1; i >= 0; i--) { - TraceInterval cur = parent.splitChildren.get(i); - if (cur.to() <= opId && (result == null || result.to() < cur.to())) { - result = cur; - } - } - - assert result != null : "no split child found"; - return result; - } - - // checks if opId is covered by any split child - boolean splitChildCovers(int opId, LIRInstruction.OperandMode mode) { - assert isSplitParent() : "can only be called for split parents"; - assert opId >= 0 : "invalid opId (method can not be called for spill moves)"; - - if (splitChildren.isEmpty()) { - // simple case if interval was not split - return covers(opId, mode); - - } else { - // extended case: check all split children - int len = splitChildren.size(); - for (int i = 0; i < len; i++) { - TraceInterval cur = splitChildren.get(i); - if (cur.covers(opId, mode)) { - return true; - } - } - return false; - } - } - - private RegisterPriority adaptPriority(RegisterPriority priority) { - /* - * In case of re-materialized values we require that use-operands are registers, because we - * don't have the value in a stack location. (Note that ShouldHaveRegister means that the - * operand can also be a StackSlot). - */ - if (priority == RegisterPriority.ShouldHaveRegister && canMaterialize()) { - return RegisterPriority.MustHaveRegister; - } - return priority; - } - - // Note: use positions are sorted descending . first use has highest index - int firstUsage(RegisterPriority minRegisterPriority) { - assert isVariable(operand) : "cannot access use positions for fixed intervals"; - - for (int i = usePosList.size() - 1; i >= 0; --i) { - RegisterPriority registerPriority = adaptPriority(usePosList.registerPriority(i)); - if (registerPriority.greaterEqual(minRegisterPriority)) { - return usePosList.usePos(i); - } - } - return Integer.MAX_VALUE; - } - - int nextUsage(RegisterPriority minRegisterPriority, int from) { - assert isVariable(operand) : "cannot access use positions for fixed intervals"; - - for (int i = usePosList.size() - 1; i >= 0; --i) { - int usePos = usePosList.usePos(i); - if (usePos >= from && adaptPriority(usePosList.registerPriority(i)).greaterEqual(minRegisterPriority)) { - return usePos; - } - } - return Integer.MAX_VALUE; - } - - int nextUsageExact(RegisterPriority exactRegisterPriority, int from) { - assert isVariable(operand) : "cannot access use positions for fixed intervals"; - - for (int i = usePosList.size() - 1; i >= 0; --i) { - int usePos = usePosList.usePos(i); - if (usePos >= from && adaptPriority(usePosList.registerPriority(i)) == exactRegisterPriority) { - return usePos; - } - } - return Integer.MAX_VALUE; - } - - int previousUsage(RegisterPriority minRegisterPriority, int from) { - assert isVariable(operand) : "cannot access use positions for fixed intervals"; - - int prev = -1; - for (int i = usePosList.size() - 1; i >= 0; --i) { - int usePos = usePosList.usePos(i); - if (usePos > from) { - return prev; - } - if (adaptPriority(usePosList.registerPriority(i)).greaterEqual(minRegisterPriority)) { - prev = usePos; - } - } - return prev; - } - - public void addUsePos(int pos, RegisterPriority registerPriority) { - assert isEmpty() || covers(pos, LIRInstruction.OperandMode.USE) : String.format("use position %d not covered by live range of interval %s", pos, this); - - // do not add use positions for precolored intervals because they are never used - if (registerPriority != RegisterPriority.None && isVariable(operand)) { - if (DetailedAsserts.getValue()) { - for (int i = 0; i < usePosList.size(); i++) { - assert pos <= usePosList.usePos(i) : "already added a use-position with lower position"; - if (i > 0) { - assert usePosList.usePos(i) < usePosList.usePos(i - 1) : "not sorted descending"; - } - } - } - - // Note: addUse is called in descending order, so list gets sorted - // automatically by just appending new use positions - int len = usePosList.size(); - if (len == 0 || usePosList.usePos(len - 1) > pos) { - usePosList.add(pos, registerPriority); - } else if (usePosList.registerPriority(len - 1).lessThan(registerPriority)) { - assert usePosList.usePos(len - 1) == pos : "list not sorted correctly"; - usePosList.setRegisterPriority(len - 1, registerPriority); - } - } - } - - public void addRange(int from, int to) { - assert from < to : "invalid range"; - - if (from < intFrom) { - setFrom(from); - } - if (intTo == Integer.MAX_VALUE || intTo < to) { - setTo(to); - } - } - - TraceInterval newSplitChild(TraceLinearScan allocator) { - // allocate new interval - TraceInterval parent = splitParent(); - TraceInterval result = allocator.createDerivedInterval(parent); - result.setKind(kind()); - - result.splitParent = parent; - result.setLocationHint(parent); - - // insert new interval in children-list of parent - if (parent.splitChildren.isEmpty()) { - assert isSplitParent() : "list must be initialized at first split"; - - // Create new non-shared list - parent.splitChildren = new ArrayList<>(4); - parent.splitChildren.add(this); - } - parent.splitChildren.add(result); - - return result; - } - - /** - * Splits this interval at a specified position and returns the remainder as a new <i>child</i> - * interval of this interval's {@linkplain #splitParent() parent} interval. - * <p> - * When an interval is split, a bi-directional link is established between the original - * <i>parent</i> interval and the <i>children</i> intervals that are split off this interval. - * When a split child is split again, the new created interval is a direct child of the original - * parent. That is, there is no tree of split children stored, just a flat list. All split - * children are spilled to the same {@linkplain #spillSlot spill slot}. - * - * @param splitPos the position at which to split this interval - * @param allocator the register allocator context - * @return the child interval split off from this interval - */ - TraceInterval split(int splitPos, TraceLinearScan allocator) { - assert isVariable(operand) : "cannot split fixed intervals"; - - // allocate new interval - TraceInterval result = newSplitChild(allocator); - - // split the ranges - result.setTo(intTo); - result.setFrom(splitPos); - intTo = splitPos; - - // split list of use positions - result.usePosList = usePosList.splitAt(splitPos); - - if (DetailedAsserts.getValue()) { - for (int i = 0; i < usePosList.size(); i++) { - assert usePosList.usePos(i) < splitPos; - } - for (int i = 0; i < result.usePosList.size(); i++) { - assert result.usePosList.usePos(i) >= splitPos; - } - } - return result; - } - - // returns true if the opId is inside the interval - boolean covers(int opId, LIRInstruction.OperandMode mode) { - if (mode == LIRInstruction.OperandMode.DEF) { - return from() <= opId && opId < to(); - } - return from() <= opId && opId <= to(); - } - - @Override - public String toString() { - String from = "?"; - String to = "?"; - if (!isEmpty()) { - from = String.valueOf(from()); - to = String.valueOf(to()); - } - String locationString = this.location == null ? "" : "@" + this.location; - return operandNumber + ":" + operand + (isRegister(operand) ? "" : locationString) + "[" + from + "," + to + "]"; - } - - /** - * Gets the use position information for this interval. - */ - public UsePosList usePosList() { - return usePosList; - } - - /** - * Gets a single line string for logging the details of this interval to a log stream. - * - * @param allocator the register allocator context - */ - @Override - public String logString(TraceLinearScan allocator) { - StringBuilder buf = new StringBuilder(100); - buf.append("any ").append(operandNumber).append(':').append(operand).append(' '); - if (!isRegister(operand)) { - if (location != null) { - buf.append("location{").append(location).append("} "); - } - } - - buf.append("hints{").append(splitParent.operandNumber); - IntervalHint hint = locationHint(false); - if (hint != null) { - buf.append(", ").append(hint.location()); - } - buf.append("} ranges{"); - - // print range - buf.append("[" + from() + ", " + to() + "]"); - buf.append("} uses{"); - - // print use positions - int prev = -1; - for (int i = usePosList.size() - 1; i >= 0; --i) { - assert prev < usePosList.usePos(i) : "use positions not sorted"; - if (i != usePosList.size() - 1) { - buf.append(", "); - } - buf.append(usePosList.usePos(i)).append(':').append(usePosList.registerPriority(i).shortName()); - prev = usePosList.usePos(i); - } - buf.append("} spill-state{").append(spillState()).append("}"); - if (canMaterialize()) { - buf.append(" (remat:").append(getMaterializedValue().toString()).append(")"); - } - return buf.toString(); - } - - List<TraceInterval> getSplitChildren() { - return Collections.unmodifiableList(splitChildren); - } - - boolean isFixedInterval() { - return isRegister(operand); - } - - private static boolean isDefinitionPosition(int usePos) { - return (usePos & 1) == 1; - } - - int currentFrom(int currentPosition) { - assert isFixedInterval(); - for (int i = 0; i < usePosList.size(); i++) { - int usePos = usePosList.usePos(i); - if (usePos <= currentPosition && isDefinitionPosition(usePos)) { - return usePos; - } - - } - return Integer.MAX_VALUE; - } - - int currentIntersectsAt(int currentPosition, TraceInterval current) { - assert isFixedInterval(); - assert !current.isFixedInterval(); - int from = Integer.MAX_VALUE; - int to = Integer.MIN_VALUE; - - for (int i = 0; i < usePosList.size(); i++) { - int usePos = usePosList.usePos(i); - if (isDefinitionPosition(usePos)) { - if (usePos <= currentPosition) { - from = usePos; - break; - } - to = Integer.MIN_VALUE; - } else { - if (to < usePos) { - to = usePos; - } - } - } - if (from < current.from()) { - if (to <= current.from()) { - return -1; - } - return current.from(); - } else { - if (current.to() <= from) { - return -1; - } - return from; - } - } -}
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/TraceIntervalDumper.java Tue Dec 08 12:29:24 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2015, 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.graal.lir.alloc.trace; - -import static jdk.vm.ci.code.ValueUtil.isRegister; -import jdk.vm.ci.meta.AllocatableValue; -import jdk.vm.ci.meta.Value; - -import com.oracle.graal.lir.debug.IntervalDumper; - -final class TraceIntervalDumper implements IntervalDumper { - private final FixedInterval[] fixedIntervals; - private final TraceInterval[] intervals; - - public TraceIntervalDumper(FixedInterval[] fixedIntervals, TraceInterval[] intervals) { - this.fixedIntervals = fixedIntervals; - this.intervals = intervals; - } - - public void visitIntervals(IntervalVisitor visitor) { - for (FixedInterval interval : fixedIntervals) { - if (interval != null) { - printFixedInterval(interval, visitor); - } - } - for (TraceInterval interval : intervals) { - if (interval != null) { - printInterval(interval, visitor); - } - } - } - - private static void printFixedInterval(FixedInterval interval, IntervalVisitor visitor) { - Value hint = null; - AllocatableValue operand = interval.operand; - String type = "fixed"; - char typeChar = operand.getPlatformKind().getTypeChar(); - visitor.visitIntervalStart(operand, operand, operand, hint, type, typeChar); - - // print ranges - for (FixedRange range = interval.first(); range != FixedRange.EndMarker; range = range.next) { - visitor.visitRange(range.from, range.to); - } - - // no use positions - - visitor.visitIntervalEnd("NOT_SUPPORTED"); - - } - - private static void printInterval(TraceInterval interval, IntervalVisitor visitor) { - Value hint = interval.locationHint(false) != null ? interval.locationHint(false).location() : null; - AllocatableValue operand = interval.operand; - String type = isRegister(operand) ? "fixed" : operand.getLIRKind().getPlatformKind().toString(); - char typeChar = operand.getPlatformKind().getTypeChar(); - visitor.visitIntervalStart(interval.splitParent().operand, operand, interval.location(), hint, type, typeChar); - - // print ranges - visitor.visitRange(interval.from(), interval.to()); - - // print use positions - int prev = -1; - UsePosList usePosList = interval.usePosList(); - for (int i = usePosList.size() - 1; i >= 0; --i) { - assert prev < usePosList.usePos(i) : "use positions not sorted"; - visitor.visitUsePos(usePosList.usePos(i), usePosList.registerPriority(i)); - prev = usePosList.usePos(i); - } - - visitor.visitIntervalEnd(interval.spillState()); - } - -}
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/TraceIntervalWalker.java Tue Dec 08 12:29:24 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,323 +0,0 @@ -/* - * Copyright (c) 2009, 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.graal.lir.alloc.trace; - -import jdk.vm.ci.common.JVMCIError; - -import com.oracle.graal.debug.Debug; -import com.oracle.graal.debug.Indent; -import com.oracle.graal.lir.alloc.trace.FixedInterval.FixedList; -import com.oracle.graal.lir.alloc.trace.TraceInterval.AnyList; -import com.oracle.graal.lir.alloc.trace.TraceInterval.RegisterBinding; -import com.oracle.graal.lir.alloc.trace.TraceInterval.State; - -/** - */ -class TraceIntervalWalker { - - protected final TraceLinearScan allocator; - - /** - * Sorted list of intervals, not live before the current position. - */ - protected AnyList unhandledAnyList; - - /** - * Sorted list of intervals, live at the current position. - */ - protected AnyList activeAnyList; - protected FixedList activeFixedList; - - /** - * Sorted list of intervals in a life time hole at the current position. - */ - protected FixedList inactiveFixedList; - - /** - * The current position (intercept point through the intervals). - */ - protected int currentPosition; - - /** - * Processes the {@code currentInterval} interval in an attempt to allocate a physical register - * to it and thus allow it to be moved to a list of {@linkplain #activeAnyList active} - * intervals. - * - * @param currentInterval The interval to be activated. - * - * @return {@code true} if a register was allocated to the {@code currentInterval} interval - */ - protected boolean activateCurrent(TraceInterval currentInterval) { - if (Debug.isLogEnabled()) { - logCurrentStatus(); - } - return true; - } - - @SuppressWarnings("try") - protected void logCurrentStatus() { - try (Indent i = Debug.logAndIndent("active:")) { - logList(activeFixedList.getFixed()); - logList(activeAnyList.getAny()); - } - try (Indent i = Debug.logAndIndent("inactive(fixed):")) { - logList(inactiveFixedList.getFixed()); - } - } - - private void logList(FixedInterval i) { - for (FixedInterval interval = i; interval != FixedInterval.EndMarker; interval = interval.next) { - Debug.log("%s", interval.logString(allocator)); - } - } - - private void logList(TraceInterval i) { - for (TraceInterval interval = i; interval != TraceInterval.EndMarker; interval = interval.next) { - Debug.log("%s", interval.logString(allocator)); - } - } - - void walkBefore(int lirOpId) { - walkTo(lirOpId - 1); - } - - void walk() { - walkTo(Integer.MAX_VALUE); - } - - /** - * Creates a new interval walker. - * - * @param allocator the register allocator context - * @param unhandledFixed the list of unhandled {@linkplain RegisterBinding#Fixed fixed} - * intervals - * @param unhandledAny the list of unhandled {@linkplain RegisterBinding#Any non-fixed} - * intervals - */ - TraceIntervalWalker(TraceLinearScan allocator, FixedInterval unhandledFixed, TraceInterval unhandledAny) { - this.allocator = allocator; - - unhandledAnyList = new AnyList(unhandledAny); - activeAnyList = new AnyList(TraceInterval.EndMarker); - activeFixedList = new FixedList(FixedInterval.EndMarker); - // we don't need a separate unhandled list for fixed. - inactiveFixedList = new FixedList(unhandledFixed); - currentPosition = -1; - } - - protected void removeFromList(TraceInterval interval) { - if (interval.state == State.Active) { - activeAnyList.removeAny(interval); - } else { - assert interval.state == State.Inactive : "invalid state"; - // inactiveAnyLists.removeAny(interval); - throw JVMCIError.shouldNotReachHere(); - } - } - - /** - * Walks up to {@code from} and updates the state of {@link FixedInterval fixed intervals}. - * - * Fixed intervals can switch back and forth between the states {@link State#Active} and - * {@link State#Inactive} (and eventually to {@link State#Handled} but handled intervals are not - * managed). - */ - @SuppressWarnings("try") - private void walkToFixed(State state, int from) { - assert state == State.Active || state == State.Inactive : "wrong state"; - FixedInterval prevprev = null; - FixedInterval prev = (state == State.Active) ? activeFixedList.getFixed() : inactiveFixedList.getFixed(); - FixedInterval next = prev; - if (Debug.isLogEnabled()) { - try (Indent i = Debug.logAndIndent("walkToFixed(%s, %d):", state, from)) { - logList(next); - } - } - while (next.currentFrom() <= from) { - FixedInterval cur = next; - next = cur.next; - - boolean rangeHasChanged = false; - while (cur.currentTo() <= from) { - cur.nextRange(); - rangeHasChanged = true; - } - - // also handle move from inactive list to active list - rangeHasChanged = rangeHasChanged || (state == State.Inactive && cur.currentFrom() <= from); - - if (rangeHasChanged) { - // remove cur from list - if (prevprev == null) { - if (state == State.Active) { - activeFixedList.setFixed(next); - } else { - inactiveFixedList.setFixed(next); - } - } else { - prevprev.next = next; - } - prev = next; - TraceInterval.State newState; - if (cur.currentAtEnd()) { - // move to handled state (not maintained as a list) - newState = State.Handled; - } else { - if (cur.currentFrom() <= from) { - // sort into active list - activeFixedList.addToListSortedByCurrentFromPositions(cur); - newState = State.Active; - } else { - // sort into inactive list - inactiveFixedList.addToListSortedByCurrentFromPositions(cur); - newState = State.Inactive; - } - if (prev == cur) { - assert state == newState; - prevprev = prev; - prev = cur.next; - } - } - intervalMoved(cur, state, newState); - } else { - prevprev = prev; - prev = cur.next; - } - } - } - - /** - * Walks up to {@code from} and updates the state of {@link TraceInterval intervals}. - * - * Trace intervals can switch once from {@link State#Unhandled} to {@link State#Active} and then - * to {@link State#Handled} but handled intervals are not managed. - */ - @SuppressWarnings("try") - private void walkToAny(int from) { - TraceInterval prevprev = null; - TraceInterval prev = activeAnyList.getAny(); - TraceInterval next = prev; - if (Debug.isLogEnabled()) { - try (Indent i = Debug.logAndIndent("walkToAny(%d):", from)) { - logList(next); - } - } - while (next.from() <= from) { - TraceInterval cur = next; - next = cur.next; - - if (cur.to() <= from) { - // remove cur from list - if (prevprev == null) { - activeAnyList.setAny(next); - } else { - prevprev.next = next; - } - intervalMoved(cur, State.Active, State.Handled); - } else { - prevprev = prev; - } - prev = next; - } - } - - /** - * Get the next interval from {@linkplain #unhandledAnyList} which starts before or at - * {@code toOpId}. The returned interval is removed. - * - * @postcondition all intervals in {@linkplain #unhandledAnyList} start after {@code toOpId}. - * - * @return The next interval or null if there is no {@linkplain #unhandledAnyList unhandled} - * interval at position {@code toOpId}. - */ - private TraceInterval nextInterval(int toOpId) { - TraceInterval any = unhandledAnyList.getAny(); - - if (any != TraceInterval.EndMarker) { - TraceInterval currentInterval = unhandledAnyList.getAny(); - if (toOpId < currentInterval.from()) { - return null; - } - - unhandledAnyList.setAny(currentInterval.next); - currentInterval.next = TraceInterval.EndMarker; - return currentInterval; - } - return null; - - } - - /** - * Walk up to {@code toOpId}. - * - * @postcondition {@link #currentPosition} is set to {@code toOpId}, {@link #activeFixedList} - * and {@link #inactiveFixedList} are populated and {@link TraceInterval#state}s - * are up to date. - */ - @SuppressWarnings("try") - protected void walkTo(int toOpId) { - assert currentPosition <= toOpId : "can not walk backwards"; - for (TraceInterval currentInterval = nextInterval(toOpId); currentInterval != null; currentInterval = nextInterval(toOpId)) { - int opId = currentInterval.from(); - - // set currentPosition prior to call of walkTo - currentPosition = opId; - - // update unhandled stack intervals - // updateUnhandledStackIntervals(opId); - - // call walkTo even if currentPosition == id - walkToFixed(State.Active, opId); - walkToFixed(State.Inactive, opId); - walkToAny(opId); - - try (Indent indent = Debug.logAndIndent("walk to op %d", opId)) { - currentInterval.state = State.Active; - if (activateCurrent(currentInterval)) { - activeAnyList.addToListSortedByFromPositions(currentInterval); - intervalMoved(currentInterval, State.Unhandled, State.Active); - } - } - } - // set currentPosition prior to call of walkTo - currentPosition = toOpId; - - if (currentPosition <= allocator.maxOpId()) { - // update unhandled stack intervals - // updateUnhandledStackIntervals(toOpId); - - // call walkTo if still in range - walkToFixed(State.Active, toOpId); - walkToFixed(State.Inactive, toOpId); - walkToAny(toOpId); - } - } - - private void intervalMoved(IntervalHint interval, State from, State to) { - // intervalMoved() is called whenever an interval moves from one interval list to another. - // In the implementation of this method it is prohibited to move the interval to any list. - if (Debug.isLogEnabled()) { - Debug.log("interval moved from %s to %s: %s", from, to, interval.logString(allocator)); - } - } -}
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/TraceLinearScan.java Tue Dec 08 12:29:24 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1012 +0,0 @@ -/* - * Copyright (c) 2009, 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.graal.lir.alloc.trace; - -import static com.oracle.graal.compiler.common.GraalOptions.DetailedAsserts; -import static com.oracle.graal.lir.LIRValueUtil.isVariable; -import static jdk.vm.ci.code.CodeUtil.isEven; -import static jdk.vm.ci.code.ValueUtil.asRegister; -import static jdk.vm.ci.code.ValueUtil.asRegisterValue; -import static jdk.vm.ci.code.ValueUtil.isIllegal; -import static jdk.vm.ci.code.ValueUtil.isLegal; -import static jdk.vm.ci.code.ValueUtil.isRegister; - -import java.util.Arrays; -import java.util.BitSet; -import java.util.EnumSet; -import java.util.List; - -import jdk.vm.ci.code.BailoutException; -import jdk.vm.ci.code.Register; -import jdk.vm.ci.code.RegisterAttributes; -import jdk.vm.ci.code.RegisterValue; -import jdk.vm.ci.code.TargetDescription; -import jdk.vm.ci.common.JVMCIError; -import jdk.vm.ci.meta.AllocatableValue; -import jdk.vm.ci.meta.LIRKind; -import jdk.vm.ci.meta.Value; -import jdk.vm.ci.options.NestedBooleanOptionValue; -import jdk.vm.ci.options.Option; -import jdk.vm.ci.options.OptionType; -import jdk.vm.ci.options.OptionValue; - -import com.oracle.graal.compiler.common.alloc.RegisterAllocationConfig; -import com.oracle.graal.compiler.common.alloc.TraceBuilder.TraceBuilderResult; -import com.oracle.graal.compiler.common.cfg.AbstractBlockBase; -import com.oracle.graal.compiler.common.cfg.BlockMap; -import com.oracle.graal.debug.Debug; -import com.oracle.graal.debug.Debug.Scope; -import com.oracle.graal.debug.Indent; -import com.oracle.graal.lir.LIR; -import com.oracle.graal.lir.LIRInstruction; -import com.oracle.graal.lir.LIRInstruction.OperandFlag; -import com.oracle.graal.lir.LIRInstruction.OperandMode; -import com.oracle.graal.lir.ValueConsumer; -import com.oracle.graal.lir.Variable; -import com.oracle.graal.lir.VirtualStackSlot; -import com.oracle.graal.lir.alloc.trace.TraceLinearScanAllocationPhase.TraceLinearScanAllocationContext; -import com.oracle.graal.lir.framemap.FrameMapBuilder; -import com.oracle.graal.lir.gen.LIRGenerationResult; -import com.oracle.graal.lir.gen.LIRGeneratorTool.MoveFactory; -import com.oracle.graal.lir.phases.LIRPhase; - -/** - * An implementation of the linear scan register allocator algorithm described in <a - * href="http://doi.acm.org/10.1145/1064979.1064998" - * >"Optimized Interval Splitting in a Linear Scan Register Allocator"</a> by Christian Wimmer and - * Hanspeter Moessenboeck. - */ -final class TraceLinearScan { - - public static class Options { - // @formatter:off - @Option(help = "Enable spill position optimization", type = OptionType.Debug) - public static final OptionValue<Boolean> LIROptTraceRAEliminateSpillMoves = new NestedBooleanOptionValue(LIRPhase.Options.LIROptimization, true); - // @formatter:on - } - - private static final TraceLinearScanRegisterAllocationPhase TRACE_LINEAR_SCAN_REGISTER_ALLOCATION_PHASE = new TraceLinearScanRegisterAllocationPhase(); - private static final TraceLinearScanAssignLocationsPhase TRACE_LINEAR_SCAN_ASSIGN_LOCATIONS_PHASE = new TraceLinearScanAssignLocationsPhase(); - private static final TraceLinearScanEliminateSpillMovePhase TRACE_LINEAR_SCAN_ELIMINATE_SPILL_MOVE_PHASE = new TraceLinearScanEliminateSpillMovePhase(); - private static final TraceLinearScanResolveDataFlowPhase TRACE_LINEAR_SCAN_RESOLVE_DATA_FLOW_PHASE = new TraceLinearScanResolveDataFlowPhase(); - private static final TraceLinearScanLifetimeAnalysisPhase TRACE_LINEAR_SCAN_LIFETIME_ANALYSIS_PHASE = new TraceLinearScanLifetimeAnalysisPhase(); - - public static class BlockData { - - /** - * Bit map specifying which operands are live upon entry to this block. These are values - * used in this block or any of its successors where such value are not defined in this - * block. The bit index of an operand is its - * {@linkplain TraceLinearScan#operandNumber(Value) operand number}. - */ - public BitSet liveIn; - - /** - * Bit map specifying which operands are live upon exit from this block. These are values - * used in a successor block that are either defined in this block or were live upon entry - * to this block. The bit index of an operand is its - * {@linkplain TraceLinearScan#operandNumber(Value) operand number}. - */ - public BitSet liveOut; - - /** - * Bit map specifying which operands are used (before being defined) in this block. That is, - * these are the values that are live upon entry to the block. The bit index of an operand - * is its {@linkplain TraceLinearScan#operandNumber(Value) operand number}. - */ - public BitSet liveGen; - - /** - * Bit map specifying which operands are defined/overwritten in this block. The bit index of - * an operand is its {@linkplain TraceLinearScan#operandNumber(Value) operand number}. - */ - public BitSet liveKill; - } - - public static final int DOMINATOR_SPILL_MOVE_ID = -2; - private static final int SPLIT_INTERVALS_CAPACITY_RIGHT_SHIFT = 1; - - private final LIR ir; - private final FrameMapBuilder frameMapBuilder; - private final RegisterAttributes[] registerAttributes; - private final Register[] registers; - private final RegisterAllocationConfig regAllocConfig; - private final MoveFactory moveFactory; - - private final BlockMap<BlockData> blockData; - - /** - * List of blocks in linear-scan order. This is only correct as long as the CFG does not change. - */ - private final List<? extends AbstractBlockBase<?>> sortedBlocks; - - /** @see #fixedIntervals() */ - private final FixedInterval[] fixedIntervals; - - /** @see #intervals() */ - private TraceInterval[] intervals; - - /** - * The number of valid entries in {@link #intervals}. - */ - private int intervalsSize; - - /** - * The index of the first entry in {@link #intervals} for a - * {@linkplain #createDerivedInterval(TraceInterval) derived interval}. - */ - private int firstDerivedIntervalIndex = -1; - - /** - * Intervals sorted by {@link TraceInterval#from()}. - */ - private TraceInterval[] sortedIntervals; - - /** - * Fixed intervals sorted by {@link FixedInterval#from()}. - */ - private FixedInterval[] sortedFixedIntervals; - - /** - * Map from an instruction {@linkplain LIRInstruction#id id} to the instruction. Entries should - * be retrieved with {@link #instructionForId(int)} as the id is not simply an index into this - * array. - */ - private LIRInstruction[] opIdToInstructionMap; - - /** - * Map from an instruction {@linkplain LIRInstruction#id id} to the - * {@linkplain AbstractBlockBase block} containing the instruction. Entries should be retrieved - * with {@link #blockForId(int)} as the id is not simply an index into this array. - */ - private AbstractBlockBase<?>[] opIdToBlockMap; - - protected final TraceBuilderResult<?> traceBuilderResult; - private final boolean neverSpillConstants; - - protected TraceLinearScan(TargetDescription target, LIRGenerationResult res, MoveFactory spillMoveFactory, RegisterAllocationConfig regAllocConfig, - List<? extends AbstractBlockBase<?>> sortedBlocks, TraceBuilderResult<?> traceBuilderResult, boolean neverSpillConstants) { - this.ir = res.getLIR(); - this.moveFactory = spillMoveFactory; - this.frameMapBuilder = res.getFrameMapBuilder(); - this.sortedBlocks = sortedBlocks; - this.registerAttributes = regAllocConfig.getRegisterConfig().getAttributesMap(); - this.regAllocConfig = regAllocConfig; - - this.registers = target.arch.getRegisters(); - this.fixedIntervals = new FixedInterval[registers.length]; - this.blockData = new BlockMap<>(ir.getControlFlowGraph()); - this.traceBuilderResult = traceBuilderResult; - this.neverSpillConstants = neverSpillConstants; - } - - public int getFirstLirInstructionId(AbstractBlockBase<?> block) { - int result = ir.getLIRforBlock(block).get(0).id(); - assert result >= 0; - return result; - } - - public int getLastLirInstructionId(AbstractBlockBase<?> block) { - List<LIRInstruction> instructions = ir.getLIRforBlock(block); - int result = instructions.get(instructions.size() - 1).id(); - assert result >= 0; - return result; - } - - public MoveFactory getSpillMoveFactory() { - return moveFactory; - } - - protected TraceLocalMoveResolver createMoveResolver() { - TraceLocalMoveResolver moveResolver = new TraceLocalMoveResolver(this); - assert moveResolver.checkEmpty(); - return moveResolver; - } - - public static boolean isVariableOrRegister(Value value) { - return isVariable(value) || isRegister(value); - } - - /** - * Converts an operand (variable or register) to an index in a flat address space covering all - * the {@linkplain Variable variables} and {@linkplain RegisterValue registers} being processed - * by this allocator. - */ - @SuppressWarnings("static-method") - int operandNumber(Value operand) { - assert !isRegister(operand) : "Register do not have operand numbers: " + operand; - assert isVariable(operand) : "Unsupported Value " + operand; - return ((Variable) operand).index; - } - - /** - * Gets the number of operands. This value will increase by 1 for new variable. - */ - int operandSize() { - return ir.numVariables(); - } - - /** - * Gets the number of registers. This value will never change. - */ - int numRegisters() { - return registers.length; - } - - public BlockData getBlockData(AbstractBlockBase<?> block) { - return blockData.get(block); - } - - void initBlockData(AbstractBlockBase<?> block) { - blockData.put(block, new BlockData()); - } - - static final IntervalPredicate IS_PRECOLORED_INTERVAL = new IntervalPredicate() { - - @Override - public boolean apply(TraceInterval i) { - return isRegister(i.operand); - } - }; - - static final IntervalPredicate IS_VARIABLE_INTERVAL = new IntervalPredicate() { - - @Override - public boolean apply(TraceInterval i) { - return isVariable(i.operand); - } - }; - - static final IntervalPredicate IS_STACK_INTERVAL = new IntervalPredicate() { - - @Override - public boolean apply(TraceInterval i) { - return !isRegister(i.operand); - } - }; - - /** - * Gets an object describing the attributes of a given register according to this register - * configuration. - */ - public RegisterAttributes attributes(Register reg) { - return registerAttributes[reg.number]; - } - - void assignSpillSlot(TraceInterval interval) { - /* - * Assign the canonical spill slot of the parent (if a part of the interval is already - * spilled) or allocate a new spill slot. - */ - if (interval.canMaterialize()) { - interval.assignLocation(Value.ILLEGAL); - } else if (interval.spillSlot() != null) { - interval.assignLocation(interval.spillSlot()); - } else { - VirtualStackSlot slot = frameMapBuilder.allocateSpillSlot(interval.kind()); - interval.setSpillSlot(slot); - interval.assignLocation(slot); - } - } - - /** - * Map from {@linkplain #operandNumber(Value) operand numbers} to intervals. - */ - public TraceInterval[] intervals() { - return intervals; - } - - /** - * Map from {@linkplain #operandNumber(Value) operand numbers} to intervals. - */ - public FixedInterval[] fixedIntervals() { - return fixedIntervals; - } - - void initIntervals() { - intervalsSize = operandSize(); - intervals = new TraceInterval[intervalsSize + (intervalsSize >> SPLIT_INTERVALS_CAPACITY_RIGHT_SHIFT)]; - } - - /** - * Creates a new fixed interval. - * - * @param reg the operand for the interval - * @return the created interval - */ - FixedInterval createFixedInterval(RegisterValue reg) { - FixedInterval interval = new FixedInterval(reg); - int operandNumber = reg.getRegister().number; - assert fixedIntervals[operandNumber] == null; - fixedIntervals[operandNumber] = interval; - return interval; - } - - /** - * Creates a new interval. - * - * @param operand the operand for the interval - * @return the created interval - */ - TraceInterval createInterval(AllocatableValue operand) { - assert isLegal(operand); - int operandNumber = operandNumber(operand); - TraceInterval interval = new TraceInterval(operand, operandNumber); - assert operandNumber < intervalsSize; - assert intervals[operandNumber] == null; - intervals[operandNumber] = interval; - return interval; - } - - /** - * Creates an interval as a result of splitting or spilling another interval. - * - * @param source an interval being split of spilled - * @return a new interval derived from {@code source} - */ - TraceInterval createDerivedInterval(TraceInterval source) { - if (firstDerivedIntervalIndex == -1) { - firstDerivedIntervalIndex = intervalsSize; - } - if (intervalsSize == intervals.length) { - intervals = Arrays.copyOf(intervals, intervals.length + (intervals.length >> SPLIT_INTERVALS_CAPACITY_RIGHT_SHIFT)); - } - intervalsSize++; - Variable variable = new Variable(source.kind(), ir.nextVariable()); - - TraceInterval interval = createInterval(variable); - assert intervals[intervalsSize - 1] == interval; - return interval; - } - - // access to block list (sorted in linear scan order) - public int blockCount() { - return sortedBlocks.size(); - } - - public AbstractBlockBase<?> blockAt(int index) { - return sortedBlocks.get(index); - } - - /** - * Gets the size of the {@link BlockData#liveIn} and {@link BlockData#liveOut} sets for a basic - * block. These sets do not include any operands allocated as a result of creating - * {@linkplain #createDerivedInterval(TraceInterval) derived intervals}. - */ - public int liveSetSize() { - return firstDerivedIntervalIndex == -1 ? operandSize() : firstDerivedIntervalIndex; - } - - int numLoops() { - return ir.getControlFlowGraph().getLoops().size(); - } - - public FixedInterval fixedIntervalFor(RegisterValue reg) { - return fixedIntervals[reg.getRegister().number]; - } - - public FixedInterval getOrCreateFixedInterval(RegisterValue reg) { - FixedInterval ret = fixedIntervalFor(reg); - if (ret == null) { - return createFixedInterval(reg); - } else { - return ret; - } - } - - TraceInterval intervalFor(int operandNumber) { - return intervals[operandNumber]; - } - - public TraceInterval intervalFor(Value operand) { - int operandNumber = operandNumber(operand); - assert operandNumber < intervalsSize; - return intervals[operandNumber]; - } - - public TraceInterval getOrCreateInterval(AllocatableValue operand) { - TraceInterval ret = intervalFor(operand); - if (ret == null) { - return createInterval(operand); - } else { - return ret; - } - } - - void initOpIdMaps(int numInstructions) { - opIdToInstructionMap = new LIRInstruction[numInstructions]; - opIdToBlockMap = new AbstractBlockBase<?>[numInstructions]; - } - - void putOpIdMaps(int index, LIRInstruction op, AbstractBlockBase<?> block) { - opIdToInstructionMap[index] = op; - opIdToBlockMap[index] = block; - } - - /** - * Gets the highest instruction id allocated by this object. - */ - int maxOpId() { - assert opIdToInstructionMap.length > 0 : "no operations"; - return (opIdToInstructionMap.length - 1) << 1; - } - - /** - * Converts an {@linkplain LIRInstruction#id instruction id} to an instruction index. All LIR - * instructions in a method have an index one greater than their linear-scan order predecessor - * with the first instruction having an index of 0. - */ - private static int opIdToIndex(int opId) { - return opId >> 1; - } - - /** - * Retrieves the {@link LIRInstruction} based on its {@linkplain LIRInstruction#id id}. - * - * @param opId an instruction {@linkplain LIRInstruction#id id} - * @return the instruction whose {@linkplain LIRInstruction#id} {@code == id} - */ - public LIRInstruction instructionForId(int opId) { - assert isEven(opId) : "opId not even"; - LIRInstruction instr = opIdToInstructionMap[opIdToIndex(opId)]; - assert instr.id() == opId; - return instr; - } - - /** - * Gets the block containing a given instruction. - * - * @param opId an instruction {@linkplain LIRInstruction#id id} - * @return the block containing the instruction denoted by {@code opId} - */ - public AbstractBlockBase<?> blockForId(int opId) { - assert opIdToBlockMap.length > 0 && opId >= 0 && opId <= maxOpId() + 1 : "opId out of range: " + opId; - return opIdToBlockMap[opIdToIndex(opId)]; - } - - boolean isBlockBegin(int opId) { - return opId == 0 || blockForId(opId) != blockForId(opId - 1); - } - - boolean coversBlockBegin(int opId1, int opId2) { - return blockForId(opId1) != blockForId(opId2); - } - - /** - * Determines if an {@link LIRInstruction} destroys all caller saved registers. - * - * @param opId an instruction {@linkplain LIRInstruction#id id} - * @return {@code true} if the instruction denoted by {@code id} destroys all caller saved - * registers. - */ - boolean hasCall(int opId) { - assert isEven(opId) : "opId not even"; - return instructionForId(opId).destroysCallerSavedRegisters(); - } - - abstract static class IntervalPredicate { - - abstract boolean apply(TraceInterval i); - } - - public boolean isProcessed(Value operand) { - return !isRegister(operand) || attributes(asRegister(operand)).isAllocatable(); - } - - // * Phase 5: actual register allocation - - private static <T extends IntervalHint> boolean isSorted(T[] intervals) { - int from = -1; - for (T interval : intervals) { - assert interval != null; - assert from <= interval.from(); - from = interval.from(); - } - return true; - } - - private static TraceInterval addToList(TraceInterval first, TraceInterval prev, TraceInterval interval) { - TraceInterval newFirst = first; - if (prev != null) { - prev.next = interval; - } else { - newFirst = interval; - } - return newFirst; - } - - TraceInterval createUnhandledList(IntervalPredicate isList1) { - assert isSorted(sortedIntervals) : "interval list is not sorted"; - - TraceInterval list1 = TraceInterval.EndMarker; - - TraceInterval list1Prev = null; - TraceInterval v; - - int n = sortedIntervals.length; - for (int i = 0; i < n; i++) { - v = sortedIntervals[i]; - if (v == null) { - continue; - } - - if (isList1.apply(v)) { - list1 = addToList(list1, list1Prev, v); - list1Prev = v; - } - } - - if (list1Prev != null) { - list1Prev.next = TraceInterval.EndMarker; - } - - assert list1Prev == null || list1Prev.next == TraceInterval.EndMarker : "linear list ends not with sentinel"; - - return list1; - } - - private static FixedInterval addToList(FixedInterval first, FixedInterval prev, FixedInterval interval) { - FixedInterval newFirst = first; - if (prev != null) { - prev.next = interval; - } else { - newFirst = interval; - } - return newFirst; - } - - FixedInterval createFixedUnhandledList() { - assert isSorted(sortedFixedIntervals) : "interval list is not sorted"; - - FixedInterval list1 = FixedInterval.EndMarker; - - FixedInterval list1Prev = null; - FixedInterval v; - - int n = sortedFixedIntervals.length; - for (int i = 0; i < n; i++) { - v = sortedFixedIntervals[i]; - if (v == null) { - continue; - } - - v.rewindRange(); - list1 = addToList(list1, list1Prev, v); - list1Prev = v; - } - - if (list1Prev != null) { - list1Prev.next = FixedInterval.EndMarker; - } - - assert list1Prev == null || list1Prev.next == FixedInterval.EndMarker : "linear list ends not with sentinel"; - - return list1; - } - - // SORTING - - protected void sortIntervalsBeforeAllocation() { - int sortedLen = 0; - for (TraceInterval interval : intervals) { - if (interval != null) { - sortedLen++; - } - } - sortedIntervals = sortIntervalsBeforeAllocation(intervals, new TraceInterval[sortedLen]); - } - - protected void sortFixedIntervalsBeforeAllocation() { - int sortedLen = 0; - for (FixedInterval interval : fixedIntervals) { - if (interval != null) { - sortedLen++; - } - } - sortedFixedIntervals = sortIntervalsBeforeAllocation(fixedIntervals, new FixedInterval[sortedLen]); - } - - private static <T extends IntervalHint> T[] sortIntervalsBeforeAllocation(T[] intervals, T[] sortedList) { - int sortedIdx = 0; - int sortedFromMax = -1; - - // special sorting algorithm: the original interval-list is almost sorted, - // only some intervals are swapped. So this is much faster than a complete QuickSort - for (T interval : intervals) { - if (interval != null) { - int from = interval.from(); - - if (sortedFromMax <= from) { - sortedList[sortedIdx++] = interval; - sortedFromMax = interval.from(); - } else { - // the assumption that the intervals are already sorted failed, - // so this interval must be sorted in manually - int j; - for (j = sortedIdx - 1; j >= 0 && from < sortedList[j].from(); j--) { - sortedList[j + 1] = sortedList[j]; - } - sortedList[j + 1] = interval; - sortedIdx++; - } - } - } - return sortedList; - } - - void sortIntervalsAfterAllocation() { - if (firstDerivedIntervalIndex == -1) { - // no intervals have been added during allocation, so sorted list is already up to date - return; - } - - TraceInterval[] oldList = sortedIntervals; - TraceInterval[] newList = Arrays.copyOfRange(intervals, firstDerivedIntervalIndex, intervalsSize); - int oldLen = oldList.length; - int newLen = newList.length; - - // conventional sort-algorithm for new intervals - Arrays.sort(newList, (TraceInterval a, TraceInterval b) -> a.from() - b.from()); - - // merge old and new list (both already sorted) into one combined list - TraceInterval[] combinedList = new TraceInterval[oldLen + newLen]; - int oldIdx = 0; - int newIdx = 0; - - while (oldIdx + newIdx < combinedList.length) { - if (newIdx >= newLen || (oldIdx < oldLen && oldList[oldIdx].from() <= newList[newIdx].from())) { - combinedList[oldIdx + newIdx] = oldList[oldIdx]; - oldIdx++; - } else { - combinedList[oldIdx + newIdx] = newList[newIdx]; - newIdx++; - } - } - - sortedIntervals = combinedList; - } - - // wrapper for Interval.splitChildAtOpId that performs a bailout in product mode - // instead of returning null - public TraceInterval splitChildAtOpId(TraceInterval interval, int opId, LIRInstruction.OperandMode mode) { - TraceInterval result = interval.getSplitChildAtOpId(opId, mode, this); - - if (result != null) { - if (Debug.isLogEnabled()) { - Debug.log("Split child at pos %d of interval %s is %s", opId, interval, result); - } - return result; - } - - throw new BailoutException("LinearScan: interval is null"); - } - - static AllocatableValue canonicalSpillOpr(TraceInterval interval) { - assert interval.spillSlot() != null : "canonical spill slot not set"; - return interval.spillSlot(); - } - - boolean isMaterialized(AllocatableValue operand, int opId, OperandMode mode) { - TraceInterval interval = intervalFor(operand); - assert interval != null : "interval must exist"; - - if (opId != -1) { - /* - * Operands are not changed when an interval is split during allocation, so search the - * right interval here. - */ - interval = splitChildAtOpId(interval, opId, mode); - } - - return isIllegal(interval.location()) && interval.canMaterialize(); - } - - boolean isCallerSave(Value operand) { - return attributes(asRegister(operand)).isCallerSave(); - } - - @SuppressWarnings("try") - protected <B extends AbstractBlockBase<B>> void allocate(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, MoveFactory spillMoveFactory, - RegisterAllocationConfig registerAllocationConfig) { - - /* - * This is the point to enable debug logging for the whole register allocation. - */ - try (Indent indent = Debug.logAndIndent("LinearScan allocate")) { - TraceLinearScanAllocationContext context = new TraceLinearScanAllocationContext(spillMoveFactory, registerAllocationConfig, traceBuilderResult, this); - - TRACE_LINEAR_SCAN_LIFETIME_ANALYSIS_PHASE.apply(target, lirGenRes, codeEmittingOrder, linearScanOrder, context, false); - - try (Scope s = Debug.scope("AfterLifetimeAnalysis", (Object) intervals())) { - sortIntervalsBeforeAllocation(); - sortFixedIntervalsBeforeAllocation(); - - TRACE_LINEAR_SCAN_REGISTER_ALLOCATION_PHASE.apply(target, lirGenRes, codeEmittingOrder, linearScanOrder, context, false); - - // resolve intra-trace data-flow - TRACE_LINEAR_SCAN_RESOLVE_DATA_FLOW_PHASE.apply(target, lirGenRes, codeEmittingOrder, linearScanOrder, context, false); - Debug.dump(TraceRegisterAllocationPhase.TRACE_DUMP_LEVEL, sortedBlocks(), "%s", TRACE_LINEAR_SCAN_RESOLVE_DATA_FLOW_PHASE.getName()); - - // eliminate spill moves - if (Options.LIROptTraceRAEliminateSpillMoves.getValue()) { - TRACE_LINEAR_SCAN_ELIMINATE_SPILL_MOVE_PHASE.apply(target, lirGenRes, codeEmittingOrder, linearScanOrder, context, false); - Debug.dump(TraceRegisterAllocationPhase.TRACE_DUMP_LEVEL, sortedBlocks(), "%s", TRACE_LINEAR_SCAN_ELIMINATE_SPILL_MOVE_PHASE.getName()); - } - - TRACE_LINEAR_SCAN_ASSIGN_LOCATIONS_PHASE.apply(target, lirGenRes, codeEmittingOrder, linearScanOrder, context, false); - - if (DetailedAsserts.getValue()) { - verifyIntervals(); - } - } catch (Throwable e) { - throw Debug.handle(e); - } - } - } - - @SuppressWarnings("try") - public void printIntervals(String label) { - if (Debug.isDumpEnabled(TraceRegisterAllocationPhase.TRACE_DUMP_LEVEL)) { - if (Debug.isLogEnabled()) { - try (Indent indent = Debug.logAndIndent("intervals %s", label)) { - for (FixedInterval interval : fixedIntervals) { - if (interval != null) { - Debug.log("%s", interval.logString(this)); - } - } - - for (TraceInterval interval : intervals) { - if (interval != null) { - Debug.log("%s", interval.logString(this)); - } - } - - try (Indent indent2 = Debug.logAndIndent("Basic Blocks")) { - for (int i = 0; i < blockCount(); i++) { - AbstractBlockBase<?> block = blockAt(i); - Debug.log("B%d [%d, %d, %s] ", block.getId(), getFirstLirInstructionId(block), getLastLirInstructionId(block), block.getLoop()); - } - } - } - } - Debug.dump(new TraceIntervalDumper(Arrays.copyOf(fixedIntervals, fixedIntervals.length), Arrays.copyOf(intervals, intervalsSize)), label); - } - } - - public void printLir(String label, @SuppressWarnings("unused") boolean hirValid) { - if (Debug.isDumpEnabled(TraceRegisterAllocationPhase.TRACE_DUMP_LEVEL)) { - Debug.dump(TraceRegisterAllocationPhase.TRACE_DUMP_LEVEL, sortedBlocks(), label); - } - } - - boolean verify() { - // (check that all intervals have a correct register and that no registers are overwritten) - verifyIntervals(); - - verifyRegisters(); - - Debug.log("no errors found"); - - return true; - } - - @SuppressWarnings("try") - private void verifyRegisters() { - // Enable this logging to get output for the verification process. - try (Indent indent = Debug.logAndIndent("verifying register allocation")) { - RegisterVerifier verifier = new RegisterVerifier(this); - verifier.verify(blockAt(0)); - } - } - - @SuppressWarnings("try") - protected void verifyIntervals() { - try (Indent indent = Debug.logAndIndent("verifying intervals")) { - int len = intervalsSize; - - for (int i = 0; i < len; i++) { - final TraceInterval i1 = intervals[i]; - if (i1 == null) { - continue; - } - - i1.checkSplitChildren(); - - if (i1.operandNumber != i) { - Debug.log("Interval %d is on position %d in list", i1.operandNumber, i); - Debug.log(i1.logString(this)); - throw new JVMCIError(""); - } - - if (isVariable(i1.operand) && i1.kind().equals(LIRKind.Illegal)) { - Debug.log("Interval %d has no type assigned", i1.operandNumber); - Debug.log(i1.logString(this)); - throw new JVMCIError(""); - } - - if (i1.location() == null) { - Debug.log("Interval %d has no register assigned", i1.operandNumber); - Debug.log(i1.logString(this)); - throw new JVMCIError(""); - } - - if (i1.isEmpty()) { - Debug.log("Interval %d has no Range", i1.operandNumber); - Debug.log(i1.logString(this)); - throw new JVMCIError(""); - } - - if (i1.from() >= i1.to()) { - Debug.log("Interval %d has zero length range", i1.operandNumber); - Debug.log(i1.logString(this)); - throw new JVMCIError(""); - } - - // special intervals that are created in MoveResolver - // . ignore them because the range information has no meaning there - if (i1.from() == 1 && i1.to() == 2) { - continue; - } - // check any intervals - for (int j = i + 1; j < len; j++) { - final TraceInterval i2 = intervals[j]; - if (i2 == null) { - continue; - } - - // special intervals that are created in MoveResolver - // . ignore them because the range information has no meaning there - if (i2.from() == 1 && i2.to() == 2) { - continue; - } - Value l1 = i1.location(); - Value l2 = i2.location(); - boolean intersects = i1.intersects(i2); - if (intersects && !isIllegal(l1) && (l1.equals(l2))) { - if (DetailedAsserts.getValue()) { - Debug.log("Intervals %s and %s overlap and have the same register assigned", i1, i2); - Debug.log(i1.logString(this)); - Debug.log(i2.logString(this)); - } - throw new BailoutException(""); - } - } - // check fixed intervals - for (FixedInterval i2 : fixedIntervals) { - if (i2 == null) { - continue; - } - - Value l1 = i1.location(); - Value l2 = i2.location(); - boolean intersects = i2.intersects(i1); - if (intersects && !isIllegal(l1) && (l1.equals(l2))) { - if (DetailedAsserts.getValue()) { - Debug.log("Intervals %s and %s overlap and have the same register assigned", i1, i2); - Debug.log(i1.logString(this)); - Debug.log(i2.logString(this)); - } - throw new BailoutException(""); - } - } - } - } - } - - class CheckConsumer implements ValueConsumer { - - boolean ok; - FixedInterval curInterval; - - @Override - public void visitValue(Value operand, OperandMode mode, EnumSet<OperandFlag> flags) { - if (isRegister(operand)) { - if (fixedIntervalFor(asRegisterValue(operand)) == curInterval) { - ok = true; - } - } - } - } - - @SuppressWarnings("try") - void verifyNoOopsInFixedIntervals() { - try (Indent indent = Debug.logAndIndent("verifying that no oops are in fixed intervals *")) { - CheckConsumer checkConsumer = new CheckConsumer(); - - TraceInterval otherIntervals; - FixedInterval fixedInts = createFixedUnhandledList(); - // to ensure a walking until the last instruction id, add a dummy interval - // with a high operation id - otherIntervals = new TraceInterval(Value.ILLEGAL, -1); - otherIntervals.addRange(Integer.MAX_VALUE - 2, Integer.MAX_VALUE - 1); - TraceIntervalWalker iw = new TraceIntervalWalker(this, fixedInts, otherIntervals); - - for (AbstractBlockBase<?> block : sortedBlocks) { - List<LIRInstruction> instructions = ir.getLIRforBlock(block); - - for (int j = 0; j < instructions.size(); j++) { - LIRInstruction op = instructions.get(j); - - if (op.hasState()) { - iw.walkBefore(op.id()); - boolean checkLive = true; - - /* - * Make sure none of the fixed registers is live across an oopmap since we - * can't handle that correctly. - */ - if (checkLive) { - for (FixedInterval interval = iw.activeFixedList.getFixed(); interval != FixedInterval.EndMarker; interval = interval.next) { - if (interval.to() > op.id() + 1) { - /* - * This interval is live out of this op so make sure that this - * interval represents some value that's referenced by this op - * either as an input or output. - */ - checkConsumer.curInterval = interval; - checkConsumer.ok = false; - - op.visitEachInput(checkConsumer); - op.visitEachAlive(checkConsumer); - op.visitEachTemp(checkConsumer); - op.visitEachOutput(checkConsumer); - - assert checkConsumer.ok : "fixed intervals should never be live across an oopmap point"; - } - } - } - } - } - } - } - } - - public LIR getLIR() { - return ir; - } - - public FrameMapBuilder getFrameMapBuilder() { - return frameMapBuilder; - } - - public List<? extends AbstractBlockBase<?>> sortedBlocks() { - return sortedBlocks; - } - - public Register[] getRegisters() { - return registers; - } - - public RegisterAllocationConfig getRegisterAllocationConfig() { - return regAllocConfig; - } - - public boolean callKillsRegisters() { - return regAllocConfig.getRegisterConfig().areAllAllocatableRegistersCallerSaved(); - } - - boolean neverSpillConstants() { - return neverSpillConstants; - } - -}
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/TraceLinearScanAllocationPhase.java Tue Dec 08 12:29:24 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2015, 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.graal.lir.alloc.trace; - -import java.util.List; - -import jdk.vm.ci.code.TargetDescription; - -import com.oracle.graal.compiler.common.alloc.RegisterAllocationConfig; -import com.oracle.graal.compiler.common.alloc.TraceBuilder.TraceBuilderResult; -import com.oracle.graal.compiler.common.cfg.AbstractBlockBase; -import com.oracle.graal.lir.gen.LIRGenerationResult; -import com.oracle.graal.lir.gen.LIRGeneratorTool.MoveFactory; -import com.oracle.graal.lir.phases.LIRPhase; - -public abstract class TraceLinearScanAllocationPhase extends LIRPhase<TraceLinearScanAllocationPhase.TraceLinearScanAllocationContext> { - - public static final class TraceLinearScanAllocationContext { - private final MoveFactory spillMoveFactory; - private final RegisterAllocationConfig registerAllocationConfig; - private final TraceBuilderResult<?> traceBuilderResult; - private final TraceLinearScan allocator; - - public TraceLinearScanAllocationContext(MoveFactory spillMoveFactory, RegisterAllocationConfig registerAllocationConfig, TraceBuilderResult<?> traceBuilderResult, TraceLinearScan allocator) { - this.spillMoveFactory = spillMoveFactory; - this.registerAllocationConfig = registerAllocationConfig; - this.traceBuilderResult = traceBuilderResult; - this.allocator = allocator; - } - } - - @Override - protected final <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, - TraceLinearScanAllocationContext context) { - run(target, lirGenRes, codeEmittingOrder, linearScanOrder, context.spillMoveFactory, context.registerAllocationConfig, context.traceBuilderResult, context.allocator); - } - - protected abstract <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, - MoveFactory spillMoveFactory, RegisterAllocationConfig registerAllocationConfig, TraceBuilderResult<?> traceBuilderResult, TraceLinearScan allocator); - -}
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/TraceLinearScanAssignLocationsPhase.java Tue Dec 08 12:29:24 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,276 +0,0 @@ -/* - * Copyright (c) 2015, 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.graal.lir.alloc.trace; - -import static com.oracle.graal.compiler.common.GraalOptions.DetailedAsserts; -import static com.oracle.graal.lir.LIRValueUtil.isConstantValue; -import static com.oracle.graal.lir.LIRValueUtil.isStackSlotValue; -import static com.oracle.graal.lir.LIRValueUtil.isVariable; -import static com.oracle.graal.lir.LIRValueUtil.isVirtualStackSlot; -import static com.oracle.graal.lir.alloc.trace.TraceRegisterAllocationPhase.Options.TraceRAshareSpillInformation; -import static jdk.vm.ci.code.ValueUtil.isIllegal; -import static jdk.vm.ci.code.ValueUtil.isRegister; - -import java.util.Collections; -import java.util.EnumSet; -import java.util.List; - -import jdk.vm.ci.code.RegisterValue; -import jdk.vm.ci.code.StackSlot; -import jdk.vm.ci.code.TargetDescription; -import jdk.vm.ci.meta.AllocatableValue; -import jdk.vm.ci.meta.Value; - -import com.oracle.graal.compiler.common.alloc.RegisterAllocationConfig; -import com.oracle.graal.compiler.common.alloc.TraceBuilder.TraceBuilderResult; -import com.oracle.graal.compiler.common.cfg.AbstractBlockBase; -import com.oracle.graal.debug.Debug; -import com.oracle.graal.debug.Indent; -import com.oracle.graal.lir.ConstantValue; -import com.oracle.graal.lir.InstructionValueProcedure; -import com.oracle.graal.lir.LIRFrameState; -import com.oracle.graal.lir.LIRInstruction; -import com.oracle.graal.lir.LIRInstruction.OperandFlag; -import com.oracle.graal.lir.LIRInstruction.OperandMode; -import com.oracle.graal.lir.StandardOp; -import com.oracle.graal.lir.StandardOp.BlockEndOp; -import com.oracle.graal.lir.StandardOp.LabelOp; -import com.oracle.graal.lir.StandardOp.MoveOp; -import com.oracle.graal.lir.StandardOp.ValueMoveOp; -import com.oracle.graal.lir.Variable; -import com.oracle.graal.lir.gen.LIRGenerationResult; -import com.oracle.graal.lir.gen.LIRGeneratorTool.MoveFactory; - -/** - * Specialization of {@link com.oracle.graal.lir.alloc.lsra.LinearScanAssignLocationsPhase} that - * inserts {@link ShadowedRegisterValue}s to describe {@link RegisterValue}s that are also available - * on the {@link StackSlot stack}. - */ -final class TraceLinearScanAssignLocationsPhase extends TraceLinearScanAllocationPhase { - - @Override - protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, MoveFactory spillMoveFactory, - RegisterAllocationConfig registerAllocationConfig, TraceBuilderResult<?> traceBuilderResult, TraceLinearScan allocator) { - new Assigner(allocator).assignLocations(); - } - - private static final class Assigner { - private final TraceLinearScan allocator; - - private Assigner(TraceLinearScan allocator) { - this.allocator = allocator; - } - - /** - * Assigns the allocated location for an LIR instruction operand back into the instruction. - * - * @param op current {@link LIRInstruction} - * @param operand an LIR instruction operand - * @param mode the usage mode for {@code operand} by the instruction - * @return the location assigned for the operand - */ - private Value colorLirOperand(LIRInstruction op, Variable operand, OperandMode mode) { - int opId = op.id(); - TraceInterval interval = allocator.intervalFor(operand); - assert interval != null : "interval must exist"; - - if (opId != -1) { - if (DetailedAsserts.getValue()) { - AbstractBlockBase<?> block = allocator.blockForId(opId); - if (block.getSuccessorCount() <= 1 && opId == allocator.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. - */ - LIRInstruction instr = allocator.getLIR().getLIRforBlock(block).get(allocator.getLIR().getLIRforBlock(block).size() - 1); - if (instr instanceof StandardOp.JumpOp) { - if (allocator.getBlockData(block).liveOut.get(allocator.operandNumber(operand))) { - 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); - } - } - } - } - - /* - * Operands are not changed when an interval is split during allocation, so search - * the right interval here. - */ - interval = allocator.splitChildAtOpId(interval, opId, mode); - } - - if (isIllegal(interval.location()) && interval.canMaterialize()) { - assert mode != OperandMode.DEF; - return new ConstantValue(interval.kind(), interval.getMaterializedValue()); - } - return interval.location(); - } - - /** - * @param op - * @param operand - * @param valueMode - * @param flags - * @see InstructionValueProcedure#doValue(LIRInstruction, Value, OperandMode, EnumSet) - */ - private Value debugInfoProcedure(LIRInstruction op, Value operand, OperandMode valueMode, EnumSet<OperandFlag> flags) { - if (isVirtualStackSlot(operand)) { - return operand; - } - int tempOpId = op.id(); - OperandMode mode = OperandMode.USE; - AbstractBlockBase<?> block = allocator.blockForId(tempOpId); - if (block.getSuccessorCount() == 1 && tempOpId == allocator.getLastLirInstructionId(block)) { - /* - * Generating debug information for the last instruction of a block. If this - * instruction is a branch, spill moves are inserted before this branch and so the - * wrong operand would be returned (spill moves at block boundaries are not - * considered in the live ranges of intervals). - * - * Solution: use the first opId of the branch target block instead. - */ - final LIRInstruction instr = allocator.getLIR().getLIRforBlock(block).get(allocator.getLIR().getLIRforBlock(block).size() - 1); - if (instr instanceof StandardOp.JumpOp) { - if (allocator.getBlockData(block).liveOut.get(allocator.operandNumber(operand))) { - tempOpId = allocator.getFirstLirInstructionId(block.getSuccessors().iterator().next()); - mode = OperandMode.DEF; - } - } - } - - /* - * Get current location of operand. The operand must be live because debug information - * is considered when building the intervals if the interval is not live, - * colorLirOperand will cause an assert on failure. - */ - Value result = colorLirOperand(op, (Variable) operand, mode); - assert !allocator.hasCall(tempOpId) || isStackSlotValue(result) || isConstantValue(result) || !allocator.isCallerSave(result) : "cannot have caller-save register operands at calls"; - return result; - } - - private void computeDebugInfo(final LIRInstruction op, LIRFrameState info) { - info.forEachState(op, this::debugInfoProcedure); - } - - private void assignLocations(List<LIRInstruction> instructions) { - int numInst = instructions.size(); - boolean hasDead = false; - - for (int j = 0; j < numInst; j++) { - final LIRInstruction op = instructions.get(j); - if (op == null) { - /* - * this can happen when spill-moves are removed in eliminateSpillMoves - */ - hasDead = true; - } else if (assignLocations(op)) { - instructions.set(j, null); - hasDead = true; - } - } - - if (hasDead) { - // Remove null values from the list. - instructions.removeAll(Collections.singleton(null)); - } - } - - /** - * Assigns the operand of an {@link LIRInstruction}. - * - * @param op The {@link LIRInstruction} that should be colored. - * @return {@code true} if the instruction should be deleted. - */ - private boolean assignLocations(LIRInstruction op) { - assert op != null; - if (TraceRAshareSpillInformation.getValue()) { - if (op instanceof BlockEndOp) { - ((BlockEndOp) op).forEachOutgoingValue(colorOutgoingIncomingValues); - } else if (op instanceof LabelOp) { - ((LabelOp) op).forEachIncomingValue(colorOutgoingIncomingValues); - } - } - - InstructionValueProcedure assignProc = (inst, operand, mode, flags) -> isVariable(operand) ? colorLirOperand(inst, (Variable) operand, mode) : operand; - // remove useless moves - if (op instanceof MoveOp) { - AllocatableValue result = ((MoveOp) op).getResult(); - if (isVariable(result) && allocator.isMaterialized(result, op.id(), OperandMode.DEF)) { - /* - * This happens if a materializable interval is originally not spilled but then - * kicked out in LinearScanWalker.splitForSpilling(). When kicking out such an - * interval this move operation was already generated. - */ - return true; - } - } - - op.forEachInput(assignProc); - op.forEachAlive(assignProc); - op.forEachTemp(assignProc); - op.forEachOutput(assignProc); - - // compute reference map and debug information - op.forEachState((inst, state) -> computeDebugInfo(inst, state)); - - // remove useless moves - if (op instanceof ValueMoveOp) { - ValueMoveOp move = (ValueMoveOp) op; - if (move.getInput().equals(move.getResult())) { - return true; - } - } - return false; - } - - @SuppressWarnings("try") - private void assignLocations() { - try (Indent indent = Debug.logAndIndent("assign locations")) { - for (AbstractBlockBase<?> block : allocator.sortedBlocks()) { - try (Indent indent2 = Debug.logAndIndent("assign locations in block B%d", block.getId())) { - assignLocations(allocator.getLIR().getLIRforBlock(block)); - } - } - } - } - - private InstructionValueProcedure colorOutgoingIncomingValues = new InstructionValueProcedure() { - - public Value doValue(LIRInstruction instruction, Value value, OperandMode mode, EnumSet<OperandFlag> flags) { - if (isVariable(value)) { - TraceInterval interval = allocator.intervalFor(value); - assert interval != null : "interval must exist"; - interval = allocator.splitChildAtOpId(interval, instruction.id(), mode); - - if (interval.inMemoryAt(instruction.id()) && isRegister(interval.location())) { - return new ShadowedRegisterValue((RegisterValue) interval.location(), interval.spillSlot()); - } - } - return value; - } - }; - } - -}
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/TraceLinearScanEliminateSpillMovePhase.java Tue Dec 08 12:29:24 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,222 +0,0 @@ -/* - * Copyright (c) 2015, 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.graal.lir.alloc.trace; - -import static com.oracle.graal.compiler.common.GraalOptions.DetailedAsserts; -import static com.oracle.graal.lir.LIRValueUtil.isStackSlotValue; -import static com.oracle.graal.lir.LIRValueUtil.isVariable; -import static jdk.vm.ci.code.ValueUtil.isRegister; - -import java.util.List; - -import jdk.vm.ci.code.TargetDescription; -import jdk.vm.ci.meta.AllocatableValue; - -import com.oracle.graal.compiler.common.alloc.RegisterAllocationConfig; -import com.oracle.graal.compiler.common.alloc.TraceBuilder.TraceBuilderResult; -import com.oracle.graal.compiler.common.cfg.AbstractBlockBase; -import com.oracle.graal.debug.Debug; -import com.oracle.graal.debug.Indent; -import com.oracle.graal.lir.LIRInsertionBuffer; -import com.oracle.graal.lir.LIRInstruction; -import com.oracle.graal.lir.StandardOp.LoadConstantOp; -import com.oracle.graal.lir.StandardOp.MoveOp; -import com.oracle.graal.lir.StandardOp.ValueMoveOp; -import com.oracle.graal.lir.alloc.trace.TraceInterval.SpillState; -import com.oracle.graal.lir.alloc.trace.TraceLinearScan.IntervalPredicate; -import com.oracle.graal.lir.gen.LIRGenerationResult; -import com.oracle.graal.lir.gen.LIRGeneratorTool.MoveFactory; - -final class TraceLinearScanEliminateSpillMovePhase extends TraceLinearScanAllocationPhase { - - private static final IntervalPredicate mustStoreAtDefinition = new TraceLinearScan.IntervalPredicate() { - - @Override - public boolean apply(TraceInterval i) { - return i.isSplitParent() && i.spillState() == SpillState.StoreAtDefinition; - } - }; - - @Override - protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, MoveFactory spillMoveFactory, - RegisterAllocationConfig registerAllocationConfig, TraceBuilderResult<?> traceBuilderResult, TraceLinearScan allocator) { - boolean shouldEliminateSpillMoves = shouldEliminateSpillMoves(traceBuilderResult, allocator); - eliminateSpillMoves(allocator, shouldEliminateSpillMoves, traceBuilderResult); - } - - private static boolean shouldEliminateSpillMoves(TraceBuilderResult<?> traceBuilderResult, TraceLinearScan allocator) { - return !traceBuilderResult.incomingSideEdges(traceBuilderResult.getTraceForBlock(allocator.sortedBlocks().get(0))); - } - - // called once before assignment of register numbers - @SuppressWarnings("try") - private static void eliminateSpillMoves(TraceLinearScan allocator, boolean shouldEliminateSpillMoves, TraceBuilderResult<?> traceBuilderResult) { - try (Indent indent = Debug.logAndIndent("Eliminating unnecessary spill moves: Trace%d", traceBuilderResult.getTraceForBlock(allocator.sortedBlocks().get(0)))) { - - /* - * collect all intervals that must be stored after their definition. The list is sorted - * by Interval.spillDefinitionPos. - */ - TraceInterval interval = allocator.createUnhandledList(mustStoreAtDefinition); - if (DetailedAsserts.getValue()) { - checkIntervals(interval); - } - - LIRInsertionBuffer insertionBuffer = new LIRInsertionBuffer(); - for (AbstractBlockBase<?> block : allocator.sortedBlocks()) { - try (Indent indent1 = Debug.logAndIndent("Handle %s", block)) { - List<LIRInstruction> instructions = allocator.getLIR().getLIRforBlock(block); - int numInst = instructions.size(); - - int lastOpId = -1; - // iterate all instructions of the block. - for (int j = 0; j < numInst; j++) { - LIRInstruction op = instructions.get(j); - int opId = op.id(); - - if (opId == -1) { - MoveOp move = (MoveOp) op; - /* - * Remove move from register to stack if the stack slot is guaranteed to - * be correct. Only moves that have been inserted by LinearScan can be - * removed. - */ - if (shouldEliminateSpillMoves && canEliminateSpillMove(allocator, block, move, lastOpId)) { - /* - * Move target is a stack slot that is always correct, so eliminate - * instruction. - */ - if (Debug.isLogEnabled()) { - if (move instanceof ValueMoveOp) { - ValueMoveOp vmove = (ValueMoveOp) move; - Debug.log("eliminating move from interval %d (%s) to %d (%s) in block %s", allocator.operandNumber(vmove.getInput()), vmove.getInput(), - allocator.operandNumber(vmove.getResult()), vmove.getResult(), block); - } else { - LoadConstantOp load = (LoadConstantOp) move; - Debug.log("eliminating constant load from %s to %d (%s) in block %s", load.getConstant(), allocator.operandNumber(load.getResult()), load.getResult(), block); - } - } - - // null-instructions are deleted by assignRegNum - instructions.set(j, null); - } - - } else { - lastOpId = opId; - /* - * Insert move from register to stack just after the beginning of the - * interval. - */ - assert interval == TraceInterval.EndMarker || interval.spillDefinitionPos() >= opId : "invalid order"; - assert interval == TraceInterval.EndMarker || (interval.isSplitParent() && interval.spillState() == SpillState.StoreAtDefinition) : "invalid interval"; - - while (interval != TraceInterval.EndMarker && interval.spillDefinitionPos() == opId) { - if (!interval.canMaterialize()) { - if (!insertionBuffer.initialized()) { - /* - * prepare insertion buffer (appended when all instructions - * in the block are processed) - */ - insertionBuffer.init(instructions); - } - - AllocatableValue fromLocation = interval.location(); - AllocatableValue toLocation = TraceLinearScan.canonicalSpillOpr(interval); - if (!fromLocation.equals(toLocation)) { - - assert isRegister(fromLocation) : "from operand must be a register but is: " + fromLocation + " toLocation=" + toLocation + " spillState=" + - interval.spillState(); - assert isStackSlotValue(toLocation) : "to operand must be a stack slot"; - - LIRInstruction move = allocator.getSpillMoveFactory().createMove(toLocation, fromLocation); - insertionBuffer.append(j + 1, move); - - if (Debug.isLogEnabled()) { - Debug.log("inserting move after definition of interval %d to stack slot %s at opId %d", interval.operandNumber, interval.spillSlot(), opId); - } - } - } - interval = interval.next; - } - } - } // end of instruction iteration - - if (insertionBuffer.initialized()) { - insertionBuffer.finish(); - } - } - } // end of block iteration - - assert interval == TraceInterval.EndMarker : "missed an interval"; - } - } - - /** - * @param allocator - * @param block The block {@code move} is located in. - * @param move Spill move. - * @param lastOpId The id of last "normal" instruction before the spill move. (Spill moves have - * no valid opId but -1.) - */ - private static boolean canEliminateSpillMove(TraceLinearScan allocator, AbstractBlockBase<?> block, MoveOp move, int lastOpId) { - assert isVariable(move.getResult()) : "LinearScan inserts only moves to variables: " + move; - assert lastOpId >= 0 : "Invalid lastOpId: " + lastOpId; - - TraceInterval curInterval = allocator.intervalFor(move.getResult()); - - if (!isRegister(curInterval.location()) && curInterval.inMemoryAt(lastOpId) && isPhiResolutionMove(allocator, move)) { - assert isStackSlotValue(curInterval.location()) : "Not a stack slot: " + curInterval.location(); - return true; - } - return false; - } - - private static boolean isPhiResolutionMove(TraceLinearScan allocator, MoveOp move) { - TraceInterval curInterval = allocator.intervalFor(move.getResult()); - return !curInterval.isSplitParent(); - } - - private static void checkIntervals(TraceInterval interval) { - TraceInterval prev = null; - TraceInterval temp = interval; - while (temp != TraceInterval.EndMarker) { - assert temp.spillDefinitionPos() >= 0 : "invalid spill definition pos"; - if (prev != null) { - assert temp.from() >= prev.from() : "intervals not sorted"; - assert temp.spillDefinitionPos() >= prev.spillDefinitionPos() : "when intervals are sorted by from : then they must also be sorted by spillDefinitionPos"; - } - - assert temp.spillSlot() != null || temp.canMaterialize() : "interval has no spill slot assigned"; - assert temp.spillDefinitionPos() >= temp.from() : "invalid order"; - assert temp.spillDefinitionPos() <= temp.from() + 2 : "only intervals defined once at their start-pos can be optimized"; - - if (Debug.isLogEnabled()) { - Debug.log("interval %d (from %d to %d) must be stored at %d", temp.operandNumber, temp.from(), temp.to(), temp.spillDefinitionPos()); - } - - prev = temp; - temp = temp.next; - } - } - -}
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/TraceLinearScanLifetimeAnalysisPhase.java Tue Dec 08 12:29:24 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,667 +0,0 @@ -/* - * Copyright (c) 2015, 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.graal.lir.alloc.trace; - -import static com.oracle.graal.lir.LIRValueUtil.asVariable; -import static com.oracle.graal.lir.LIRValueUtil.isStackSlotValue; -import static com.oracle.graal.lir.LIRValueUtil.isVariable; -import static com.oracle.graal.lir.alloc.trace.TraceLinearScan.isVariableOrRegister; -import static com.oracle.graal.lir.alloc.trace.TraceRegisterAllocationPhase.Options.TraceRAshareSpillInformation; -import static com.oracle.graal.lir.alloc.trace.TraceRegisterAllocationPhase.Options.TraceRAuseInterTraceHints; -import static com.oracle.graal.lir.alloc.trace.TraceUtil.asShadowedRegisterValue; -import static com.oracle.graal.lir.alloc.trace.TraceUtil.isShadowedRegisterValue; -import static jdk.vm.ci.code.ValueUtil.asRegisterValue; -import static jdk.vm.ci.code.ValueUtil.asStackSlot; -import static jdk.vm.ci.code.ValueUtil.isRegister; -import static jdk.vm.ci.code.ValueUtil.isStackSlot; - -import java.util.BitSet; -import java.util.EnumSet; -import java.util.List; - -import jdk.vm.ci.code.BailoutException; -import jdk.vm.ci.code.Register; -import jdk.vm.ci.code.RegisterValue; -import jdk.vm.ci.code.TargetDescription; -import jdk.vm.ci.common.JVMCIError; -import jdk.vm.ci.meta.AllocatableValue; -import jdk.vm.ci.meta.JavaConstant; -import jdk.vm.ci.meta.LIRKind; -import jdk.vm.ci.meta.Value; - -import com.oracle.graal.compiler.common.alloc.ComputeBlockOrder; -import com.oracle.graal.compiler.common.alloc.RegisterAllocationConfig; -import com.oracle.graal.compiler.common.alloc.TraceBuilder.TraceBuilderResult; -import com.oracle.graal.compiler.common.cfg.AbstractBlockBase; -import com.oracle.graal.debug.Debug; -import com.oracle.graal.debug.Indent; -import com.oracle.graal.lir.InstructionValueConsumer; -import com.oracle.graal.lir.LIR; -import com.oracle.graal.lir.LIRInstruction; -import com.oracle.graal.lir.LIRInstruction.OperandFlag; -import com.oracle.graal.lir.LIRInstruction.OperandMode; -import com.oracle.graal.lir.LIRValueUtil; -import com.oracle.graal.lir.StandardOp.BlockEndOp; -import com.oracle.graal.lir.StandardOp.LabelOp; -import com.oracle.graal.lir.StandardOp.LoadConstantOp; -import com.oracle.graal.lir.StandardOp.ValueMoveOp; -import com.oracle.graal.lir.ValueConsumer; -import com.oracle.graal.lir.Variable; -import com.oracle.graal.lir.alloc.trace.TraceInterval.RegisterPriority; -import com.oracle.graal.lir.alloc.trace.TraceInterval.SpillState; -import com.oracle.graal.lir.gen.LIRGenerationResult; -import com.oracle.graal.lir.gen.LIRGeneratorTool.MoveFactory; -import com.oracle.graal.lir.ssi.SSIUtil; - -final class TraceLinearScanLifetimeAnalysisPhase extends TraceLinearScanAllocationPhase { - - @Override - protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, MoveFactory spillMoveFactory, - RegisterAllocationConfig registerAllocationConfig, TraceBuilderResult<?> traceBuilderResult, TraceLinearScan allocator) { - new Analyser(allocator, traceBuilderResult).analyze(); - } - - private static final class Analyser { - private static final int DUMP_DURING_ANALYSIS_LEVEL = 4; - private final TraceLinearScan allocator; - private final TraceBuilderResult<?> traceBuilderResult; - - /** - * @param linearScan - * @param traceBuilderResult - */ - private Analyser(TraceLinearScan linearScan, TraceBuilderResult<?> traceBuilderResult) { - allocator = linearScan; - this.traceBuilderResult = traceBuilderResult; - } - - private void analyze() { - numberInstructions(); - allocator.printLir("Before register allocation", true); - buildIntervals(); - } - - private boolean sameTrace(AbstractBlockBase<?> a, AbstractBlockBase<?> b) { - return traceBuilderResult.getTraceForBlock(b) == traceBuilderResult.getTraceForBlock(a); - } - - private boolean isAllocatedOrCurrent(AbstractBlockBase<?> currentBlock, AbstractBlockBase<?> other) { - return traceBuilderResult.getTraceForBlock(other) <= traceBuilderResult.getTraceForBlock(currentBlock); - } - - private static void setHint(final LIRInstruction op, TraceInterval to, IntervalHint from) { - IntervalHint currentHint = to.locationHint(false); - if (currentHint == null) { - /* - * Update hint if there was none or if the hint interval starts after the hinted - * interval. - */ - to.setLocationHint(from); - if (Debug.isLogEnabled()) { - Debug.log("operation at opId %d: added hint from interval %s to %s", op.id(), from, to); - } - } - } - - /** - * Numbers all instructions in all blocks. The numbering follows the - * {@linkplain ComputeBlockOrder linear scan order}. - */ - private void numberInstructions() { - - allocator.initIntervals(); - - ValueConsumer setVariableConsumer = (value, mode, flags) -> { - if (isVariable(value)) { - allocator.getOrCreateInterval(asVariable(value)); - } - }; - - // Assign IDs to LIR nodes and build a mapping, lirOps, from ID to LIRInstruction node. - int numInstructions = 0; - for (AbstractBlockBase<?> block : allocator.sortedBlocks()) { - numInstructions += allocator.getLIR().getLIRforBlock(block).size(); - } - - // initialize with correct length - allocator.initOpIdMaps(numInstructions); - - int opId = 0; - int index = 0; - for (AbstractBlockBase<?> block : allocator.sortedBlocks()) { - allocator.initBlockData(block); - - List<LIRInstruction> instructions = allocator.getLIR().getLIRforBlock(block); - - int numInst = instructions.size(); - for (int j = 0; j < numInst; j++) { - LIRInstruction op = instructions.get(j); - op.setId(opId); - - allocator.putOpIdMaps(index, op, block); - assert allocator.instructionForId(opId) == op : "must match"; - - op.visitEachTemp(setVariableConsumer); - op.visitEachOutput(setVariableConsumer); - - index++; - opId += 2; // numbering of lirOps by two - } - } - assert index == numInstructions : "must match"; - assert (index << 1) == opId : "must match: " + (index << 1); - } - - private void addUse(AllocatableValue operand, int from, int to, RegisterPriority registerPriority, LIRKind kind) { - if (!allocator.isProcessed(operand)) { - return; - } - if (isRegister(operand)) { - addFixedUse(asRegisterValue(operand), from, to); - } else { - assert isVariable(operand) : operand; - addVariableUse(asVariable(operand), from, to, registerPriority, kind); - } - } - - private void addFixedUse(RegisterValue reg, int from, int to) { - FixedInterval interval = allocator.getOrCreateFixedInterval(reg); - interval.addRange(from, to); - if (Debug.isLogEnabled()) { - Debug.log("add fixed use: %s, at %d", interval, to); - } - } - - private void addVariableUse(Variable operand, int from, int to, RegisterPriority registerPriority, LIRKind kind) { - TraceInterval interval = allocator.getOrCreateInterval(operand); - - if (!kind.equals(LIRKind.Illegal)) { - interval.setKind(kind); - } - - interval.addRange(from, to); - - // Register use position at even instruction id. - interval.addUsePos(to & ~1, registerPriority); - - if (Debug.isLogEnabled()) { - Debug.log("add use: %s, at %d (%s)", interval, to, registerPriority.name()); - } - } - - private void addTemp(AllocatableValue operand, int tempPos, RegisterPriority registerPriority, LIRKind kind) { - if (!allocator.isProcessed(operand)) { - return; - } - if (isRegister(operand)) { - addFixedTemp(asRegisterValue(operand), tempPos); - } else { - assert isVariable(operand) : operand; - addVariableTemp(asVariable(operand), tempPos, registerPriority, kind); - } - } - - private void addFixedTemp(RegisterValue reg, int tempPos) { - FixedInterval interval = allocator.getOrCreateFixedInterval(reg); - interval.addRange(tempPos, tempPos + 1); - if (Debug.isLogEnabled()) { - Debug.log("add fixed temp: %s, at %d", interval, tempPos); - } - } - - private void addVariableTemp(Variable operand, int tempPos, RegisterPriority registerPriority, LIRKind kind) { - TraceInterval interval = allocator.getOrCreateInterval(operand); - - if (!kind.equals(LIRKind.Illegal)) { - interval.setKind(kind); - } - - if (interval.isEmpty()) { - interval.addRange(tempPos, tempPos + 1); - } else if (interval.from() > tempPos) { - interval.setFrom(tempPos); - } - - interval.addUsePos(tempPos, registerPriority); - interval.addMaterializationValue(null); - - if (Debug.isLogEnabled()) { - Debug.log("add temp: %s tempPos %d (%s)", interval, tempPos, RegisterPriority.MustHaveRegister.name()); - } - } - - private void addDef(AllocatableValue operand, LIRInstruction op, RegisterPriority registerPriority, LIRKind kind) { - if (!allocator.isProcessed(operand)) { - return; - } - if (isRegister(operand)) { - addFixedDef(asRegisterValue(operand), op); - } else { - assert isVariable(operand) : operand; - addVariableDef(asVariable(operand), op, registerPriority, kind); - } - } - - private void addFixedDef(RegisterValue reg, LIRInstruction op) { - FixedInterval interval = allocator.getOrCreateFixedInterval(reg); - int defPos = op.id(); - if (interval.from() <= defPos) { - /* - * Update the starting point (when a range is first created for a use, its start is - * the beginning of the current block until a def is encountered). - */ - interval.setFrom(defPos); - - } else { - /* - * Dead value - make vacuous interval also add register priority for dead intervals - */ - interval.addRange(defPos, defPos + 1); - if (Debug.isLogEnabled()) { - Debug.log("Warning: def of operand %s at %d occurs without use", reg, defPos); - } - } - if (Debug.isLogEnabled()) { - Debug.log("add fixed def: %s, at %d", interval, defPos); - } - } - - private void addVariableDef(Variable operand, LIRInstruction op, RegisterPriority registerPriority, LIRKind kind) { - int defPos = op.id(); - - TraceInterval interval = allocator.getOrCreateInterval(operand); - - if (!kind.equals(LIRKind.Illegal)) { - interval.setKind(kind); - } - - if (interval.isEmpty()) { - /* - * Dead value - make vacuous interval also add register priority for dead intervals - */ - interval.addRange(defPos, defPos + 1); - interval.addUsePos(defPos, registerPriority); - if (Debug.isLogEnabled()) { - Debug.log("Warning: def of operand %s at %d occurs without use", operand, defPos); - } - } else { - /* - * Update the starting point (when a range is first created for a use, its start is - * the beginning of the current block until a def is encountered). - */ - interval.setFrom(defPos); - interval.addUsePos(defPos, registerPriority); - } - - changeSpillDefinitionPos(op, operand, interval, defPos); - if (registerPriority == RegisterPriority.None && interval.spillState().ordinal() <= SpillState.StartInMemory.ordinal() && isStackSlot(operand)) { - // detection of method-parameters and roundfp-results - interval.setSpillState(SpillState.StartInMemory); - } - interval.addMaterializationValue(getMaterializedValue(op, operand, interval)); - - if (Debug.isLogEnabled()) { - Debug.log("add def: %s defPos %d (%s)", interval, defPos, registerPriority.name()); - } - } - - private void addRegisterHint(final LIRInstruction op, final Value targetValue, OperandMode mode, EnumSet<OperandFlag> flags, final boolean hintAtDef) { - if (flags.contains(OperandFlag.HINT) && TraceLinearScan.isVariableOrRegister(targetValue)) { - - op.forEachRegisterHint(targetValue, mode, (registerHint, valueMode, valueFlags) -> { - if (TraceLinearScan.isVariableOrRegister(registerHint)) { - AllocatableValue fromValue; - AllocatableValue toValue; - /* hints always point from def to use */ - if (hintAtDef) { - fromValue = (AllocatableValue) registerHint; - toValue = (AllocatableValue) targetValue; - } else { - fromValue = (AllocatableValue) targetValue; - toValue = (AllocatableValue) registerHint; - } - if (isRegister(toValue)) { - /* fixed register: no need for a hint */ - return null; - } - - TraceInterval to = allocator.getOrCreateInterval(toValue); - IntervalHint from = getIntervalHint(fromValue); - - to.setLocationHint(from); - if (Debug.isLogEnabled()) { - Debug.log("operation at opId %d: added hint from interval %s to %s", op.id(), from, to); - } - - return registerHint; - } - return null; - }); - } - } - - private IntervalHint getIntervalHint(AllocatableValue from) { - if (isRegister(from)) { - return allocator.getOrCreateFixedInterval(asRegisterValue(from)); - } - return allocator.getOrCreateInterval(from); - } - - /** - * Eliminates moves from register to stack if the stack slot is known to be correct. - * - * @param op - * @param operand - */ - private void changeSpillDefinitionPos(LIRInstruction op, AllocatableValue operand, TraceInterval interval, int defPos) { - assert interval.isSplitParent() : "can only be called for split parents"; - - switch (interval.spillState()) { - case NoDefinitionFound: - // assert interval.spillDefinitionPos() == -1 : "must no be set before"; - interval.setSpillDefinitionPos(defPos); - if (!(op instanceof LabelOp)) { - // Do not update state for labels. This will be done afterwards. - interval.setSpillState(SpillState.NoSpillStore); - } - break; - - case NoSpillStore: - assert defPos <= interval.spillDefinitionPos() : "positions are processed in reverse order when intervals are created"; - if (defPos < interval.spillDefinitionPos() - 2) { - /* - * Second definition found, so no spill optimization possible for this - * interval. - */ - interval.setSpillState(SpillState.NoOptimization); - } else { - // two consecutive definitions (because of two-operand LIR form) - assert allocator.blockForId(defPos) == allocator.blockForId(interval.spillDefinitionPos()) : "block must be equal"; - } - break; - - case NoOptimization: - // nothing to do - break; - - default: - throw new BailoutException("other states not allowed at this time"); - } - } - - private static boolean optimizeMethodArgument(Value value) { - /* - * Object method arguments that are passed on the stack are currently not optimized - * because this requires that the runtime visits method arguments during stack walking. - */ - return isStackSlot(value) && asStackSlot(value).isInCallerFrame() && value.getLIRKind().isValue(); - } - - /** - * Determines the register priority for an instruction's output/result operand. - */ - private static RegisterPriority registerPriorityOfOutputOperand(LIRInstruction op) { - if (op instanceof LabelOp) { - // skip method header - return RegisterPriority.None; - } - if (op instanceof ValueMoveOp) { - ValueMoveOp move = (ValueMoveOp) op; - if (optimizeMethodArgument(move.getInput())) { - return RegisterPriority.None; - } - } - - // all other operands require a register - return RegisterPriority.MustHaveRegister; - } - - /** - * Determines the priority which with an instruction's input operand will be allocated a - * register. - */ - private static RegisterPriority registerPriorityOfInputOperand(EnumSet<OperandFlag> flags) { - if (flags.contains(OperandFlag.OUTGOING)) { - return RegisterPriority.None; - } - if (flags.contains(OperandFlag.STACK)) { - return RegisterPriority.ShouldHaveRegister; - } - // all other operands require a register - return RegisterPriority.MustHaveRegister; - } - - @SuppressWarnings("try") - private void buildIntervals() { - - try (Indent indent = Debug.logAndIndent("build intervals")) { - InstructionValueConsumer outputConsumer = (op, operand, mode, flags) -> { - if (TraceLinearScan.isVariableOrRegister(operand)) { - addDef((AllocatableValue) operand, op, registerPriorityOfOutputOperand(op), operand.getLIRKind()); - addRegisterHint(op, operand, mode, flags, true); - } - }; - - InstructionValueConsumer tempConsumer = (op, operand, mode, flags) -> { - if (TraceLinearScan.isVariableOrRegister(operand)) { - addTemp((AllocatableValue) operand, op.id(), RegisterPriority.MustHaveRegister, operand.getLIRKind()); - addRegisterHint(op, operand, mode, flags, false); - } - }; - - InstructionValueConsumer aliveConsumer = (op, operand, mode, flags) -> { - if (TraceLinearScan.isVariableOrRegister(operand)) { - RegisterPriority p = registerPriorityOfInputOperand(flags); - int opId = op.id(); - int blockFrom = allocator.getFirstLirInstructionId((allocator.blockForId(opId))); - addUse((AllocatableValue) operand, blockFrom, opId + 1, p, operand.getLIRKind()); - addRegisterHint(op, operand, mode, flags, false); - } - }; - - InstructionValueConsumer inputConsumer = (op, operand, mode, flags) -> { - if (TraceLinearScan.isVariableOrRegister(operand)) { - int opId = op.id(); - RegisterPriority p = registerPriorityOfInputOperand(flags); - int blockFrom = allocator.getFirstLirInstructionId((allocator.blockForId(opId))); - addUse((AllocatableValue) operand, blockFrom, opId, p, operand.getLIRKind()); - addRegisterHint(op, operand, mode, flags, false); - } - }; - - InstructionValueConsumer stateProc = (op, operand, mode, flags) -> { - if (TraceLinearScan.isVariableOrRegister(operand)) { - int opId = op.id(); - int blockFrom = allocator.getFirstLirInstructionId((allocator.blockForId(opId))); - addUse((AllocatableValue) operand, blockFrom, opId + 1, RegisterPriority.None, operand.getLIRKind()); - } - }; - - // create a list with all caller-save registers (cpu, fpu, xmm) - Register[] callerSaveRegs = allocator.getRegisterAllocationConfig().getRegisterConfig().getCallerSaveRegisters(); - - // iterate all blocks in reverse order - for (int i = allocator.blockCount() - 1; i >= 0; i--) { - - AbstractBlockBase<?> block = allocator.blockAt(i); - // TODO (je) make empty bitset - remove - allocator.getBlockData(block).liveIn = new BitSet(); - allocator.getBlockData(block).liveOut = new BitSet(); - try (Indent indent2 = Debug.logAndIndent("handle block %d", block.getId())) { - - List<LIRInstruction> instructions = allocator.getLIR().getLIRforBlock(block); - - /* - * Iterate all instructions of the block in reverse order. definitions of - * intervals are processed before uses. - */ - for (int j = instructions.size() - 1; j >= 0; j--) { - final LIRInstruction op = instructions.get(j); - final int opId = op.id(); - - try (Indent indent3 = Debug.logAndIndent("handle inst %d: %s", opId, op)) { - - // add a temp range for each register if operation destroys - // caller-save registers - if (op.destroysCallerSavedRegisters()) { - for (Register r : callerSaveRegs) { - if (allocator.attributes(r).isAllocatable()) { - addTemp(r.asValue(), opId, RegisterPriority.None, LIRKind.Illegal); - } - } - if (Debug.isLogEnabled()) { - Debug.log("operation destroys all caller-save registers"); - } - } - - op.visitEachOutput(outputConsumer); - op.visitEachTemp(tempConsumer); - op.visitEachAlive(aliveConsumer); - op.visitEachInput(inputConsumer); - - /* - * Add uses of live locals from interpreter's point of view for - * proper debug information generation. Treat these operands as temp - * values (if the live range is extended to a call site, the value - * would be in a register at the call otherwise). - */ - op.visitEachState(stateProc); - } - - } // end of instruction iteration - } - if (Debug.isDumpEnabled(DUMP_DURING_ANALYSIS_LEVEL)) { - allocator.printIntervals("After Block " + block); - } - } // end of block iteration - - // fix spill state for phi/sigma intervals - for (TraceInterval interval : allocator.intervals()) { - if (interval != null && interval.spillState().equals(SpillState.NoDefinitionFound) && interval.spillDefinitionPos() != -1) { - // there was a definition in a phi/sigma - interval.setSpillState(SpillState.NoSpillStore); - } - } - if (TraceRAuseInterTraceHints.getValue()) { - addInterTraceHints(); - } - /* - * Add the range [-1, 0] to all fixed intervals. the register allocator need not - * handle unhandled fixed intervals. - */ - for (FixedInterval interval : allocator.fixedIntervals()) { - if (interval != null) { - /* We use [-1, 0] to avoid intersection with incoming values. */ - interval.addRange(-1, 0); - } - } - } - } - - private void addInterTraceHints() { - // set hints for phi/sigma intervals - LIR lir = allocator.getLIR(); - for (AbstractBlockBase<?> block : allocator.sortedBlocks()) { - LabelOp label = SSIUtil.incoming(lir, block); - for (AbstractBlockBase<?> pred : block.getPredecessors()) { - if (isAllocatedOrCurrent(block, pred)) { - BlockEndOp outgoing = SSIUtil.outgoing(lir, pred); - for (int i = 0; i < outgoing.getOutgoingSize(); i++) { - Value toValue = label.getIncomingValue(i); - assert !isShadowedRegisterValue(toValue) : "Shadowed Registers are not allowed here: " + toValue; - if (isVariable(toValue)) { - Value fromValue = outgoing.getOutgoingValue(i); - assert sameTrace(block, pred) || !isVariable(fromValue) : "Unallocated variable: " + fromValue; - if (!LIRValueUtil.isConstantValue(fromValue)) { - addInterTraceHint(label, (AllocatableValue) toValue, fromValue); - } - } - } - } - } - } - } - - private void addInterTraceHint(LabelOp label, AllocatableValue toValue, Value fromValue) { - assert isVariable(toValue) : "Wrong toValue: " + toValue; - assert isRegister(fromValue) || isVariable(fromValue) || isStackSlotValue(fromValue) || isShadowedRegisterValue(fromValue) : "Wrong fromValue: " + fromValue; - if (isVariableOrRegister(fromValue)) { - TraceInterval to = allocator.getOrCreateInterval(toValue); - IntervalHint from = getIntervalHint((AllocatableValue) fromValue); - setHint(label, to, from); - } else if (isStackSlotValue(fromValue)) { - TraceInterval to = allocator.getOrCreateInterval(toValue); - to.setSpillSlot((AllocatableValue) fromValue); - to.setSpillState(SpillState.StartInMemory); - } else if (TraceRAshareSpillInformation.getValue() && isShadowedRegisterValue(fromValue)) { - ShadowedRegisterValue shadowedRegisterValue = asShadowedRegisterValue(fromValue); - IntervalHint from = getIntervalHint(shadowedRegisterValue.getRegister()); - TraceInterval to = allocator.getOrCreateInterval(toValue); - setHint(label, to, from); - to.setSpillSlot(shadowedRegisterValue.getStackSlot()); - to.setSpillState(SpillState.StartInMemory); - } else { - throw JVMCIError.shouldNotReachHere(); - } - } - - /** - * Returns a value for a interval definition, which can be used for re-materialization. - * - * @param op An instruction which defines a value - * @param operand The destination operand of the instruction - * @param interval The interval for this defined value. - * @return Returns the value which is moved to the instruction and which can be reused at - * all reload-locations in case the interval of this instruction is spilled. - * Currently this can only be a {@link JavaConstant}. - */ - private JavaConstant getMaterializedValue(LIRInstruction op, Value operand, TraceInterval interval) { - if (op instanceof LoadConstantOp) { - LoadConstantOp move = (LoadConstantOp) op; - if (move.getConstant() instanceof JavaConstant) { - if (!allocator.neverSpillConstants()) { - if (!allocator.getSpillMoveFactory().allowConstantToStackMove(move.getConstant())) { - return null; - } - /* - * Check if the interval has any uses which would accept an stack location - * (priority == ShouldHaveRegister). Rematerialization of such intervals can - * result in a degradation, because rematerialization always inserts a - * constant load, even if the value is not needed in a register. - */ - UsePosList usePosList = interval.usePosList(); - int numUsePos = usePosList.size(); - for (int useIdx = 0; useIdx < numUsePos; useIdx++) { - TraceInterval.RegisterPriority priority = usePosList.registerPriority(useIdx); - if (priority == TraceInterval.RegisterPriority.ShouldHaveRegister) { - return null; - } - } - } - return (JavaConstant) move.getConstant(); - } - } - return null; - } - } -}
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/TraceLinearScanRegisterAllocationPhase.java Tue Dec 08 12:29:24 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2015, 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.graal.lir.alloc.trace; - -import java.util.List; - -import jdk.vm.ci.code.TargetDescription; - -import com.oracle.graal.compiler.common.alloc.RegisterAllocationConfig; -import com.oracle.graal.compiler.common.alloc.TraceBuilder.TraceBuilderResult; -import com.oracle.graal.compiler.common.cfg.AbstractBlockBase; -import com.oracle.graal.debug.Debug; -import com.oracle.graal.debug.Indent; -import com.oracle.graal.lir.gen.LIRGenerationResult; -import com.oracle.graal.lir.gen.LIRGeneratorTool.MoveFactory; - -final class TraceLinearScanRegisterAllocationPhase extends TraceLinearScanAllocationPhase { - - @Override - protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, MoveFactory spillMoveFactory, - RegisterAllocationConfig registerAllocationConfig, TraceBuilderResult<?> traceBuilderResult, TraceLinearScan allocator) { - allocator.printIntervals("Before register allocation"); - allocateRegisters(allocator); - allocator.printIntervals("After register allocation"); - } - - @SuppressWarnings("try") - private static void allocateRegisters(TraceLinearScan allocator) { - try (Indent indent = Debug.logAndIndent("allocate registers")) { - FixedInterval precoloredIntervals = allocator.createFixedUnhandledList(); - TraceInterval notPrecoloredIntervals = allocator.createUnhandledList(TraceLinearScan.IS_VARIABLE_INTERVAL); - - // allocate cpu registers - TraceLinearScanWalker lsw = new TraceLinearScanWalker(allocator, precoloredIntervals, notPrecoloredIntervals); - lsw.walk(); - lsw.finishAllocation(); - } - } - -}
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/TraceLinearScanResolveDataFlowPhase.java Tue Dec 08 12:29:24 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,240 +0,0 @@ -/* - * Copyright (c) 2015, 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.graal.lir.alloc.trace; - -import static com.oracle.graal.compiler.common.GraalOptions.DetailedAsserts; -import static com.oracle.graal.lir.LIRValueUtil.asConstant; -import static com.oracle.graal.lir.LIRValueUtil.isConstantValue; -import static com.oracle.graal.lir.LIRValueUtil.isStackSlotValue; -import static com.oracle.graal.lir.LIRValueUtil.isVirtualStackSlot; -import static jdk.vm.ci.code.ValueUtil.isRegister; - -import java.util.BitSet; -import java.util.List; -import java.util.ListIterator; - -import jdk.vm.ci.code.TargetDescription; -import jdk.vm.ci.meta.Value; - -import com.oracle.graal.compiler.common.alloc.RegisterAllocationConfig; -import com.oracle.graal.compiler.common.alloc.TraceBuilder.TraceBuilderResult; -import com.oracle.graal.compiler.common.cfg.AbstractBlockBase; -import com.oracle.graal.debug.Debug; -import com.oracle.graal.debug.DebugMetric; -import com.oracle.graal.debug.Indent; -import com.oracle.graal.lir.LIRInstruction; -import com.oracle.graal.lir.StandardOp; -import com.oracle.graal.lir.gen.LIRGenerationResult; -import com.oracle.graal.lir.gen.LIRGeneratorTool.MoveFactory; -import com.oracle.graal.lir.ssa.SSAUtil.PhiValueVisitor; -import com.oracle.graal.lir.ssi.SSIUtil; - -/** - * Phase 6: resolve data flow - * - * Insert moves at edges between blocks if intervals have been split. - */ -final class TraceLinearScanResolveDataFlowPhase extends TraceLinearScanAllocationPhase { - - @Override - protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, MoveFactory spillMoveFactory, - RegisterAllocationConfig registerAllocationConfig, TraceBuilderResult<?> traceBuilderResult, TraceLinearScan allocator) { - new Resolver(allocator, traceBuilderResult).resolveDataFlow(allocator.sortedBlocks()); - } - - private static final class Resolver { - private final TraceLinearScan allocator; - private final TraceBuilderResult<?> traceBuilderResult; - - private Resolver(TraceLinearScan allocator, TraceBuilderResult<?> traceBuilderResult) { - this.allocator = allocator; - this.traceBuilderResult = traceBuilderResult; - } - - private void resolveFindInsertPos(AbstractBlockBase<?> fromBlock, AbstractBlockBase<?> toBlock, TraceLocalMoveResolver moveResolver) { - if (fromBlock.getSuccessorCount() <= 1) { - if (Debug.isLogEnabled()) { - Debug.log("inserting moves at end of fromBlock B%d", fromBlock.getId()); - } - - List<LIRInstruction> instructions = allocator.getLIR().getLIRforBlock(fromBlock); - LIRInstruction instr = instructions.get(instructions.size() - 1); - if (instr instanceof StandardOp.JumpOp) { - // insert moves before branch - moveResolver.setInsertPosition(instructions, instructions.size() - 1); - } else { - moveResolver.setInsertPosition(instructions, instructions.size()); - } - - } else { - if (Debug.isLogEnabled()) { - Debug.log("inserting moves at beginning of toBlock B%d", toBlock.getId()); - } - - if (DetailedAsserts.getValue()) { - assert allocator.getLIR().getLIRforBlock(fromBlock).get(0) instanceof StandardOp.LabelOp : "block does not start with a label"; - - /* - * Because the number of predecessor edges matches the number of successor - * edges, blocks which are reached by switch statements may have be more than - * one predecessor but it will be guaranteed that all predecessors will be the - * same. - */ - for (AbstractBlockBase<?> predecessor : toBlock.getPredecessors()) { - assert fromBlock == predecessor : "all critical edges must be broken"; - } - } - - moveResolver.setInsertPosition(allocator.getLIR().getLIRforBlock(toBlock), 1); - } - } - - /** - * Inserts necessary moves (spilling or reloading) at edges between blocks for intervals - * that have been split. - */ - @SuppressWarnings("try") - private void resolveDataFlow(List<? extends AbstractBlockBase<?>> blocks) { - if (blocks.size() < 2) { - // no resolution necessary - return; - } - try (Indent indent = Debug.logAndIndent("resolve data flow")) { - - TraceLocalMoveResolver moveResolver = allocator.createMoveResolver(); - ListIterator<? extends AbstractBlockBase<?>> it = blocks.listIterator(); - AbstractBlockBase<?> toBlock = null; - for (AbstractBlockBase<?> fromBlock = it.next(); it.hasNext(); fromBlock = toBlock) { - toBlock = it.next(); - assert containedInTrace(fromBlock) : "Not in Trace: " + fromBlock; - assert containedInTrace(toBlock) : "Not in Trace: " + toBlock; - resolveCollectMappings(fromBlock, toBlock, moveResolver); - } - assert blocks.get(blocks.size() - 1).equals(toBlock); - if (toBlock.isLoopEnd()) { - assert toBlock.getSuccessorCount() == 1; - AbstractBlockBase<?> loopHeader = toBlock.getSuccessors().get(0); - if (containedInTrace(loopHeader)) { - resolveCollectMappings(toBlock, loopHeader, moveResolver); - } - } - - } - } - - @SuppressWarnings("try") - private void resolveCollectMappings(AbstractBlockBase<?> fromBlock, AbstractBlockBase<?> toBlock, TraceLocalMoveResolver moveResolver) { - try (Indent indent0 = Debug.logAndIndent("Edge %s -> %s", fromBlock, toBlock)) { - collectLSRAMappings(fromBlock, toBlock, moveResolver); - collectSSIMappings(fromBlock, toBlock, moveResolver); - } - } - - protected void collectLSRAMappings(AbstractBlockBase<?> fromBlock, AbstractBlockBase<?> toBlock, TraceLocalMoveResolver moveResolver) { - assert moveResolver.checkEmpty(); - - int toBlockFirstInstructionId = allocator.getFirstLirInstructionId(toBlock); - int fromBlockLastInstructionId = allocator.getLastLirInstructionId(fromBlock) + 1; - int numOperands = allocator.operandSize(); - BitSet liveAtEdge = allocator.getBlockData(toBlock).liveIn; - - // visit all variables for which the liveAtEdge bit is set - for (int operandNum = liveAtEdge.nextSetBit(0); operandNum >= 0; operandNum = liveAtEdge.nextSetBit(operandNum + 1)) { - assert operandNum < numOperands : "live information set for not exisiting interval"; - assert allocator.getBlockData(fromBlock).liveOut.get(operandNum) && allocator.getBlockData(toBlock).liveIn.get(operandNum) : "interval not live at this edge"; - - TraceInterval fromInterval = allocator.splitChildAtOpId(allocator.intervalFor(operandNum), fromBlockLastInstructionId, LIRInstruction.OperandMode.DEF); - TraceInterval toInterval = allocator.splitChildAtOpId(allocator.intervalFor(operandNum), toBlockFirstInstructionId, LIRInstruction.OperandMode.DEF); - - if (fromInterval != toInterval && !fromInterval.location().equals(toInterval.location())) { - // need to insert move instruction - moveResolver.addMapping(fromInterval, toInterval); - } - } - } - - protected void collectSSIMappings(AbstractBlockBase<?> fromBlock, AbstractBlockBase<?> toBlock, TraceLocalMoveResolver moveResolver) { - // collect all intervals that have been split between - // fromBlock and toBlock - SSIUtil.forEachValuePair(allocator.getLIR(), toBlock, fromBlock, new MyPhiValueVisitor(moveResolver, toBlock, fromBlock)); - if (moveResolver.hasMappings()) { - resolveFindInsertPos(fromBlock, toBlock, moveResolver); - moveResolver.resolveAndAppendMoves(); - } - } - - private boolean containedInTrace(AbstractBlockBase<?> block) { - return currentTrace() == traceBuilderResult.getTraceForBlock(block); - } - - private int currentTrace() { - return traceBuilderResult.getTraceForBlock(allocator.sortedBlocks().get(0)); - } - - private static final DebugMetric numSSIResolutionMoves = Debug.metric("SSI LSRA[numSSIResolutionMoves]"); - private static final DebugMetric numStackToStackMoves = Debug.metric("SSI LSRA[numStackToStackMoves]"); - - private class MyPhiValueVisitor implements PhiValueVisitor { - final TraceLocalMoveResolver moveResolver; - final int toId; - final int fromId; - - public MyPhiValueVisitor(TraceLocalMoveResolver moveResolver, AbstractBlockBase<?> toBlock, AbstractBlockBase<?> fromBlock) { - this.moveResolver = moveResolver; - toId = allocator.getFirstLirInstructionId(toBlock); - fromId = allocator.getLastLirInstructionId(fromBlock); - assert fromId >= 0; - } - - public void visit(Value phiIn, Value phiOut) { - assert !isRegister(phiOut) : "Out is a register: " + phiOut; - assert !isRegister(phiIn) : "In is a register: " + phiIn; - if (Value.ILLEGAL.equals(phiIn)) { - // The value not needed in this branch. - return; - } - if (isVirtualStackSlot(phiIn) && isVirtualStackSlot(phiOut) && phiIn.equals(phiOut)) { - // no need to handle virtual stack slots - return; - } - TraceInterval toInterval = allocator.splitChildAtOpId(allocator.intervalFor(phiIn), toId, LIRInstruction.OperandMode.DEF); - if (isConstantValue(phiOut)) { - numSSIResolutionMoves.increment(); - moveResolver.addMapping(asConstant(phiOut), toInterval); - } else { - TraceInterval fromInterval = allocator.splitChildAtOpId(allocator.intervalFor(phiOut), fromId, LIRInstruction.OperandMode.DEF); - if (fromInterval != toInterval) { - numSSIResolutionMoves.increment(); - if (!(isStackSlotValue(toInterval.location()) && isStackSlotValue(fromInterval.location()))) { - moveResolver.addMapping(fromInterval, toInterval); - } else { - numStackToStackMoves.increment(); - moveResolver.addMapping(fromInterval, toInterval); - } - } - } - } - } - } - -}
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/TraceLinearScanWalker.java Tue Dec 08 12:29:24 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1036 +0,0 @@ -/* - * Copyright (c) 2009, 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.graal.lir.alloc.trace; - -import static com.oracle.graal.lir.LIRValueUtil.isStackSlotValue; -import static com.oracle.graal.lir.LIRValueUtil.isVariable; -import static jdk.vm.ci.code.CodeUtil.isOdd; -import static jdk.vm.ci.code.ValueUtil.asRegister; -import static jdk.vm.ci.code.ValueUtil.isRegister; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import jdk.vm.ci.code.BailoutException; -import jdk.vm.ci.code.Register; -import jdk.vm.ci.common.JVMCIError; -import jdk.vm.ci.meta.Value; - -import com.oracle.graal.compiler.common.alloc.RegisterAllocationConfig.AllocatableRegisters; -import com.oracle.graal.compiler.common.cfg.AbstractBlockBase; -import com.oracle.graal.compiler.common.util.Util; -import com.oracle.graal.debug.Debug; -import com.oracle.graal.debug.Indent; -import com.oracle.graal.lir.LIRInstruction; -import com.oracle.graal.lir.StandardOp.BlockEndOp; -import com.oracle.graal.lir.StandardOp.LabelOp; -import com.oracle.graal.lir.StandardOp.ValueMoveOp; -import com.oracle.graal.lir.alloc.lsra.OutOfRegistersException; -import com.oracle.graal.lir.alloc.trace.TraceInterval.RegisterPriority; -import com.oracle.graal.lir.alloc.trace.TraceInterval.SpillState; -import com.oracle.graal.lir.alloc.trace.TraceInterval.State; - -/** - */ -final class TraceLinearScanWalker extends TraceIntervalWalker { - - private Register[] availableRegs; - - private final int[] usePos; - private final int[] blockPos; - - private List<TraceInterval>[] spillIntervals; - - private TraceLocalMoveResolver moveResolver; // for ordering spill moves - - private int minReg; - - private int maxReg; - - /** - * Only 10% of the lists in {@link #spillIntervals} are actually used. But when they are used, - * they can grow quite long. The maximum length observed was 45 (all numbers taken from a - * bootstrap run of Graal). Therefore, we initialize {@link #spillIntervals} with this marker - * value, and allocate a "real" list only on demand in {@link #setUsePos}. - */ - private static final List<TraceInterval> EMPTY_LIST = new ArrayList<>(0); - - // accessors mapped to same functions in class LinearScan - private int blockCount() { - return allocator.blockCount(); - } - - private AbstractBlockBase<?> blockAt(int idx) { - return allocator.blockAt(idx); - } - - @SuppressWarnings("unused") - private AbstractBlockBase<?> blockOfOpWithId(int opId) { - return allocator.blockForId(opId); - } - - TraceLinearScanWalker(TraceLinearScan allocator, FixedInterval unhandledFixedFirst, TraceInterval unhandledAnyFirst) { - super(allocator, unhandledFixedFirst, unhandledAnyFirst); - - moveResolver = allocator.createMoveResolver(); - spillIntervals = Util.uncheckedCast(new List<?>[allocator.getRegisters().length]); - for (int i = 0; i < allocator.getRegisters().length; i++) { - spillIntervals[i] = EMPTY_LIST; - } - usePos = new int[allocator.getRegisters().length]; - blockPos = new int[allocator.getRegisters().length]; - } - - private void initUseLists(boolean onlyProcessUsePos) { - for (Register register : availableRegs) { - int i = register.number; - usePos[i] = Integer.MAX_VALUE; - - if (!onlyProcessUsePos) { - blockPos[i] = Integer.MAX_VALUE; - spillIntervals[i].clear(); - } - } - } - - private int maxRegisterNumber() { - return maxReg; - } - - private int minRegisterNumber() { - return minReg; - } - - private boolean isRegisterInRange(int reg) { - return reg >= minRegisterNumber() && reg <= maxRegisterNumber(); - } - - private void excludeFromUse(IntervalHint i) { - Value location = i.location(); - int i1 = asRegister(location).number; - if (isRegisterInRange(i1)) { - usePos[i1] = 0; - } - } - - private void setUsePos(TraceInterval interval, int usePos, boolean onlyProcessUsePos) { - if (usePos != -1) { - assert usePos != 0 : "must use excludeFromUse to set usePos to 0"; - int i = asRegister(interval.location()).number; - if (isRegisterInRange(i)) { - if (this.usePos[i] > usePos) { - this.usePos[i] = usePos; - } - if (!onlyProcessUsePos) { - List<TraceInterval> list = spillIntervals[i]; - if (list == EMPTY_LIST) { - list = new ArrayList<>(2); - spillIntervals[i] = list; - } - list.add(interval); - } - } - } - } - - private void setUsePos(FixedInterval interval, int usePos, boolean onlyProcessUsePos) { - assert onlyProcessUsePos; - if (usePos != -1) { - assert usePos != 0 : "must use excludeFromUse to set usePos to 0"; - int i = asRegister(interval.location()).number; - if (isRegisterInRange(i)) { - if (this.usePos[i] > usePos) { - this.usePos[i] = usePos; - } - } - } - } - - private void setBlockPos(IntervalHint i, int blockPos) { - if (blockPos != -1) { - int reg = asRegister(i.location()).number; - if (isRegisterInRange(reg)) { - if (this.blockPos[reg] > blockPos) { - this.blockPos[reg] = blockPos; - } - if (usePos[reg] > blockPos) { - usePos[reg] = blockPos; - } - } - } - } - - private void freeExcludeActiveFixed() { - FixedInterval interval = activeFixedList.getFixed(); - while (interval != FixedInterval.EndMarker) { - assert isRegister(interval.location()) : "active interval must have a register assigned"; - excludeFromUse(interval); - interval = interval.next; - } - } - - private void freeExcludeActiveAny() { - TraceInterval interval = activeAnyList.getAny(); - while (interval != TraceInterval.EndMarker) { - assert isRegister(interval.location()) : "active interval must have a register assigned"; - excludeFromUse(interval); - interval = interval.next; - } - } - - private void freeCollectInactiveFixed(TraceInterval current) { - FixedInterval interval = inactiveFixedList.getFixed(); - while (interval != FixedInterval.EndMarker) { - if (current.to() <= interval.from()) { - assert interval.intersectsAt(current) == -1 : "must not intersect"; - setUsePos(interval, interval.from(), true); - } else { - setUsePos(interval, interval.currentIntersectsAt(current), true); - } - interval = interval.next; - } - } - - private void spillExcludeActiveFixed() { - FixedInterval interval = activeFixedList.getFixed(); - while (interval != FixedInterval.EndMarker) { - excludeFromUse(interval); - interval = interval.next; - } - } - - private void spillBlockInactiveFixed(TraceInterval current) { - FixedInterval interval = inactiveFixedList.getFixed(); - while (interval != FixedInterval.EndMarker) { - if (current.to() > interval.currentFrom()) { - setBlockPos(interval, interval.currentIntersectsAt(current)); - } else { - assert interval.currentIntersectsAt(current) == -1 : "invalid optimization: intervals intersect"; - } - - interval = interval.next; - } - } - - private void spillCollectActiveAny(RegisterPriority registerPriority) { - TraceInterval interval = activeAnyList.getAny(); - while (interval != TraceInterval.EndMarker) { - setUsePos(interval, Math.min(interval.nextUsage(registerPriority, currentPosition), interval.to()), false); - interval = interval.next; - } - } - - @SuppressWarnings("unused") - private int insertIdAtBasicBlockBoundary(int opId) { - assert allocator.isBlockBegin(opId) : "Not a block begin: " + opId; - assert allocator.instructionForId(opId) instanceof LabelOp; - assert allocator.instructionForId(opId - 2) instanceof BlockEndOp; - - AbstractBlockBase<?> toBlock = allocator.blockForId(opId); - AbstractBlockBase<?> fromBlock = allocator.blockForId(opId - 2); - - if (fromBlock.getSuccessorCount() == 1) { - // insert move in predecessor - return opId - 2; - } - assert toBlock.getPredecessorCount() == 1 : String.format("Critical Edge? %s->%s", fromBlock, toBlock); - // insert move in successor - return opId + 2; - } - - private void insertMove(int operandId, TraceInterval srcIt, TraceInterval dstIt) { - // output all moves here. When source and target are equal, the move is - // optimized away later in assignRegNums - - int opId = (operandId + 1) & ~1; - AbstractBlockBase<?> opBlock = allocator.blockForId(opId); - assert opId > 0 && allocator.blockForId(opId - 2) == opBlock : "cannot insert move at block boundary"; - - // calculate index of instruction inside instruction list of current block - // the minimal index (for a block with no spill moves) can be calculated because the - // numbering of instructions is known. - // When the block already contains spill moves, the index must be increased until the - // correct index is reached. - List<LIRInstruction> instructions = allocator.getLIR().getLIRforBlock(opBlock); - int index = (opId - instructions.get(0).id()) >> 1; - assert instructions.get(index).id() <= opId : "error in calculation"; - - while (instructions.get(index).id() != opId) { - index++; - assert 0 <= index && index < instructions.size() : "index out of bounds"; - } - assert 1 <= index && index < instructions.size() : "index out of bounds"; - assert instructions.get(index).id() == opId : "error in calculation"; - - // insert new instruction before instruction at position index - moveResolver.moveInsertPosition(instructions, index); - moveResolver.addMapping(srcIt, dstIt); - } - - private int findOptimalSplitPos(AbstractBlockBase<?> minBlock, AbstractBlockBase<?> maxBlock, int maxSplitPos) { - int fromBlockNr = minBlock.getLinearScanNumber(); - int toBlockNr = maxBlock.getLinearScanNumber(); - - assert 0 <= fromBlockNr && fromBlockNr < blockCount() : "out of range"; - assert 0 <= toBlockNr && toBlockNr < blockCount() : "out of range"; - assert fromBlockNr < toBlockNr : "must cross block boundary"; - - // Try to split at end of maxBlock. If this would be after - // maxSplitPos, then use the begin of maxBlock - int optimalSplitPos = allocator.getLastLirInstructionId(maxBlock) + 2; - if (optimalSplitPos > maxSplitPos) { - optimalSplitPos = allocator.getFirstLirInstructionId(maxBlock); - } - - // minimal block probability - double minProbability = maxBlock.probability(); - for (int i = toBlockNr - 1; i >= fromBlockNr; i--) { - AbstractBlockBase<?> cur = blockAt(i); - - if (cur.probability() < minProbability) { - // Block with lower probability found. Split at the end of this block. - minProbability = cur.probability(); - optimalSplitPos = allocator.getLastLirInstructionId(cur) + 2; - } - } - assert optimalSplitPos > allocator.maxOpId() || allocator.isBlockBegin(optimalSplitPos) : "algorithm must move split pos to block boundary"; - - return optimalSplitPos; - } - - private int findOptimalSplitPos(TraceInterval interval, int minSplitPos, int maxSplitPos, boolean doLoopOptimization) { - int optimalSplitPos = findOptimalSplitPos0(interval, minSplitPos, maxSplitPos, doLoopOptimization); - if (Debug.isLogEnabled()) { - Debug.log("optimal split position: %d", optimalSplitPos); - } - return optimalSplitPos; - } - - @SuppressWarnings({"unused"}) - private int findOptimalSplitPos0(TraceInterval interval, int minSplitPos, int maxSplitPos, boolean doLoopOptimization) { - // TODO (je) implement - if (minSplitPos == maxSplitPos) { - // trivial case, no optimization of split position possible - if (Debug.isLogEnabled()) { - Debug.log("min-pos and max-pos are equal, no optimization possible"); - } - return minSplitPos; - - } - assert minSplitPos < maxSplitPos : "must be true then"; - assert minSplitPos > 0 : "cannot access minSplitPos - 1 otherwise"; - - // reason for using minSplitPos - 1: when the minimal split pos is exactly at the - // beginning of a block, then minSplitPos is also a possible split position. - // Use the block before as minBlock, because then minBlock.lastLirInstructionId() + 2 == - // minSplitPos - AbstractBlockBase<?> minBlock = allocator.blockForId(minSplitPos - 1); - - // reason for using maxSplitPos - 1: otherwise there would be an assert on failure - // when an interval ends at the end of the last block of the method - // (in this case, maxSplitPos == allocator().maxLirOpId() + 2, and there is no - // block at this opId) - AbstractBlockBase<?> maxBlock = allocator.blockForId(maxSplitPos - 1); - - assert minBlock.getLinearScanNumber() <= maxBlock.getLinearScanNumber() : "invalid order"; - if (minBlock == maxBlock) { - // split position cannot be moved to block boundary : so split as late as possible - if (Debug.isLogEnabled()) { - Debug.log("cannot move split pos to block boundary because minPos and maxPos are in same block"); - } - return maxSplitPos; - - } - // seach optimal block boundary between minSplitPos and maxSplitPos - if (Debug.isLogEnabled()) { - Debug.log("moving split pos to optimal block boundary between block B%d and B%d", minBlock.getId(), maxBlock.getId()); - } - - return findOptimalSplitPos(minBlock, maxBlock, maxSplitPos); - } - - // split an interval at the optimal position between minSplitPos and - // maxSplitPos in two parts: - // 1) the left part has already a location assigned - // 2) the right part is sorted into to the unhandled-list - @SuppressWarnings("try") - private void splitBeforeUsage(TraceInterval interval, int minSplitPos, int maxSplitPos) { - - try (Indent indent = Debug.logAndIndent("splitting interval %s between %d and %d", interval, minSplitPos, maxSplitPos)) { - - assert interval.from() < minSplitPos : "cannot split at start of interval"; - assert currentPosition < minSplitPos : "cannot split before current position"; - assert minSplitPos <= maxSplitPos : "invalid order"; - assert maxSplitPos <= interval.to() : "cannot split after end of interval"; - - final int optimalSplitPos = findOptimalSplitPos(interval, minSplitPos, maxSplitPos, true); - - if (optimalSplitPos == interval.to() && interval.nextUsage(RegisterPriority.MustHaveRegister, minSplitPos) == Integer.MAX_VALUE) { - // the split position would be just before the end of the interval - // . no split at all necessary - if (Debug.isLogEnabled()) { - Debug.log("no split necessary because optimal split position is at end of interval"); - } - return; - } - // must calculate this before the actual split is performed and before split position is - // moved to odd opId - final int optimalSplitPosFinal; - boolean blockBegin = allocator.isBlockBegin(optimalSplitPos); - if (blockBegin) { - assert (optimalSplitPos & 1) == 0 : "Block begins must be even: " + optimalSplitPos; - // move position after the label (odd optId) - optimalSplitPosFinal = optimalSplitPos + 1; - } else { - // move position before actual instruction (odd opId) - optimalSplitPosFinal = (optimalSplitPos - 1) | 1; - } - - // TODO( je) better define what min split pos max split pos mean. - assert minSplitPos <= optimalSplitPosFinal && optimalSplitPosFinal <= maxSplitPos || minSplitPos == maxSplitPos && optimalSplitPosFinal == minSplitPos - 1 : "out of range"; - assert optimalSplitPosFinal <= interval.to() : "cannot split after end of interval"; - assert optimalSplitPosFinal > interval.from() : "cannot split at start of interval"; - - if (Debug.isLogEnabled()) { - Debug.log("splitting at position %d", optimalSplitPosFinal); - } - assert optimalSplitPosFinal > currentPosition : "Can not split interval " + interval + " at current position: " + currentPosition; - - // was: - // assert isBlockBegin || ((optimalSplitPos1 & 1) == 1) : - // "split pos must be odd when not on block boundary"; - // assert !isBlockBegin || ((optimalSplitPos1 & 1) == 0) : - // "split pos must be even on block boundary"; - assert (optimalSplitPosFinal & 1) == 1 : "split pos must be odd"; - - // TODO (je) duplicate code. try to fold - if (optimalSplitPosFinal == interval.to() && interval.nextUsage(RegisterPriority.MustHaveRegister, minSplitPos) == Integer.MAX_VALUE) { - // the split position would be just before the end of the interval - // . no split at all necessary - if (Debug.isLogEnabled()) { - Debug.log("no split necessary because optimal split position is at end of interval"); - } - return; - } - TraceInterval splitPart = interval.split(optimalSplitPosFinal, allocator); - - boolean moveNecessary = true; - splitPart.setInsertMoveWhenActivated(moveNecessary); - - assert splitPart.from() >= currentPosition : "cannot append new interval before current walk position"; - unhandledAnyList.addToListSortedByStartAndUsePositions(splitPart); - - if (Debug.isLogEnabled()) { - Debug.log("left interval %s: %s", moveNecessary ? " " : "", interval.logString(allocator)); - Debug.log("right interval %s: %s", moveNecessary ? "(move)" : "", splitPart.logString(allocator)); - } - } - } - - // split an interval at the optimal position between minSplitPos and - // maxSplitPos in two parts: - // 1) the left part has already a location assigned - // 2) the right part is always on the stack and therefore ignored in further processing - @SuppressWarnings("try") - private void splitForSpilling(TraceInterval interval) { - // calculate allowed range of splitting position - int maxSplitPos = currentPosition; - int previousUsage = interval.previousUsage(RegisterPriority.ShouldHaveRegister, maxSplitPos); - if (previousUsage == currentPosition) { - /* - * If there is a usage with ShouldHaveRegister priority at the current position fall - * back to MustHaveRegister priority. This only happens if register priority was - * downgraded to MustHaveRegister in #allocLockedRegister. - */ - previousUsage = interval.previousUsage(RegisterPriority.MustHaveRegister, maxSplitPos); - } - int minSplitPos = Math.max(previousUsage + 1, interval.from()); - - try (Indent indent = Debug.logAndIndent("splitting and spilling interval %s between %d and %d", interval, minSplitPos, maxSplitPos)) { - - assert interval.state == State.Active : "why spill interval that is not active?"; - assert interval.from() <= minSplitPos : "cannot split before start of interval"; - assert minSplitPos <= maxSplitPos : "invalid order"; - assert maxSplitPos < interval.to() : "cannot split at end end of interval"; - assert currentPosition < interval.to() : "interval must not end before current position"; - - if (minSplitPos == interval.from()) { - // the whole interval is never used, so spill it entirely to memory - - try (Indent indent2 = Debug.logAndIndent("spilling entire interval because split pos is at beginning of interval (use positions: %d)", interval.usePosList().size())) { - - assert interval.firstUsage(RegisterPriority.MustHaveRegister) > currentPosition : String.format("interval %s must not have use position before currentPosition %d", interval, - currentPosition); - - allocator.assignSpillSlot(interval); - handleSpillSlot(interval); - changeSpillState(interval, minSplitPos); - - // Also kick parent intervals out of register to memory when they have no use - // position. This avoids short interval in register surrounded by intervals in - // memory . avoid useless moves from memory to register and back - TraceInterval parent = interval; - while (parent != null && parent.isSplitChild()) { - parent = parent.getSplitChildBeforeOpId(parent.from()); - - if (isRegister(parent.location())) { - if (parent.firstUsage(RegisterPriority.ShouldHaveRegister) == Integer.MAX_VALUE) { - // parent is never used, so kick it out of its assigned register - if (Debug.isLogEnabled()) { - Debug.log("kicking out interval %d out of its register because it is never used", parent.operandNumber); - } - allocator.assignSpillSlot(parent); - handleSpillSlot(parent); - } else { - // do not go further back because the register is actually used by - // the interval - parent = null; - } - } - } - } - - } else { - // search optimal split pos, split interval and spill only the right hand part - int optimalSplitPos = findOptimalSplitPos(interval, minSplitPos, maxSplitPos, false); - - assert minSplitPos <= optimalSplitPos && optimalSplitPos <= maxSplitPos : "out of range"; - assert optimalSplitPos < interval.to() : "cannot split at end of interval"; - assert optimalSplitPos >= interval.from() : "cannot split before start of interval"; - - if (!allocator.isBlockBegin(optimalSplitPos)) { - // move position before actual instruction (odd opId) - optimalSplitPos = (optimalSplitPos - 1) | 1; - } - - try (Indent indent2 = Debug.logAndIndent("splitting at position %d", optimalSplitPos)) { - assert allocator.isBlockBegin(optimalSplitPos) || ((optimalSplitPos & 1) == 1) : "split pos must be odd when not on block boundary"; - assert !allocator.isBlockBegin(optimalSplitPos) || ((optimalSplitPos & 1) == 0) : "split pos must be even on block boundary"; - - TraceInterval spilledPart = interval.split(optimalSplitPos, allocator); - allocator.assignSpillSlot(spilledPart); - handleSpillSlot(spilledPart); - changeSpillState(spilledPart, optimalSplitPos); - - if (!allocator.isBlockBegin(optimalSplitPos)) { - if (Debug.isLogEnabled()) { - Debug.log("inserting move from interval %s to %s", interval, spilledPart); - } - insertMove(optimalSplitPos, interval, spilledPart); - } else { - if (Debug.isLogEnabled()) { - Debug.log("no need to insert move. done by data-flow resolution"); - } - } - - // the currentSplitChild is needed later when moves are inserted for reloading - assert spilledPart.currentSplitChild() == interval : "overwriting wrong currentSplitChild"; - spilledPart.makeCurrentSplitChild(); - - if (Debug.isLogEnabled()) { - Debug.log("left interval: %s", interval.logString(allocator)); - Debug.log("spilled interval : %s", spilledPart.logString(allocator)); - } - } - } - } - } - - // called during register allocation - private void changeSpillState(TraceInterval interval, int spillPos) { - if (TraceLinearScan.Options.LIROptTraceRAEliminateSpillMoves.getValue()) { - switch (interval.spillState()) { - case NoSpillStore: { - int defLoopDepth = allocator.blockForId(interval.spillDefinitionPos()).getLoopDepth(); - int spillLoopDepth = allocator.blockForId(spillPos).getLoopDepth(); - - if (defLoopDepth < spillLoopDepth) { - /* - * The loop depth of the spilling position is higher then the loop depth at - * the definition of the interval. Move write to memory out of loop. - */ - // store at definition of the interval - interval.setSpillState(SpillState.StoreAtDefinition); - } else { - /* - * The interval is currently spilled only once, so for now there is no - * reason to store the interval at the definition. - */ - interval.setSpillState(SpillState.OneSpillStore); - } - break; - } - - case OneSpillStore: { - // It is better to store it to memory at the definition. - interval.setSpillState(SpillState.StoreAtDefinition); - break; - } - - case SpillInDominator: - case StoreAtDefinition: - case StartInMemory: - case NoOptimization: - case NoDefinitionFound: - // nothing to do - break; - - default: - throw new BailoutException("other states not allowed at this time"); - } - } else { - interval.setSpillState(SpillState.NoOptimization); - } - } - - /** - * This is called for every interval that is assigned to a stack slot. - */ - private static void handleSpillSlot(TraceInterval interval) { - assert interval.location() != null && (interval.canMaterialize() || isStackSlotValue(interval.location())) : "interval not assigned to a stack slot " + interval; - // Do nothing. Stack slots are not processed in this implementation. - } - - private void splitStackInterval(TraceInterval interval) { - int minSplitPos = currentPosition + 1; - int maxSplitPos = Math.min(interval.firstUsage(RegisterPriority.ShouldHaveRegister), interval.to()); - - splitBeforeUsage(interval, minSplitPos, maxSplitPos); - } - - private void splitWhenPartialRegisterAvailable(TraceInterval interval, int registerAvailableUntil) { - int minSplitPos = Math.max(interval.previousUsage(RegisterPriority.ShouldHaveRegister, registerAvailableUntil), interval.from() + 1); - splitBeforeUsage(interval, minSplitPos, registerAvailableUntil); - } - - private void splitAndSpillInterval(TraceInterval interval) { - assert interval.state == State.Active || interval.state == State.Inactive : "other states not allowed"; - - int currentPos = currentPosition; - if (interval.state == State.Inactive) { - // the interval is currently inactive, so no spill slot is needed for now. - // when the split part is activated, the interval has a new chance to get a register, - // so in the best case no stack slot is necessary - throw JVMCIError.shouldNotReachHere("TraceIntervals can not be inactive!"); - - } else { - // search the position where the interval must have a register and split - // at the optimal position before. - // The new created part is added to the unhandled list and will get a register - // when it is activated - int minSplitPos = currentPos + 1; - int maxSplitPos = interval.nextUsage(RegisterPriority.MustHaveRegister, minSplitPos); - - if (maxSplitPos <= interval.to()) { - splitBeforeUsage(interval, minSplitPos, maxSplitPos); - } else { - Debug.log("No more usage, no need to split: %s", interval); - } - - assert interval.nextUsage(RegisterPriority.MustHaveRegister, currentPos) == Integer.MAX_VALUE : "the remaining part is spilled to stack and therefore has no register"; - splitForSpilling(interval); - } - } - - @SuppressWarnings("try") - private boolean allocFreeRegister(TraceInterval interval) { - try (Indent indent = Debug.logAndIndent("trying to find free register for %s", interval)) { - - initUseLists(true); - freeExcludeActiveFixed(); - freeCollectInactiveFixed(interval); - freeExcludeActiveAny(); - // freeCollectUnhandled(fixedKind, cur); - - // usePos contains the start of the next interval that has this register assigned - // (either as a fixed register or a normal allocated register in the past) - // only intervals overlapping with cur are processed, non-overlapping invervals can be - // ignored safely - if (Debug.isLogEnabled()) { - // Enable this logging to see all register states - try (Indent indent2 = Debug.logAndIndent("state of registers:")) { - for (Register register : availableRegs) { - int i = register.number; - Debug.log("reg %d (%s): usePos: %d", register.number, register, usePos[i]); - } - } - } - - Register hint = null; - IntervalHint locationHint = interval.locationHint(true); - if (locationHint != null && locationHint.location() != null && isRegister(locationHint.location())) { - hint = asRegister(locationHint.location()); - if (Debug.isLogEnabled()) { - Debug.log("hint register %3d (%4s) from interval %s", hint.number, hint, locationHint); - } - } - assert interval.location() == null : "register already assigned to interval"; - - // the register must be free at least until this position - int regNeededUntil = interval.from() + 1; - int intervalTo = interval.to(); - - boolean needSplit = false; - int splitPos = -1; - - Register reg = null; - Register minFullReg = null; - Register maxPartialReg = null; - - for (Register availableReg : availableRegs) { - int number = availableReg.number; - if (usePos[number] >= intervalTo) { - // this register is free for the full interval - if (minFullReg == null || availableReg.equals(hint) || (usePos[number] < usePos[minFullReg.number] && !minFullReg.equals(hint))) { - minFullReg = availableReg; - } - } else if (usePos[number] > regNeededUntil) { - // this register is at least free until regNeededUntil - if (maxPartialReg == null || availableReg.equals(hint) || (usePos[number] > usePos[maxPartialReg.number] && !maxPartialReg.equals(hint))) { - maxPartialReg = availableReg; - } - } - } - - if (minFullReg != null) { - reg = minFullReg; - } else if (maxPartialReg != null) { - needSplit = true; - reg = maxPartialReg; - } else { - return false; - } - - splitPos = usePos[reg.number]; - interval.assignLocation(reg.asValue(interval.kind())); - if (Debug.isLogEnabled()) { - Debug.log("selected register %d (%s)", reg.number, reg); - } - - assert splitPos > 0 : "invalid splitPos"; - if (needSplit) { - // register not available for full interval, so split it - splitWhenPartialRegisterAvailable(interval, splitPos); - } - // only return true if interval is completely assigned - return true; - } - } - - private void splitAndSpillIntersectingIntervals(Register reg) { - assert reg != null : "no register assigned"; - - for (int i = 0; i < spillIntervals[reg.number].size(); i++) { - TraceInterval interval = spillIntervals[reg.number].get(i); - removeFromList(interval); - splitAndSpillInterval(interval); - } - } - - // Split an Interval and spill it to memory so that cur can be placed in a register - @SuppressWarnings("try") - private void allocLockedRegister(TraceInterval interval) { - try (Indent indent = Debug.logAndIndent("alloc locked register: need to split and spill to get register for %s", interval)) { - - // the register must be free at least until this position - int firstUsage = interval.firstUsage(RegisterPriority.MustHaveRegister); - int firstShouldHaveUsage = interval.firstUsage(RegisterPriority.ShouldHaveRegister); - int regNeededUntil = Math.min(firstUsage, interval.from() + 1); - int intervalTo = interval.to(); - assert regNeededUntil >= 0 && regNeededUntil < Integer.MAX_VALUE : "interval has no use"; - - Register reg; - Register ignore; - /* - * In the common case we don't spill registers that have _any_ use position that is - * closer than the next use of the current interval, but if we can't spill the current - * interval we weaken this strategy and also allow spilling of intervals that have a - * non-mandatory requirements (no MustHaveRegister use position). - */ - for (RegisterPriority registerPriority = RegisterPriority.LiveAtLoopEnd; true; registerPriority = RegisterPriority.MustHaveRegister) { - // collect current usage of registers - initUseLists(false); - spillExcludeActiveFixed(); - // spillBlockUnhandledFixed(cur); - spillBlockInactiveFixed(interval); - spillCollectActiveAny(registerPriority); - if (Debug.isLogEnabled()) { - printRegisterState(); - } - - reg = null; - ignore = interval.location() != null && isRegister(interval.location()) ? asRegister(interval.location()) : null; - - for (Register availableReg : availableRegs) { - int number = availableReg.number; - if (availableReg.equals(ignore)) { - // this register must be ignored - } else if (usePos[number] > regNeededUntil) { - if (reg == null || (usePos[number] > usePos[reg.number])) { - reg = availableReg; - } - } - } - - int regUsePos = (reg == null ? 0 : usePos[reg.number]); - if (regUsePos <= firstShouldHaveUsage) { - if (Debug.isLogEnabled()) { - Debug.log("able to spill current interval. firstUsage(register): %d, usePos: %d", firstUsage, regUsePos); - } - - if (firstUsage <= interval.from() + 1) { - if (registerPriority.equals(RegisterPriority.LiveAtLoopEnd)) { - /* - * Tool of last resort: we can not spill the current interval so we try - * to spill an active interval that has a usage but do not require a - * register. - */ - Debug.log("retry with register priority must have register"); - continue; - } - String description = "cannot spill interval (" + interval + ") that is used in first instruction (possible reason: no register found) firstUsage=" + firstUsage + - ", interval.from()=" + interval.from() + "; already used candidates: " + Arrays.toString(availableRegs); - /* - * assign a reasonable register and do a bailout in product mode to avoid - * errors - */ - allocator.assignSpillSlot(interval); - Debug.dump(allocator.getLIR(), description); - allocator.printIntervals(description); - throw new OutOfRegistersException("LinearScan: no register found", description); - } - - splitAndSpillInterval(interval); - return; - } - break; - } - - boolean needSplit = blockPos[reg.number] <= intervalTo; - - int splitPos = blockPos[reg.number]; - - if (Debug.isLogEnabled()) { - Debug.log("decided to use register %d", reg.number); - } - assert splitPos > 0 : "invalid splitPos"; - assert needSplit || splitPos > interval.from() : "splitting interval at from"; - - interval.assignLocation(reg.asValue(interval.kind())); - if (needSplit) { - // register not available for full interval : so split it - splitWhenPartialRegisterAvailable(interval, splitPos); - } - - // perform splitting and spilling for all affected intervals - splitAndSpillIntersectingIntervals(reg); - return; - } - } - - @SuppressWarnings("try") - private void printRegisterState() { - try (Indent indent2 = Debug.logAndIndent("state of registers:")) { - for (Register reg : availableRegs) { - int i = reg.number; - try (Indent indent3 = Debug.logAndIndent("reg %d: usePos: %d, blockPos: %d, intervals: ", i, usePos[i], blockPos[i])) { - for (int j = 0; j < spillIntervals[i].size(); j++) { - Debug.log("%s ", spillIntervals[i].get(j)); - } - } - } - } - } - - private boolean noAllocationPossible(TraceInterval interval) { - if (allocator.callKillsRegisters()) { - // fast calculation of intervals that can never get a register because the - // the next instruction is a call that blocks all registers - // Note: this only works if a call kills all registers - - // check if this interval is the result of a split operation - // (an interval got a register until this position) - int pos = interval.from(); - if (isOdd(pos)) { - // the current instruction is a call that blocks all registers - if (pos < allocator.maxOpId() && allocator.hasCall(pos + 1) && interval.to() > pos + 1) { - if (Debug.isLogEnabled()) { - Debug.log("free register cannot be available because all registers blocked by following call"); - } - - // safety check that there is really no register available - assert !allocFreeRegister(interval) : "found a register for this interval"; - return true; - } - } - } - return false; - } - - private void initVarsForAlloc(TraceInterval interval) { - AllocatableRegisters allocatableRegisters = allocator.getRegisterAllocationConfig().getAllocatableRegisters(interval.kind().getPlatformKind()); - availableRegs = allocatableRegisters.allocatableRegisters; - minReg = allocatableRegisters.minRegisterNumber; - maxReg = allocatableRegisters.maxRegisterNumber; - } - - private static boolean isMove(LIRInstruction op, TraceInterval from, TraceInterval to) { - if (op instanceof ValueMoveOp) { - ValueMoveOp move = (ValueMoveOp) op; - if (isVariable(move.getInput()) && isVariable(move.getResult())) { - return move.getInput() != null && move.getInput().equals(from.operand) && move.getResult() != null && move.getResult().equals(to.operand); - } - } - return false; - } - - // optimization (especially for phi functions of nested loops): - // assign same spill slot to non-intersecting intervals - private void combineSpilledIntervals(TraceInterval interval) { - if (interval.isSplitChild()) { - // optimization is only suitable for split parents - return; - } - - IntervalHint locationHint = interval.locationHint(false); - if (locationHint == null || !(locationHint instanceof TraceInterval)) { - return; - } - TraceInterval registerHint = (TraceInterval) locationHint; - assert registerHint.isSplitParent() : "register hint must be split parent"; - - if (interval.spillState() != SpillState.NoOptimization || registerHint.spillState() != SpillState.NoOptimization) { - // combining the stack slots for intervals where spill move optimization is applied - // is not benefitial and would cause problems - return; - } - - int beginPos = interval.from(); - int endPos = interval.to(); - if (endPos > allocator.maxOpId() || isOdd(beginPos) || isOdd(endPos)) { - // safety check that lirOpWithId is allowed - return; - } - - if (!isMove(allocator.instructionForId(beginPos), registerHint, interval) || !isMove(allocator.instructionForId(endPos), interval, registerHint)) { - // cur and registerHint are not connected with two moves - return; - } - - TraceInterval beginHint = registerHint.getSplitChildAtOpId(beginPos, LIRInstruction.OperandMode.USE, allocator); - TraceInterval endHint = registerHint.getSplitChildAtOpId(endPos, LIRInstruction.OperandMode.DEF, allocator); - if (beginHint == endHint || beginHint.to() != beginPos || endHint.from() != endPos) { - // registerHint must be split : otherwise the re-writing of use positions does not work - return; - } - - assert beginHint.location() != null : "must have register assigned"; - assert endHint.location() == null : "must not have register assigned"; - assert interval.firstUsage(RegisterPriority.MustHaveRegister) == beginPos : "must have use position at begin of interval because of move"; - assert endHint.firstUsage(RegisterPriority.MustHaveRegister) == endPos : "must have use position at begin of interval because of move"; - - if (isRegister(beginHint.location())) { - // registerHint is not spilled at beginPos : so it would not be benefitial to - // immediately spill cur - return; - } - assert registerHint.spillSlot() != null : "must be set when part of interval was spilled"; - - // modify intervals such that cur gets the same stack slot as registerHint - // delete use positions to prevent the intervals to get a register at beginning - interval.setSpillSlot(registerHint.spillSlot()); - interval.removeFirstUsePos(); - endHint.removeFirstUsePos(); - } - - // allocate a physical register or memory location to an interval - @Override - @SuppressWarnings("try") - protected boolean activateCurrent(TraceInterval interval) { - if (Debug.isLogEnabled()) { - logCurrentStatus(); - } - boolean result = true; - - try (Indent indent = Debug.logAndIndent("activating interval %s, splitParent: %d", interval, interval.splitParent().operandNumber)) { - - final Value operand = interval.operand; - if (interval.location() != null && isStackSlotValue(interval.location())) { - // activating an interval that has a stack slot assigned . split it at first use - // position - // used for method parameters - if (Debug.isLogEnabled()) { - Debug.log("interval has spill slot assigned (method parameter) . split it before first use"); - } - splitStackInterval(interval); - result = false; - - } else { - if (interval.location() == null) { - // interval has not assigned register . normal allocation - // (this is the normal case for most intervals) - if (Debug.isLogEnabled()) { - Debug.log("normal allocation of register"); - } - - // assign same spill slot to non-intersecting intervals - combineSpilledIntervals(interval); - - initVarsForAlloc(interval); - if (noAllocationPossible(interval) || !allocFreeRegister(interval)) { - // no empty register available. - // split and spill another interval so that this interval gets a register - allocLockedRegister(interval); - } - - // spilled intervals need not be move to active-list - if (!isRegister(interval.location())) { - result = false; - } - } - } - - // load spilled values that become active from stack slot to register - if (interval.insertMoveWhenActivated()) { - assert interval.isSplitChild(); - assert interval.currentSplitChild() != null; - assert !interval.currentSplitChild().operand.equals(operand) : "cannot insert move between same interval"; - if (Debug.isLogEnabled()) { - Debug.log("Inserting move from interval %d to %d because insertMoveWhenActivated is set", interval.currentSplitChild().operandNumber, interval.operandNumber); - } - - insertMove(interval.from(), interval.currentSplitChild(), interval); - } - interval.makeCurrentSplitChild(); - - } - - return result; // true = interval is moved to active list - } - - void finishAllocation() { - // must be called when all intervals are allocated - moveResolver.resolveAndAppendMoves(); - } -}
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/TraceLocalMoveResolver.java Tue Dec 08 12:29:24 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,550 +0,0 @@ -/* - * Copyright (c) 2009, 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.graal.lir.alloc.trace; - -import static com.oracle.graal.lir.LIRValueUtil.asVirtualStackSlot; -import static com.oracle.graal.lir.LIRValueUtil.isStackSlotValue; -import static com.oracle.graal.lir.LIRValueUtil.isVirtualStackSlot; -import static jdk.vm.ci.code.ValueUtil.asRegister; -import static jdk.vm.ci.code.ValueUtil.asStackSlot; -import static jdk.vm.ci.code.ValueUtil.isIllegal; -import static jdk.vm.ci.code.ValueUtil.isRegister; -import static jdk.vm.ci.code.ValueUtil.isStackSlot; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; - -import jdk.vm.ci.code.StackSlot; -import jdk.vm.ci.common.JVMCIError; -import jdk.vm.ci.meta.AllocatableValue; -import jdk.vm.ci.meta.Constant; -import jdk.vm.ci.meta.JavaConstant; -import jdk.vm.ci.meta.LIRKind; -import jdk.vm.ci.meta.Value; - -import com.oracle.graal.debug.Debug; -import com.oracle.graal.debug.Indent; -import com.oracle.graal.lir.LIRInsertionBuffer; -import com.oracle.graal.lir.LIRInstruction; -import com.oracle.graal.lir.VirtualStackSlot; -import com.oracle.graal.lir.framemap.FrameMap; -import com.oracle.graal.lir.framemap.FrameMapBuilderTool; - -/** - */ -public class TraceLocalMoveResolver { - - private static final int STACK_SLOT_IN_CALLER_FRAME_IDX = -1; - private final TraceLinearScan allocator; - - private int insertIdx; - private LIRInsertionBuffer insertionBuffer; // buffer where moves are inserted - - private final List<TraceInterval> mappingFrom; - private final List<Constant> mappingFromOpr; - private final List<TraceInterval> mappingTo; - private final int[] registerBlocked; - - private int[] stackBlocked; - private final int firstVirtualStackIndex; - - private int getStackArrayIndex(Value stackSlotValue) { - if (isStackSlot(stackSlotValue)) { - return getStackArrayIndex(asStackSlot(stackSlotValue)); - } - if (isVirtualStackSlot(stackSlotValue)) { - return getStackArrayIndex(asVirtualStackSlot(stackSlotValue)); - } - throw JVMCIError.shouldNotReachHere("value is not a stack slot: " + stackSlotValue); - } - - private int getStackArrayIndex(StackSlot stackSlot) { - int stackIdx; - if (stackSlot.isInCallerFrame()) { - // incoming stack arguments can be ignored - stackIdx = STACK_SLOT_IN_CALLER_FRAME_IDX; - } else { - assert stackSlot.getRawAddFrameSize() : "Unexpected stack slot: " + stackSlot; - int offset = -stackSlot.getRawOffset(); - assert 0 <= offset && offset < firstVirtualStackIndex : String.format("Wrong stack slot offset: %d (first virtual stack slot index: %d", offset, firstVirtualStackIndex); - stackIdx = offset; - } - return stackIdx; - } - - private int getStackArrayIndex(VirtualStackSlot virtualStackSlot) { - return firstVirtualStackIndex + virtualStackSlot.getId(); - } - - protected void setValueBlocked(Value location, int direction) { - assert direction == 1 || direction == -1 : "out of bounds"; - if (isStackSlotValue(location)) { - int stackIdx = getStackArrayIndex(location); - if (stackIdx == STACK_SLOT_IN_CALLER_FRAME_IDX) { - // incoming stack arguments can be ignored - return; - } - if (stackIdx >= stackBlocked.length) { - stackBlocked = Arrays.copyOf(stackBlocked, stackIdx + 1); - } - stackBlocked[stackIdx] += direction; - } else { - assert direction == 1 || direction == -1 : "out of bounds"; - if (isRegister(location)) { - registerBlocked[asRegister(location).number] += direction; - } else { - throw JVMCIError.shouldNotReachHere("unhandled value " + location); - } - } - } - - protected TraceInterval getMappingFrom(int i) { - return mappingFrom.get(i); - } - - protected int mappingFromSize() { - return mappingFrom.size(); - } - - protected int valueBlocked(Value location) { - if (isStackSlotValue(location)) { - int stackIdx = getStackArrayIndex(location); - if (stackIdx == STACK_SLOT_IN_CALLER_FRAME_IDX) { - // incoming stack arguments are always blocked (aka they can not be written) - return 1; - } - if (stackIdx >= stackBlocked.length) { - return 0; - } - return stackBlocked[stackIdx]; - } - if (isRegister(location)) { - return registerBlocked[asRegister(location).number]; - } - throw JVMCIError.shouldNotReachHere("unhandled value " + location); - } - - protected boolean areMultipleReadsAllowed() { - return true; - } - - boolean hasMappings() { - return mappingFrom.size() > 0; - } - - protected TraceLinearScan getAllocator() { - return allocator; - } - - protected TraceLocalMoveResolver(TraceLinearScan allocator) { - - this.allocator = allocator; - this.mappingFrom = new ArrayList<>(8); - this.mappingFromOpr = new ArrayList<>(8); - this.mappingTo = new ArrayList<>(8); - this.insertIdx = -1; - this.insertionBuffer = new LIRInsertionBuffer(); - this.registerBlocked = new int[allocator.getRegisters().length]; - FrameMapBuilderTool frameMapBuilderTool = (FrameMapBuilderTool) allocator.getFrameMapBuilder(); - FrameMap frameMap = frameMapBuilderTool.getFrameMap(); - this.stackBlocked = new int[frameMapBuilderTool.getNumberOfStackSlots()]; - this.firstVirtualStackIndex = !frameMap.frameNeedsAllocating() ? 0 : frameMap.currentFrameSize() + 1; - } - - protected 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 < stackBlocked.length; i++) { - assert stackBlocked[i] == 0 : "stack map must be empty before and after processing"; - } - for (int i = 0; i < getAllocator().getRegisters().length; i++) { - assert registerBlocked[i] == 0 : "register map must be empty before and after processing"; - } - checkMultipleReads(); - return true; - } - - protected void checkMultipleReads() { - // multiple reads are allowed in SSA LSRA - } - - private boolean verifyBeforeResolve() { - assert mappingFrom.size() == mappingFromOpr.size() : "length must be equal"; - assert mappingFrom.size() == mappingTo.size() : "length must be equal"; - assert insertIdx != -1 : "insert position not set"; - - int i; - int j; - if (!areMultipleReadsAllowed()) { - for (i = 0; i < mappingFrom.size(); i++) { - for (j = i + 1; j < mappingFrom.size(); j++) { - assert mappingFrom.get(i) == null || mappingFrom.get(i) != mappingFrom.get(j) : "cannot read from same interval twice"; - } - } - } - - for (i = 0; i < mappingTo.size(); i++) { - for (j = i + 1; j < mappingTo.size(); j++) { - assert mappingTo.get(i) != mappingTo.get(j) : "cannot write to same interval twice"; - } - } - - HashSet<Value> usedRegs = new HashSet<>(); - if (!areMultipleReadsAllowed()) { - for (i = 0; i < mappingFrom.size(); i++) { - TraceInterval interval = mappingFrom.get(i); - if (interval != null && !isIllegal(interval.location())) { - boolean unique = usedRegs.add(interval.location()); - assert unique : "cannot read from same register twice"; - } - } - } - - usedRegs.clear(); - for (i = 0; i < mappingTo.size(); i++) { - TraceInterval interval = mappingTo.get(i); - if (isIllegal(interval.location())) { - // After insertion the location may become illegal, so don't check it since multiple - // intervals might be illegal. - continue; - } - boolean unique = usedRegs.add(interval.location()); - assert unique : "cannot write to same register twice"; - } - - verifyStackSlotMapping(); - - return true; - } - - protected void verifyStackSlotMapping() { - // relax disjoint stack maps invariant - } - - // mark assignedReg and assignedRegHi of the interval as blocked - private void blockRegisters(TraceInterval interval) { - Value location = interval.location(); - if (mightBeBlocked(location)) { - assert areMultipleReadsAllowed() || valueBlocked(location) == 0 : "location already marked as used: " + location; - int direction = 1; - setValueBlocked(location, direction); - Debug.log("block %s", location); - } - } - - // mark assignedReg and assignedRegHi of the interval as unblocked - private void unblockRegisters(TraceInterval interval) { - Value location = interval.location(); - if (mightBeBlocked(location)) { - assert valueBlocked(location) > 0 : "location already marked as unused: " + location; - setValueBlocked(location, -1); - Debug.log("unblock %s", location); - } - } - - /** - * Checks if the {@linkplain TraceInterval#location() location} of {@code to} is not blocked or - * is only blocked by {@code from}. - */ - private boolean safeToProcessMove(TraceInterval from, TraceInterval to) { - Value fromReg = from != null ? from.location() : null; - - Value location = to.location(); - if (mightBeBlocked(location)) { - if ((valueBlocked(location) > 1 || (valueBlocked(location) == 1 && !isMoveToSelf(fromReg, location)))) { - return false; - } - } - - return true; - } - - protected boolean isMoveToSelf(Value from, Value to) { - assert to != null; - if (to.equals(from)) { - return true; - } - if (from != null && isRegister(from) && isRegister(to) && asRegister(from).equals(asRegister(to))) { - assert LIRKind.verifyMoveKinds(to.getLIRKind(), from.getLIRKind()) : String.format("Same register but Kind mismatch %s <- %s", to, from); - return true; - } - return false; - } - - protected boolean mightBeBlocked(Value location) { - if (isRegister(location)) { - return true; - } - if (isStackSlotValue(location)) { - return true; - } - return false; - } - - private void createInsertionBuffer(List<LIRInstruction> list) { - assert !insertionBuffer.initialized() : "overwriting existing buffer"; - insertionBuffer.init(list); - } - - private void appendInsertionBuffer() { - if (insertionBuffer.initialized()) { - insertionBuffer.finish(); - } - assert !insertionBuffer.initialized() : "must be uninitialized now"; - - insertIdx = -1; - } - - private void insertMove(TraceInterval fromInterval, TraceInterval toInterval) { - assert !fromInterval.operand.equals(toInterval.operand) : "from and to interval equal: " + fromInterval; - assert LIRKind.verifyMoveKinds(toInterval.kind(), fromInterval.kind()) : "move between different types"; - assert insertIdx != -1 : "must setup insert position first"; - - insertionBuffer.append(insertIdx, createMove(fromInterval.operand, toInterval.operand, fromInterval.location(), toInterval.location())); - - if (Debug.isLogEnabled()) { - Debug.log("insert move from %s to %s at %d", fromInterval, toInterval, insertIdx); - } - } - - /** - * @param fromOpr {@link TraceInterval#operand operand} of the {@code from} interval - * @param toOpr {@link TraceInterval#operand operand} of the {@code to} interval - * @param fromLocation {@link TraceInterval#location() location} of the {@code to} interval - * @param toLocation {@link TraceInterval#location() location} of the {@code to} interval - */ - protected LIRInstruction createMove(AllocatableValue fromOpr, AllocatableValue toOpr, AllocatableValue fromLocation, AllocatableValue toLocation) { - if (isStackSlotValue(toLocation) && isStackSlotValue(fromLocation)) { - return getAllocator().getSpillMoveFactory().createStackMove(toOpr, fromOpr); - } - return getAllocator().getSpillMoveFactory().createMove(toOpr, fromOpr); - } - - private void insertMove(Constant fromOpr, TraceInterval toInterval) { - assert insertIdx != -1 : "must setup insert position first"; - - AllocatableValue toOpr = toInterval.operand; - LIRInstruction move = getAllocator().getSpillMoveFactory().createLoad(toOpr, fromOpr); - insertionBuffer.append(insertIdx, move); - - if (Debug.isLogEnabled()) { - Debug.log("insert move from value %s to %s at %d", fromOpr, toInterval, insertIdx); - } - } - - @SuppressWarnings("try") - private void resolveMappings() { - try (Indent indent = Debug.logAndIndent("resolveMapping")) { - assert verifyBeforeResolve(); - if (Debug.isLogEnabled()) { - printMapping(); - } - - // Block all registers that are used as input operands of a move. - // When a register is blocked, no move to this register is emitted. - // This is necessary for detecting cycles in moves. - int i; - for (i = mappingFrom.size() - 1; i >= 0; i--) { - TraceInterval fromInterval = mappingFrom.get(i); - if (fromInterval != null) { - blockRegisters(fromInterval); - } - } - - int spillCandidate = -1; - while (mappingFrom.size() > 0) { - boolean processedInterval = false; - - for (i = mappingFrom.size() - 1; i >= 0; i--) { - TraceInterval fromInterval = mappingFrom.get(i); - TraceInterval toInterval = mappingTo.get(i); - - if (safeToProcessMove(fromInterval, toInterval)) { - // this interval can be processed because target is free - if (fromInterval != null) { - insertMove(fromInterval, toInterval); - unblockRegisters(fromInterval); - } else { - insertMove(mappingFromOpr.get(i), toInterval); - } - mappingFrom.remove(i); - mappingFromOpr.remove(i); - mappingTo.remove(i); - - processedInterval = true; - } else if (fromInterval != null && isRegister(fromInterval.location())) { - // this interval cannot be processed now because target is not free - // it starts in a register, so it is a possible candidate for spilling - spillCandidate = i; - } - } - - if (!processedInterval) { - breakCycle(spillCandidate); - } - } - } - - // check that all intervals have been processed - assert checkEmpty(); - } - - protected void breakCycle(int spillCandidate) { - if (spillCandidate != -1) { - // no move could be processed because there is a cycle in the move list - // (e.g. r1 . r2, r2 . r1), so one interval must be spilled to memory - assert spillCandidate != -1 : "no interval in register for spilling found"; - - // create a new spill interval and assign a stack slot to it - TraceInterval fromInterval1 = mappingFrom.get(spillCandidate); - // do not allocate a new spill slot for temporary interval, but - // use spill slot assigned to fromInterval. Otherwise moves from - // one stack slot to another can happen (not allowed by LIRAssembler - AllocatableValue spillSlot1 = fromInterval1.spillSlot(); - if (spillSlot1 == null) { - spillSlot1 = getAllocator().getFrameMapBuilder().allocateSpillSlot(fromInterval1.kind()); - fromInterval1.setSpillSlot(spillSlot1); - } - spillInterval(spillCandidate, fromInterval1, spillSlot1); - return; - } - assert mappingFromSize() > 1; - // Arbitrarily select the first entry for spilling. - int stackSpillCandidate = 0; - TraceInterval fromInterval = getMappingFrom(stackSpillCandidate); - assert isStackSlotValue(fromInterval.location()); - // allocate new stack slot - VirtualStackSlot spillSlot = getAllocator().getFrameMapBuilder().allocateSpillSlot(fromInterval.kind()); - spillInterval(stackSpillCandidate, fromInterval, spillSlot); - } - - protected void spillInterval(int spillCandidate, TraceInterval fromInterval, AllocatableValue spillSlot) { - assert mappingFrom.get(spillCandidate).equals(fromInterval); - TraceInterval spillInterval = getAllocator().createDerivedInterval(fromInterval); - spillInterval.setKind(fromInterval.kind()); - - // add a dummy range because real position is difficult to calculate - // Note: this range is a special case when the integrity of the allocation is - // checked - spillInterval.addRange(1, 2); - - spillInterval.assignLocation(spillSlot); - - if (Debug.isLogEnabled()) { - Debug.log("created new Interval for spilling: %s", spillInterval); - } - blockRegisters(spillInterval); - - // insert a move from register to stack and update the mapping - insertMove(fromInterval, spillInterval); - mappingFrom.set(spillCandidate, spillInterval); - unblockRegisters(fromInterval); - } - - @SuppressWarnings("try") - private void printMapping() { - try (Indent indent = Debug.logAndIndent("Mapping")) { - for (int i = mappingFrom.size() - 1; i >= 0; i--) { - TraceInterval fromInterval = mappingFrom.get(i); - TraceInterval toInterval = mappingTo.get(i); - String from; - Value to = toInterval.location(); - if (fromInterval == null) { - from = mappingFromOpr.get(i).toString(); - } else { - from = fromInterval.location().toString(); - } - Debug.log("move %s <- %s", from, to); - } - } - } - - void setInsertPosition(List<LIRInstruction> insertList, int insertIdx) { - assert this.insertIdx == -1 : "use moveInsertPosition instead of setInsertPosition when data already set"; - - createInsertionBuffer(insertList); - this.insertIdx = insertIdx; - } - - void moveInsertPosition(List<LIRInstruction> newInsertList, int newInsertIdx) { - if (insertionBuffer.lirList() != null && (insertionBuffer.lirList() != newInsertList || this.insertIdx != newInsertIdx)) { - // insert position changed . resolve current mappings - resolveMappings(); - } - - assert insertionBuffer.lirList() != newInsertList || newInsertIdx >= insertIdx : String.format("Decreasing insert index: old=%d new=%d", insertIdx, newInsertIdx); - - if (insertionBuffer.lirList() != newInsertList) { - // block changed . append insertionBuffer because it is - // bound to a specific block and create a new insertionBuffer - appendInsertionBuffer(); - createInsertionBuffer(newInsertList); - } - - this.insertIdx = newInsertIdx; - } - - public void addMapping(TraceInterval fromInterval, TraceInterval toInterval) { - - if (isIllegal(toInterval.location()) && toInterval.canMaterialize()) { - if (Debug.isLogEnabled()) { - Debug.log("no store to rematerializable interval %s needed", toInterval); - } - return; - } - if (isIllegal(fromInterval.location()) && fromInterval.canMaterialize()) { - // Instead of a reload, re-materialize the value - JavaConstant rematValue = fromInterval.getMaterializedValue(); - addMapping(rematValue, toInterval); - return; - } - if (Debug.isLogEnabled()) { - Debug.log("add move mapping from %s to %s", fromInterval, toInterval); - } - - assert !fromInterval.operand.equals(toInterval.operand) : "from and to interval equal: " + fromInterval; - assert LIRKind.verifyMoveKinds(toInterval.kind(), fromInterval.kind()) : String.format("Kind mismatch: %s vs. %s, from=%s, to=%s", fromInterval.kind(), toInterval.kind(), fromInterval, - toInterval); - mappingFrom.add(fromInterval); - mappingFromOpr.add(null); - mappingTo.add(toInterval); - } - - public void addMapping(Constant fromOpr, TraceInterval toInterval) { - if (Debug.isLogEnabled()) { - Debug.log("add move mapping from %s to %s", fromOpr, toInterval); - } - - mappingFrom.add(null); - mappingFromOpr.add(fromOpr); - mappingTo.add(toInterval); - } - - void resolveAndAppendMoves() { - if (hasMappings()) { - resolveMappings(); - } - appendInsertionBuffer(); - } -}
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/TraceRegisterAllocationPhase.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/TraceRegisterAllocationPhase.java Tue Dec 08 12:30:15 2015 -0800 @@ -22,8 +22,6 @@ */ package com.oracle.graal.lir.alloc.trace; -import static com.oracle.graal.lir.LIRValueUtil.isStackSlotValue; - import java.util.List; import jdk.vm.ci.code.TargetDescription; @@ -44,8 +42,8 @@ import com.oracle.graal.lir.LIRInstruction; import com.oracle.graal.lir.StandardOp.JumpOp; import com.oracle.graal.lir.StandardOp.LabelOp; -import com.oracle.graal.lir.StandardOp.ValueMoveOp; import com.oracle.graal.lir.alloc.trace.TraceAllocationPhase.TraceAllocationContext; +import com.oracle.graal.lir.alloc.trace.lsra.TraceLinearScan; import com.oracle.graal.lir.gen.LIRGenerationResult; import com.oracle.graal.lir.gen.LIRGeneratorTool.MoveFactory; import com.oracle.graal.lir.phases.AllocationPhase; @@ -68,14 +66,18 @@ // @formatter:on } - static final int TRACE_DUMP_LEVEL = 3; + private static final TraceGlobalMoveResolutionPhase TRACE_GLOBAL_MOVE_RESOLUTION_PHASE = new TraceGlobalMoveResolutionPhase(); + private static final TraceTrivialAllocator TRACE_TRIVIAL_ALLOCATOR = new TraceTrivialAllocator(); + + public static final int TRACE_DUMP_LEVEL = 3; private static final DebugMetric trivialTracesMetric = Debug.metric("TraceRA[trivialTraces]"); private static final DebugMetric tracesMetric = Debug.metric("TraceRA[traces]"); @Override @SuppressWarnings("try") - protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, MoveFactory spillMoveFactory, - RegisterAllocationConfig registerAllocationConfig) { + protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, AllocationContext context) { + MoveFactory spillMoveFactory = context.spillMoveFactory; + RegisterAllocationConfig registerAllocationConfig = context.registerAllocationConfig; LIR lir = lirGenRes.getLIR(); assert SSIVerifier.verify(lir) : "LIR not in SSI form."; B startBlock = linearScanOrder.get(0); @@ -83,6 +85,8 @@ TraceBuilderResult<B> resultTraces = TraceBuilder.computeTraces(startBlock, linearScanOrder); TraceStatisticsPrinter.printTraceStatistics(resultTraces, lirGenRes.getCompilationUnitName()); + TraceAllocationContext traceContext = new TraceAllocationContext(spillMoveFactory, registerAllocationConfig, resultTraces); + Debug.dump(lir, "Before TraceRegisterAllocation"); int traceNumber = 0; for (List<B> trace : resultTraces.getTraces()) { @@ -93,7 +97,7 @@ } Debug.dump(TRACE_DUMP_LEVEL, trace, "Trace" + traceNumber + ": " + trace); if (Options.TraceRAtrivialBlockAllocator.getValue() && isTrivialTrace(lir, trace)) { - new TraceTrivialAllocator(resultTraces).apply(target, lirGenRes, codeEmittingOrder, trace, new TraceAllocationContext(spillMoveFactory, registerAllocationConfig), false); + TRACE_TRIVIAL_ALLOCATOR.apply(target, lirGenRes, codeEmittingOrder, trace, traceContext, false); } else { TraceLinearScan allocator = new TraceLinearScan(target, lirGenRes, spillMoveFactory, registerAllocationConfig, trace, resultTraces, false); allocator.allocate(target, lirGenRes, codeEmittingOrder, linearScanOrder, spillMoveFactory, registerAllocationConfig); @@ -107,26 +111,26 @@ } Debug.dump(lir, "After trace allocation"); - new TraceGlobalMoveResolutionPhase(resultTraces).apply(target, lirGenRes, codeEmittingOrder, linearScanOrder, new TraceAllocationContext(spillMoveFactory, registerAllocationConfig)); + TRACE_GLOBAL_MOVE_RESOLUTION_PHASE.apply(target, lirGenRes, codeEmittingOrder, linearScanOrder, traceContext); + deconstructSSIForm(lir); + } - try (Scope s = Debug.scope("TraceRegisterAllocationFixup")) { - if (replaceStackToStackMoves(lir, spillMoveFactory)) { - Debug.dump(lir, "After fixing stack to stack moves"); - } - /* - * Incoming Values are needed for the RegisterVerifier, otherwise SIGMAs/PHIs where the - * Out and In value matches (ie. there is no resolution move) are falsely detected as - * errors. - */ - for (AbstractBlockBase<?> block : lir.getControlFlowGraph().getBlocks()) { - try (Indent i = Debug.logAndIndent("Fixup Block %s", block)) { - if (block.getPredecessorCount() != 0) { - SSIUtil.removeIncoming(lir, block); - } else { - assert lir.getControlFlowGraph().getStartBlock().equals(block); - } - SSIUtil.removeOutgoing(lir, block); + /** + * Remove Phi/Sigma In/Out. + * + * Note: Incoming Values are needed for the RegisterVerifier, otherwise SIGMAs/PHIs where the + * Out and In value matches (ie. there is no resolution move) are falsely detected as errors. + */ + @SuppressWarnings("try") + private static void deconstructSSIForm(LIR lir) { + for (AbstractBlockBase<?> block : lir.getControlFlowGraph().getBlocks()) { + try (Indent i = Debug.logAndIndent("Fixup Block %s", block)) { + if (block.getPredecessorCount() != 0) { + SSIUtil.removeIncoming(lir, block); + } else { + assert lir.getControlFlowGraph().getStartBlock().equals(block); } + SSIUtil.removeOutgoing(lir, block); } } } @@ -147,30 +151,6 @@ return instructions.get(1) instanceof JumpOp; } - /** - * Fixup stack to stack moves introduced by stack arguments. - * - * TODO (je) find a better solution. - */ - private static boolean replaceStackToStackMoves(LIR lir, MoveFactory spillMoveFactory) { - boolean changed = false; - for (AbstractBlockBase<?> block : lir.getControlFlowGraph().getBlocks()) { - List<LIRInstruction> instructions = lir.getLIRforBlock(block); - for (int i = 0; i < instructions.size(); i++) { - LIRInstruction inst = instructions.get(i); - - if (inst instanceof ValueMoveOp) { - ValueMoveOp move = (ValueMoveOp) inst; - if (isStackSlotValue(move.getInput()) && isStackSlotValue(move.getResult())) { - instructions.set(i, spillMoveFactory.createStackMove(move.getResult(), move.getInput())); - changed = true; - } - } - } - } - return changed; - } - private static void unnumberInstructions(List<? extends AbstractBlockBase<?>> trace, LIR lir) { trace.stream().flatMap(b -> lir.getLIRforBlock(b).stream()).forEach(op -> op.setId(-1)); }
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/TraceTrivialAllocator.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/TraceTrivialAllocator.java Tue Dec 08 12:30:15 2015 -0800 @@ -31,7 +31,6 @@ import jdk.vm.ci.code.TargetDescription; import jdk.vm.ci.meta.Value; -import com.oracle.graal.compiler.common.alloc.RegisterAllocationConfig; import com.oracle.graal.compiler.common.alloc.TraceBuilder.TraceBuilderResult; import com.oracle.graal.compiler.common.cfg.AbstractBlockBase; import com.oracle.graal.lir.LIR; @@ -42,7 +41,6 @@ import com.oracle.graal.lir.ValueProcedure; import com.oracle.graal.lir.Variable; import com.oracle.graal.lir.gen.LIRGenerationResult; -import com.oracle.graal.lir.gen.LIRGeneratorTool.MoveFactory; import com.oracle.graal.lir.ssi.SSIUtil; import com.oracle.graal.lir.util.VariableVirtualStackValueMap; @@ -52,16 +50,10 @@ */ final class TraceTrivialAllocator extends TraceAllocationPhase { - private final TraceBuilderResult<?> resultTraces; - - public TraceTrivialAllocator(TraceBuilderResult<?> resultTraces) { - this.resultTraces = resultTraces; - } - @Override - protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> trace, MoveFactory spillMoveFactory, - RegisterAllocationConfig registerAllocationConfig) { + protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> trace, TraceAllocationContext context) { LIR lir = lirGenRes.getLIR(); + TraceBuilderResult<?> resultTraces = context.resultTraces; assert isTrivialTrace(lir, trace) : "Not a trivial trace! " + trace; B block = trace.iterator().next();
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/TraceUtil.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/TraceUtil.java Tue Dec 08 12:30:15 2015 -0800 @@ -27,9 +27,9 @@ import com.oracle.graal.compiler.common.alloc.TraceBuilder.TraceBuilderResult; import com.oracle.graal.compiler.common.cfg.AbstractBlockBase; -class TraceUtil { +public class TraceUtil { - static AbstractBlockBase<?> getBestTraceInterPredecessor(TraceBuilderResult<?> traceResult, AbstractBlockBase<?> block) { + public static AbstractBlockBase<?> getBestTraceInterPredecessor(TraceBuilderResult<?> traceResult, AbstractBlockBase<?> block) { AbstractBlockBase<?> bestPred = null; int bestTraceId = traceResult.getTraceForBlock(block); for (AbstractBlockBase<?> pred : block.getPredecessors()) { @@ -42,12 +42,12 @@ return bestPred; } - static boolean isShadowedRegisterValue(Value value) { + public static boolean isShadowedRegisterValue(Value value) { assert value != null; return value instanceof ShadowedRegisterValue; } - static ShadowedRegisterValue asShadowedRegisterValue(Value value) { + public static ShadowedRegisterValue asShadowedRegisterValue(Value value) { assert isShadowedRegisterValue(value); return (ShadowedRegisterValue) value; }
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/UsePosList.java Tue Dec 08 12:29:24 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,125 +0,0 @@ -/* - * Copyright (c) 2015, 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.graal.lir.alloc.trace; - -import com.oracle.graal.compiler.common.util.IntList; -import com.oracle.graal.lir.alloc.trace.TraceInterval.RegisterPriority; - -/** - * List of use positions. Each entry in the list records the use position and register priority - * associated with the use position. The entries in the list are in descending order of use - * position. - * - */ -public final class UsePosList { - - private IntList list; - - /** - * Creates a use list. - * - * @param initialCapacity the initial capacity of the list in terms of entries - */ - public UsePosList(int initialCapacity) { - list = new IntList(initialCapacity * 2); - } - - private UsePosList(IntList list) { - this.list = list; - } - - /** - * Splits this list around a given position. All entries in this list with a use position - * greater or equal than {@code splitPos} are removed from this list and added to the returned - * list. - * - * @param splitPos the position for the split - * @return a use position list containing all entries removed from this list that have a use - * position greater or equal than {@code splitPos} - */ - public UsePosList splitAt(int splitPos) { - int i = size() - 1; - int len = 0; - while (i >= 0 && usePos(i) < splitPos) { - --i; - len += 2; - } - int listSplitIndex = (i + 1) * 2; - IntList childList = list; - list = IntList.copy(this.list, listSplitIndex, len); - childList.setSize(listSplitIndex); - UsePosList child = new UsePosList(childList); - return child; - } - - /** - * Gets the use position at a specified index in this list. - * - * @param index the index of the entry for which the use position is returned - * @return the use position of entry {@code index} in this list - */ - public int usePos(int index) { - return list.get(index << 1); - } - - /** - * Gets the register priority for the use position at a specified index in this list. - * - * @param index the index of the entry for which the register priority is returned - * @return the register priority of entry {@code index} in this list - */ - public RegisterPriority registerPriority(int index) { - return RegisterPriority.VALUES[list.get((index << 1) + 1)]; - } - - public void add(int usePos, RegisterPriority registerPriority) { - assert list.size() == 0 || usePos(size() - 1) > usePos; - list.add(usePos); - list.add(registerPriority.ordinal()); - } - - public int size() { - return list.size() >> 1; - } - - public void removeLowestUsePos() { - list.setSize(list.size() - 2); - } - - public void setRegisterPriority(int index, RegisterPriority registerPriority) { - list.set((index << 1) + 1, registerPriority.ordinal()); - } - - @Override - public String toString() { - StringBuilder buf = new StringBuilder("["); - for (int i = size() - 1; i >= 0; --i) { - if (buf.length() != 1) { - buf.append(", "); - } - RegisterPriority prio = registerPriority(i); - buf.append(usePos(i)).append(" -> ").append(prio.ordinal()).append(':').append(prio); - } - return buf.append("]").toString(); - } -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/lsra/FixedInterval.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,310 @@ +/* + * Copyright (c) 2015, 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.graal.lir.alloc.trace.lsra; + +import static jdk.vm.ci.code.ValueUtil.asRegister; +import static jdk.vm.ci.code.ValueUtil.isRegister; +import jdk.vm.ci.meta.AllocatableValue; +import jdk.vm.ci.meta.Value; + +import com.oracle.graal.lir.LIRInstruction; + +/** + * Represents a fixed interval. + */ +final class FixedInterval extends IntervalHint { + + static final class FixedList { + + public FixedInterval fixed; + + public FixedList(FixedInterval fixed) { + this.fixed = fixed; + } + + /** + * Gets the fixed list. + */ + public FixedInterval getFixed() { + return fixed; + } + + /** + * Sets the fixed list. + */ + public void setFixed(FixedInterval list) { + fixed = list; + } + + /** + * Adds an interval to a list sorted by {@linkplain FixedInterval#currentFrom() current + * from} positions. + * + * @param interval the interval to add + */ + public void addToListSortedByCurrentFromPositions(FixedInterval interval) { + FixedInterval list = getFixed(); + FixedInterval prev = null; + FixedInterval cur = list; + while (cur.currentFrom() < interval.currentFrom()) { + prev = cur; + cur = cur.next; + } + FixedInterval result = list; + if (prev == null) { + // add to head of list + result = interval; + } else { + // add before 'cur' + prev.next = interval; + } + interval.next = cur; + setFixed(result); + } + + } + + /** + * The fixed operand of this interval. + */ + public final AllocatableValue operand; + + /** + * The head of the list of ranges describing this interval. This list is sorted by + * {@linkplain LIRInstruction#id instruction ids}. + */ + private FixedRange first; + + /** + * Iterator used to traverse the ranges of an interval. + */ + private FixedRange current; + + /** + * Link to next interval in a sorted list of intervals that ends with {@link #EndMarker}. + */ + FixedInterval next; + + private int cachedTo; // cached value: to of last range (-1: not cached) + + public FixedRange first() { + return first; + } + + @Override + public int from() { + return first.from; + } + + public int to() { + if (cachedTo == -1) { + cachedTo = calcTo(); + } + assert cachedTo == calcTo() : "invalid cached value"; + return cachedTo; + } + + // test intersection + boolean intersects(TraceInterval i) { + return first.intersects(i); + } + + int intersectsAt(TraceInterval i) { + return first.intersectsAt(i); + } + + // range iteration + void rewindRange() { + current = first; + } + + void nextRange() { + assert this != EndMarker : "not allowed on sentinel"; + current = current.next; + } + + int currentFrom() { + return current.from; + } + + int currentTo() { + return current.to; + } + + boolean currentAtEnd() { + return current == FixedRange.EndMarker; + } + + boolean currentIntersects(TraceInterval it) { + return current.intersects(it); + } + + int currentIntersectsAt(TraceInterval it) { + return current.intersectsAt(it); + } + + // range creation + public void setFrom(int from) { + assert !isEmpty(); + first().from = from; + } + + private boolean isEmpty() { + return first() == FixedRange.EndMarker; + } + + public void addRange(int from, int to) { + if (isEmpty()) { + first = new FixedRange(from, to, first()); + return; + } + if (to <= to() && from >= from()) { + return; + } + if (from() == to) { + first().from = from; + } else { + first = new FixedRange(from, to, first()); + } + } + + @Override + public AllocatableValue location() { + return operand; + } + + /** + * Sentinel interval to denote the end of an interval list. + */ + static final FixedInterval EndMarker = new FixedInterval(Value.ILLEGAL); + + FixedInterval(AllocatableValue operand) { + assert operand != null; + this.operand = operand; + this.first = FixedRange.EndMarker; + this.current = FixedRange.EndMarker; + this.next = FixedInterval.EndMarker; + this.cachedTo = -1; + } + + int calcTo() { + assert first != FixedRange.EndMarker : "interval has no range"; + + FixedRange r = first; + while (r.next != FixedRange.EndMarker) { + r = r.next; + } + return r.to; + } + + // returns true if the opId is inside the interval + boolean covers(int opId, LIRInstruction.OperandMode mode) { + FixedRange cur = first; + + while (cur != FixedRange.EndMarker && cur.to < opId) { + cur = cur.next; + } + if (cur != FixedRange.EndMarker) { + assert cur.to != cur.next.from : "ranges not separated"; + + if (mode == LIRInstruction.OperandMode.DEF) { + return cur.from <= opId && opId < cur.to; + } else { + return cur.from <= opId && opId <= cur.to; + } + } + return false; + } + + // returns true if the interval has any hole between holeFrom and holeTo + // (even if the hole has only the length 1) + boolean hasHoleBetween(int holeFrom, int holeTo) { + assert holeFrom < holeTo : "check"; + assert from() <= holeFrom && holeTo <= to() : "index out of interval"; + + FixedRange cur = first; + while (cur != FixedRange.EndMarker) { + assert cur.to < cur.next.from : "no space between ranges"; + + // hole-range starts before this range . hole + if (holeFrom < cur.from) { + return true; + + // hole-range completely inside this range . no hole + } else { + if (holeTo <= cur.to) { + return false; + + // overlapping of hole-range with this range . hole + } else { + if (holeFrom <= cur.to) { + return true; + } + } + } + + cur = cur.next; + } + + return false; + } + + @Override + public String toString() { + String from = "?"; + String to = "?"; + if (first != null && first != FixedRange.EndMarker) { + from = String.valueOf(from()); + // to() may cache a computed value, modifying the current object, which is a bad idea + // for a printing function. Compute it directly instead. + to = String.valueOf(calcTo()); + } + String locationString = "@" + this.operand; + return asRegister(operand).number + ":" + operand + (isRegister(operand) ? "" : locationString) + "[" + from + "," + to + "]"; + } + + /** + * Gets a single line string for logging the details of this interval to a log stream. + */ + @Override + public String logString(TraceLinearScan allocator) { + StringBuilder buf = new StringBuilder(100); + buf.append("fix ").append(asRegister(operand).number).append(':').append(operand).append(' '); + + buf.append(" ranges{"); + + // print ranges + FixedRange cur = first; + while (cur != FixedRange.EndMarker) { + if (cur != first) { + buf.append(", "); + } + buf.append(cur); + cur = cur.next; + assert cur != null : "range list not closed with range sentinel"; + } + buf.append("}"); + return buf.toString(); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/lsra/FixedRange.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2015, 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.graal.lir.alloc.trace.lsra; + +/** + * Represents a range of integers from a start (inclusive) to an end (exclusive). + */ +final class FixedRange { + + public static final FixedRange EndMarker = new FixedRange(Integer.MAX_VALUE, Integer.MAX_VALUE, null); + + /** + * The start of the range, inclusive. + */ + public int from; + + /** + * The end of the range, exclusive. + */ + public int to; + + /** + * A link to allow the range to be put into a singly linked list. + */ + public FixedRange next; + + boolean intersects(TraceInterval i) { + return intersectsAt(i) != -1; + } + + /** + * Creates a new range. + * + * @param from the start of the range, inclusive + * @param to the end of the range, exclusive + * @param next link to the next range in a linked list + */ + FixedRange(int from, int to, FixedRange next) { + this.from = from; + this.to = to; + this.next = next; + } + + int intersectsAt(TraceInterval other) { + FixedRange range = this; + assert other != null : "null ranges not allowed"; + assert range != EndMarker && other != TraceInterval.EndMarker : "empty ranges not allowed"; + int intervalFrom = other.from(); + int intervalTo = other.to(); + + do { + if (range.from < intervalFrom) { + if (range.to <= intervalFrom) { + range = range.next; + if (range == EndMarker) { + return -1; + } + } else { + return intervalFrom; + } + } else { + if (intervalFrom < range.from) { + if (intervalTo <= range.from) { + return -1; + } + return range.from; + } else { + assert range.from == intervalFrom; + if (range.from == range.to) { + range = range.next; + if (range == EndMarker) { + return -1; + } + } else { + if (intervalFrom == intervalTo) { + return -1; + } + return range.from; + } + } + } + } while (true); + } + + @Override + public String toString() { + return "[" + from + ", " + to + "]"; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/lsra/IntervalHint.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2015, 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.graal.lir.alloc.trace.lsra; + +import jdk.vm.ci.meta.AllocatableValue; + +/** + * An interval that is a hint for an {@code TraceInterval interval}. + */ +abstract class IntervalHint { + + public abstract AllocatableValue location(); + + public abstract int from(); + + public abstract String logString(TraceLinearScan allocator); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/lsra/RegisterVerifier.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,264 @@ +/* + * 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.lir.alloc.trace.lsra; + +import static jdk.vm.ci.code.ValueUtil.asRegister; +import static jdk.vm.ci.code.ValueUtil.isRegister; + +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; + +import jdk.vm.ci.code.Register; +import jdk.vm.ci.common.JVMCIError; +import jdk.vm.ci.meta.Value; + +import com.oracle.graal.compiler.common.cfg.AbstractBlockBase; +import com.oracle.graal.compiler.common.util.ArrayMap; +import com.oracle.graal.debug.Debug; +import com.oracle.graal.debug.Debug.Scope; +import com.oracle.graal.debug.Indent; +import com.oracle.graal.lir.InstructionValueConsumer; +import com.oracle.graal.lir.LIRInstruction; +import com.oracle.graal.lir.LIRInstruction.OperandFlag; +import com.oracle.graal.lir.LIRInstruction.OperandMode; + +/** + */ +final class RegisterVerifier { + + TraceLinearScan allocator; + List<AbstractBlockBase<?>> workList; // all blocks that must be processed + ArrayMap<TraceInterval[]> savedStates; // saved information of previous check + + // simplified access to methods of LinearScan + TraceInterval intervalAt(Value operand) { + return allocator.intervalFor(operand); + } + + // currently, only registers are processed + int stateSize() { + return allocator.numRegisters(); + } + + // accessors + TraceInterval[] stateForBlock(AbstractBlockBase<?> block) { + return savedStates.get(block.getId()); + } + + void setStateForBlock(AbstractBlockBase<?> block, TraceInterval[] savedState) { + savedStates.put(block.getId(), savedState); + } + + void addToWorkList(AbstractBlockBase<?> block) { + if (!workList.contains(block)) { + workList.add(block); + } + } + + RegisterVerifier(TraceLinearScan allocator) { + this.allocator = allocator; + workList = new ArrayList<>(16); + this.savedStates = new ArrayMap<>(); + + } + + @SuppressWarnings("try") + void verify(AbstractBlockBase<?> start) { + try (Scope s = Debug.scope("RegisterVerifier")) { + // setup input registers (method arguments) for first block + TraceInterval[] inputState = new TraceInterval[stateSize()]; + setStateForBlock(start, inputState); + addToWorkList(start); + + // main loop for verification + do { + AbstractBlockBase<?> block = workList.get(0); + workList.remove(0); + + processBlock(block); + } while (!workList.isEmpty()); + } + } + + @SuppressWarnings("try") + private void processBlock(AbstractBlockBase<?> block) { + try (Indent indent = Debug.logAndIndent("processBlock B%d", block.getId())) { + // must copy state because it is modified + TraceInterval[] inputState = copy(stateForBlock(block)); + + try (Indent indent2 = Debug.logAndIndent("Input-State of intervals:")) { + printState(inputState); + } + + // process all operations of the block + processOperations(block, inputState); + + try (Indent indent2 = Debug.logAndIndent("Output-State of intervals:")) { + printState(inputState); + } + + // iterate all successors + for (AbstractBlockBase<?> succ : block.getSuccessors()) { + processSuccessor(succ, inputState); + } + } + } + + protected void printState(TraceInterval[] inputState) { + for (int i = 0; i < stateSize(); i++) { + Register reg = allocator.getRegisters()[i]; + assert reg.number == i; + if (inputState[i] != null) { + Debug.log(" %6s %4d -- %s", reg, inputState[i].operandNumber, inputState[i]); + } else { + Debug.log(" %6s __", reg); + } + } + } + + private void processSuccessor(AbstractBlockBase<?> block, TraceInterval[] inputState) { + TraceInterval[] savedState = stateForBlock(block); + + if (savedState != null) { + // this block was already processed before. + // check if new inputState is consistent with savedState + + boolean savedStateCorrect = true; + for (int i = 0; i < stateSize(); i++) { + if (inputState[i] != savedState[i]) { + // current inputState and previous savedState assume a different + // interval in this register . assume that this register is invalid + if (savedState[i] != null) { + // invalidate old calculation only if it assumed that + // register was valid. when the register was already invalid, + // then the old calculation was correct. + savedStateCorrect = false; + savedState[i] = null; + + Debug.log("processSuccessor B%d: invalidating slot %d", block.getId(), i); + } + } + } + + if (savedStateCorrect) { + // already processed block with correct inputState + Debug.log("processSuccessor B%d: previous visit already correct", block.getId()); + } else { + // must re-visit this block + Debug.log("processSuccessor B%d: must re-visit because input state changed", block.getId()); + addToWorkList(block); + } + + } else { + // block was not processed before, so set initial inputState + Debug.log("processSuccessor B%d: initial visit", block.getId()); + + setStateForBlock(block, copy(inputState)); + addToWorkList(block); + } + } + + static TraceInterval[] copy(TraceInterval[] inputState) { + return inputState.clone(); + } + + static void statePut(TraceInterval[] inputState, Value location, TraceInterval interval) { + if (location != null && isRegister(location)) { + Register reg = asRegister(location); + int regNum = reg.number; + if (interval != null) { + Debug.log("%s = %s", reg, interval.operand); + } else if (inputState[regNum] != null) { + Debug.log("%s = null", reg); + } + + inputState[regNum] = interval; + } + } + + static boolean checkState(AbstractBlockBase<?> block, LIRInstruction op, TraceInterval[] inputState, Value operand, Value reg, TraceInterval interval) { + if (reg != null && isRegister(reg)) { + if (inputState[asRegister(reg).number] != interval) { + throw new JVMCIError( + "Error in register allocation: operation (%s) in block %s expected register %s (operand %s) to contain the value of interval %s but data-flow says it contains interval %s", + op, block, reg, operand, interval, inputState[asRegister(reg).number]); + } + } + return true; + } + + void processOperations(AbstractBlockBase<?> block, final TraceInterval[] inputState) { + List<LIRInstruction> ops = allocator.getLIR().getLIRforBlock(block); + InstructionValueConsumer useConsumer = new InstructionValueConsumer() { + + @Override + public void visitValue(LIRInstruction op, Value operand, OperandMode mode, EnumSet<OperandFlag> flags) { + // we skip spill moves inserted by the spill position optimization + if (TraceLinearScan.isVariableOrRegister(operand) && allocator.isProcessed(operand) && op.id() != TraceLinearScan.DOMINATOR_SPILL_MOVE_ID) { + TraceInterval interval = intervalAt(operand); + if (op.id() != -1) { + interval = interval.getSplitChildAtOpId(op.id(), mode, allocator); + } + + assert checkState(block, op, inputState, interval.operand, interval.location(), interval.splitParent()); + } + } + }; + + InstructionValueConsumer defConsumer = (op, operand, mode, flags) -> { + if (TraceLinearScan.isVariableOrRegister(operand) && allocator.isProcessed(operand)) { + TraceInterval interval = intervalAt(operand); + if (op.id() != -1) { + interval = interval.getSplitChildAtOpId(op.id(), mode, allocator); + } + + statePut(inputState, interval.location(), interval.splitParent()); + } + }; + + // visit all instructions of the block + for (int i = 0; i < ops.size(); i++) { + final LIRInstruction op = ops.get(i); + + if (Debug.isLogEnabled()) { + Debug.log("%s", op.toStringWithIdPrefix()); + } + + // check if input operands are correct + op.visitEachInput(useConsumer); + // invalidate all caller save registers at calls + if (op.destroysCallerSavedRegisters()) { + for (Register r : allocator.getRegisterAllocationConfig().getRegisterConfig().getCallerSaveRegisters()) { + statePut(inputState, r.asValue(), null); + } + } + op.visitEachAlive(useConsumer); + // set temp operands (some operations use temp operands also as output operands, so + // can't set them null) + op.visitEachTemp(defConsumer); + // set output operands + op.visitEachOutput(defConsumer); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/lsra/TraceInterval.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,1091 @@ +/* + * Copyright (c) 2009, 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.graal.lir.alloc.trace.lsra; + +import static com.oracle.graal.compiler.common.GraalOptions.DetailedAsserts; +import static com.oracle.graal.lir.LIRValueUtil.isStackSlotValue; +import static com.oracle.graal.lir.LIRValueUtil.isVariable; +import static com.oracle.graal.lir.LIRValueUtil.isVirtualStackSlot; +import static jdk.vm.ci.code.ValueUtil.asRegister; +import static jdk.vm.ci.code.ValueUtil.isIllegal; +import static jdk.vm.ci.code.ValueUtil.isRegister; +import static jdk.vm.ci.code.ValueUtil.isStackSlot; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; + +import jdk.vm.ci.code.BailoutException; +import jdk.vm.ci.code.RegisterValue; +import jdk.vm.ci.code.StackSlot; +import jdk.vm.ci.common.JVMCIError; +import jdk.vm.ci.meta.AllocatableValue; +import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.LIRKind; +import jdk.vm.ci.meta.Value; + +import com.oracle.graal.compiler.common.util.Util; +import com.oracle.graal.debug.TTY; +import com.oracle.graal.lir.LIRInstruction; +import com.oracle.graal.lir.Variable; + +/** + * Represents an interval in the {@linkplain TraceLinearScan linear scan register allocator}. + */ +final class TraceInterval extends IntervalHint { + + static final class AnyList { + + /** + * List of intervals whose binding is currently {@link RegisterBinding#Any}. + */ + public TraceInterval any; + + public AnyList(TraceInterval any) { + this.any = any; + } + + /** + * Gets the any list. + */ + public TraceInterval getAny() { + return any; + } + + /** + * Sets the any list. + */ + public void setAny(TraceInterval list) { + any = list; + } + + /** + * Adds an interval to a list sorted by {@linkplain TraceInterval#from() current from} + * positions. + * + * @param interval the interval to add + */ + public void addToListSortedByFromPositions(TraceInterval interval) { + TraceInterval list = getAny(); + TraceInterval prev = null; + TraceInterval cur = list; + while (cur.from() < interval.from()) { + prev = cur; + cur = cur.next; + } + TraceInterval result = list; + if (prev == null) { + // add to head of list + result = interval; + } else { + // add before 'cur' + prev.next = interval; + } + interval.next = cur; + setAny(result); + } + + /** + * Adds an interval to a list sorted by {@linkplain TraceInterval#from() start} positions + * and {@linkplain TraceInterval#firstUsage(RegisterPriority) first usage} positions. + * + * @param interval the interval to add + */ + public void addToListSortedByStartAndUsePositions(TraceInterval interval) { + TraceInterval list = getAny(); + TraceInterval prev = null; + TraceInterval cur = list; + while (cur.from() < interval.from() || (cur.from() == interval.from() && cur.firstUsage(RegisterPriority.None) < interval.firstUsage(RegisterPriority.None))) { + prev = cur; + cur = cur.next; + } + if (prev == null) { + list = interval; + } else { + prev.next = interval; + } + interval.next = cur; + setAny(list); + } + + /** + * Removes an interval from a list. + * + * @param i the interval to remove + */ + public void removeAny(TraceInterval i) { + TraceInterval list = getAny(); + TraceInterval prev = null; + TraceInterval cur = list; + while (cur != i) { + assert cur != null && cur != TraceInterval.EndMarker : "interval has not been found in list: " + i; + prev = cur; + cur = cur.next; + } + if (prev == null) { + setAny(cur.next); + } else { + prev.next = cur.next; + } + } + } + + /** + * Constants denoting the register usage priority for an interval. The constants are declared in + * increasing order of priority are are used to optimize spilling when multiple overlapping + * intervals compete for limited registers. + */ + public enum RegisterPriority { + /** + * No special reason for an interval to be allocated a register. + */ + None, + + /** + * Priority level for intervals live at the end of a loop. + */ + LiveAtLoopEnd, + + /** + * Priority level for intervals that should be allocated to a register. + */ + ShouldHaveRegister, + + /** + * Priority level for intervals that must be allocated to a register. + */ + MustHaveRegister; + + public static final RegisterPriority[] VALUES = values(); + + /** + * Determines if this priority is higher than or equal to a given priority. + */ + public boolean greaterEqual(RegisterPriority other) { + return ordinal() >= other.ordinal(); + } + + /** + * Determines if this priority is lower than a given priority. + */ + public boolean lessThan(RegisterPriority other) { + return ordinal() < other.ordinal(); + } + + public CharSequence shortName() { + return name().subSequence(0, 1); + } + } + + /** + * Constants denoting whether an interval is bound to a specific register. This models platform + * dependencies on register usage for certain instructions. + */ + enum RegisterBinding { + /** + * Interval is bound to a specific register as required by the platform. + */ + Fixed, + + /** + * Interval has no specific register requirements. + */ + Any, + + /** + * Interval is bound to a stack slot. + */ + Stack; + + public static final RegisterBinding[] VALUES = values(); + } + + /** + * Constants denoting the linear-scan states an interval may be in with respect to the + * {@linkplain TraceInterval#from() start} {@code position} of the interval being processed. + */ + enum State { + /** + * An interval that starts after {@code position}. + */ + Unhandled, + + /** + * An interval that {@linkplain TraceInterval#covers covers} {@code position} and has an + * assigned register. + */ + Active, + + /** + * An interval that starts before and ends after {@code position} but does not + * {@linkplain TraceInterval#covers cover} it due to a lifetime hole. + */ + Inactive, + + /** + * An interval that ends before {@code position} or is spilled to memory. + */ + Handled; + } + + /** + * Constants used in optimization of spilling of an interval. + */ + public enum SpillState { + /** + * Starting state of calculation: no definition found yet. + */ + NoDefinitionFound, + + /** + * One definition has already been found. Two consecutive definitions are treated as one + * (e.g. a consecutive move and add because of two-operand LIR form). The position of this + * definition is given by {@link TraceInterval#spillDefinitionPos()}. + */ + NoSpillStore, + + /** + * A spill move has already been inserted. + */ + SpillStore, + + /** + * The interval starts in memory (e.g. method parameter), so a store is never necessary. + */ + StartInMemory, + + /** + * The interval has more than one definition (e.g. resulting from phi moves), so stores to + * memory are not optimized. + */ + NoOptimization; + + public static final EnumSet<SpillState> IN_MEMORY = EnumSet.of(SpillStore, StartInMemory); + } + + /** + * The {@linkplain RegisterValue register} or {@linkplain Variable variable} for this interval + * prior to register allocation. + */ + public final AllocatableValue operand; + + /** + * The operand number for this interval's {@linkplain #operand operand}. + */ + public final int operandNumber; + + /** + * The {@linkplain RegisterValue register} or {@linkplain StackSlot spill slot} assigned to this + * interval. In case of a spilled interval which is re-materialized this is + * {@link Value#ILLEGAL}. + */ + private AllocatableValue location; + + /** + * The stack slot to which all splits of this interval are spilled if necessary. + */ + private AllocatableValue spillSlot; + + /** + * The kind of this interval. + */ + private LIRKind kind; + + /** + * The start of the range, inclusive. + */ + public int intFrom; + + /** + * The end of the range, exclusive. + */ + public int intTo; + + /** + * List of (use-positions, register-priorities) pairs, sorted by use-positions. + */ + private UsePosList usePosList; + + /** + * Link to next interval in a sorted list of intervals that ends with {@link #EndMarker}. + */ + TraceInterval next; + + /** + * The linear-scan state of this interval. + */ + State state; + + /** + * The interval from which this one is derived. If this is a {@linkplain #isSplitParent() split + * parent}, it points to itself. + */ + private TraceInterval splitParent; + + /** + * List of all intervals that are split off from this interval. This is only used if this is a + * {@linkplain #isSplitParent() split parent}. + */ + private List<TraceInterval> splitChildren = Collections.emptyList(); + + /** + * Current split child that has been active or inactive last (always stored in split parents). + */ + private TraceInterval currentSplitChild; + + /** + * Specifies if move is inserted between currentSplitChild and this interval when interval gets + * active the first time. + */ + private boolean insertMoveWhenActivated; + + /** + * For spill move optimization. + */ + private SpillState spillState; + + /** + * Position where this interval is defined (if defined only once). + */ + private int spillDefinitionPos; + + /** + * This interval should be assigned the same location as the hint interval. + */ + private IntervalHint locationHint; + + /** + * The value with which a spilled child interval can be re-materialized. Currently this must be + * a Constant. + */ + private JavaConstant materializedValue; + + /** + * The number of times {@link #addMaterializationValue(JavaConstant)} is called. + */ + private int numMaterializationValuesAdded; + + void assignLocation(AllocatableValue newLocation) { + if (isRegister(newLocation)) { + assert this.location == null : "cannot re-assign location for " + this; + if (newLocation.getLIRKind().equals(LIRKind.Illegal) && !kind.equals(LIRKind.Illegal)) { + this.location = asRegister(newLocation).asValue(kind); + return; + } + } else if (isIllegal(newLocation)) { + assert canMaterialize(); + } else { + assert this.location == null || isRegister(this.location) || (isVirtualStackSlot(this.location) && isStackSlot(newLocation)) : "cannot re-assign location for " + this; + assert isStackSlotValue(newLocation); + assert !newLocation.getLIRKind().equals(LIRKind.Illegal); + assert newLocation.getLIRKind().equals(this.kind); + } + this.location = newLocation; + } + + /** + * Gets the {@linkplain RegisterValue register} or {@linkplain StackSlot spill slot} assigned to + * this interval. + */ + @Override + public AllocatableValue location() { + return location; + } + + public LIRKind kind() { + assert !isRegister(operand) : "cannot access type for fixed interval"; + return kind; + } + + public void setKind(LIRKind kind) { + assert isRegister(operand) || this.kind().equals(LIRKind.Illegal) || this.kind().equals(kind) : "overwriting existing type"; + this.kind = kind; + } + + public boolean isEmpty() { + return intFrom == Integer.MAX_VALUE && intTo == Integer.MAX_VALUE; + } + + public void setTo(int pos) { + assert intFrom == Integer.MAX_VALUE || intFrom < pos; + intTo = pos; + } + + public void setFrom(int pos) { + assert intTo == Integer.MAX_VALUE || pos < intTo; + intFrom = pos; + } + + @Override + public int from() { + return intFrom; + } + + int to() { + return intTo; + } + + int numUsePositions() { + return usePosList.size(); + } + + public void setLocationHint(IntervalHint interval) { + locationHint = interval; + } + + public boolean isSplitParent() { + return splitParent == this; + } + + boolean isSplitChild() { + return splitParent != this; + } + + /** + * Gets the split parent for this interval. + */ + public TraceInterval splitParent() { + assert splitParent.isSplitParent() : "not a split parent: " + this; + return splitParent; + } + + /** + * Gets the canonical spill slot for this interval. + */ + public AllocatableValue spillSlot() { + return splitParent().spillSlot; + } + + public void setSpillSlot(AllocatableValue slot) { + assert isStackSlotValue(slot); + assert splitParent().spillSlot == null || (isVirtualStackSlot(splitParent().spillSlot) && isStackSlot(slot)) : "connot overwrite existing spill slot"; + splitParent().spillSlot = slot; + } + + TraceInterval currentSplitChild() { + return splitParent().currentSplitChild; + } + + void makeCurrentSplitChild() { + splitParent().currentSplitChild = this; + } + + boolean insertMoveWhenActivated() { + return insertMoveWhenActivated; + } + + void setInsertMoveWhenActivated(boolean b) { + insertMoveWhenActivated = b; + } + + // for spill optimization + public SpillState spillState() { + return splitParent().spillState; + } + + public int spillDefinitionPos() { + return splitParent().spillDefinitionPos; + } + + public void setSpillState(SpillState state) { + assert state.ordinal() >= spillState().ordinal() : "state cannot decrease"; + splitParent().spillState = state; + } + + public void setSpillDefinitionPos(int pos) { + assert spillState() == SpillState.NoDefinitionFound || spillState() == SpillState.NoSpillStore || spillDefinitionPos() == -1 : "cannot set the position twice"; + int to = to(); + assert pos < to : String.format("Cannot spill %s at %d", this, pos); + splitParent().spillDefinitionPos = pos; + } + + /** + * Returns true if this interval has a shadow copy on the stack that is correct after + * {@code opId}. + */ + public boolean inMemoryAt(int opId) { + SpillState spillSt = spillState(); + return spillSt == SpillState.StartInMemory || (spillSt == SpillState.SpillStore && opId > spillDefinitionPos() && !canMaterialize()); + } + + void removeFirstUsePos() { + usePosList.removeLowestUsePos(); + } + + // test intersection + boolean intersects(TraceInterval i) { + return intersectsAt(i) != -1; + } + + int intersectsAt(TraceInterval i) { + TraceInterval i1; + TraceInterval i2; + if (i.from() < this.from()) { + i1 = i; + i2 = this; + } else { + i1 = this; + i2 = i; + } + assert i1.from() <= i2.from(); + + if (i1.to() <= i2.from()) { + return -1; + } + return i2.from(); + } + + /** + * Sentinel interval to denote the end of an interval list. + */ + static final TraceInterval EndMarker = new TraceInterval(Value.ILLEGAL, -1); + + TraceInterval(AllocatableValue operand, int operandNumber) { + assert operand != null; + this.operand = operand; + this.operandNumber = operandNumber; + if (isRegister(operand)) { + location = operand; + } else { + assert isIllegal(operand) || isVariable(operand); + } + this.kind = LIRKind.Illegal; + this.intFrom = Integer.MAX_VALUE; + this.intTo = Integer.MAX_VALUE; + this.usePosList = new UsePosList(4); + this.next = EndMarker; + this.spillState = SpillState.NoDefinitionFound; + this.spillDefinitionPos = -1; + splitParent = this; + currentSplitChild = this; + } + + /** + * Sets the value which is used for re-materialization. + */ + public void addMaterializationValue(JavaConstant value) { + if (numMaterializationValuesAdded == 0) { + materializedValue = value; + } else { + // Interval is defined on multiple places -> no materialization is possible. + materializedValue = null; + } + numMaterializationValuesAdded++; + } + + /** + * Returns true if this interval can be re-materialized when spilled. This means that no + * spill-moves are needed. Instead of restore-moves the {@link #materializedValue} is restored. + */ + public boolean canMaterialize() { + return getMaterializedValue() != null; + } + + /** + * Returns a value which can be moved to a register instead of a restore-move from stack. + */ + public JavaConstant getMaterializedValue() { + return splitParent().materializedValue; + } + + // consistency check of split-children + boolean checkSplitChildren() { + if (!splitChildren.isEmpty()) { + assert isSplitParent() : "only split parents can have children"; + + for (int i = 0; i < splitChildren.size(); i++) { + TraceInterval i1 = splitChildren.get(i); + + assert i1.splitParent() == this : "not a split child of this interval"; + assert i1.kind().equals(kind()) : "must be equal for all split children"; + assert (i1.spillSlot() == null && spillSlot == null) || i1.spillSlot().equals(spillSlot()) : "must be equal for all split children"; + + for (int j = i + 1; j < splitChildren.size(); j++) { + TraceInterval i2 = splitChildren.get(j); + + assert !i1.operand.equals(i2.operand) : "same register number"; + + if (i1.from() < i2.from()) { + assert i1.to() <= i2.from() && i1.to() < i2.to() : "intervals overlapping"; + } else { + assert i2.from() < i1.from() : "intervals start at same opId"; + assert i2.to() <= i1.from() && i2.to() < i1.to() : "intervals overlapping"; + } + } + } + } + + return true; + } + + public IntervalHint locationHint(boolean searchSplitChild) { + if (!searchSplitChild) { + return locationHint; + } + + if (locationHint != null) { + assert !(locationHint instanceof TraceInterval) || ((TraceInterval) locationHint).isSplitParent() : "ony split parents are valid hint registers"; + + if (locationHint.location() != null && isRegister(locationHint.location())) { + return locationHint; + } else if (locationHint instanceof TraceInterval) { + TraceInterval hint = (TraceInterval) locationHint; + if (!hint.splitChildren.isEmpty()) { + // search the first split child that has a register assigned + int len = hint.splitChildren.size(); + for (int i = 0; i < len; i++) { + TraceInterval interval = hint.splitChildren.get(i); + if (interval.location != null && isRegister(interval.location)) { + return interval; + } + } + } + } + } + + // no hint interval found that has a register assigned + return null; + } + + TraceInterval getSplitChildAtOpId(int opId, LIRInstruction.OperandMode mode, TraceLinearScan allocator) { + assert isSplitParent() : "can only be called for split parents"; + assert opId >= 0 : "invalid opId (method cannot be called for spill moves)"; + + if (splitChildren.isEmpty()) { + assert this.covers(opId, mode) : this + " does not cover " + opId; + return this; + } else { + TraceInterval result = null; + int len = splitChildren.size(); + + // in outputMode, the end of the interval (opId == cur.to()) is not valid + int toOffset = (mode == LIRInstruction.OperandMode.DEF ? 0 : 1); + + int i; + for (i = 0; i < len; i++) { + TraceInterval cur = splitChildren.get(i); + if (cur.from() <= opId && opId < cur.to() + toOffset) { + if (i > 0) { + // exchange current split child to start of list (faster access for next + // call) + Util.atPutGrow(splitChildren, i, splitChildren.get(0), null); + Util.atPutGrow(splitChildren, 0, cur, null); + } + + // interval found + result = cur; + break; + } + } + + assert checkSplitChild(result, opId, allocator, toOffset, mode); + return result; + } + } + + private boolean checkSplitChild(TraceInterval result, int opId, TraceLinearScan allocator, int toOffset, LIRInstruction.OperandMode mode) { + if (result == null) { + // this is an error + StringBuilder msg = new StringBuilder(this.toString()).append(" has no child at ").append(opId); + if (!splitChildren.isEmpty()) { + TraceInterval firstChild = splitChildren.get(0); + TraceInterval lastChild = splitChildren.get(splitChildren.size() - 1); + msg.append(" (first = ").append(firstChild).append(", last = ").append(lastChild).append(")"); + } + throw new JVMCIError("Linear Scan Error: %s", msg); + } + + if (!splitChildren.isEmpty()) { + for (TraceInterval interval : splitChildren) { + if (interval != result && interval.from() <= opId && opId < interval.to() + toOffset) { + TTY.println(String.format("two valid result intervals found for opId %d: %d and %d", opId, result.operandNumber, interval.operandNumber)); + TTY.println(result.logString(allocator)); + TTY.println(interval.logString(allocator)); + throw new BailoutException("two valid result intervals found"); + } + } + } + assert result.covers(opId, mode) : "opId not covered by interval"; + return true; + } + + // returns the interval that covers the given opId or null if there is none + TraceInterval getIntervalCoveringOpId(int opId) { + assert opId >= 0 : "invalid opId"; + assert opId < to() : "can only look into the past"; + + if (opId >= from()) { + return this; + } + + TraceInterval parent = splitParent(); + TraceInterval result = null; + + assert !parent.splitChildren.isEmpty() : "no split children available"; + int len = parent.splitChildren.size(); + + for (int i = len - 1; i >= 0; i--) { + TraceInterval cur = parent.splitChildren.get(i); + if (cur.from() <= opId && opId < cur.to()) { + assert result == null : "covered by multiple split children " + result + " and " + cur; + result = cur; + } + } + + return result; + } + + // returns the last split child that ends before the given opId + TraceInterval getSplitChildBeforeOpId(int opId) { + assert opId >= 0 : "invalid opId"; + + TraceInterval parent = splitParent(); + TraceInterval result = null; + + assert !parent.splitChildren.isEmpty() : "no split children available"; + int len = parent.splitChildren.size(); + + for (int i = len - 1; i >= 0; i--) { + TraceInterval cur = parent.splitChildren.get(i); + if (cur.to() <= opId && (result == null || result.to() < cur.to())) { + result = cur; + } + } + + assert result != null : "no split child found"; + return result; + } + + // checks if opId is covered by any split child + boolean splitChildCovers(int opId, LIRInstruction.OperandMode mode) { + assert isSplitParent() : "can only be called for split parents"; + assert opId >= 0 : "invalid opId (method can not be called for spill moves)"; + + if (splitChildren.isEmpty()) { + // simple case if interval was not split + return covers(opId, mode); + + } else { + // extended case: check all split children + int len = splitChildren.size(); + for (int i = 0; i < len; i++) { + TraceInterval cur = splitChildren.get(i); + if (cur.covers(opId, mode)) { + return true; + } + } + return false; + } + } + + private RegisterPriority adaptPriority(RegisterPriority priority) { + /* + * In case of re-materialized values we require that use-operands are registers, because we + * don't have the value in a stack location. (Note that ShouldHaveRegister means that the + * operand can also be a StackSlot). + */ + if (priority == RegisterPriority.ShouldHaveRegister && canMaterialize()) { + return RegisterPriority.MustHaveRegister; + } + return priority; + } + + // Note: use positions are sorted descending . first use has highest index + int firstUsage(RegisterPriority minRegisterPriority) { + assert isVariable(operand) : "cannot access use positions for fixed intervals"; + + for (int i = usePosList.size() - 1; i >= 0; --i) { + RegisterPriority registerPriority = adaptPriority(usePosList.registerPriority(i)); + if (registerPriority.greaterEqual(minRegisterPriority)) { + return usePosList.usePos(i); + } + } + return Integer.MAX_VALUE; + } + + int nextUsage(RegisterPriority minRegisterPriority, int from) { + assert isVariable(operand) : "cannot access use positions for fixed intervals"; + + for (int i = usePosList.size() - 1; i >= 0; --i) { + int usePos = usePosList.usePos(i); + if (usePos >= from && adaptPriority(usePosList.registerPriority(i)).greaterEqual(minRegisterPriority)) { + return usePos; + } + } + return Integer.MAX_VALUE; + } + + int nextUsageExact(RegisterPriority exactRegisterPriority, int from) { + assert isVariable(operand) : "cannot access use positions for fixed intervals"; + + for (int i = usePosList.size() - 1; i >= 0; --i) { + int usePos = usePosList.usePos(i); + if (usePos >= from && adaptPriority(usePosList.registerPriority(i)) == exactRegisterPriority) { + return usePos; + } + } + return Integer.MAX_VALUE; + } + + int previousUsage(RegisterPriority minRegisterPriority, int from) { + assert isVariable(operand) : "cannot access use positions for fixed intervals"; + + int prev = -1; + for (int i = usePosList.size() - 1; i >= 0; --i) { + int usePos = usePosList.usePos(i); + if (usePos > from) { + return prev; + } + if (adaptPriority(usePosList.registerPriority(i)).greaterEqual(minRegisterPriority)) { + prev = usePos; + } + } + return prev; + } + + public void addUsePos(int pos, RegisterPriority registerPriority) { + assert isEmpty() || covers(pos, LIRInstruction.OperandMode.USE) : String.format("use position %d not covered by live range of interval %s", pos, this); + + // do not add use positions for precolored intervals because they are never used + if (registerPriority != RegisterPriority.None && isVariable(operand)) { + if (DetailedAsserts.getValue()) { + for (int i = 0; i < usePosList.size(); i++) { + assert pos <= usePosList.usePos(i) : "already added a use-position with lower position"; + if (i > 0) { + assert usePosList.usePos(i) < usePosList.usePos(i - 1) : "not sorted descending"; + } + } + } + + // Note: addUse is called in descending order, so list gets sorted + // automatically by just appending new use positions + int len = usePosList.size(); + if (len == 0 || usePosList.usePos(len - 1) > pos) { + usePosList.add(pos, registerPriority); + } else if (usePosList.registerPriority(len - 1).lessThan(registerPriority)) { + assert usePosList.usePos(len - 1) == pos : "list not sorted correctly"; + usePosList.setRegisterPriority(len - 1, registerPriority); + } + } + } + + public void addRange(int from, int to) { + assert from < to : "invalid range"; + + if (from < intFrom) { + setFrom(from); + } + if (intTo == Integer.MAX_VALUE || intTo < to) { + setTo(to); + } + } + + TraceInterval newSplitChild(TraceLinearScan allocator) { + // allocate new interval + TraceInterval parent = splitParent(); + TraceInterval result = allocator.createDerivedInterval(parent); + result.setKind(kind()); + + result.splitParent = parent; + result.setLocationHint(parent); + + // insert new interval in children-list of parent + if (parent.splitChildren.isEmpty()) { + assert isSplitParent() : "list must be initialized at first split"; + + // Create new non-shared list + parent.splitChildren = new ArrayList<>(4); + parent.splitChildren.add(this); + } + parent.splitChildren.add(result); + + return result; + } + + /** + * Splits this interval at a specified position and returns the remainder as a new <i>child</i> + * interval of this interval's {@linkplain #splitParent() parent} interval. + * <p> + * When an interval is split, a bi-directional link is established between the original + * <i>parent</i> interval and the <i>children</i> intervals that are split off this interval. + * When a split child is split again, the new created interval is a direct child of the original + * parent. That is, there is no tree of split children stored, just a flat list. All split + * children are spilled to the same {@linkplain #spillSlot spill slot}. + * + * @param splitPos the position at which to split this interval + * @param allocator the register allocator context + * @return the child interval split off from this interval + */ + TraceInterval split(int splitPos, TraceLinearScan allocator) { + assert isVariable(operand) : "cannot split fixed intervals"; + + // allocate new interval + TraceInterval result = newSplitChild(allocator); + + // split the ranges + result.setTo(intTo); + result.setFrom(splitPos); + intTo = splitPos; + + // split list of use positions + result.usePosList = usePosList.splitAt(splitPos); + + if (DetailedAsserts.getValue()) { + for (int i = 0; i < usePosList.size(); i++) { + assert usePosList.usePos(i) < splitPos; + } + for (int i = 0; i < result.usePosList.size(); i++) { + assert result.usePosList.usePos(i) >= splitPos; + } + } + return result; + } + + // returns true if the opId is inside the interval + boolean covers(int opId, LIRInstruction.OperandMode mode) { + if (mode == LIRInstruction.OperandMode.DEF) { + return from() <= opId && opId < to(); + } + return from() <= opId && opId <= to(); + } + + @Override + public String toString() { + String from = "?"; + String to = "?"; + if (!isEmpty()) { + from = String.valueOf(from()); + to = String.valueOf(to()); + } + String locationString = this.location == null ? "" : "@" + this.location; + return operandNumber + ":" + operand + (isRegister(operand) ? "" : locationString) + "[" + from + "," + to + "]"; + } + + /** + * Gets the use position information for this interval. + */ + public UsePosList usePosList() { + return usePosList; + } + + /** + * Gets a single line string for logging the details of this interval to a log stream. + * + * @param allocator the register allocator context + */ + @Override + public String logString(TraceLinearScan allocator) { + StringBuilder buf = new StringBuilder(100); + buf.append("any ").append(operandNumber).append(':').append(operand).append(' '); + if (!isRegister(operand)) { + if (location != null) { + buf.append("location{").append(location).append("} "); + } + } + + buf.append("hints{").append(splitParent.operandNumber); + IntervalHint hint = locationHint(false); + if (hint != null) { + buf.append(", ").append(hint.location()); + } + buf.append("} ranges{"); + + // print range + buf.append("[" + from() + ", " + to() + "]"); + buf.append("} uses{"); + + // print use positions + int prev = -1; + for (int i = usePosList.size() - 1; i >= 0; --i) { + assert prev < usePosList.usePos(i) : "use positions not sorted"; + if (i != usePosList.size() - 1) { + buf.append(", "); + } + buf.append(usePosList.usePos(i)).append(':').append(usePosList.registerPriority(i).shortName()); + prev = usePosList.usePos(i); + } + buf.append("} spill-state{").append(spillState()).append("}"); + if (canMaterialize()) { + buf.append(" (remat:").append(getMaterializedValue().toString()).append(")"); + } + return buf.toString(); + } + + List<TraceInterval> getSplitChildren() { + return Collections.unmodifiableList(splitChildren); + } + + boolean isFixedInterval() { + return isRegister(operand); + } + + private static boolean isDefinitionPosition(int usePos) { + return (usePos & 1) == 1; + } + + int currentFrom(int currentPosition) { + assert isFixedInterval(); + for (int i = 0; i < usePosList.size(); i++) { + int usePos = usePosList.usePos(i); + if (usePos <= currentPosition && isDefinitionPosition(usePos)) { + return usePos; + } + + } + return Integer.MAX_VALUE; + } + + int currentIntersectsAt(int currentPosition, TraceInterval current) { + assert isFixedInterval(); + assert !current.isFixedInterval(); + int from = Integer.MAX_VALUE; + int to = Integer.MIN_VALUE; + + for (int i = 0; i < usePosList.size(); i++) { + int usePos = usePosList.usePos(i); + if (isDefinitionPosition(usePos)) { + if (usePos <= currentPosition) { + from = usePos; + break; + } + to = Integer.MIN_VALUE; + } else { + if (to < usePos) { + to = usePos; + } + } + } + if (from < current.from()) { + if (to <= current.from()) { + return -1; + } + return current.from(); + } else { + if (current.to() <= from) { + return -1; + } + return from; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/lsra/TraceIntervalDumper.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2015, 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.graal.lir.alloc.trace.lsra; + +import static jdk.vm.ci.code.ValueUtil.isRegister; +import jdk.vm.ci.meta.AllocatableValue; +import jdk.vm.ci.meta.Value; + +import com.oracle.graal.lir.debug.IntervalDumper; + +final class TraceIntervalDumper implements IntervalDumper { + private final FixedInterval[] fixedIntervals; + private final TraceInterval[] intervals; + + public TraceIntervalDumper(FixedInterval[] fixedIntervals, TraceInterval[] intervals) { + this.fixedIntervals = fixedIntervals; + this.intervals = intervals; + } + + public void visitIntervals(IntervalVisitor visitor) { + for (FixedInterval interval : fixedIntervals) { + if (interval != null) { + printFixedInterval(interval, visitor); + } + } + for (TraceInterval interval : intervals) { + if (interval != null) { + printInterval(interval, visitor); + } + } + } + + private static void printFixedInterval(FixedInterval interval, IntervalVisitor visitor) { + Value hint = null; + AllocatableValue operand = interval.operand; + String type = "fixed"; + char typeChar = operand.getPlatformKind().getTypeChar(); + visitor.visitIntervalStart(operand, operand, operand, hint, type, typeChar); + + // print ranges + for (FixedRange range = interval.first(); range != FixedRange.EndMarker; range = range.next) { + visitor.visitRange(range.from, range.to); + } + + // no use positions + + visitor.visitIntervalEnd("NOT_SUPPORTED"); + + } + + private static void printInterval(TraceInterval interval, IntervalVisitor visitor) { + Value hint = interval.locationHint(false) != null ? interval.locationHint(false).location() : null; + AllocatableValue operand = interval.operand; + String type = isRegister(operand) ? "fixed" : operand.getLIRKind().getPlatformKind().toString(); + char typeChar = operand.getPlatformKind().getTypeChar(); + visitor.visitIntervalStart(interval.splitParent().operand, operand, interval.location(), hint, type, typeChar); + + // print ranges + visitor.visitRange(interval.from(), interval.to()); + + // print use positions + int prev = -1; + UsePosList usePosList = interval.usePosList(); + for (int i = usePosList.size() - 1; i >= 0; --i) { + assert prev < usePosList.usePos(i) : "use positions not sorted"; + visitor.visitUsePos(usePosList.usePos(i), usePosList.registerPriority(i)); + prev = usePosList.usePos(i); + } + + visitor.visitIntervalEnd(interval.spillState()); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/lsra/TraceIntervalWalker.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2009, 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.graal.lir.alloc.trace.lsra; + +import jdk.vm.ci.common.JVMCIError; + +import com.oracle.graal.debug.Debug; +import com.oracle.graal.debug.Indent; +import com.oracle.graal.lir.alloc.trace.lsra.FixedInterval.FixedList; +import com.oracle.graal.lir.alloc.trace.lsra.TraceInterval.AnyList; +import com.oracle.graal.lir.alloc.trace.lsra.TraceInterval.RegisterBinding; +import com.oracle.graal.lir.alloc.trace.lsra.TraceInterval.State; + +/** + */ +class TraceIntervalWalker { + + protected final TraceLinearScan allocator; + + /** + * Sorted list of intervals, not live before the current position. + */ + protected AnyList unhandledAnyList; + + /** + * Sorted list of intervals, live at the current position. + */ + protected AnyList activeAnyList; + protected FixedList activeFixedList; + + /** + * Sorted list of intervals in a life time hole at the current position. + */ + protected FixedList inactiveFixedList; + + /** + * The current position (intercept point through the intervals). + */ + protected int currentPosition; + + /** + * Processes the {@code currentInterval} interval in an attempt to allocate a physical register + * to it and thus allow it to be moved to a list of {@linkplain #activeAnyList active} + * intervals. + * + * @param currentInterval The interval to be activated. + * + * @return {@code true} if a register was allocated to the {@code currentInterval} interval + */ + protected boolean activateCurrent(TraceInterval currentInterval) { + if (Debug.isLogEnabled()) { + logCurrentStatus(); + } + return true; + } + + @SuppressWarnings("try") + protected void logCurrentStatus() { + try (Indent i = Debug.logAndIndent("active:")) { + logList(activeFixedList.getFixed()); + logList(activeAnyList.getAny()); + } + try (Indent i = Debug.logAndIndent("inactive(fixed):")) { + logList(inactiveFixedList.getFixed()); + } + } + + private void logList(FixedInterval i) { + for (FixedInterval interval = i; interval != FixedInterval.EndMarker; interval = interval.next) { + Debug.log("%s", interval.logString(allocator)); + } + } + + private void logList(TraceInterval i) { + for (TraceInterval interval = i; interval != TraceInterval.EndMarker; interval = interval.next) { + Debug.log("%s", interval.logString(allocator)); + } + } + + void walkBefore(int lirOpId) { + walkTo(lirOpId - 1); + } + + void walk() { + walkTo(Integer.MAX_VALUE); + } + + /** + * Creates a new interval walker. + * + * @param allocator the register allocator context + * @param unhandledFixed the list of unhandled {@linkplain RegisterBinding#Fixed fixed} + * intervals + * @param unhandledAny the list of unhandled {@linkplain RegisterBinding#Any non-fixed} + * intervals + */ + TraceIntervalWalker(TraceLinearScan allocator, FixedInterval unhandledFixed, TraceInterval unhandledAny) { + this.allocator = allocator; + + unhandledAnyList = new AnyList(unhandledAny); + activeAnyList = new AnyList(TraceInterval.EndMarker); + activeFixedList = new FixedList(FixedInterval.EndMarker); + // we don't need a separate unhandled list for fixed. + inactiveFixedList = new FixedList(unhandledFixed); + currentPosition = -1; + } + + protected void removeFromList(TraceInterval interval) { + if (interval.state == State.Active) { + activeAnyList.removeAny(interval); + } else { + assert interval.state == State.Inactive : "invalid state"; + // inactiveAnyLists.removeAny(interval); + throw JVMCIError.shouldNotReachHere(); + } + } + + /** + * Walks up to {@code from} and updates the state of {@link FixedInterval fixed intervals}. + * + * Fixed intervals can switch back and forth between the states {@link State#Active} and + * {@link State#Inactive} (and eventually to {@link State#Handled} but handled intervals are not + * managed). + */ + @SuppressWarnings("try") + private void walkToFixed(State state, int from) { + assert state == State.Active || state == State.Inactive : "wrong state"; + FixedInterval prevprev = null; + FixedInterval prev = (state == State.Active) ? activeFixedList.getFixed() : inactiveFixedList.getFixed(); + FixedInterval next = prev; + if (Debug.isLogEnabled()) { + try (Indent i = Debug.logAndIndent("walkToFixed(%s, %d):", state, from)) { + logList(next); + } + } + while (next.currentFrom() <= from) { + FixedInterval cur = next; + next = cur.next; + + boolean rangeHasChanged = false; + while (cur.currentTo() <= from) { + cur.nextRange(); + rangeHasChanged = true; + } + + // also handle move from inactive list to active list + rangeHasChanged = rangeHasChanged || (state == State.Inactive && cur.currentFrom() <= from); + + if (rangeHasChanged) { + // remove cur from list + if (prevprev == null) { + if (state == State.Active) { + activeFixedList.setFixed(next); + } else { + inactiveFixedList.setFixed(next); + } + } else { + prevprev.next = next; + } + prev = next; + TraceInterval.State newState; + if (cur.currentAtEnd()) { + // move to handled state (not maintained as a list) + newState = State.Handled; + } else { + if (cur.currentFrom() <= from) { + // sort into active list + activeFixedList.addToListSortedByCurrentFromPositions(cur); + newState = State.Active; + } else { + // sort into inactive list + inactiveFixedList.addToListSortedByCurrentFromPositions(cur); + newState = State.Inactive; + } + if (prev == cur) { + assert state == newState; + prevprev = prev; + prev = cur.next; + } + } + intervalMoved(cur, state, newState); + } else { + prevprev = prev; + prev = cur.next; + } + } + } + + /** + * Walks up to {@code from} and updates the state of {@link TraceInterval intervals}. + * + * Trace intervals can switch once from {@link State#Unhandled} to {@link State#Active} and then + * to {@link State#Handled} but handled intervals are not managed. + */ + @SuppressWarnings("try") + private void walkToAny(int from) { + TraceInterval prevprev = null; + TraceInterval prev = activeAnyList.getAny(); + TraceInterval next = prev; + if (Debug.isLogEnabled()) { + try (Indent i = Debug.logAndIndent("walkToAny(%d):", from)) { + logList(next); + } + } + while (next.from() <= from) { + TraceInterval cur = next; + next = cur.next; + + if (cur.to() <= from) { + // remove cur from list + if (prevprev == null) { + activeAnyList.setAny(next); + } else { + prevprev.next = next; + } + intervalMoved(cur, State.Active, State.Handled); + } else { + prevprev = prev; + } + prev = next; + } + } + + /** + * Get the next interval from {@linkplain #unhandledAnyList} which starts before or at + * {@code toOpId}. The returned interval is removed. + * + * @postcondition all intervals in {@linkplain #unhandledAnyList} start after {@code toOpId}. + * + * @return The next interval or null if there is no {@linkplain #unhandledAnyList unhandled} + * interval at position {@code toOpId}. + */ + private TraceInterval nextInterval(int toOpId) { + TraceInterval any = unhandledAnyList.getAny(); + + if (any != TraceInterval.EndMarker) { + TraceInterval currentInterval = unhandledAnyList.getAny(); + if (toOpId < currentInterval.from()) { + return null; + } + + unhandledAnyList.setAny(currentInterval.next); + currentInterval.next = TraceInterval.EndMarker; + return currentInterval; + } + return null; + + } + + /** + * Walk up to {@code toOpId}. + * + * @postcondition {@link #currentPosition} is set to {@code toOpId}, {@link #activeFixedList} + * and {@link #inactiveFixedList} are populated and {@link TraceInterval#state}s + * are up to date. + */ + @SuppressWarnings("try") + protected void walkTo(int toOpId) { + assert currentPosition <= toOpId : "can not walk backwards"; + for (TraceInterval currentInterval = nextInterval(toOpId); currentInterval != null; currentInterval = nextInterval(toOpId)) { + int opId = currentInterval.from(); + + // set currentPosition prior to call of walkTo + currentPosition = opId; + + // update unhandled stack intervals + // updateUnhandledStackIntervals(opId); + + // call walkTo even if currentPosition == id + walkToFixed(State.Active, opId); + walkToFixed(State.Inactive, opId); + walkToAny(opId); + + try (Indent indent = Debug.logAndIndent("walk to op %d", opId)) { + currentInterval.state = State.Active; + if (activateCurrent(currentInterval)) { + activeAnyList.addToListSortedByFromPositions(currentInterval); + intervalMoved(currentInterval, State.Unhandled, State.Active); + } + } + } + // set currentPosition prior to call of walkTo + currentPosition = toOpId; + + if (currentPosition <= allocator.maxOpId()) { + // update unhandled stack intervals + // updateUnhandledStackIntervals(toOpId); + + // call walkTo if still in range + walkToFixed(State.Active, toOpId); + walkToFixed(State.Inactive, toOpId); + walkToAny(toOpId); + } + } + + private void intervalMoved(IntervalHint interval, State from, State to) { + // intervalMoved() is called whenever an interval moves from one interval list to another. + // In the implementation of this method it is prohibited to move the interval to any list. + if (Debug.isLogEnabled()) { + Debug.log("interval moved from %s to %s: %s", from, to, interval.logString(allocator)); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/lsra/TraceLinearScan.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,1045 @@ +/* + * Copyright (c) 2009, 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.graal.lir.alloc.trace.lsra; + +import static com.oracle.graal.compiler.common.GraalOptions.DetailedAsserts; +import static com.oracle.graal.lir.LIRValueUtil.isVariable; +import static jdk.vm.ci.code.CodeUtil.isEven; +import static jdk.vm.ci.code.ValueUtil.asRegister; +import static jdk.vm.ci.code.ValueUtil.asRegisterValue; +import static jdk.vm.ci.code.ValueUtil.isIllegal; +import static jdk.vm.ci.code.ValueUtil.isLegal; +import static jdk.vm.ci.code.ValueUtil.isRegister; + +import java.util.Arrays; +import java.util.BitSet; +import java.util.EnumSet; +import java.util.List; + +import jdk.vm.ci.code.BailoutException; +import jdk.vm.ci.code.Register; +import jdk.vm.ci.code.RegisterAttributes; +import jdk.vm.ci.code.RegisterValue; +import jdk.vm.ci.code.TargetDescription; +import jdk.vm.ci.common.JVMCIError; +import jdk.vm.ci.meta.AllocatableValue; +import jdk.vm.ci.meta.LIRKind; +import jdk.vm.ci.meta.Value; +import jdk.vm.ci.options.NestedBooleanOptionValue; +import jdk.vm.ci.options.Option; +import jdk.vm.ci.options.OptionType; +import jdk.vm.ci.options.OptionValue; + +import com.oracle.graal.compiler.common.alloc.RegisterAllocationConfig; +import com.oracle.graal.compiler.common.alloc.TraceBuilder.TraceBuilderResult; +import com.oracle.graal.compiler.common.cfg.AbstractBlockBase; +import com.oracle.graal.compiler.common.cfg.BlockMap; +import com.oracle.graal.debug.Debug; +import com.oracle.graal.debug.Debug.Scope; +import com.oracle.graal.debug.Indent; +import com.oracle.graal.lir.LIR; +import com.oracle.graal.lir.LIRInstruction; +import com.oracle.graal.lir.LIRInstruction.OperandFlag; +import com.oracle.graal.lir.LIRInstruction.OperandMode; +import com.oracle.graal.lir.StandardOp.BlockEndOp; +import com.oracle.graal.lir.ValueConsumer; +import com.oracle.graal.lir.Variable; +import com.oracle.graal.lir.VirtualStackSlot; +import com.oracle.graal.lir.alloc.trace.TraceRegisterAllocationPhase; +import com.oracle.graal.lir.alloc.trace.lsra.TraceLinearScanAllocationPhase.TraceLinearScanAllocationContext; +import com.oracle.graal.lir.framemap.FrameMapBuilder; +import com.oracle.graal.lir.gen.LIRGenerationResult; +import com.oracle.graal.lir.gen.LIRGeneratorTool.MoveFactory; +import com.oracle.graal.lir.phases.LIRPhase; + +/** + * An implementation of the linear scan register allocator algorithm described in <a + * href="http://doi.acm.org/10.1145/1064979.1064998" + * >"Optimized Interval Splitting in a Linear Scan Register Allocator"</a> by Christian Wimmer and + * Hanspeter Moessenboeck. + */ +public final class TraceLinearScan { + + public static class Options { + // @formatter:off + @Option(help = "Enable spill position optimization", type = OptionType.Debug) + public static final OptionValue<Boolean> LIROptTraceRAEliminateSpillMoves = new NestedBooleanOptionValue(LIRPhase.Options.LIROptimization, true); + // @formatter:on + } + + private static final TraceLinearScanRegisterAllocationPhase TRACE_LINEAR_SCAN_REGISTER_ALLOCATION_PHASE = new TraceLinearScanRegisterAllocationPhase(); + private static final TraceLinearScanAssignLocationsPhase TRACE_LINEAR_SCAN_ASSIGN_LOCATIONS_PHASE = new TraceLinearScanAssignLocationsPhase(); + private static final TraceLinearScanEliminateSpillMovePhase TRACE_LINEAR_SCAN_ELIMINATE_SPILL_MOVE_PHASE = new TraceLinearScanEliminateSpillMovePhase(); + private static final TraceLinearScanResolveDataFlowPhase TRACE_LINEAR_SCAN_RESOLVE_DATA_FLOW_PHASE = new TraceLinearScanResolveDataFlowPhase(); + private static final TraceLinearScanLifetimeAnalysisPhase TRACE_LINEAR_SCAN_LIFETIME_ANALYSIS_PHASE = new TraceLinearScanLifetimeAnalysisPhase(); + + public static class BlockData { + + /** + * Bit map specifying which operands are live upon entry to this block. These are values + * used in this block or any of its successors where such value are not defined in this + * block. The bit index of an operand is its + * {@linkplain TraceLinearScan#operandNumber(Value) operand number}. + */ + public BitSet liveIn; + + /** + * Bit map specifying which operands are live upon exit from this block. These are values + * used in a successor block that are either defined in this block or were live upon entry + * to this block. The bit index of an operand is its + * {@linkplain TraceLinearScan#operandNumber(Value) operand number}. + */ + public BitSet liveOut; + + /** + * Bit map specifying which operands are used (before being defined) in this block. That is, + * these are the values that are live upon entry to the block. The bit index of an operand + * is its {@linkplain TraceLinearScan#operandNumber(Value) operand number}. + */ + public BitSet liveGen; + + /** + * Bit map specifying which operands are defined/overwritten in this block. The bit index of + * an operand is its {@linkplain TraceLinearScan#operandNumber(Value) operand number}. + */ + public BitSet liveKill; + } + + public static final int DOMINATOR_SPILL_MOVE_ID = -2; + private static final int SPLIT_INTERVALS_CAPACITY_RIGHT_SHIFT = 1; + + private final LIR ir; + private final FrameMapBuilder frameMapBuilder; + private final RegisterAttributes[] registerAttributes; + private final Register[] registers; + private final RegisterAllocationConfig regAllocConfig; + private final MoveFactory moveFactory; + + private final BlockMap<BlockData> blockData; + + /** + * List of blocks in linear-scan order. This is only correct as long as the CFG does not change. + */ + private final List<? extends AbstractBlockBase<?>> sortedBlocks; + + /** @see #fixedIntervals() */ + private final FixedInterval[] fixedIntervals; + + /** @see #intervals() */ + private TraceInterval[] intervals; + + /** + * The number of valid entries in {@link #intervals}. + */ + private int intervalsSize; + + /** + * The index of the first entry in {@link #intervals} for a + * {@linkplain #createDerivedInterval(TraceInterval) derived interval}. + */ + private int firstDerivedIntervalIndex = -1; + + /** + * Intervals sorted by {@link TraceInterval#from()}. + */ + private TraceInterval[] sortedIntervals; + + /** + * Fixed intervals sorted by {@link FixedInterval#from()}. + */ + private FixedInterval[] sortedFixedIntervals; + + /** + * Map from an instruction {@linkplain LIRInstruction#id id} to the instruction. Entries should + * be retrieved with {@link #instructionForId(int)} as the id is not simply an index into this + * array. + */ + private LIRInstruction[] opIdToInstructionMap; + + /** + * Map from an instruction {@linkplain LIRInstruction#id id} to the + * {@linkplain AbstractBlockBase block} containing the instruction. Entries should be retrieved + * with {@link #blockForId(int)} as the id is not simply an index into this array. + */ + private AbstractBlockBase<?>[] opIdToBlockMap; + + protected final TraceBuilderResult<?> traceBuilderResult; + private final boolean neverSpillConstants; + + public TraceLinearScan(TargetDescription target, LIRGenerationResult res, MoveFactory spillMoveFactory, RegisterAllocationConfig regAllocConfig, List<? extends AbstractBlockBase<?>> sortedBlocks, + TraceBuilderResult<?> traceBuilderResult, boolean neverSpillConstants) { + this.ir = res.getLIR(); + this.moveFactory = spillMoveFactory; + this.frameMapBuilder = res.getFrameMapBuilder(); + this.sortedBlocks = sortedBlocks; + this.registerAttributes = regAllocConfig.getRegisterConfig().getAttributesMap(); + this.regAllocConfig = regAllocConfig; + + this.registers = target.arch.getRegisters(); + this.fixedIntervals = new FixedInterval[registers.length]; + this.blockData = new BlockMap<>(ir.getControlFlowGraph()); + this.traceBuilderResult = traceBuilderResult; + this.neverSpillConstants = neverSpillConstants; + } + + public int getFirstLirInstructionId(AbstractBlockBase<?> block) { + int result = ir.getLIRforBlock(block).get(0).id(); + assert result >= 0; + return result; + } + + public int getLastLirInstructionId(AbstractBlockBase<?> block) { + List<LIRInstruction> instructions = ir.getLIRforBlock(block); + int result = instructions.get(instructions.size() - 1).id(); + assert result >= 0; + return result; + } + + public MoveFactory getSpillMoveFactory() { + return moveFactory; + } + + protected TraceLocalMoveResolver createMoveResolver() { + TraceLocalMoveResolver moveResolver = new TraceLocalMoveResolver(this); + assert moveResolver.checkEmpty(); + return moveResolver; + } + + public static boolean isVariableOrRegister(Value value) { + return isVariable(value) || isRegister(value); + } + + /** + * Converts an operand (variable or register) to an index in a flat address space covering all + * the {@linkplain Variable variables} and {@linkplain RegisterValue registers} being processed + * by this allocator. + */ + @SuppressWarnings("static-method") + int operandNumber(Value operand) { + assert !isRegister(operand) : "Register do not have operand numbers: " + operand; + assert isVariable(operand) : "Unsupported Value " + operand; + return ((Variable) operand).index; + } + + /** + * Gets the number of operands. This value will increase by 1 for new variable. + */ + int operandSize() { + return ir.numVariables(); + } + + /** + * Gets the number of registers. This value will never change. + */ + int numRegisters() { + return registers.length; + } + + public BlockData getBlockData(AbstractBlockBase<?> block) { + return blockData.get(block); + } + + void initBlockData(AbstractBlockBase<?> block) { + blockData.put(block, new BlockData()); + } + + static final IntervalPredicate IS_PRECOLORED_INTERVAL = new IntervalPredicate() { + + @Override + public boolean apply(TraceInterval i) { + return isRegister(i.operand); + } + }; + + static final IntervalPredicate IS_VARIABLE_INTERVAL = new IntervalPredicate() { + + @Override + public boolean apply(TraceInterval i) { + return isVariable(i.operand); + } + }; + + static final IntervalPredicate IS_STACK_INTERVAL = new IntervalPredicate() { + + @Override + public boolean apply(TraceInterval i) { + return !isRegister(i.operand); + } + }; + + /** + * Gets an object describing the attributes of a given register according to this register + * configuration. + */ + public RegisterAttributes attributes(Register reg) { + return registerAttributes[reg.number]; + } + + void assignSpillSlot(TraceInterval interval) { + /* + * Assign the canonical spill slot of the parent (if a part of the interval is already + * spilled) or allocate a new spill slot. + */ + if (interval.canMaterialize()) { + interval.assignLocation(Value.ILLEGAL); + } else if (interval.spillSlot() != null) { + interval.assignLocation(interval.spillSlot()); + } else { + VirtualStackSlot slot = frameMapBuilder.allocateSpillSlot(interval.kind()); + interval.setSpillSlot(slot); + interval.assignLocation(slot); + } + } + + /** + * Map from {@linkplain #operandNumber(Value) operand numbers} to intervals. + */ + public TraceInterval[] intervals() { + return intervals; + } + + /** + * Map from {@linkplain #operandNumber(Value) operand numbers} to intervals. + */ + public FixedInterval[] fixedIntervals() { + return fixedIntervals; + } + + void initIntervals() { + intervalsSize = operandSize(); + intervals = new TraceInterval[intervalsSize + (intervalsSize >> SPLIT_INTERVALS_CAPACITY_RIGHT_SHIFT)]; + } + + /** + * Creates a new fixed interval. + * + * @param reg the operand for the interval + * @return the created interval + */ + FixedInterval createFixedInterval(RegisterValue reg) { + FixedInterval interval = new FixedInterval(reg); + int operandNumber = reg.getRegister().number; + assert fixedIntervals[operandNumber] == null; + fixedIntervals[operandNumber] = interval; + return interval; + } + + /** + * Creates a new interval. + * + * @param operand the operand for the interval + * @return the created interval + */ + TraceInterval createInterval(AllocatableValue operand) { + assert isLegal(operand); + int operandNumber = operandNumber(operand); + TraceInterval interval = new TraceInterval(operand, operandNumber); + assert operandNumber < intervalsSize; + assert intervals[operandNumber] == null; + intervals[operandNumber] = interval; + return interval; + } + + /** + * Creates an interval as a result of splitting or spilling another interval. + * + * @param source an interval being split of spilled + * @return a new interval derived from {@code source} + */ + TraceInterval createDerivedInterval(TraceInterval source) { + if (firstDerivedIntervalIndex == -1) { + firstDerivedIntervalIndex = intervalsSize; + } + if (intervalsSize == intervals.length) { + intervals = Arrays.copyOf(intervals, intervals.length + (intervals.length >> SPLIT_INTERVALS_CAPACITY_RIGHT_SHIFT)); + } + intervalsSize++; + Variable variable = new Variable(source.kind(), ir.nextVariable()); + + TraceInterval interval = createInterval(variable); + assert intervals[intervalsSize - 1] == interval; + return interval; + } + + // access to block list (sorted in linear scan order) + public int blockCount() { + return sortedBlocks.size(); + } + + public AbstractBlockBase<?> blockAt(int index) { + return sortedBlocks.get(index); + } + + /** + * Gets the size of the {@link BlockData#liveIn} and {@link BlockData#liveOut} sets for a basic + * block. These sets do not include any operands allocated as a result of creating + * {@linkplain #createDerivedInterval(TraceInterval) derived intervals}. + */ + public int liveSetSize() { + return firstDerivedIntervalIndex == -1 ? operandSize() : firstDerivedIntervalIndex; + } + + int numLoops() { + return ir.getControlFlowGraph().getLoops().size(); + } + + public FixedInterval fixedIntervalFor(RegisterValue reg) { + return fixedIntervals[reg.getRegister().number]; + } + + public FixedInterval getOrCreateFixedInterval(RegisterValue reg) { + FixedInterval ret = fixedIntervalFor(reg); + if (ret == null) { + return createFixedInterval(reg); + } else { + return ret; + } + } + + TraceInterval intervalFor(int operandNumber) { + return intervals[operandNumber]; + } + + public TraceInterval intervalFor(Value operand) { + int operandNumber = operandNumber(operand); + assert operandNumber < intervalsSize; + return intervals[operandNumber]; + } + + public TraceInterval getOrCreateInterval(AllocatableValue operand) { + TraceInterval ret = intervalFor(operand); + if (ret == null) { + return createInterval(operand); + } else { + return ret; + } + } + + void initOpIdMaps(int numInstructions) { + opIdToInstructionMap = new LIRInstruction[numInstructions]; + opIdToBlockMap = new AbstractBlockBase<?>[numInstructions]; + } + + void putOpIdMaps(int index, LIRInstruction op, AbstractBlockBase<?> block) { + opIdToInstructionMap[index] = op; + opIdToBlockMap[index] = block; + } + + /** + * Gets the highest instruction id allocated by this object. + */ + int maxOpId() { + assert opIdToInstructionMap.length > 0 : "no operations"; + return (opIdToInstructionMap.length - 1) << 1; + } + + /** + * Converts an {@linkplain LIRInstruction#id instruction id} to an instruction index. All LIR + * instructions in a method have an index one greater than their linear-scan order predecessor + * with the first instruction having an index of 0. + */ + private static int opIdToIndex(int opId) { + return opId >> 1; + } + + /** + * Retrieves the {@link LIRInstruction} based on its {@linkplain LIRInstruction#id id}. + * + * @param opId an instruction {@linkplain LIRInstruction#id id} + * @return the instruction whose {@linkplain LIRInstruction#id} {@code == id} + */ + public LIRInstruction instructionForId(int opId) { + assert isEven(opId) : "opId not even"; + LIRInstruction instr = opIdToInstructionMap[opIdToIndex(opId)]; + assert instr.id() == opId; + return instr; + } + + /** + * Gets the block containing a given instruction. + * + * @param opId an instruction {@linkplain LIRInstruction#id id} + * @return the block containing the instruction denoted by {@code opId} + */ + public AbstractBlockBase<?> blockForId(int opId) { + assert opIdToBlockMap.length > 0 && opId >= 0 && opId <= maxOpId() + 1 : "opId out of range: " + opId; + return opIdToBlockMap[opIdToIndex(opId)]; + } + + boolean isBlockBegin(int opId) { + return opId == 0 || blockForId(opId) != blockForId(opId - 1); + } + + boolean isBlockEnd(int opId) { + boolean isBlockBegin = isBlockBegin(opId + 2); + assert isBlockBegin == (instructionForId(opId & (~1)) instanceof BlockEndOp); + return isBlockBegin; + } + + boolean coversBlockBegin(int opId1, int opId2) { + return blockForId(opId1) != blockForId(opId2); + } + + /** + * Determines if an {@link LIRInstruction} destroys all caller saved registers. + * + * @param opId an instruction {@linkplain LIRInstruction#id id} + * @return {@code true} if the instruction denoted by {@code id} destroys all caller saved + * registers. + */ + boolean hasCall(int opId) { + assert isEven(opId) : "opId not even"; + return instructionForId(opId).destroysCallerSavedRegisters(); + } + + abstract static class IntervalPredicate { + + abstract boolean apply(TraceInterval i); + } + + public boolean isProcessed(Value operand) { + return !isRegister(operand) || attributes(asRegister(operand)).isAllocatable(); + } + + // * Phase 5: actual register allocation + + private static <T extends IntervalHint> boolean isSortedByFrom(T[] intervals) { + int from = -1; + for (T interval : intervals) { + assert interval != null; + assert from <= interval.from(); + from = interval.from(); + } + return true; + } + + private static boolean isSortedBySpillPos(TraceInterval[] intervals) { + int from = -1; + for (TraceInterval interval : intervals) { + assert interval != null; + assert from <= interval.spillDefinitionPos(); + from = interval.spillDefinitionPos(); + } + return true; + } + + private static TraceInterval addToList(TraceInterval first, TraceInterval prev, TraceInterval interval) { + TraceInterval newFirst = first; + if (prev != null) { + prev.next = interval; + } else { + newFirst = interval; + } + return newFirst; + } + + TraceInterval createUnhandledListByFrom(IntervalPredicate isList1) { + assert isSortedByFrom(sortedIntervals) : "interval list is not sorted"; + return createUnhandledList(isList1); + } + + TraceInterval createUnhandledListBySpillPos(IntervalPredicate isList1) { + assert isSortedBySpillPos(sortedIntervals) : "interval list is not sorted"; + return createUnhandledList(isList1); + } + + private TraceInterval createUnhandledList(IntervalPredicate isList1) { + + TraceInterval list1 = TraceInterval.EndMarker; + + TraceInterval list1Prev = null; + TraceInterval v; + + int n = sortedIntervals.length; + for (int i = 0; i < n; i++) { + v = sortedIntervals[i]; + if (v == null) { + continue; + } + + if (isList1.apply(v)) { + list1 = addToList(list1, list1Prev, v); + list1Prev = v; + } + } + + if (list1Prev != null) { + list1Prev.next = TraceInterval.EndMarker; + } + + assert list1Prev == null || list1Prev.next == TraceInterval.EndMarker : "linear list ends not with sentinel"; + + return list1; + } + + private static FixedInterval addToList(FixedInterval first, FixedInterval prev, FixedInterval interval) { + FixedInterval newFirst = first; + if (prev != null) { + prev.next = interval; + } else { + newFirst = interval; + } + return newFirst; + } + + FixedInterval createFixedUnhandledList() { + assert isSortedByFrom(sortedFixedIntervals) : "interval list is not sorted"; + + FixedInterval list1 = FixedInterval.EndMarker; + + FixedInterval list1Prev = null; + FixedInterval v; + + int n = sortedFixedIntervals.length; + for (int i = 0; i < n; i++) { + v = sortedFixedIntervals[i]; + if (v == null) { + continue; + } + + v.rewindRange(); + list1 = addToList(list1, list1Prev, v); + list1Prev = v; + } + + if (list1Prev != null) { + list1Prev.next = FixedInterval.EndMarker; + } + + assert list1Prev == null || list1Prev.next == FixedInterval.EndMarker : "linear list ends not with sentinel"; + + return list1; + } + + // SORTING + + protected void sortIntervalsBeforeAllocation() { + int sortedLen = 0; + for (TraceInterval interval : intervals) { + if (interval != null) { + sortedLen++; + } + } + sortedIntervals = sortIntervalsBeforeAllocation(intervals, new TraceInterval[sortedLen]); + } + + protected void sortFixedIntervalsBeforeAllocation() { + int sortedLen = 0; + for (FixedInterval interval : fixedIntervals) { + if (interval != null) { + sortedLen++; + } + } + sortedFixedIntervals = sortIntervalsBeforeAllocation(fixedIntervals, new FixedInterval[sortedLen]); + } + + private static <T extends IntervalHint> T[] sortIntervalsBeforeAllocation(T[] intervals, T[] sortedList) { + int sortedIdx = 0; + int sortedFromMax = -1; + + // special sorting algorithm: the original interval-list is almost sorted, + // only some intervals are swapped. So this is much faster than a complete QuickSort + for (T interval : intervals) { + if (interval != null) { + int from = interval.from(); + + if (sortedFromMax <= from) { + sortedList[sortedIdx++] = interval; + sortedFromMax = interval.from(); + } else { + // the assumption that the intervals are already sorted failed, + // so this interval must be sorted in manually + int j; + for (j = sortedIdx - 1; j >= 0 && from < sortedList[j].from(); j--) { + sortedList[j + 1] = sortedList[j]; + } + sortedList[j + 1] = interval; + sortedIdx++; + } + } + } + return sortedList; + } + + void sortIntervalsAfterAllocation() { + if (firstDerivedIntervalIndex == -1) { + // no intervals have been added during allocation, so sorted list is already up to date + return; + } + + TraceInterval[] oldList = sortedIntervals; + TraceInterval[] newList = Arrays.copyOfRange(intervals, firstDerivedIntervalIndex, intervalsSize); + int oldLen = oldList.length; + int newLen = newList.length; + + // conventional sort-algorithm for new intervals + Arrays.sort(newList, (TraceInterval a, TraceInterval b) -> a.from() - b.from()); + + // merge old and new list (both already sorted) into one combined list + TraceInterval[] combinedList = new TraceInterval[oldLen + newLen]; + int oldIdx = 0; + int newIdx = 0; + + while (oldIdx + newIdx < combinedList.length) { + if (newIdx >= newLen || (oldIdx < oldLen && oldList[oldIdx].from() <= newList[newIdx].from())) { + combinedList[oldIdx + newIdx] = oldList[oldIdx]; + oldIdx++; + } else { + combinedList[oldIdx + newIdx] = newList[newIdx]; + newIdx++; + } + } + + sortedIntervals = combinedList; + } + + void sortIntervalsBySpillPos() { + // TODO (JE): better algorithm? + // conventional sort-algorithm for new intervals + Arrays.sort(sortedIntervals, (TraceInterval a, TraceInterval b) -> a.spillDefinitionPos() - b.spillDefinitionPos()); + } + + // wrapper for Interval.splitChildAtOpId that performs a bailout in product mode + // instead of returning null + public TraceInterval splitChildAtOpId(TraceInterval interval, int opId, LIRInstruction.OperandMode mode) { + TraceInterval result = interval.getSplitChildAtOpId(opId, mode, this); + + if (result != null) { + if (Debug.isLogEnabled()) { + Debug.log("Split child at pos %d of interval %s is %s", opId, interval, result); + } + return result; + } + + throw new BailoutException("LinearScan: interval is null"); + } + + static AllocatableValue canonicalSpillOpr(TraceInterval interval) { + assert interval.spillSlot() != null : "canonical spill slot not set"; + return interval.spillSlot(); + } + + boolean isMaterialized(AllocatableValue operand, int opId, OperandMode mode) { + TraceInterval interval = intervalFor(operand); + assert interval != null : "interval must exist"; + + if (opId != -1) { + /* + * Operands are not changed when an interval is split during allocation, so search the + * right interval here. + */ + interval = splitChildAtOpId(interval, opId, mode); + } + + return isIllegal(interval.location()) && interval.canMaterialize(); + } + + boolean isCallerSave(Value operand) { + return attributes(asRegister(operand)).isCallerSave(); + } + + @SuppressWarnings("try") + public <B extends AbstractBlockBase<B>> void allocate(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, MoveFactory spillMoveFactory, + RegisterAllocationConfig registerAllocationConfig) { + + /* + * This is the point to enable debug logging for the whole register allocation. + */ + try (Indent indent = Debug.logAndIndent("LinearScan allocate")) { + TraceLinearScanAllocationContext context = new TraceLinearScanAllocationContext(spillMoveFactory, registerAllocationConfig, traceBuilderResult, this); + + TRACE_LINEAR_SCAN_LIFETIME_ANALYSIS_PHASE.apply(target, lirGenRes, codeEmittingOrder, linearScanOrder, context, false); + + try (Scope s = Debug.scope("AfterLifetimeAnalysis", (Object) intervals())) { + sortIntervalsBeforeAllocation(); + sortFixedIntervalsBeforeAllocation(); + + TRACE_LINEAR_SCAN_REGISTER_ALLOCATION_PHASE.apply(target, lirGenRes, codeEmittingOrder, linearScanOrder, context, false); + + // resolve intra-trace data-flow + TRACE_LINEAR_SCAN_RESOLVE_DATA_FLOW_PHASE.apply(target, lirGenRes, codeEmittingOrder, linearScanOrder, context, false); + Debug.dump(TraceRegisterAllocationPhase.TRACE_DUMP_LEVEL, sortedBlocks(), "%s", TRACE_LINEAR_SCAN_RESOLVE_DATA_FLOW_PHASE.getName()); + + // eliminate spill moves + if (Options.LIROptTraceRAEliminateSpillMoves.getValue()) { + TRACE_LINEAR_SCAN_ELIMINATE_SPILL_MOVE_PHASE.apply(target, lirGenRes, codeEmittingOrder, linearScanOrder, context, false); + Debug.dump(TraceRegisterAllocationPhase.TRACE_DUMP_LEVEL, sortedBlocks(), "%s", TRACE_LINEAR_SCAN_ELIMINATE_SPILL_MOVE_PHASE.getName()); + } + + TRACE_LINEAR_SCAN_ASSIGN_LOCATIONS_PHASE.apply(target, lirGenRes, codeEmittingOrder, linearScanOrder, context, false); + + if (DetailedAsserts.getValue()) { + verifyIntervals(); + } + } catch (Throwable e) { + throw Debug.handle(e); + } + } + } + + @SuppressWarnings("try") + public void printIntervals(String label) { + if (Debug.isDumpEnabled(TraceRegisterAllocationPhase.TRACE_DUMP_LEVEL)) { + if (Debug.isLogEnabled()) { + try (Indent indent = Debug.logAndIndent("intervals %s", label)) { + for (FixedInterval interval : fixedIntervals) { + if (interval != null) { + Debug.log("%s", interval.logString(this)); + } + } + + for (TraceInterval interval : intervals) { + if (interval != null) { + Debug.log("%s", interval.logString(this)); + } + } + + try (Indent indent2 = Debug.logAndIndent("Basic Blocks")) { + for (int i = 0; i < blockCount(); i++) { + AbstractBlockBase<?> block = blockAt(i); + Debug.log("B%d [%d, %d, %s] ", block.getId(), getFirstLirInstructionId(block), getLastLirInstructionId(block), block.getLoop()); + } + } + } + } + Debug.dump(new TraceIntervalDumper(Arrays.copyOf(fixedIntervals, fixedIntervals.length), Arrays.copyOf(intervals, intervalsSize)), label); + } + } + + public void printLir(String label, @SuppressWarnings("unused") boolean hirValid) { + if (Debug.isDumpEnabled(TraceRegisterAllocationPhase.TRACE_DUMP_LEVEL)) { + Debug.dump(TraceRegisterAllocationPhase.TRACE_DUMP_LEVEL, sortedBlocks(), label); + } + } + + boolean verify() { + // (check that all intervals have a correct register and that no registers are overwritten) + verifyIntervals(); + + verifyRegisters(); + + Debug.log("no errors found"); + + return true; + } + + @SuppressWarnings("try") + private void verifyRegisters() { + // Enable this logging to get output for the verification process. + try (Indent indent = Debug.logAndIndent("verifying register allocation")) { + RegisterVerifier verifier = new RegisterVerifier(this); + verifier.verify(blockAt(0)); + } + } + + @SuppressWarnings("try") + protected void verifyIntervals() { + try (Indent indent = Debug.logAndIndent("verifying intervals")) { + int len = intervalsSize; + + for (int i = 0; i < len; i++) { + final TraceInterval i1 = intervals[i]; + if (i1 == null) { + continue; + } + + i1.checkSplitChildren(); + + if (i1.operandNumber != i) { + Debug.log("Interval %d is on position %d in list", i1.operandNumber, i); + Debug.log(i1.logString(this)); + throw new JVMCIError(""); + } + + if (isVariable(i1.operand) && i1.kind().equals(LIRKind.Illegal)) { + Debug.log("Interval %d has no type assigned", i1.operandNumber); + Debug.log(i1.logString(this)); + throw new JVMCIError(""); + } + + if (i1.location() == null) { + Debug.log("Interval %d has no register assigned", i1.operandNumber); + Debug.log(i1.logString(this)); + throw new JVMCIError(""); + } + + if (i1.isEmpty()) { + Debug.log("Interval %d has no Range", i1.operandNumber); + Debug.log(i1.logString(this)); + throw new JVMCIError(""); + } + + if (i1.from() >= i1.to()) { + Debug.log("Interval %d has zero length range", i1.operandNumber); + Debug.log(i1.logString(this)); + throw new JVMCIError(""); + } + + // special intervals that are created in MoveResolver + // . ignore them because the range information has no meaning there + if (i1.from() == 1 && i1.to() == 2) { + continue; + } + // check any intervals + for (int j = i + 1; j < len; j++) { + final TraceInterval i2 = intervals[j]; + if (i2 == null) { + continue; + } + + // special intervals that are created in MoveResolver + // . ignore them because the range information has no meaning there + if (i2.from() == 1 && i2.to() == 2) { + continue; + } + Value l1 = i1.location(); + Value l2 = i2.location(); + boolean intersects = i1.intersects(i2); + if (intersects && !isIllegal(l1) && (l1.equals(l2))) { + if (DetailedAsserts.getValue()) { + Debug.log("Intervals %s and %s overlap and have the same register assigned", i1, i2); + Debug.log(i1.logString(this)); + Debug.log(i2.logString(this)); + } + throw new BailoutException(""); + } + } + // check fixed intervals + for (FixedInterval i2 : fixedIntervals) { + if (i2 == null) { + continue; + } + + Value l1 = i1.location(); + Value l2 = i2.location(); + boolean intersects = i2.intersects(i1); + if (intersects && !isIllegal(l1) && (l1.equals(l2))) { + if (DetailedAsserts.getValue()) { + Debug.log("Intervals %s and %s overlap and have the same register assigned", i1, i2); + Debug.log(i1.logString(this)); + Debug.log(i2.logString(this)); + } + throw new BailoutException(""); + } + } + } + } + } + + class CheckConsumer implements ValueConsumer { + + boolean ok; + FixedInterval curInterval; + + @Override + public void visitValue(Value operand, OperandMode mode, EnumSet<OperandFlag> flags) { + if (isRegister(operand)) { + if (fixedIntervalFor(asRegisterValue(operand)) == curInterval) { + ok = true; + } + } + } + } + + @SuppressWarnings("try") + void verifyNoOopsInFixedIntervals() { + try (Indent indent = Debug.logAndIndent("verifying that no oops are in fixed intervals *")) { + CheckConsumer checkConsumer = new CheckConsumer(); + + TraceInterval otherIntervals; + FixedInterval fixedInts = createFixedUnhandledList(); + // to ensure a walking until the last instruction id, add a dummy interval + // with a high operation id + otherIntervals = new TraceInterval(Value.ILLEGAL, -1); + otherIntervals.addRange(Integer.MAX_VALUE - 2, Integer.MAX_VALUE - 1); + TraceIntervalWalker iw = new TraceIntervalWalker(this, fixedInts, otherIntervals); + + for (AbstractBlockBase<?> block : sortedBlocks) { + List<LIRInstruction> instructions = ir.getLIRforBlock(block); + + for (int j = 0; j < instructions.size(); j++) { + LIRInstruction op = instructions.get(j); + + if (op.hasState()) { + iw.walkBefore(op.id()); + boolean checkLive = true; + + /* + * Make sure none of the fixed registers is live across an oopmap since we + * can't handle that correctly. + */ + if (checkLive) { + for (FixedInterval interval = iw.activeFixedList.getFixed(); interval != FixedInterval.EndMarker; interval = interval.next) { + if (interval.to() > op.id() + 1) { + /* + * This interval is live out of this op so make sure that this + * interval represents some value that's referenced by this op + * either as an input or output. + */ + checkConsumer.curInterval = interval; + checkConsumer.ok = false; + + op.visitEachInput(checkConsumer); + op.visitEachAlive(checkConsumer); + op.visitEachTemp(checkConsumer); + op.visitEachOutput(checkConsumer); + + assert checkConsumer.ok : "fixed intervals should never be live across an oopmap point"; + } + } + } + } + } + } + } + } + + public LIR getLIR() { + return ir; + } + + public FrameMapBuilder getFrameMapBuilder() { + return frameMapBuilder; + } + + public List<? extends AbstractBlockBase<?>> sortedBlocks() { + return sortedBlocks; + } + + public Register[] getRegisters() { + return registers; + } + + public RegisterAllocationConfig getRegisterAllocationConfig() { + return regAllocConfig; + } + + public boolean callKillsRegisters() { + return regAllocConfig.getRegisterConfig().areAllAllocatableRegistersCallerSaved(); + } + + boolean neverSpillConstants() { + return neverSpillConstants; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/lsra/TraceLinearScanAllocationPhase.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2015, 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.graal.lir.alloc.trace.lsra; + +import com.oracle.graal.compiler.common.alloc.RegisterAllocationConfig; +import com.oracle.graal.compiler.common.alloc.TraceBuilder.TraceBuilderResult; +import com.oracle.graal.lir.gen.LIRGeneratorTool.MoveFactory; +import com.oracle.graal.lir.phases.LIRPhase; + +abstract class TraceLinearScanAllocationPhase extends LIRPhase<TraceLinearScanAllocationPhase.TraceLinearScanAllocationContext> { + + static final class TraceLinearScanAllocationContext { + public final MoveFactory spillMoveFactory; + public final RegisterAllocationConfig registerAllocationConfig; + public final TraceBuilderResult<?> traceBuilderResult; + public final TraceLinearScan allocator; + + TraceLinearScanAllocationContext(MoveFactory spillMoveFactory, RegisterAllocationConfig registerAllocationConfig, TraceBuilderResult<?> traceBuilderResult, TraceLinearScan allocator) { + this.spillMoveFactory = spillMoveFactory; + this.registerAllocationConfig = registerAllocationConfig; + this.traceBuilderResult = traceBuilderResult; + this.allocator = allocator; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/lsra/TraceLinearScanAssignLocationsPhase.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2015, 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.graal.lir.alloc.trace.lsra; + +import static com.oracle.graal.compiler.common.GraalOptions.DetailedAsserts; +import static com.oracle.graal.lir.LIRValueUtil.isConstantValue; +import static com.oracle.graal.lir.LIRValueUtil.isStackSlotValue; +import static com.oracle.graal.lir.LIRValueUtil.isVariable; +import static com.oracle.graal.lir.LIRValueUtil.isVirtualStackSlot; +import static com.oracle.graal.lir.alloc.trace.TraceRegisterAllocationPhase.Options.TraceRAshareSpillInformation; +import static jdk.vm.ci.code.ValueUtil.isIllegal; +import static jdk.vm.ci.code.ValueUtil.isRegister; + +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; + +import jdk.vm.ci.code.RegisterValue; +import jdk.vm.ci.code.StackSlot; +import jdk.vm.ci.code.TargetDescription; +import jdk.vm.ci.meta.AllocatableValue; +import jdk.vm.ci.meta.Value; + +import com.oracle.graal.compiler.common.cfg.AbstractBlockBase; +import com.oracle.graal.debug.Debug; +import com.oracle.graal.debug.Indent; +import com.oracle.graal.lir.ConstantValue; +import com.oracle.graal.lir.InstructionValueProcedure; +import com.oracle.graal.lir.LIRFrameState; +import com.oracle.graal.lir.LIRInstruction; +import com.oracle.graal.lir.LIRInstruction.OperandFlag; +import com.oracle.graal.lir.LIRInstruction.OperandMode; +import com.oracle.graal.lir.StandardOp; +import com.oracle.graal.lir.StandardOp.BlockEndOp; +import com.oracle.graal.lir.StandardOp.LabelOp; +import com.oracle.graal.lir.StandardOp.MoveOp; +import com.oracle.graal.lir.StandardOp.ValueMoveOp; +import com.oracle.graal.lir.Variable; +import com.oracle.graal.lir.alloc.trace.ShadowedRegisterValue; +import com.oracle.graal.lir.gen.LIRGenerationResult; +import com.oracle.graal.lir.gen.LIRGeneratorTool.MoveFactory; + +/** + * Specialization of {@link com.oracle.graal.lir.alloc.lsra.LinearScanAssignLocationsPhase} that + * inserts {@link ShadowedRegisterValue}s to describe {@link RegisterValue}s that are also available + * on the {@link StackSlot stack}. + */ +final class TraceLinearScanAssignLocationsPhase extends TraceLinearScanAllocationPhase { + + @Override + protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, + TraceLinearScanAllocationContext context) { + TraceLinearScan allocator = context.allocator; + MoveFactory spillMoveFactory = context.spillMoveFactory; + new Assigner(allocator, spillMoveFactory).assignLocations(); + } + + private static final class Assigner { + private final TraceLinearScan allocator; + private final MoveFactory spillMoveFactory; + + private Assigner(TraceLinearScan allocator, MoveFactory spillMoveFactory) { + this.allocator = allocator; + this.spillMoveFactory = spillMoveFactory; + } + + /** + * Assigns the allocated location for an LIR instruction operand back into the instruction. + * + * @param op current {@link LIRInstruction} + * @param operand an LIR instruction operand + * @param mode the usage mode for {@code operand} by the instruction + * @return the location assigned for the operand + */ + private Value colorLirOperand(LIRInstruction op, Variable operand, OperandMode mode) { + int opId = op.id(); + TraceInterval interval = allocator.intervalFor(operand); + assert interval != null : "interval must exist"; + + if (opId != -1) { + if (DetailedAsserts.getValue()) { + AbstractBlockBase<?> block = allocator.blockForId(opId); + if (block.getSuccessorCount() <= 1 && opId == allocator.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. + */ + LIRInstruction instr = allocator.getLIR().getLIRforBlock(block).get(allocator.getLIR().getLIRforBlock(block).size() - 1); + if (instr instanceof StandardOp.JumpOp) { + if (allocator.getBlockData(block).liveOut.get(allocator.operandNumber(operand))) { + 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); + } + } + } + } + + /* + * Operands are not changed when an interval is split during allocation, so search + * the right interval here. + */ + interval = allocator.splitChildAtOpId(interval, opId, mode); + } + + if (isIllegal(interval.location()) && interval.canMaterialize()) { + assert mode != OperandMode.DEF; + return new ConstantValue(interval.kind(), interval.getMaterializedValue()); + } + return interval.location(); + } + + /** + * @param op + * @param operand + * @param valueMode + * @param flags + * @see InstructionValueProcedure#doValue(LIRInstruction, Value, OperandMode, EnumSet) + */ + private Value debugInfoProcedure(LIRInstruction op, Value operand, OperandMode valueMode, EnumSet<OperandFlag> flags) { + if (isVirtualStackSlot(operand)) { + return operand; + } + int tempOpId = op.id(); + OperandMode mode = OperandMode.USE; + AbstractBlockBase<?> block = allocator.blockForId(tempOpId); + if (block.getSuccessorCount() == 1 && tempOpId == allocator.getLastLirInstructionId(block)) { + /* + * Generating debug information for the last instruction of a block. If this + * instruction is a branch, spill moves are inserted before this branch and so the + * wrong operand would be returned (spill moves at block boundaries are not + * considered in the live ranges of intervals). + * + * Solution: use the first opId of the branch target block instead. + */ + final LIRInstruction instr = allocator.getLIR().getLIRforBlock(block).get(allocator.getLIR().getLIRforBlock(block).size() - 1); + if (instr instanceof StandardOp.JumpOp) { + if (allocator.getBlockData(block).liveOut.get(allocator.operandNumber(operand))) { + tempOpId = allocator.getFirstLirInstructionId(block.getSuccessors().iterator().next()); + mode = OperandMode.DEF; + } + } + } + + /* + * Get current location of operand. The operand must be live because debug information + * is considered when building the intervals if the interval is not live, + * colorLirOperand will cause an assert on failure. + */ + Value result = colorLirOperand(op, (Variable) operand, mode); + assert !allocator.hasCall(tempOpId) || isStackSlotValue(result) || isConstantValue(result) || !allocator.isCallerSave(result) : "cannot have caller-save register operands at calls"; + return result; + } + + private void computeDebugInfo(final LIRInstruction op, LIRFrameState info) { + info.forEachState(op, this::debugInfoProcedure); + } + + private void assignLocations(List<LIRInstruction> instructions) { + int numInst = instructions.size(); + boolean hasDead = false; + + for (int j = 0; j < numInst; j++) { + final LIRInstruction op = instructions.get(j); + if (op == null) { + /* + * this can happen when spill-moves are removed in eliminateSpillMoves + */ + hasDead = true; + } else if (assignLocations(op, instructions, j)) { + hasDead = true; + } + } + + if (hasDead) { + // Remove null values from the list. + instructions.removeAll(Collections.singleton(null)); + } + } + + /** + * Assigns the operand of an {@link LIRInstruction}. + * + * @param op The {@link LIRInstruction} that should be colored. + * @param j The index of {@code op} in the {@code instructions} list. + * @param instructions The instructions of the current block. + * @return {@code true} if the instruction was deleted. + */ + private boolean assignLocations(LIRInstruction op, List<LIRInstruction> instructions, int j) { + assert op != null && instructions.get(j) == op; + if (TraceRAshareSpillInformation.getValue()) { + if (op instanceof BlockEndOp) { + ((BlockEndOp) op).forEachOutgoingValue(colorOutgoingIncomingValues); + } else if (op instanceof LabelOp) { + ((LabelOp) op).forEachIncomingValue(colorOutgoingIncomingValues); + } + } + + InstructionValueProcedure assignProc = (inst, operand, mode, flags) -> isVariable(operand) ? colorLirOperand(inst, (Variable) operand, mode) : operand; + // remove useless moves + if (op instanceof MoveOp) { + AllocatableValue result = ((MoveOp) op).getResult(); + if (isVariable(result) && allocator.isMaterialized(result, op.id(), OperandMode.DEF)) { + /* + * This happens if a materializable interval is originally not spilled but then + * kicked out in LinearScanWalker.splitForSpilling(). When kicking out such an + * interval this move operation was already generated. + */ + instructions.set(j, null); + return true; + } + } + + op.forEachInput(assignProc); + op.forEachAlive(assignProc); + op.forEachTemp(assignProc); + op.forEachOutput(assignProc); + + // compute reference map and debug information + op.forEachState((inst, state) -> computeDebugInfo(inst, state)); + + // remove useless moves + if (op instanceof ValueMoveOp) { + ValueMoveOp move = (ValueMoveOp) op; + if (move.getInput().equals(move.getResult())) { + instructions.set(j, null); + return true; + } + if (isStackSlotValue(move.getInput()) && isStackSlotValue(move.getResult())) { + // rewrite stack to stack moves + instructions.set(j, spillMoveFactory.createStackMove(move.getResult(), move.getInput())); + return true; + } + } + return false; + } + + @SuppressWarnings("try") + private void assignLocations() { + try (Indent indent = Debug.logAndIndent("assign locations")) { + for (AbstractBlockBase<?> block : allocator.sortedBlocks()) { + try (Indent indent2 = Debug.logAndIndent("assign locations in block B%d", block.getId())) { + assignLocations(allocator.getLIR().getLIRforBlock(block)); + } + } + } + } + + private InstructionValueProcedure colorOutgoingIncomingValues = new InstructionValueProcedure() { + + public Value doValue(LIRInstruction instruction, Value value, OperandMode mode, EnumSet<OperandFlag> flags) { + if (isVariable(value)) { + TraceInterval interval = allocator.intervalFor(value); + assert interval != null : "interval must exist"; + interval = allocator.splitChildAtOpId(interval, instruction.id(), mode); + + if (interval.inMemoryAt(instruction.id()) && isRegister(interval.location())) { + return new ShadowedRegisterValue((RegisterValue) interval.location(), interval.spillSlot()); + } + } + return value; + } + }; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/lsra/TraceLinearScanEliminateSpillMovePhase.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2015, 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.graal.lir.alloc.trace.lsra; + +import static com.oracle.graal.compiler.common.GraalOptions.DetailedAsserts; +import static com.oracle.graal.lir.LIRValueUtil.isStackSlotValue; +import static com.oracle.graal.lir.LIRValueUtil.isVariable; +import static jdk.vm.ci.code.ValueUtil.isRegister; + +import java.util.List; + +import jdk.vm.ci.code.TargetDescription; +import jdk.vm.ci.meta.AllocatableValue; + +import com.oracle.graal.compiler.common.alloc.TraceBuilder.TraceBuilderResult; +import com.oracle.graal.compiler.common.cfg.AbstractBlockBase; +import com.oracle.graal.debug.Debug; +import com.oracle.graal.debug.Indent; +import com.oracle.graal.lir.LIRInsertionBuffer; +import com.oracle.graal.lir.LIRInstruction; +import com.oracle.graal.lir.LIRInstruction.OperandMode; +import com.oracle.graal.lir.StandardOp.LoadConstantOp; +import com.oracle.graal.lir.StandardOp.MoveOp; +import com.oracle.graal.lir.StandardOp.ValueMoveOp; +import com.oracle.graal.lir.alloc.trace.lsra.TraceInterval.SpillState; +import com.oracle.graal.lir.alloc.trace.lsra.TraceLinearScan.IntervalPredicate; +import com.oracle.graal.lir.gen.LIRGenerationResult; + +final class TraceLinearScanEliminateSpillMovePhase extends TraceLinearScanAllocationPhase { + + private static final IntervalPredicate spilledIntervals = new TraceLinearScan.IntervalPredicate() { + + @Override + public boolean apply(TraceInterval i) { + return i.isSplitParent() && SpillState.IN_MEMORY.contains(i.spillState()); + } + }; + + @Override + protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, + TraceLinearScanAllocationContext context) { + TraceBuilderResult<?> traceBuilderResult = context.traceBuilderResult; + TraceLinearScan allocator = context.allocator; + boolean shouldEliminateSpillMoves = shouldEliminateSpillMoves(traceBuilderResult, allocator); + eliminateSpillMoves(allocator, shouldEliminateSpillMoves, traceBuilderResult); + } + + private static boolean shouldEliminateSpillMoves(TraceBuilderResult<?> traceBuilderResult, TraceLinearScan allocator) { + return !traceBuilderResult.incomingSideEdges(traceBuilderResult.getTraceForBlock(allocator.sortedBlocks().get(0))); + } + + // called once before assignment of register numbers + @SuppressWarnings("try") + private static void eliminateSpillMoves(TraceLinearScan allocator, boolean shouldEliminateSpillMoves, TraceBuilderResult<?> traceBuilderResult) { + try (Indent indent = Debug.logAndIndent("Eliminating unnecessary spill moves: Trace%d", traceBuilderResult.getTraceForBlock(allocator.sortedBlocks().get(0)))) { + allocator.sortIntervalsBySpillPos(); + + /* + * collect all intervals that must be stored after their definition. The list is sorted + * by Interval.spillDefinitionPos. + */ + TraceInterval interval = allocator.createUnhandledListBySpillPos(spilledIntervals); + if (DetailedAsserts.getValue()) { + checkIntervals(interval); + } + if (Debug.isLogEnabled()) { + try (Indent indent2 = Debug.logAndIndent("Sorted intervals")) { + for (TraceInterval i = interval; i != null; i = i.next) { + Debug.log("%5d: %s", i.spillDefinitionPos(), i); + } + } + } + + LIRInsertionBuffer insertionBuffer = new LIRInsertionBuffer(); + for (AbstractBlockBase<?> block : allocator.sortedBlocks()) { + try (Indent indent1 = Debug.logAndIndent("Handle %s", block)) { + List<LIRInstruction> instructions = allocator.getLIR().getLIRforBlock(block); + int numInst = instructions.size(); + + int lastOpId = -1; + // iterate all instructions of the block. + for (int j = 0; j < numInst; j++) { + LIRInstruction op = instructions.get(j); + int opId = op.id(); + try (Indent indent2 = Debug.logAndIndent("%5d %s", opId, op)) { + + if (opId == -1) { + MoveOp move = (MoveOp) op; + /* + * Remove move from register to stack if the stack slot is + * guaranteed to be correct. Only moves that have been inserted by + * LinearScan can be removed. + */ + if (shouldEliminateSpillMoves && canEliminateSpillMove(allocator, block, move, lastOpId)) { + /* + * Move target is a stack slot that is always correct, so + * eliminate instruction. + */ + if (Debug.isLogEnabled()) { + if (move instanceof ValueMoveOp) { + ValueMoveOp vmove = (ValueMoveOp) move; + Debug.log("eliminating move from interval %d (%s) to %d (%s) in block %s", allocator.operandNumber(vmove.getInput()), vmove.getInput(), + allocator.operandNumber(vmove.getResult()), vmove.getResult(), block); + } else { + LoadConstantOp load = (LoadConstantOp) move; + Debug.log("eliminating constant load from %s to %d (%s) in block %s", load.getConstant(), allocator.operandNumber(load.getResult()), load.getResult(), + block); + } + } + + // null-instructions are deleted by assignRegNum + instructions.set(j, null); + } + + } else { + lastOpId = opId; + /* + * Insert move from register to stack just after the beginning of + * the interval. + */ + // assert interval == TraceInterval.EndMarker || + // interval.spillDefinitionPos() >= opId : "invalid order"; + assert interval == TraceInterval.EndMarker || (interval.isSplitParent() && SpillState.IN_MEMORY.contains(interval.spillState())) : "invalid interval"; + + while (interval != TraceInterval.EndMarker && interval.spillDefinitionPos() == opId) { + Debug.log("handle %s", interval); + if (!interval.canMaterialize() && interval.spillState() != SpillState.StartInMemory) { + + AllocatableValue fromLocation = interval.getSplitChildAtOpId(opId, OperandMode.DEF, allocator).location(); + AllocatableValue toLocation = TraceLinearScan.canonicalSpillOpr(interval); + if (!fromLocation.equals(toLocation)) { + + if (!insertionBuffer.initialized()) { + /* + * prepare insertion buffer (appended when all + * instructions in the block are processed) + */ + insertionBuffer.init(instructions); + } + + assert isRegister(fromLocation) : "from operand must be a register but is: " + fromLocation + " toLocation=" + toLocation + " spillState=" + + interval.spillState(); + assert isStackSlotValue(toLocation) : "to operand must be a stack slot"; + + LIRInstruction move = allocator.getSpillMoveFactory().createMove(toLocation, fromLocation); + insertionBuffer.append(j + 1, move); + + if (Debug.isLogEnabled()) { + Debug.log("inserting move after definition of interval %d to stack slot %s at opId %d", interval.operandNumber, interval.spillSlot(), opId); + } + } + } + interval = interval.next; + } + } + } + } // end of instruction iteration + + if (insertionBuffer.initialized()) { + insertionBuffer.finish(); + } + } + } // end of block iteration + + assert interval == TraceInterval.EndMarker : "missed an interval"; + } + } + + /** + * @param allocator + * @param block The block {@code move} is located in. + * @param move Spill move. + * @param lastOpId The id of last "normal" instruction before the spill move. (Spill moves have + * no valid opId but -1.) + */ + private static boolean canEliminateSpillMove(TraceLinearScan allocator, AbstractBlockBase<?> block, MoveOp move, int lastOpId) { + assert ((LIRInstruction) move).id() == -1 : "Not a spill move: " + move; + assert isVariable(move.getResult()) : "LinearScan inserts only moves to variables: " + move; + assert lastOpId >= 0 : "Invalid lastOpId: " + lastOpId; + + TraceInterval curInterval = allocator.intervalFor(move.getResult()); + + if (!isRegister(curInterval.location()) && curInterval.inMemoryAt(lastOpId) && !isPhiResolutionMove(allocator, move)) { + /* Phi resolution moves cannot be removed because they define the value. */ + // TODO (je) check if the comment is still valid! + assert isStackSlotValue(curInterval.location()) : "Not a stack slot: " + curInterval.location(); + return true; + } + return false; + } + + /** + * Checks if a (spill or split) move is a Phi resolution move. + * + * A spill or split move connects a split parent or a split child with another split child. + * Therefore the destination of the move is always a split child. Phi resolution moves look like + * spill moves (i.e. {@link LIRInstruction#id() id} is {@code 0}, but they define a new + * variable. As a result the destination interval is a split parent. + */ + private static boolean isPhiResolutionMove(TraceLinearScan allocator, MoveOp move) { + assert ((LIRInstruction) move).id() == -1 : "Not a spill move: " + move; + TraceInterval curInterval = allocator.intervalFor(move.getResult()); + return curInterval.isSplitParent(); + } + + private static void checkIntervals(TraceInterval interval) { + TraceInterval prev = null; + TraceInterval temp = interval; + while (temp != TraceInterval.EndMarker) { + assert temp.spillDefinitionPos() >= 0 : "invalid spill definition pos"; + if (prev != null) { + // assert temp.from() >= prev.from() : "intervals not sorted"; + assert temp.spillDefinitionPos() >= prev.spillDefinitionPos() : "when intervals are sorted by from : then they must also be sorted by spillDefinitionPos"; + } + + assert temp.spillSlot() != null || temp.canMaterialize() : "interval has no spill slot assigned"; + assert temp.spillDefinitionPos() >= temp.from() : "invalid order"; + // assert temp.spillDefinitionPos() <= temp.from() + 2 : + // "only intervals defined once at their start-pos can be optimized"; + + if (Debug.isLogEnabled()) { + Debug.log("interval %d (from %d to %d) must be stored at %d", temp.operandNumber, temp.from(), temp.to(), temp.spillDefinitionPos()); + } + + prev = temp; + temp = temp.next; + } + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/lsra/TraceLinearScanLifetimeAnalysisPhase.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,678 @@ +/* + * Copyright (c) 2015, 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.graal.lir.alloc.trace.lsra; + +import static com.oracle.graal.lir.LIRValueUtil.asVariable; +import static com.oracle.graal.lir.LIRValueUtil.isStackSlotValue; +import static com.oracle.graal.lir.LIRValueUtil.isVariable; +import static com.oracle.graal.lir.alloc.trace.TraceRegisterAllocationPhase.Options.TraceRAshareSpillInformation; +import static com.oracle.graal.lir.alloc.trace.TraceRegisterAllocationPhase.Options.TraceRAuseInterTraceHints; +import static com.oracle.graal.lir.alloc.trace.TraceUtil.asShadowedRegisterValue; +import static com.oracle.graal.lir.alloc.trace.TraceUtil.isShadowedRegisterValue; +import static com.oracle.graal.lir.alloc.trace.lsra.TraceLinearScan.isVariableOrRegister; +import static jdk.vm.ci.code.ValueUtil.asRegisterValue; +import static jdk.vm.ci.code.ValueUtil.asStackSlot; +import static jdk.vm.ci.code.ValueUtil.isRegister; +import static jdk.vm.ci.code.ValueUtil.isStackSlot; + +import java.util.BitSet; +import java.util.EnumSet; +import java.util.List; + +import jdk.vm.ci.code.BailoutException; +import jdk.vm.ci.code.Register; +import jdk.vm.ci.code.RegisterValue; +import jdk.vm.ci.code.TargetDescription; +import jdk.vm.ci.common.JVMCIError; +import jdk.vm.ci.meta.AllocatableValue; +import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.LIRKind; +import jdk.vm.ci.meta.Value; + +import com.oracle.graal.compiler.common.alloc.ComputeBlockOrder; +import com.oracle.graal.compiler.common.alloc.TraceBuilder.TraceBuilderResult; +import com.oracle.graal.compiler.common.cfg.AbstractBlockBase; +import com.oracle.graal.debug.Debug; +import com.oracle.graal.debug.Indent; +import com.oracle.graal.lir.InstructionValueConsumer; +import com.oracle.graal.lir.LIR; +import com.oracle.graal.lir.LIRInstruction; +import com.oracle.graal.lir.LIRInstruction.OperandFlag; +import com.oracle.graal.lir.LIRInstruction.OperandMode; +import com.oracle.graal.lir.LIRValueUtil; +import com.oracle.graal.lir.StandardOp.BlockEndOp; +import com.oracle.graal.lir.StandardOp.LabelOp; +import com.oracle.graal.lir.StandardOp.LoadConstantOp; +import com.oracle.graal.lir.StandardOp.ValueMoveOp; +import com.oracle.graal.lir.ValueConsumer; +import com.oracle.graal.lir.Variable; +import com.oracle.graal.lir.alloc.trace.ShadowedRegisterValue; +import com.oracle.graal.lir.alloc.trace.lsra.TraceInterval.RegisterPriority; +import com.oracle.graal.lir.alloc.trace.lsra.TraceInterval.SpillState; +import com.oracle.graal.lir.gen.LIRGenerationResult; +import com.oracle.graal.lir.ssi.SSIUtil; + +final class TraceLinearScanLifetimeAnalysisPhase extends TraceLinearScanAllocationPhase { + + @Override + protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, + TraceLinearScanAllocationContext context) { + TraceBuilderResult<?> traceBuilderResult = context.traceBuilderResult; + TraceLinearScan allocator = context.allocator; + new Analyser(allocator, traceBuilderResult).analyze(); + } + + private static final class Analyser { + private static final int DUMP_DURING_ANALYSIS_LEVEL = 4; + private final TraceLinearScan allocator; + private final TraceBuilderResult<?> traceBuilderResult; + + /** + * @param linearScan + * @param traceBuilderResult + */ + private Analyser(TraceLinearScan linearScan, TraceBuilderResult<?> traceBuilderResult) { + allocator = linearScan; + this.traceBuilderResult = traceBuilderResult; + } + + private void analyze() { + numberInstructions(); + allocator.printLir("Before register allocation", true); + buildIntervals(); + } + + private boolean sameTrace(AbstractBlockBase<?> a, AbstractBlockBase<?> b) { + return traceBuilderResult.getTraceForBlock(b) == traceBuilderResult.getTraceForBlock(a); + } + + private boolean isAllocatedOrCurrent(AbstractBlockBase<?> currentBlock, AbstractBlockBase<?> other) { + return traceBuilderResult.getTraceForBlock(other) <= traceBuilderResult.getTraceForBlock(currentBlock); + } + + private static void setHint(final LIRInstruction op, TraceInterval to, IntervalHint from) { + IntervalHint currentHint = to.locationHint(false); + if (currentHint == null) { + /* + * Update hint if there was none or if the hint interval starts after the hinted + * interval. + */ + to.setLocationHint(from); + if (Debug.isLogEnabled()) { + Debug.log("operation at opId %d: added hint from interval %s to %s", op.id(), from, to); + } + } + } + + /** + * Numbers all instructions in all blocks. The numbering follows the + * {@linkplain ComputeBlockOrder linear scan order}. + */ + private void numberInstructions() { + + allocator.initIntervals(); + + ValueConsumer setVariableConsumer = (value, mode, flags) -> { + if (isVariable(value)) { + allocator.getOrCreateInterval(asVariable(value)); + } + }; + + // Assign IDs to LIR nodes and build a mapping, lirOps, from ID to LIRInstruction node. + int numInstructions = 0; + for (AbstractBlockBase<?> block : allocator.sortedBlocks()) { + numInstructions += allocator.getLIR().getLIRforBlock(block).size(); + } + + // initialize with correct length + allocator.initOpIdMaps(numInstructions); + + int opId = 0; + int index = 0; + for (AbstractBlockBase<?> block : allocator.sortedBlocks()) { + allocator.initBlockData(block); + + List<LIRInstruction> instructions = allocator.getLIR().getLIRforBlock(block); + + int numInst = instructions.size(); + for (int j = 0; j < numInst; j++) { + LIRInstruction op = instructions.get(j); + op.setId(opId); + + allocator.putOpIdMaps(index, op, block); + assert allocator.instructionForId(opId) == op : "must match"; + + op.visitEachTemp(setVariableConsumer); + op.visitEachOutput(setVariableConsumer); + + index++; + opId += 2; // numbering of lirOps by two + } + } + assert index == numInstructions : "must match"; + assert (index << 1) == opId : "must match: " + (index << 1); + } + + private void addUse(AllocatableValue operand, int from, int to, RegisterPriority registerPriority, LIRKind kind) { + if (!allocator.isProcessed(operand)) { + return; + } + if (isRegister(operand)) { + addFixedUse(asRegisterValue(operand), from, to); + } else { + assert isVariable(operand) : operand; + addVariableUse(asVariable(operand), from, to, registerPriority, kind); + } + } + + private void addFixedUse(RegisterValue reg, int from, int to) { + FixedInterval interval = allocator.getOrCreateFixedInterval(reg); + interval.addRange(from, to); + if (Debug.isLogEnabled()) { + Debug.log("add fixed use: %s, at %d", interval, to); + } + } + + private void addVariableUse(Variable operand, int from, int to, RegisterPriority registerPriority, LIRKind kind) { + TraceInterval interval = allocator.getOrCreateInterval(operand); + + if (!kind.equals(LIRKind.Illegal)) { + interval.setKind(kind); + } + + interval.addRange(from, to); + + // Register use position at even instruction id. + interval.addUsePos(to & ~1, registerPriority); + + if (Debug.isLogEnabled()) { + Debug.log("add use: %s, at %d (%s)", interval, to, registerPriority.name()); + } + } + + private void addTemp(AllocatableValue operand, int tempPos, RegisterPriority registerPriority, LIRKind kind) { + if (!allocator.isProcessed(operand)) { + return; + } + if (isRegister(operand)) { + addFixedTemp(asRegisterValue(operand), tempPos); + } else { + assert isVariable(operand) : operand; + addVariableTemp(asVariable(operand), tempPos, registerPriority, kind); + } + } + + private void addFixedTemp(RegisterValue reg, int tempPos) { + FixedInterval interval = allocator.getOrCreateFixedInterval(reg); + interval.addRange(tempPos, tempPos + 1); + if (Debug.isLogEnabled()) { + Debug.log("add fixed temp: %s, at %d", interval, tempPos); + } + } + + private void addVariableTemp(Variable operand, int tempPos, RegisterPriority registerPriority, LIRKind kind) { + TraceInterval interval = allocator.getOrCreateInterval(operand); + + if (!kind.equals(LIRKind.Illegal)) { + interval.setKind(kind); + } + + if (interval.isEmpty()) { + interval.addRange(tempPos, tempPos + 1); + } else if (interval.from() > tempPos) { + interval.setFrom(tempPos); + } + + interval.addUsePos(tempPos, registerPriority); + interval.addMaterializationValue(null); + + if (Debug.isLogEnabled()) { + Debug.log("add temp: %s tempPos %d (%s)", interval, tempPos, RegisterPriority.MustHaveRegister.name()); + } + } + + private void addDef(AllocatableValue operand, LIRInstruction op, RegisterPriority registerPriority, LIRKind kind) { + if (!allocator.isProcessed(operand)) { + return; + } + if (isRegister(operand)) { + addFixedDef(asRegisterValue(operand), op); + } else { + assert isVariable(operand) : operand; + addVariableDef(asVariable(operand), op, registerPriority, kind); + } + } + + private void addFixedDef(RegisterValue reg, LIRInstruction op) { + FixedInterval interval = allocator.getOrCreateFixedInterval(reg); + int defPos = op.id(); + if (interval.from() <= defPos) { + /* + * Update the starting point (when a range is first created for a use, its start is + * the beginning of the current block until a def is encountered). + */ + interval.setFrom(defPos); + + } else { + /* + * Dead value - make vacuous interval also add register priority for dead intervals + */ + interval.addRange(defPos, defPos + 1); + if (Debug.isLogEnabled()) { + Debug.log("Warning: def of operand %s at %d occurs without use", reg, defPos); + } + } + if (Debug.isLogEnabled()) { + Debug.log("add fixed def: %s, at %d", interval, defPos); + } + } + + private void addVariableDef(Variable operand, LIRInstruction op, RegisterPriority registerPriority, LIRKind kind) { + int defPos = op.id(); + + TraceInterval interval = allocator.getOrCreateInterval(operand); + + if (!kind.equals(LIRKind.Illegal)) { + interval.setKind(kind); + } + + if (interval.isEmpty()) { + /* + * Dead value - make vacuous interval also add register priority for dead intervals + */ + interval.addRange(defPos, defPos + 1); + interval.addUsePos(defPos, registerPriority); + if (Debug.isLogEnabled()) { + Debug.log("Warning: def of operand %s at %d occurs without use", operand, defPos); + } + } else { + /* + * Update the starting point (when a range is first created for a use, its start is + * the beginning of the current block until a def is encountered). + */ + interval.setFrom(defPos); + interval.addUsePos(defPos, registerPriority); + } + + changeSpillDefinitionPos(op, operand, interval, defPos); + if (registerPriority == RegisterPriority.None && interval.spillState().ordinal() <= SpillState.StartInMemory.ordinal() && isStackSlot(operand)) { + // detection of method-parameters and roundfp-results + interval.setSpillState(SpillState.StartInMemory); + } + interval.addMaterializationValue(getMaterializedValue(op, operand, interval)); + + if (Debug.isLogEnabled()) { + Debug.log("add def: %s defPos %d (%s)", interval, defPos, registerPriority.name()); + } + } + + private void addRegisterHint(final LIRInstruction op, final Value targetValue, OperandMode mode, EnumSet<OperandFlag> flags, final boolean hintAtDef) { + if (flags.contains(OperandFlag.HINT) && TraceLinearScan.isVariableOrRegister(targetValue)) { + + op.forEachRegisterHint(targetValue, mode, (registerHint, valueMode, valueFlags) -> { + if (TraceLinearScan.isVariableOrRegister(registerHint)) { + /* + * TODO (je): clean up + */ + final AllocatableValue fromValue; + final AllocatableValue toValue; + /* hints always point from def to use */ + if (hintAtDef) { + fromValue = (AllocatableValue) registerHint; + toValue = (AllocatableValue) targetValue; + } else { + fromValue = (AllocatableValue) targetValue; + toValue = (AllocatableValue) registerHint; + } + Debug.log("addRegisterHint %s to %s", fromValue, toValue); + final TraceInterval to; + final IntervalHint from; + if (isRegister(toValue)) { + if (isRegister(fromValue)) { + // fixed to fixed move + return null; + } + from = getIntervalHint(toValue); + to = allocator.getOrCreateInterval(fromValue); + } else { + to = allocator.getOrCreateInterval(toValue); + from = getIntervalHint(fromValue); + } + + to.setLocationHint(from); + if (Debug.isLogEnabled()) { + Debug.log("operation at opId %d: added hint from interval %s to %s", op.id(), from, to); + } + + return registerHint; + } + return null; + }); + } + } + + private IntervalHint getIntervalHint(AllocatableValue from) { + if (isRegister(from)) { + return allocator.getOrCreateFixedInterval(asRegisterValue(from)); + } + return allocator.getOrCreateInterval(from); + } + + /** + * Eliminates moves from register to stack if the stack slot is known to be correct. + * + * @param op + * @param operand + */ + private void changeSpillDefinitionPos(LIRInstruction op, AllocatableValue operand, TraceInterval interval, int defPos) { + assert interval.isSplitParent() : "can only be called for split parents"; + + switch (interval.spillState()) { + case NoDefinitionFound: + // assert interval.spillDefinitionPos() == -1 : "must no be set before"; + interval.setSpillDefinitionPos(defPos); + if (!(op instanceof LabelOp)) { + // Do not update state for labels. This will be done afterwards. + interval.setSpillState(SpillState.NoSpillStore); + } + break; + + case NoSpillStore: + assert defPos <= interval.spillDefinitionPos() : "positions are processed in reverse order when intervals are created"; + if (defPos < interval.spillDefinitionPos() - 2) { + /* + * Second definition found, so no spill optimization possible for this + * interval. + */ + interval.setSpillState(SpillState.NoOptimization); + } else { + // two consecutive definitions (because of two-operand LIR form) + assert allocator.blockForId(defPos) == allocator.blockForId(interval.spillDefinitionPos()) : "block must be equal"; + } + break; + + case NoOptimization: + // nothing to do + break; + + default: + throw new BailoutException("other states not allowed at this time"); + } + } + + private static boolean optimizeMethodArgument(Value value) { + /* + * Object method arguments that are passed on the stack are currently not optimized + * because this requires that the runtime visits method arguments during stack walking. + */ + return isStackSlot(value) && asStackSlot(value).isInCallerFrame() && value.getLIRKind().isValue(); + } + + /** + * Determines the register priority for an instruction's output/result operand. + */ + private static RegisterPriority registerPriorityOfOutputOperand(LIRInstruction op) { + if (op instanceof LabelOp) { + // skip method header + return RegisterPriority.None; + } + if (op instanceof ValueMoveOp) { + ValueMoveOp move = (ValueMoveOp) op; + if (optimizeMethodArgument(move.getInput())) { + return RegisterPriority.None; + } + } + + // all other operands require a register + return RegisterPriority.MustHaveRegister; + } + + /** + * Determines the priority which with an instruction's input operand will be allocated a + * register. + */ + private static RegisterPriority registerPriorityOfInputOperand(EnumSet<OperandFlag> flags) { + if (flags.contains(OperandFlag.OUTGOING)) { + return RegisterPriority.None; + } + if (flags.contains(OperandFlag.STACK)) { + return RegisterPriority.ShouldHaveRegister; + } + // all other operands require a register + return RegisterPriority.MustHaveRegister; + } + + @SuppressWarnings("try") + private void buildIntervals() { + + try (Indent indent = Debug.logAndIndent("build intervals")) { + InstructionValueConsumer outputConsumer = (op, operand, mode, flags) -> { + if (TraceLinearScan.isVariableOrRegister(operand)) { + addDef((AllocatableValue) operand, op, registerPriorityOfOutputOperand(op), operand.getLIRKind()); + addRegisterHint(op, operand, mode, flags, true); + } + }; + + InstructionValueConsumer tempConsumer = (op, operand, mode, flags) -> { + if (TraceLinearScan.isVariableOrRegister(operand)) { + addTemp((AllocatableValue) operand, op.id(), RegisterPriority.MustHaveRegister, operand.getLIRKind()); + addRegisterHint(op, operand, mode, flags, false); + } + }; + + InstructionValueConsumer aliveConsumer = (op, operand, mode, flags) -> { + if (TraceLinearScan.isVariableOrRegister(operand)) { + RegisterPriority p = registerPriorityOfInputOperand(flags); + int opId = op.id(); + int blockFrom = allocator.getFirstLirInstructionId((allocator.blockForId(opId))); + addUse((AllocatableValue) operand, blockFrom, opId + 1, p, operand.getLIRKind()); + addRegisterHint(op, operand, mode, flags, false); + } + }; + + InstructionValueConsumer inputConsumer = (op, operand, mode, flags) -> { + if (TraceLinearScan.isVariableOrRegister(operand)) { + int opId = op.id(); + RegisterPriority p = registerPriorityOfInputOperand(flags); + int blockFrom = allocator.getFirstLirInstructionId((allocator.blockForId(opId))); + addUse((AllocatableValue) operand, blockFrom, opId, p, operand.getLIRKind()); + addRegisterHint(op, operand, mode, flags, false); + } + }; + + InstructionValueConsumer stateProc = (op, operand, mode, flags) -> { + if (TraceLinearScan.isVariableOrRegister(operand)) { + int opId = op.id(); + int blockFrom = allocator.getFirstLirInstructionId((allocator.blockForId(opId))); + addUse((AllocatableValue) operand, blockFrom, opId + 1, RegisterPriority.None, operand.getLIRKind()); + } + }; + + // create a list with all caller-save registers (cpu, fpu, xmm) + Register[] callerSaveRegs = allocator.getRegisterAllocationConfig().getRegisterConfig().getCallerSaveRegisters(); + + // iterate all blocks in reverse order + for (int i = allocator.blockCount() - 1; i >= 0; i--) { + + AbstractBlockBase<?> block = allocator.blockAt(i); + // TODO (je) make empty bitset - remove + allocator.getBlockData(block).liveIn = new BitSet(); + allocator.getBlockData(block).liveOut = new BitSet(); + try (Indent indent2 = Debug.logAndIndent("handle block %d", block.getId())) { + + List<LIRInstruction> instructions = allocator.getLIR().getLIRforBlock(block); + + /* + * Iterate all instructions of the block in reverse order. definitions of + * intervals are processed before uses. + */ + for (int j = instructions.size() - 1; j >= 0; j--) { + final LIRInstruction op = instructions.get(j); + final int opId = op.id(); + + try (Indent indent3 = Debug.logAndIndent("handle inst %d: %s", opId, op)) { + + // add a temp range for each register if operation destroys + // caller-save registers + if (op.destroysCallerSavedRegisters()) { + for (Register r : callerSaveRegs) { + if (allocator.attributes(r).isAllocatable()) { + addTemp(r.asValue(), opId, RegisterPriority.None, LIRKind.Illegal); + } + } + if (Debug.isLogEnabled()) { + Debug.log("operation destroys all caller-save registers"); + } + } + + op.visitEachOutput(outputConsumer); + op.visitEachTemp(tempConsumer); + op.visitEachAlive(aliveConsumer); + op.visitEachInput(inputConsumer); + + /* + * Add uses of live locals from interpreter's point of view for + * proper debug information generation. Treat these operands as temp + * values (if the live range is extended to a call site, the value + * would be in a register at the call otherwise). + */ + op.visitEachState(stateProc); + } + + } // end of instruction iteration + } + if (Debug.isDumpEnabled(DUMP_DURING_ANALYSIS_LEVEL)) { + allocator.printIntervals("After Block " + block); + } + } // end of block iteration + + // fix spill state for phi/sigma intervals + for (TraceInterval interval : allocator.intervals()) { + if (interval != null && interval.spillState().equals(SpillState.NoDefinitionFound) && interval.spillDefinitionPos() != -1) { + // there was a definition in a phi/sigma + interval.setSpillState(SpillState.NoSpillStore); + } + } + if (TraceRAuseInterTraceHints.getValue()) { + addInterTraceHints(); + } + /* + * Add the range [-1, 0] to all fixed intervals. the register allocator need not + * handle unhandled fixed intervals. + */ + for (FixedInterval interval : allocator.fixedIntervals()) { + if (interval != null) { + /* We use [-1, 0] to avoid intersection with incoming values. */ + interval.addRange(-1, 0); + } + } + } + } + + private void addInterTraceHints() { + // set hints for phi/sigma intervals + LIR lir = allocator.getLIR(); + for (AbstractBlockBase<?> block : allocator.sortedBlocks()) { + LabelOp label = SSIUtil.incoming(lir, block); + for (AbstractBlockBase<?> pred : block.getPredecessors()) { + if (isAllocatedOrCurrent(block, pred)) { + BlockEndOp outgoing = SSIUtil.outgoing(lir, pred); + for (int i = 0; i < outgoing.getOutgoingSize(); i++) { + Value toValue = label.getIncomingValue(i); + assert !isShadowedRegisterValue(toValue) : "Shadowed Registers are not allowed here: " + toValue; + if (isVariable(toValue)) { + Value fromValue = outgoing.getOutgoingValue(i); + assert sameTrace(block, pred) || !isVariable(fromValue) : "Unallocated variable: " + fromValue; + if (!LIRValueUtil.isConstantValue(fromValue)) { + addInterTraceHint(label, (AllocatableValue) toValue, fromValue); + } + } + } + } + } + } + } + + private void addInterTraceHint(LabelOp label, AllocatableValue toValue, Value fromValue) { + assert isVariable(toValue) : "Wrong toValue: " + toValue; + assert isRegister(fromValue) || isVariable(fromValue) || isStackSlotValue(fromValue) || isShadowedRegisterValue(fromValue) : "Wrong fromValue: " + fromValue; + if (isVariableOrRegister(fromValue)) { + TraceInterval to = allocator.getOrCreateInterval(toValue); + IntervalHint from = getIntervalHint((AllocatableValue) fromValue); + setHint(label, to, from); + } else if (isStackSlotValue(fromValue)) { + TraceInterval to = allocator.getOrCreateInterval(toValue); + to.setSpillSlot((AllocatableValue) fromValue); + to.setSpillState(SpillState.StartInMemory); + } else if (TraceRAshareSpillInformation.getValue() && isShadowedRegisterValue(fromValue)) { + ShadowedRegisterValue shadowedRegisterValue = asShadowedRegisterValue(fromValue); + IntervalHint from = getIntervalHint(shadowedRegisterValue.getRegister()); + TraceInterval to = allocator.getOrCreateInterval(toValue); + setHint(label, to, from); + to.setSpillSlot(shadowedRegisterValue.getStackSlot()); + to.setSpillState(SpillState.StartInMemory); + } else { + throw JVMCIError.shouldNotReachHere(); + } + } + + /** + * Returns a value for a interval definition, which can be used for re-materialization. + * + * @param op An instruction which defines a value + * @param operand The destination operand of the instruction + * @param interval The interval for this defined value. + * @return Returns the value which is moved to the instruction and which can be reused at + * all reload-locations in case the interval of this instruction is spilled. + * Currently this can only be a {@link JavaConstant}. + */ + private JavaConstant getMaterializedValue(LIRInstruction op, Value operand, TraceInterval interval) { + if (op instanceof LoadConstantOp) { + LoadConstantOp move = (LoadConstantOp) op; + if (move.getConstant() instanceof JavaConstant) { + if (!allocator.neverSpillConstants()) { + if (!allocator.getSpillMoveFactory().allowConstantToStackMove(move.getConstant())) { + return null; + } + /* + * Check if the interval has any uses which would accept an stack location + * (priority == ShouldHaveRegister). Rematerialization of such intervals can + * result in a degradation, because rematerialization always inserts a + * constant load, even if the value is not needed in a register. + */ + UsePosList usePosList = interval.usePosList(); + int numUsePos = usePosList.size(); + for (int useIdx = 0; useIdx < numUsePos; useIdx++) { + TraceInterval.RegisterPriority priority = usePosList.registerPriority(useIdx); + if (priority == TraceInterval.RegisterPriority.ShouldHaveRegister) { + return null; + } + } + } + return (JavaConstant) move.getConstant(); + } + } + return null; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/lsra/TraceLinearScanRegisterAllocationPhase.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2015, 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.graal.lir.alloc.trace.lsra; + +import java.util.List; + +import jdk.vm.ci.code.TargetDescription; + +import com.oracle.graal.compiler.common.cfg.AbstractBlockBase; +import com.oracle.graal.debug.Debug; +import com.oracle.graal.debug.Indent; +import com.oracle.graal.lir.gen.LIRGenerationResult; + +final class TraceLinearScanRegisterAllocationPhase extends TraceLinearScanAllocationPhase { + + @Override + protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, + TraceLinearScanAllocationContext context) { + TraceLinearScan allocator = context.allocator; + allocator.printIntervals("Before register allocation"); + allocateRegisters(allocator); + allocator.printIntervals("After register allocation"); + } + + @SuppressWarnings("try") + private static void allocateRegisters(TraceLinearScan allocator) { + try (Indent indent = Debug.logAndIndent("allocate registers")) { + FixedInterval precoloredIntervals = allocator.createFixedUnhandledList(); + TraceInterval notPrecoloredIntervals = allocator.createUnhandledListByFrom(TraceLinearScan.IS_VARIABLE_INTERVAL); + + // allocate cpu registers + TraceLinearScanWalker lsw = new TraceLinearScanWalker(allocator, precoloredIntervals, notPrecoloredIntervals); + lsw.walk(); + lsw.finishAllocation(); + } + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/lsra/TraceLinearScanResolveDataFlowPhase.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2015, 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.graal.lir.alloc.trace.lsra; + +import static com.oracle.graal.compiler.common.GraalOptions.DetailedAsserts; +import static com.oracle.graal.lir.LIRValueUtil.asConstant; +import static com.oracle.graal.lir.LIRValueUtil.isConstantValue; +import static com.oracle.graal.lir.LIRValueUtil.isStackSlotValue; +import static com.oracle.graal.lir.LIRValueUtil.isVirtualStackSlot; +import static jdk.vm.ci.code.ValueUtil.isRegister; + +import java.util.BitSet; +import java.util.List; +import java.util.ListIterator; + +import jdk.vm.ci.code.TargetDescription; +import jdk.vm.ci.meta.Value; + +import com.oracle.graal.compiler.common.alloc.TraceBuilder.TraceBuilderResult; +import com.oracle.graal.compiler.common.cfg.AbstractBlockBase; +import com.oracle.graal.debug.Debug; +import com.oracle.graal.debug.DebugMetric; +import com.oracle.graal.debug.Indent; +import com.oracle.graal.lir.LIRInstruction; +import com.oracle.graal.lir.StandardOp; +import com.oracle.graal.lir.gen.LIRGenerationResult; +import com.oracle.graal.lir.ssa.SSAUtil.PhiValueVisitor; +import com.oracle.graal.lir.ssi.SSIUtil; + +/** + * Phase 6: resolve data flow + * + * Insert moves at edges between blocks if intervals have been split. + */ +final class TraceLinearScanResolveDataFlowPhase extends TraceLinearScanAllocationPhase { + + @Override + protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, + TraceLinearScanAllocationContext context) { + TraceBuilderResult<?> traceBuilderResult = context.traceBuilderResult; + TraceLinearScan allocator = context.allocator; + new Resolver(allocator, traceBuilderResult).resolveDataFlow(allocator.sortedBlocks()); + } + + private static final class Resolver { + private final TraceLinearScan allocator; + private final TraceBuilderResult<?> traceBuilderResult; + + private Resolver(TraceLinearScan allocator, TraceBuilderResult<?> traceBuilderResult) { + this.allocator = allocator; + this.traceBuilderResult = traceBuilderResult; + } + + private void resolveFindInsertPos(AbstractBlockBase<?> fromBlock, AbstractBlockBase<?> toBlock, TraceLocalMoveResolver moveResolver) { + if (fromBlock.getSuccessorCount() <= 1) { + if (Debug.isLogEnabled()) { + Debug.log("inserting moves at end of fromBlock B%d", fromBlock.getId()); + } + + List<LIRInstruction> instructions = allocator.getLIR().getLIRforBlock(fromBlock); + LIRInstruction instr = instructions.get(instructions.size() - 1); + if (instr instanceof StandardOp.JumpOp) { + // insert moves before branch + moveResolver.setInsertPosition(instructions, instructions.size() - 1); + } else { + moveResolver.setInsertPosition(instructions, instructions.size()); + } + + } else { + if (Debug.isLogEnabled()) { + Debug.log("inserting moves at beginning of toBlock B%d", toBlock.getId()); + } + + if (DetailedAsserts.getValue()) { + assert allocator.getLIR().getLIRforBlock(fromBlock).get(0) instanceof StandardOp.LabelOp : "block does not start with a label"; + + /* + * Because the number of predecessor edges matches the number of successor + * edges, blocks which are reached by switch statements may have be more than + * one predecessor but it will be guaranteed that all predecessors will be the + * same. + */ + for (AbstractBlockBase<?> predecessor : toBlock.getPredecessors()) { + assert fromBlock == predecessor : "all critical edges must be broken"; + } + } + + moveResolver.setInsertPosition(allocator.getLIR().getLIRforBlock(toBlock), 1); + } + } + + /** + * Inserts necessary moves (spilling or reloading) at edges between blocks for intervals + * that have been split. + */ + @SuppressWarnings("try") + private void resolveDataFlow(List<? extends AbstractBlockBase<?>> blocks) { + if (blocks.size() < 2) { + // no resolution necessary + return; + } + try (Indent indent = Debug.logAndIndent("resolve data flow")) { + + TraceLocalMoveResolver moveResolver = allocator.createMoveResolver(); + ListIterator<? extends AbstractBlockBase<?>> it = blocks.listIterator(); + AbstractBlockBase<?> toBlock = null; + for (AbstractBlockBase<?> fromBlock = it.next(); it.hasNext(); fromBlock = toBlock) { + toBlock = it.next(); + assert containedInTrace(fromBlock) : "Not in Trace: " + fromBlock; + assert containedInTrace(toBlock) : "Not in Trace: " + toBlock; + resolveCollectMappings(fromBlock, toBlock, moveResolver); + } + assert blocks.get(blocks.size() - 1).equals(toBlock); + if (toBlock.isLoopEnd()) { + assert toBlock.getSuccessorCount() == 1; + AbstractBlockBase<?> loopHeader = toBlock.getSuccessors().get(0); + if (containedInTrace(loopHeader)) { + resolveCollectMappings(toBlock, loopHeader, moveResolver); + } + } + + } + } + + @SuppressWarnings("try") + private void resolveCollectMappings(AbstractBlockBase<?> fromBlock, AbstractBlockBase<?> toBlock, TraceLocalMoveResolver moveResolver) { + try (Indent indent0 = Debug.logAndIndent("Edge %s -> %s", fromBlock, toBlock)) { + collectLSRAMappings(fromBlock, toBlock, moveResolver); + collectSSIMappings(fromBlock, toBlock, moveResolver); + } + } + + protected void collectLSRAMappings(AbstractBlockBase<?> fromBlock, AbstractBlockBase<?> toBlock, TraceLocalMoveResolver moveResolver) { + assert moveResolver.checkEmpty(); + + int toBlockFirstInstructionId = allocator.getFirstLirInstructionId(toBlock); + int fromBlockLastInstructionId = allocator.getLastLirInstructionId(fromBlock) + 1; + int numOperands = allocator.operandSize(); + BitSet liveAtEdge = allocator.getBlockData(toBlock).liveIn; + + // visit all variables for which the liveAtEdge bit is set + for (int operandNum = liveAtEdge.nextSetBit(0); operandNum >= 0; operandNum = liveAtEdge.nextSetBit(operandNum + 1)) { + assert operandNum < numOperands : "live information set for not exisiting interval"; + assert allocator.getBlockData(fromBlock).liveOut.get(operandNum) && allocator.getBlockData(toBlock).liveIn.get(operandNum) : "interval not live at this edge"; + + TraceInterval fromInterval = allocator.splitChildAtOpId(allocator.intervalFor(operandNum), fromBlockLastInstructionId, LIRInstruction.OperandMode.DEF); + TraceInterval toInterval = allocator.splitChildAtOpId(allocator.intervalFor(operandNum), toBlockFirstInstructionId, LIRInstruction.OperandMode.DEF); + + if (fromInterval != toInterval && !fromInterval.location().equals(toInterval.location())) { + // need to insert move instruction + moveResolver.addMapping(fromInterval, toInterval); + } + } + } + + protected void collectSSIMappings(AbstractBlockBase<?> fromBlock, AbstractBlockBase<?> toBlock, TraceLocalMoveResolver moveResolver) { + // collect all intervals that have been split between + // fromBlock and toBlock + SSIUtil.forEachValuePair(allocator.getLIR(), toBlock, fromBlock, new MyPhiValueVisitor(moveResolver, toBlock, fromBlock)); + if (moveResolver.hasMappings()) { + resolveFindInsertPos(fromBlock, toBlock, moveResolver); + moveResolver.resolveAndAppendMoves(); + } + } + + private boolean containedInTrace(AbstractBlockBase<?> block) { + return currentTrace() == traceBuilderResult.getTraceForBlock(block); + } + + private int currentTrace() { + return traceBuilderResult.getTraceForBlock(allocator.sortedBlocks().get(0)); + } + + private static final DebugMetric numSSIResolutionMoves = Debug.metric("SSI LSRA[numSSIResolutionMoves]"); + private static final DebugMetric numStackToStackMoves = Debug.metric("SSI LSRA[numStackToStackMoves]"); + + private class MyPhiValueVisitor implements PhiValueVisitor { + final TraceLocalMoveResolver moveResolver; + final int toId; + final int fromId; + + public MyPhiValueVisitor(TraceLocalMoveResolver moveResolver, AbstractBlockBase<?> toBlock, AbstractBlockBase<?> fromBlock) { + this.moveResolver = moveResolver; + toId = allocator.getFirstLirInstructionId(toBlock); + fromId = allocator.getLastLirInstructionId(fromBlock); + assert fromId >= 0; + } + + public void visit(Value phiIn, Value phiOut) { + assert !isRegister(phiOut) : "Out is a register: " + phiOut; + assert !isRegister(phiIn) : "In is a register: " + phiIn; + if (Value.ILLEGAL.equals(phiIn)) { + // The value not needed in this branch. + return; + } + if (isVirtualStackSlot(phiIn) && isVirtualStackSlot(phiOut) && phiIn.equals(phiOut)) { + // no need to handle virtual stack slots + return; + } + TraceInterval toInterval = allocator.splitChildAtOpId(allocator.intervalFor(phiIn), toId, LIRInstruction.OperandMode.DEF); + if (isConstantValue(phiOut)) { + numSSIResolutionMoves.increment(); + moveResolver.addMapping(asConstant(phiOut), toInterval); + } else { + TraceInterval fromInterval = allocator.splitChildAtOpId(allocator.intervalFor(phiOut), fromId, LIRInstruction.OperandMode.DEF); + if (fromInterval != toInterval) { + numSSIResolutionMoves.increment(); + if (!(isStackSlotValue(toInterval.location()) && isStackSlotValue(fromInterval.location()))) { + moveResolver.addMapping(fromInterval, toInterval); + } else { + numStackToStackMoves.increment(); + moveResolver.addMapping(fromInterval, toInterval); + } + } + } + } + } + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/lsra/TraceLinearScanWalker.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,1126 @@ +/* + * Copyright (c) 2009, 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.graal.lir.alloc.trace.lsra; + +import static com.oracle.graal.lir.LIRValueUtil.isStackSlotValue; +import static com.oracle.graal.lir.LIRValueUtil.isVariable; +import static jdk.vm.ci.code.CodeUtil.isOdd; +import static jdk.vm.ci.code.ValueUtil.asRegister; +import static jdk.vm.ci.code.ValueUtil.isRegister; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.BitSet; +import java.util.List; + +import jdk.vm.ci.code.BailoutException; +import jdk.vm.ci.code.Register; +import jdk.vm.ci.common.JVMCIError; +import jdk.vm.ci.meta.Value; + +import com.oracle.graal.compiler.common.alloc.RegisterAllocationConfig.AllocatableRegisters; +import com.oracle.graal.compiler.common.cfg.AbstractBlockBase; +import com.oracle.graal.compiler.common.util.Util; +import com.oracle.graal.debug.Debug; +import com.oracle.graal.debug.Indent; +import com.oracle.graal.lir.LIRInstruction; +import com.oracle.graal.lir.StandardOp.BlockEndOp; +import com.oracle.graal.lir.StandardOp.LabelOp; +import com.oracle.graal.lir.StandardOp.ValueMoveOp; +import com.oracle.graal.lir.alloc.lsra.OutOfRegistersException; +import com.oracle.graal.lir.alloc.trace.lsra.TraceInterval.RegisterPriority; +import com.oracle.graal.lir.alloc.trace.lsra.TraceInterval.SpillState; +import com.oracle.graal.lir.alloc.trace.lsra.TraceInterval.State; + +/** + */ +final class TraceLinearScanWalker extends TraceIntervalWalker { + + private Register[] availableRegs; + + private final int[] usePos; + private final int[] blockPos; + private final BitSet isInMemory; + + private List<TraceInterval>[] spillIntervals; + + private TraceLocalMoveResolver moveResolver; // for ordering spill moves + + private int minReg; + + private int maxReg; + + /** + * Only 10% of the lists in {@link #spillIntervals} are actually used. But when they are used, + * they can grow quite long. The maximum length observed was 45 (all numbers taken from a + * bootstrap run of Graal). Therefore, we initialize {@link #spillIntervals} with this marker + * value, and allocate a "real" list only on demand in {@link #setUsePos}. + */ + private static final List<TraceInterval> EMPTY_LIST = new ArrayList<>(0); + + // accessors mapped to same functions in class LinearScan + private int blockCount() { + return allocator.blockCount(); + } + + private AbstractBlockBase<?> blockAt(int idx) { + return allocator.blockAt(idx); + } + + @SuppressWarnings("unused") + private AbstractBlockBase<?> blockOfOpWithId(int opId) { + return allocator.blockForId(opId); + } + + TraceLinearScanWalker(TraceLinearScan allocator, FixedInterval unhandledFixedFirst, TraceInterval unhandledAnyFirst) { + super(allocator, unhandledFixedFirst, unhandledAnyFirst); + + moveResolver = allocator.createMoveResolver(); + int numRegs = allocator.getRegisters().length; + spillIntervals = Util.uncheckedCast(new List<?>[numRegs]); + for (int i = 0; i < numRegs; i++) { + spillIntervals[i] = EMPTY_LIST; + } + usePos = new int[numRegs]; + blockPos = new int[numRegs]; + isInMemory = new BitSet(numRegs); + } + + private void initUseLists(boolean onlyProcessUsePos) { + for (Register register : availableRegs) { + int i = register.number; + usePos[i] = Integer.MAX_VALUE; + + if (!onlyProcessUsePos) { + blockPos[i] = Integer.MAX_VALUE; + spillIntervals[i].clear(); + isInMemory.clear(i); + } + } + } + + private int maxRegisterNumber() { + return maxReg; + } + + private int minRegisterNumber() { + return minReg; + } + + private boolean isRegisterInRange(int reg) { + return reg >= minRegisterNumber() && reg <= maxRegisterNumber(); + } + + private void excludeFromUse(IntervalHint i) { + Value location = i.location(); + int i1 = asRegister(location).number; + if (isRegisterInRange(i1)) { + usePos[i1] = 0; + } + } + + private void setUsePos(TraceInterval interval, int usePos, boolean onlyProcessUsePos) { + if (usePos != -1) { + assert usePos != 0 : "must use excludeFromUse to set usePos to 0"; + int i = asRegister(interval.location()).number; + if (isRegisterInRange(i)) { + if (this.usePos[i] > usePos) { + this.usePos[i] = usePos; + } + if (!onlyProcessUsePos) { + List<TraceInterval> list = spillIntervals[i]; + if (list == EMPTY_LIST) { + list = new ArrayList<>(2); + spillIntervals[i] = list; + } + list.add(interval); + // set is in memory flag + if (interval.inMemoryAt(currentPosition)) { + isInMemory.set(i); + } + } + } + } + } + + private void setUsePos(FixedInterval interval, int usePos, boolean onlyProcessUsePos) { + assert onlyProcessUsePos; + if (usePos != -1) { + assert usePos != 0 : "must use excludeFromUse to set usePos to 0"; + int i = asRegister(interval.location()).number; + if (isRegisterInRange(i)) { + if (this.usePos[i] > usePos) { + this.usePos[i] = usePos; + } + } + } + } + + private void setBlockPos(IntervalHint i, int blockPos) { + if (blockPos != -1) { + int reg = asRegister(i.location()).number; + if (isRegisterInRange(reg)) { + if (this.blockPos[reg] > blockPos) { + this.blockPos[reg] = blockPos; + } + if (usePos[reg] > blockPos) { + usePos[reg] = blockPos; + } + } + } + } + + private void freeExcludeActiveFixed() { + FixedInterval interval = activeFixedList.getFixed(); + while (interval != FixedInterval.EndMarker) { + assert isRegister(interval.location()) : "active interval must have a register assigned"; + excludeFromUse(interval); + interval = interval.next; + } + } + + private void freeExcludeActiveAny() { + TraceInterval interval = activeAnyList.getAny(); + while (interval != TraceInterval.EndMarker) { + assert isRegister(interval.location()) : "active interval must have a register assigned"; + excludeFromUse(interval); + interval = interval.next; + } + } + + private void freeCollectInactiveFixed(TraceInterval current) { + FixedInterval interval = inactiveFixedList.getFixed(); + while (interval != FixedInterval.EndMarker) { + if (current.to() <= interval.from()) { + assert interval.intersectsAt(current) == -1 : "must not intersect"; + setUsePos(interval, interval.from(), true); + } else { + setUsePos(interval, interval.currentIntersectsAt(current), true); + } + interval = interval.next; + } + } + + private void spillExcludeActiveFixed() { + FixedInterval interval = activeFixedList.getFixed(); + while (interval != FixedInterval.EndMarker) { + excludeFromUse(interval); + interval = interval.next; + } + } + + private void spillBlockInactiveFixed(TraceInterval current) { + FixedInterval interval = inactiveFixedList.getFixed(); + while (interval != FixedInterval.EndMarker) { + if (current.to() > interval.currentFrom()) { + setBlockPos(interval, interval.currentIntersectsAt(current)); + } else { + assert interval.currentIntersectsAt(current) == -1 : "invalid optimization: intervals intersect"; + } + + interval = interval.next; + } + } + + private void spillCollectActiveAny(RegisterPriority registerPriority) { + TraceInterval interval = activeAnyList.getAny(); + while (interval != TraceInterval.EndMarker) { + setUsePos(interval, Math.min(interval.nextUsage(registerPriority, currentPosition), interval.to()), false); + interval = interval.next; + } + } + + @SuppressWarnings("unused") + private int insertIdAtBasicBlockBoundary(int opId) { + assert allocator.isBlockBegin(opId) : "Not a block begin: " + opId; + assert allocator.instructionForId(opId) instanceof LabelOp; + assert allocator.instructionForId(opId - 2) instanceof BlockEndOp; + + AbstractBlockBase<?> toBlock = allocator.blockForId(opId); + AbstractBlockBase<?> fromBlock = allocator.blockForId(opId - 2); + + if (fromBlock.getSuccessorCount() == 1) { + // insert move in predecessor + return opId - 2; + } + assert toBlock.getPredecessorCount() == 1 : String.format("Critical Edge? %s->%s", fromBlock, toBlock); + // insert move in successor + return opId + 2; + } + + private void insertMove(int operandId, TraceInterval srcIt, TraceInterval dstIt) { + // output all moves here. When source and target are equal, the move is + // optimized away later in assignRegNums + + int opId = (operandId + 1) & ~1; + AbstractBlockBase<?> opBlock = allocator.blockForId(opId); + assert opId > 0 && allocator.blockForId(opId - 2) == opBlock : "cannot insert move at block boundary"; + + // calculate index of instruction inside instruction list of current block + // the minimal index (for a block with no spill moves) can be calculated because the + // numbering of instructions is known. + // When the block already contains spill moves, the index must be increased until the + // correct index is reached. + List<LIRInstruction> instructions = allocator.getLIR().getLIRforBlock(opBlock); + int index = (opId - instructions.get(0).id()) >> 1; + assert instructions.get(index).id() <= opId : "error in calculation"; + + while (instructions.get(index).id() != opId) { + index++; + assert 0 <= index && index < instructions.size() : "index out of bounds"; + } + assert 1 <= index && index < instructions.size() : "index out of bounds"; + assert instructions.get(index).id() == opId : "error in calculation"; + + // insert new instruction before instruction at position index + moveResolver.moveInsertPosition(instructions, index); + moveResolver.addMapping(srcIt, dstIt); + } + + private int findOptimalSplitPos(AbstractBlockBase<?> minBlock, AbstractBlockBase<?> maxBlock, int maxSplitPos) { + int fromBlockNr = minBlock.getLinearScanNumber(); + int toBlockNr = maxBlock.getLinearScanNumber(); + + assert 0 <= fromBlockNr && fromBlockNr < blockCount() : "out of range"; + assert 0 <= toBlockNr && toBlockNr < blockCount() : "out of range"; + assert fromBlockNr < toBlockNr : "must cross block boundary"; + + // Try to split at end of maxBlock. If this would be after + // maxSplitPos, then use the begin of maxBlock + int optimalSplitPos = allocator.getLastLirInstructionId(maxBlock) + 2; + if (optimalSplitPos > maxSplitPos) { + optimalSplitPos = allocator.getFirstLirInstructionId(maxBlock); + } + + // minimal block probability + double minProbability = maxBlock.probability(); + for (int i = toBlockNr - 1; i >= fromBlockNr; i--) { + AbstractBlockBase<?> cur = blockAt(i); + + if (cur.probability() < minProbability) { + // Block with lower probability found. Split at the end of this block. + minProbability = cur.probability(); + optimalSplitPos = allocator.getLastLirInstructionId(cur) + 2; + } + } + assert optimalSplitPos > allocator.maxOpId() || allocator.isBlockBegin(optimalSplitPos) : "algorithm must move split pos to block boundary"; + + return optimalSplitPos; + } + + @SuppressWarnings({"unused"}) + private int findOptimalSplitPos(TraceInterval interval, int minSplitPos, int maxSplitPos, boolean doLoopOptimization) { + int optimalSplitPos = findOptimalSplitPos0(minSplitPos, maxSplitPos); + if (Debug.isLogEnabled()) { + Debug.log("optimal split position: %d", optimalSplitPos); + } + return optimalSplitPos; + } + + private int findOptimalSplitPos0(int minSplitPos, int maxSplitPos) { + if (minSplitPos == maxSplitPos) { + // trivial case, no optimization of split position possible + if (Debug.isLogEnabled()) { + Debug.log("min-pos and max-pos are equal, no optimization possible"); + } + return minSplitPos; + + } + assert minSplitPos < maxSplitPos : "must be true then"; + assert minSplitPos > 0 : "cannot access minSplitPos - 1 otherwise"; + + // reason for using minSplitPos - 1: when the minimal split pos is exactly at the + // beginning of a block, then minSplitPos is also a possible split position. + // Use the block before as minBlock, because then minBlock.lastLirInstructionId() + 2 == + // minSplitPos + AbstractBlockBase<?> minBlock = allocator.blockForId(minSplitPos - 1); + + // reason for using maxSplitPos - 1: otherwise there would be an assert on failure + // when an interval ends at the end of the last block of the method + // (in this case, maxSplitPos == allocator().maxLirOpId() + 2, and there is no + // block at this opId) + AbstractBlockBase<?> maxBlock = allocator.blockForId(maxSplitPos - 1); + + assert minBlock.getLinearScanNumber() <= maxBlock.getLinearScanNumber() : "invalid order"; + if (minBlock == maxBlock) { + // split position cannot be moved to block boundary : so split as late as possible + if (Debug.isLogEnabled()) { + Debug.log("cannot move split pos to block boundary because minPos and maxPos are in same block"); + } + return maxSplitPos; + + } + // seach optimal block boundary between minSplitPos and maxSplitPos + if (Debug.isLogEnabled()) { + Debug.log("moving split pos to optimal block boundary between block B%d and B%d", minBlock.getId(), maxBlock.getId()); + } + + return findOptimalSplitPos(minBlock, maxBlock, maxSplitPos); + } + + // split an interval at the optimal position between minSplitPos and + // maxSplitPos in two parts: + // 1) the left part has already a location assigned + // 2) the right part is sorted into to the unhandled-list + @SuppressWarnings("try") + private void splitBeforeUsage(TraceInterval interval, int minSplitPos, int maxSplitPos) { + + try (Indent indent = Debug.logAndIndent("splitting interval %s between %d and %d", interval, minSplitPos, maxSplitPos)) { + + assert interval.from() < minSplitPos : "cannot split at start of interval"; + assert currentPosition < minSplitPos : "cannot split before current position"; + assert minSplitPos <= maxSplitPos : "invalid order"; + assert maxSplitPos <= interval.to() : "cannot split after end of interval"; + + final int optimalSplitPos = findOptimalSplitPos(interval, minSplitPos, maxSplitPos, true); + + if (optimalSplitPos == interval.to() && interval.nextUsage(RegisterPriority.MustHaveRegister, minSplitPos) == Integer.MAX_VALUE) { + // the split position would be just before the end of the interval + // . no split at all necessary + if (Debug.isLogEnabled()) { + Debug.log("no split necessary because optimal split position is at end of interval"); + } + return; + } + // must calculate this before the actual split is performed and before split position is + // moved to odd opId + final int optimalSplitPosFinal; + boolean blockBegin = allocator.isBlockBegin(optimalSplitPos); + if (blockBegin) { + assert (optimalSplitPos & 1) == 0 : "Block begins must be even: " + optimalSplitPos; + // move position after the label (odd optId) + optimalSplitPosFinal = optimalSplitPos + 1; + } else { + // move position before actual instruction (odd opId) + optimalSplitPosFinal = (optimalSplitPos - 1) | 1; + } + + // TODO( je) better define what min split pos max split pos mean. + assert minSplitPos <= optimalSplitPosFinal && optimalSplitPosFinal <= maxSplitPos || minSplitPos == maxSplitPos && optimalSplitPosFinal == minSplitPos - 1 : "out of range"; + assert optimalSplitPosFinal <= interval.to() : "cannot split after end of interval"; + assert optimalSplitPosFinal > interval.from() : "cannot split at start of interval"; + + if (Debug.isLogEnabled()) { + Debug.log("splitting at position %d", optimalSplitPosFinal); + } + assert optimalSplitPosFinal > currentPosition : "Can not split interval " + interval + " at current position: " + currentPosition; + + // was: + // assert isBlockBegin || ((optimalSplitPos1 & 1) == 1) : + // "split pos must be odd when not on block boundary"; + // assert !isBlockBegin || ((optimalSplitPos1 & 1) == 0) : + // "split pos must be even on block boundary"; + assert (optimalSplitPosFinal & 1) == 1 : "split pos must be odd"; + + // TODO (je) duplicate code. try to fold + if (optimalSplitPosFinal == interval.to() && interval.nextUsage(RegisterPriority.MustHaveRegister, minSplitPos) == Integer.MAX_VALUE) { + // the split position would be just before the end of the interval + // . no split at all necessary + if (Debug.isLogEnabled()) { + Debug.log("no split necessary because optimal split position is at end of interval"); + } + return; + } + TraceInterval splitPart = interval.split(optimalSplitPosFinal, allocator); + + boolean moveNecessary = true; + splitPart.setInsertMoveWhenActivated(moveNecessary); + + assert splitPart.from() >= currentPosition : "cannot append new interval before current walk position"; + unhandledAnyList.addToListSortedByStartAndUsePositions(splitPart); + + if (Debug.isLogEnabled()) { + Debug.log("left interval %s: %s", moveNecessary ? " " : "", interval.logString(allocator)); + Debug.log("right interval %s: %s", moveNecessary ? "(move)" : "", splitPart.logString(allocator)); + } + } + } + + // split an interval at the optimal position between minSplitPos and + // maxSplitPos in two parts: + // 1) the left part has already a location assigned + // 2) the right part is always on the stack and therefore ignored in further processing + @SuppressWarnings("try") + private void splitForSpilling(TraceInterval interval) { + // calculate allowed range of splitting position + int maxSplitPos = currentPosition; + int previousUsage = interval.previousUsage(RegisterPriority.ShouldHaveRegister, maxSplitPos); + if (previousUsage == currentPosition) { + /* + * If there is a usage with ShouldHaveRegister priority at the current position fall + * back to MustHaveRegister priority. This only happens if register priority was + * downgraded to MustHaveRegister in #allocLockedRegister. + */ + previousUsage = interval.previousUsage(RegisterPriority.MustHaveRegister, maxSplitPos); + } + int minSplitPos = Math.max(previousUsage + 1, interval.from()); + + try (Indent indent = Debug.logAndIndent("splitting and spilling interval %s between %d and %d", interval, minSplitPos, maxSplitPos)) { + + assert interval.state == State.Active : "why spill interval that is not active?"; + assert interval.from() <= minSplitPos : "cannot split before start of interval"; + assert minSplitPos <= maxSplitPos : "invalid order"; + assert maxSplitPos < interval.to() : "cannot split at end end of interval"; + assert currentPosition < interval.to() : "interval must not end before current position"; + + if (minSplitPos == interval.from()) { + // the whole interval is never used, so spill it entirely to memory + + try (Indent indent2 = Debug.logAndIndent("spilling entire interval because split pos is at beginning of interval (use positions: %d)", interval.usePosList().size())) { + + assert interval.firstUsage(RegisterPriority.MustHaveRegister) > currentPosition : String.format("interval %s must not have use position before currentPosition %d", interval, + currentPosition); + + allocator.assignSpillSlot(interval); + handleSpillSlot(interval); + changeSpillState(interval, minSplitPos); + + // Also kick parent intervals out of register to memory when they have no use + // position. This avoids short interval in register surrounded by intervals in + // memory . avoid useless moves from memory to register and back + TraceInterval parent = interval; + while (parent != null && parent.isSplitChild()) { + parent = parent.getSplitChildBeforeOpId(parent.from()); + + if (isRegister(parent.location())) { + if (parent.firstUsage(RegisterPriority.ShouldHaveRegister) == Integer.MAX_VALUE) { + // parent is never used, so kick it out of its assigned register + if (Debug.isLogEnabled()) { + Debug.log("kicking out interval %d out of its register because it is never used", parent.operandNumber); + } + allocator.assignSpillSlot(parent); + handleSpillSlot(parent); + } else { + // do not go further back because the register is actually used by + // the interval + parent = null; + } + } + } + } + + } else { + // search optimal split pos, split interval and spill only the right hand part + int optimalSplitPos = findOptimalSplitPos(interval, minSplitPos, maxSplitPos, false); + + assert minSplitPos <= optimalSplitPos && optimalSplitPos <= maxSplitPos : "out of range"; + assert optimalSplitPos < interval.to() : "cannot split at end of interval"; + assert optimalSplitPos >= interval.from() : "cannot split before start of interval"; + + if (!allocator.isBlockBegin(optimalSplitPos)) { + // move position before actual instruction (odd opId) + optimalSplitPos = (optimalSplitPos - 1) | 1; + } + + try (Indent indent2 = Debug.logAndIndent("splitting at position %d", optimalSplitPos)) { + assert allocator.isBlockBegin(optimalSplitPos) || ((optimalSplitPos & 1) == 1) : "split pos must be odd when not on block boundary"; + assert !allocator.isBlockBegin(optimalSplitPos) || ((optimalSplitPos & 1) == 0) : "split pos must be even on block boundary"; + + TraceInterval spilledPart = interval.split(optimalSplitPos, allocator); + allocator.assignSpillSlot(spilledPart); + handleSpillSlot(spilledPart); + changeSpillState(spilledPart, optimalSplitPos); + + if (!allocator.isBlockBegin(optimalSplitPos)) { + if (Debug.isLogEnabled()) { + Debug.log("inserting move from interval %s to %s", interval, spilledPart); + } + insertMove(optimalSplitPos, interval, spilledPart); + } else { + if (Debug.isLogEnabled()) { + Debug.log("no need to insert move. done by data-flow resolution"); + } + } + + // the currentSplitChild is needed later when moves are inserted for reloading + assert spilledPart.currentSplitChild() == interval : "overwriting wrong currentSplitChild"; + spilledPart.makeCurrentSplitChild(); + + if (Debug.isLogEnabled()) { + Debug.log("left interval: %s", interval.logString(allocator)); + Debug.log("spilled interval : %s", spilledPart.logString(allocator)); + } + } + } + } + } + + /** + * Change spill state of an interval. + * + * Note: called during register allocation. + * + * @param spillPos position of the spill + */ + private void changeSpillState(TraceInterval interval, int spillPos) { + if (TraceLinearScan.Options.LIROptTraceRAEliminateSpillMoves.getValue()) { + switch (interval.spillState()) { + case NoSpillStore: + final int minSpillPos = interval.spillDefinitionPos(); + final int maxSpillPost = spillPos; + + final int optimalSpillPos = findOptimalSpillPos(minSpillPos, maxSpillPost); + + // assert !allocator.isBlockBegin(optimalSpillPos); + assert !allocator.isBlockEnd(optimalSpillPos); + assert (optimalSpillPos & 1) == 0 : "Spill pos must be even"; + + interval.setSpillDefinitionPos(optimalSpillPos); + interval.setSpillState(SpillState.SpillStore); + break; + case SpillStore: + case StartInMemory: + case NoOptimization: + case NoDefinitionFound: + // nothing to do + break; + + default: + throw new BailoutException("other states not allowed at this time"); + } + } else { + interval.setSpillState(SpillState.NoOptimization); + } + } + + /** + * @param minSpillPos minimal spill position + * @param maxSpillPos maximal spill position + */ + private int findOptimalSpillPos(int minSpillPos, int maxSpillPos) { + int optimalSpillPos = findOptimalSpillPos0(minSpillPos, maxSpillPos) & (~1); + if (Debug.isLogEnabled()) { + Debug.log("optimal spill position: %d", optimalSpillPos); + } + return optimalSpillPos; + } + + private int findOptimalSpillPos0(int minSpillPos, int maxSpillPos) { + if (minSpillPos == maxSpillPos) { + // trivial case, no optimization of split position possible + if (Debug.isLogEnabled()) { + Debug.log("min-pos and max-pos are equal, no optimization possible"); + } + return minSpillPos; + + } + assert minSpillPos < maxSpillPos : "must be true then"; + assert minSpillPos >= 0 : "cannot access minSplitPos - 1 otherwise"; + + AbstractBlockBase<?> minBlock = allocator.blockForId(minSpillPos); + AbstractBlockBase<?> maxBlock = allocator.blockForId(maxSpillPos); + + assert minBlock.getLinearScanNumber() <= maxBlock.getLinearScanNumber() : "invalid order"; + if (minBlock == maxBlock) { + // split position cannot be moved to block boundary : so split as late as possible + if (Debug.isLogEnabled()) { + Debug.log("cannot move split pos to block boundary because minPos and maxPos are in same block"); + } + return maxSpillPos; + + } + // search optimal block boundary between minSplitPos and maxSplitPos + if (Debug.isLogEnabled()) { + Debug.log("moving split pos to optimal block boundary between block B%d and B%d", minBlock.getId(), maxBlock.getId()); + } + + // currently using the same heuristic as for splitting + return findOptimalSpillPos(minBlock, maxBlock, maxSpillPos); + } + + private int findOptimalSpillPos(AbstractBlockBase<?> minBlock, AbstractBlockBase<?> maxBlock, int maxSplitPos) { + int fromBlockNr = minBlock.getLinearScanNumber(); + int toBlockNr = maxBlock.getLinearScanNumber(); + + assert 0 <= fromBlockNr && fromBlockNr < blockCount() : "out of range"; + assert 0 <= toBlockNr && toBlockNr < blockCount() : "out of range"; + assert fromBlockNr < toBlockNr : "must cross block boundary"; + + /* + * Try to split at end of maxBlock. If this would be after maxSplitPos, then use the begin + * of maxBlock. We use last instruction -2 because we want to insert the move before the + * block end op. + */ + int optimalSplitPos = allocator.getLastLirInstructionId(maxBlock) - 2; + if (optimalSplitPos > maxSplitPos) { + optimalSplitPos = allocator.getFirstLirInstructionId(maxBlock); + } + + // minimal block probability + double minProbability = maxBlock.probability(); + for (int i = toBlockNr - 1; i >= fromBlockNr; i--) { + AbstractBlockBase<?> cur = blockAt(i); + + if (cur.probability() < minProbability) { + // Block with lower probability found. Split at the end of this block. + minProbability = cur.probability(); + optimalSplitPos = allocator.getLastLirInstructionId(cur) - 2; + } + } + assert optimalSplitPos > allocator.maxOpId() || allocator.isBlockBegin(optimalSplitPos) || allocator.isBlockEnd(optimalSplitPos + 2) : "algorithm must move split pos to block boundary"; + + return optimalSplitPos; + } + + /** + * This is called for every interval that is assigned to a stack slot. + */ + private static void handleSpillSlot(TraceInterval interval) { + assert interval.location() != null && (interval.canMaterialize() || isStackSlotValue(interval.location())) : "interval not assigned to a stack slot " + interval; + // Do nothing. Stack slots are not processed in this implementation. + } + + private void splitStackInterval(TraceInterval interval) { + int minSplitPos = currentPosition + 1; + int maxSplitPos = Math.min(interval.firstUsage(RegisterPriority.ShouldHaveRegister), interval.to()); + + splitBeforeUsage(interval, minSplitPos, maxSplitPos); + } + + private void splitWhenPartialRegisterAvailable(TraceInterval interval, int registerAvailableUntil) { + int minSplitPos = Math.max(interval.previousUsage(RegisterPriority.ShouldHaveRegister, registerAvailableUntil), interval.from() + 1); + splitBeforeUsage(interval, minSplitPos, registerAvailableUntil); + } + + private void splitAndSpillInterval(TraceInterval interval) { + assert interval.state == State.Active || interval.state == State.Inactive : "other states not allowed"; + + int currentPos = currentPosition; + if (interval.state == State.Inactive) { + // the interval is currently inactive, so no spill slot is needed for now. + // when the split part is activated, the interval has a new chance to get a register, + // so in the best case no stack slot is necessary + throw JVMCIError.shouldNotReachHere("TraceIntervals can not be inactive!"); + + } else { + // search the position where the interval must have a register and split + // at the optimal position before. + // The new created part is added to the unhandled list and will get a register + // when it is activated + int minSplitPos = currentPos + 1; + int maxSplitPos = interval.nextUsage(RegisterPriority.MustHaveRegister, minSplitPos); + + if (maxSplitPos <= interval.to()) { + splitBeforeUsage(interval, minSplitPos, maxSplitPos); + } else { + Debug.log("No more usage, no need to split: %s", interval); + } + + assert interval.nextUsage(RegisterPriority.MustHaveRegister, currentPos) == Integer.MAX_VALUE : "the remaining part is spilled to stack and therefore has no register"; + splitForSpilling(interval); + } + } + + @SuppressWarnings("try") + private boolean allocFreeRegister(TraceInterval interval) { + try (Indent indent = Debug.logAndIndent("trying to find free register for %s", interval)) { + + initUseLists(true); + freeExcludeActiveFixed(); + freeCollectInactiveFixed(interval); + freeExcludeActiveAny(); + // freeCollectUnhandled(fixedKind, cur); + + // usePos contains the start of the next interval that has this register assigned + // (either as a fixed register or a normal allocated register in the past) + // only intervals overlapping with cur are processed, non-overlapping invervals can be + // ignored safely + if (Debug.isLogEnabled()) { + // Enable this logging to see all register states + try (Indent indent2 = Debug.logAndIndent("state of registers:")) { + for (Register register : availableRegs) { + int i = register.number; + Debug.log("reg %d (%s): usePos: %d", register.number, register, usePos[i]); + } + } + } + + Register hint = null; + IntervalHint locationHint = interval.locationHint(true); + if (locationHint != null && locationHint.location() != null && isRegister(locationHint.location())) { + hint = asRegister(locationHint.location()); + if (Debug.isLogEnabled()) { + Debug.log("hint register %3d (%4s) from interval %s", hint.number, hint, locationHint); + } + } + assert interval.location() == null : "register already assigned to interval"; + + // the register must be free at least until this position + int regNeededUntil = interval.from() + 1; + int intervalTo = interval.to(); + + boolean needSplit = false; + int splitPos = -1; + + Register reg = null; + Register minFullReg = null; + Register maxPartialReg = null; + + for (Register availableReg : availableRegs) { + int number = availableReg.number; + if (usePos[number] >= intervalTo) { + // this register is free for the full interval + if (minFullReg == null || availableReg.equals(hint) || (usePos[number] < usePos[minFullReg.number] && !minFullReg.equals(hint))) { + minFullReg = availableReg; + } + } else if (usePos[number] > regNeededUntil) { + // this register is at least free until regNeededUntil + if (maxPartialReg == null || availableReg.equals(hint) || (usePos[number] > usePos[maxPartialReg.number] && !maxPartialReg.equals(hint))) { + maxPartialReg = availableReg; + } + } + } + + if (minFullReg != null) { + reg = minFullReg; + } else if (maxPartialReg != null) { + needSplit = true; + reg = maxPartialReg; + } else { + return false; + } + + splitPos = usePos[reg.number]; + interval.assignLocation(reg.asValue(interval.kind())); + if (Debug.isLogEnabled()) { + Debug.log("selected register %d (%s)", reg.number, reg); + } + + assert splitPos > 0 : "invalid splitPos"; + if (needSplit) { + // register not available for full interval, so split it + splitWhenPartialRegisterAvailable(interval, splitPos); + } + // only return true if interval is completely assigned + return true; + } + } + + private void splitAndSpillIntersectingIntervals(Register reg) { + assert reg != null : "no register assigned"; + + for (int i = 0; i < spillIntervals[reg.number].size(); i++) { + TraceInterval interval = spillIntervals[reg.number].get(i); + removeFromList(interval); + splitAndSpillInterval(interval); + } + } + + // Split an Interval and spill it to memory so that cur can be placed in a register + @SuppressWarnings("try") + private void allocLockedRegister(TraceInterval interval) { + try (Indent indent = Debug.logAndIndent("alloc locked register: need to split and spill to get register for %s", interval)) { + + // the register must be free at least until this position + int firstUsage = interval.firstUsage(RegisterPriority.MustHaveRegister); + int firstShouldHaveUsage = interval.firstUsage(RegisterPriority.ShouldHaveRegister); + int regNeededUntil = Math.min(firstUsage, interval.from() + 1); + int intervalTo = interval.to(); + assert regNeededUntil >= 0 && regNeededUntil < Integer.MAX_VALUE : "interval has no use"; + + Register reg; + Register ignore; + /* + * In the common case we don't spill registers that have _any_ use position that is + * closer than the next use of the current interval, but if we can't spill the current + * interval we weaken this strategy and also allow spilling of intervals that have a + * non-mandatory requirements (no MustHaveRegister use position). + */ + for (RegisterPriority registerPriority = RegisterPriority.LiveAtLoopEnd; true; registerPriority = RegisterPriority.MustHaveRegister) { + // collect current usage of registers + initUseLists(false); + spillExcludeActiveFixed(); + // spillBlockUnhandledFixed(cur); + spillBlockInactiveFixed(interval); + spillCollectActiveAny(registerPriority); + if (Debug.isLogEnabled()) { + printRegisterState(); + } + + reg = null; + ignore = interval.location() != null && isRegister(interval.location()) ? asRegister(interval.location()) : null; + + for (Register availableReg : availableRegs) { + int number = availableReg.number; + if (availableReg.equals(ignore)) { + // this register must be ignored + } else if (usePos[number] > regNeededUntil) { + /* + * If the use position is the same, prefer registers (active intervals) + * where the value is already on the stack. + */ + if (reg == null || (usePos[number] > usePos[reg.number]) || (usePos[number] == usePos[reg.number] && (!isInMemory.get(reg.number) && isInMemory.get(number)))) { + reg = availableReg; + } + } + } + + if (Debug.isLogEnabled()) { + Debug.log("Register Selected: %s", reg); + } + + int regUsePos = (reg == null ? 0 : usePos[reg.number]); + if (regUsePos <= firstShouldHaveUsage) { + /* Check if there is another interval that is already in memory. */ + if (reg == null || interval.inMemoryAt(currentPosition) || !isInMemory.get(reg.number)) { + if (Debug.isLogEnabled()) { + Debug.log("able to spill current interval. firstUsage(register): %d, usePos: %d", firstUsage, regUsePos); + } + + if (firstUsage <= interval.from() + 1) { + if (registerPriority.equals(RegisterPriority.LiveAtLoopEnd)) { + /* + * Tool of last resort: we can not spill the current interval so we + * try to spill an active interval that has a usage but do not + * require a register. + */ + Debug.log("retry with register priority must have register"); + continue; + } + String description = "cannot spill interval (" + interval + ") that is used in first instruction (possible reason: no register found) firstUsage=" + firstUsage + + ", interval.from()=" + interval.from() + "; already used candidates: " + Arrays.toString(availableRegs); + /* + * assign a reasonable register and do a bailout in product mode to + * avoid errors + */ + allocator.assignSpillSlot(interval); + Debug.dump(allocator.getLIR(), description); + allocator.printIntervals(description); + throw new OutOfRegistersException("LinearScan: no register found", description); + } + + splitAndSpillInterval(interval); + return; + } + } + // common case: break out of the loop + break; + } + + boolean needSplit = blockPos[reg.number] <= intervalTo; + + int splitPos = blockPos[reg.number]; + + if (Debug.isLogEnabled()) { + Debug.log("decided to use register %d", reg.number); + } + assert splitPos > 0 : "invalid splitPos"; + assert needSplit || splitPos > interval.from() : "splitting interval at from"; + + interval.assignLocation(reg.asValue(interval.kind())); + if (needSplit) { + // register not available for full interval : so split it + splitWhenPartialRegisterAvailable(interval, splitPos); + } + + // perform splitting and spilling for all affected intervals + splitAndSpillIntersectingIntervals(reg); + return; + } + } + + @SuppressWarnings("try") + private void printRegisterState() { + try (Indent indent2 = Debug.logAndIndent("state of registers:")) { + for (Register reg : availableRegs) { + int i = reg.number; + try (Indent indent3 = Debug.logAndIndent("reg %d: usePos: %d, blockPos: %d, inMemory: %b, intervals: ", i, usePos[i], blockPos[i], isInMemory.get(i))) { + for (int j = 0; j < spillIntervals[i].size(); j++) { + Debug.log("%s", spillIntervals[i].get(j)); + } + } + } + } + } + + private boolean noAllocationPossible(TraceInterval interval) { + if (allocator.callKillsRegisters()) { + // fast calculation of intervals that can never get a register because the + // the next instruction is a call that blocks all registers + // Note: this only works if a call kills all registers + + // check if this interval is the result of a split operation + // (an interval got a register until this position) + int pos = interval.from(); + if (isOdd(pos)) { + // the current instruction is a call that blocks all registers + if (pos < allocator.maxOpId() && allocator.hasCall(pos + 1) && interval.to() > pos + 1) { + if (Debug.isLogEnabled()) { + Debug.log("free register cannot be available because all registers blocked by following call"); + } + + // safety check that there is really no register available + assert !allocFreeRegister(interval) : "found a register for this interval"; + return true; + } + } + } + return false; + } + + private void initVarsForAlloc(TraceInterval interval) { + AllocatableRegisters allocatableRegisters = allocator.getRegisterAllocationConfig().getAllocatableRegisters(interval.kind().getPlatformKind()); + availableRegs = allocatableRegisters.allocatableRegisters; + minReg = allocatableRegisters.minRegisterNumber; + maxReg = allocatableRegisters.maxRegisterNumber; + } + + private static boolean isMove(LIRInstruction op, TraceInterval from, TraceInterval to) { + if (op instanceof ValueMoveOp) { + ValueMoveOp move = (ValueMoveOp) op; + if (isVariable(move.getInput()) && isVariable(move.getResult())) { + return move.getInput() != null && move.getInput().equals(from.operand) && move.getResult() != null && move.getResult().equals(to.operand); + } + } + return false; + } + + // optimization (especially for phi functions of nested loops): + // assign same spill slot to non-intersecting intervals + private void combineSpilledIntervals(TraceInterval interval) { + if (interval.isSplitChild()) { + // optimization is only suitable for split parents + return; + } + + IntervalHint locationHint = interval.locationHint(false); + if (locationHint == null || !(locationHint instanceof TraceInterval)) { + return; + } + TraceInterval registerHint = (TraceInterval) locationHint; + assert registerHint.isSplitParent() : "register hint must be split parent"; + + if (interval.spillState() != SpillState.NoOptimization || registerHint.spillState() != SpillState.NoOptimization) { + // combining the stack slots for intervals where spill move optimization is applied + // is not benefitial and would cause problems + return; + } + + int beginPos = interval.from(); + int endPos = interval.to(); + if (endPos > allocator.maxOpId() || isOdd(beginPos) || isOdd(endPos)) { + // safety check that lirOpWithId is allowed + return; + } + + if (!isMove(allocator.instructionForId(beginPos), registerHint, interval) || !isMove(allocator.instructionForId(endPos), interval, registerHint)) { + // cur and registerHint are not connected with two moves + return; + } + + TraceInterval beginHint = registerHint.getSplitChildAtOpId(beginPos, LIRInstruction.OperandMode.USE, allocator); + TraceInterval endHint = registerHint.getSplitChildAtOpId(endPos, LIRInstruction.OperandMode.DEF, allocator); + if (beginHint == endHint || beginHint.to() != beginPos || endHint.from() != endPos) { + // registerHint must be split : otherwise the re-writing of use positions does not work + return; + } + + assert beginHint.location() != null : "must have register assigned"; + assert endHint.location() == null : "must not have register assigned"; + assert interval.firstUsage(RegisterPriority.MustHaveRegister) == beginPos : "must have use position at begin of interval because of move"; + assert endHint.firstUsage(RegisterPriority.MustHaveRegister) == endPos : "must have use position at begin of interval because of move"; + + if (isRegister(beginHint.location())) { + // registerHint is not spilled at beginPos : so it would not be benefitial to + // immediately spill cur + return; + } + assert registerHint.spillSlot() != null : "must be set when part of interval was spilled"; + + // modify intervals such that cur gets the same stack slot as registerHint + // delete use positions to prevent the intervals to get a register at beginning + interval.setSpillSlot(registerHint.spillSlot()); + interval.removeFirstUsePos(); + endHint.removeFirstUsePos(); + } + + // allocate a physical register or memory location to an interval + @Override + @SuppressWarnings("try") + protected boolean activateCurrent(TraceInterval interval) { + if (Debug.isLogEnabled()) { + logCurrentStatus(); + } + boolean result = true; + + try (Indent indent = Debug.logAndIndent("activating interval %s, splitParent: %d", interval, interval.splitParent().operandNumber)) { + + final Value operand = interval.operand; + if (interval.location() != null && isStackSlotValue(interval.location())) { + // activating an interval that has a stack slot assigned . split it at first use + // position + // used for method parameters + if (Debug.isLogEnabled()) { + Debug.log("interval has spill slot assigned (method parameter) . split it before first use"); + } + splitStackInterval(interval); + result = false; + + } else { + if (interval.location() == null) { + // interval has not assigned register . normal allocation + // (this is the normal case for most intervals) + if (Debug.isLogEnabled()) { + Debug.log("normal allocation of register"); + } + + // assign same spill slot to non-intersecting intervals + combineSpilledIntervals(interval); + + initVarsForAlloc(interval); + if (noAllocationPossible(interval) || !allocFreeRegister(interval)) { + // no empty register available. + // split and spill another interval so that this interval gets a register + allocLockedRegister(interval); + } + + // spilled intervals need not be move to active-list + if (!isRegister(interval.location())) { + result = false; + } + } + } + + // load spilled values that become active from stack slot to register + if (interval.insertMoveWhenActivated()) { + assert interval.isSplitChild(); + assert interval.currentSplitChild() != null; + assert !interval.currentSplitChild().operand.equals(operand) : "cannot insert move between same interval"; + if (Debug.isLogEnabled()) { + Debug.log("Inserting move from interval %d to %d because insertMoveWhenActivated is set", interval.currentSplitChild().operandNumber, interval.operandNumber); + } + + insertMove(interval.from(), interval.currentSplitChild(), interval); + } + interval.makeCurrentSplitChild(); + + } + + return result; // true = interval is moved to active list + } + + void finishAllocation() { + // must be called when all intervals are allocated + moveResolver.resolveAndAppendMoves(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/lsra/TraceLocalMoveResolver.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,553 @@ +/* + * Copyright (c) 2009, 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.graal.lir.alloc.trace.lsra; + +import static com.oracle.graal.lir.LIRValueUtil.asVirtualStackSlot; +import static com.oracle.graal.lir.LIRValueUtil.isStackSlotValue; +import static com.oracle.graal.lir.LIRValueUtil.isVirtualStackSlot; +import static jdk.vm.ci.code.ValueUtil.asRegister; +import static jdk.vm.ci.code.ValueUtil.asStackSlot; +import static jdk.vm.ci.code.ValueUtil.isIllegal; +import static jdk.vm.ci.code.ValueUtil.isRegister; +import static jdk.vm.ci.code.ValueUtil.isStackSlot; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; + +import jdk.vm.ci.code.StackSlot; +import jdk.vm.ci.common.JVMCIError; +import jdk.vm.ci.meta.AllocatableValue; +import jdk.vm.ci.meta.Constant; +import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.LIRKind; +import jdk.vm.ci.meta.Value; + +import com.oracle.graal.debug.Debug; +import com.oracle.graal.debug.Indent; +import com.oracle.graal.lir.LIRInsertionBuffer; +import com.oracle.graal.lir.LIRInstruction; +import com.oracle.graal.lir.VirtualStackSlot; +import com.oracle.graal.lir.framemap.FrameMap; +import com.oracle.graal.lir.framemap.FrameMapBuilderTool; + +/** + */ +final class TraceLocalMoveResolver { + + private static final int STACK_SLOT_IN_CALLER_FRAME_IDX = -1; + private final TraceLinearScan allocator; + + private int insertIdx; + private LIRInsertionBuffer insertionBuffer; // buffer where moves are inserted + + private final List<TraceInterval> mappingFrom; + private final List<Constant> mappingFromOpr; + private final List<TraceInterval> mappingTo; + private final int[] registerBlocked; + + private int[] stackBlocked; + private final int firstVirtualStackIndex; + + private int getStackArrayIndex(Value stackSlotValue) { + if (isStackSlot(stackSlotValue)) { + return getStackArrayIndex(asStackSlot(stackSlotValue)); + } + if (isVirtualStackSlot(stackSlotValue)) { + return getStackArrayIndex(asVirtualStackSlot(stackSlotValue)); + } + throw JVMCIError.shouldNotReachHere("value is not a stack slot: " + stackSlotValue); + } + + private int getStackArrayIndex(StackSlot stackSlot) { + int stackIdx; + if (stackSlot.isInCallerFrame()) { + // incoming stack arguments can be ignored + stackIdx = STACK_SLOT_IN_CALLER_FRAME_IDX; + } else { + assert stackSlot.getRawAddFrameSize() : "Unexpected stack slot: " + stackSlot; + int offset = -stackSlot.getRawOffset(); + assert 0 <= offset && offset < firstVirtualStackIndex : String.format("Wrong stack slot offset: %d (first virtual stack slot index: %d", offset, firstVirtualStackIndex); + stackIdx = offset; + } + return stackIdx; + } + + private int getStackArrayIndex(VirtualStackSlot virtualStackSlot) { + return firstVirtualStackIndex + virtualStackSlot.getId(); + } + + protected void setValueBlocked(Value location, int direction) { + assert direction == 1 || direction == -1 : "out of bounds"; + if (isStackSlotValue(location)) { + int stackIdx = getStackArrayIndex(location); + if (stackIdx == STACK_SLOT_IN_CALLER_FRAME_IDX) { + // incoming stack arguments can be ignored + return; + } + if (stackIdx >= stackBlocked.length) { + stackBlocked = Arrays.copyOf(stackBlocked, stackIdx + 1); + } + stackBlocked[stackIdx] += direction; + } else { + assert direction == 1 || direction == -1 : "out of bounds"; + if (isRegister(location)) { + registerBlocked[asRegister(location).number] += direction; + } else { + throw JVMCIError.shouldNotReachHere("unhandled value " + location); + } + } + } + + protected TraceInterval getMappingFrom(int i) { + return mappingFrom.get(i); + } + + protected int mappingFromSize() { + return mappingFrom.size(); + } + + protected int valueBlocked(Value location) { + if (isStackSlotValue(location)) { + int stackIdx = getStackArrayIndex(location); + if (stackIdx == STACK_SLOT_IN_CALLER_FRAME_IDX) { + // incoming stack arguments are always blocked (aka they can not be written) + return 1; + } + if (stackIdx >= stackBlocked.length) { + return 0; + } + return stackBlocked[stackIdx]; + } + if (isRegister(location)) { + return registerBlocked[asRegister(location).number]; + } + throw JVMCIError.shouldNotReachHere("unhandled value " + location); + } + + /* + * TODO (je) remove? + */ + protected static boolean areMultipleReadsAllowed() { + return true; + } + + boolean hasMappings() { + return mappingFrom.size() > 0; + } + + protected TraceLinearScan getAllocator() { + return allocator; + } + + protected TraceLocalMoveResolver(TraceLinearScan allocator) { + + this.allocator = allocator; + this.mappingFrom = new ArrayList<>(8); + this.mappingFromOpr = new ArrayList<>(8); + this.mappingTo = new ArrayList<>(8); + this.insertIdx = -1; + this.insertionBuffer = new LIRInsertionBuffer(); + this.registerBlocked = new int[allocator.getRegisters().length]; + FrameMapBuilderTool frameMapBuilderTool = (FrameMapBuilderTool) allocator.getFrameMapBuilder(); + FrameMap frameMap = frameMapBuilderTool.getFrameMap(); + this.stackBlocked = new int[frameMapBuilderTool.getNumberOfStackSlots()]; + this.firstVirtualStackIndex = !frameMap.frameNeedsAllocating() ? 0 : frameMap.currentFrameSize() + 1; + } + + protected 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 < stackBlocked.length; i++) { + assert stackBlocked[i] == 0 : "stack map must be empty before and after processing"; + } + for (int i = 0; i < getAllocator().getRegisters().length; i++) { + assert registerBlocked[i] == 0 : "register map must be empty before and after processing"; + } + checkMultipleReads(); + return true; + } + + protected void checkMultipleReads() { + // multiple reads are allowed in SSA LSRA + } + + private boolean verifyBeforeResolve() { + assert mappingFrom.size() == mappingFromOpr.size() : "length must be equal"; + assert mappingFrom.size() == mappingTo.size() : "length must be equal"; + assert insertIdx != -1 : "insert position not set"; + + int i; + int j; + if (!areMultipleReadsAllowed()) { + for (i = 0; i < mappingFrom.size(); i++) { + for (j = i + 1; j < mappingFrom.size(); j++) { + assert mappingFrom.get(i) == null || mappingFrom.get(i) != mappingFrom.get(j) : "cannot read from same interval twice"; + } + } + } + + for (i = 0; i < mappingTo.size(); i++) { + for (j = i + 1; j < mappingTo.size(); j++) { + assert mappingTo.get(i) != mappingTo.get(j) : "cannot write to same interval twice"; + } + } + + HashSet<Value> usedRegs = new HashSet<>(); + if (!areMultipleReadsAllowed()) { + for (i = 0; i < mappingFrom.size(); i++) { + TraceInterval interval = mappingFrom.get(i); + if (interval != null && !isIllegal(interval.location())) { + boolean unique = usedRegs.add(interval.location()); + assert unique : "cannot read from same register twice"; + } + } + } + + usedRegs.clear(); + for (i = 0; i < mappingTo.size(); i++) { + TraceInterval interval = mappingTo.get(i); + if (isIllegal(interval.location())) { + // After insertion the location may become illegal, so don't check it since multiple + // intervals might be illegal. + continue; + } + boolean unique = usedRegs.add(interval.location()); + assert unique : "cannot write to same register twice"; + } + + verifyStackSlotMapping(); + + return true; + } + + protected void verifyStackSlotMapping() { + // relax disjoint stack maps invariant + } + + // mark assignedReg and assignedRegHi of the interval as blocked + private void blockRegisters(TraceInterval interval) { + Value location = interval.location(); + if (mightBeBlocked(location)) { + assert areMultipleReadsAllowed() || valueBlocked(location) == 0 : "location already marked as used: " + location; + int direction = 1; + setValueBlocked(location, direction); + Debug.log("block %s", location); + } + } + + // mark assignedReg and assignedRegHi of the interval as unblocked + private void unblockRegisters(TraceInterval interval) { + Value location = interval.location(); + if (mightBeBlocked(location)) { + assert valueBlocked(location) > 0 : "location already marked as unused: " + location; + setValueBlocked(location, -1); + Debug.log("unblock %s", location); + } + } + + /** + * Checks if the {@linkplain TraceInterval#location() location} of {@code to} is not blocked or + * is only blocked by {@code from}. + */ + private boolean safeToProcessMove(TraceInterval from, TraceInterval to) { + Value fromReg = from != null ? from.location() : null; + + Value location = to.location(); + if (mightBeBlocked(location)) { + if ((valueBlocked(location) > 1 || (valueBlocked(location) == 1 && !isMoveToSelf(fromReg, location)))) { + return false; + } + } + + return true; + } + + protected static boolean isMoveToSelf(Value from, Value to) { + assert to != null; + if (to.equals(from)) { + return true; + } + if (from != null && isRegister(from) && isRegister(to) && asRegister(from).equals(asRegister(to))) { + assert LIRKind.verifyMoveKinds(to.getLIRKind(), from.getLIRKind()) : String.format("Same register but Kind mismatch %s <- %s", to, from); + return true; + } + return false; + } + + protected static boolean mightBeBlocked(Value location) { + if (isRegister(location)) { + return true; + } + if (isStackSlotValue(location)) { + return true; + } + return false; + } + + private void createInsertionBuffer(List<LIRInstruction> list) { + assert !insertionBuffer.initialized() : "overwriting existing buffer"; + insertionBuffer.init(list); + } + + private void appendInsertionBuffer() { + if (insertionBuffer.initialized()) { + insertionBuffer.finish(); + } + assert !insertionBuffer.initialized() : "must be uninitialized now"; + + insertIdx = -1; + } + + private void insertMove(TraceInterval fromInterval, TraceInterval toInterval) { + assert !fromInterval.operand.equals(toInterval.operand) : "from and to interval equal: " + fromInterval; + assert LIRKind.verifyMoveKinds(toInterval.kind(), fromInterval.kind()) : "move between different types"; + assert insertIdx != -1 : "must setup insert position first"; + + insertionBuffer.append(insertIdx, createMove(fromInterval.operand, toInterval.operand, fromInterval.location(), toInterval.location())); + + if (Debug.isLogEnabled()) { + Debug.log("insert move from %s to %s at %d", fromInterval, toInterval, insertIdx); + } + } + + /** + * @param fromOpr {@link TraceInterval#operand operand} of the {@code from} interval + * @param toOpr {@link TraceInterval#operand operand} of the {@code to} interval + * @param fromLocation {@link TraceInterval#location() location} of the {@code to} interval + * @param toLocation {@link TraceInterval#location() location} of the {@code to} interval + */ + protected LIRInstruction createMove(AllocatableValue fromOpr, AllocatableValue toOpr, AllocatableValue fromLocation, AllocatableValue toLocation) { + if (isStackSlotValue(toLocation) && isStackSlotValue(fromLocation)) { + return getAllocator().getSpillMoveFactory().createStackMove(toOpr, fromOpr); + } + return getAllocator().getSpillMoveFactory().createMove(toOpr, fromOpr); + } + + private void insertMove(Constant fromOpr, TraceInterval toInterval) { + assert insertIdx != -1 : "must setup insert position first"; + + AllocatableValue toOpr = toInterval.operand; + LIRInstruction move = getAllocator().getSpillMoveFactory().createLoad(toOpr, fromOpr); + insertionBuffer.append(insertIdx, move); + + if (Debug.isLogEnabled()) { + Debug.log("insert move from value %s to %s at %d", fromOpr, toInterval, insertIdx); + } + } + + @SuppressWarnings("try") + private void resolveMappings() { + try (Indent indent = Debug.logAndIndent("resolveMapping")) { + assert verifyBeforeResolve(); + if (Debug.isLogEnabled()) { + printMapping(); + } + + // Block all registers that are used as input operands of a move. + // When a register is blocked, no move to this register is emitted. + // This is necessary for detecting cycles in moves. + int i; + for (i = mappingFrom.size() - 1; i >= 0; i--) { + TraceInterval fromInterval = mappingFrom.get(i); + if (fromInterval != null) { + blockRegisters(fromInterval); + } + } + + int spillCandidate = -1; + while (mappingFrom.size() > 0) { + boolean processedInterval = false; + + for (i = mappingFrom.size() - 1; i >= 0; i--) { + TraceInterval fromInterval = mappingFrom.get(i); + TraceInterval toInterval = mappingTo.get(i); + + if (safeToProcessMove(fromInterval, toInterval)) { + // this interval can be processed because target is free + if (fromInterval != null) { + insertMove(fromInterval, toInterval); + unblockRegisters(fromInterval); + } else { + insertMove(mappingFromOpr.get(i), toInterval); + } + mappingFrom.remove(i); + mappingFromOpr.remove(i); + mappingTo.remove(i); + + processedInterval = true; + } else if (fromInterval != null && isRegister(fromInterval.location())) { + // this interval cannot be processed now because target is not free + // it starts in a register, so it is a possible candidate for spilling + spillCandidate = i; + } + } + + if (!processedInterval) { + breakCycle(spillCandidate); + } + } + } + + // check that all intervals have been processed + assert checkEmpty(); + } + + protected void breakCycle(int spillCandidate) { + if (spillCandidate != -1) { + // no move could be processed because there is a cycle in the move list + // (e.g. r1 . r2, r2 . r1), so one interval must be spilled to memory + assert spillCandidate != -1 : "no interval in register for spilling found"; + + // create a new spill interval and assign a stack slot to it + TraceInterval fromInterval1 = mappingFrom.get(spillCandidate); + // do not allocate a new spill slot for temporary interval, but + // use spill slot assigned to fromInterval. Otherwise moves from + // one stack slot to another can happen (not allowed by LIRAssembler + AllocatableValue spillSlot1 = fromInterval1.spillSlot(); + if (spillSlot1 == null) { + spillSlot1 = getAllocator().getFrameMapBuilder().allocateSpillSlot(fromInterval1.kind()); + fromInterval1.setSpillSlot(spillSlot1); + } + spillInterval(spillCandidate, fromInterval1, spillSlot1); + return; + } + assert mappingFromSize() > 1; + // Arbitrarily select the first entry for spilling. + int stackSpillCandidate = 0; + TraceInterval fromInterval = getMappingFrom(stackSpillCandidate); + assert isStackSlotValue(fromInterval.location()); + // allocate new stack slot + VirtualStackSlot spillSlot = getAllocator().getFrameMapBuilder().allocateSpillSlot(fromInterval.kind()); + spillInterval(stackSpillCandidate, fromInterval, spillSlot); + } + + protected void spillInterval(int spillCandidate, TraceInterval fromInterval, AllocatableValue spillSlot) { + assert mappingFrom.get(spillCandidate).equals(fromInterval); + TraceInterval spillInterval = getAllocator().createDerivedInterval(fromInterval); + spillInterval.setKind(fromInterval.kind()); + + // add a dummy range because real position is difficult to calculate + // Note: this range is a special case when the integrity of the allocation is + // checked + spillInterval.addRange(1, 2); + + spillInterval.assignLocation(spillSlot); + + if (Debug.isLogEnabled()) { + Debug.log("created new Interval for spilling: %s", spillInterval); + } + blockRegisters(spillInterval); + + // insert a move from register to stack and update the mapping + insertMove(fromInterval, spillInterval); + mappingFrom.set(spillCandidate, spillInterval); + unblockRegisters(fromInterval); + } + + @SuppressWarnings("try") + private void printMapping() { + try (Indent indent = Debug.logAndIndent("Mapping")) { + for (int i = mappingFrom.size() - 1; i >= 0; i--) { + TraceInterval fromInterval = mappingFrom.get(i); + TraceInterval toInterval = mappingTo.get(i); + String from; + Value to = toInterval.location(); + if (fromInterval == null) { + from = mappingFromOpr.get(i).toString(); + } else { + from = fromInterval.location().toString(); + } + Debug.log("move %s <- %s", from, to); + } + } + } + + void setInsertPosition(List<LIRInstruction> insertList, int insertIdx) { + assert this.insertIdx == -1 : "use moveInsertPosition instead of setInsertPosition when data already set"; + + createInsertionBuffer(insertList); + this.insertIdx = insertIdx; + } + + void moveInsertPosition(List<LIRInstruction> newInsertList, int newInsertIdx) { + if (insertionBuffer.lirList() != null && (insertionBuffer.lirList() != newInsertList || this.insertIdx != newInsertIdx)) { + // insert position changed . resolve current mappings + resolveMappings(); + } + + assert insertionBuffer.lirList() != newInsertList || newInsertIdx >= insertIdx : String.format("Decreasing insert index: old=%d new=%d", insertIdx, newInsertIdx); + + if (insertionBuffer.lirList() != newInsertList) { + // block changed . append insertionBuffer because it is + // bound to a specific block and create a new insertionBuffer + appendInsertionBuffer(); + createInsertionBuffer(newInsertList); + } + + this.insertIdx = newInsertIdx; + } + + public void addMapping(TraceInterval fromInterval, TraceInterval toInterval) { + + if (isIllegal(toInterval.location()) && toInterval.canMaterialize()) { + if (Debug.isLogEnabled()) { + Debug.log("no store to rematerializable interval %s needed", toInterval); + } + return; + } + if (isIllegal(fromInterval.location()) && fromInterval.canMaterialize()) { + // Instead of a reload, re-materialize the value + JavaConstant rematValue = fromInterval.getMaterializedValue(); + addMapping(rematValue, toInterval); + return; + } + if (Debug.isLogEnabled()) { + Debug.log("add move mapping from %s to %s", fromInterval, toInterval); + } + + assert !fromInterval.operand.equals(toInterval.operand) : "from and to interval equal: " + fromInterval; + assert LIRKind.verifyMoveKinds(toInterval.kind(), fromInterval.kind()) : String.format("Kind mismatch: %s vs. %s, from=%s, to=%s", fromInterval.kind(), toInterval.kind(), fromInterval, + toInterval); + mappingFrom.add(fromInterval); + mappingFromOpr.add(null); + mappingTo.add(toInterval); + } + + public void addMapping(Constant fromOpr, TraceInterval toInterval) { + if (Debug.isLogEnabled()) { + Debug.log("add move mapping from %s to %s", fromOpr, toInterval); + } + + mappingFrom.add(null); + mappingFromOpr.add(fromOpr); + mappingTo.add(toInterval); + } + + void resolveAndAppendMoves() { + if (hasMappings()) { + resolveMappings(); + } + appendInsertionBuffer(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/trace/lsra/UsePosList.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2015, 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.graal.lir.alloc.trace.lsra; + +import com.oracle.graal.compiler.common.util.IntList; +import com.oracle.graal.lir.alloc.trace.lsra.TraceInterval.RegisterPriority; + +/** + * List of use positions. Each entry in the list records the use position and register priority + * associated with the use position. The entries in the list are in descending order of use + * position. + * + */ +public final class UsePosList { + + private IntList list; + + /** + * Creates a use list. + * + * @param initialCapacity the initial capacity of the list in terms of entries + */ + public UsePosList(int initialCapacity) { + list = new IntList(initialCapacity * 2); + } + + private UsePosList(IntList list) { + this.list = list; + } + + /** + * Splits this list around a given position. All entries in this list with a use position + * greater or equal than {@code splitPos} are removed from this list and added to the returned + * list. + * + * @param splitPos the position for the split + * @return a use position list containing all entries removed from this list that have a use + * position greater or equal than {@code splitPos} + */ + public UsePosList splitAt(int splitPos) { + int i = size() - 1; + int len = 0; + while (i >= 0 && usePos(i) < splitPos) { + --i; + len += 2; + } + int listSplitIndex = (i + 1) * 2; + IntList childList = list; + list = IntList.copy(this.list, listSplitIndex, len); + childList.setSize(listSplitIndex); + UsePosList child = new UsePosList(childList); + return child; + } + + /** + * Gets the use position at a specified index in this list. + * + * @param index the index of the entry for which the use position is returned + * @return the use position of entry {@code index} in this list + */ + public int usePos(int index) { + return list.get(index << 1); + } + + /** + * Gets the register priority for the use position at a specified index in this list. + * + * @param index the index of the entry for which the register priority is returned + * @return the register priority of entry {@code index} in this list + */ + public RegisterPriority registerPriority(int index) { + return RegisterPriority.VALUES[list.get((index << 1) + 1)]; + } + + public void add(int usePos, RegisterPriority registerPriority) { + assert list.size() == 0 || usePos(size() - 1) > usePos; + list.add(usePos); + list.add(registerPriority.ordinal()); + } + + public int size() { + return list.size() >> 1; + } + + public void removeLowestUsePos() { + list.setSize(list.size() - 2); + } + + public void setRegisterPriority(int index, RegisterPriority registerPriority) { + list.set((index << 1) + 1, registerPriority.ordinal()); + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder("["); + for (int i = size() - 1; i >= 0; --i) { + if (buf.length() != 1) { + buf.append(", "); + } + RegisterPriority prio = registerPriority(i); + buf.append(usePos(i)).append(" -> ").append(prio.ordinal()).append(':').append(prio); + } + return buf.append("]").toString(); + } +}
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/asm/CompilationResultBuilder.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/asm/CompilationResultBuilder.java Tue Dec 08 12:30:15 2015 -0800 @@ -147,7 +147,7 @@ /** * Sets the {@linkplain CompilationResult#setTargetCode(byte[], int) code} and * {@linkplain CompilationResult#recordExceptionHandler(int, int) exception handler} fields of - * the compilation result. + * the compilation result and then {@linkplain #closeCompilationResult() closes} it. */ public void finish() { int position = asm.position(); @@ -160,6 +160,14 @@ compilationResult.recordExceptionHandler(codeOffset, ei.exceptionEdge.label().position()); } } + closeCompilationResult(); + } + + /** + * Calls {@link CompilationResult#close()} on {@link #compilationResult}. + */ + protected void closeCompilationResult() { + compilationResult.close(); } public void recordExceptionHandlers(int pcOffset, LIRFrameState info) { @@ -417,9 +425,9 @@ } } - public void reset() { + public void resetForEmittingCode() { asm.reset(); - compilationResult.reset(); + compilationResult.resetForEmittingCode(); if (exceptionInfoList != null) { exceptionInfoList.clear(); }
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/constopt/ConstantLoadOptimization.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/constopt/ConstantLoadOptimization.java Tue Dec 08 12:30:15 2015 -0800 @@ -77,7 +77,9 @@ } @Override - protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, LIRGeneratorTool lirGen) { + protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, + PreAllocationOptimizationContext context) { + LIRGeneratorTool lirGen = context.lirGen; new Optimization(lirGenRes.getLIR(), lirGen).apply(); }
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/dfa/LocationMarkerPhase.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/dfa/LocationMarkerPhase.java Tue Dec 08 12:30:15 2015 -0800 @@ -34,7 +34,6 @@ import jdk.vm.ci.meta.LIRKind; import jdk.vm.ci.meta.Value; -import com.oracle.graal.compiler.common.alloc.RegisterAllocationConfig; import com.oracle.graal.compiler.common.cfg.AbstractBlockBase; import com.oracle.graal.lir.LIR; import com.oracle.graal.lir.LIRFrameState; @@ -42,7 +41,6 @@ import com.oracle.graal.lir.framemap.FrameMap; import com.oracle.graal.lir.framemap.ReferenceMapBuilder; import com.oracle.graal.lir.gen.LIRGenerationResult; -import com.oracle.graal.lir.gen.LIRGeneratorTool.MoveFactory; import com.oracle.graal.lir.phases.AllocationPhase; /** @@ -52,8 +50,7 @@ public final class LocationMarkerPhase extends AllocationPhase { @Override - protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, MoveFactory spillMoveFactory, - RegisterAllocationConfig registerAllocationConfig) { + protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, AllocationContext context) { new Marker<B>(lirGenRes.getLIR(), lirGenRes.getFrameMap()).build(); }
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/dfa/MarkBasePointersPhase.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/dfa/MarkBasePointersPhase.java Tue Dec 08 12:30:15 2015 -0800 @@ -27,7 +27,6 @@ import jdk.vm.ci.code.TargetDescription; import jdk.vm.ci.meta.Value; -import com.oracle.graal.compiler.common.alloc.RegisterAllocationConfig; import com.oracle.graal.compiler.common.cfg.AbstractBlockBase; import com.oracle.graal.lir.LIR; import com.oracle.graal.lir.LIRFrameState; @@ -35,7 +34,6 @@ import com.oracle.graal.lir.Variable; import com.oracle.graal.lir.framemap.FrameMap; import com.oracle.graal.lir.gen.LIRGenerationResult; -import com.oracle.graal.lir.gen.LIRGeneratorTool.MoveFactory; import com.oracle.graal.lir.phases.AllocationPhase; import com.oracle.graal.lir.util.IndexedValueMap; import com.oracle.graal.lir.util.ValueSet; @@ -46,8 +44,7 @@ public final class MarkBasePointersPhase extends AllocationPhase { @Override - protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, MoveFactory spillMoveFactory, - RegisterAllocationConfig registerAllocationConfig) { + protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, AllocationContext context) { new Marker<B>(lirGenRes.getLIR(), null).build(); }
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/LIRGenerator.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/LIRGenerator.java Tue Dec 08 12:30:15 2015 -0800 @@ -276,7 +276,7 @@ if (op instanceof SimpleInfopointOp) { currentInfo = null; } else if (currentInfo != null) { - lirForBlock.add(new SimpleInfopointOp(InfopointReason.LINE_NUMBER, currentInfo)); + lirForBlock.add(new SimpleInfopointOp(InfopointReason.BYTECODE_POSITION, currentInfo)); currentInfo = null; } lirForBlock.add(op);
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/phases/AllocationPhase.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/phases/AllocationPhase.java Tue Dec 08 12:30:15 2015 -0800 @@ -22,20 +22,14 @@ */ package com.oracle.graal.lir.phases; -import java.util.List; - -import jdk.vm.ci.code.TargetDescription; - import com.oracle.graal.compiler.common.alloc.RegisterAllocationConfig; -import com.oracle.graal.compiler.common.cfg.AbstractBlockBase; -import com.oracle.graal.lir.gen.LIRGenerationResult; import com.oracle.graal.lir.gen.LIRGeneratorTool.MoveFactory; public abstract class AllocationPhase extends LIRPhase<AllocationPhase.AllocationContext> { public static final class AllocationContext { - private final MoveFactory spillMoveFactory; - private final RegisterAllocationConfig registerAllocationConfig; + public final MoveFactory spillMoveFactory; + public final RegisterAllocationConfig registerAllocationConfig; public AllocationContext(MoveFactory spillMoveFactory, RegisterAllocationConfig registerAllocationConfig) { this.spillMoveFactory = spillMoveFactory; @@ -43,12 +37,4 @@ } } - @Override - protected final <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, AllocationContext context) { - run(target, lirGenRes, codeEmittingOrder, linearScanOrder, context.spillMoveFactory, context.registerAllocationConfig); - } - - protected abstract <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, - MoveFactory spillMoveFactory, RegisterAllocationConfig registerAllocationConfig); - }
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/phases/PostAllocationOptimizationPhase.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/phases/PostAllocationOptimizationPhase.java Tue Dec 08 12:30:15 2015 -0800 @@ -22,31 +22,15 @@ */ package com.oracle.graal.lir.phases; -import java.util.List; - -import jdk.vm.ci.code.TargetDescription; - -import com.oracle.graal.compiler.common.cfg.AbstractBlockBase; import com.oracle.graal.lir.gen.BenchmarkCounterFactory; -import com.oracle.graal.lir.gen.LIRGenerationResult; public abstract class PostAllocationOptimizationPhase extends LIRPhase<PostAllocationOptimizationPhase.PostAllocationOptimizationContext> { public static final class PostAllocationOptimizationContext { - private final BenchmarkCounterFactory counterFactory; + public final BenchmarkCounterFactory counterFactory; public PostAllocationOptimizationContext(BenchmarkCounterFactory counterFactory) { this.counterFactory = counterFactory; } } - - @Override - protected final <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, - PostAllocationOptimizationContext context) { - run(target, lirGenRes, codeEmittingOrder, linearScanOrder, context.counterFactory); - } - - protected abstract <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, - BenchmarkCounterFactory counterFactory); - }
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/phases/PreAllocationOptimizationPhase.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/phases/PreAllocationOptimizationPhase.java Tue Dec 08 12:30:15 2015 -0800 @@ -22,18 +22,12 @@ */ package com.oracle.graal.lir.phases; -import java.util.List; - -import jdk.vm.ci.code.TargetDescription; - -import com.oracle.graal.compiler.common.cfg.AbstractBlockBase; -import com.oracle.graal.lir.gen.LIRGenerationResult; import com.oracle.graal.lir.gen.LIRGeneratorTool; public abstract class PreAllocationOptimizationPhase extends LIRPhase<PreAllocationOptimizationPhase.PreAllocationOptimizationContext> { public static final class PreAllocationOptimizationContext { - private final LIRGeneratorTool lirGen; + public final LIRGeneratorTool lirGen; public PreAllocationOptimizationContext(LIRGeneratorTool lirGen) { this.lirGen = lirGen; @@ -41,12 +35,4 @@ } - @Override - protected final <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, - PreAllocationOptimizationContext context) { - run(target, lirGenRes, codeEmittingOrder, linearScanOrder, context.lirGen); - } - - protected abstract <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, LIRGeneratorTool lirGen); - }
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/profiling/MoveProfiling.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/profiling/MoveProfiling.java Tue Dec 08 12:30:15 2015 -0800 @@ -54,7 +54,8 @@ @Override protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, - BenchmarkCounterFactory counterFactory) { + PostAllocationOptimizationContext context) { + BenchmarkCounterFactory counterFactory = context.counterFactory; new Analyzer(target, lirGenRes.getLIR(), counterFactory).run(); }
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/ssa/SSADestructionPhase.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/ssa/SSADestructionPhase.java Tue Dec 08 12:30:15 2015 -0800 @@ -39,7 +39,9 @@ public final class SSADestructionPhase extends PreAllocationOptimizationPhase { @Override - protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, LIRGeneratorTool lirGen) { + protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, + PreAllocationOptimizationContext context) { + LIRGeneratorTool lirGen = context.lirGen; LIR lir = lirGenRes.getLIR(); AbstractControlFlowGraph<?> cfg = lir.getControlFlowGraph(); for (AbstractBlockBase<?> block : cfg.getBlocks()) {
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/ssi/SSIConstructionPhase.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/ssi/SSIConstructionPhase.java Tue Dec 08 12:30:15 2015 -0800 @@ -46,7 +46,9 @@ public final class SSIConstructionPhase extends PreAllocationOptimizationPhase { @Override - protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, LIRGeneratorTool lirGen) { + protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, + PreAllocationOptimizationContext context) { + LIRGeneratorTool lirGen = context.lirGen; assert SSAUtil.verifySSAForm(lirGenRes.getLIR()); LIR lir = lirGenRes.getLIR(); new SSIBuilder(lir, lirGenRes.getFrameMapBuilder()).build(lirGen);
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/stackslotalloc/LSStackSlotAllocator.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/stackslotalloc/LSStackSlotAllocator.java Tue Dec 08 12:30:15 2015 -0800 @@ -44,7 +44,6 @@ import jdk.vm.ci.options.Option; import jdk.vm.ci.options.OptionType; -import com.oracle.graal.compiler.common.alloc.RegisterAllocationConfig; import com.oracle.graal.compiler.common.cfg.AbstractBlockBase; import com.oracle.graal.debug.Debug; import com.oracle.graal.debug.Debug.Scope; @@ -61,7 +60,6 @@ import com.oracle.graal.lir.framemap.SimpleVirtualStackSlot; import com.oracle.graal.lir.framemap.VirtualStackSlotRange; import com.oracle.graal.lir.gen.LIRGenerationResult; -import com.oracle.graal.lir.gen.LIRGeneratorTool.MoveFactory; import com.oracle.graal.lir.phases.AllocationPhase; /** @@ -91,8 +89,7 @@ private static final DebugTimer AssignSlotsTimer = Debug.timer("LSStackSlotAllocator[AssignSlots]"); @Override - protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, MoveFactory spillMoveFactory, - RegisterAllocationConfig registerAllocationConfig) { + protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, AllocationContext context) { lirGenRes.buildFrameMap(this); }
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/stackslotalloc/SimpleStackSlotAllocator.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/stackslotalloc/SimpleStackSlotAllocator.java Tue Dec 08 12:30:15 2015 -0800 @@ -31,7 +31,6 @@ import jdk.vm.ci.code.TargetDescription; import jdk.vm.ci.common.JVMCIError; -import com.oracle.graal.compiler.common.alloc.RegisterAllocationConfig; import com.oracle.graal.compiler.common.cfg.AbstractBlockBase; import com.oracle.graal.debug.Debug; import com.oracle.graal.debug.Debug.Scope; @@ -43,14 +42,12 @@ import com.oracle.graal.lir.framemap.SimpleVirtualStackSlot; import com.oracle.graal.lir.framemap.VirtualStackSlotRange; import com.oracle.graal.lir.gen.LIRGenerationResult; -import com.oracle.graal.lir.gen.LIRGeneratorTool.MoveFactory; import com.oracle.graal.lir.phases.AllocationPhase; public class SimpleStackSlotAllocator extends AllocationPhase implements StackSlotAllocator { @Override - protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, MoveFactory spillMoveFactory, - RegisterAllocationConfig registerAllocationConfig) { + protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, AllocationContext context) { lirGenRes.buildFrameMap(this); }
--- a/graal/com.oracle.graal.loop.phases/src/com/oracle/graal/loop/phases/LoopSafepointEliminationPhase.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.loop.phases/src/com/oracle/graal/loop/phases/LoopSafepointEliminationPhase.java Tue Dec 08 12:30:15 2015 -0800 @@ -53,13 +53,13 @@ } } } - for (LoopEx loop : loops.countedLoops()) { + for (LoopEx loop : loops.loops()) { for (LoopEndNode loopEnd : loop.loopBegin().loopEnds()) { Block b = loops.getCFG().blockFor(loopEnd); blocks: while (b != loop.loop().getHeader()) { assert b != null; for (FixedNode node : b.getNodes()) { - if (node instanceof Invoke || node instanceof ForeignCallNode) { + if (node instanceof Invoke || (node instanceof ForeignCallNode && ((ForeignCallNode) node).isGuaranteedSafepoint())) { loopEnd.disableSafepoint(); break blocks; }
--- a/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/LoopEx.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/LoopEx.java Tue Dec 08 12:30:15 2015 -0800 @@ -258,7 +258,6 @@ } else { return false; } - oneOff = true; break; } case LE:
--- a/graal/com.oracle.graal.microbenchmarks/src/com/oracle/graal/microbenchmarks/graal/ArrayDuplicationBenchmark.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.microbenchmarks/src/com/oracle/graal/microbenchmarks/graal/ArrayDuplicationBenchmark.java Tue Dec 08 12:30:15 2015 -0800 @@ -33,7 +33,7 @@ import org.openjdk.jmh.annotations.TearDown; @State(Scope.Thread) -public class ArrayDuplicationBenchmark { +public class ArrayDuplicationBenchmark extends GraalBenchmark { /** How large should the test-arrays be. */ private static final int TESTSIZE = 300;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.microbenchmarks/src/com/oracle/graal/microbenchmarks/graal/SimpleSyncBenchmark.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2015, 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.graal.microbenchmarks.graal; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +/** + * Benchmarks cost of non-contended synchronization. + */ +public class SimpleSyncBenchmark extends GraalBenchmark { + + public static class Person { + public int age; + + public Person(int age) { + this.age = age; + } + + public synchronized int getAge() { + return age; + } + + public synchronized void setAge(int age) { + this.age = age; + } + + public synchronized void setAgeIfNonZero(int age) { + if (age != 0) { + this.age = age; + } + } + } + + @State(Scope.Benchmark) + public static class ThreadState { + Person person = new Person(22); + int newAge = 45; + } + + @Benchmark + @Warmup(iterations = 20) + public void setAgeCond(ThreadState state) { + Person person = state.person; + person.setAgeIfNonZero(state.newAge); + } + + @Benchmark + @Warmup(iterations = 20) + public int getAge(ThreadState state) { + Person person = state.person; + return person.getAge(); + } + + @Benchmark + @Warmup(iterations = 20) + public int getAndIncAge(ThreadState state) { + Person person = state.person; + int oldAge = person.getAge(); + person.setAge(oldAge + 1); + return oldAge; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.microbenchmarks/src/com/oracle/graal/microbenchmarks/graal/TestJMH.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2015, 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.graal.microbenchmarks.graal; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Warmup; + +@Warmup(iterations = 1) +@Measurement(iterations = 1) +@Fork(1) +/** + * This dummy class is used to verify that the JMH microbenchmarking environment is set up properly. + */ +public class TestJMH { + + @Benchmark + public void testJMH() { + // This method was intentionally left blank. + } + +}
--- a/graal/com.oracle.graal.microbenchmarks/src/com/oracle/graal/microbenchmarks/graal/util/GraalUtil.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.microbenchmarks/src/com/oracle/graal/microbenchmarks/graal/util/GraalUtil.java Tue Dec 08 12:30:15 2015 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -29,11 +29,11 @@ import jdk.vm.ci.meta.ResolvedJavaMethod; import com.oracle.graal.graph.Node; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins; -import com.oracle.graal.graphbuilderconf.InvocationPlugins; import com.oracle.graal.java.GraphBuilderPhase; import com.oracle.graal.nodes.StructuredGraph; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugins; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; import com.oracle.graal.phases.OptimisticOptimizations; import com.oracle.graal.phases.PhaseSuite; import com.oracle.graal.phases.tiers.HighTierContext;
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ConstantNode.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ConstantNode.java Tue Dec 08 12:30:15 2015 -0800 @@ -463,21 +463,25 @@ } public static ConstantNode defaultForKind(JavaKind kind, StructuredGraph graph) { + return unique(graph, defaultForKind(kind)); + } + + public static ConstantNode defaultForKind(JavaKind kind) { switch (kind) { case Boolean: case Byte: case Char: case Short: case Int: - return ConstantNode.forInt(0, graph); + return ConstantNode.forInt(0); case Double: - return ConstantNode.forDouble(0.0, graph); + return ConstantNode.forDouble(0.0); case Float: - return ConstantNode.forFloat(0.0f, graph); + return ConstantNode.forFloat(0.0f); case Long: - return ConstantNode.forLong(0L, graph); + return ConstantNode.forLong(0L); case Object: - return ConstantNode.forConstant(JavaConstant.NULL_POINTER, null, graph); + return ConstantNode.forConstant(JavaConstant.NULL_POINTER, null); default: return null; }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/PiNode.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/PiNode.java Tue Dec 08 12:30:15 2015 -0800 @@ -36,6 +36,9 @@ import com.oracle.graal.graph.spi.CanonicalizerTool; import com.oracle.graal.nodeinfo.NodeInfo; import com.oracle.graal.nodes.extended.GuardingNode; +import com.oracle.graal.nodes.extended.UnsafeLoadNode; +import com.oracle.graal.nodes.java.LoadFieldNode; +import com.oracle.graal.nodes.java.LoadIndexedNode; import com.oracle.graal.nodes.spi.LIRLowerable; import com.oracle.graal.nodes.spi.NodeLIRBuilderTool; import com.oracle.graal.nodes.spi.ValueProxy; @@ -79,6 +82,10 @@ this.piStamp = stamp; } + public PiNode(ValueNode object, ValueNode anchor) { + this(object, object.stamp().join(StampFactory.objectNonNull()), anchor); + } + public PiNode(ValueNode object, ResolvedJavaType toType, boolean exactType, boolean nonNull) { this(object, StampFactory.object(toType, exactType, nonNull || StampTool.isPointerNonNull(object.stamp()), true)); } @@ -116,19 +123,43 @@ return this; } inferStamp(); - if (stamp().equals(object().stamp())) { - return object(); + ValueNode o = object(); + + // The pi node does not give any additional information => skip it. + if (stamp().equals(o.stamp())) { + return o; } - if (getGuard() != null) { - for (PiNode otherPi : getGuard().asNode().usages().filter(PiNode.class)) { - if (object() == otherPi.object() && stamp().equals(otherPi.stamp())) { - /* - * Two PiNodes with the same guard and same result, so return the one with the - * more precise piStamp. - */ - Stamp newStamp = piStamp.join(otherPi.piStamp); - if (newStamp.equals(otherPi.piStamp)) { - return otherPi; + + GuardingNode g = getGuard(); + if (g == null) { + + // Try to merge the pi node with a load node. + if (o instanceof LoadFieldNode) { + LoadFieldNode loadFieldNode = (LoadFieldNode) o; + loadFieldNode.setStamp(loadFieldNode.stamp().improveWith(this.piStamp)); + return loadFieldNode; + } else if (o instanceof UnsafeLoadNode) { + UnsafeLoadNode unsafeLoadNode = (UnsafeLoadNode) o; + unsafeLoadNode.setStamp(unsafeLoadNode.stamp().improveWith(this.piStamp)); + return unsafeLoadNode; + } else if (o instanceof LoadIndexedNode) { + LoadIndexedNode loadIndexedNode = (LoadIndexedNode) o; + loadIndexedNode.setStamp(loadIndexedNode.stamp().improveWith(this.piStamp)); + return loadIndexedNode; + } + } else { + for (Node n : g.asNode().usages()) { + if (n instanceof PiNode) { + PiNode otherPi = (PiNode) n; + if (o == otherPi.object() && stamp().equals(otherPi.stamp())) { + /* + * Two PiNodes with the same guard and same result, so return the one with + * the more precise piStamp. + */ + Stamp newStamp = piStamp.join(otherPi.piStamp); + if (newStamp.equals(otherPi.piStamp)) { + return otherPi; + } } } } @@ -148,6 +179,13 @@ return asNonNullClassIntrinsic(object, Class.class, true, true); } + /** + * Casts an object to have an exact, non-null stamp representing {@link Class}. + */ + public static Class<?> asNonNullObject(Object object) { + return asNonNullClassIntrinsic(object, Object.class, false, true); + } + @NodeIntrinsic(PiNode.class) private static native Class<?> asNonNullClassIntrinsic(Object object, @ConstantNodeParameter Class<?> toType, @ConstantNodeParameter boolean exactType, @ConstantNodeParameter boolean nonNull); @@ -158,13 +196,20 @@ public static native Object piCast(Object object, @ConstantNodeParameter Stamp stamp); /** - * Changes the stamp of an object and ensures the newly stamped value does float above a given - * anchor. + * Changes the stamp of an object and ensures the newly stamped value does not float above a + * given anchor. */ @NodeIntrinsic public static native Object piCast(Object object, @ConstantNodeParameter Stamp stamp, GuardingNode anchor); /** + * Changes the stamp of an object and ensures the newly stamped value is non-null and does not + * float above a given anchor. + */ + @NodeIntrinsic + public static native Object piCastNonNull(Object object, GuardingNode anchor); + + /** * Changes the stamp of an object to represent a given type and to indicate that the object is * not null. */
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/ConditionalNode.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/ConditionalNode.java Tue Dec 08 12:30:15 2015 -0800 @@ -27,6 +27,8 @@ import com.oracle.graal.compiler.common.calc.Condition; import com.oracle.graal.compiler.common.type.IntegerStamp; +import com.oracle.graal.compiler.common.type.Stamp; +import com.oracle.graal.compiler.common.type.StampFactory; import com.oracle.graal.graph.NodeClass; import com.oracle.graal.graph.spi.Canonicalizable; import com.oracle.graal.graph.spi.CanonicalizerTool; @@ -83,7 +85,33 @@ @Override public boolean inferStamp() { - return updateStamp(trueValue.stamp().meet(falseValue.stamp())); + Stamp valueStamp = trueValue.stamp().meet(falseValue.stamp()); + if (condition instanceof IntegerLessThanNode) { + IntegerLessThanNode lessThan = (IntegerLessThanNode) condition; + if (lessThan.getX() == trueValue && lessThan.getY() == falseValue) { + // this encodes a min operation + JavaConstant constant = lessThan.getX().asJavaConstant(); + if (constant == null) { + constant = lessThan.getY().asJavaConstant(); + } + if (constant != null) { + IntegerStamp bounds = StampFactory.forInteger(constant.getJavaKind(), constant.getJavaKind().getMinValue(), constant.asLong()); + valueStamp = valueStamp.join(bounds); + } + } else if (lessThan.getX() == falseValue && lessThan.getY() == trueValue) { + // this encodes a max operation + JavaConstant constant = lessThan.getX().asJavaConstant(); + if (constant == null) { + constant = lessThan.getY().asJavaConstant(); + } + if (constant != null) { + IntegerStamp bounds = StampFactory.forInteger(constant.getJavaKind(), constant.asLong(), constant.getJavaKind().getMaxValue()); + valueStamp = valueStamp.join(bounds); + } + } + + } + return updateStamp(valueStamp); } public ValueNode trueValue() { @@ -124,6 +152,27 @@ return trueValue(); } + if (condition instanceof IntegerLessThanNode && trueValue().stamp() instanceof IntegerStamp) { + /* + * Convert a conditional add ((x < 0) ? (x + y) : x) into (x + (y & (x >> (bits - 1)))) + * to avoid the test. + */ + IntegerLessThanNode lt = (IntegerLessThanNode) condition; + if (lt.getY().isConstant() && lt.getY().asConstant().isDefaultForKind()) { + if (falseValue() == lt.getX()) { + if (trueValue() instanceof AddNode) { + AddNode add = (AddNode) trueValue(); + if (add.getX() == falseValue()) { + int bits = ((IntegerStamp) trueValue().stamp()).getBits(); + ValueNode shift = new RightShiftNode(lt.getX(), ConstantNode.forIntegerBits(32, bits - 1)); + ValueNode and = new AndNode(shift, add.getY()); + return new AddNode(add.getX(), and); + } + } + } + } + } + return this; }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/cfg/ControlFlowGraph.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/cfg/ControlFlowGraph.java Tue Dec 08 12:30:15 2015 -0800 @@ -297,7 +297,7 @@ } } - if (graph.hasValueProxies()) { + if (graph.getGuardsStage() != GuardsStage.AFTER_FSA) { // The following loop can add new blocks to the end of the loop's block list. int size = loop.getBlocks().size(); for (int i = 0; i < size; ++i) { @@ -353,7 +353,7 @@ if (block.getLoop() == loop) { nextState = stepOut; } else { - assert block.loop == loop.getParent(); + assert block.loop == loop.getParent() : block; block.loop = c.loop; assert !c.loop.getBlocks().contains(block);
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/ForeignCallNode.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/ForeignCallNode.java Tue Dec 08 12:30:15 2015 -0800 @@ -42,6 +42,7 @@ import com.oracle.graal.nodes.DeoptimizingNode; import com.oracle.graal.nodes.FrameState; import com.oracle.graal.nodes.ValueNode; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderContext; import com.oracle.graal.nodes.memory.AbstractMemoryCheckpoint; import com.oracle.graal.nodes.memory.MemoryCheckpoint; import com.oracle.graal.nodes.spi.LIRLowerable; @@ -61,11 +62,36 @@ protected final ForeignCallDescriptor descriptor; protected int bci = BytecodeFrame.UNKNOWN_BCI; - public ForeignCallNode(@InjectedNodeParameter ForeignCallsProvider foreignCalls, ForeignCallDescriptor descriptor, ValueNode... arguments) { + public static boolean intrinsify(GraphBuilderContext b, @InjectedNodeParameter Stamp returnStamp, @InjectedNodeParameter ForeignCallsProvider foreignCalls, ForeignCallDescriptor descriptor, + ValueNode... arguments) { + ForeignCallNode node = new ForeignCallNode(foreignCalls, descriptor, arguments); + node.setStamp(returnStamp); + + /* + * Need to update the BCI of a ForeignCallNode so that it gets the stateDuring in the case + * that the foreign call can deoptimize. As with all deoptimization, we need a state in a + * non-intrinsic method. + */ + GraphBuilderContext nonIntrinsicAncestor = b.getNonIntrinsicAncestor(); + if (nonIntrinsicAncestor != null) { + node.setBci(nonIntrinsicAncestor.bci()); + } + + JavaKind returnKind = returnStamp.getStackKind(); + if (returnKind == JavaKind.Void) { + b.add(node); + } else { + b.addPush(returnKind, node); + } + + return true; + } + + public ForeignCallNode(ForeignCallsProvider foreignCalls, ForeignCallDescriptor descriptor, ValueNode... arguments) { this(TYPE, foreignCalls, descriptor, arguments); } - public ForeignCallNode(@InjectedNodeParameter ForeignCallsProvider foreignCalls, ForeignCallDescriptor descriptor, Stamp stamp, List<ValueNode> arguments) { + public ForeignCallNode(ForeignCallsProvider foreignCalls, ForeignCallDescriptor descriptor, Stamp stamp, List<ValueNode> arguments) { super(TYPE, stamp); this.arguments = new NodeInputList<>(this, arguments); this.descriptor = descriptor; @@ -73,7 +99,7 @@ assert descriptor.getArgumentTypes().length == this.arguments.size() : "wrong number of arguments to " + this; } - public ForeignCallNode(@InjectedNodeParameter ForeignCallsProvider foreignCalls, ForeignCallDescriptor descriptor, Stamp stamp) { + public ForeignCallNode(ForeignCallsProvider foreignCalls, ForeignCallDescriptor descriptor, Stamp stamp) { super(TYPE, stamp); this.arguments = new NodeInputList<>(this); this.descriptor = descriptor; @@ -176,4 +202,8 @@ public boolean canDeoptimize() { return foreignCalls.canDeoptimize(descriptor); } + + public boolean isGuaranteedSafepoint() { + return foreignCalls.isGuaranteedSafepoint(descriptor); + } }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/LoadHubNode.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/LoadHubNode.java Tue Dec 08 12:30:15 2015 -0800 @@ -34,9 +34,9 @@ import com.oracle.graal.graph.spi.CanonicalizerTool; import com.oracle.graal.nodeinfo.NodeInfo; import com.oracle.graal.nodes.ConstantNode; -import com.oracle.graal.nodes.FloatingGuardedNode; import com.oracle.graal.nodes.StructuredGraph; import com.oracle.graal.nodes.ValueNode; +import com.oracle.graal.nodes.calc.FloatingNode; import com.oracle.graal.nodes.spi.Lowerable; import com.oracle.graal.nodes.spi.LoweringTool; import com.oracle.graal.nodes.spi.StampProvider; @@ -47,7 +47,7 @@ * Loads an object's hub. The object is not null-checked by this operation. */ @NodeInfo -public final class LoadHubNode extends FloatingGuardedNode implements Lowerable, Canonicalizable, Virtualizable { +public final class LoadHubNode extends FloatingNode implements Lowerable, Canonicalizable, Virtualizable { public static final NodeClass<LoadHubNode> TYPE = NodeClass.create(LoadHubNode.class); @Input ValueNode value; @@ -67,20 +67,15 @@ if (synonym != null) { return synonym; } - return new LoadHubNode(stamp, value, null); + return new LoadHubNode(stamp, value); } public LoadHubNode(@InjectedNodeParameter StampProvider stampProvider, ValueNode value) { - this(stampProvider, value, null); + this(hubStamp(stampProvider, value), value); } - public LoadHubNode(@InjectedNodeParameter StampProvider stampProvider, ValueNode value, ValueNode guard) { - this(hubStamp(stampProvider, value), value, guard); - } - - public LoadHubNode(Stamp stamp, ValueNode value, ValueNode guard) { - super(TYPE, stamp, (GuardingNode) guard); - assert value != guard; + public LoadHubNode(Stamp stamp, ValueNode value) { + super(TYPE, stamp); this.value = value; }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/UnsafeCopyNode.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/UnsafeCopyNode.java Tue Dec 08 12:30:15 2015 -0800 @@ -25,93 +25,24 @@ import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.LocationIdentity; -import com.oracle.graal.compiler.common.type.StampFactory; -import com.oracle.graal.graph.NodeClass; -import com.oracle.graal.nodeinfo.InputType; -import com.oracle.graal.nodeinfo.NodeInfo; -import com.oracle.graal.nodes.FixedWithNextNode; -import com.oracle.graal.nodes.FrameState; -import com.oracle.graal.nodes.StateSplit; +import com.oracle.graal.graph.Node.ConstantNodeParameter; +import com.oracle.graal.graph.Node.NodeIntrinsic; import com.oracle.graal.nodes.ValueNode; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderContext; /** * Copy a value at a location specified as an offset relative to a source object to another location * specified as an offset relative to destination object. No null checks are performed. - * - * This node must be replaced during processing of node intrinsics with an {@link UnsafeLoadNode} - * and {@link UnsafeStoreNode} pair. */ -@NodeInfo -public final class UnsafeCopyNode extends FixedWithNextNode implements StateSplit { - - public static final NodeClass<UnsafeCopyNode> TYPE = NodeClass.create(UnsafeCopyNode.class); - @Input ValueNode sourceObject; - @Input ValueNode destinationObject; - @Input ValueNode sourceOffset; - @Input ValueNode destinationOffset; - protected final JavaKind accessKind; - protected final LocationIdentity locationIdentity; - @OptionalInput(InputType.State) FrameState stateAfter; - - public UnsafeCopyNode(ValueNode sourceObject, ValueNode sourceOffset, ValueNode destinationObject, ValueNode destinationOffset, JavaKind accessKind, LocationIdentity locationIdentity) { - this(sourceObject, sourceOffset, destinationObject, destinationOffset, accessKind, locationIdentity, null); - } - - public UnsafeCopyNode(ValueNode sourceObject, ValueNode sourceOffset, ValueNode destinationObject, ValueNode destinationOffset, JavaKind accessKind, LocationIdentity locationIdentity, - FrameState stateAfter) { - super(TYPE, StampFactory.forVoid()); - this.sourceObject = sourceObject; - this.sourceOffset = sourceOffset; - this.destinationObject = destinationObject; - this.destinationOffset = destinationOffset; - this.accessKind = accessKind; - this.locationIdentity = locationIdentity; - this.stateAfter = stateAfter; - assert accessKind != JavaKind.Void && accessKind != JavaKind.Illegal; - } - - public ValueNode sourceObject() { - return sourceObject; - } +public final class UnsafeCopyNode { - public ValueNode destinationObject() { - return destinationObject; - } - - public LocationIdentity getLocationIdentity() { - return locationIdentity; - } - - public ValueNode sourceOffset() { - return sourceOffset; - } - - public ValueNode destinationOffset() { - return destinationOffset; - } - - public JavaKind accessKind() { - return accessKind; - } - - public FrameState stateAfter() { - return stateAfter; - } - - public void setStateAfter(FrameState x) { - assert x == null || x.isAlive() : "frame state must be in a graph"; - updateUsages(stateAfter, x); - stateAfter = x; - } - - public boolean hasSideEffect() { + public static boolean intrinsify(GraphBuilderContext b, ValueNode sourceObject, ValueNode sourceOffset, ValueNode destinationObject, ValueNode destinationOffset, JavaKind accessKind, + LocationIdentity locationIdentity) { + UnsafeLoadNode value = b.add(new UnsafeLoadNode(sourceObject, sourceOffset, accessKind, locationIdentity)); + b.add(new UnsafeStoreNode(destinationObject, destinationOffset, value, accessKind, locationIdentity)); return true; } - public FrameState getState() { - return stateAfter; - } - @NodeIntrinsic public static native void copy(Object srcObject, long srcOffset, Object destObject, long destOffset, @ConstantNodeParameter JavaKind kind, @ConstantNodeParameter LocationIdentity locationIdentity); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/graphbuilderconf/ForeignCallPlugin.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,50 @@ +/* + * 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.graal.nodes.graphbuilderconf; + +import jdk.vm.ci.meta.ResolvedJavaMethod; + +import com.oracle.graal.compiler.common.spi.ForeignCallDescriptor; +import com.oracle.graal.compiler.common.spi.ForeignCallsProvider; +import com.oracle.graal.nodes.ValueNode; +import com.oracle.graal.nodes.extended.ForeignCallNode; + +/** + * {@link InvocationPlugin} for converting a method call directly to a foreign call. + */ +public final class ForeignCallPlugin implements InvocationPlugin { + private final ForeignCallsProvider foreignCalls; + private final ForeignCallDescriptor descriptor; + + public ForeignCallPlugin(ForeignCallsProvider foreignCalls, ForeignCallDescriptor descriptor) { + this.foreignCalls = foreignCalls; + this.descriptor = descriptor; + } + + public boolean execute(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode[] args) { + ForeignCallNode foreignCall = new ForeignCallNode(foreignCalls, descriptor, args); + foreignCall.setBci(b.bci()); + b.addPush(targetMethod.getSignature().getReturnKind(), foreignCall); + return true; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/graphbuilderconf/GeneratedInvocationPlugin.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,32 @@ +/* + * 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.graal.nodes.graphbuilderconf; + +import jdk.vm.ci.meta.ResolvedJavaMethod; + +import com.oracle.graal.nodes.ValueNode; + +public abstract class GeneratedInvocationPlugin implements InvocationPlugin { + + public abstract boolean execute(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode[] args); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/graphbuilderconf/GraphBuilderConfiguration.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,301 @@ +/* + * Copyright (c) 2011, 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.graal.nodes.graphbuilderconf; + +import java.util.Arrays; + +import jdk.vm.ci.meta.ResolvedJavaType; + +import com.oracle.graal.compiler.common.GraalOptions; +import com.oracle.graal.nodes.FullInfopointNode; +import com.oracle.graal.nodes.SimpleInfopointNode; + +public class GraphBuilderConfiguration { + + public static class Plugins { + private final InvocationPlugins invocationPlugins; + private NodePlugin[] nodePlugins; + private ParameterPlugin[] parameterPlugins; + private InlineInvokePlugin[] inlineInvokePlugins; + private LoopExplosionPlugin loopExplosionPlugin; + + /** + * Creates a copy of a given set of plugins. The {@link InvocationPlugins} in + * {@code copyFrom} become the {@linkplain InvocationPlugins#getParent() default} + * {@linkplain #getInvocationPlugins() invocation plugins} in this object. + */ + public Plugins(Plugins copyFrom) { + this.invocationPlugins = new InvocationPlugins(copyFrom.invocationPlugins); + this.nodePlugins = copyFrom.nodePlugins; + this.parameterPlugins = copyFrom.parameterPlugins; + this.inlineInvokePlugins = copyFrom.inlineInvokePlugins; + this.loopExplosionPlugin = copyFrom.loopExplosionPlugin; + } + + /** + * Creates a new set of plugins. + * + * @param invocationPlugins the {@linkplain #getInvocationPlugins() invocation plugins} in + * this object + */ + public Plugins(InvocationPlugins invocationPlugins) { + this.invocationPlugins = invocationPlugins; + this.nodePlugins = new NodePlugin[0]; + this.parameterPlugins = new ParameterPlugin[0]; + this.inlineInvokePlugins = new InlineInvokePlugin[0]; + } + + public InvocationPlugins getInvocationPlugins() { + return invocationPlugins; + } + + public NodePlugin[] getNodePlugins() { + return nodePlugins; + } + + public void appendNodePlugin(NodePlugin plugin) { + nodePlugins = Arrays.copyOf(nodePlugins, nodePlugins.length + 1); + nodePlugins[nodePlugins.length - 1] = plugin; + } + + public void prependNodePlugin(NodePlugin plugin) { + NodePlugin[] newPlugins = new NodePlugin[nodePlugins.length + 1]; + System.arraycopy(nodePlugins, 0, newPlugins, 1, nodePlugins.length); + newPlugins[0] = plugin; + nodePlugins = newPlugins; + } + + public void clearNodePlugin() { + nodePlugins = new NodePlugin[0]; + } + + public ParameterPlugin[] getParameterPlugins() { + return parameterPlugins; + } + + public void appendParameterPlugin(ParameterPlugin plugin) { + parameterPlugins = Arrays.copyOf(parameterPlugins, parameterPlugins.length + 1); + parameterPlugins[parameterPlugins.length - 1] = plugin; + } + + public void prependParameterPlugin(ParameterPlugin plugin) { + ParameterPlugin[] newPlugins = new ParameterPlugin[parameterPlugins.length + 1]; + System.arraycopy(parameterPlugins, 0, newPlugins, 1, parameterPlugins.length); + newPlugins[0] = plugin; + parameterPlugins = newPlugins; + } + + public void clearParameterPlugin() { + parameterPlugins = new ParameterPlugin[0]; + } + + public InlineInvokePlugin[] getInlineInvokePlugins() { + return inlineInvokePlugins; + } + + public void appendInlineInvokePlugin(InlineInvokePlugin plugin) { + inlineInvokePlugins = Arrays.copyOf(inlineInvokePlugins, inlineInvokePlugins.length + 1); + inlineInvokePlugins[inlineInvokePlugins.length - 1] = plugin; + } + + public void prependInlineInvokePlugin(InlineInvokePlugin plugin) { + InlineInvokePlugin[] newPlugins = new InlineInvokePlugin[inlineInvokePlugins.length + 1]; + System.arraycopy(inlineInvokePlugins, 0, newPlugins, 1, inlineInvokePlugins.length); + newPlugins[0] = plugin; + inlineInvokePlugins = newPlugins; + } + + public void clearInlineInvokePlugins() { + inlineInvokePlugins = new InlineInvokePlugin[0]; + } + + public LoopExplosionPlugin getLoopExplosionPlugin() { + return loopExplosionPlugin; + } + + public void setLoopExplosionPlugin(LoopExplosionPlugin plugin) { + this.loopExplosionPlugin = plugin; + } + } + + private static final ResolvedJavaType[] EMPTY = new ResolvedJavaType[]{}; + + private final boolean eagerResolving; + private final boolean omitAllExceptionEdges; + private final boolean omitAssertions; + private final ResolvedJavaType[] skippedExceptionTypes; + private final DebugInfoMode debugInfoMode; + private final boolean clearNonLiveLocals; + private boolean useProfiling; + private final Plugins plugins; + + public static enum DebugInfoMode { + SafePointsOnly, + /** + * This mode inserts {@link SimpleInfopointNode}s in places where no safepoints would be + * inserted: inlining boundaries, and line number switches. + * <p> + * In this mode the infopoint only have a location (method and bytecode index) and no + * values. + * <p> + * This is useful to have better program counter to bci mapping and has no influence on the + * generated code. However it can increase the amount of metadata and does not allow access + * to accessing values at runtime. + */ + Simple, + /** + * In this mode, {@link FullInfopointNode}s are generated in the same locations as in + * {@link #Simple} mode but the infopoints have access to the runtime values. + * <p> + * This is relevant when code is to be generated for native, machine-code level debugging + * but can have a limit the amount of optimization applied to the code. + */ + Full, + } + + protected GraphBuilderConfiguration(boolean eagerResolving, boolean omitAllExceptionEdges, boolean omitAssertions, DebugInfoMode debugInfoMode, ResolvedJavaType[] skippedExceptionTypes, + boolean clearNonLiveLocals, Plugins plugins) { + this.eagerResolving = eagerResolving; + this.omitAllExceptionEdges = omitAllExceptionEdges; + this.omitAssertions = omitAssertions; + this.debugInfoMode = debugInfoMode; + this.skippedExceptionTypes = skippedExceptionTypes; + this.clearNonLiveLocals = clearNonLiveLocals; + this.useProfiling = true; + this.plugins = plugins; + } + + /** + * Creates a copy of this configuration with all its plugins. The {@link InvocationPlugins} in + * this configuration become the {@linkplain InvocationPlugins#getParent() parent} of the + * {@link InvocationPlugins} in the copy. + */ + public GraphBuilderConfiguration copy() { + Plugins newPlugins = new Plugins(plugins); + GraphBuilderConfiguration result = new GraphBuilderConfiguration(eagerResolving, omitAllExceptionEdges, omitAssertions, debugInfoMode, skippedExceptionTypes, clearNonLiveLocals, newPlugins); + result.useProfiling = useProfiling; + return result; + } + + public boolean getUseProfiling() { + return useProfiling; + } + + public void setUseProfiling(boolean b) { + this.useProfiling = b; + } + + public GraphBuilderConfiguration withEagerResolving(boolean newEagerResolving) { + return new GraphBuilderConfiguration(newEagerResolving, omitAllExceptionEdges, omitAssertions, debugInfoMode, skippedExceptionTypes, clearNonLiveLocals, plugins); + } + + public GraphBuilderConfiguration withSkippedExceptionTypes(ResolvedJavaType[] newSkippedExceptionTypes) { + return new GraphBuilderConfiguration(eagerResolving, omitAllExceptionEdges, omitAssertions, debugInfoMode, newSkippedExceptionTypes, clearNonLiveLocals, plugins); + } + + public GraphBuilderConfiguration withOmitAllExceptionEdges(boolean newOmitAllExceptionEdges) { + return new GraphBuilderConfiguration(eagerResolving, newOmitAllExceptionEdges, omitAssertions, debugInfoMode, skippedExceptionTypes, clearNonLiveLocals, plugins); + } + + public GraphBuilderConfiguration withOmitAssertions(boolean newOmitAssertions) { + return new GraphBuilderConfiguration(eagerResolving, omitAllExceptionEdges, newOmitAssertions, debugInfoMode, skippedExceptionTypes, clearNonLiveLocals, plugins); + } + + public GraphBuilderConfiguration withDebugInfoMode(DebugInfoMode newDebugInfoMode) { + ResolvedJavaType[] newSkippedExceptionTypes = skippedExceptionTypes == EMPTY ? EMPTY : Arrays.copyOf(skippedExceptionTypes, skippedExceptionTypes.length); + return new GraphBuilderConfiguration(eagerResolving, omitAllExceptionEdges, omitAssertions, newDebugInfoMode, newSkippedExceptionTypes, clearNonLiveLocals, plugins); + } + + public GraphBuilderConfiguration withClearNonLiveLocals(boolean newClearNonLiveLocals) { + return new GraphBuilderConfiguration(eagerResolving, omitAllExceptionEdges, omitAssertions, debugInfoMode, skippedExceptionTypes, newClearNonLiveLocals, plugins); + } + + public ResolvedJavaType[] getSkippedExceptionTypes() { + return skippedExceptionTypes; + } + + public boolean eagerResolving() { + return eagerResolving; + } + + public boolean omitAllExceptionEdges() { + return omitAllExceptionEdges; + } + + public boolean omitAssertions() { + return omitAssertions; + } + + public boolean insertNonSafepointDebugInfo() { + return debugInfoMode.ordinal() >= DebugInfoMode.Simple.ordinal(); + } + + public boolean insertFullDebugInfo() { + return debugInfoMode.ordinal() >= DebugInfoMode.Full.ordinal(); + } + + public boolean insertSimpleDebugInfo() { + return debugInfoMode == DebugInfoMode.Simple; + } + + public boolean clearNonLiveLocals() { + return clearNonLiveLocals; + } + + public static GraphBuilderConfiguration getDefault(Plugins plugins) { + return new GraphBuilderConfiguration(false, false, false, DebugInfoMode.SafePointsOnly, EMPTY, GraalOptions.OptClearNonLiveLocals.getValue(), plugins); + } + + public static GraphBuilderConfiguration getInfopointDefault(Plugins plugins) { + return new GraphBuilderConfiguration(true, false, false, DebugInfoMode.Simple, EMPTY, GraalOptions.OptClearNonLiveLocals.getValue(), plugins); + } + + public static GraphBuilderConfiguration getEagerDefault(Plugins plugins) { + return new GraphBuilderConfiguration(true, false, false, DebugInfoMode.SafePointsOnly, EMPTY, GraalOptions.OptClearNonLiveLocals.getValue(), plugins); + } + + public static GraphBuilderConfiguration getInfopointEagerDefault(Plugins plugins) { + return new GraphBuilderConfiguration(true, false, false, DebugInfoMode.Simple, EMPTY, GraalOptions.OptClearNonLiveLocals.getValue(), plugins); + } + + public static GraphBuilderConfiguration getSnippetDefault(Plugins plugins) { + return new GraphBuilderConfiguration(true, true, false, DebugInfoMode.SafePointsOnly, EMPTY, GraalOptions.OptClearNonLiveLocals.getValue(), plugins); + } + + public static GraphBuilderConfiguration getFullDebugDefault(Plugins plugins) { + return new GraphBuilderConfiguration(true, false, false, DebugInfoMode.Full, EMPTY, GraalOptions.OptClearNonLiveLocals.getValue(), plugins); + } + + /** + * Returns {@code true} if it is an error for a class/field/method resolution to fail. The + * default is the same result as returned by {@link #eagerResolving()}. However, it may be + * overridden to allow failure even when {@link #eagerResolving} is {@code true}. + */ + public boolean unresolvedIsError() { + return eagerResolving; + } + + public Plugins getPlugins() { + return plugins; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/graphbuilderconf/GraphBuilderContext.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,266 @@ +/* + * 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.graal.nodes.graphbuilderconf; + +import static com.oracle.graal.compiler.common.type.StampFactory.objectNonNull; +import static jdk.vm.ci.meta.DeoptimizationAction.InvalidateReprofile; +import static jdk.vm.ci.meta.DeoptimizationReason.NullCheckException; +import jdk.vm.ci.code.BailoutException; +import jdk.vm.ci.meta.Assumptions; +import jdk.vm.ci.meta.ConstantReflectionProvider; +import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.JavaType; +import jdk.vm.ci.meta.MetaAccessProvider; +import jdk.vm.ci.meta.ResolvedJavaMethod; +import jdk.vm.ci.meta.ResolvedJavaType; + +import com.oracle.graal.compiler.common.type.ObjectStamp; +import com.oracle.graal.compiler.common.type.Stamp; +import com.oracle.graal.compiler.common.type.StampFactory; +import com.oracle.graal.nodes.CallTargetNode.InvokeKind; +import com.oracle.graal.nodes.FixedGuardNode; +import com.oracle.graal.nodes.PiNode; +import com.oracle.graal.nodes.StateSplit; +import com.oracle.graal.nodes.StructuredGraph; +import com.oracle.graal.nodes.ValueNode; +import com.oracle.graal.nodes.calc.IsNullNode; +import com.oracle.graal.nodes.spi.StampProvider; +import com.oracle.graal.nodes.type.StampTool; + +/** + * Used by a {@link GraphBuilderPlugin} to interface with an object that parses the bytecode of a + * single {@linkplain #getMethod() method} as part of building a {@linkplain #getGraph() graph} . + */ +public interface GraphBuilderContext { + + /** + * Raw operation for adding a node to the graph when neither {@link #add} nor + * {@link #addPush(JavaKind, ValueNode)} can be used. + * + * @return either the node added or an equivalent node + */ + <T extends ValueNode> T append(T value); + + /** + * Adds the given node to the graph and also adds recursively all referenced inputs. + * + * @param value the node to be added to the graph + * @return either the node added or an equivalent node + */ + <T extends ValueNode> T recursiveAppend(T value); + + /** + * Pushes a given value to the frame state stack using an explicit kind. This should be used + * when {@code value.getJavaKind()} is different from the kind that the bytecode instruction + * currently being parsed pushes to the stack. + * + * @param kind the kind to use when type checking this operation + * @param value the value to push to the stack. The value must already have been + * {@linkplain #append(ValueNode) appended}. + */ + void push(JavaKind kind, ValueNode value); + + /** + * Adds a node to the graph. If the returned node is a {@link StateSplit} with a null + * {@linkplain StateSplit#stateAfter() frame state}, the frame state is initialized. + * + * @param value the value to add to the graph and push to the stack. The + * {@code value.getJavaKind()} kind is used when type checking this operation. + * @return a node equivalent to {@code value} in the graph + */ + default <T extends ValueNode> T add(T value) { + if (value.graph() != null) { + assert !(value instanceof StateSplit) || ((StateSplit) value).stateAfter() != null; + return value; + } + T equivalentValue = append(value); + if (equivalentValue instanceof StateSplit) { + StateSplit stateSplit = (StateSplit) equivalentValue; + if (stateSplit.stateAfter() == null && stateSplit.hasSideEffect()) { + setStateAfter(stateSplit); + } + } + return equivalentValue; + } + + /** + * Adds a node with a non-void kind to the graph, pushes it to the stack. If the returned node + * is a {@link StateSplit} with a null {@linkplain StateSplit#stateAfter() frame state}, the + * frame state is initialized. + * + * @param kind the kind to use when type checking this operation + * @param value the value to add to the graph and push to the stack + * @return a node equivalent to {@code value} in the graph + */ + default <T extends ValueNode> T addPush(JavaKind kind, T value) { + T equivalentValue = value.graph() != null ? value : append(value); + push(kind, equivalentValue); + if (equivalentValue instanceof StateSplit) { + StateSplit stateSplit = (StateSplit) equivalentValue; + if (stateSplit.stateAfter() == null && stateSplit.hasSideEffect()) { + setStateAfter(stateSplit); + } + } + return equivalentValue; + } + + /** + * Handles an invocation that a plugin determines can replace the original invocation (i.e., the + * one for which the plugin was applied). This applies all standard graph builder processing to + * the replaced invocation including applying any relevant plugins. + * + * @param invokeKind the kind of the replacement invocation + * @param targetMethod the target of the replacement invocation + * @param args the arguments to the replacement invocation + * @param forceInlineEverything specifies if all invocations encountered in the scope of + * handling the replaced invoke are to be force inlined + */ + void handleReplacedInvoke(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] args, boolean forceInlineEverything); + + /** + * Intrinsifies an invocation of a given method by inlining the bytecodes of a given + * substitution method. + * + * @param targetMethod the method being intrinsified + * @param substitute the intrinsic implementation + * @param args the arguments with which to inline the invocation + */ + void intrinsify(ResolvedJavaMethod targetMethod, ResolvedJavaMethod substitute, ValueNode[] args); + + StampProvider getStampProvider(); + + MetaAccessProvider getMetaAccess(); + + default Assumptions getAssumptions() { + return getGraph().getAssumptions(); + } + + ConstantReflectionProvider getConstantReflection(); + + /** + * Gets the graph being constructed. + */ + StructuredGraph getGraph(); + + /** + * Creates a snap shot of the current frame state with the BCI of the instruction after the one + * currently being parsed and assigns it to a given {@linkplain StateSplit#hasSideEffect() side + * effect} node. + * + * @param sideEffect a side effect node just appended to the graph + */ + void setStateAfter(StateSplit sideEffect); + + /** + * Gets the parsing context for the method that inlines the method being parsed by this context. + */ + GraphBuilderContext getParent(); + + /** + * Gets the first ancestor parsing context that is not parsing a + * {@linkplain #parsingIntrinsic() intrinsic}. + */ + default GraphBuilderContext getNonIntrinsicAncestor() { + GraphBuilderContext ancestor = getParent(); + while (ancestor != null && ancestor.parsingIntrinsic()) { + ancestor = ancestor.getParent(); + } + return ancestor; + } + + /** + * Gets the method being parsed by this context. + */ + ResolvedJavaMethod getMethod(); + + /** + * Gets the index of the bytecode instruction currently being parsed. + */ + int bci(); + + /** + * Gets the kind of invocation currently being parsed. + */ + InvokeKind getInvokeKind(); + + /** + * Gets the return type of the invocation currently being parsed. + */ + JavaType getInvokeReturnType(); + + default Stamp getInvokeReturnStamp() { + JavaType returnType = getInvokeReturnType(); + if (returnType.getJavaKind() == JavaKind.Object && returnType instanceof ResolvedJavaType) { + return StampFactory.declared((ResolvedJavaType) returnType); + } else { + return StampFactory.forKind(returnType.getJavaKind()); + } + } + + /** + * Gets the inline depth of this context. A return value of 0 implies that this is the context + * for the parse root. + */ + default int getDepth() { + GraphBuilderContext parent = getParent(); + return parent == null ? 0 : 1 + parent.getDepth(); + } + + /** + * Determines if this parsing context is within the bytecode of an intrinsic or a method inlined + * by an intrinsic. + */ + default boolean parsingIntrinsic() { + return getIntrinsic() != null; + } + + /** + * Gets the intrinsic of the current parsing context or {@code null} if not + * {@link #parsingIntrinsic() parsing an intrinsic}. + */ + IntrinsicContext getIntrinsic(); + + BailoutException bailout(String string); + + /** + * Gets a version of a given value that has a {@linkplain StampTool#isPointerNonNull(ValueNode) + * non-null} stamp. + */ + default ValueNode nullCheckedValue(ValueNode value) { + if (!StampTool.isPointerNonNull(value.stamp())) { + IsNullNode condition = getGraph().unique(new IsNullNode(value)); + ObjectStamp receiverStamp = (ObjectStamp) value.stamp(); + Stamp stamp = receiverStamp.join(objectNonNull()); + FixedGuardNode fixedGuard = append(new FixedGuardNode(condition, NullCheckException, InvalidateReprofile, true)); + PiNode nonNullReceiver = getGraph().unique(new PiNode(value, stamp)); + nonNullReceiver.setGuard(fixedGuard); + // TODO: Propogating the non-null into the frame state would + // remove subsequent null-checks on the same value. However, + // it currently causes an assertion failure when merging states. + // + // frameState.replace(value, nonNullReceiver); + return nonNullReceiver; + } + return value; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/graphbuilderconf/GraphBuilderPlugin.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,29 @@ +/* + * 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.graal.nodes.graphbuilderconf; + +/** + * Marker interface for graph builder plugins. + */ +public interface GraphBuilderPlugin { +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/graphbuilderconf/InlineInvokePlugin.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,130 @@ +/* + * 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.graal.nodes.graphbuilderconf; + +import jdk.vm.ci.meta.JavaType; +import jdk.vm.ci.meta.ResolvedJavaMethod; + +import com.oracle.graal.nodes.Invoke; +import com.oracle.graal.nodes.ValueNode; + +/** + * Plugin for specifying what is inlined during graph parsing. This plugin is also notified + * {@link #notifyBeforeInline before} and {@link #notifyAfterInline} the inlining, as well as of + * {@link #notifyNotInlined non-inlined} invocations (i.e., those for which an {@link Invoke} node + * is created). + */ +public interface InlineInvokePlugin extends GraphBuilderPlugin { + + /** + * Result of a {@link #shouldInlineInvoke inlining decision}. + */ + public static class InlineInfo { + + /** + * Denotes a call site that must not be inlined and should be implemented by a node that + * does not speculate on the call not raising an exception. + */ + public static final InlineInfo DO_NOT_INLINE_WITH_EXCEPTION = new InlineInfo(null, false); + + /** + * Denotes a call site must not be inlined and can be implemented by a node that speculates + * the call will not throw an exception. + */ + public static final InlineInfo DO_NOT_INLINE_NO_EXCEPTION = new InlineInfo(null, false); + + private final ResolvedJavaMethod methodToInline; + private final boolean isIntrinsic; + + public InlineInfo(ResolvedJavaMethod methodToInline, boolean isIntrinsic) { + this.methodToInline = methodToInline; + this.isIntrinsic = isIntrinsic; + } + + /** + * Returns the method to be inlined, or {@code null} if the call site must not be inlined. + */ + public ResolvedJavaMethod getMethodToInline() { + return methodToInline; + } + + /** + * Specifies if {@link #methodToInline} is an intrinsic for the original method (i.e., the + * {@code method} passed to {@link InlineInvokePlugin#shouldInlineInvoke}). + */ + public boolean isIntrinsic() { + return isIntrinsic; + } + } + + /** + * Determines whether a call to a given method is to be inlined. The return value is a + * tri-state: + * <p> + * Non-null return value with a non-null {@link InlineInfo#getMethodToInline method}: That + * {@link InlineInfo#getMethodToInline method} is inlined. Note that it can be a different + * method than the one specified here as the parameter, which allows method substitutions. + * <p> + * Non-null return value with a null {@link InlineInfo#getMethodToInline method}, e.g., + * {@link InlineInfo#DO_NOT_INLINE_WITH_EXCEPTION}: The method is not inlined, and other plugins + * with a lower priority cannot overwrite this decision. + * <p> + * Null return value: This plugin made no decision, other plugins with a lower priority are + * asked. + * + * @param b the context + * @param method the target method of an invoke + * @param args the arguments to the invoke + * @param returnType the return type derived from {@code method}'s signature + */ + default InlineInfo shouldInlineInvoke(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args, JavaType returnType) { + return null; + } + + /** + * Notification that a method is about to be inlined. + * + * @param methodToInline the inlined method + */ + default void notifyBeforeInline(ResolvedJavaMethod methodToInline) { + } + + /** + * Notification that a method was inlined. + * + * @param methodToInline the inlined method + */ + default void notifyAfterInline(ResolvedJavaMethod methodToInline) { + } + + /** + * Notifies this plugin of the {@link Invoke} node created for a method that was not inlined per + * {@link #shouldInlineInvoke}. + * + * @param b the context + * @param method the method that was not inlined + * @param invoke the invoke node created for the call to {@code method} + */ + default void notifyNotInlined(GraphBuilderContext b, ResolvedJavaMethod method, Invoke invoke) { + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/graphbuilderconf/IntrinsicContext.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,170 @@ +/* + * 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.graal.nodes.graphbuilderconf; + +import static com.oracle.graal.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.INLINE_AFTER_PARSING; +import static com.oracle.graal.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.ROOT_COMPILATION; +import static jdk.vm.ci.code.BytecodeFrame.AFTER_BCI; +import static jdk.vm.ci.code.BytecodeFrame.BEFORE_BCI; +import static jdk.vm.ci.code.BytecodeFrame.INVALID_FRAMESTATE_BCI; +import jdk.vm.ci.meta.ResolvedJavaMethod; + +import com.oracle.graal.nodes.AbstractMergeNode; +import com.oracle.graal.nodes.FrameState; +import com.oracle.graal.nodes.Invoke; +import com.oracle.graal.nodes.StateSplit; +import com.oracle.graal.nodes.StructuredGraph; + +/** + * An intrinsic is a substitute implementation of a Java method (or a bytecode in the case of + * snippets) that is itself implemented in Java. This interface provides information about the + * intrinsic currently being processed by the graph builder. + * + * When in the scope of an intrinsic, the graph builder does not check the value kinds flowing + * through the JVM state since intrinsics can employ non-Java kinds to represent values such as raw + * machine words and pointers. + */ +public class IntrinsicContext { + + /** + * Gets the method being intrinsified. + */ + final ResolvedJavaMethod method; + + /** + * Gets the method providing the intrinsic implementation. + */ + final ResolvedJavaMethod intrinsic; + + public ResolvedJavaMethod getOriginalMethod() { + return method; + } + + public ResolvedJavaMethod getIntrinsicMethod() { + return intrinsic; + } + + /** + * Determines if a call within the compilation scope of this intrinsic represents a call to the + * {@linkplain #getOriginalMethod() original} method. This denotes the path where a partial + * intrinsification falls back to the original method. + */ + public boolean isCallToOriginal(ResolvedJavaMethod targetMethod) { + return method.equals(targetMethod) || intrinsic.equals(targetMethod); + } + + final CompilationContext compilationContext; + + public IntrinsicContext(ResolvedJavaMethod method, ResolvedJavaMethod intrinsic, CompilationContext compilationContext) { + this.method = method; + this.intrinsic = intrinsic; + this.compilationContext = compilationContext; + assert !isCompilationRoot() || method.hasBytecodes() : "Cannot root compile intrinsic for native or abstract method " + method.format("%H.%n(%p)"); + } + + public boolean isPostParseInlined() { + return compilationContext.equals(INLINE_AFTER_PARSING); + } + + public boolean isCompilationRoot() { + return compilationContext.equals(ROOT_COMPILATION); + } + + /** + * Denotes the compilation context in which an intrinsic is being parsed. + */ + public enum CompilationContext { + /** + * An intrinsic is being processed when parsing an invoke bytecode that calls the + * intrinsified method. + */ + INLINE_DURING_PARSING, + + /** + * An intrinsic is being processed when inlining an {@link Invoke} in an existing graph. + */ + INLINE_AFTER_PARSING, + + /** + * An intrinsic is the root of compilation. + */ + ROOT_COMPILATION + } + + /** + * Models the state of a graph in terms of {@link StateSplit#hasSideEffect() side effects} that + * are control flow predecessors of the current point in a graph. + */ + public interface SideEffectsState { + + /** + * Determines if the current program point is preceded by one or more side effects. + */ + boolean isAfterSideEffect(); + + /** + * Gets the side effects preceding the current program point. + */ + Iterable<StateSplit> sideEffects(); + + /** + * Records a side effect for the current program point. + */ + void addSideEffect(StateSplit sideEffect); + } + + public FrameState createFrameState(StructuredGraph graph, SideEffectsState sideEffects, StateSplit forStateSplit) { + assert forStateSplit != graph.start(); + if (forStateSplit.hasSideEffect()) { + if (sideEffects.isAfterSideEffect()) { + // Only the last side effect on any execution path in a replacement + // can inherit the stateAfter of the replaced node + FrameState invalid = graph.add(new FrameState(INVALID_FRAMESTATE_BCI)); + for (StateSplit lastSideEffect : sideEffects.sideEffects()) { + lastSideEffect.setStateAfter(invalid); + } + } + sideEffects.addSideEffect(forStateSplit); + return graph.add(new FrameState(AFTER_BCI)); + } else { + if (forStateSplit instanceof AbstractMergeNode) { + // Merge nodes always need a frame state + if (sideEffects.isAfterSideEffect()) { + // A merge after one or more side effects + return graph.add(new FrameState(AFTER_BCI)); + } else { + // A merge before any side effects + return graph.add(new FrameState(BEFORE_BCI)); + } + } else { + // Other non-side-effects do not need a state + return null; + } + } + } + + @Override + public String toString() { + return "Intrinsic{original: " + method.format("%H.%n(%p)") + ", intrinsic: " + intrinsic.format("%H.%n(%p)") + ", context: " + compilationContext + "}"; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/graphbuilderconf/InvocationPlugin.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,208 @@ +/* + * 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.graal.nodes.graphbuilderconf; + +import java.lang.invoke.MethodHandle; +import java.lang.reflect.Method; + +import jdk.vm.ci.common.JVMCIError; +import jdk.vm.ci.meta.MetaAccessProvider; +import jdk.vm.ci.meta.ResolvedJavaMethod; + +import com.oracle.graal.nodes.Invoke; +import com.oracle.graal.nodes.ValueNode; +import com.oracle.graal.nodes.type.StampTool; + +/** + * Plugin for handling a specific method invocation. + */ +public interface InvocationPlugin extends GraphBuilderPlugin { + + /** + * The receiver in a non-static method. The class literal for this interface must be used with + * {@link InvocationPlugins#put(InvocationPlugin, boolean, boolean, boolean, Class, String, Class...)} + * to denote the receiver argument for such a non-static method. + */ + public interface Receiver { + /** + * Gets the receiver value, null checking it first if necessary. + * + * @return the receiver value with a {@linkplain StampTool#isPointerNonNull(ValueNode) + * non-null} stamp + */ + ValueNode get(); + + /** + * Determines if the receiver is constant. + */ + default boolean isConstant() { + return false; + } + } + + /** + * Determines if this plugin is for a method with a polymorphic signature (e.g. + * {@link MethodHandle#invokeExact(Object...)}). + */ + default boolean isSignaturePolymorphic() { + return false; + } + + /** + * Determines if this plugin can only be used when inlining the method is it associated with. + * That is, this plugin cannot be used when the associated method is the compilation root. + */ + default boolean inlineOnly() { + return isSignaturePolymorphic(); + } + + /** + * Handles invocation of a signature polymorphic method. + * + * @param receiver access to the receiver, {@code null} if {@code targetMethod} is static + * @param argsIncludingReceiver all arguments to the invocation include the raw receiver in + * position 0 if {@code targetMethod} is not static + * @see #execute + */ + default boolean applyPolymorphic(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode... argsIncludingReceiver) { + return defaultHandler(b, targetMethod, receiver, argsIncludingReceiver); + } + + /** + * @see #execute + */ + default boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver) { + return defaultHandler(b, targetMethod, receiver); + } + + /** + * @see #execute + */ + default boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode arg) { + return defaultHandler(b, targetMethod, receiver, arg); + } + + /** + * @see #execute + */ + default boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode arg1, ValueNode arg2) { + return defaultHandler(b, targetMethod, receiver, arg1, arg2); + } + + /** + * @see #execute + */ + default boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode arg1, ValueNode arg2, ValueNode arg3) { + return defaultHandler(b, targetMethod, receiver, arg1, arg2, arg3); + } + + /** + * @see #execute + */ + default boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode arg1, ValueNode arg2, ValueNode arg3, ValueNode arg4) { + return defaultHandler(b, targetMethod, receiver, arg1, arg2, arg3, arg4); + } + + /** + * @see #execute + */ + default boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode arg1, ValueNode arg2, ValueNode arg3, ValueNode arg4, ValueNode arg5) { + return defaultHandler(b, targetMethod, receiver, arg1, arg2, arg3, arg4, arg5); + } + + /** + * Executes this plugin against a set of invocation arguments. + * + * The default implementation in {@link InvocationPlugin} dispatches to the {@code apply(...)} + * method that matches the number of arguments or to {@link #applyPolymorphic} if {@code plugin} + * is {@linkplain #isSignaturePolymorphic() signature polymorphic}. + * + * @param targetMethod the method for which this plugin is being applied + * @param receiver access to the receiver, {@code null} if {@code targetMethod} is static + * @param argsIncludingReceiver all arguments to the invocation include the receiver in position + * 0 if {@code targetMethod} is not static + * @return {@code true} if this plugin handled the invocation of {@code targetMethod} + * {@code false} if the graph builder should process the invoke further (e.g., by + * inlining it or creating an {@link Invoke} node). A plugin that does not handle an + * invocation must not modify the graph being constructed. + */ + default boolean execute(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode[] argsIncludingReceiver) { + if (isSignaturePolymorphic()) { + return applyPolymorphic(b, targetMethod, receiver, argsIncludingReceiver); + } else if (receiver != null) { + assert !targetMethod.isStatic(); + assert argsIncludingReceiver.length > 0; + if (argsIncludingReceiver.length == 1) { + return apply(b, targetMethod, receiver); + } else if (argsIncludingReceiver.length == 2) { + return apply(b, targetMethod, receiver, argsIncludingReceiver[1]); + } else if (argsIncludingReceiver.length == 3) { + return apply(b, targetMethod, receiver, argsIncludingReceiver[1], argsIncludingReceiver[2]); + } else if (argsIncludingReceiver.length == 4) { + return apply(b, targetMethod, receiver, argsIncludingReceiver[1], argsIncludingReceiver[2], argsIncludingReceiver[3]); + } else if (argsIncludingReceiver.length == 5) { + return apply(b, targetMethod, receiver, argsIncludingReceiver[1], argsIncludingReceiver[2], argsIncludingReceiver[3], argsIncludingReceiver[4]); + } else { + return defaultHandler(b, targetMethod, receiver, argsIncludingReceiver); + } + } else { + assert targetMethod.isStatic(); + if (argsIncludingReceiver.length == 0) { + return apply(b, targetMethod, null); + } else if (argsIncludingReceiver.length == 1) { + return apply(b, targetMethod, null, argsIncludingReceiver[0]); + } else if (argsIncludingReceiver.length == 2) { + return apply(b, targetMethod, null, argsIncludingReceiver[0], argsIncludingReceiver[1]); + } else if (argsIncludingReceiver.length == 3) { + return apply(b, targetMethod, null, argsIncludingReceiver[0], argsIncludingReceiver[1], argsIncludingReceiver[2]); + } else if (argsIncludingReceiver.length == 4) { + return apply(b, targetMethod, null, argsIncludingReceiver[0], argsIncludingReceiver[1], argsIncludingReceiver[2], argsIncludingReceiver[3]); + } else if (argsIncludingReceiver.length == 5) { + return apply(b, targetMethod, null, argsIncludingReceiver[0], argsIncludingReceiver[1], argsIncludingReceiver[2], argsIncludingReceiver[3], argsIncludingReceiver[4]); + } else { + return defaultHandler(b, targetMethod, receiver, argsIncludingReceiver); + } + + } + } + + /** + * Handles an invocation when a specific {@code apply} method is not available. + */ + default boolean defaultHandler(@SuppressWarnings("unused") GraphBuilderContext b, ResolvedJavaMethod targetMethod, @SuppressWarnings("unused") InvocationPlugin.Receiver receiver, + ValueNode... args) { + throw new JVMCIError("Invocation plugin for %s does not handle invocations with %d arguments", targetMethod.format("%H.%n(%p)"), args.length); + } + + default StackTraceElement getApplySourceLocation(MetaAccessProvider metaAccess) { + Class<?> c = getClass(); + for (Method m : c.getDeclaredMethods()) { + if (m.getName().equals("apply")) { + return metaAccess.lookupJavaMethod(m).asStackTraceElement(0); + } else if (m.getName().equals("defaultHandler")) { + return metaAccess.lookupJavaMethod(m).asStackTraceElement(0); + } + } + throw new JVMCIError("could not find method named \"apply\" or \"defaultHandler\" in " + c.getName()); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/graphbuilderconf/InvocationPlugins.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,627 @@ +/* + * 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.graal.nodes.graphbuilderconf; + +import static java.lang.String.format; + +import java.lang.reflect.Executable; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import jdk.vm.ci.common.JVMCIError; +import jdk.vm.ci.meta.MetaAccessProvider; +import jdk.vm.ci.meta.ResolvedJavaMethod; + +import com.oracle.graal.graph.Node; +import com.oracle.graal.graph.iterators.NodeIterable; +import com.oracle.graal.nodes.ValueNode; + +/** + * Manages a set of {@link InvocationPlugin}s. + */ +public class InvocationPlugins { + + public static class InvocationPluginReceiver implements InvocationPlugin.Receiver { + private final GraphBuilderContext parser; + private ValueNode[] args; + private ValueNode value; + + public InvocationPluginReceiver(GraphBuilderContext parser) { + this.parser = parser; + } + + @Override + public ValueNode get() { + assert args != null : "Cannot get the receiver of a static method"; + if (value == null) { + value = parser.nullCheckedValue(args[0]); + if (value != args[0]) { + args[0] = value; + } + } + return value; + } + + @Override + public boolean isConstant() { + return args[0].isConstant(); + } + + public InvocationPluginReceiver init(ResolvedJavaMethod targetMethod, ValueNode[] newArgs) { + if (!targetMethod.isStatic()) { + this.args = newArgs; + this.value = null; + return this; + } + return null; + } + } + + /** + * Utility for + * {@linkplain InvocationPlugins#register(InvocationPlugin, Class, String, Class...) + * registration} of invocation plugins. + */ + public static class Registration { + + private final InvocationPlugins plugins; + private final Class<?> declaringClass; + private boolean allowOverwrite; + + /** + * Creates an object for registering {@link InvocationPlugin}s for methods declared by a + * given class. + * + * @param plugins where to register the plugins + * @param declaringClass the class declaring the methods for which plugins will be + * registered via this object + */ + public Registration(InvocationPlugins plugins, Class<?> declaringClass) { + this.plugins = plugins; + this.declaringClass = declaringClass; + } + + /** + * Creates an object for registering {@link InvocationPlugin}s for methods declared by a + * given class. + * + * @param plugins where to register the plugins + * @param declaringClassName the name of the class class declaring the methods for which + * plugins will be registered via this object + */ + public Registration(InvocationPlugins plugins, String declaringClassName) { + this.plugins = plugins; + try { + this.declaringClass = Class.forName(declaringClassName); + } catch (ClassNotFoundException ex) { + throw JVMCIError.shouldNotReachHere(ex); + } + } + + /** + * Configures this registration to allow or disallow overwriting of invocation plugins. + */ + public Registration setAllowOverwrite(boolean allowOverwrite) { + this.allowOverwrite = allowOverwrite; + return this; + } + + /** + * Registers a plugin for a method with no arguments. + * + * @param name the name of the method + * @param plugin the plugin to be registered + */ + public void register0(String name, InvocationPlugin plugin) { + plugins.register(plugin, false, allowOverwrite, declaringClass, name); + } + + /** + * Registers a plugin for a method with 1 argument. + * + * @param name the name of the method + * @param plugin the plugin to be registered + */ + public void register1(String name, Class<?> arg, InvocationPlugin plugin) { + plugins.register(plugin, false, allowOverwrite, declaringClass, name, arg); + } + + /** + * Registers a plugin for a method with 2 arguments. + * + * @param name the name of the method + * @param plugin the plugin to be registered + */ + public void register2(String name, Class<?> arg1, Class<?> arg2, InvocationPlugin plugin) { + plugins.register(plugin, false, allowOverwrite, declaringClass, name, arg1, arg2); + } + + /** + * Registers a plugin for a method with 3 arguments. + * + * @param name the name of the method + * @param plugin the plugin to be registered + */ + public void register3(String name, Class<?> arg1, Class<?> arg2, Class<?> arg3, InvocationPlugin plugin) { + plugins.register(plugin, false, allowOverwrite, declaringClass, name, arg1, arg2, arg3); + } + + /** + * Registers a plugin for a method with 4 arguments. + * + * @param name the name of the method + * @param plugin the plugin to be registered + */ + public void register4(String name, Class<?> arg1, Class<?> arg2, Class<?> arg3, Class<?> arg4, InvocationPlugin plugin) { + plugins.register(plugin, false, allowOverwrite, declaringClass, name, arg1, arg2, arg3, arg4); + } + + /** + * Registers a plugin for a method with 5 arguments. + * + * @param name the name of the method + * @param plugin the plugin to be registered + */ + public void register5(String name, Class<?> arg1, Class<?> arg2, Class<?> arg3, Class<?> arg4, Class<?> arg5, InvocationPlugin plugin) { + plugins.register(plugin, false, allowOverwrite, declaringClass, name, arg1, arg2, arg3, arg4, arg5); + } + + /** + * Registers a plugin for an optional method with no arguments. + * + * @param name the name of the method + * @param plugin the plugin to be registered + */ + public void registerOptional0(String name, InvocationPlugin plugin) { + plugins.register(plugin, true, allowOverwrite, declaringClass, name); + } + + /** + * Registers a plugin for an optional method with 1 argument. + * + * @param name the name of the method + * @param plugin the plugin to be registered + */ + public void registerOptional1(String name, Class<?> arg, InvocationPlugin plugin) { + plugins.register(plugin, true, allowOverwrite, declaringClass, name, arg); + } + + /** + * Registers a plugin for an optional method with 2 arguments. + * + * @param name the name of the method + * @param plugin the plugin to be registered + */ + public void registerOptional2(String name, Class<?> arg1, Class<?> arg2, InvocationPlugin plugin) { + plugins.register(plugin, true, allowOverwrite, declaringClass, name, arg1, arg2); + } + + /** + * Registers a plugin for an optional method with 3 arguments. + * + * @param name the name of the method + * @param plugin the plugin to be registered + */ + public void registerOptional3(String name, Class<?> arg1, Class<?> arg2, Class<?> arg3, InvocationPlugin plugin) { + plugins.register(plugin, true, allowOverwrite, declaringClass, name, arg1, arg2, arg3); + } + + /** + * Registers a plugin for an optional method with 4 arguments. + * + * @param name the name of the method + * @param plugin the plugin to be registered + */ + public void registerOptional4(String name, Class<?> arg1, Class<?> arg2, Class<?> arg3, Class<?> arg4, InvocationPlugin plugin) { + plugins.register(plugin, true, allowOverwrite, declaringClass, name, arg1, arg2, arg3, arg4); + } + + /** + * Registers a plugin that implements a method based on the bytecode of a substitute method. + * + * @param substituteDeclaringClass the class declaring the substitute method + * @param name the name of both the original and substitute method + * @param argumentTypes the argument types of the method. Element 0 of this array must be + * the {@link Class} value for {@link InvocationPlugin.Receiver} iff the method + * is non-static. Upon returning, element 0 will have been rewritten to + * {@code declaringClass} + */ + public void registerMethodSubstitution(Class<?> substituteDeclaringClass, String name, Class<?>... argumentTypes) { + registerMethodSubstitution(substituteDeclaringClass, name, name, argumentTypes); + } + + /** + * Registers a plugin that implements a method based on the bytecode of a substitute method. + * + * @param substituteDeclaringClass the class declaring the substitute method + * @param name the name of both the original method + * @param substituteName the name of the substitute method + * @param argumentTypes the argument types of the method. Element 0 of this array must be + * the {@link Class} value for {@link InvocationPlugin.Receiver} iff the method + * is non-static. Upon returning, element 0 will have been rewritten to + * {@code declaringClass} + */ + public void registerMethodSubstitution(Class<?> substituteDeclaringClass, String name, String substituteName, Class<?>... argumentTypes) { + MethodSubstitutionPlugin plugin = new MethodSubstitutionPlugin(substituteDeclaringClass, substituteName, argumentTypes); + plugins.register(plugin, false, allowOverwrite, declaringClass, name, argumentTypes); + } + } + + /** + * Key for a method. + */ + static class MethodKey { + final boolean isStatic; + + /** + * This method is optional. This is used for new API methods not present in previous JDK + * versions. + */ + final boolean isOptional; + + final Class<?> declaringClass; + final String name; + final Class<?>[] argumentTypes; + final InvocationPlugin value; + + MethodKey(InvocationPlugin data, boolean isStatic, boolean isOptional, Class<?> declaringClass, String name, Class<?>... argumentTypes) { + assert isStatic || argumentTypes[0] == declaringClass; + this.value = data; + this.isStatic = isStatic; + this.isOptional = isOptional; + this.declaringClass = declaringClass; + this.name = name; + this.argumentTypes = argumentTypes; + assert isOptional || resolveJava() != null; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof MethodKey) { + MethodKey that = (MethodKey) obj; + boolean res = this.name.equals(that.name) && this.declaringClass.equals(that.declaringClass) && Arrays.equals(this.argumentTypes, that.argumentTypes); + assert !res || this.isStatic == that.isStatic; + return res; + } + return false; + } + + public int getDeclaredParameterCount() { + return isStatic ? argumentTypes.length : argumentTypes.length - 1; + } + + @Override + public int hashCode() { + // Replay compilation mandates use of stable hash codes + return declaringClass.getName().hashCode() ^ name.hashCode(); + } + + private ResolvedJavaMethod resolve(MetaAccessProvider metaAccess) { + Executable method = resolveJava(); + if (method == null) { + return null; + } + return metaAccess.lookupJavaMethod(method); + } + + private Executable resolveJava() { + try { + Executable res; + Class<?>[] parameterTypes = isStatic ? argumentTypes : Arrays.copyOfRange(argumentTypes, 1, argumentTypes.length); + if (name.equals("<init>")) { + res = declaringClass.getDeclaredConstructor(parameterTypes); + } else { + res = declaringClass.getDeclaredMethod(name, parameterTypes); + } + assert Modifier.isStatic(res.getModifiers()) == isStatic; + return res; + } catch (NoSuchMethodException | SecurityException e) { + if (isOptional) { + return null; + } + throw new InternalError(e); + } + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(declaringClass.getName()).append('.').append(name).append('('); + for (Class<?> p : argumentTypes) { + if (sb.charAt(sb.length() - 1) != '(') { + sb.append(", "); + } + sb.append(p.getSimpleName()); + } + return sb.append(')').toString(); + } + } + + private final MetaAccessProvider metaAccess; + + /** + * Initial list of entries. + */ + private final List<MethodKey> registrations = new ArrayList<>(INITIAL_CAPACITY); + + /** + * Deferred registrations as well as guard for initialization of {@link #entries}. The guard + * uses double-checked locking which is why this field is {@code volatile}. + */ + private volatile List<Runnable> deferredRegistrations = new ArrayList<>(); + + /** + * Adds a {@link Runnable} for doing registration deferred until the first time + * {@link #get(ResolvedJavaMethod)} or {@link #closeRegistration()} is called on this object. + */ + public void defer(Runnable deferrable) { + assert entries == null && deferredRegistrations != null : "registration is closed"; + deferredRegistrations.add(deferrable); + } + + /** + * Entry map that is initialized by {@link #initializeMap()}. + */ + private Map<ResolvedJavaMethod, InvocationPlugin> entries; + + private static final int INITIAL_CAPACITY = 64; + + /** + * Adds an entry to this map for a specified method. + * + * @param value value to be associated with the specified method + * @param isStatic specifies if the method is static + * @param isOptional specifies if the method is optional + * @param declaringClass the class declaring the method + * @param name the name of the method + * @param argumentTypes the argument types of the method. Element 0 of this array must be + * {@code declaringClass} iff the method is non-static. + * @return an object representing the method + */ + MethodKey put(InvocationPlugin value, boolean isStatic, boolean isOptional, boolean allowOverwrite, Class<?> declaringClass, String name, Class<?>... argumentTypes) { + assert isStatic || argumentTypes[0] == declaringClass; + MethodKey methodKey = new MethodKey(value, isStatic, isOptional, declaringClass, name, argumentTypes); + assert entries == null : "registration is closed"; + assert allowOverwrite || !registrations.contains(methodKey) : "a value is already registered for " + methodKey; + registrations.add(methodKey); + return methodKey; + } + + /** + * Determines if a method denoted by a given {@link MethodKey} is in this map. + */ + boolean containsKey(MethodKey key) { + return registrations.contains(key); + } + + InvocationPlugin get(ResolvedJavaMethod method) { + if (deferredRegistrations != null) { + initializeMap(); + } + return entries.get(method); + } + + /** + * Disallows new registrations of new plugins, and creates the internal tables for method + * lookup. + */ + public void closeRegistration() { + if (deferredRegistrations != null) { + initializeMap(); + } + } + + void initializeMap() { + if (deferredRegistrations != null) { + synchronized (this) { + if (deferredRegistrations != null) { + List<Runnable> localDeferredRegistrations = deferredRegistrations; + for (Runnable deferrable : localDeferredRegistrations) { + deferrable.run(); + } + if (registrations.isEmpty()) { + entries = Collections.emptyMap(); + } else { + Map<ResolvedJavaMethod, InvocationPlugin> newEntries = new HashMap<>(); + for (MethodKey methodKey : registrations) { + ResolvedJavaMethod m = methodKey.resolve(metaAccess); + newEntries.put(m, methodKey.value); + } + entries = newEntries; + } + deferredRegistrations = null; + } + } + } + } + + public int size() { + return registrations.size(); + } + + /** + * The plugins {@linkplain #lookupInvocation(ResolvedJavaMethod) searched} before searching in + * this object. + */ + protected final InvocationPlugins parent; + + private InvocationPlugins(InvocationPlugins parent, MetaAccessProvider metaAccess) { + this.metaAccess = metaAccess; + InvocationPlugins p = parent; + this.parent = p; + } + + /** + * Creates a set of invocation plugins with a non-null {@linkplain #getParent() parent}. + */ + public InvocationPlugins(InvocationPlugins parent) { + this(parent, parent.getMetaAccess()); + } + + public MetaAccessProvider getMetaAccess() { + return metaAccess; + } + + public InvocationPlugins(MetaAccessProvider metaAccess) { + this(null, metaAccess); + } + + private void register(InvocationPlugin plugin, boolean isOptional, boolean allowOverwrite, Class<?> declaringClass, String name, Class<?>... argumentTypes) { + boolean isStatic = argumentTypes.length == 0 || argumentTypes[0] != InvocationPlugin.Receiver.class; + if (!isStatic) { + argumentTypes[0] = declaringClass; + } + MethodKey methodInfo = put(plugin, isStatic, isOptional, allowOverwrite, declaringClass, name, argumentTypes); + assert Checker.check(this, methodInfo, plugin); + } + + /** + * Registers an invocation plugin for a given method. There must be no plugin currently + * registered for {@code method}. + * + * @param argumentTypes the argument types of the method. Element 0 of this array must be the + * {@link Class} value for {@link InvocationPlugin.Receiver} iff the method is + * non-static. Upon returning, element 0 will have been rewritten to + * {@code declaringClass} + */ + public void register(InvocationPlugin plugin, Class<?> declaringClass, String name, Class<?>... argumentTypes) { + register(plugin, false, false, declaringClass, name, argumentTypes); + } + + /** + * Registers an invocation plugin for a given, optional method. There must be no plugin + * currently registered for {@code method}. + * + * @param argumentTypes the argument types of the method. Element 0 of this array must be the + * {@link Class} value for {@link InvocationPlugin.Receiver} iff the method is + * non-static. Upon returning, element 0 will have been rewritten to + * {@code declaringClass} + */ + public void registerOptional(InvocationPlugin plugin, Class<?> declaringClass, String name, Class<?>... argumentTypes) { + register(plugin, true, false, declaringClass, name, argumentTypes); + } + + /** + * Gets the plugin for a given method. + * + * @param method the method to lookup + * @return the plugin associated with {@code method} or {@code null} if none exists + */ + public InvocationPlugin lookupInvocation(ResolvedJavaMethod method) { + if (parent != null) { + InvocationPlugin plugin = parent.lookupInvocation(method); + if (plugin != null) { + return plugin; + } + } + return get(method); + } + + /** + * Gets the invocation plugins {@linkplain #lookupInvocation(ResolvedJavaMethod) searched} + * before searching in this object. + */ + public InvocationPlugins getParent() { + return parent; + } + + @Override + public String toString() { + return registrations.stream().map(MethodKey::toString).collect(Collectors.joining(", ")) + " / parent: " + this.parent; + } + + private static class Checker { + private static final int MAX_ARITY = 5; + /** + * The set of all {@link InvocationPlugin#apply} method signatures. + */ + static final Class<?>[][] SIGS; + + static { + ArrayList<Class<?>[]> sigs = new ArrayList<>(MAX_ARITY); + for (Method method : InvocationPlugin.class.getDeclaredMethods()) { + if (!Modifier.isStatic(method.getModifiers()) && method.getName().equals("apply")) { + Class<?>[] sig = method.getParameterTypes(); + assert sig[0] == GraphBuilderContext.class; + assert sig[1] == ResolvedJavaMethod.class; + assert sig[2] == InvocationPlugin.Receiver.class; + assert Arrays.asList(sig).subList(3, sig.length).stream().allMatch(c -> c == ValueNode.class); + while (sigs.size() < sig.length - 2) { + sigs.add(null); + } + sigs.set(sig.length - 3, sig); + } + } + assert sigs.indexOf(null) == -1 : format("need to add an apply() method to %s that takes %d %s arguments ", InvocationPlugin.class.getName(), sigs.indexOf(null), + ValueNode.class.getSimpleName()); + SIGS = sigs.toArray(new Class<?>[sigs.size()][]); + } + + public static boolean check(InvocationPlugins plugins, MethodKey method, InvocationPlugin plugin) { + InvocationPlugins p = plugins.parent; + while (p != null) { + assert !p.containsKey(method) : "a plugin is already registered for " + method; + p = p.parent; + } + if (plugin instanceof ForeignCallPlugin || plugin instanceof GeneratedInvocationPlugin) { + return true; + } + if (plugin instanceof MethodSubstitutionPlugin) { + MethodSubstitutionPlugin msplugin = (MethodSubstitutionPlugin) plugin; + msplugin.getJavaSubstitute(); + return true; + } + int arguments = method.getDeclaredParameterCount(); + assert arguments < SIGS.length : format("need to extend %s to support method with %d arguments: %s", InvocationPlugin.class.getSimpleName(), arguments, method); + for (Method m : plugin.getClass().getDeclaredMethods()) { + if (m.getName().equals("apply")) { + Class<?>[] parameterTypes = m.getParameterTypes(); + if (Arrays.equals(SIGS[arguments], parameterTypes)) { + return true; + } + } + } + throw new AssertionError(format("graph builder plugin for %s not found", method)); + } + } + + /** + * Checks a set of nodes added to the graph by an {@link InvocationPlugin}. + * + * @param b the graph builder that applied the plugin + * @param plugin a plugin that was just applied + * @param newNodes the nodes added to the graph by {@code plugin} + * @throws AssertionError if any check fail + */ + public void checkNewNodes(GraphBuilderContext b, InvocationPlugin plugin, NodeIterable<Node> newNodes) { + if (parent != null) { + parent.checkNewNodes(b, plugin, newNodes); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/graphbuilderconf/LoopExplosionPlugin.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,31 @@ +/* + * 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.graal.nodes.graphbuilderconf; + +import jdk.vm.ci.meta.ResolvedJavaMethod; + +public interface LoopExplosionPlugin extends GraphBuilderPlugin { + boolean shouldExplodeLoops(ResolvedJavaMethod method); + + boolean shouldMergeExplosions(ResolvedJavaMethod method); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/graphbuilderconf/MethodSubstitutionPlugin.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,206 @@ +/* + * 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.graal.nodes.graphbuilderconf; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.stream.Collectors; + +import jdk.vm.ci.common.JVMCIError; +import jdk.vm.ci.meta.MetaAccessProvider; +import jdk.vm.ci.meta.ResolvedJavaMethod; +import sun.misc.Launcher; + +import com.oracle.graal.nodes.ValueNode; + +/** + * An {@link InvocationPlugin} for a method where the implementation of the method is provided by a + * {@linkplain #getSubstitute(MetaAccessProvider) substitute} method. A substitute method must be + * static even if the substituted method is not. + */ +public final class MethodSubstitutionPlugin implements InvocationPlugin { + + private ResolvedJavaMethod cachedSubstitute; + + /** + * The class in which the substitute method is declared. + */ + private final Class<?> declaringClass; + + /** + * The name of the original and substitute method. + */ + private final String name; + + /** + * The parameter types of the substitute method. + */ + private final Class<?>[] parameters; + + private final boolean originalIsStatic; + + /** + * Creates a method substitution plugin. + * + * @param declaringClass the class in which the substitute method is declared + * @param name the name of the substitute method + * @param parameters the parameter types of the substitute method. If the original method is not + * static, then {@code parameters[0]} must be the {@link Class} value denoting + * {@link InvocationPlugin.Receiver} + */ + public MethodSubstitutionPlugin(Class<?> declaringClass, String name, Class<?>... parameters) { + this.declaringClass = declaringClass; + this.name = name; + this.parameters = parameters; + this.originalIsStatic = parameters.length == 0 || parameters[0] != InvocationPlugin.Receiver.class; + } + + /** + * Creates a method substitution plugin. + * + * @param declaringClass the class in which the substitute method is declared + * @param name the name of the substitute method + * @param parameters the parameter types of the substitute method + */ + public MethodSubstitutionPlugin(boolean originalIsStatic, Class<?> declaringClass, String name, Class<?>... parameters) { + this.declaringClass = declaringClass; + this.name = name; + this.parameters = parameters; + this.originalIsStatic = originalIsStatic; + } + + public boolean inlineOnly() { + // Conservatively assume MacroNodes may be used in a substitution + return true; + } + + /** + * Gets the substitute method, resolving it first if necessary. + */ + public ResolvedJavaMethod getSubstitute(MetaAccessProvider metaAccess) { + if (cachedSubstitute == null) { + cachedSubstitute = metaAccess.lookupJavaMethod(getJavaSubstitute()); + } + return cachedSubstitute; + } + + /** + * Gets the reflection API version of the substitution method. + */ + Method getJavaSubstitute() throws JVMCIError { + Method substituteMethod = lookupSubstitute(); + int modifiers = substituteMethod.getModifiers(); + if (Modifier.isAbstract(modifiers) || Modifier.isNative(modifiers)) { + throw new JVMCIError("Substitution method must not be abstract or native: " + substituteMethod); + } + if (!Modifier.isStatic(modifiers)) { + throw new JVMCIError("Substitution method must be static: " + substituteMethod); + } + return substituteMethod; + } + + /** + * Determines if a given method is the substitute method of this plugin. + */ + private boolean isSubstitute(Method m) { + if (Modifier.isStatic(m.getModifiers()) && m.getName().equals(name)) { + if (parameters.length == m.getParameterCount()) { + Class<?>[] mparams = m.getParameterTypes(); + int start = 0; + if (!originalIsStatic) { + start = 1; + if (!mparams[0].isAssignableFrom(parameters[0])) { + return false; + } + } + for (int i = start; i < mparams.length; i++) { + if (mparams[i] != parameters[i]) { + return false; + } + } + } + return true; + } + return false; + } + + /** + * Gets the substitute method of this plugin. + */ + private Method lookupSubstitute() { + for (Method m : declaringClass.getDeclaredMethods()) { + if (isSubstitute(m)) { + return m; + } + } + throw new JVMCIError("No method found specified by %s", this); + } + + /** + * Resolves a name to a class. + * + * @param className the name of the class to resolve + * @param optional if true, resolution failure returns null + * @return the resolved class or null if resolution fails and {@code optional} is true + */ + public static Class<?> resolveClass(String className, boolean optional) { + try { + // Need to use launcher class path to handle classes + // that are not on the boot class path + ClassLoader cl = Launcher.getLauncher().getClassLoader(); + return Class.forName(className, false, cl); + } catch (ClassNotFoundException e) { + if (optional) { + return null; + } + throw new JVMCIError("Could not resolve type " + className); + } + } + + @Override + public boolean execute(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode[] argsIncludingReceiver) { + ResolvedJavaMethod subst = getSubstitute(b.getMetaAccess()); + if (receiver != null) { + receiver.get(); + } + b.intrinsify(targetMethod, subst, argsIncludingReceiver); + return true; + } + + public StackTraceElement getApplySourceLocation(MetaAccessProvider metaAccess) { + Class<?> c = getClass(); + for (Method m : c.getDeclaredMethods()) { + if (m.getName().equals("execute")) { + return metaAccess.lookupJavaMethod(m).asStackTraceElement(0); + } + } + throw new JVMCIError("could not find method named \"execute\" in " + c.getName()); + } + + @Override + public String toString() { + return String.format("%s[%s.%s(%s)]", getClass().getSimpleName(), declaringClass.getName(), name, + Arrays.asList(parameters).stream().map(c -> c.getSimpleName()).collect(Collectors.joining(", "))); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/graphbuilderconf/NodeIntrinsicPluginFactory.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,37 @@ +/* + * 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.graal.nodes.graphbuilderconf; + +import com.oracle.graal.compiler.common.type.Stamp; + +public interface NodeIntrinsicPluginFactory { + + public interface InjectionProvider { + + <T> T getInjectedArgument(Class<T> type); + + Stamp getReturnStamp(Class<?> type); + } + + void registerPlugin(InvocationPlugins plugins, InjectionProvider injection); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/graphbuilderconf/NodePlugin.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,213 @@ +/* + * 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.graal.nodes.graphbuilderconf; + +import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.JavaTypeProfile; +import jdk.vm.ci.meta.ResolvedJavaField; +import jdk.vm.ci.meta.ResolvedJavaMethod; +import jdk.vm.ci.meta.ResolvedJavaType; +import jdk.vm.ci.meta.Signature; + +import com.oracle.graal.nodes.ValueNode; + +public interface NodePlugin extends GraphBuilderPlugin { + /** + * Handle the parsing of a method invocation bytecode to a method that can be bound statically. + * If the method returns true, it must {@link GraphBuilderContext#push push} a value as the + * result of the method invocation using the {@link Signature#getReturnKind return kind} of the + * method. + * + * @param b the context + * @param method the statically bound, invoked method + * @param args the arguments of the method invocation + * @return true if the plugin handles the invocation, false otherwise + */ + default boolean handleInvoke(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args) { + return false; + } + + /** + * Handle the parsing of a GETFIELD bytecode. If the method returns true, it must + * {@link GraphBuilderContext#push push} a value using the + * {@link ResolvedJavaField#getJavaKind() kind} of the field. + * + * @param b the context + * @param object the receiver object for the field access + * @param field the accessed field + * @return true if the plugin handles the field access, false otherwise + */ + default boolean handleLoadField(GraphBuilderContext b, ValueNode object, ResolvedJavaField field) { + return false; + } + + /** + * Handle the parsing of a GETSTATIC bytecode. If the method returns true, it must + * {@link GraphBuilderContext#push push} a value using the + * {@link ResolvedJavaField#getJavaKind() kind} of the field. + * + * @param b the context + * @param field the accessed field + * @return true if the plugin handles the field access, false otherwise + */ + default boolean handleLoadStaticField(GraphBuilderContext b, ResolvedJavaField field) { + return false; + } + + /** + * Handle the parsing of a PUTFIELD bytecode. + * + * @param b the context + * @param object the receiver object for the field access + * @param field the accessed field + * @param value the value to be stored into the field + * @return true if the plugin handles the field access, false otherwise + */ + default boolean handleStoreField(GraphBuilderContext b, ValueNode object, ResolvedJavaField field, ValueNode value) { + return false; + } + + /** + * Handle the parsing of a PUTSTATIC bytecode. + * + * @param b the context + * @param field the accessed field + * @param value the value to be stored into the field + * @return true if the plugin handles the field access, false otherwise. + */ + default boolean handleStoreStaticField(GraphBuilderContext b, ResolvedJavaField field, ValueNode value) { + return false; + } + + /** + * Handle the parsing of an array load bytecode. If the method returns true, it must + * {@link GraphBuilderContext#push push} a value using the provided elementKind. + * + * @param b the context + * @param array the accessed array + * @param index the index for the array access + * @param elementKind the element kind of the accessed array + * @return true if the plugin handles the array access, false otherwise. + */ + default boolean handleLoadIndexed(GraphBuilderContext b, ValueNode array, ValueNode index, JavaKind elementKind) { + return false; + } + + /** + * Handle the parsing of an array store bytecode. + * + * @param b the context + * @param array the accessed array + * @param index the index for the array access + * @param elementKind the element kind of the accessed array + * @param value the value to be stored into the array + * @return true if the plugin handles the array access, false otherwise. + */ + default boolean handleStoreIndexed(GraphBuilderContext b, ValueNode array, ValueNode index, JavaKind elementKind, ValueNode value) { + return false; + } + + /** + * Handle the parsing of a CHECKCAST bytecode. If the method returns true, it must + * {@link GraphBuilderContext#push push} a value with the result of the cast using + * {@link JavaKind#Object}. + * + * @param b the context + * @param object the object to be type checked + * @param type the type that the object is checked against + * @param profile the profiling information for the type check, or null if no profiling + * information is available + * @return true if the plugin handles the cast, false otherwise + */ + default boolean handleCheckCast(GraphBuilderContext b, ValueNode object, ResolvedJavaType type, JavaTypeProfile profile) { + return false; + } + + /** + * Handle the parsing of a INSTANCEOF bytecode. If the method returns true, it must + * {@link GraphBuilderContext#push push} a value with the result of the instanceof using + * {@link JavaKind#Int}. + * + * @param b the context + * @param object the object to be type checked + * @param type the type that the object is checked against + * @param profile the profiling information for the type check, or null if no profiling + * information is available + * @return true if the plugin handles the instanceof, false otherwise + */ + default boolean handleInstanceOf(GraphBuilderContext b, ValueNode object, ResolvedJavaType type, JavaTypeProfile profile) { + return false; + } + + /** + * Handle the parsing of a NEW bytecode. If the method returns true, it must + * {@link GraphBuilderContext#push push} a value with the result of the allocation using + * {@link JavaKind#Object}. + * + * @param b the context + * @param type the type to be instantiated + * @return true if the plugin handles the bytecode, false otherwise + */ + default boolean handleNewInstance(GraphBuilderContext b, ResolvedJavaType type) { + return false; + } + + /** + * Handle the parsing of a NEWARRAY and ANEWARRAY bytecode. If the method returns true, it must + * {@link GraphBuilderContext#push push} a value with the result of the allocation using + * {@link JavaKind#Object}. + * + * @param b the context + * @param elementType the element type of the array to be instantiated + * @param length the length of the new array + * @return true if the plugin handles the bytecode, false otherwise + */ + default boolean handleNewArray(GraphBuilderContext b, ResolvedJavaType elementType, ValueNode length) { + return false; + } + + /** + * Handle the parsing of a MULTIANEWARRAY bytecode. If the method returns true, it must + * {@link GraphBuilderContext#push push} a value with the result of the allocation using + * {@link JavaKind#Object}. + * + * @param b the context + * @param type the type of the outermost array to be instantiated + * @param dimensions the array of lengths for all the dimensions to be instantiated + * @return true if the plugin handles the bytecode, false otherwise + */ + default boolean handleNewMultiArray(GraphBuilderContext b, ResolvedJavaType type, ValueNode[] dimensions) { + return false; + } + + /** + * If the plugin {@link GraphBuilderContext#push pushes} a value with a different + * {@link JavaKind} than specified by the bytecode, it must override this method and return + * {@code true}. This disables assertion checking for value kinds. + * + * @param b the context + */ + default boolean canChangeStackKind(GraphBuilderContext b) { + return false; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/graphbuilderconf/ParameterPlugin.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,30 @@ +/* + * 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.graal.nodes.graphbuilderconf; + +import com.oracle.graal.compiler.common.type.Stamp; +import com.oracle.graal.nodes.calc.FloatingNode; + +public interface ParameterPlugin extends GraphBuilderPlugin { + FloatingNode interceptParameter(GraphBuilderContext b, int index, Stamp stamp); +}
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/memory/WriteNode.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/memory/WriteNode.java Tue Dec 08 12:30:15 2015 -0800 @@ -49,7 +49,7 @@ public static final NodeClass<WriteNode> TYPE = NodeClass.create(WriteNode.class); - private WriteNode(ValueNode address, LocationIdentity location, ValueNode value, BarrierType barrierType) { + protected WriteNode(ValueNode address, LocationIdentity location, ValueNode value, BarrierType barrierType) { this((AddressNode) address, location, value, barrierType); }
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/CanonicalizerPhase.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/CanonicalizerPhase.java Tue Dec 08 12:30:15 2015 -0800 @@ -242,9 +242,23 @@ return true; } } + assert assertLeafGVN(node, nodeClass); return false; } + /** + * Ensure that leaf nodes have already been GVN'ed. This is normally handled automatically + * but it's possible to add nodes to the graph with looking for duplicates and it's the + * responsibility of code that does that to clean it up. + */ + private boolean assertLeafGVN(Node node, NodeClass<?> nodeClass) { + if (node.isAlive() && nodeClass.valueNumberable() && nodeClass.isLeafNode()) { + Node newNode = node.graph().findDuplicate(node); + return (newNode == null || newNode == node); + } + return true; + } + private AutoCloseable getCanonicalizeableContractAssertion(Node node) { boolean needsAssertion = false; assert (needsAssertion = true) == true;
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/DominatorConditionalEliminationPhase.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/DominatorConditionalEliminationPhase.java Tue Dec 08 12:30:15 2015 -0800 @@ -568,6 +568,9 @@ private Iterable<InfoElement> getInfoElements(ValueNode proxiedValue) { ValueNode value = GraphUtil.unproxify(proxiedValue); + if (value == null) { + return Collections.emptyList(); + } Info info = map.get(value); if (info == null) { return Collections.emptyList();
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/RemoveValueProxyPhase.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/RemoveValueProxyPhase.java Tue Dec 08 12:30:15 2015 -0800 @@ -22,11 +22,11 @@ */ package com.oracle.graal.phases.common; +import com.oracle.graal.nodes.EntryProxyNode; import com.oracle.graal.nodes.FrameState; import com.oracle.graal.nodes.LoopExitNode; import com.oracle.graal.nodes.ProxyNode; import com.oracle.graal.nodes.StructuredGraph; -import com.oracle.graal.nodes.ValueProxyNode; import com.oracle.graal.nodes.util.GraphUtil; import com.oracle.graal.phases.Phase; @@ -35,7 +35,7 @@ @Override protected void run(StructuredGraph graph) { for (ProxyNode vpn : graph.getNodes(ProxyNode.TYPE)) { - if (vpn instanceof ValueProxyNode) { + if (!(vpn instanceof EntryProxyNode)) { graph.replaceFloating(vpn, vpn.value()); } }
--- a/graal/com.oracle.graal.replacements.amd64/src/com/oracle/graal/replacements/amd64/AMD64GraphBuilderPlugins.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.replacements.amd64/src/com/oracle/graal/replacements/amd64/AMD64GraphBuilderPlugins.java Tue Dec 08 12:30:15 2015 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -32,14 +32,14 @@ import sun.misc.Unsafe; import com.oracle.graal.compiler.common.spi.ForeignCallsProvider; -import com.oracle.graal.graphbuilderconf.ForeignCallPlugin; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins; -import com.oracle.graal.graphbuilderconf.GraphBuilderContext; -import com.oracle.graal.graphbuilderconf.InvocationPlugin; -import com.oracle.graal.graphbuilderconf.InvocationPlugin.Receiver; -import com.oracle.graal.graphbuilderconf.InvocationPlugins; -import com.oracle.graal.graphbuilderconf.InvocationPlugins.Registration; import com.oracle.graal.nodes.ValueNode; +import com.oracle.graal.nodes.graphbuilderconf.ForeignCallPlugin; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderContext; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugin; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugins; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugin.Receiver; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugins.Registration; import com.oracle.graal.nodes.java.AtomicReadAndAddNode; import com.oracle.graal.nodes.java.AtomicReadAndWriteNode; import com.oracle.graal.nodes.memory.address.AddressNode; @@ -54,10 +54,14 @@ public static void register(Plugins plugins, ForeignCallsProvider foreignCalls, AMD64 arch) { InvocationPlugins invocationPlugins = plugins.getInvocationPlugins(); - registerIntegerLongPlugins(invocationPlugins, IntegerSubstitutions.class, JavaKind.Int, arch); - registerIntegerLongPlugins(invocationPlugins, LongSubstitutions.class, JavaKind.Long, arch); - registerUnsafePlugins(invocationPlugins); - registerMathPlugins(invocationPlugins, foreignCalls); + invocationPlugins.defer(new Runnable() { + public void run() { + registerIntegerLongPlugins(invocationPlugins, IntegerSubstitutions.class, JavaKind.Int, arch); + registerIntegerLongPlugins(invocationPlugins, LongSubstitutions.class, JavaKind.Long, arch); + registerUnsafePlugins(invocationPlugins); + registerMathPlugins(invocationPlugins, foreignCalls); + } + }); } private static void registerIntegerLongPlugins(InvocationPlugins plugins, Class<?> substituteDeclaringClass, JavaKind kind, AMD64 arch) {
--- a/graal/com.oracle.graal.replacements.amd64/src/com/oracle/graal/replacements/amd64/AMD64MathSubstitutions.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.replacements.amd64/src/com/oracle/graal/replacements/amd64/AMD64MathSubstitutions.java Tue Dec 08 12:30:15 2015 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -114,9 +114,9 @@ } } - @NodeIntrinsic(value = ForeignCallNode.class, setStampFromReturnType = true) + @NodeIntrinsic(value = ForeignCallNode.class) private static native double callDouble1(@ConstantNodeParameter ForeignCallDescriptor descriptor, double value); - @NodeIntrinsic(value = ForeignCallNode.class, setStampFromReturnType = true) + @NodeIntrinsic(value = ForeignCallNode.class) private static native double callDouble2(@ConstantNodeParameter ForeignCallDescriptor descriptor, double a, double b); }
--- a/graal/com.oracle.graal.replacements.sparc/src/com/oracle/graal/replacements/sparc/SPARCGraphBuilderPlugins.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.replacements.sparc/src/com/oracle/graal/replacements/sparc/SPARCGraphBuilderPlugins.java Tue Dec 08 12:30:15 2015 -0800 @@ -33,13 +33,13 @@ import jdk.vm.ci.meta.ResolvedJavaMethod; import com.oracle.graal.compiler.common.spi.ForeignCallsProvider; -import com.oracle.graal.graphbuilderconf.ForeignCallPlugin; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins; -import com.oracle.graal.graphbuilderconf.GraphBuilderContext; -import com.oracle.graal.graphbuilderconf.InvocationPlugin; -import com.oracle.graal.graphbuilderconf.InvocationPlugins; -import com.oracle.graal.graphbuilderconf.InvocationPlugins.Registration; import com.oracle.graal.nodes.ValueNode; +import com.oracle.graal.nodes.graphbuilderconf.ForeignCallPlugin; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderContext; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugin; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugins; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugins.Registration; import com.oracle.graal.replacements.IntegerSubstitutions; import com.oracle.graal.replacements.LongSubstitutions; import com.oracle.graal.replacements.nodes.BitCountNode; @@ -48,9 +48,13 @@ public static void register(Plugins plugins, ForeignCallsProvider foreignCalls) { InvocationPlugins invocationPlugins = plugins.getInvocationPlugins(); - registerIntegerLongPlugins(invocationPlugins, IntegerSubstitutions.class, JavaKind.Int); - registerIntegerLongPlugins(invocationPlugins, LongSubstitutions.class, JavaKind.Long); - registerMathPlugins(invocationPlugins, foreignCalls); + invocationPlugins.defer(new Runnable() { + public void run() { + registerIntegerLongPlugins(invocationPlugins, IntegerSubstitutions.class, JavaKind.Int); + registerIntegerLongPlugins(invocationPlugins, LongSubstitutions.class, JavaKind.Long); + registerMathPlugins(invocationPlugins, foreignCalls); + } + }); } private static void registerIntegerLongPlugins(InvocationPlugins plugins, Class<?> substituteDeclaringClass, JavaKind kind) {
--- a/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/DerivedOopTest.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/DerivedOopTest.java Tue Dec 08 12:30:15 2015 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -31,12 +31,12 @@ import com.oracle.graal.api.directives.GraalDirectives; import com.oracle.graal.compiler.test.GraalCompilerTest; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins; -import com.oracle.graal.graphbuilderconf.GraphBuilderContext; -import com.oracle.graal.graphbuilderconf.InvocationPlugin; -import com.oracle.graal.graphbuilderconf.InvocationPlugins.Registration; import com.oracle.graal.nodes.StructuredGraph; import com.oracle.graal.nodes.ValueNode; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderContext; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugin; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugins.Registration; import com.oracle.graal.replacements.Snippets; import com.oracle.graal.word.Word; import com.oracle.graal.word.nodes.WordCastNode;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/FoldTest.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,118 @@ +/* + * 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.graal.replacements.test; + +import org.junit.Test; + +import com.oracle.graal.api.directives.GraalDirectives; +import com.oracle.graal.api.replacements.ClassSubstitution; +import com.oracle.graal.api.replacements.Fold; +import com.oracle.graal.api.replacements.MethodSubstitution; +import com.oracle.graal.compiler.test.GraalCompilerTest; +import com.oracle.graal.nodes.ReturnNode; +import com.oracle.graal.nodes.StartNode; +import com.oracle.graal.nodes.StructuredGraph; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; +import com.oracle.graal.nodes.graphbuilderconf.NodeIntrinsicPluginFactory.InjectionProvider; +import com.oracle.graal.replacements.NodeIntrinsificationProvider; + +public class FoldTest extends GraalCompilerTest { + + private static class TestMethod { + + public static int test() { + return 42; + } + } + + static class FoldUtils { + + private int number; + + FoldUtils(int number) { + this.number = number; + } + + @Fold + static int multiply(int a, int b) { + // we want to test whether @Fold works, so prevent automatic constant folding + return a * GraalDirectives.opaque(b); + } + + @Fold + int getNumber() { + // we want to test whether @Fold works, so prevent automatic constant folding + return GraalDirectives.opaque(number); + } + } + + @ClassSubstitution(TestMethod.class) + private static class TestMethodSubstitution { + + private static final FoldUtils utils = new FoldUtils(21); + + @MethodSubstitution + public static int test() { + return FoldUtils.multiply(utils.getNumber(), 2); + } + } + + private static boolean substitutionsInstalled; + + public FoldTest() { + if (!substitutionsInstalled) { + getProviders().getReplacements().registerSubstitutions(TestMethod.class, TestMethodSubstitution.class); + substitutionsInstalled = true; + } + } + + public static int callTest() { + return TestMethod.test(); + } + + @Override + protected Plugins getDefaultGraphBuilderPlugins() { + Plugins ret = super.getDefaultGraphBuilderPlugins(); + // manually register generated factories, jvmci service providers don't work from unit tests + InjectionProvider injection = new NodeIntrinsificationProvider(getMetaAccess(), getSnippetReflection(), getProviders().getForeignCalls(), null); + new FoldFactory_FoldTest_FoldUtils_getNumber().registerPlugin(ret.getInvocationPlugins(), injection); + new FoldFactory_FoldTest_FoldUtils_multiply_255f288().registerPlugin(ret.getInvocationPlugins(), injection); + return ret; + } + + @Override + protected boolean checkHighTierGraph(StructuredGraph graph) { + // check that folding happened correctly + StartNode start = graph.start(); + assert start.next() instanceof ReturnNode : "expected ReturnNode, got " + start.next(); + + ReturnNode ret = (ReturnNode) start.next(); + assert ret.result().isConstant() : "expected ConstantNode, got " + ret.result(); + return true; + } + + @Test + public void snippetTest() { + test("callTest"); + } +}
--- a/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/PEGraphDecoderTest.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/PEGraphDecoderTest.java Tue Dec 08 12:30:15 2015 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -32,16 +32,16 @@ import com.oracle.graal.compiler.common.type.StampFactory; import com.oracle.graal.compiler.test.GraalCompilerTest; import com.oracle.graal.debug.Debug; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration; -import com.oracle.graal.graphbuilderconf.GraphBuilderContext; -import com.oracle.graal.graphbuilderconf.InlineInvokePlugin; -import com.oracle.graal.graphbuilderconf.InvocationPlugin; -import com.oracle.graal.graphbuilderconf.InvocationPlugins; -import com.oracle.graal.graphbuilderconf.InvocationPlugins.Registration; import com.oracle.graal.nodes.AbstractBeginNode; import com.oracle.graal.nodes.StructuredGraph; import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions; import com.oracle.graal.nodes.ValueNode; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderContext; +import com.oracle.graal.nodes.graphbuilderconf.InlineInvokePlugin; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugin; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugins; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugins.Registration; import com.oracle.graal.nodes.memory.HeapAccess.BarrierType; import com.oracle.graal.nodes.memory.ReadNode; import com.oracle.graal.nodes.memory.address.AddressNode;
--- a/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/PointerTrackingTest.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/PointerTrackingTest.java Tue Dec 08 12:30:15 2015 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -31,11 +31,11 @@ import com.oracle.graal.compiler.test.GraalCompilerTest; import com.oracle.graal.debug.Debug; import com.oracle.graal.debug.DebugConfigScope; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins; -import com.oracle.graal.graphbuilderconf.GraphBuilderContext; -import com.oracle.graal.graphbuilderconf.InvocationPlugin; -import com.oracle.graal.graphbuilderconf.InvocationPlugins.Registration; import com.oracle.graal.nodes.ValueNode; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderContext; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugin; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugins.Registration; import com.oracle.graal.replacements.Snippets; import com.oracle.graal.word.Word;
--- a/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/ReplacementsParseTest.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/ReplacementsParseTest.java Tue Dec 08 12:30:15 2015 -0800 @@ -35,6 +35,7 @@ import com.oracle.graal.graph.Node.ConstantNodeParameter; import com.oracle.graal.graph.Node.NodeIntrinsic; import com.oracle.graal.nodes.PiNode; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; import com.oracle.graal.nodes.spi.Replacements; import com.oracle.graal.runtime.RuntimeProvider; @@ -43,6 +44,14 @@ */ public class ReplacementsParseTest extends GraalCompilerTest { + @Override + protected Plugins getDefaultGraphBuilderPlugins() { + Plugins ret = super.getDefaultGraphBuilderPlugins(); + // manually register generated factory, jvmci service providers don't work from unit tests + new NodeIntrinsicFactory_ReplacementsParseTest_TestMethodsSubstitutions_asNonNullStringIntrinsic_2bfccb54().registerPlugin(ret.getInvocationPlugins(), null); + return ret; + } + private static final Object THROW_EXCEPTION_MARKER = new Object() { @Override public String toString() {
--- a/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/SubstitutionsTest.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/SubstitutionsTest.java Tue Dec 08 12:30:15 2015 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -46,12 +46,13 @@ import com.oracle.graal.nodes.ValueNode; import com.oracle.graal.nodes.calc.FloatingNode; import com.oracle.graal.nodes.extended.GuardingNode; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; import com.oracle.graal.nodes.memory.MemoryNode; public class SubstitutionsTest extends GraalCompilerTest { @NodeInfo(allowedUsageTypes = {Memory}) - private static class TestMemory extends FixedWithNextNode implements MemoryNode { + static class TestMemory extends FixedWithNextNode implements MemoryNode { private static final NodeClass<TestMemory> TYPE = NodeClass.create(TestMemory.class); public TestMemory() { @@ -63,7 +64,7 @@ } @NodeInfo(allowedUsageTypes = {Guard}) - private static class TestGuard extends FloatingNode implements GuardingNode { + static class TestGuard extends FloatingNode implements GuardingNode { private static final NodeClass<TestGuard> TYPE = NodeClass.create(TestGuard.class); @Input(Memory) MemoryNode memory; @@ -78,7 +79,7 @@ } @NodeInfo - private static class TestValue extends FloatingNode { + static class TestValue extends FloatingNode { private static final NodeClass<TestValue> TYPE = NodeClass.create(TestValue.class); @Input(Guard) GuardingNode guard; @@ -124,6 +125,16 @@ } @Override + protected Plugins getDefaultGraphBuilderPlugins() { + Plugins ret = super.getDefaultGraphBuilderPlugins(); + // manually register generated factories, jvmci service providers don't work from unit tests + new NodeIntrinsicFactory_SubstitutionsTest_TestGuard_guard_1c2b7e8f().registerPlugin(ret.getInvocationPlugins(), null); + new NodeIntrinsicFactory_SubstitutionsTest_TestMemory_memory().registerPlugin(ret.getInvocationPlugins(), null); + new NodeIntrinsicFactory_SubstitutionsTest_TestValue_value_a22f0f5f().registerPlugin(ret.getInvocationPlugins(), null); + return ret; + } + + @Override protected boolean checkHighTierGraph(StructuredGraph graph) { // Check that the graph contains the expected test nodes. NodeIterable<ReturnNode> retNodes = graph.getNodes().filter(ReturnNode.class);
--- a/graal/com.oracle.graal.replacements.verifier/src/com/oracle/graal/replacements/verifier/APHotSpotSignature.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.replacements.verifier/src/com/oracle/graal/replacements/verifier/APHotSpotSignature.java Tue Dec 08 12:30:15 2015 -0800 @@ -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 @@ -26,9 +26,18 @@ import java.util.List; import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.PackageElement; import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.ArrayType; +import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.TypeVariable; +import javax.lang.model.type.WildcardType; import javax.tools.Diagnostic.Kind; /** @@ -96,6 +105,81 @@ return cur; } + private static void getSignatureName(StringBuilder ret, Element element) { + Element enclosing = element.getEnclosingElement(); + if (enclosing.getKind() == ElementKind.PACKAGE) { + ret.append(((PackageElement) enclosing).getQualifiedName().toString().replace('.', '/')); + ret.append('/'); + } else { + getSignatureName(ret, enclosing); + ret.append('$'); + } + ret.append(element.getSimpleName()); + } + + private static void getSignatureString(StringBuilder ret, TypeMirror type) { + switch (type.getKind()) { + case ARRAY: + ret.append('['); + getSignatureString(ret, ((ArrayType) type).getComponentType()); + break; + case BOOLEAN: + ret.append('Z'); + break; + case BYTE: + ret.append('B'); + break; + case SHORT: + ret.append('S'); + break; + case CHAR: + ret.append('C'); + break; + case INT: + ret.append('I'); + break; + case LONG: + ret.append('J'); + break; + case FLOAT: + ret.append('F'); + break; + case DOUBLE: + ret.append('D'); + break; + case VOID: + ret.append('V'); + break; + case DECLARED: + ret.append('L'); + getSignatureName(ret, ((DeclaredType) type).asElement()); + ret.append(';'); + break; + case TYPEVAR: + getSignatureString(ret, ((TypeVariable) type).getUpperBound()); + break; + case WILDCARD: + getSignatureString(ret, ((WildcardType) type).getExtendsBound()); + break; + case INTERSECTION: + ret.append("Ljava/lang/Object;"); + break; + default: + throw new IllegalArgumentException(type.toString()); + } + } + + public static String toSignature(ExecutableElement intrinsicMethod) { + StringBuilder ret = new StringBuilder(); + ret.append('('); + for (VariableElement param : intrinsicMethod.getParameters()) { + getSignatureString(ret, param.asType()); + } + ret.append(')'); + getSignatureString(ret, intrinsicMethod.getReturnType()); + return ret.toString(); + } + public int getParameterCount(boolean withReceiver) { return arguments.size() + (withReceiver ? 1 : 0); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.replacements.verifier/src/com/oracle/graal/replacements/verifier/FoldPluginGenerator.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,173 @@ +/* + * 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.graal.replacements.verifier; + +import java.io.PrintWriter; +import java.lang.annotation.Annotation; +import java.util.List; + +import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.TypeMirror; +import javax.tools.Diagnostic.Kind; + +import com.oracle.graal.api.replacements.Fold; +import com.oracle.graal.replacements.verifier.InjectedDependencies.WellKnownDependency; + +/** + * Create graph builder plugins for {@link Fold} methods. + */ +public class FoldPluginGenerator extends PluginGenerator { + + private class FoldVerifier extends AbstractVerifier { + + public FoldVerifier(ProcessingEnvironment env) { + super(env); + } + + @Override + public void verify(Element element, AnnotationMirror annotation) { + if (element.getKind() != ElementKind.METHOD) { + assert false : "Element is guaranteed to be a method."; + return; + } + + ExecutableElement intrinsicMethod = (ExecutableElement) element; + if (intrinsicMethod.getModifiers().contains(Modifier.PRIVATE)) { + env.getMessager().printMessage(Kind.ERROR, "@Fold method can not be private.", element); + } else { + FoldPluginGenerator.this.createPluginFactory(intrinsicMethod, null, null); + } + } + + @Override + public Class<? extends Annotation> getAnnotationClass() { + return Fold.class; + } + } + + private TypeMirror stringType() { + return env.getElementUtils().getTypeElement("java.lang.String").asType(); + } + + public FoldPluginGenerator(ProcessingEnvironment env) { + super(env); + } + + public AbstractVerifier getVerifier() { + return new FoldVerifier(env); + } + + @Override + protected String getBaseName() { + return "FoldFactory"; + } + + @Override + protected void createImports(PrintWriter out, ExecutableElement intrinsicMethod, ExecutableElement targetMethod) { + out.printf("import jdk.vm.ci.meta.JavaConstant;\n"); + out.printf("import jdk.vm.ci.meta.JavaKind;\n"); + out.printf("import com.oracle.graal.nodes.ConstantNode;\n"); + super.createImports(out, intrinsicMethod, targetMethod); + } + + @Override + protected InjectedDependencies createExecute(PrintWriter out, ExecutableElement intrinsicMethod, ExecutableElement constructor, TypeMirror[] signature) { + InjectedDependencies deps = new InjectedDependencies(); + List<? extends VariableElement> params = intrinsicMethod.getParameters(); + + int argCount = 0; + Object receiver; + if (intrinsicMethod.getModifiers().contains(Modifier.STATIC)) { + receiver = intrinsicMethod.getEnclosingElement(); + } else { + receiver = "arg0"; + TypeElement type = (TypeElement) intrinsicMethod.getEnclosingElement(); + constantArgument(out, deps, argCount, type.asType(), argCount); + argCount++; + } + + int firstArg = argCount; + for (VariableElement param : params) { + constantArgument(out, deps, argCount, param.asType(), argCount); + argCount++; + } + + if (intrinsicMethod.getAnnotation(Deprecated.class) != null) { + out.printf(" @SuppressWarnings(\"deprecation\")\n"); + } + out.printf(" %s result = %s.%s(", intrinsicMethod.getReturnType(), receiver, intrinsicMethod.getSimpleName()); + if (argCount > firstArg) { + out.printf("arg%d", firstArg); + for (int i = firstArg + 1; i < argCount; i++) { + out.printf(", arg%d", i); + } + } + out.printf(");\n"); + + TypeMirror returnType = intrinsicMethod.getReturnType(); + switch (returnType.getKind()) { + case BOOLEAN: + out.printf(" JavaConstant constant = JavaConstant.forInt(result ? 1 : 0);\n"); + break; + case BYTE: + case SHORT: + case CHAR: + case INT: + out.printf(" JavaConstant constant = JavaConstant.forInt(result);\n"); + break; + case LONG: + out.printf(" JavaConstant constant = JavaConstant.forLong(result);\n"); + break; + case FLOAT: + out.printf(" JavaConstant constant = JavaConstant.forFloat(result);\n"); + break; + case DOUBLE: + out.printf(" JavaConstant constant = JavaConstant.forDouble(result);\n"); + break; + case ARRAY: + case TYPEVAR: + case DECLARED: + if (returnType.equals(stringType())) { + out.printf(" JavaConstant constant = %s.forString(result);\n", deps.use(WellKnownDependency.CONSTANT_REFLECTION)); + } else { + out.printf(" JavaConstant constant = %s.forObject(result);\n", deps.use(WellKnownDependency.SNIPPET_REFLECTION)); + } + break; + default: + throw new IllegalArgumentException(returnType.toString()); + } + + out.printf(" ConstantNode node = ConstantNode.forConstant(constant, %s, %s);\n", deps.use(WellKnownDependency.META_ACCESS), deps.use(WellKnownDependency.STRUCTURED_GRAPH)); + out.printf(" b.push(JavaKind.%s, node);\n", getReturnKind(intrinsicMethod).name()); + out.printf(" return true;\n"); + + return deps; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.replacements.verifier/src/com/oracle/graal/replacements/verifier/InjectedDependencies.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,139 @@ +/* + * 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.graal.replacements.verifier; + +import java.util.HashMap; +import java.util.Iterator; + +import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeMirror; + +import com.oracle.graal.replacements.verifier.InjectedDependencies.Dependency; + +public class InjectedDependencies implements Iterable<Dependency> { + + public abstract static class Dependency { + + public final String name; + public final String type; + + private Dependency(String name, String type) { + this.name = name; + this.type = type; + } + + public abstract String inject(ExecutableElement inject); + } + + private static final class InjectedDependency extends Dependency { + + private InjectedDependency(String name, String type) { + super(name, type); + } + + @Override + public String inject(ExecutableElement inject) { + return String.format("injection.getInjectedArgument(%s.class)", type); + } + } + + private static final class StampDependency extends Dependency { + + private StampDependency() { + super("returnStamp", "com.oracle.graal.compiler.common.type.Stamp"); + } + + @Override + public String inject(ExecutableElement inject) { + return String.format("injection.getReturnStamp(%s.class)", NodeIntrinsicPluginGenerator.getErasedType(inject.getReturnType())); + } + } + + public enum WellKnownDependency { + CONSTANT_REFLECTION("b.getConstantReflection()", "jdk.vm.ci.meta.ConstantReflectionProvider"), + META_ACCESS("b.getMetaAccess()", "jdk.vm.ci.meta.MetaAccessProvider"), + RETURN_STAMP(new StampDependency()), + SNIPPET_REFLECTION(new InjectedDependency("snippetReflection", "com.oracle.graal.api.replacements.SnippetReflectionProvider")), + STAMP_PROVIDER("b.getStampProvider()", "com.oracle.graal.nodes.spi.StampProvider"), + STRUCTURED_GRAPH("b.getGraph()", "com.oracle.graal.nodes.StructuredGraph"); + + private final String expr; + private final String type; + private final Dependency generateMember; + + private WellKnownDependency(String expr, String type) { + this.expr = expr; + this.type = type; + this.generateMember = null; + } + + private WellKnownDependency(Dependency generateMember) { + this.expr = generateMember.name; + this.type = generateMember.type; + this.generateMember = generateMember; + } + + private TypeMirror getType(ProcessingEnvironment env) { + return env.getElementUtils().getTypeElement(type).asType(); + } + } + + private final HashMap<String, Dependency> deps; + + public InjectedDependencies() { + deps = new HashMap<>(); + } + + public String use(WellKnownDependency wellKnown) { + if (wellKnown.generateMember != null) { + deps.put(wellKnown.type, wellKnown.generateMember); + } + return wellKnown.expr; + } + + public String use(ProcessingEnvironment env, DeclaredType type) { + for (WellKnownDependency wellKnown : WellKnownDependency.values()) { + if (env.getTypeUtils().isAssignable(wellKnown.getType(env), type)) { + return use(wellKnown); + } + } + + String typeName = type.toString(); + Dependency ret = deps.get(typeName); + if (ret == null) { + ret = new InjectedDependency("injected" + type.asElement().getSimpleName(), typeName); + deps.put(typeName, ret); + } + return ret.name; + } + + public Iterator<Dependency> iterator() { + return deps.values().iterator(); + } + + public boolean isEmpty() { + return deps.isEmpty(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.replacements.verifier/src/com/oracle/graal/replacements/verifier/NodeIntrinsicPluginGenerator.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,133 @@ +/* + * 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.graal.replacements.verifier; + +import java.io.PrintWriter; +import java.util.List; + +import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeMirror; +import javax.tools.Diagnostic.Kind; + +import jdk.vm.ci.meta.JavaKind; + +import com.oracle.graal.graph.Node.ConstantNodeParameter; +import com.oracle.graal.graph.Node.InjectedNodeParameter; +import com.oracle.graal.graph.Node.NodeIntrinsic; +import com.oracle.graal.replacements.verifier.InjectedDependencies.WellKnownDependency; + +/** + * Create graph builder plugins for {@link NodeIntrinsic} methods. + */ +public class NodeIntrinsicPluginGenerator extends PluginGenerator { + + public NodeIntrinsicPluginGenerator(ProcessingEnvironment env) { + super(env); + } + + private TypeMirror valueNodeType() { + return env.getElementUtils().getTypeElement("com.oracle.graal.nodes.ValueNode").asType(); + } + + @Override + protected String getBaseName() { + return "NodeIntrinsicFactory"; + } + + @Override + protected void createImports(PrintWriter out, ExecutableElement intrinsicMethod, ExecutableElement targetMethod) { + if (targetMethod.getKind() == ElementKind.CONSTRUCTOR && getReturnKind(intrinsicMethod) != JavaKind.Void) { + out.printf("import jdk.vm.ci.meta.JavaKind;\n"); + } + super.createImports(out, intrinsicMethod, targetMethod); + } + + @Override + protected InjectedDependencies createExecute(PrintWriter out, ExecutableElement intrinsicMethod, ExecutableElement constructor, TypeMirror[] signature) { + InjectedDependencies deps = new InjectedDependencies(); + + List<? extends VariableElement> params = constructor.getParameters(); + + boolean customFactory = constructor.getKind() != ElementKind.CONSTRUCTOR; + int idx = customFactory ? 1 : 0; + for (; idx < params.size(); idx++) { + VariableElement param = params.get(idx); + if (param.getAnnotation(InjectedNodeParameter.class) == null) { + break; + } + + out.printf(" %s arg%d = %s;\n", param.asType(), idx, deps.use(env, (DeclaredType) param.asType())); + } + + for (int i = 0; i < signature.length; i++, idx++) { + if (intrinsicMethod.getParameters().get(i).getAnnotation(ConstantNodeParameter.class) != null) { + constantArgument(out, deps, idx, signature[i], i); + } else { + if (signature[i].equals(valueNodeType())) { + out.printf(" ValueNode arg%d = args[%d];\n", idx, i); + } else { + out.printf(" %s arg%d = (%s) args[%d];\n", signature[i], idx, signature[i], i); + } + } + } + + if (customFactory) { + out.printf(" return %s.%s(b", constructor.getEnclosingElement(), constructor.getSimpleName()); + for (int i = 1; i < idx; i++) { + out.printf(", arg%d", i); + } + out.printf(");\n"); + + if (intrinsicMethod.getAnnotation(NodeIntrinsic.class).setStampFromReturnType()) { + env.getMessager().printMessage(Kind.WARNING, "Ignoring setStampFromReturnType because a custom 'intrinsify' method is used.", intrinsicMethod); + } + } else { + out.printf(" %s node = new %s(", constructor.getEnclosingElement(), constructor.getEnclosingElement()); + if (idx > 0) { + out.printf("arg0"); + for (int i = 1; i < idx; i++) { + out.printf(", arg%d", i); + } + } + out.printf(");\n"); + + if (intrinsicMethod.getAnnotation(NodeIntrinsic.class).setStampFromReturnType()) { + out.printf(" node.setStamp(%s);\n", deps.use(WellKnownDependency.RETURN_STAMP)); + } + + JavaKind returnKind = getReturnKind(intrinsicMethod); + if (returnKind == JavaKind.Void) { + out.printf(" b.add(node);\n"); + } else { + out.printf(" b.addPush(JavaKind.%s, node);\n", returnKind.name()); + } + out.printf(" return true;\n"); + } + + return deps; + } +}
--- a/graal/com.oracle.graal.replacements.verifier/src/com/oracle/graal/replacements/verifier/NodeIntrinsicVerifier.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.replacements.verifier/src/com/oracle/graal/replacements/verifier/NodeIntrinsicVerifier.java Tue Dec 08 12:30:15 2015 -0800 @@ -73,8 +73,15 @@ return env.getElementUtils().getTypeElement("com.oracle.graal.nodeinfo.StructuralInput").asType(); } + private TypeMirror graphBuilderContextType() { + return env.getElementUtils().getTypeElement("com.oracle.graal.nodes.graphbuilderconf.GraphBuilderContext").asType(); + } + + private final NodeIntrinsicPluginGenerator factoryGen; + public NodeIntrinsicVerifier(ProcessingEnvironment env) { super(env); + factoryGen = new NodeIntrinsicPluginGenerator(env); } @Override @@ -114,20 +121,28 @@ env.getMessager().printMessage(Kind.ERROR, "@NodeIntrinsic cannot have a generic return type.", element, annotation); } - if (isNodeType(nodeClass)) { - if (nodeClass.getModifiers().contains(Modifier.ABSTRACT)) { - env.getMessager().printMessage(Kind.ERROR, String.format("Cannot make @NodeIntrinsic for abstract node class %s.", nodeClass.getSimpleName()), element, annotation); + TypeMirror[] constructorSignature = constructorSignature(intrinsicMethod); + ExecutableElement custom = findCustomIntrinsifyMethod(nodeClass, constructorSignature); + if (custom != null) { + factoryGen.createPluginFactory(intrinsicMethod, custom, constructorSignature); + } else { + if (isNodeType(nodeClass)) { + if (nodeClass.getModifiers().contains(Modifier.ABSTRACT)) { + env.getMessager().printMessage(Kind.ERROR, String.format("Cannot make @NodeIntrinsic for abstract node class %s.", nodeClass.getSimpleName()), element, annotation); + } else { + TypeMirror ret = intrinsicMethod.getReturnType(); + if (env.getTypeUtils().isAssignable(ret, structuralInputType())) { + checkInputType(nodeClass, ret, element, annotation); + } + + ExecutableElement constructor = findConstructor(nodeClass, constructorSignature, intrinsicMethod, annotation); + if (constructor != null) { + factoryGen.createPluginFactory(intrinsicMethod, constructor, constructorSignature); + } + } } else { - TypeMirror ret = intrinsicMethod.getReturnType(); - if (env.getTypeUtils().isAssignable(ret, structuralInputType())) { - checkInputType(nodeClass, ret, element, annotation); - } - - TypeMirror[] constructorSignature = constructorSignature(intrinsicMethod); - findConstructor(nodeClass, constructorSignature, intrinsicMethod, annotation); + env.getMessager().printMessage(Kind.ERROR, String.format("The class %s is not a Node subclass.", nodeClass.getSimpleName()), element, annotation); } - } else { - env.getMessager().printMessage(Kind.ERROR, String.format("The class %s is not a Node subclass.", nodeClass.getSimpleName()), element, annotation); } } @@ -185,47 +200,18 @@ return parameters; } - private void findConstructor(TypeElement nodeClass, TypeMirror[] signature, ExecutableElement intrinsicMethod, AnnotationMirror intrinsicAnnotation) { + private ExecutableElement findConstructor(TypeElement nodeClass, TypeMirror[] signature, ExecutableElement intrinsicMethod, AnnotationMirror intrinsicAnnotation) { List<ExecutableElement> constructors = ElementFilter.constructorsIn(nodeClass.getEnclosedElements()); List<String> failureReasons = new ArrayList<>(); - nextConstructor: for (ExecutableElement constructor : constructors) { - int sIdx = 0; - int cIdx = 0; - while (cIdx < constructor.getParameters().size()) { - VariableElement parameter = constructor.getParameters().get(cIdx++); - if (parameter.getAnnotation(InjectedNodeParameter.class) != null) { - // skip injected parameters - continue; - } - - TypeMirror paramType = parameter.asType(); - if (cIdx == constructor.getParameters().size() && paramType.getKind() == TypeKind.ARRAY) { - // last argument of constructor is varargs, match remaining intrinsic arguments - TypeMirror varargsType = ((ArrayType) paramType).getComponentType(); - while (sIdx < signature.length) { - if (!isTypeCompatible(varargsType, signature[sIdx++])) { - failureReasons.add(String.format("Constructor %s failed because the types of argument %d are incompatible: %s != %s", constructor, sIdx, varargsType, signature[sIdx - 1])); - continue nextConstructor; - } - } - } else if (sIdx >= signature.length) { - // too many arguments in intrinsic method - failureReasons.add(String.format("Too many arguments for %s", constructor)); - continue nextConstructor; - } else if (!isTypeCompatible(paramType, signature[sIdx++])) { - failureReasons.add(String.format("Constructor %s failed because the types of argument %d are incompatible: %s != %s", constructor, sIdx, paramType, signature[sIdx - 1])); - continue nextConstructor; - } + for (ExecutableElement constructor : constructors) { + String failureReason = matchSignature(false, constructor, signature); + if (failureReason == null) { + // found + return constructor; } - if (sIdx == signature.length) { - // found - return; - } - - // too many arguments in constructor - failureReasons.add(String.format("Not enough arguments for %s", constructor)); + failureReasons.add(failureReason); } // not found @@ -236,6 +222,70 @@ env.getMessager().printMessage(Kind.ERROR, reason, intrinsicMethod, intrinsicAnnotation); } } + + return null; + } + + private ExecutableElement findCustomIntrinsifyMethod(TypeElement nodeClass, TypeMirror[] signature) { + List<ExecutableElement> methods = ElementFilter.methodsIn(nodeClass.getEnclosedElements()); + for (ExecutableElement method : methods) { + if (!method.getSimpleName().toString().equals("intrinsify")) { + continue; + } + + if (method.getParameters().isEmpty()) { + continue; + } + + VariableElement firstArg = method.getParameters().get(0); + if (!isTypeCompatible(firstArg.asType(), graphBuilderContextType())) { + continue; + } + + String failureReason = matchSignature(true, method, signature); + if (failureReason == null) { + // found + return method; + } + } + + return null; + } + + private String matchSignature(boolean skipFirst, ExecutableElement method, TypeMirror[] signature) { + int sIdx = 0; + int cIdx = skipFirst ? 1 : 0; + while (cIdx < method.getParameters().size()) { + VariableElement parameter = method.getParameters().get(cIdx++); + if (parameter.getAnnotation(InjectedNodeParameter.class) != null) { + // skip injected parameters + continue; + } + + TypeMirror paramType = parameter.asType(); + if (cIdx == method.getParameters().size() && paramType.getKind() == TypeKind.ARRAY) { + // last argument of constructor is varargs, match remaining intrinsic arguments + TypeMirror varargsType = ((ArrayType) paramType).getComponentType(); + while (sIdx < signature.length) { + if (!isTypeCompatible(varargsType, signature[sIdx++])) { + return String.format("%s failed because the types of argument %d are incompatible: %s != %s", method, sIdx, varargsType, signature[sIdx - 1]); + } + } + } else if (sIdx >= signature.length) { + // too many arguments in intrinsic method + return String.format("Too many arguments for %s", method); + } else if (!isTypeCompatible(paramType, signature[sIdx++])) { + return String.format("%s failed because the types of argument %d are incompatible: %s != %s", method, sIdx, paramType, signature[sIdx - 1]); + } + } + + if (sIdx == signature.length) { + // found + return null; + } + + // too many arguments in constructor + return String.format("Not enough arguments for %s", method); } private boolean isTypeCompatible(TypeMirror originalType, TypeMirror substitutionType) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.replacements.verifier/src/com/oracle/graal/replacements/verifier/PluginGenerator.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,267 @@ +/* + * 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.graal.replacements.verifier; + +import java.io.IOException; +import java.io.PrintWriter; + +import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.ArrayType; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.TypeVariable; +import javax.lang.model.type.WildcardType; +import javax.tools.Diagnostic; +import javax.tools.JavaFileObject; + +import jdk.vm.ci.meta.JavaKind; + +import com.oracle.graal.replacements.verifier.InjectedDependencies.Dependency; +import com.oracle.graal.replacements.verifier.InjectedDependencies.WellKnownDependency; + +public abstract class PluginGenerator { + + protected final ProcessingEnvironment env; + + public PluginGenerator(ProcessingEnvironment env) { + this.env = env; + } + + private TypeMirror resolvedJavaTypeType() { + return env.getElementUtils().getTypeElement("jdk.vm.ci.meta.ResolvedJavaType").asType(); + } + + private static Element getTopLevelClass(Element element) { + Element prev = element; + Element enclosing = element.getEnclosingElement(); + while (enclosing != null && enclosing.getKind() != ElementKind.PACKAGE) { + prev = enclosing; + enclosing = enclosing.getEnclosingElement(); + } + return prev; + } + + private static void mkClassName(StringBuilder ret, Element cls) { + Element enclosingClass = cls.getEnclosingElement(); + if (enclosingClass.getKind() == ElementKind.CLASS || enclosingClass.getKind() == ElementKind.INTERFACE) { + mkClassName(ret, enclosingClass); + ret.append('_'); + } + ret.append(cls.getSimpleName()); + } + + static String getErasedType(TypeMirror type) { + switch (type.getKind()) { + case DECLARED: + DeclaredType declared = (DeclaredType) type; + TypeElement element = (TypeElement) declared.asElement(); + return element.getQualifiedName().toString(); + case TYPEVAR: + return getErasedType(((TypeVariable) type).getUpperBound()); + case WILDCARD: + return getErasedType(((WildcardType) type).getExtendsBound()); + case ARRAY: + return getErasedType(((ArrayType) type).getComponentType()) + "[]"; + default: + return type.toString(); + } + } + + protected abstract String getBaseName(); + + private String mkFactoryClassName(ExecutableElement intrinsicMethod) { + StringBuilder ret = new StringBuilder(); + ret.append(getBaseName()); + ret.append('_'); + mkClassName(ret, intrinsicMethod.getEnclosingElement()); + ret.append('_'); + ret.append(intrinsicMethod.getSimpleName()); + if (!intrinsicMethod.getParameters().isEmpty()) { + ret.append('_'); + ret.append(Integer.toHexString(APHotSpotSignature.toSignature(intrinsicMethod).hashCode())); + } + return ret.toString(); + } + + void createPluginFactory(ExecutableElement intrinsicMethod, ExecutableElement targetMethod, TypeMirror[] constructorSignature) { + Element declaringClass = intrinsicMethod.getEnclosingElement(); + Element topLevelClass = getTopLevelClass(declaringClass); + PackageElement pkg = (PackageElement) topLevelClass.getEnclosingElement(); + + String genClassName = mkFactoryClassName(intrinsicMethod); + + try { + JavaFileObject factory = env.getFiler().createSourceFile(pkg.getQualifiedName() + "." + genClassName, topLevelClass, declaringClass, intrinsicMethod); + try (PrintWriter out = new PrintWriter(factory.openWriter())) { + out.printf("// CheckStyle: stop header check\n"); + out.printf("// CheckStyle: stop line length check\n"); + out.printf("// GENERATED CONTENT - DO NOT EDIT\n"); + out.printf("package %s;\n", pkg.getQualifiedName()); + out.printf("\n"); + createImports(out, intrinsicMethod, targetMethod); + out.printf("\n"); + out.printf("@ServiceProvider(NodeIntrinsicPluginFactory.class)\n"); + out.printf("public class %s implements NodeIntrinsicPluginFactory {\n", genClassName); + out.printf("\n"); + out.printf(" private static final class Plugin extends GeneratedInvocationPlugin {\n"); + out.printf("\n"); + + out.printf(" @Override\n"); + out.printf(" public boolean execute(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode[] args) {\n"); + out.printf(" if (!b.parsingIntrinsic()) {\n"); + out.printf(" return false;\n"); + out.printf(" }\n"); + InjectedDependencies deps = createExecute(out, intrinsicMethod, targetMethod, constructorSignature); + out.printf(" }\n"); + + createPrivateMembers(out, intrinsicMethod, deps); + + out.printf(" }\n"); + out.printf("\n"); + createPluginFactoryMethod(out, intrinsicMethod, deps); + out.printf("}\n"); + } + } catch (IOException e) { + env.getMessager().printMessage(Diagnostic.Kind.ERROR, e.getMessage()); + } + } + + protected abstract InjectedDependencies createExecute(PrintWriter out, ExecutableElement intrinsicMethod, ExecutableElement constructor, TypeMirror[] signature); + + protected void createImports(PrintWriter out, @SuppressWarnings("unused") ExecutableElement intrinsicMethod, @SuppressWarnings("unused") ExecutableElement targetMethod) { + out.printf("import jdk.vm.ci.meta.ResolvedJavaMethod;\n"); + out.printf("import jdk.vm.ci.service.ServiceProvider;\n"); + out.printf("\n"); + out.printf("import com.oracle.graal.nodes.ValueNode;\n"); + out.printf("import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderContext;\n"); + out.printf("import com.oracle.graal.nodes.graphbuilderconf.GeneratedInvocationPlugin;\n"); + out.printf("import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugin;\n"); + out.printf("import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugins;\n"); + out.printf("import com.oracle.graal.nodes.graphbuilderconf.NodeIntrinsicPluginFactory;\n"); + } + + private static void createPrivateMembers(PrintWriter out, ExecutableElement intrinsicMethod, InjectedDependencies deps) { + if (!deps.isEmpty()) { + out.printf("\n"); + for (Dependency dep : deps) { + out.printf(" private final %s %s;\n", dep.type, dep.name); + } + + out.printf("\n"); + out.printf(" private Plugin(InjectionProvider injection) {\n"); + for (Dependency dep : deps) { + out.printf(" this.%s = %s;\n", dep.name, dep.inject(intrinsicMethod)); + } + out.printf(" }\n"); + } + } + + private static void createPluginFactoryMethod(PrintWriter out, ExecutableElement intrinsicMethod, InjectedDependencies deps) { + out.printf(" public void registerPlugin(InvocationPlugins plugins, InjectionProvider injection) {\n"); + out.printf(" Plugin plugin = new Plugin(%s);\n", deps.isEmpty() ? "" : "injection"); + out.printf(" plugins.register(plugin, %s.class, \"%s\"", intrinsicMethod.getEnclosingElement(), intrinsicMethod.getSimpleName()); + if (!intrinsicMethod.getModifiers().contains(Modifier.STATIC)) { + out.printf(", InvocationPlugin.Receiver.class"); + } + for (VariableElement arg : intrinsicMethod.getParameters()) { + out.printf(", %s.class", getErasedType(arg.asType())); + } + out.printf(");\n"); + out.printf(" }\n"); + } + + protected static JavaKind getReturnKind(ExecutableElement method) { + switch (method.getReturnType().getKind()) { + case BOOLEAN: + case BYTE: + case SHORT: + case CHAR: + case INT: + return JavaKind.Int; + case LONG: + return JavaKind.Long; + case FLOAT: + return JavaKind.Float; + case DOUBLE: + return JavaKind.Double; + case VOID: + return JavaKind.Void; + case ARRAY: + case TYPEVAR: + case DECLARED: + return JavaKind.Object; + default: + throw new IllegalArgumentException(method.getReturnType().toString()); + } + } + + protected void constantArgument(PrintWriter out, InjectedDependencies deps, int argIdx, TypeMirror type, int nodeIdx) { + out.printf(" %s arg%d;\n", type, argIdx); + out.printf(" if (args[%d].isConstant()) {\n", nodeIdx); + if (type.equals(resolvedJavaTypeType())) { + out.printf(" arg%d = %s.asJavaType(args[%d].asConstant());\n", argIdx, deps.use(WellKnownDependency.CONSTANT_REFLECTION), nodeIdx); + } else { + switch (type.getKind()) { + case BOOLEAN: + out.printf(" arg%d = args[%d].asJavaConstant().asInt() != 0;\n", argIdx, nodeIdx); + break; + case BYTE: + out.printf(" arg%d = (byte) args[%d].asJavaConstant().asInt();\n", argIdx, nodeIdx); + break; + case CHAR: + out.printf(" arg%d = (char) args[%d].asJavaConstant().asInt();\n", argIdx, nodeIdx); + break; + case SHORT: + out.printf(" arg%d = (short) args[%d].asJavaConstant().asInt();\n", argIdx, nodeIdx); + break; + case INT: + out.printf(" arg%d = args[%d].asJavaConstant().asInt();\n", argIdx, nodeIdx); + break; + case LONG: + out.printf(" arg%d = args[%d].asJavaConstant().asLong();\n", argIdx, nodeIdx); + break; + case FLOAT: + out.printf(" arg%d = args[%d].asJavaConstant().asFloat();\n", argIdx, nodeIdx); + break; + case DOUBLE: + out.printf(" arg%d = args[%d].asJavaConstant().asDouble();\n", argIdx, nodeIdx); + break; + case DECLARED: + out.printf(" arg%d = %s.asObject(%s.class, args[%d].asJavaConstant());\n", argIdx, deps.use(WellKnownDependency.SNIPPET_REFLECTION), type, nodeIdx); + break; + default: + throw new IllegalArgumentException(); + } + } + out.printf(" } else {\n"); + out.printf(" return false;\n"); + out.printf(" }\n"); + } +}
--- a/graal/com.oracle.graal.replacements.verifier/src/com/oracle/graal/replacements/verifier/VerifierAnnotationProcessor.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.replacements.verifier/src/com/oracle/graal/replacements/verifier/VerifierAnnotationProcessor.java Tue Dec 08 12:30:15 2015 -0800 @@ -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 @@ -84,6 +84,7 @@ verifiers.add(new ClassSubstitutionVerifier(this.processingEnv)); verifiers.add(new MethodSubstitutionVerifier(this.processingEnv)); verifiers.add(new NodeIntrinsicVerifier(this.processingEnv)); + verifiers.add(new FoldPluginGenerator(this.processingEnv).getVerifier()); } return verifiers; }
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/CachingPEGraphDecoder.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/CachingPEGraphDecoder.java Tue Dec 08 12:30:15 2015 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -22,7 +22,7 @@ */ package com.oracle.graal.replacements; -import static com.oracle.graal.graphbuilderconf.IntrinsicContext.CompilationContext.INLINE_AFTER_PARSING; +import static com.oracle.graal.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.INLINE_AFTER_PARSING; import java.util.HashMap; import java.util.Map; @@ -31,13 +31,13 @@ import jdk.vm.ci.meta.ResolvedJavaMethod; import com.oracle.graal.debug.Debug; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration; -import com.oracle.graal.graphbuilderconf.IntrinsicContext; import com.oracle.graal.java.GraphBuilderPhase; import com.oracle.graal.nodes.EncodedGraph; import com.oracle.graal.nodes.GraphEncoder; import com.oracle.graal.nodes.StructuredGraph; import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration; +import com.oracle.graal.nodes.graphbuilderconf.IntrinsicContext; import com.oracle.graal.phases.OptimisticOptimizations; import com.oracle.graal.phases.common.CanonicalizerPhase; import com.oracle.graal.phases.tiers.PhaseContext;
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ConstantBindingParameterPlugin.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ConstantBindingParameterPlugin.java Tue Dec 08 12:30:15 2015 -0800 @@ -27,10 +27,10 @@ import com.oracle.graal.api.replacements.SnippetReflectionProvider; import com.oracle.graal.compiler.common.type.Stamp; -import com.oracle.graal.graphbuilderconf.GraphBuilderContext; -import com.oracle.graal.graphbuilderconf.ParameterPlugin; import com.oracle.graal.nodes.ConstantNode; import com.oracle.graal.nodes.calc.FloatingNode; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderContext; +import com.oracle.graal.nodes.graphbuilderconf.ParameterPlugin; /** * A {@link ParameterPlugin} that binds constant values to some parameters.
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/DefaultJavaLoweringProvider.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/DefaultJavaLoweringProvider.java Tue Dec 08 12:30:15 2015 -0800 @@ -192,7 +192,7 @@ } private void lowerTypeCheckNode(TypeCheckNode n, LoweringTool tool, StructuredGraph graph) { - ValueNode hub = createReadHub(graph, n.getValue(), null, tool); + ValueNode hub = createReadHub(graph, n.getValue(), tool); ValueNode clazz = graph.unique(ConstantNode.forConstant(tool.getStampProvider().createHubStamp((ObjectStamp) n.getValue().stamp()), n.type().getObjectHub(), tool.getMetaAccess())); LogicNode objectEquals = graph.unique(PointerEqualsNode.create(hub, clazz)); n.replaceAndDelete(objectEquals); @@ -344,7 +344,7 @@ */ GuardingNode nullCheck = nullCheckReturn[0]; assert nullCheckReturn[0] != null || createNullCheck(array, storeIndexed, tool) == null; - ValueNode arrayClass = createReadHub(graph, array, nullCheck, tool); + ValueNode arrayClass = createReadHub(graph, graph.unique(new PiNode(array, (ValueNode) nullCheck)), tool); ValueNode componentHub = createReadArrayComponentHub(graph, arrayClass, storeIndexed); checkCastNode = graph.add(new CheckCastDynamicNode(componentHub, value, true)); graph.addBeforeFixed(storeIndexed, checkCastNode); @@ -383,7 +383,7 @@ if (graph.getGuardsStage().allowsFloatingGuards()) { return; } - ValueNode hub = createReadHub(graph, loadHub.getValue(), loadHub.getGuard(), tool); + ValueNode hub = createReadHub(graph, loadHub.getValue(), tool); graph.replaceFloating(loadHub, hub); } @@ -715,40 +715,63 @@ return stamp; } - public ValueNode implicitLoadConvert(StructuredGraph graph, JavaKind kind, ValueNode value) { + public final ValueNode implicitLoadConvert(StructuredGraph graph, JavaKind kind, ValueNode value) { return implicitLoadConvert(graph, kind, value, true); + } + public ValueNode implicitLoadConvert(JavaKind kind, ValueNode value) { + return implicitLoadConvert(kind, value, true); } - protected ValueNode implicitLoadConvert(StructuredGraph graph, JavaKind kind, ValueNode value, @SuppressWarnings("unused") boolean compressible) { + protected final ValueNode implicitLoadConvert(StructuredGraph graph, JavaKind kind, ValueNode value, boolean compressible) { + ValueNode ret = implicitLoadConvert(kind, value, compressible); + if (!ret.isAlive()) { + ret = graph.addOrUnique(ret); + } + return ret; + } + + protected ValueNode implicitLoadConvert(JavaKind kind, ValueNode value, @SuppressWarnings("unused") boolean compressible) { switch (kind) { case Byte: case Short: - return graph.unique(new SignExtendNode(value, 32)); + return new SignExtendNode(value, 32); case Boolean: case Char: - return graph.unique(new ZeroExtendNode(value, 32)); + return new ZeroExtendNode(value, 32); } return value; } - public ValueNode implicitStoreConvert(StructuredGraph graph, JavaKind kind, ValueNode value) { + public final ValueNode implicitStoreConvert(StructuredGraph graph, JavaKind kind, ValueNode value) { return implicitStoreConvert(graph, kind, value, true); } - protected ValueNode implicitStoreConvert(StructuredGraph graph, JavaKind kind, ValueNode value, @SuppressWarnings("unused") boolean compressible) { + public ValueNode implicitStoreConvert(JavaKind kind, ValueNode value) { + return implicitStoreConvert(kind, value, true); + } + + protected final ValueNode implicitStoreConvert(StructuredGraph graph, JavaKind kind, ValueNode value, boolean compressible) { + ValueNode ret = implicitStoreConvert(kind, value, compressible); + if (!ret.isAlive()) { + ret = graph.addOrUnique(ret); + } + return ret; + } + + protected ValueNode implicitStoreConvert(JavaKind kind, ValueNode value, @SuppressWarnings("unused") boolean compressible) { switch (kind) { case Boolean: case Byte: - return graph.unique(new NarrowNode(value, 8)); + return new NarrowNode(value, 8); case Char: case Short: - return graph.unique(new NarrowNode(value, 16)); + return new NarrowNode(value, 16); } return value; } - protected abstract ValueNode createReadHub(StructuredGraph graph, ValueNode object, GuardingNode guard, LoweringTool tool); + protected abstract ValueNode createReadHub(StructuredGraph graph, ValueNode object, LoweringTool tool); protected abstract ValueNode createReadArrayComponentHub(StructuredGraph graph, ValueNode arrayHub, FixedNode anchor);
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/GraphKit.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/GraphKit.java Tue Dec 08 12:30:15 2015 -0800 @@ -22,7 +22,7 @@ */ package com.oracle.graal.replacements; -import static com.oracle.graal.graphbuilderconf.IntrinsicContext.CompilationContext.INLINE_AFTER_PARSING; +import static com.oracle.graal.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.INLINE_AFTER_PARSING; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -39,9 +39,6 @@ import com.oracle.graal.graph.Graph; import com.oracle.graal.graph.Node; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins; -import com.oracle.graal.graphbuilderconf.IntrinsicContext; import com.oracle.graal.java.FrameStateBuilder; import com.oracle.graal.java.GraphBuilderPhase; import com.oracle.graal.nodes.AbstractBeginNode; @@ -60,6 +57,9 @@ import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions; import com.oracle.graal.nodes.ValueNode; import com.oracle.graal.nodes.calc.FloatingNode; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration; +import com.oracle.graal.nodes.graphbuilderconf.IntrinsicContext; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; import com.oracle.graal.nodes.java.MethodCallTargetNode; import com.oracle.graal.nodes.type.StampTool; import com.oracle.graal.phases.OptimisticOptimizations;
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/InlineDuringParsingPlugin.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/InlineDuringParsingPlugin.java Tue Dec 08 12:30:15 2015 -0800 @@ -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 @@ -27,9 +27,9 @@ import jdk.vm.ci.meta.JavaType; import jdk.vm.ci.meta.ResolvedJavaMethod; -import com.oracle.graal.graphbuilderconf.GraphBuilderContext; -import com.oracle.graal.graphbuilderconf.InlineInvokePlugin; import com.oracle.graal.nodes.ValueNode; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderContext; +import com.oracle.graal.nodes.graphbuilderconf.InlineInvokePlugin; public final class InlineDuringParsingPlugin implements InlineInvokePlugin {
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/InstanceOfSnippetsTemplates.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/InstanceOfSnippetsTemplates.java Tue Dec 08 12:30:15 2015 -0800 @@ -110,7 +110,7 @@ */ protected InstanceOfUsageReplacer createReplacer(FloatingNode instanceOf, Instantiation instantiation, Node usage, final StructuredGraph graph) { InstanceOfUsageReplacer replacer; - if (usage instanceof IfNode || usage instanceof FixedGuardNode || usage instanceof ShortCircuitOrNode || usage instanceof ConditionAnchorNode) { + if (!canMaterialize(usage)) { ValueNode trueValue = ConstantNode.forInt(1, graph); ValueNode falseValue = ConstantNode.forInt(0, graph); if (instantiation.isInitialized() && (trueValue != instantiation.trueValue || falseValue != instantiation.falseValue)) { @@ -131,6 +131,20 @@ } /** + * Determines if an {@code instanceof} usage can be materialized. + */ + protected boolean canMaterialize(Node usage) { + if (usage instanceof ConditionalNode) { + ConditionalNode cn = (ConditionalNode) usage; + return cn.trueValue().isConstant() && cn.falseValue().isConstant(); + } + if (usage instanceof IfNode || usage instanceof FixedGuardNode || usage instanceof ShortCircuitOrNode || usage instanceof ConditionAnchorNode) { + return false; + } + return true; + } + + /** * The result of instantiating an instanceof snippet. This enables a snippet instantiation to be * re-used which reduces compile time and produces better code. */
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/IntrinsicGraphBuilder.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/IntrinsicGraphBuilder.java Tue Dec 08 12:30:15 2015 -0800 @@ -35,10 +35,6 @@ import com.oracle.graal.compiler.common.type.Stamp; import com.oracle.graal.compiler.common.type.StampFactory; -import com.oracle.graal.graphbuilderconf.GraphBuilderContext; -import com.oracle.graal.graphbuilderconf.IntrinsicContext; -import com.oracle.graal.graphbuilderconf.InvocationPlugin; -import com.oracle.graal.graphbuilderconf.InvocationPlugin.Receiver; import com.oracle.graal.nodes.CallTargetNode.InvokeKind; import com.oracle.graal.nodes.FixedNode; import com.oracle.graal.nodes.FixedWithNextNode; @@ -50,6 +46,10 @@ import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions; import com.oracle.graal.nodes.ValueNode; import com.oracle.graal.nodes.calc.FloatingNode; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderContext; +import com.oracle.graal.nodes.graphbuilderconf.IntrinsicContext; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugin; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugin.Receiver; import com.oracle.graal.nodes.spi.StampProvider; /**
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/MethodHandlePlugin.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/MethodHandlePlugin.java Tue Dec 08 12:30:15 2015 -0800 @@ -29,10 +29,10 @@ import jdk.vm.ci.meta.ResolvedJavaMethod; import com.oracle.graal.graph.NodeInputList; -import com.oracle.graal.graphbuilderconf.GraphBuilderContext; -import com.oracle.graal.graphbuilderconf.NodePlugin; import com.oracle.graal.nodes.CallTargetNode; import com.oracle.graal.nodes.CallTargetNode.InvokeKind; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderContext; +import com.oracle.graal.nodes.graphbuilderconf.NodePlugin; import com.oracle.graal.nodes.InvokeNode; import com.oracle.graal.nodes.ValueNode; import com.oracle.graal.replacements.nodes.MethodHandleNode;
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/NodeIntrinsificationPhase.java Tue Dec 08 12:29:24 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,506 +0,0 @@ -/* - * Copyright (c) 2011, 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.graal.replacements; - -import static jdk.vm.ci.meta.MetaUtil.resolveJavaTypes; - -import java.lang.reflect.Array; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import jdk.vm.ci.common.JVMCIError; -import jdk.vm.ci.meta.Constant; -import jdk.vm.ci.meta.ConstantReflectionProvider; -import jdk.vm.ci.meta.JavaConstant; -import jdk.vm.ci.meta.JavaKind; -import jdk.vm.ci.meta.JavaMethod; -import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.PrimitiveConstant; -import jdk.vm.ci.meta.ResolvedJavaMethod; -import jdk.vm.ci.meta.ResolvedJavaType; - -import com.oracle.graal.api.replacements.Fold; -import com.oracle.graal.api.replacements.SnippetReflectionProvider; -import com.oracle.graal.compiler.common.spi.ForeignCallsProvider; -import com.oracle.graal.compiler.common.type.Stamp; -import com.oracle.graal.compiler.common.type.StampFactory; -import com.oracle.graal.debug.Debug; -import com.oracle.graal.debug.internal.DebugScope; -import com.oracle.graal.graph.Node; -import com.oracle.graal.graph.Node.ConstantNodeParameter; -import com.oracle.graal.graph.Node.InjectedNodeParameter; -import com.oracle.graal.graph.Node.NodeIntrinsic; -import com.oracle.graal.nodes.ConstantNode; -import com.oracle.graal.nodes.FrameState; -import com.oracle.graal.nodes.LogicConstantNode; -import com.oracle.graal.nodes.PiNode; -import com.oracle.graal.nodes.ProxyNode; -import com.oracle.graal.nodes.ReturnNode; -import com.oracle.graal.nodes.StructuredGraph; -import com.oracle.graal.nodes.ValueNode; -import com.oracle.graal.nodes.ValueProxyNode; -import com.oracle.graal.nodes.calc.FloatingNode; -import com.oracle.graal.nodes.calc.IsNullNode; -import com.oracle.graal.nodes.extended.UnboxNode; -import com.oracle.graal.nodes.extended.UnsafeStoreNode; -import com.oracle.graal.nodes.extended.ValueAnchorNode; -import com.oracle.graal.nodes.java.CheckCastNode; -import com.oracle.graal.nodes.java.LoadFieldNode; -import com.oracle.graal.nodes.java.MethodCallTargetNode; -import com.oracle.graal.nodes.spi.StampProvider; -import com.oracle.graal.nodes.util.GraphUtil; -import com.oracle.graal.phases.Phase; - -/** - * Replaces calls to {@link NodeIntrinsic}s with nodes and calls to methods annotated with - * {@link Fold} with the result of invoking the annotated method via reflection. - */ -public class NodeIntrinsificationPhase extends Phase { - - private final MetaAccessProvider metaAccess; - private final ConstantReflectionProvider constantReflection; - private final SnippetReflectionProvider snippetReflection; - private final ForeignCallsProvider foreignCalls; - private final StampProvider stampProvider; - - public NodeIntrinsificationPhase(MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection, SnippetReflectionProvider snippetReflection, ForeignCallsProvider foreignCalls, - StampProvider stampProvider) { - this.metaAccess = metaAccess; - this.constantReflection = constantReflection; - this.snippetReflection = snippetReflection; - this.foreignCalls = foreignCalls; - this.stampProvider = stampProvider; - } - - @Override - protected void run(StructuredGraph graph) { - ArrayList<Node> cleanUpReturnList = new ArrayList<>(); - for (MethodCallTargetNode node : graph.getNodes(MethodCallTargetNode.TYPE)) { - tryIntrinsify(node, cleanUpReturnList); - } - - for (Node node : cleanUpReturnList) { - cleanUpReturnCheckCast(node); - } - } - - protected boolean tryIntrinsify(MethodCallTargetNode methodCallTargetNode, List<Node> cleanUpReturnList) { - ResolvedJavaMethod target = methodCallTargetNode.targetMethod(); - ResolvedJavaType declaringClass = target.getDeclaringClass(); - StructuredGraph graph = methodCallTargetNode.graph(); - - NodeIntrinsic intrinsic = getIntrinsic(target); - if (intrinsic != null) { - Stamp stamp = methodCallTargetNode.invoke().asNode().stamp(); - Node newInstance = createIntrinsicNode(methodCallTargetNode.arguments(), stamp, target, graph, intrinsic); - if (newInstance == null) { - return false; - } - - // Replace the invoke with the new node. - newInstance = graph.addOrUnique(newInstance); - methodCallTargetNode.invoke().intrinsify(newInstance); - - // Clean up checkcast instructions inserted by javac if the return type is generic. - cleanUpReturnList.add(newInstance); - } else if (isFoldable(target)) { - ResolvedJavaType[] parameterTypes = resolveJavaTypes(target.toParameterTypes(), declaringClass); - JavaConstant constant = tryFold(methodCallTargetNode.arguments(), parameterTypes, target); - if (constant != null && constant.equals(COULD_NOT_FOLD)) { - return false; - } - - if (constant != null) { - // Replace the invoke with the result of the call - ConstantNode node = ConstantNode.forConstant(constant, metaAccess, methodCallTargetNode.graph()); - methodCallTargetNode.invoke().intrinsify(node); - - // Clean up checkcast instructions inserted by javac if the return type is generic. - cleanUpReturnList.add(node); - } else { - // Remove the invoke - methodCallTargetNode.invoke().intrinsify(null); - } - } - return true; - } - - public static final JavaConstant COULD_NOT_FOLD = new PrimitiveConstant(JavaKind.Illegal, 100) { - @Override - public boolean equals(Object o) { - return this == o; - } - }; - - public JavaConstant tryFold(List<ValueNode> args, ResolvedJavaType[] parameterTypes, ResolvedJavaMethod target) { - JavaConstant[] reflectArgs = (JavaConstant[]) prepareArguments(args, parameterTypes, target, true); - if (reflectArgs == null) { - return COULD_NOT_FOLD; - } - JavaConstant receiver = null; - if (!target.isStatic()) { - receiver = reflectArgs[0]; - reflectArgs = Arrays.copyOfRange(reflectArgs, 1, reflectArgs.length); - } - - // Call the method - return target.invoke(receiver, reflectArgs); - } - - /** - * Attempts to create a node to replace a call to a {@link NodeIntrinsic} annotated method. - * - * @param arguments the arguments of the call - * @param stamp the stamp to use for the returned node - * @param method the method annotated with {@link NodeIntrinsic} - * @param graph the graph into which the created node will be added - * @return {@code null} if intrinsification could not (yet) be performed, otherwise the node - * representing the intrinsic - */ - public ValueNode createIntrinsicNode(List<ValueNode> arguments, Stamp stamp, ResolvedJavaMethod method, StructuredGraph graph, NodeIntrinsic intrinsic) { - assert method.getAnnotation(Fold.class) == null; - assert method.isStatic() : "node intrinsic must be static: " + method; - - ResolvedJavaType[] parameterTypes = resolveJavaTypes(method.toParameterTypes(), method.getDeclaringClass()); - - // Prepare the arguments for the reflective constructor call on the node class. - Object[] nodeConstructorArguments = prepareArguments(arguments, parameterTypes, method, false); - if (nodeConstructorArguments == null) { - return null; - } - - // Create the new node instance. - ResolvedJavaType c = getNodeClass(method, intrinsic); - return createNodeInstance(graph, c, parameterTypes, stamp, intrinsic.setStampFromReturnType(), nodeConstructorArguments); - } - - /** - * Permits a subclass to override the default definition of "intrinsic". - */ - public NodeIntrinsic getIntrinsic(ResolvedJavaMethod method) { - return method.getAnnotation(Node.NodeIntrinsic.class); - } - - /** - * Permits a subclass to override the default definition of "foldable". - */ - public boolean isFoldable(ResolvedJavaMethod method) { - return method.getAnnotation(Fold.class) != null; - } - - /** - * Converts the arguments of an invoke node to object values suitable for use as the arguments - * to a reflective invocation of a Java constructor or method. - * - * @param folding specifies if the invocation is for handling a {@link Fold} annotation - * @return the arguments for the reflective invocation or null if an argument of {@code invoke} - * that is expected to be constant isn't - */ - private Object[] prepareArguments(List<ValueNode> arguments, ResolvedJavaType[] parameterTypes, ResolvedJavaMethod target, boolean folding) { - Object[] reflectionCallArguments = folding ? new JavaConstant[arguments.size()] : new Object[arguments.size()]; - for (int i = 0; i < reflectionCallArguments.length; ++i) { - int parameterIndex = i; - if (!target.isStatic()) { - parameterIndex--; - } - ValueNode argument = arguments.get(i); - if (folding || target.getParameterAnnotation(ConstantNodeParameter.class, parameterIndex) != null) { - if (!(argument instanceof ConstantNode)) { - return null; - } - ConstantNode constantNode = (ConstantNode) argument; - Constant constant = constantNode.asConstant(); - /* - * For intrinsification (but not for folding) if we have a Class<?> object we want - * the corresponding ResolvedJavaType. - */ - ResolvedJavaType type = folding ? null : constantReflection.asJavaType(constant); - Object arg; - if (type != null) { - /* If we found such a type then it's our arg */ - arg = type; - parameterTypes[i] = metaAccess.lookupJavaType(ResolvedJavaType.class); - } else { - JavaConstant javaConstant = (JavaConstant) constant; - if (folding) { - /* For folding we want JavaConstants */ - arg = javaConstant; - } else { - /* For intrinsification we want want corresponding objects */ - if (parameterTypes[i].getJavaKind() == JavaKind.Boolean) { - arg = Boolean.valueOf(javaConstant.asInt() != 0); - } else if (parameterTypes[i].getJavaKind() == JavaKind.Byte) { - arg = Byte.valueOf((byte) javaConstant.asInt()); - } else if (parameterTypes[i].getJavaKind() == JavaKind.Short) { - arg = Short.valueOf((short) javaConstant.asInt()); - } else if (parameterTypes[i].getJavaKind() == JavaKind.Char) { - arg = Character.valueOf((char) javaConstant.asInt()); - } else if (parameterTypes[i].getJavaKind() == JavaKind.Object) { - arg = snippetReflection.asObject(parameterTypes[i], javaConstant); - } else { - arg = javaConstant.asBoxedPrimitive(); - } - } - } - - assert folding || !(arg instanceof JavaConstant); - reflectionCallArguments[i] = arg; - } else { - reflectionCallArguments[i] = argument; - parameterTypes[i] = metaAccess.lookupJavaType(ValueNode.class); - } - } - return reflectionCallArguments; - } - - public ResolvedJavaType getNodeClass(ResolvedJavaMethod target, NodeIntrinsic intrinsic) { - ResolvedJavaType result; - if (intrinsic.value() == NodeIntrinsic.class) { - result = target.getDeclaringClass(); - } else { - result = metaAccess.lookupJavaType(intrinsic.value()); - } - assert metaAccess.lookupJavaType(ValueNode.class).isAssignableFrom(result) : "Node intrinsic class " + result.toJavaName(false) + " derived from @" + NodeIntrinsic.class.getSimpleName() + - " annotation on " + target.format("%H.%n(%p)") + " is not a subclass of " + ValueNode.class; - return result; - } - - protected ValueNode createNodeInstance(StructuredGraph graph, ResolvedJavaType nodeClass, ResolvedJavaType[] parameterTypes, Stamp invokeStamp, boolean setStampFromReturnType, - Object[] nodeConstructorArguments) { - ResolvedJavaMethod constructor = null; - Object[] arguments = null; - - for (ResolvedJavaMethod c : nodeClass.getDeclaredConstructors()) { - Object[] match = match(graph, invokeStamp, c, parameterTypes, nodeConstructorArguments); - - if (match != null) { - if (constructor == null) { - constructor = c; - arguments = match; - if (!Debug.isEnabled()) { - // Don't verify there's a unique match in non-debug mode - break; - } - } else { - throw new JVMCIError("Found multiple constructors in %s compatible with signature %s: %s, %s", nodeClass.toJavaName(), sigString(parameterTypes), constructor, c); - } - } - } - if (constructor == null) { - throw new JVMCIError("Could not find constructor in %s compatible with signature %s", nodeClass.toJavaName(), sigString(parameterTypes)); - } - - try { - ValueNode intrinsicNode = (ValueNode) invokeConstructor(constructor, arguments); - - if (setStampFromReturnType) { - intrinsicNode.setStamp(invokeStamp); - } - return intrinsicNode; - } catch (Exception e) { - throw new RuntimeException(constructor + Arrays.toString(nodeConstructorArguments), e); - } - } - - protected Object invokeConstructor(ResolvedJavaMethod constructor, Object[] arguments) { - return snippetReflection.invoke(constructor, null, arguments); - } - - private static String sigString(ResolvedJavaType[] types) { - StringBuilder sb = new StringBuilder("("); - for (int i = 0; i < types.length; i++) { - if (i != 0) { - sb.append(", "); - } - sb.append(types[i].toJavaName()); - } - return sb.append(")").toString(); - } - - private static boolean checkNoMoreInjected(ResolvedJavaMethod c, int start) { - int count = c.getSignature().getParameterCount(false); - for (int i = start; i < count; i++) { - if (c.getParameterAnnotation(InjectedNodeParameter.class, i) != null) { - throw new JVMCIError("Injected parameter %d of type %s must precede all non-injected parameters of %s", i, - c.getSignature().getParameterType(i, c.getDeclaringClass()).toJavaName(false), c.format("%H.%n(%p)")); - } - } - return true; - } - - private Object[] match(StructuredGraph graph, Stamp invokeStamp, ResolvedJavaMethod c, ResolvedJavaType[] parameterTypes, Object[] nodeConstructorArguments) { - Object[] arguments = null; - Object[] injected = null; - - ResolvedJavaType[] signature = resolveJavaTypes(c.getSignature().toParameterTypes(null), c.getDeclaringClass()); - for (int i = 0; i < signature.length; i++) { - if (c.getParameterAnnotation(InjectedNodeParameter.class, i) != null) { - injected = injected == null ? new Object[1] : Arrays.copyOf(injected, injected.length + 1); - Object injectedParameter = snippetReflection.getInjectedNodeIntrinsicParameter(signature[i]); - if (injectedParameter != null) { - injected[injected.length - 1] = injectedParameter; - } else if (signature[i].equals(metaAccess.lookupJavaType(MetaAccessProvider.class))) { - injected[injected.length - 1] = metaAccess; - } else if (signature[i].equals(metaAccess.lookupJavaType(StructuredGraph.class))) { - injected[injected.length - 1] = graph; - } else if (signature[i].equals(metaAccess.lookupJavaType(ForeignCallsProvider.class))) { - injected[injected.length - 1] = foreignCalls; - } else if (signature[i].equals(metaAccess.lookupJavaType(SnippetReflectionProvider.class))) { - injected[injected.length - 1] = snippetReflection; - } else if (signature[i].isAssignableFrom(metaAccess.lookupJavaType(Stamp.class))) { - injected[injected.length - 1] = invokeStamp; - } else if (signature[i].isAssignableFrom(metaAccess.lookupJavaType(StampProvider.class))) { - injected[injected.length - 1] = stampProvider; - } else { - throw new JVMCIError("Cannot handle injected argument of type %s in %s", signature[i].toJavaName(), c.format("%H.%n(%p)")); - } - } else { - assert checkNoMoreInjected(c, i); - break; - } - } - if (injected != null) { - // Chop injected arguments from signature - signature = Arrays.copyOfRange(signature, injected.length, signature.length); - } - - if (Arrays.equals(parameterTypes, signature)) { - // Exact match - arguments = nodeConstructorArguments; - - } else if (signature.length > 0 && signature[signature.length - 1].isArray()) { - // Last constructor parameter is an array, so check if we have a vararg match - int fixedArgs = signature.length - 1; - if (parameterTypes.length < fixedArgs) { - return null; - } - for (int i = 0; i < fixedArgs; i++) { - if (!parameterTypes[i].equals(signature[i])) { - return null; - } - } - - ResolvedJavaType componentType = signature[fixedArgs].getComponentType(); - assert componentType != null; - for (int i = fixedArgs; i < nodeConstructorArguments.length; i++) { - if (!parameterTypes[i].equals(componentType)) { - return null; - } - } - arguments = Arrays.copyOf(nodeConstructorArguments, fixedArgs + 1); - arguments[fixedArgs] = snippetReflection.newArray(componentType, nodeConstructorArguments.length - fixedArgs); - - Object varargs = arguments[fixedArgs]; - for (int i = fixedArgs; i < nodeConstructorArguments.length; i++) { - if (componentType.isPrimitive()) { - Array.set(varargs, i - fixedArgs, nodeConstructorArguments[i]); - } else { - ((Object[]) varargs)[i - fixedArgs] = nodeConstructorArguments[i]; - } - } - } else { - return null; - } - - if (injected != null) { - Object[] copy = new Object[injected.length + arguments.length]; - System.arraycopy(injected, 0, copy, 0, injected.length); - System.arraycopy(arguments, 0, copy, injected.length, arguments.length); - arguments = copy; - } - return arguments; - } - - private static String sourceLocation(Node n) { - String loc = GraphUtil.approxSourceLocation(n); - return loc == null ? "<unknown>" : loc; - } - - public void cleanUpReturnCheckCast(Node newInstance) { - if (newInstance instanceof ValueNode && (((ValueNode) newInstance).getStackKind() != JavaKind.Object || ((ValueNode) newInstance).stamp() == StampFactory.forNodeIntrinsic())) { - StructuredGraph graph = (StructuredGraph) newInstance.graph(); - for (CheckCastNode checkCastNode : newInstance.usages().filter(CheckCastNode.class).snapshot()) { - for (Node checkCastUsage : checkCastNode.usages().snapshot()) { - checkCheckCastUsage(graph, newInstance, checkCastNode, checkCastUsage); - } - GraphUtil.unlinkFixedNode(checkCastNode); - GraphUtil.killCFG(checkCastNode); - } - } - } - - private static void checkCheckCastUsage(StructuredGraph graph, Node intrinsifiedNode, Node input, Node usage) { - if (usage instanceof ValueAnchorNode) { - ValueAnchorNode valueAnchorNode = (ValueAnchorNode) usage; - valueAnchorNode.removeAnchoredNode(); - Debug.log("%s: Removed a ValueAnchor input", Debug.contextSnapshot(JavaMethod.class)); - } else if (usage instanceof UnboxNode) { - UnboxNode unbox = (UnboxNode) usage; - unbox.replaceAtUsages(intrinsifiedNode); - graph.removeFixed(unbox); - Debug.log("%s: Removed an UnboxNode", Debug.contextSnapshot(JavaMethod.class)); - } else if (usage instanceof UnsafeStoreNode) { - UnsafeStoreNode store = (UnsafeStoreNode) usage; - store.replaceFirstInput(input, intrinsifiedNode); - } else if (usage instanceof LoadFieldNode) { - LoadFieldNode load = (LoadFieldNode) usage; - load.replaceAtUsages(intrinsifiedNode); - graph.removeFixed(load); - } else if (usage instanceof MethodCallTargetNode) { - MethodCallTargetNode checkCastCallTarget = (MethodCallTargetNode) usage; - assert checkCastCallTarget.targetMethod().getAnnotation(NodeIntrinsic.class) != null : "checkcast at " + sourceLocation(input) + - " not used by an unboxing method or node intrinsic, but by a call at " + sourceLocation(checkCastCallTarget.usages().first()) + " to " + checkCastCallTarget.targetMethod(); - usage.replaceFirstInput(input, intrinsifiedNode); - Debug.log("%s: Checkcast used in an other node intrinsic", Debug.contextSnapshot(JavaMethod.class)); - } else if (usage instanceof FrameState) { - usage.replaceFirstInput(input, null); - Debug.log("%s: Checkcast used in a FS", Debug.contextSnapshot(JavaMethod.class)); - } else if (usage instanceof ReturnNode && ((ValueNode) intrinsifiedNode).stamp() == StampFactory.forNodeIntrinsic()) { - usage.replaceFirstInput(input, intrinsifiedNode); - Debug.log("%s: Checkcast used in a return with forNodeIntrinsic stamp", Debug.contextSnapshot(JavaMethod.class)); - } else if (usage instanceof IsNullNode) { - if (!usage.hasNoUsages()) { - assert usage.getUsageCount() == 1 && usage.usages().first().predecessor() == input : usage + " " + input; - graph.replaceFloating((FloatingNode) usage, LogicConstantNode.contradiction(graph)); - Debug.log("%s: Replaced IsNull with false", Debug.contextSnapshot(JavaMethod.class)); - } else { - // Removed as usage of a GuardingPiNode - } - } else if (usage instanceof ProxyNode) { - ProxyNode proxy = (ProxyNode) usage; - assert proxy instanceof ValueProxyNode; - ProxyNode newProxy = ProxyNode.forValue((ValueNode) intrinsifiedNode, proxy.proxyPoint(), graph); - for (Node proxyUsage : usage.usages().snapshot()) { - checkCheckCastUsage(graph, newProxy, proxy, proxyUsage); - } - } else if (usage instanceof PiNode) { - for (Node piUsage : usage.usages().snapshot()) { - checkCheckCastUsage(graph, intrinsifiedNode, usage, piUsage); - } - } else { - DebugScope.forceDump(graph, "exception"); - assert false : sourceLocation(usage) + " has unexpected usage " + usage + " of checkcast " + input + " at " + sourceLocation(input); - } - } -}
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/NodeIntrinsificationPlugin.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/NodeIntrinsificationPlugin.java Tue Dec 08 12:30:15 2015 -0800 @@ -22,55 +22,21 @@ */ package com.oracle.graal.replacements; -import static com.oracle.graal.replacements.NodeIntrinsificationPhase.COULD_NOT_FOLD; -import static jdk.vm.ci.meta.MetaUtil.resolveJavaTypes; - -import java.util.Arrays; -import java.util.List; - import jdk.vm.ci.common.JVMCIError; -import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; -import jdk.vm.ci.meta.JavaType; -import jdk.vm.ci.meta.MetaAccessProvider; import jdk.vm.ci.meta.ResolvedJavaMethod; -import jdk.vm.ci.meta.ResolvedJavaType; -import jdk.vm.ci.meta.Signature; import com.oracle.graal.api.replacements.Fold; -import com.oracle.graal.compiler.common.type.ObjectStamp; -import com.oracle.graal.compiler.common.type.Stamp; -import com.oracle.graal.compiler.common.type.StampFactory; import com.oracle.graal.debug.MethodFilter; import com.oracle.graal.graph.Node.NodeIntrinsic; -import com.oracle.graal.graphbuilderconf.GraphBuilderContext; -import com.oracle.graal.graphbuilderconf.NodePlugin; -import com.oracle.graal.nodeinfo.InputType; -import com.oracle.graal.nodeinfo.StructuralInput; -import com.oracle.graal.nodeinfo.StructuralInput.MarkerType; -import com.oracle.graal.nodes.ConstantNode; import com.oracle.graal.nodes.ValueNode; -import com.oracle.graal.nodes.extended.ForeignCallNode; -import com.oracle.graal.nodes.extended.UnsafeCopyNode; -import com.oracle.graal.nodes.extended.UnsafeLoadNode; -import com.oracle.graal.nodes.extended.UnsafeStoreNode; -import com.oracle.graal.word.WordTypes; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderContext; +import com.oracle.graal.nodes.graphbuilderconf.NodePlugin; /** * An {@link NodePlugin} that handles methods annotated by {@link Fold} and {@link NodeIntrinsic}. */ public class NodeIntrinsificationPlugin implements NodePlugin { - protected final NodeIntrinsificationPhase nodeIntrinsification; - private final WordTypes wordTypes; - private final ResolvedJavaType structuralInputType; - private final boolean mustIntrinsify; - - public NodeIntrinsificationPlugin(MetaAccessProvider metaAccess, NodeIntrinsificationPhase nodeIntrinsification, WordTypes wordTypes, boolean mustIntrinsify) { - this.nodeIntrinsification = nodeIntrinsification; - this.wordTypes = wordTypes; - this.mustIntrinsify = mustIntrinsify; - this.structuralInputType = metaAccess.lookupJavaType(StructuralInput.class); - } /** * Calls in replacements to methods matching one of these filters are elided. Only void methods @@ -89,46 +55,7 @@ @Override public boolean handleInvoke(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args) { - NodeIntrinsic intrinsic = nodeIntrinsification.getIntrinsic(method); - if (intrinsic != null) { - Signature sig = method.getSignature(); - JavaKind returnKind = sig.getReturnKind(); - Stamp stamp = StampFactory.forKind(returnKind); - if (returnKind == JavaKind.Object) { - JavaType returnType = sig.getReturnType(method.getDeclaringClass()); - if (returnType instanceof ResolvedJavaType) { - ResolvedJavaType resolvedReturnType = (ResolvedJavaType) returnType; - if (wordTypes.isWord(resolvedReturnType)) { - stamp = wordTypes.getWordStamp(resolvedReturnType); - } else { - stamp = StampFactory.declared(resolvedReturnType); - } - } - } - - boolean result = processNodeIntrinsic(b, method, intrinsic, Arrays.asList(args), returnKind, stamp); - if (!result && mustIntrinsify) { - reportIntrinsificationFailure(b, method, args); - } - return result; - - } else if (nodeIntrinsification.isFoldable(method)) { - ResolvedJavaType[] parameterTypes = resolveJavaTypes(method.toParameterTypes(), method.getDeclaringClass()); - JavaConstant constant = nodeIntrinsification.tryFold(Arrays.asList(args), parameterTypes, method); - if (!COULD_NOT_FOLD.equals(constant)) { - if (constant != null) { - // Replace the invoke with the result of the call - b.push(method.getSignature().getReturnKind(), ConstantNode.forConstant(constant, b.getMetaAccess(), b.getGraph())); - } else { - // This must be a void invoke - assert method.getSignature().getReturnKind() == JavaKind.Void; - } - return true; - } else if (mustIntrinsify) { - reportIntrinsificationFailure(b, method, args); - } - - } else if (MethodsElidedInSnippets != null) { + if (MethodsElidedInSnippets != null) { if (MethodFilter.matches(MethodsElidedInSnippets, method)) { if (method.getSignature().getReturnKind() != JavaKind.Void) { throw new JVMCIError("Cannot elide non-void method " + method.format("%H.%n(%p)")); @@ -138,79 +65,4 @@ } return false; } - - private static boolean reportIntrinsificationFailure(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args) { - StringBuilder msg = new StringBuilder(); - msg.append("Call in ").append(b.getMethod().format("%H.%n(%p)")); - msg.append(" to ").append(method.format("%H.%n(%p)")); - msg.append(" cannot be intrinsified or folded, probably because an argument is not a constant. Arguments: "); - String sep = ""; - for (ValueNode node : args) { - msg.append(sep).append(node.toString()); - sep = ", "; - } - throw new JVMCIError(msg.toString()); - } - - private InputType getInputType(ResolvedJavaType type) { - if (type != null && structuralInputType.isAssignableFrom(type)) { - MarkerType markerType = type.getAnnotation(MarkerType.class); - if (markerType != null) { - return markerType.value(); - } else { - throw JVMCIError.shouldNotReachHere(String.format("%s extends StructuralInput, but is not annotated with @MarkerType", type)); - } - } else { - return InputType.Value; - } - } - - private boolean processNodeIntrinsic(GraphBuilderContext b, ResolvedJavaMethod method, NodeIntrinsic intrinsic, List<ValueNode> args, JavaKind returnKind, Stamp stamp) { - ValueNode res = createNodeIntrinsic(b, method, intrinsic, args, stamp); - if (res == null) { - return false; - } - if (res instanceof UnsafeCopyNode) { - UnsafeCopyNode copy = (UnsafeCopyNode) res; - UnsafeLoadNode value = b.add(new UnsafeLoadNode(copy.sourceObject(), copy.sourceOffset(), copy.accessKind(), copy.getLocationIdentity())); - b.add(new UnsafeStoreNode(copy.destinationObject(), copy.destinationOffset(), value, copy.accessKind(), copy.getLocationIdentity())); - return true; - } else if (res instanceof ForeignCallNode) { - /* - * Need to update the BCI of a ForeignCallNode so that it gets the stateDuring in the - * case that the foreign call can deoptimize. As with all deoptimization, we need a - * state in a non-intrinsic method. - */ - GraphBuilderContext nonIntrinsicAncestor = b.getNonIntrinsicAncestor(); - if (nonIntrinsicAncestor != null) { - ForeignCallNode foreign = (ForeignCallNode) res; - foreign.setBci(nonIntrinsicAncestor.bci()); - } - } - - boolean nonValueType = false; - if (returnKind == JavaKind.Object && stamp instanceof ObjectStamp) { - ResolvedJavaType type = ((ObjectStamp) stamp).type(); - if (type != null && structuralInputType.isAssignableFrom(type)) { - assert res.isAllowedUsageType(getInputType(type)); - nonValueType = true; - } - } - - if (returnKind != JavaKind.Void) { - assert nonValueType || res.getStackKind() != JavaKind.Void; - res = b.addPush(returnKind, res); - } else { - assert res.getStackKind() == JavaKind.Void; - res = b.add(res); - } - - return true; - } - - private ValueNode createNodeIntrinsic(GraphBuilderContext b, ResolvedJavaMethod method, NodeIntrinsic intrinsic, List<ValueNode> args, Stamp stamp) { - ValueNode res = nodeIntrinsification.createIntrinsicNode(args, stamp, method, b.getGraph(), intrinsic); - assert res != null : String.format("Could not create node intrinsic for call to %s as one of the arguments expected to be constant isn't: arguments=%s", method.format("%H.%n(%p)"), args); - return res; - } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/NodeIntrinsificationProvider.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2011, 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.graal.replacements; + +import jdk.vm.ci.common.JVMCIError; +import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.MetaAccessProvider; +import jdk.vm.ci.meta.ResolvedJavaType; + +import com.oracle.graal.api.replacements.SnippetReflectionProvider; +import com.oracle.graal.compiler.common.spi.ForeignCallsProvider; +import com.oracle.graal.compiler.common.type.Stamp; +import com.oracle.graal.compiler.common.type.StampFactory; +import com.oracle.graal.nodes.graphbuilderconf.NodeIntrinsicPluginFactory.InjectionProvider; +import com.oracle.graal.word.WordTypes; + +public class NodeIntrinsificationProvider implements InjectionProvider { + + private final MetaAccessProvider metaAccess; + private final SnippetReflectionProvider snippetReflection; + private final ForeignCallsProvider foreignCalls; + private final WordTypes wordTypes; + + public NodeIntrinsificationProvider(MetaAccessProvider metaAccess, SnippetReflectionProvider snippetReflection, ForeignCallsProvider foreignCalls, WordTypes wordTypes) { + this.metaAccess = metaAccess; + this.snippetReflection = snippetReflection; + this.foreignCalls = foreignCalls; + this.wordTypes = wordTypes; + } + + @Override + public Stamp getReturnStamp(Class<?> type) { + JavaKind kind = JavaKind.fromJavaClass(type); + if (kind == JavaKind.Object) { + ResolvedJavaType returnType = metaAccess.lookupJavaType(type); + if (wordTypes.isWord(returnType)) { + return wordTypes.getWordStamp(returnType); + } else { + return StampFactory.declared(returnType); + } + } else { + return StampFactory.forKind(kind); + } + } + + @Override + public <T> T getInjectedArgument(Class<T> type) { + T injected = snippetReflection.getInjectedNodeIntrinsicParameter(type); + if (injected != null) { + return injected; + } else if (type.equals(ForeignCallsProvider.class)) { + return type.cast(foreignCalls); + } else if (type.equals(SnippetReflectionProvider.class)) { + return type.cast(snippetReflection); + } else { + throw new JVMCIError("Cannot handle injected argument of type %s.", type.getName()); + } + } +}
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/PEGraphDecoder.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/PEGraphDecoder.java Tue Dec 08 12:30:15 2015 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -53,15 +53,6 @@ import com.oracle.graal.graph.Node; import com.oracle.graal.graph.NodeClass; import com.oracle.graal.graph.spi.Canonicalizable; -import com.oracle.graal.graphbuilderconf.GraphBuilderContext; -import com.oracle.graal.graphbuilderconf.InlineInvokePlugin; -import com.oracle.graal.graphbuilderconf.InlineInvokePlugin.InlineInfo; -import com.oracle.graal.graphbuilderconf.IntrinsicContext; -import com.oracle.graal.graphbuilderconf.InvocationPlugin; -import com.oracle.graal.graphbuilderconf.InvocationPlugins; -import com.oracle.graal.graphbuilderconf.InvocationPlugins.InvocationPluginReceiver; -import com.oracle.graal.graphbuilderconf.LoopExplosionPlugin; -import com.oracle.graal.graphbuilderconf.ParameterPlugin; import com.oracle.graal.java.GraphBuilderPhase; import com.oracle.graal.nodeinfo.NodeInfo; import com.oracle.graal.nodes.AbstractBeginNode; @@ -87,6 +78,15 @@ import com.oracle.graal.nodes.ValueNode; import com.oracle.graal.nodes.extended.ForeignCallNode; import com.oracle.graal.nodes.extended.IntegerSwitchNode; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderContext; +import com.oracle.graal.nodes.graphbuilderconf.InlineInvokePlugin; +import com.oracle.graal.nodes.graphbuilderconf.IntrinsicContext; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugin; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugins; +import com.oracle.graal.nodes.graphbuilderconf.LoopExplosionPlugin; +import com.oracle.graal.nodes.graphbuilderconf.ParameterPlugin; +import com.oracle.graal.nodes.graphbuilderconf.InlineInvokePlugin.InlineInfo; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugins.InvocationPluginReceiver; import com.oracle.graal.nodes.java.MethodCallTargetNode; import com.oracle.graal.nodes.java.MonitorIdNode; import com.oracle.graal.nodes.spi.StampProvider;
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java Tue Dec 08 12:30:15 2015 -0800 @@ -24,9 +24,9 @@ import static com.oracle.graal.compiler.common.GraalOptions.DeoptALot; import static com.oracle.graal.compiler.common.GraalOptions.OptCanonicalizer; -import static com.oracle.graal.graphbuilderconf.IntrinsicContext.CompilationContext.INLINE_AFTER_PARSING; import static com.oracle.graal.java.BytecodeParserOptions.InlineDuringParsing; import static com.oracle.graal.java.BytecodeParserOptions.InlineIntrinsicsDuringParsing; +import static com.oracle.graal.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.INLINE_AFTER_PARSING; import static com.oracle.graal.phases.common.DeadCodeEliminationPhase.Optionality.Required; import static java.lang.String.format; import static jdk.vm.ci.meta.MetaUtil.toInternalName; @@ -73,13 +73,6 @@ import com.oracle.graal.debug.DebugTimer; import com.oracle.graal.graph.Node; import com.oracle.graal.graph.Node.NodeIntrinsic; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins; -import com.oracle.graal.graphbuilderconf.GraphBuilderContext; -import com.oracle.graal.graphbuilderconf.InlineInvokePlugin; -import com.oracle.graal.graphbuilderconf.IntrinsicContext; -import com.oracle.graal.graphbuilderconf.InvocationPlugin; -import com.oracle.graal.graphbuilderconf.MethodSubstitutionPlugin; import com.oracle.graal.java.GraphBuilderPhase; import com.oracle.graal.java.GraphBuilderPhase.Instance; import com.oracle.graal.nodes.CallTargetNode; @@ -88,6 +81,14 @@ import com.oracle.graal.nodes.StructuredGraph; import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions; import com.oracle.graal.nodes.ValueNode; +import com.oracle.graal.nodes.graphbuilderconf.GeneratedInvocationPlugin; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderContext; +import com.oracle.graal.nodes.graphbuilderconf.InlineInvokePlugin; +import com.oracle.graal.nodes.graphbuilderconf.IntrinsicContext; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugin; +import com.oracle.graal.nodes.graphbuilderconf.MethodSubstitutionPlugin; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; import com.oracle.graal.nodes.java.MethodCallTargetNode; import com.oracle.graal.nodes.spi.Replacements; import com.oracle.graal.nodes.spi.StampProvider; @@ -120,8 +121,12 @@ return graphBuilderPlugins; } + protected boolean hasGeneratedInvocationPluginAnnotation(ResolvedJavaMethod method) { + return method.getAnnotation(Node.NodeIntrinsic.class) != null || method.getAnnotation(Fold.class) != null; + } + protected boolean hasGenericInvocationPluginAnnotation(ResolvedJavaMethod method) { - return method.getAnnotation(Node.NodeIntrinsic.class) != null || method.getAnnotation(Word.Operation.class) != null || method.getAnnotation(Fold.class) != null; + return method.getAnnotation(Word.Operation.class) != null; } private static final int MAX_GRAPH_INLINING_DEPTH = 100; // more than enough @@ -144,7 +149,8 @@ return null; } if (b.parsingIntrinsic()) { - assert !hasGenericInvocationPluginAnnotation(method) : format("%s should have been handled by %s", method.format("%H.%n(%p)"), NodeIntrinsificationPlugin.class.getName()); + assert !hasGeneratedInvocationPluginAnnotation(method) : format("%s should have been handled by a %s", method.format("%H.%n(%p)"), GeneratedInvocationPlugin.class.getSimpleName()); + assert !hasGenericInvocationPluginAnnotation(method) : format("%s should have been handled by %s", method.format("%H.%n(%p)"), WordOperationPlugin.class.getSimpleName()); assert b.getDepth() < MAX_GRAPH_INLINING_DEPTH : "inlining limit exceeded";
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/SnippetCounterNode.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/SnippetCounterNode.java Tue Dec 08 12:30:15 2015 -0800 @@ -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 @@ -127,7 +127,7 @@ static class SnippetCounterSnippets implements Snippets { @Fold - private static int countOffset() { + static int countOffset() { try { return (int) UNSAFE.objectFieldOffset(SnippetCounter.class.getDeclaredField("value")); } catch (Exception e) {
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/StandardGraphBuilderPlugins.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/StandardGraphBuilderPlugins.java Tue Dec 08 12:30:15 2015 -0800 @@ -41,12 +41,6 @@ import com.oracle.graal.graph.Edges; import com.oracle.graal.graph.Node; import com.oracle.graal.graph.NodeList; -import com.oracle.graal.graphbuilderconf.GraphBuilderContext; -import com.oracle.graal.graphbuilderconf.InvocationPlugin; -import com.oracle.graal.graphbuilderconf.InvocationPlugin.Receiver; -import com.oracle.graal.graphbuilderconf.InvocationPlugins; -import com.oracle.graal.graphbuilderconf.InvocationPlugins.Registration; -import com.oracle.graal.graphbuilderconf.MethodSubstitutionPlugin; import com.oracle.graal.nodes.ConstantNode; import com.oracle.graal.nodes.DeoptimizeNode; import com.oracle.graal.nodes.FixedGuardNode; @@ -78,6 +72,12 @@ import com.oracle.graal.nodes.extended.UnboxNode; import com.oracle.graal.nodes.extended.UnsafeLoadNode; import com.oracle.graal.nodes.extended.UnsafeStoreNode; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderContext; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugin; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugins; +import com.oracle.graal.nodes.graphbuilderconf.MethodSubstitutionPlugin; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugin.Receiver; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugins.Registration; import com.oracle.graal.nodes.java.ClassIsAssignableFromNode; import com.oracle.graal.nodes.java.CompareAndSwapNode; import com.oracle.graal.nodes.java.DynamicNewArrayNode; @@ -488,10 +488,9 @@ for (Class<?> c : new Class<?>[]{Node.class, NodeList.class}) { r.register2("get" + c.getSimpleName() + "Unsafe", Node.class, long.class, new InvocationPlugin() { public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode node, ValueNode offset) { - ValueNode value = b.add(new UnsafeLoadNode(node, offset, JavaKind.Object, LocationIdentity.any())); - boolean exactType = false; - boolean nonNull = false; - b.addPush(JavaKind.Object, new PiNode(value, metaAccess.lookupJavaType(c), exactType, nonNull)); + UnsafeLoadNode value = b.add(new UnsafeLoadNode(node, offset, JavaKind.Object, LocationIdentity.any())); + value.setStamp(StampFactory.declared(metaAccess.lookupJavaType(c))); + b.addPush(JavaKind.Object, value); return true; } });
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/StringSubstitutions.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/StringSubstitutions.java Tue Dec 08 12:30:15 2015 -0800 @@ -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 @@ -23,7 +23,7 @@ package com.oracle.graal.replacements; import com.oracle.graal.compiler.common.SuppressFBWarnings; -import com.oracle.graal.graphbuilderconf.InvocationPlugin; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugin; import com.oracle.graal.nodes.java.LoadFieldNode; import com.oracle.graal.replacements.nodes.ArrayEqualsNode;
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/WordOperationPlugin.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/WordOperationPlugin.java Tue Dec 08 12:30:15 2015 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -39,10 +39,6 @@ import com.oracle.graal.api.replacements.SnippetReflectionProvider; import com.oracle.graal.compiler.common.calc.Condition; import com.oracle.graal.compiler.common.type.Stamp; -import com.oracle.graal.graphbuilderconf.GraphBuilderContext; -import com.oracle.graal.graphbuilderconf.InlineInvokePlugin; -import com.oracle.graal.graphbuilderconf.NodePlugin; -import com.oracle.graal.graphbuilderconf.ParameterPlugin; import com.oracle.graal.nodes.ConstantNode; import com.oracle.graal.nodes.Invoke; import com.oracle.graal.nodes.ParameterNode; @@ -59,6 +55,10 @@ import com.oracle.graal.nodes.calc.ZeroExtendNode; import com.oracle.graal.nodes.extended.JavaReadNode; import com.oracle.graal.nodes.extended.JavaWriteNode; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderContext; +import com.oracle.graal.nodes.graphbuilderconf.InlineInvokePlugin; +import com.oracle.graal.nodes.graphbuilderconf.NodePlugin; +import com.oracle.graal.nodes.graphbuilderconf.ParameterPlugin; import com.oracle.graal.nodes.java.LoadFieldNode; import com.oracle.graal.nodes.java.LoadIndexedNode; import com.oracle.graal.nodes.java.StoreIndexedNode;
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/DeferredPiNode.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/DeferredPiNode.java Tue Dec 08 12:30:15 2015 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -36,12 +36,11 @@ import com.oracle.graal.nodes.PiNode; import com.oracle.graal.nodes.ValueNode; import com.oracle.graal.nodes.calc.FloatingNode; -import com.oracle.graal.replacements.NodeIntrinsificationPhase; /** * A node for use in method substitutions or snippets that changes the type of its input where the - * type is not immediately available at {@link NodeIntrinsificationPhase intrinsification} time. It - * is replaced by a {@link PiNode} once the type becomes constant (which <b>must</b> happen). + * type is not immediately available at intrinsification time. It is replaced by a {@link PiNode} + * once the type becomes constant (which <b>must</b> happen). */ @NodeInfo public final class DeferredPiNode extends FloatingNode implements Canonicalizable {
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/ResolvedMethodHandleCallTargetNode.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/ResolvedMethodHandleCallTargetNode.java Tue Dec 08 12:30:15 2015 -0800 @@ -22,11 +22,6 @@ */ package com.oracle.graal.replacements.nodes; -import static sun.misc.Version.jdkMajorVersion; -import static sun.misc.Version.jdkMicroVersion; -import static sun.misc.Version.jdkMinorVersion; -import static sun.misc.Version.jdkUpdateVersion; - import java.lang.invoke.MethodHandle; import jdk.vm.ci.common.JVMCIError; @@ -65,10 +60,6 @@ */ public static MethodCallTargetNode create(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] arguments, JavaType returnType, ResolvedJavaMethod originalTargetMethod, ValueNode[] originalArguments, JavaType originalReturnType) { - if (jdkMajorVersion() >= 1 && jdkMinorVersion() >= 8 && jdkMicroVersion() >= 0 && jdkUpdateVersion() >= 60) { - // https://bugs.openjdk.java.net/browse/JDK-8072008 is targeted for 8u60 - return new MethodCallTargetNode(invokeKind, targetMethod, arguments, returnType, null); - } return new ResolvedMethodHandleCallTargetNode(invokeKind, targetMethod, arguments, returnType, originalTargetMethod, originalArguments, originalReturnType); }
--- a/graal/com.oracle.graal.truffle.hotspot/src/com/oracle/graal/truffle/hotspot/HotSpotTruffleRuntime.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.truffle.hotspot/src/com/oracle/graal/truffle/hotspot/HotSpotTruffleRuntime.java Tue Dec 08 12:30:15 2015 -0800 @@ -64,9 +64,6 @@ import com.oracle.graal.debug.DebugEnvironment; import com.oracle.graal.debug.GraalDebugConfig; import com.oracle.graal.debug.TTY; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins; -import com.oracle.graal.graphbuilderconf.InvocationPlugins; import com.oracle.graal.hotspot.HotSpotBackend; import com.oracle.graal.hotspot.meta.HotSpotProviders; import com.oracle.graal.java.GraphBuilderPhase; @@ -74,6 +71,9 @@ import com.oracle.graal.lir.phases.LIRSuites; import com.oracle.graal.nodes.StructuredGraph; import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugins; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; import com.oracle.graal.phases.BasePhase; import com.oracle.graal.phases.OptimisticOptimizations; import com.oracle.graal.phases.PhaseSuite; @@ -160,11 +160,20 @@ @Override public TruffleCompiler getTruffleCompiler() { if (truffleCompiler == null) { - truffleCompiler = DefaultTruffleCompiler.create(this); + initializeTruffleCompiler(); } return truffleCompiler; } + private void initializeTruffleCompiler() { + synchronized (this) { + // might occur for multiple compiler threads at the same time. + if (truffleCompiler == null) { + truffleCompiler = DefaultTruffleCompiler.create(this); + } + } + } + @Override public RootCallTarget createCallTarget(RootNode rootNode) { return createCallTargetImpl(null, rootNode); @@ -280,14 +289,6 @@ } @Override - public void compile(OptimizedCallTarget optimizedCallTarget, boolean mayBeAsynchronous) { - /* Ensure compiler is created. */ - getTruffleCompiler(); - - super.compile(optimizedCallTarget, mayBeAsynchronous); - } - - @Override public boolean cancelInstalledTask(OptimizedCallTarget optimizedCallTarget, Object source, CharSequence reason) { if (lazy == null) { // if truffle wasn't initialized yet, this is a noop
--- a/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/ConditionAnchoringTest.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/ConditionAnchoringTest.java Tue Dec 08 12:30:15 2015 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -36,10 +36,6 @@ import com.oracle.graal.compiler.test.GraalCompilerTest; import com.oracle.graal.graph.iterators.NodeIterable; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration; -import com.oracle.graal.graphbuilderconf.GraphBuilderContext; -import com.oracle.graal.graphbuilderconf.InlineInvokePlugin; -import com.oracle.graal.graphbuilderconf.InvocationPlugins.Registration; import com.oracle.graal.nodes.BeginNode; import com.oracle.graal.nodes.ConditionAnchorNode; import com.oracle.graal.nodes.IfNode; @@ -47,6 +43,10 @@ import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions; import com.oracle.graal.nodes.ValueNode; import com.oracle.graal.nodes.extended.UnsafeLoadNode; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderContext; +import com.oracle.graal.nodes.graphbuilderconf.InlineInvokePlugin; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugins.Registration; import com.oracle.graal.nodes.memory.FloatingReadNode; import com.oracle.graal.nodes.memory.ReadNode; import com.oracle.graal.nodes.spi.LoweringTool.StandardLoweringStage;
--- a/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/ExactMathTest.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/ExactMathTest.java Tue Dec 08 12:30:15 2015 -0800 @@ -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 @@ -25,7 +25,7 @@ import org.junit.Test; import com.oracle.graal.compiler.test.GraalCompilerTest; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration; import com.oracle.graal.truffle.substitutions.TruffleGraphBuilderPlugins; import com.oracle.truffle.api.ExactMath;
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/DefaultTruffleCompiler.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/DefaultTruffleCompiler.java Tue Dec 08 12:30:15 2015 -0800 @@ -26,9 +26,9 @@ import com.oracle.graal.api.replacements.SnippetReflectionProvider; import com.oracle.graal.compiler.target.Backend; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins; import com.oracle.graal.java.GraphBuilderPhase; import com.oracle.graal.lir.phases.LIRSuites; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; import com.oracle.graal.phases.BasePhase; import com.oracle.graal.phases.PhaseSuite; import com.oracle.graal.phases.tiers.HighTierContext;
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/FrameWithoutBoxing.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/FrameWithoutBoxing.java Tue Dec 08 12:30:15 2015 -0800 @@ -281,7 +281,7 @@ checkSlotIndex(slotIndex); boolean condition = getTags()[slotIndex] == tag; if (!condition) { - CompilerDirectives.transferToInterpreter(); + CompilerDirectives.transferToInterpreterAndInvalidate(); throw new FrameSlotTypeException(); } return condition; @@ -303,7 +303,7 @@ public Object getValue(FrameSlot slot) { int slotIndex = slot.getIndex(); if (CompilerDirectives.inInterpreter() && slotIndex >= getTags().length) { - CompilerDirectives.transferToInterpreter(); + CompilerDirectives.transferToInterpreterAndInvalidate(); resize(); } byte tag = getTags()[slotIndex]; @@ -357,7 +357,7 @@ return cachedTags[slotIndex]; } - CompilerDirectives.transferToInterpreter(); + CompilerDirectives.transferToInterpreterAndInvalidate(); resize(); return getTags()[slotIndex]; }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/GraalTruffleRuntime.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/GraalTruffleRuntime.java Tue Dec 08 12:30:15 2015 -0800 @@ -359,7 +359,7 @@ private void doCompile0(OptimizedCallTarget optimizedCallTarget) { boolean success = true; try (Scope s = Debug.scope("Truffle", new TruffleDebugJavaMethod(optimizedCallTarget))) { - truffleCompiler.compileMethod(optimizedCallTarget); + getTruffleCompiler().compileMethod(optimizedCallTarget); } catch (Throwable e) { optimizedCallTarget.notifyCompilationFailed(e); success = false; @@ -371,14 +371,13 @@ protected abstract BackgroundCompileQueue getCompileQueue(); public void compile(OptimizedCallTarget optimizedCallTarget, boolean mayBeAsynchronous) { - Runnable r = new Runnable() { + BackgroundCompileQueue l = getCompileQueue(); + Future<?> future = l.compileQueue.submit(new Runnable() { @Override public void run() { doCompile(optimizedCallTarget); } - }; - BackgroundCompileQueue l = getCompileQueue(); - Future<?> future = l.compileQueue.submit(r); + }); l.compilations.put(optimizedCallTarget, future); getCompilationNotify().notifyCompilationQueued(optimizedCallTarget);
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java Tue Dec 08 12:30:15 2015 -0800 @@ -194,6 +194,7 @@ public Object call(Object... args) { compilationProfile.reportIndirectCall(); if (profiledArgumentTypesAssumption != null && profiledArgumentTypesAssumption.isValid()) { + // Argument profiling is not possible for targets of indirect calls. CompilerDirectives.transferToInterpreterAndInvalidate(); profiledArgumentTypesAssumption.invalidate(); profiledArgumentTypes = null; @@ -232,20 +233,26 @@ @ExplodeLoop void profileArguments(Object[] args) { - if (profiledArgumentTypesAssumption == null) { + Assumption typesAssumption = profiledArgumentTypesAssumption; + if (typesAssumption == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); initializeProfiledArgumentTypes(args); - } else if (profiledArgumentTypes != null) { - if (profiledArgumentTypes.length != args.length) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - profiledArgumentTypesAssumption.invalidate(); - profiledArgumentTypes = null; - } else if (TruffleArgumentTypeSpeculation.getValue() && profiledArgumentTypesAssumption.isValid()) { - for (int i = 0; i < profiledArgumentTypes.length; i++) { - if (profiledArgumentTypes[i] != null && !profiledArgumentTypes[i].isInstance(args[i])) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - updateProfiledArgumentTypes(args); - break; + } else { + Class<?>[] types = profiledArgumentTypes; + if (types != null) { + if (types.length != args.length) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + typesAssumption.invalidate(); + profiledArgumentTypes = null; + } else if (typesAssumption.isValid()) { + for (int i = 0; i < types.length; i++) { + Class<?> type = types[i]; + Object value = args[i]; + if (type != null && (value == null || value.getClass() != type)) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + updateProfiledArgumentTypes(args, types); + break; + } } } } @@ -255,19 +262,21 @@ private void initializeProfiledArgumentTypes(Object[] args) { CompilerAsserts.neverPartOfCompilation(); profiledArgumentTypesAssumption = Truffle.getRuntime().createAssumption("Profiled Argument Types"); - profiledArgumentTypes = new Class<?>[args.length]; if (TruffleArgumentTypeSpeculation.getValue()) { + Class<?>[] result = new Class<?>[args.length]; for (int i = 0; i < args.length; i++) { - profiledArgumentTypes[i] = classOf(args[i]); + result[i] = classOf(args[i]); } + + profiledArgumentTypes = result; } } - private void updateProfiledArgumentTypes(Object[] args) { + private void updateProfiledArgumentTypes(Object[] args, Class<?>[] types) { CompilerAsserts.neverPartOfCompilation(); profiledArgumentTypesAssumption.invalidate(); - for (int j = 0; j < profiledArgumentTypes.length; j++) { - profiledArgumentTypes[j] = joinTypes(profiledArgumentTypes[j], classOf(args[j])); + for (int j = 0; j < types.length; j++) { + types[j] = joinTypes(types[j], classOf(args[j])); } profiledArgumentTypesAssumption = Truffle.getRuntime().createAssumption("Profiled Argument Types"); } @@ -279,14 +288,8 @@ private static Class<?> joinTypes(Class<?> class1, Class<?> class2) { if (class1 == class2) { return class1; - } else if (class1 == null || class2 == null) { + } else { return null; - } else if (class1.isAssignableFrom(class2)) { - return class1; - } else if (class2.isAssignableFrom(class1)) { - return class2; - } else { - return Object.class; } } @@ -308,9 +311,10 @@ public final Object callRoot(Object[] originalArguments) { Object[] args = originalArguments; - if (this.profiledArgumentTypesAssumption != null && CompilerDirectives.inCompiledCode() && profiledArgumentTypesAssumption.isValid()) { - args = unsafeCast(castArrayFixedLength(args, profiledArgumentTypes.length), Object[].class, true, true); - if (TruffleArgumentTypeSpeculation.getValue()) { + if (CompilerDirectives.inCompiledCode()) { + Assumption argumentTypesAssumption = this.profiledArgumentTypesAssumption; + if (argumentTypesAssumption != null && argumentTypesAssumption.isValid()) { + args = unsafeCast(castArrayFixedLength(args, profiledArgumentTypes.length), Object[].class, true, true); args = castArguments(args); } } @@ -324,7 +328,8 @@ } void profileReturnType(Object result) { - if (profiledReturnTypeAssumption == null) { + Assumption returnTypeAssumption = profiledReturnTypeAssumption; + if (returnTypeAssumption == null) { if (TruffleReturnTypeSpeculation.getValue()) { CompilerDirectives.transferToInterpreterAndInvalidate(); profiledReturnType = (result == null ? null : result.getClass()); @@ -334,7 +339,7 @@ if (result == null || profiledReturnType != result.getClass()) { CompilerDirectives.transferToInterpreterAndInvalidate(); profiledReturnType = null; - profiledReturnTypeAssumption.invalidate(); + returnTypeAssumption.invalidate(); } } } @@ -475,9 +480,10 @@ @ExplodeLoop private Object[] castArguments(Object[] originalArguments) { - Object[] castArguments = new Object[profiledArgumentTypes.length]; - for (int i = 0; i < profiledArgumentTypes.length; i++) { - castArguments[i] = profiledArgumentTypes[i] != null ? unsafeCast(originalArguments[i], profiledArgumentTypes[i], true, true) : originalArguments[i]; + Class<?>[] types = profiledArgumentTypes; + Object[] castArguments = new Object[types.length]; + for (int i = 0; i < types.length; i++) { + castArguments[i] = types[i] != null ? unsafeCast(originalArguments[i], types[i], true, true) : originalArguments[i]; } return castArguments; }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java Tue Dec 08 12:30:15 2015 -0800 @@ -50,13 +50,6 @@ import com.oracle.graal.debug.Debug; import com.oracle.graal.debug.Debug.Scope; import com.oracle.graal.debug.Indent; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins; -import com.oracle.graal.graphbuilderconf.GraphBuilderContext; -import com.oracle.graal.graphbuilderconf.InlineInvokePlugin; -import com.oracle.graal.graphbuilderconf.InvocationPlugins; -import com.oracle.graal.graphbuilderconf.LoopExplosionPlugin; -import com.oracle.graal.graphbuilderconf.ParameterPlugin; import com.oracle.graal.java.ComputeLoopFrequenciesClosure; import com.oracle.graal.java.GraphBuilderPhase; import com.oracle.graal.nodes.ConstantNode; @@ -64,6 +57,13 @@ import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions; import com.oracle.graal.nodes.ValueNode; import com.oracle.graal.nodes.calc.FloatingNode; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderContext; +import com.oracle.graal.nodes.graphbuilderconf.InlineInvokePlugin; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugins; +import com.oracle.graal.nodes.graphbuilderconf.LoopExplosionPlugin; +import com.oracle.graal.nodes.graphbuilderconf.ParameterPlugin; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; import com.oracle.graal.nodes.java.CheckCastNode; import com.oracle.graal.nodes.java.InstanceOfNode; import com.oracle.graal.nodes.java.MethodCallTargetNode; @@ -159,7 +159,7 @@ @SuppressWarnings("try") public StructuredGraph createGraph(final OptimizedCallTarget callTarget, AllowAssumptions allowAssumptions) { try (Scope c = Debug.scope("TruffleTree")) { - Debug.dump(callTarget, callTarget.toString()); + Debug.dump(callTarget, "%s", callTarget); } catch (Throwable e) { throw Debug.handle(e); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilationResultBuilderFactory.java Tue Dec 08 12:30:15 2015 -0800 @@ -0,0 +1,99 @@ +/* + * 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.graal.truffle; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import jdk.vm.ci.code.CodeCacheProvider; +import jdk.vm.ci.code.CompilationResult; +import jdk.vm.ci.meta.Assumptions.Assumption; + +import com.oracle.graal.asm.Assembler; +import com.oracle.graal.compiler.common.spi.ForeignCallsProvider; +import com.oracle.graal.lir.asm.CompilationResultBuilder; +import com.oracle.graal.lir.asm.CompilationResultBuilderFactory; +import com.oracle.graal.lir.asm.FrameContext; +import com.oracle.graal.lir.framemap.FrameMap; +import com.oracle.graal.nodes.StructuredGraph; +import com.oracle.graal.truffle.nodes.AssumptionValidAssumption; + +/** + * A mechanism for Truffle to update a {@link CompilationResult} before it is + * {@linkplain CompilationResult#close() closed} by the Graal compiler. + */ +class TruffleCompilationResultBuilderFactory implements CompilationResultBuilderFactory { + + /** + * The graph being compiled. + */ + private final StructuredGraph graph; + + /** + * List into which {@link AssumptionValidAssumption}s are added. + */ + private final List<AssumptionValidAssumption> validAssumptions; + + public TruffleCompilationResultBuilderFactory(StructuredGraph graph, List<AssumptionValidAssumption> validAssumptions) { + this.graph = graph; + this.validAssumptions = validAssumptions; + } + + public CompilationResultBuilder createBuilder(CodeCacheProvider codeCache, ForeignCallsProvider foreignCalls, FrameMap frameMap, Assembler asm, FrameContext frameContext, + CompilationResult compilationResult) { + return new CompilationResultBuilder(codeCache, foreignCalls, frameMap, asm, frameContext, compilationResult) { + @Override + protected void closeCompilationResult() { + CompilationResult result = this.compilationResult; + result.setMethods(graph.method(), graph.getInlinedMethods()); + result.setBytecodeSize(graph.getBytecodeSize()); + + Set<Assumption> newAssumptions = new HashSet<>(); + for (Assumption assumption : graph.getAssumptions()) { + TruffleCompilationResultBuilderFactory.processAssumption(newAssumptions, assumption, validAssumptions); + } + + if (result.getAssumptions() != null) { + for (Assumption assumption : result.getAssumptions()) { + TruffleCompilationResultBuilderFactory.processAssumption(newAssumptions, assumption, validAssumptions); + } + } + + result.setAssumptions(newAssumptions.toArray(new Assumption[newAssumptions.size()])); + super.closeCompilationResult(); + } + }; + } + + static void processAssumption(Set<Assumption> newAssumptions, Assumption assumption, List<AssumptionValidAssumption> manual) { + if (assumption != null) { + if (assumption instanceof AssumptionValidAssumption) { + AssumptionValidAssumption assumptionValidAssumption = (AssumptionValidAssumption) assumption; + manual.add(assumptionValidAssumption); + } else { + newAssumptions.add(assumption); + } + } + } +}
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompiler.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompiler.java Tue Dec 08 12:30:15 2015 -0800 @@ -27,16 +27,13 @@ import static jdk.vm.ci.code.CodeUtil.getCallingConvention; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; -import java.util.Set; import jdk.vm.ci.code.CallingConvention; import jdk.vm.ci.code.CallingConvention.Type; import jdk.vm.ci.code.CodeCacheProvider; import jdk.vm.ci.code.CompilationResult; import jdk.vm.ci.code.InstalledCode; -import jdk.vm.ci.meta.Assumptions.Assumption; import jdk.vm.ci.meta.ConstantReflectionProvider; import jdk.vm.ci.meta.MetaAccessProvider; import jdk.vm.ci.meta.ResolvedJavaType; @@ -50,12 +47,11 @@ import com.oracle.graal.debug.DebugEnvironment; import com.oracle.graal.debug.DebugMemUseTracker; import com.oracle.graal.debug.DebugTimer; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration; -import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins; -import com.oracle.graal.lir.asm.CompilationResultBuilderFactory; import com.oracle.graal.lir.phases.LIRSuites; import com.oracle.graal.nodes.StructuredGraph; import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; import com.oracle.graal.phases.OptimisticOptimizations; import com.oracle.graal.phases.PhaseSuite; import com.oracle.graal.phases.tiers.HighTierContext; @@ -178,6 +174,8 @@ } CompilationResult result = null; + List<AssumptionValidAssumption> validAssumptions = new ArrayList<>(); + TruffleCompilationResultBuilderFactory factory = new TruffleCompilationResultBuilderFactory(graph, validAssumptions); try (DebugCloseable a = CompilationTime.start(); Scope s = Debug.scope("TruffleGraal.GraalCompiler", graph, providers.getCodeCache()); DebugCloseable c = CompilationMemUse.start()) { SpeculationLog speculationLog = graph.getSpeculationLog(); if (speculationLog != null) { @@ -187,31 +185,13 @@ CodeCacheProvider codeCache = providers.getCodeCache(); CallingConvention cc = getCallingConvention(codeCache, Type.JavaCallee, graph.method(), false); CompilationResult compilationResult = new CompilationResult(name); - result = compileGraph(graph, cc, graph.method(), providers, backend, graphBuilderSuite, Optimizations, getProfilingInfo(graph), suites, lirSuites, compilationResult, - CompilationResultBuilderFactory.Default); + result = compileGraph(graph, cc, graph.method(), providers, backend, graphBuilderSuite, Optimizations, getProfilingInfo(graph), suites, lirSuites, compilationResult, factory); } catch (Throwable e) { throw Debug.handle(e); } compilationNotify.notifyCompilationGraalTierFinished((OptimizedCallTarget) predefinedInstalledCode, graph); - result.setMethods(graph.method(), graph.getInlinedMethods()); - result.setBytecodeSize(graph.getBytecodeSize()); - - List<AssumptionValidAssumption> validAssumptions = new ArrayList<>(); - Set<Assumption> newAssumptions = new HashSet<>(); - for (Assumption assumption : graph.getAssumptions()) { - processAssumption(newAssumptions, assumption, validAssumptions); - } - - if (result.getAssumptions() != null) { - for (Assumption assumption : result.getAssumptions()) { - processAssumption(newAssumptions, assumption, validAssumptions); - } - } - - result.setAssumptions(newAssumptions.toArray(new Assumption[newAssumptions.size()])); - InstalledCode installedCode; try (Scope s = Debug.scope("CodeInstall", providers.getCodeCache()); DebugCloseable a = CodeInstallationTime.start(); DebugCloseable c = CodeInstallationMemUse.start()) { installedCode = providers.getCodeCache().addCode(graph.method(), result, graph.getSpeculationLog(), predefinedInstalledCode); @@ -228,17 +208,6 @@ protected abstract PhaseSuite<HighTierContext> createGraphBuilderSuite(); - public void processAssumption(Set<Assumption> newAssumptions, Assumption assumption, List<AssumptionValidAssumption> manual) { - if (assumption != null) { - if (assumption instanceof AssumptionValidAssumption) { - AssumptionValidAssumption assumptionValidAssumption = (AssumptionValidAssumption) assumption; - manual.add(assumptionValidAssumption); - } else { - newAssumptions.add(assumption); - } - } - } - public PartialEvaluator getPartialEvaluator() { return partialEvaluator; }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/debug/HistogramInlineInvokePlugin.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/debug/HistogramInlineInvokePlugin.java Tue Dec 08 12:30:15 2015 -0800 @@ -31,11 +31,11 @@ import jdk.vm.ci.meta.ResolvedJavaMethod; import com.oracle.graal.graph.Node; -import com.oracle.graal.graphbuilderconf.InlineInvokePlugin; import com.oracle.graal.nodes.BeginNode; import com.oracle.graal.nodes.DeoptimizeNode; import com.oracle.graal.nodes.StructuredGraph; import com.oracle.graal.nodes.VirtualState; +import com.oracle.graal.nodes.graphbuilderconf.InlineInvokePlugin; import com.oracle.graal.nodes.java.MethodCallTargetNode; import com.oracle.graal.nodes.virtual.VirtualObjectNode; import com.oracle.graal.truffle.OptimizedCallTarget;
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/substitutions/TruffleGraphBuilderPlugins.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/substitutions/TruffleGraphBuilderPlugins.java Tue Dec 08 12:30:15 2015 -0800 @@ -43,11 +43,6 @@ import com.oracle.graal.compiler.common.type.StampFactory; import com.oracle.graal.debug.Debug; import com.oracle.graal.graph.Node; -import com.oracle.graal.graphbuilderconf.GraphBuilderContext; -import com.oracle.graal.graphbuilderconf.InvocationPlugin; -import com.oracle.graal.graphbuilderconf.InvocationPlugin.Receiver; -import com.oracle.graal.graphbuilderconf.InvocationPlugins; -import com.oracle.graal.graphbuilderconf.InvocationPlugins.Registration; import com.oracle.graal.nodes.CallTargetNode; import com.oracle.graal.nodes.CallTargetNode.InvokeKind; import com.oracle.graal.nodes.ConditionAnchorNode; @@ -65,6 +60,11 @@ import com.oracle.graal.nodes.extended.BranchProbabilityNode; import com.oracle.graal.nodes.extended.UnsafeLoadNode; import com.oracle.graal.nodes.extended.UnsafeStoreNode; +import com.oracle.graal.nodes.graphbuilderconf.GraphBuilderContext; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugin; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugins; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugin.Receiver; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugins.Registration; import com.oracle.graal.nodes.java.MethodCallTargetNode; import com.oracle.graal.nodes.virtual.EnsureVirtualizedNode; import com.oracle.graal.replacements.nodes.arithmetic.IntegerAddExactNode; @@ -228,7 +228,7 @@ * and constant folding could still eliminate the call to bailout(). However, we * also want to stop parsing, since we are sure that we will never need the * graph beyond the bailout point. - * + * * Therefore, we manually emit the call to bailout, which will be intrinsified * later when intrinsifications can no longer be delayed. The call is followed * by a NeverPartOfCompilationNode, which is a control sink and therefore stops @@ -398,17 +398,24 @@ } else { piStamp = StampFactory.declaredTrusted(javaType, nonNull.asJavaConstant().asInt() != 0); } - LogicNode compareNode = CompareNode.createCompareNode(object.graph(), Condition.EQ, condition, ConstantNode.forBoolean(true, object.graph()), constantReflection); - boolean skipAnchor = false; - if (compareNode instanceof LogicConstantNode) { - LogicConstantNode logicConstantNode = (LogicConstantNode) compareNode; - if (logicConstantNode.getValue()) { - skipAnchor = true; + + ConditionAnchorNode valueAnchorNode = null; + if (condition.isConstant() && condition.asJavaConstant().asInt() == 1) { + // Nothing to do. + } else { + boolean skipAnchor = false; + LogicNode compareNode = CompareNode.createCompareNode(object.graph(), Condition.EQ, condition, ConstantNode.forBoolean(true, object.graph()), constantReflection); + + if (compareNode instanceof LogicConstantNode) { + LogicConstantNode logicConstantNode = (LogicConstantNode) compareNode; + if (logicConstantNode.getValue()) { + skipAnchor = true; + } } - } - ConditionAnchorNode valueAnchorNode = null; - if (!skipAnchor) { - valueAnchorNode = b.add(new ConditionAnchorNode(compareNode)); + + if (!skipAnchor) { + valueAnchorNode = b.add(new ConditionAnchorNode(compareNode)); + } } b.addPush(JavaKind.Object, new PiNode(object, piStamp, valueAnchorNode)); }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/substitutions/TruffleInvocationPluginProvider.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/substitutions/TruffleInvocationPluginProvider.java Tue Dec 08 12:30:15 2015 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -25,7 +25,7 @@ import jdk.vm.ci.meta.MetaAccessProvider; import com.oracle.graal.api.replacements.SnippetReflectionProvider; -import com.oracle.graal.graphbuilderconf.InvocationPlugins; +import com.oracle.graal.nodes.graphbuilderconf.InvocationPlugins; public interface TruffleInvocationPluginProvider { void registerInvocationPlugins(MetaAccessProvider metaAccess, InvocationPlugins plugins, boolean canDelayIntrinsification, SnippetReflectionProvider snippetReflection);
--- a/graal/com.oracle.graal.virtual.bench/src/com/oracle/graal/virtual/bench/PartialEscapeBench.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.virtual.bench/src/com/oracle/graal/virtual/bench/PartialEscapeBench.java Tue Dec 08 12:30:15 2015 -0800 @@ -27,7 +27,9 @@ import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Warmup; -public class PartialEscapeBench { +import com.oracle.graal.microbenchmarks.graal.GraalBenchmark; + +public class PartialEscapeBench extends GraalBenchmark { private static class Thing { final int id;
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/EffectsPhase.java Tue Dec 08 12:29:24 2015 -0800 +++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/EffectsPhase.java Tue Dec 08 12:30:15 2015 -0800 @@ -122,6 +122,27 @@ } protected void postIteration(final StructuredGraph graph, final PhaseContextT context, Set<Node> changedNodes) { + /* + * The nodes were added without uniqueing them first so GVN the leaf nodes now. The normal + * CanonicalizerPhase will GVN non-leaf value numberable nodes. + */ + for (Node node : changedNodes) { + if (node.isAlive() && node.getNodeClass().valueNumberable() && node.getNodeClass().isLeafNode()) { + Node duplicate = graph.findDuplicate(node); + if (duplicate != node) { + if (duplicate == null) { + /* + * There's no version of this constant in the leaf node cache so create a + * copy to add it to the table. + */ + duplicate = node.copyWithInputs(); + assert duplicate == graph.findDuplicate(duplicate); + } + node.replaceAtUsages(duplicate); + node.safeDelete(); + } + } + } if (canonicalizer != null) { canonicalizer.applyIncremental(graph, context, changedNodes); }
--- a/mx.graal/mx_graal_8.py Tue Dec 08 12:29:24 2015 -0800 +++ b/mx.graal/mx_graal_8.py Tue Dec 08 12:30:15 2015 -0800 @@ -113,12 +113,10 @@ vmArgs = ['-XX:-UseJVMCIClassLoader'] + vmArgs # look for -f in JMH arguments - containsF = False forking = True for i in range(len(jmhArgs)): arg = jmhArgs[i] if arg.startswith('-f'): - containsF = True if arg == '-f' and (i+1) < len(jmhArgs): arg += jmhArgs[i+1] try: @@ -133,10 +131,6 @@ if not forking: args += vmArgs else: - # default to -f1 if not specified otherwise - if not containsF: - jmhArgs += ['-f1'] - # find all projects with a direct JMH dependency jmhProjects = [] for p in mx.projects_opt_limit_to_suites(): @@ -237,6 +231,15 @@ out = None run_vm(self.args + _noneAsEmptyList(extraVMarguments) + ['-XX:-TieredCompilation', '-XX:+BootstrapJVMCI', '-version'], out=out) +class MicrobenchRun: + def __init__(self, name, args): + self.name = name + self.args = args + + def run(self, tasks, extraVMarguments=None): + with Task(self.name + ': hosted-product ', tasks) as t: + if t: microbench(_noneAsEmptyList(extraVMarguments) + ['--'] + self.args) + def compiler_gate_runner(suites, unit_test_runs, bootstrap_tests, tasks, extraVMarguments=None): # Build server-hosted-jvmci now so we can run the unit tests @@ -248,6 +251,11 @@ for r in unit_test_runs: r.run(suites, tasks, extraVMarguments) + # Run microbench on server-hosted-jvmci (only for testing the JMH setup) + with VM('server', 'product'): + for r in [MicrobenchRun('Microbench', ['TestJMH'])]: + r.run(tasks, extraVMarguments) + # Run ctw against rt.jar on server-hosted-jvmci with VM('server', 'product'): with Task('CTW:hosted-product', tasks) as t: @@ -289,8 +297,8 @@ graal_bootstrap_tests = [ BootstrapTest('BootstrapWithSystemAssertions', 'fastdebug', ['-esa']), BootstrapTest('BootstrapWithSystemAssertionsNoCoop', 'fastdebug', ['-esa', '-XX:-UseCompressedOops', '-G:+ExitVMOnException']), - BootstrapTest('BootstrapWithGCVecification', 'product', ['-XX:+UnlockDiagnosticVMOptions', '-XX:+VerifyBeforeGC', '-XX:+VerifyAfterGC', '-G:+ExitVMOnException'], suppress=['VerifyAfterGC:', 'VerifyBeforeGC:']), - BootstrapTest('BootstrapWithG1GCVecification', 'product', ['-XX:+UnlockDiagnosticVMOptions', '-XX:-UseSerialGC', '-XX:+UseG1GC', '-XX:+VerifyBeforeGC', '-XX:+VerifyAfterGC', '-G:+ExitVMOnException'], suppress=['VerifyAfterGC:', 'VerifyBeforeGC:']), + BootstrapTest('BootstrapWithGCVerification', 'product', ['-XX:+UnlockDiagnosticVMOptions', '-XX:+VerifyBeforeGC', '-XX:+VerifyAfterGC', '-G:+ExitVMOnException'], suppress=['VerifyAfterGC:', 'VerifyBeforeGC:']), + BootstrapTest('BootstrapWithG1GCVerification', 'product', ['-XX:+UnlockDiagnosticVMOptions', '-XX:-UseSerialGC', '-XX:+UseG1GC', '-XX:+VerifyBeforeGC', '-XX:+VerifyAfterGC', '-G:+ExitVMOnException'], suppress=['VerifyAfterGC:', 'VerifyBeforeGC:']), BootstrapTest('BootstrapEconomyWithSystemAssertions', 'fastdebug', ['-esa', '-Djvmci.compiler=graal-economy', '-G:+ExitVMOnException']), BootstrapTest('BootstrapWithExceptionEdges', 'fastdebug', ['-esa', '-G:+StressInvokeWithExceptionNode', '-G:+ExitVMOnException']), BootstrapTest('BootstrapWithRegisterPressure', 'product', ['-esa', '-G:RegisterPressure=' + _registers, '-G:+ExitVMOnException']),
--- a/mx.graal/mx_graal_9.py Tue Dec 08 12:29:24 2015 -0800 +++ b/mx.graal/mx_graal_9.py Tue Dec 08 12:30:15 2015 -0800 @@ -127,12 +127,10 @@ vmArgs, jmhArgs = mx.extract_VM_args(args, useDoubleDash=True) # look for -f in JMH arguments - containsF = False forking = True for i in range(len(jmhArgs)): arg = jmhArgs[i] if arg.startswith('-f'): - containsF = True if arg == '-f' and (i+1) < len(jmhArgs): arg += jmhArgs[i+1] try: @@ -147,10 +145,6 @@ if not forking: args += vmArgs else: - # default to -f1 if not specified otherwise - if not containsF: - jmhArgs += ['-f1'] - # find all projects with a direct JMH dependency jmhProjects = [] for p in mx.projects_opt_limit_to_suites(): @@ -235,6 +229,15 @@ out = None run_vm(self.args + _noneAsEmptyList(extraVMarguments) + ['-XX:-TieredCompilation', '-XX:+BootstrapJVMCI', '-version'], out=out) +class MicrobenchRun: + def __init__(self, name, args): + self.name = name + self.args = args + + def run(self, tasks, extraVMarguments=None): + with Task(self.name + ': hosted-product ', tasks) as t: + if t: microbench(_noneAsEmptyList(extraVMarguments) + ['--'] + self.args) + def compiler_gate_runner(suites, unit_test_runs, bootstrap_tests, tasks, extraVMarguments=None): # Run unit tests in hosted mode @@ -242,6 +245,11 @@ for r in unit_test_runs: r.run(suites, tasks, extraVMarguments) + # Run microbench in hosted mode (only for testing the JMH setup) + with JVMCIMode('hosted'): + for r in [MicrobenchRun('Microbench', ['TestJMH'])]: + r.run(tasks, extraVMarguments) + # Run ctw against rt.jar on server-hosted-jvmci with JVMCIMode('hosted'): with Task('CTW:hosted', tasks) as t: @@ -278,8 +286,8 @@ graal_bootstrap_tests = [ BootstrapTest('BootstrapWithSystemAssertions', 'fastdebug', ['-esa']), BootstrapTest('BootstrapWithSystemAssertionsNoCoop', 'fastdebug', ['-esa', '-XX:-UseCompressedOops', '-G:+ExitVMOnException']), - BootstrapTest('BootstrapWithGCVecification', 'product', ['-XX:+UnlockDiagnosticVMOptions', '-XX:+VerifyBeforeGC', '-XX:+VerifyAfterGC', '-G:+ExitVMOnException'], suppress=['VerifyAfterGC:', 'VerifyBeforeGC:']), - BootstrapTest('BootstrapWithG1GCVecification', 'product', ['-XX:+UnlockDiagnosticVMOptions', '-XX:-UseSerialGC', '-XX:+UseG1GC', '-XX:+VerifyBeforeGC', '-XX:+VerifyAfterGC', '-G:+ExitVMOnException'], suppress=['VerifyAfterGC:', 'VerifyBeforeGC:']), + BootstrapTest('BootstrapWithGCVerification', 'product', ['-XX:+UnlockDiagnosticVMOptions', '-XX:+VerifyBeforeGC', '-XX:+VerifyAfterGC', '-G:+ExitVMOnException'], suppress=['VerifyAfterGC:', 'VerifyBeforeGC:']), + BootstrapTest('BootstrapWithG1GCVerification', 'product', ['-XX:+UnlockDiagnosticVMOptions', '-XX:-UseSerialGC', '-XX:+UseG1GC', '-XX:+VerifyBeforeGC', '-XX:+VerifyAfterGC', '-G:+ExitVMOnException'], suppress=['VerifyAfterGC:', 'VerifyBeforeGC:']), BootstrapTest('BootstrapEconomyWithSystemAssertions', 'fastdebug', ['-esa', '-Djvmci.compiler=graal-economy', '-G:+ExitVMOnException']), BootstrapTest('BootstrapWithExceptionEdges', 'fastdebug', ['-esa', '-G:+StressInvokeWithExceptionNode', '-G:+ExitVMOnException']), BootstrapTest('BootstrapWithRegisterPressure', 'product', ['-esa', '-G:RegisterPressure=' + _registers, '-G:+ExitVMOnException']),
--- a/mx.graal/suite.py Tue Dec 08 12:29:24 2015 -0800 +++ b/mx.graal/suite.py Tue Dec 08 12:30:15 2015 -0800 @@ -39,7 +39,7 @@ { "name" : "jvmci", "optional" : "true", - "version" : "2dea101cdfe9aacf55083cf5bd6f84cb23106f4e", + "version" : "9ed36a1fec521a62bd9fdccec022c78cf2599244", "urls" : [ {"url" : "http://lafo.ssw.uni-linz.ac.at/hg/graal-jvmci-8", "kind" : "hg"}, {"url" : "https://curio.ssw.jku.at/nexus/content/repositories/snapshots", "kind" : "binary"}, @@ -47,7 +47,7 @@ }, { "name" : "truffle", - "version" : "fd53ccebb10b21af953de2da4340a7d17b85e5ed", + "version" : "bc1e026ef5b1ab8a1f68a3e78437e48f2d91fd91", "urls" : [ {"url" : "http://lafo.ssw.uni-linz.ac.at/hg/truffle", "kind" : "hg"}, {"url" : "https://curio.ssw.jku.at/nexus/content/repositories/snapshots", "kind" : "binary"}, @@ -85,18 +85,18 @@ }, "JMH" : { - "sha1" : "be2e08e6776191e9c559a65b7d34e92e86b4fa5c", - "urls" : ["https://lafo.ssw.uni-linz.ac.at/pub/jmh/jmh-runner-1.10.4.jar"], + "sha1" : "0fe92ac8718909c632345d4ecb4e596d1fa40071", + "urls" : ["https://lafo.ssw.uni-linz.ac.at/pub/jmh/jmh-runner-1.11.2.jar"], }, # Library that allows Graal to compile against JVMCI without the jvmci suite. # This library is not added to the boot class path at run time and so code # compiled against this library must be run on (JVMCI enabled) JDK9. "JVMCI" : { - "sha1" : "9482b9ba7760c09cd95d78f08ce28171b4081268", - "urls" : ["https://lafo.ssw.uni-linz.ac.at/pub/graal-external-deps/jvmci-1648ffb0158e.jar"], - "sourceSha1" : "b34c184cb1d383aec07bcadefadff01543f10222", - "sourceUrls" : ["https://lafo.ssw.uni-linz.ac.at/pub/graal-external-deps/jvmci-1648ffb0158e.src.zip"], + "sha1" : "31c6bb33db89e7863d716641518bb6997fe2d340", + "urls" : ["https://lafo.ssw.uni-linz.ac.at/pub/graal-external-deps/jvmci-7a570929c5e5.jar"], + "sourceSha1" : "8020f243ce1d5453c7a3b9f2bba52ad69ce689b4", + "sourceUrls" : ["https://lafo.ssw.uni-linz.ac.at/pub/graal-external-deps/jvmci-7a570929c5e5.src.zip"], "license": "GPLv2-CPE", }, }), @@ -518,6 +518,7 @@ "javaCompliance" : "1.8", "annotationProcessors" : [ "GRAAL_NODEINFO_PROCESSOR", + "GRAAL_REPLACEMENTS_VERIFIER", ], "workingSets" : "Graal,Replacements,AMD64", }, @@ -541,7 +542,10 @@ "com.oracle.graal.compiler.test", "com.oracle.graal.replacements", ], - "annotationProcessors" : ["GRAAL_NODEINFO_PROCESSOR"], + "annotationProcessors" : [ + "GRAAL_NODEINFO_PROCESSOR", + "GRAAL_REPLACEMENTS_VERIFIER" + ], "checkstyle" : "com.oracle.graal.graph", "javaCompliance" : "1.8", "workingSets" : "Graal,Replacements,Test", @@ -639,7 +643,7 @@ "com.oracle.graal.virtual.bench" : { "subDir" : "graal", "sourceDirs" : ["src"], - "dependencies" : ["JMH"], + "dependencies" : ["JMH", "com.oracle.graal.microbenchmarks"], "checkstyle" : "com.oracle.graal.graph", "javaCompliance" : "1.8", "annotationProcessors" : ["JMH"], @@ -784,7 +788,6 @@ "sourceDirs" : ["src"], "dependencies" : [ "com.oracle.graal.phases", - "com.oracle.graal.graphbuilderconf", ], "annotationProcessors" : deps(["jvmci:JVMCI_OPTIONS_PROCESSOR"]), "checkstyle" : "com.oracle.graal.graph", @@ -792,17 +795,6 @@ "workingSets" : "Graal,Java", }, - "com.oracle.graal.graphbuilderconf" : { - "subDir" : "graal", - "sourceDirs" : ["src"], - "dependencies" : [ - "com.oracle.graal.nodes", - ], - "checkstyle" : "com.oracle.graal.graph", - "javaCompliance" : "1.8", - "workingSets" : "Graal,Java", - }, - "com.oracle.graal.compiler.common" : { "subDir" : "graal", "sourceDirs" : ["src"], @@ -1123,9 +1115,10 @@ "GRAAL_REPLACEMENTS_VERIFIER" : { "subDir" : "graal", "dependencies" : ["com.oracle.graal.replacements.verifier"], - "distDependencies" : [ + "distDependencies" : deps([ "GRAAL_API", - ], + "jvmci:JVMCI_SERVICE_PROCESSOR", + ]) }, "GRAAL_COMPILER_MATCH_PROCESSOR" : {