diff --git a/make/CompileToolsHotspot.gmk b/make/CompileToolsHotspot.gmk index 87d8bd5b3fc..1ccc3e2a31b 100644 --- a/make/CompileToolsHotspot.gmk +++ b/make/CompileToolsHotspot.gmk @@ -67,6 +67,7 @@ ifeq ($(INCLUDE_GRAAL), true) $(SRC_DIR)/org.graalvm.compiler.phases.common/src \ $(SRC_DIR)/org.graalvm.compiler.serviceprovider/src \ $(SRC_DIR)/org.graalvm.compiler.virtual/src \ + $(SRC_DIR)/org.graalvm.graphio/src \ $(SRC_DIR)/org.graalvm.util/src \ $(VM_CI_SRC_DIR)/jdk.vm.ci.code/src \ $(VM_CI_SRC_DIR)/jdk.vm.ci.common/src \ @@ -125,6 +126,7 @@ ifeq ($(INCLUDE_GRAAL), true) $(SRC_DIR)/org.graalvm.compiler.nodeinfo/src \ $(SRC_DIR)/org.graalvm.compiler.options/src \ $(SRC_DIR)/org.graalvm.compiler.serviceprovider/src \ + $(SRC_DIR)/org.graalvm.graphio/src \ $(SRC_DIR)/org.graalvm.util/src \ $(VM_CI_SRC_DIR)/jdk.vm.ci.code/src \ $(VM_CI_SRC_DIR)/jdk.vm.ci.common/src \ diff --git a/src/hotspot/share/aot/aotCodeHeap.cpp b/src/hotspot/share/aot/aotCodeHeap.cpp index 15c1e696ca4..2a9b3982cd2 100644 --- a/src/hotspot/share/aot/aotCodeHeap.cpp +++ b/src/hotspot/share/aot/aotCodeHeap.cpp @@ -490,6 +490,8 @@ void AOTCodeHeap::link_stub_routines_symbols() { SET_AOT_GLOBAL_SYMBOL_VALUE("_aot_stub_routines_checkcast_arraycopy", address, StubRoutines::_checkcast_arraycopy); + SET_AOT_GLOBAL_SYMBOL_VALUE("_aot_stub_routines_generic_arraycopy", address, StubRoutines::_generic_arraycopy); + SET_AOT_GLOBAL_SYMBOL_VALUE("_aot_stub_routines_aescrypt_encryptBlock", address, StubRoutines::_aescrypt_encryptBlock); SET_AOT_GLOBAL_SYMBOL_VALUE("_aot_stub_routines_aescrypt_decryptBlock", address, StubRoutines::_aescrypt_decryptBlock); SET_AOT_GLOBAL_SYMBOL_VALUE("_aot_stub_routines_cipherBlockChaining_encryptAESCrypt", address, StubRoutines::_cipherBlockChaining_encryptAESCrypt); diff --git a/src/jdk.aot/share/classes/jdk.tools.jaotc.binformat/src/jdk/tools/jaotc/binformat/BinaryContainer.java b/src/jdk.aot/share/classes/jdk.tools.jaotc.binformat/src/jdk/tools/jaotc/binformat/BinaryContainer.java index 9e3aa91f4cf..0971479577e 100644 --- a/src/jdk.aot/share/classes/jdk.tools.jaotc.binformat/src/jdk/tools/jaotc/binformat/BinaryContainer.java +++ b/src/jdk.aot/share/classes/jdk.tools.jaotc.binformat/src/jdk/tools/jaotc/binformat/BinaryContainer.java @@ -192,6 +192,8 @@ public final class BinaryContainer implements SymbolTable { {"StubRoutines::_checkcast_arraycopy", "_aot_stub_routines_checkcast_arraycopy"}, + {"StubRoutines::_generic_arraycopy", "_aot_stub_routines_generic_arraycopy"}, + {"StubRoutines::_aescrypt_encryptBlock", "_aot_stub_routines_aescrypt_encryptBlock"}, {"StubRoutines::_aescrypt_decryptBlock", "_aot_stub_routines_aescrypt_decryptBlock"}, {"StubRoutines::_cipherBlockChaining_encryptAESCrypt", "_aot_stub_routines_cipherBlockChaining_encryptAESCrypt"}, diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.api.directives.test/src/org/graalvm/compiler/api/directives/test/BlackholeDirectiveTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.api.directives.test/src/org/graalvm/compiler/api/directives/test/BlackholeDirectiveTest.java index e6ed6ecd205..ead9c2544ec 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.api.directives.test/src/org/graalvm/compiler/api/directives/test/BlackholeDirectiveTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.api.directives.test/src/org/graalvm/compiler/api/directives/test/BlackholeDirectiveTest.java @@ -34,6 +34,9 @@ import org.graalvm.compiler.api.directives.GraalDirectives; import org.graalvm.compiler.core.test.GraalCompilerTest; import org.graalvm.compiler.nodes.ParameterNode; import org.graalvm.compiler.nodes.StructuredGraph; +import org.graalvm.compiler.phases.OptimisticOptimizations; +import org.graalvm.compiler.phases.OptimisticOptimizations.Optimization; +import org.graalvm.compiler.phases.tiers.HighTierContext; /** * Tests for {@link GraalDirectives#blackhole}. @@ -128,6 +131,11 @@ public class BlackholeDirectiveTest extends GraalCompilerTest { test("blackholeObjectSnippet", 37); } + @Override + protected HighTierContext getDefaultHighTierContext() { + return new HighTierContext(getProviders(), getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL.remove(Optimization.RemoveNeverExecutedCode)); + } + @Override protected boolean checkLowTierGraph(StructuredGraph graph) { BlackholeSnippet snippet = graph.method().getAnnotation(BlackholeSnippet.class); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.api.directives.test/src/org/graalvm/compiler/api/directives/test/ControlFlowAnchorDirectiveTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.api.directives.test/src/org/graalvm/compiler/api/directives/test/ControlFlowAnchorDirectiveTest.java index 1eaf803fc73..b2ff51fd76f 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.api.directives.test/src/org/graalvm/compiler/api/directives/test/ControlFlowAnchorDirectiveTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.api.directives.test/src/org/graalvm/compiler/api/directives/test/ControlFlowAnchorDirectiveTest.java @@ -46,6 +46,9 @@ import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions; import org.graalvm.compiler.nodes.debug.ControlFlowAnchorNode; import jdk.vm.ci.meta.ResolvedJavaMethod; +import org.graalvm.compiler.phases.OptimisticOptimizations; +import org.graalvm.compiler.phases.OptimisticOptimizations.Optimization; +import org.graalvm.compiler.phases.tiers.HighTierContext; public class ControlFlowAnchorDirectiveTest extends GraalCompilerTest { @@ -238,6 +241,11 @@ public class ControlFlowAnchorDirectiveTest extends GraalCompilerTest { return Collections.emptyList(); } + @Override + protected HighTierContext getDefaultHighTierContext() { + return new HighTierContext(getProviders(), getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL.remove(Optimization.RemoveNeverExecutedCode)); + } + @Override protected boolean checkLowTierGraph(StructuredGraph graph) { List anchors = graph.getNodes().filter(ControlFlowAnchorNode.class).snapshot(); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.api.directives.test/src/org/graalvm/compiler/api/directives/test/OpaqueDirectiveTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.api.directives.test/src/org/graalvm/compiler/api/directives/test/OpaqueDirectiveTest.java index 64e9cc42f98..6ec736902c9 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.api.directives.test/src/org/graalvm/compiler/api/directives/test/OpaqueDirectiveTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.api.directives.test/src/org/graalvm/compiler/api/directives/test/OpaqueDirectiveTest.java @@ -37,6 +37,9 @@ import org.graalvm.compiler.nodes.ReturnNode; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.calc.AddNode; import org.graalvm.compiler.nodes.calc.ConditionalNode; +import org.graalvm.compiler.phases.OptimisticOptimizations; +import org.graalvm.compiler.phases.OptimisticOptimizations.Optimization; +import org.graalvm.compiler.phases.tiers.HighTierContext; /** * Tests for {@link GraalDirectives#opaque}. @@ -127,6 +130,11 @@ public class OpaqueDirectiveTest extends GraalCompilerTest { test("opaqueObjectSnippet"); } + @Override + protected HighTierContext getDefaultHighTierContext() { + return new HighTierContext(getProviders(), getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL.remove(Optimization.RemoveNeverExecutedCode)); + } + @Override protected boolean checkLowTierGraph(StructuredGraph graph) { OpaqueSnippet snippet = graph.method().getAnnotation(OpaqueSnippet.class); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.api.replacements/src/org/graalvm/compiler/api/replacements/Snippet.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.api.replacements/src/org/graalvm/compiler/api/replacements/Snippet.java index 08cbae1ce6c..39d756eded2 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.api.replacements/src/org/graalvm/compiler/api/replacements/Snippet.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.api.replacements/src/org/graalvm/compiler/api/replacements/Snippet.java @@ -35,6 +35,13 @@ import java.lang.annotation.Target; @Target(ElementType.METHOD) public @interface Snippet { + /** + * A partial intrinsic exits by (effectively) calling the intrinsified method. Normally, this + * call must use exactly the same arguments as the call that is being intrinsified. For well + * known snippets that are used after frame state assignment, we want to relax this restriction. + */ + boolean allowPartialIntrinsicArgumentMismatch() default false; + /** * Denotes a snippet parameter representing 0 or more arguments that will be bound during * snippet template instantiation. During snippet template creation, its value must be an array diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.asm.amd64/src/org/graalvm/compiler/asm/amd64/AMD64Assembler.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.asm.amd64/src/org/graalvm/compiler/asm/amd64/AMD64Assembler.java index 56090fa7af3..04e0e22182d 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.asm.amd64/src/org/graalvm/compiler/asm/amd64/AMD64Assembler.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.asm.amd64/src/org/graalvm/compiler/asm/amd64/AMD64Assembler.java @@ -22,10 +22,14 @@ */ package org.graalvm.compiler.asm.amd64; -import static org.graalvm.compiler.core.common.NumUtil.isByte; -import static org.graalvm.compiler.core.common.NumUtil.isInt; -import static org.graalvm.compiler.core.common.NumUtil.isShiftCount; -import static org.graalvm.compiler.core.common.NumUtil.isUByte; +import static jdk.vm.ci.amd64.AMD64.CPU; +import static jdk.vm.ci.amd64.AMD64.XMM; +import static jdk.vm.ci.amd64.AMD64.r12; +import static jdk.vm.ci.amd64.AMD64.r13; +import static jdk.vm.ci.amd64.AMD64.rbp; +import static jdk.vm.ci.amd64.AMD64.rip; +import static jdk.vm.ci.amd64.AMD64.rsp; +import static jdk.vm.ci.code.MemoryBarriers.STORE_LOAD; import static org.graalvm.compiler.asm.amd64.AMD64AsmOptions.UseAddressNop; import static org.graalvm.compiler.asm.amd64.AMD64AsmOptions.UseNormalNop; import static org.graalvm.compiler.asm.amd64.AMD64Assembler.AMD64BinaryArithmetic.ADD; @@ -47,25 +51,24 @@ import static org.graalvm.compiler.asm.amd64.AMD64Assembler.OperandSize.QWORD; import static org.graalvm.compiler.asm.amd64.AMD64Assembler.OperandSize.SD; import static org.graalvm.compiler.asm.amd64.AMD64Assembler.OperandSize.SS; import static org.graalvm.compiler.asm.amd64.AMD64Assembler.OperandSize.WORD; -import static jdk.vm.ci.amd64.AMD64.CPU; -import static jdk.vm.ci.amd64.AMD64.XMM; -import static jdk.vm.ci.amd64.AMD64.r12; -import static jdk.vm.ci.amd64.AMD64.r13; -import static jdk.vm.ci.amd64.AMD64.rbp; -import static jdk.vm.ci.amd64.AMD64.rip; -import static jdk.vm.ci.amd64.AMD64.rsp; -import static jdk.vm.ci.code.MemoryBarriers.STORE_LOAD; +import static org.graalvm.compiler.core.common.NumUtil.isByte; +import static org.graalvm.compiler.core.common.NumUtil.isInt; +import static org.graalvm.compiler.core.common.NumUtil.isShiftCount; +import static org.graalvm.compiler.core.common.NumUtil.isUByte; import org.graalvm.compiler.asm.Assembler; import org.graalvm.compiler.asm.Label; -import org.graalvm.compiler.core.common.NumUtil; import org.graalvm.compiler.asm.amd64.AMD64Address.Scale; +import org.graalvm.compiler.core.common.NumUtil; +import org.graalvm.compiler.debug.GraalError; import jdk.vm.ci.amd64.AMD64; import jdk.vm.ci.amd64.AMD64.CPUFeature; +import jdk.vm.ci.amd64.AMD64Kind; import jdk.vm.ci.code.Register; import jdk.vm.ci.code.Register.RegisterCategory; import jdk.vm.ci.code.TargetDescription; +import jdk.vm.ci.meta.PlatformKind; /** * This class implements an assembler that can encode most X86 instructions. @@ -225,7 +228,7 @@ public class AMD64Assembler extends Assembler { * The x86 operand sizes. */ public enum OperandSize { - BYTE(1) { + BYTE(1, AMD64Kind.BYTE) { @Override protected void emitImmediate(AMD64Assembler asm, int imm) { assert imm == (byte) imm; @@ -238,7 +241,7 @@ public class AMD64Assembler extends Assembler { } }, - WORD(2, 0x66) { + WORD(2, AMD64Kind.WORD, 0x66) { @Override protected void emitImmediate(AMD64Assembler asm, int imm) { assert imm == (short) imm; @@ -251,7 +254,7 @@ public class AMD64Assembler extends Assembler { } }, - DWORD(4) { + DWORD(4, AMD64Kind.DWORD) { @Override protected void emitImmediate(AMD64Assembler asm, int imm) { asm.emitInt(imm); @@ -263,7 +266,7 @@ public class AMD64Assembler extends Assembler { } }, - QWORD(8) { + QWORD(8, AMD64Kind.QWORD) { @Override protected void emitImmediate(AMD64Assembler asm, int imm) { asm.emitInt(imm); @@ -275,34 +278,35 @@ public class AMD64Assembler extends Assembler { } }, - SS(4, 0xF3, true), + SS(4, AMD64Kind.SINGLE, 0xF3, true), - SD(8, 0xF2, true), + SD(8, AMD64Kind.DOUBLE, 0xF2, true), - PS(16, true), + PS(16, AMD64Kind.V128_SINGLE, true), - PD(16, 0x66, true); + PD(16, AMD64Kind.V128_DOUBLE, 0x66, true); private final int sizePrefix; - private final int bytes; private final boolean xmm; + private final AMD64Kind kind; - OperandSize(int bytes) { - this(bytes, 0); + OperandSize(int bytes, AMD64Kind kind) { + this(bytes, kind, 0); } - OperandSize(int bytes, int sizePrefix) { - this(bytes, sizePrefix, false); + OperandSize(int bytes, AMD64Kind kind, int sizePrefix) { + this(bytes, kind, sizePrefix, false); } - OperandSize(int bytes, boolean xmm) { - this(bytes, 0, xmm); + OperandSize(int bytes, AMD64Kind kind, boolean xmm) { + this(bytes, kind, 0, xmm); } - OperandSize(int bytes, int sizePrefix, boolean xmm) { + OperandSize(int bytes, AMD64Kind kind, int sizePrefix, boolean xmm) { this.sizePrefix = sizePrefix; this.bytes = bytes; + this.kind = kind; this.xmm = xmm; } @@ -314,6 +318,19 @@ public class AMD64Assembler extends Assembler { return xmm; } + public AMD64Kind getKind() { + return kind; + } + + public static OperandSize get(PlatformKind kind) { + for (OperandSize operandSize : OperandSize.values()) { + if (operandSize.kind.equals(kind)) { + return operandSize; + } + } + throw GraalError.shouldNotReachHere("Unexpected kind: " + kind.toString()); + } + /** * Emit an immediate of this size. Note that immediate {@link #QWORD} operands are encoded * as sign-extended 32-bit values. @@ -2230,6 +2247,14 @@ public class AMD64Assembler extends Assembler { emitOperandHelper(dst, src, 0); } + public final void movzbl(Register dst, Register src) { + AMD64RMOp.MOVZXB.emit(this, OperandSize.DWORD, dst, src); + } + + public final void movzbq(Register dst, Register src) { + AMD64RMOp.MOVZXB.emit(this, OperandSize.QWORD, dst, src); + } + public final void movzwl(Register dst, AMD64Address src) { prefix(src, dst); emitByte(0x0F); @@ -3198,6 +3223,13 @@ public class AMD64Assembler extends Assembler { emitByte(0xC0 | encode); } + public final void setb(ConditionFlag cc, Register dst) { + int encode = prefixAndEncode(dst.encoding, true); + emitByte(0x0F); + emitByte(0x90 | cc.getValue()); + emitByte(0xC0 | encode); + } + public final void cmovq(ConditionFlag cc, Register dst, AMD64Address src) { prefixq(src, dst); emitByte(0x0F); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.asm.amd64/src/org/graalvm/compiler/asm/amd64/AMD64MacroAssembler.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.asm.amd64/src/org/graalvm/compiler/asm/amd64/AMD64MacroAssembler.java index 36995bf6853..d91ddae485a 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.asm.amd64/src/org/graalvm/compiler/asm/amd64/AMD64MacroAssembler.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.asm.amd64/src/org/graalvm/compiler/asm/amd64/AMD64MacroAssembler.java @@ -31,8 +31,8 @@ import static org.graalvm.compiler.asm.amd64.AMD64AsmOptions.UseXmmLoadAndClearU import static org.graalvm.compiler.asm.amd64.AMD64AsmOptions.UseXmmRegToRegMoveAll; import org.graalvm.compiler.asm.Label; -import org.graalvm.compiler.core.common.NumUtil; import org.graalvm.compiler.asm.amd64.AMD64Address.Scale; +import org.graalvm.compiler.core.common.NumUtil; import jdk.vm.ci.amd64.AMD64; import jdk.vm.ci.amd64.AMD64Kind; @@ -281,6 +281,16 @@ public class AMD64MacroAssembler extends AMD64Assembler { } + public final void setl(ConditionFlag cc, Register dst) { + setb(cc, dst); + movzbl(dst, dst); + } + + public final void setq(ConditionFlag cc, Register dst) { + setb(cc, dst); + movzbq(dst, dst); + } + public final void flog(Register dest, Register value, boolean base10) { if (base10) { fldlg2(); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64.test/src/org/graalvm/compiler/core/amd64/test/AMD64AddressLoweringTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64.test/src/org/graalvm/compiler/core/amd64/test/AMD64AddressLoweringTest.java new file mode 100644 index 00000000000..99fc5192292 --- /dev/null +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64.test/src/org/graalvm/compiler/core/amd64/test/AMD64AddressLoweringTest.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2017, 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 org.graalvm.compiler.core.amd64.test; + +import static org.junit.Assume.assumeTrue; + +import org.graalvm.compiler.asm.amd64.AMD64Address.Scale; +import org.graalvm.compiler.core.amd64.AMD64AddressLowering; +import org.graalvm.compiler.core.amd64.AMD64AddressNode; +import org.graalvm.compiler.core.test.GraalCompilerTest; +import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.StructuredGraph; +import org.graalvm.compiler.nodes.ValueNode; +import org.graalvm.compiler.nodes.calc.AddNode; +import org.graalvm.compiler.nodes.calc.LeftShiftNode; +import org.graalvm.compiler.nodes.calc.NegateNode; +import org.graalvm.compiler.nodes.memory.address.AddressNode; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import jdk.vm.ci.amd64.AMD64; + +public class AMD64AddressLoweringTest extends GraalCompilerTest { + + private StructuredGraph graph; + private AMD64AddressLowering lowering; + + @Before + public void checkAMD64() { + assumeTrue("skipping AMD64 specific test", getTarget().arch instanceof AMD64); + graph = new StructuredGraph.Builder(getInitialOptions(), getDebugContext()).build(); + lowering = new AMD64AddressLowering(); + } + + @Test + public void convertBaseAndIndexToDisplacement() { + ValueNode base = graph.unique(const64(1000)); + ValueNode index = graph.unique(const64(10)); + AddressNode result = lowering.lower(base, index); + assertAddress(result, null, null, Scale.Times1, 1010); + } + + @Test + public void convertBaseToDisplacement() { + ValueNode constantAddress = graph.addOrUniqueWithInputs(const64(1000)); + AddressNode result = lowering.lower(constantAddress, null); + assertAddress(result, null, null, Scale.Times1, 1000); + } + + @Test + public void convertBaseAndShiftedIndexToDisplacement() { + ValueNode base = graph.addOrUniqueWithInputs(const64(1000)); + ValueNode index = graph.addOrUniqueWithInputs(new LeftShiftNode(const64(10), const32(1))); + AddressNode result = lowering.lower(base, index); + assertAddress(result, null, null, Scale.Times2, 1020); + } + + @Test + public void convertBaseAndNegatedShiftedIndexToDisplacement() { + ValueNode base = graph.addOrUniqueWithInputs(const64(1000)); + ValueNode index = graph.addOrUniqueWithInputs(new NegateNode(new LeftShiftNode(const64(10), const32(2)))); + AddressNode result = lowering.lower(base, index); + assertAddress(result, null, null, Scale.Times4, 960); + } + + @Test + public void convertNegatedBaseAndNegatedShiftedIndexToDisplacement() { + ValueNode base = graph.addOrUniqueWithInputs(new NegateNode(const64(1000))); + ValueNode index = graph.addOrUniqueWithInputs(new NegateNode(new LeftShiftNode(const64(10), const32(2)))); + AddressNode result = lowering.lower(base, index); + assertAddress(result, null, null, Scale.Times4, -1040); + } + + @Test + public void convertNegatedShiftedBaseAndNegatedIndexToDisplacement() { + ValueNode base = graph.addOrUniqueWithInputs(new NegateNode(new LeftShiftNode(const64(10), const32(2)))); + ValueNode index = graph.addOrUniqueWithInputs(new NegateNode(const64(1000))); + AddressNode result = lowering.lower(base, index); + assertAddress(result, null, null, Scale.Times4, -1040); + } + + @Test + public void convertTwoLevelsOfNegatedShiftedBaseAndNegatedIndexToDisplacement() { + ValueNode base = graph.addOrUniqueWithInputs(new NegateNode(new LeftShiftNode(new NegateNode(new LeftShiftNode(const64(500), const32(1))), const32(1)))); + ValueNode index = graph.addOrUniqueWithInputs(new NegateNode(new AddNode(new NegateNode(const64(13)), const64(3)))); + AddressNode result = lowering.lower(base, index); + assertAddress(result, null, null, Scale.Times4, 2010); + } + + private static ConstantNode const64(long value) { + return ConstantNode.forIntegerBits(Long.SIZE, value); + } + + private static ConstantNode const32(long value) { + return ConstantNode.forIntegerBits(Integer.SIZE, value); + } + + private static void assertAddress(AddressNode actual, ValueNode expectedBase, ValueNode expectedIndex, Scale expectedScale, int expectedDisplacement) { + AMD64AddressNode address = (AMD64AddressNode) actual; + Assert.assertEquals(expectedBase, address.getBase()); + Assert.assertEquals(expectedIndex, address.getIndex()); + Assert.assertEquals(expectedScale, address.getScale()); + Assert.assertEquals(expectedDisplacement, address.getDisplacement()); + } +} diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64AddressLowering.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64AddressLowering.java index 6a827f8c1fa..63e535bc853 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64AddressLowering.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64AddressLowering.java @@ -23,21 +23,22 @@ package org.graalvm.compiler.core.amd64; -import jdk.vm.ci.meta.JavaConstant; - -import org.graalvm.compiler.core.common.NumUtil; import org.graalvm.compiler.asm.amd64.AMD64Address.Scale; +import org.graalvm.compiler.core.common.NumUtil; import org.graalvm.compiler.core.common.type.IntegerStamp; import org.graalvm.compiler.debug.DebugContext; +import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.AddNode; import org.graalvm.compiler.nodes.calc.LeftShiftNode; +import org.graalvm.compiler.nodes.calc.NegateNode; import org.graalvm.compiler.nodes.calc.ZeroExtendNode; import org.graalvm.compiler.nodes.memory.address.AddressNode; import org.graalvm.compiler.phases.common.AddressLoweringPhase.AddressLowering; -public class AMD64AddressLowering extends AddressLowering { +import jdk.vm.ci.meta.JavaConstant; +public class AMD64AddressLowering extends AddressLowering { @Override public AddressNode lower(ValueNode address) { return lower(address, null); @@ -46,24 +47,37 @@ public class AMD64AddressLowering extends AddressLowering { @Override public AddressNode lower(ValueNode base, ValueNode offset) { AMD64AddressNode ret = new AMD64AddressNode(base, offset); + StructuredGraph graph = base.graph(); + boolean changed; do { - changed = improve(base.getDebug(), ret); + changed = improve(graph, base.getDebug(), ret, false, false); } while (changed); - return base.graph().unique(ret); + + return graph.unique(ret); } /** - * @param debug + * Tries to optimize addresses so that they match the AMD64-specific addressing mode better + * (base + index * scale + displacement). + * + * @param graph the current graph + * @param debug the current debug context + * @param ret the address that should be optimized + * @param isBaseNegated determines if the address base is negated. if so, all values that are + * extracted from the base will be negated as well + * @param isIndexNegated determines if the index is negated. if so, all values that are + * extracted from the index will be negated as well + * @return true if the address was modified */ - protected boolean improve(DebugContext debug, AMD64AddressNode ret) { - ValueNode newBase = improveInput(ret, ret.getBase(), 0); + protected boolean improve(StructuredGraph graph, DebugContext debug, AMD64AddressNode ret, boolean isBaseNegated, boolean isIndexNegated) { + ValueNode newBase = improveInput(ret, ret.getBase(), 0, isBaseNegated); if (newBase != ret.getBase()) { ret.setBase(newBase); return true; } - ValueNode newIdx = improveInput(ret, ret.getIndex(), ret.getScale().log2); + ValueNode newIdx = improveInput(ret, ret.getIndex(), ret.getScale().log2, isIndexNegated); if (newIdx != ret.getIndex()) { ret.setIndex(newIdx); return true; @@ -83,55 +97,122 @@ public class AMD64AddressLowering extends AddressLowering { } if (ret.getScale() == Scale.Times1) { - if (ret.getBase() == null || ret.getIndex() == null) { - if (ret.getBase() instanceof AddNode) { - AddNode add = (AddNode) ret.getBase(); - ret.setBase(add.getX()); - ret.setIndex(add.getY()); - return true; - } else if (ret.getIndex() instanceof AddNode) { - AddNode add = (AddNode) ret.getIndex(); - ret.setBase(add.getX()); - ret.setIndex(add.getY()); - return true; - } + if (ret.getIndex() == null && ret.getBase() instanceof AddNode) { + AddNode add = (AddNode) ret.getBase(); + ret.setBase(add.getX()); + ret.setIndex(considerNegation(graph, add.getY(), isBaseNegated)); + return true; + } else if (ret.getBase() == null && ret.getIndex() instanceof AddNode) { + AddNode add = (AddNode) ret.getIndex(); + ret.setBase(considerNegation(graph, add.getX(), isIndexNegated)); + ret.setIndex(add.getY()); + return true; } if (ret.getBase() instanceof LeftShiftNode && !(ret.getIndex() instanceof LeftShiftNode)) { ValueNode tmp = ret.getBase(); - ret.setBase(ret.getIndex()); - ret.setIndex(tmp); + ret.setBase(considerNegation(graph, ret.getIndex(), isIndexNegated != isBaseNegated)); + ret.setIndex(considerNegation(graph, tmp, isIndexNegated != isBaseNegated)); return true; } } + return improveNegation(graph, debug, ret, isBaseNegated, isIndexNegated); + } + + private boolean improveNegation(StructuredGraph graph, DebugContext debug, AMD64AddressNode ret, boolean originalBaseNegated, boolean originalIndexNegated) { + boolean baseNegated = originalBaseNegated; + boolean indexNegated = originalIndexNegated; + + ValueNode originalBase = ret.getBase(); + ValueNode originalIndex = ret.getIndex(); + + if (ret.getBase() instanceof NegateNode) { + NegateNode negate = (NegateNode) ret.getBase(); + ret.setBase(negate.getValue()); + baseNegated = !baseNegated; + } + + if (ret.getIndex() instanceof NegateNode) { + NegateNode negate = (NegateNode) ret.getIndex(); + ret.setIndex(negate.getValue()); + indexNegated = !indexNegated; + } + + if (baseNegated != originalBaseNegated || indexNegated != originalIndexNegated) { + ValueNode base = ret.getBase(); + ValueNode index = ret.getIndex(); + + boolean improved = improve(graph, debug, ret, baseNegated, indexNegated); + if (baseNegated != originalBaseNegated) { + if (base == ret.getBase()) { + ret.setBase(originalBase); + } else if (ret.getBase() != null) { + ret.setBase(graph.maybeAddOrUnique(NegateNode.create(ret.getBase()))); + } + } + + if (indexNegated != originalIndexNegated) { + if (index == ret.getIndex()) { + ret.setIndex(originalIndex); + } else if (ret.getIndex() != null) { + ret.setIndex(graph.maybeAddOrUnique(NegateNode.create(ret.getIndex()))); + } + } + return improved; + } else { + assert ret.getBase() == originalBase && ret.getIndex() == originalIndex; + } return false; } - private static ValueNode improveInput(AMD64AddressNode address, ValueNode node, int shift) { + private static ValueNode considerNegation(StructuredGraph graph, ValueNode value, boolean negate) { + if (negate && value != null) { + return graph.maybeAddOrUnique(NegateNode.create(value)); + } + return value; + } + + private ValueNode improveInput(AMD64AddressNode address, ValueNode node, int shift, boolean negateExtractedDisplacement) { if (node == null) { return null; } JavaConstant c = node.asJavaConstant(); if (c != null) { - return improveConstDisp(address, node, c, null, shift); + return improveConstDisp(address, node, c, null, shift, negateExtractedDisplacement); } else { - if (node.stamp() instanceof IntegerStamp && ((IntegerStamp) node.stamp()).getBits() == 64) { - if (node instanceof ZeroExtendNode) { - if (((ZeroExtendNode) node).getInputBits() == 32) { - /* - * We can just swallow a zero-extend from 32 bit to 64 bit because the upper - * half of the register will always be zero. - */ - return ((ZeroExtendNode) node).getValue(); + if (node.stamp() instanceof IntegerStamp) { + if (node instanceof ZeroExtendNode && (((ZeroExtendNode) node).getInputBits() == 32)) { + /* + * we can't just swallow all zero-extends as we might encounter something like + * the following: ZeroExtend(Add(negativeValue, positiveValue)). + * + * if we swallow the zero-extend in this case and subsequently optimize the add, + * we might end up with a negative value that has less than 64 bits in base or + * index. such a value would require sign extension instead of zero-extension + * but the backend can only do zero-extension. if we ever want to optimize that + * further, we would also need to be careful about over-/underflows. + * + * furthermore, we also can't swallow zero-extends with less than 32 bits as + * most of these values are immediately sign-extended to 32 bit by the backend + * (therefore, the subsequent implicit zero-extension to 64 bit won't do what we + * expect). + */ + ValueNode value = ((ZeroExtendNode) node).getValue(); + if (!mightBeOptimized(value)) { + // if the value is not optimized further by the address lowering, then we + // can safely rely on the backend doing the implicitly zero-extension. + return value; } - } else if (node instanceof AddNode) { + } + + if (node instanceof AddNode) { AddNode add = (AddNode) node; if (add.getX().isConstant()) { - return improveConstDisp(address, node, add.getX().asJavaConstant(), add.getY(), shift); + return improveConstDisp(address, node, add.getX().asJavaConstant(), add.getY(), shift, negateExtractedDisplacement); } else if (add.getY().isConstant()) { - return improveConstDisp(address, node, add.getY().asJavaConstant(), add.getX(), shift); + return improveConstDisp(address, node, add.getY().asJavaConstant(), add.getX(), shift, negateExtractedDisplacement); } } } @@ -140,15 +221,30 @@ public class AMD64AddressLowering extends AddressLowering { return node; } - private static ValueNode improveConstDisp(AMD64AddressNode address, ValueNode original, JavaConstant c, ValueNode other, int shift) { + /** + * This method returns true for all nodes that might be optimized by the address lowering. + */ + protected boolean mightBeOptimized(ValueNode value) { + return value instanceof AddNode || value instanceof LeftShiftNode || value instanceof NegateNode || value instanceof ZeroExtendNode; + } + + private static ValueNode improveConstDisp(AMD64AddressNode address, ValueNode original, JavaConstant c, ValueNode other, int shift, boolean negateExtractedDisplacement) { if (c.getJavaKind().isNumericInteger()) { - long disp = address.getDisplacement(); - disp += c.asLong() << shift; - if (NumUtil.isInt(disp)) { - address.setDisplacement((int) disp); + long delta = c.asLong() << shift; + if (updateDisplacement(address, delta, negateExtractedDisplacement)) { return other; } } return original; } + + protected static boolean updateDisplacement(AMD64AddressNode address, long displacementDelta, boolean negateDelta) { + long sign = negateDelta ? -1 : 1; + long disp = address.getDisplacement() + displacementDelta * sign; + if (NumUtil.isInt(disp)) { + address.setDisplacement((int) disp); + return true; + } + return false; + } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64LIRGenerator.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64LIRGenerator.java index e207f830464..6e7258ae62f 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64LIRGenerator.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64LIRGenerator.java @@ -33,15 +33,17 @@ import static org.graalvm.compiler.core.common.GraalOptions.GeneratePIC; import static org.graalvm.compiler.lir.LIRValueUtil.asConstantValue; import static org.graalvm.compiler.lir.LIRValueUtil.asJavaConstant; import static org.graalvm.compiler.lir.LIRValueUtil.isConstantValue; +import static org.graalvm.compiler.lir.LIRValueUtil.isIntConstant; import static org.graalvm.compiler.lir.LIRValueUtil.isJavaConstant; -import org.graalvm.compiler.core.common.NumUtil; +import org.graalvm.compiler.asm.amd64.AMD64Assembler.AMD64BinaryArithmetic; import org.graalvm.compiler.asm.amd64.AMD64Assembler.AMD64MIOp; import org.graalvm.compiler.asm.amd64.AMD64Assembler.AMD64RMOp; import org.graalvm.compiler.asm.amd64.AMD64Assembler.ConditionFlag; import org.graalvm.compiler.asm.amd64.AMD64Assembler.OperandSize; import org.graalvm.compiler.asm.amd64.AMD64Assembler.SSEOp; import org.graalvm.compiler.core.common.LIRKind; +import org.graalvm.compiler.core.common.NumUtil; import org.graalvm.compiler.core.common.calc.Condition; import org.graalvm.compiler.core.common.spi.ForeignCallLinkage; import org.graalvm.compiler.core.common.spi.LIRKindTool; @@ -58,13 +60,17 @@ import org.graalvm.compiler.lir.Variable; import org.graalvm.compiler.lir.amd64.AMD64AddressValue; import org.graalvm.compiler.lir.amd64.AMD64ArithmeticLIRGeneratorTool; import org.graalvm.compiler.lir.amd64.AMD64ArrayEqualsOp; +import org.graalvm.compiler.lir.amd64.AMD64Binary; import org.graalvm.compiler.lir.amd64.AMD64BinaryConsumer; import org.graalvm.compiler.lir.amd64.AMD64ByteSwapOp; import org.graalvm.compiler.lir.amd64.AMD64Call; +import org.graalvm.compiler.lir.amd64.AMD64ControlFlow; import org.graalvm.compiler.lir.amd64.AMD64ControlFlow.BranchOp; import org.graalvm.compiler.lir.amd64.AMD64ControlFlow.CondMoveOp; +import org.graalvm.compiler.lir.amd64.AMD64ControlFlow.CondSetOp; import org.graalvm.compiler.lir.amd64.AMD64ControlFlow.FloatBranchOp; import org.graalvm.compiler.lir.amd64.AMD64ControlFlow.FloatCondMoveOp; +import org.graalvm.compiler.lir.amd64.AMD64ControlFlow.FloatCondSetOp; import org.graalvm.compiler.lir.amd64.AMD64ControlFlow.ReturnOp; import org.graalvm.compiler.lir.amd64.AMD64ControlFlow.StrategySwitchOp; import org.graalvm.compiler.lir.amd64.AMD64ControlFlow.TableSwitchOp; @@ -257,8 +263,7 @@ public abstract class AMD64LIRGenerator extends LIRGenerator { @Override public void emitCompareBranch(PlatformKind cmpKind, Value left, Value right, Condition cond, boolean unorderedIsTrue, LabelRef trueLabel, LabelRef falseLabel, double trueLabelProbability) { - boolean mirrored = emitCompare(cmpKind, left, right); - Condition finalCondition = mirrored ? cond.mirror() : cond; + Condition finalCondition = emitCompare(cmpKind, left, right, cond); if (cmpKind == AMD64Kind.SINGLE || cmpKind == AMD64Kind.DOUBLE) { append(new FloatBranchOp(finalCondition, unorderedIsTrue, trueLabel, falseLabel, trueLabelProbability)); } else { @@ -290,14 +295,60 @@ public abstract class AMD64LIRGenerator extends LIRGenerator { @Override public Variable emitConditionalMove(PlatformKind cmpKind, Value left, Value right, Condition cond, boolean unorderedIsTrue, Value trueValue, Value falseValue) { - boolean mirrored = emitCompare(cmpKind, left, right); - Condition finalCondition = mirrored ? cond.mirror() : cond; + boolean isFloatComparison = cmpKind == AMD64Kind.SINGLE || cmpKind == AMD64Kind.DOUBLE; - Variable result = newVariable(trueValue.getValueKind()); - if (cmpKind == AMD64Kind.SINGLE || cmpKind == AMD64Kind.DOUBLE) { - append(new FloatCondMoveOp(result, finalCondition, unorderedIsTrue, load(trueValue), load(falseValue))); + Condition finalCondition = cond; + Value finalTrueValue = trueValue; + Value finalFalseValue = falseValue; + if (isFloatComparison) { + // eliminate the parity check in case of a float comparison + Value finalLeft = left; + Value finalRight = right; + if (unorderedIsTrue != AMD64ControlFlow.trueOnUnordered(finalCondition)) { + if (unorderedIsTrue == AMD64ControlFlow.trueOnUnordered(finalCondition.mirror())) { + finalCondition = finalCondition.mirror(); + finalLeft = right; + finalRight = left; + } else if (finalCondition != Condition.EQ && finalCondition != Condition.NE) { + // negating EQ and NE does not make any sense as we would need to negate + // unorderedIsTrue as well (otherwise, we would no longer fulfill the Java + // NaN semantics) + assert unorderedIsTrue == AMD64ControlFlow.trueOnUnordered(finalCondition.negate()); + finalCondition = finalCondition.negate(); + finalTrueValue = falseValue; + finalFalseValue = trueValue; + } + } + emitRawCompare(cmpKind, finalLeft, finalRight); } else { - append(new CondMoveOp(result, finalCondition, load(trueValue), loadNonConst(falseValue))); + finalCondition = emitCompare(cmpKind, left, right, cond); + } + + boolean isParityCheckNecessary = isFloatComparison && unorderedIsTrue != AMD64ControlFlow.trueOnUnordered(finalCondition); + Variable result = newVariable(finalTrueValue.getValueKind()); + if (!isParityCheckNecessary && isIntConstant(finalTrueValue, 1) && isIntConstant(finalFalseValue, 0)) { + if (isFloatComparison) { + append(new FloatCondSetOp(result, finalCondition)); + } else { + append(new CondSetOp(result, finalCondition)); + } + } else if (!isParityCheckNecessary && isIntConstant(finalTrueValue, 0) && isIntConstant(finalFalseValue, 1)) { + if (isFloatComparison) { + if (unorderedIsTrue == AMD64ControlFlow.trueOnUnordered(finalCondition.negate())) { + append(new FloatCondSetOp(result, finalCondition.negate())); + } else { + append(new FloatCondSetOp(result, finalCondition)); + Variable negatedResult = newVariable(result.getValueKind()); + append(new AMD64Binary.ConstOp(AMD64BinaryArithmetic.XOR, OperandSize.get(result.getPlatformKind()), negatedResult, result, 1)); + result = negatedResult; + } + } else { + append(new CondSetOp(result, finalCondition.negate())); + } + } else if (isFloatComparison) { + append(new FloatCondMoveOp(result, finalCondition, unorderedIsTrue, load(finalTrueValue), load(finalFalseValue))); + } else { + append(new CondMoveOp(result, finalCondition, load(finalTrueValue), loadNonConst(finalFalseValue))); } return result; } @@ -394,23 +445,21 @@ public abstract class AMD64LIRGenerator extends LIRGenerator { * * @param a the left operand of the comparison * @param b the right operand of the comparison + * @param cond the condition of the comparison * @return true if the left and right operands were switched, false otherwise */ - private boolean emitCompare(PlatformKind cmpKind, Value a, Value b) { - Variable left; - Value right; - boolean mirrored; + private Condition emitCompare(PlatformKind cmpKind, Value a, Value b, Condition cond) { if (LIRValueUtil.isVariable(b)) { - left = load(b); - right = loadNonConst(a); - mirrored = true; + emitRawCompare(cmpKind, b, a); + return cond.mirror(); } else { - left = load(a); - right = loadNonConst(b); - mirrored = false; + emitRawCompare(cmpKind, a, b); + return cond; } - ((AMD64ArithmeticLIRGeneratorTool) arithmeticLIRGen).emitCompareOp((AMD64Kind) cmpKind, left, right); - return mirrored; + } + + private void emitRawCompare(PlatformKind cmpKind, Value left, Value right) { + ((AMD64ArithmeticLIRGeneratorTool) arithmeticLIRGen).emitCompareOp((AMD64Kind) cmpKind, load(left), loadNonConst(right)); } @Override diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64LIRKindTool.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64LIRKindTool.java index a2ff74c5f66..da1d919b0ea 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64LIRKindTool.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64LIRKindTool.java @@ -28,7 +28,7 @@ import org.graalvm.compiler.debug.GraalError; import jdk.vm.ci.amd64.AMD64Kind; -public class AMD64LIRKindTool implements LIRKindTool { +public abstract class AMD64LIRKindTool implements LIRKindTool { @Override public LIRKind getIntegerKind(int bits) { @@ -67,12 +67,8 @@ public class AMD64LIRKindTool implements LIRKindTool { } @Override - public LIRKind getNarrowOopKind() { - return LIRKind.reference(AMD64Kind.DWORD); - } + public abstract LIRKind getNarrowOopKind(); @Override - public LIRKind getNarrowPointerKind() { - return LIRKind.value(AMD64Kind.DWORD); - } + public abstract LIRKind getNarrowPointerKind(); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/CompressEncoding.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/CompressEncoding.java index 4eac53452cb..5356c139188 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/CompressEncoding.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/CompressEncoding.java @@ -34,14 +34,6 @@ public final class CompressEncoding { this.shift = shift; } - public int compress(long ptr) { - if (ptr == 0L) { - return 0; - } else { - return (int) ((ptr - base) >>> shift); - } - } - public boolean hasBase() { return base != 0; } @@ -58,14 +50,6 @@ public final class CompressEncoding { return shift; } - public long uncompress(int ptr) { - if (ptr == 0) { - return 0L; - } else { - return ((ptr & 0xFFFFFFFFL) << shift) + base; - } - } - @Override public String toString() { return "base: " + base + " shift: " + shift; @@ -85,8 +69,7 @@ public final class CompressEncoding { if (obj instanceof CompressEncoding) { CompressEncoding other = (CompressEncoding) obj; return base == other.base && shift == other.shift; - } else { - return false; } + return false; } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/calc/FloatConvert.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/calc/FloatConvert.java index a7f430010dd..cc0316f4fa2 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/calc/FloatConvert.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/calc/FloatConvert.java @@ -25,21 +25,23 @@ package org.graalvm.compiler.core.common.calc; import org.graalvm.compiler.debug.GraalError; public enum FloatConvert { - F2I(FloatConvertCategory.FloatingPointToInteger), - D2I(FloatConvertCategory.FloatingPointToInteger), - F2L(FloatConvertCategory.FloatingPointToInteger), - D2L(FloatConvertCategory.FloatingPointToInteger), - I2F(FloatConvertCategory.IntegerToFloatingPoint), - L2F(FloatConvertCategory.IntegerToFloatingPoint), - D2F(FloatConvertCategory.FloatingPointToFloatingPoint), - I2D(FloatConvertCategory.IntegerToFloatingPoint), - L2D(FloatConvertCategory.IntegerToFloatingPoint), - F2D(FloatConvertCategory.FloatingPointToFloatingPoint); + F2I(FloatConvertCategory.FloatingPointToInteger, 32), + D2I(FloatConvertCategory.FloatingPointToInteger, 64), + F2L(FloatConvertCategory.FloatingPointToInteger, 32), + D2L(FloatConvertCategory.FloatingPointToInteger, 64), + I2F(FloatConvertCategory.IntegerToFloatingPoint, 32), + L2F(FloatConvertCategory.IntegerToFloatingPoint, 64), + D2F(FloatConvertCategory.FloatingPointToFloatingPoint, 64), + I2D(FloatConvertCategory.IntegerToFloatingPoint, 32), + L2D(FloatConvertCategory.IntegerToFloatingPoint, 64), + F2D(FloatConvertCategory.FloatingPointToFloatingPoint, 32); - private FloatConvertCategory category; + private final FloatConvertCategory category; + private final int inputBits; - FloatConvert(FloatConvertCategory category) { + FloatConvert(FloatConvertCategory category, int inputBits) { this.category = category; + this.inputBits = inputBits; } public FloatConvertCategory getCategory() { @@ -72,4 +74,8 @@ public enum FloatConvert { throw GraalError.shouldNotReachHere(); } } + + public int getInputBits() { + return inputBits; + } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/cfg/Loop.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/cfg/Loop.java index af3cb75d9b6..21eed6b98a0 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/cfg/Loop.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/cfg/Loop.java @@ -41,7 +41,6 @@ public abstract class Loop> { this.parent = parent; if (parent != null) { this.depth = parent.getDepth() + 1; - parent.getChildren().add(this); } else { this.depth = 1; } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/ArithmeticOpTable.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/ArithmeticOpTable.java index db33cf533d7..fb8c0b90029 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/ArithmeticOpTable.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/ArithmeticOpTable.java @@ -96,6 +96,22 @@ public final class ArithmeticOpTable { } } + public BinaryOp[] getBinaryOps() { + return new BinaryOp[]{add, sub, mul, mulHigh, umulHigh, div, rem, and, or, xor}; + } + + public UnaryOp[] getUnaryOps() { + return new UnaryOp[]{neg, not, abs, sqrt}; + } + + public ShiftOp[] getShiftOps() { + return new ShiftOp[]{shl, shr, ushr}; + } + + public IntegerConvertOp[] getIntegerConvertOps() { + return new IntegerConvertOp[]{zeroExtend, signExtend, narrow}; + } + public static final ArithmeticOpTable EMPTY = new ArithmeticOpTable(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null); public interface ArithmeticOpWrapper { @@ -562,7 +578,10 @@ public final class ArithmeticOpTable { } /** - * Apply the operation to two {@linkplain Constant Constants}. + * Applies this operation to {@code a} and {@code b}. + * + * @return the result of applying this operation or {@code null} if applying it would raise + * an exception (e.g., {@link ArithmeticException} for dividing by 0) */ public abstract Constant foldConstant(Constant a, Constant b); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/FloatStamp.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/FloatStamp.java index 2aa19dbd7a4..b9e057ef153 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/FloatStamp.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/FloatStamp.java @@ -291,7 +291,7 @@ public class FloatStamp extends PrimitiveStamp { @Override public JavaConstant asConstant() { - if (nonNaN && Double.compare(lowerBound, upperBound) == 0) { + if (isConstant()) { switch (getBits()) { case 32: return JavaConstant.forFloat((float) lowerBound); @@ -302,6 +302,68 @@ public class FloatStamp extends PrimitiveStamp { return null; } + private boolean isConstant() { + /* + * There are many forms of NaNs and any operations on them can silently convert them into + * the canonical NaN. + */ + return (Double.compare(lowerBound, upperBound) == 0 && nonNaN); + } + + private static FloatStamp stampForConstant(Constant constant) { + FloatStamp result; + PrimitiveConstant value = (PrimitiveConstant) constant; + switch (value.getJavaKind()) { + case Float: + if (Float.isNaN(value.asFloat())) { + result = new FloatStamp(32, Double.NaN, Double.NaN, false); + } else { + result = new FloatStamp(32, value.asFloat(), value.asFloat(), !Float.isNaN(value.asFloat())); + } + break; + case Double: + if (Double.isNaN(value.asDouble())) { + result = new FloatStamp(64, Double.NaN, Double.NaN, false); + } else { + result = new FloatStamp(64, value.asDouble(), value.asDouble(), !Double.isNaN(value.asDouble())); + } + break; + default: + throw GraalError.shouldNotReachHere(); + } + if (result.isConstant()) { + return result; + } + return null; + } + + private static Stamp maybeFoldConstant(UnaryOp op, FloatStamp stamp) { + if (stamp.isConstant()) { + JavaConstant constant = stamp.asConstant(); + Constant folded = op.foldConstant(constant); + if (folded != null) { + return FloatStamp.stampForConstant(folded); + } + } + return null; + } + + private static Stamp maybeFoldConstant(BinaryOp op, FloatStamp stamp1, FloatStamp stamp2) { + if (stamp1.isConstant() && stamp2.isConstant()) { + JavaConstant constant1 = stamp1.asConstant(); + JavaConstant constant2 = stamp2.asConstant(); + Constant folded = op.foldConstant(constant1, constant2); + if (folded != null) { + FloatStamp stamp = stampForConstant(folded); + if (stamp != null && stamp.isConstant()) { + assert stamp.asConstant().equals(folded); + return stamp; + } + } + } + return null; + } + public static final ArithmeticOpTable OPS = new ArithmeticOpTable( new UnaryOp.Neg() { @@ -322,8 +384,13 @@ public class FloatStamp extends PrimitiveStamp { @Override public Stamp foldStamp(Stamp s) { FloatStamp stamp = (FloatStamp) s; + Stamp folded = maybeFoldConstant(this, stamp); + if (folded != null) { + return folded; + } return new FloatStamp(stamp.getBits(), -stamp.upperBound(), -stamp.lowerBound(), stamp.isNonNaN()); } + }, new BinaryOp.Add(false, true) { @@ -344,8 +411,13 @@ public class FloatStamp extends PrimitiveStamp { } @Override - public Stamp foldStamp(Stamp stamp1, Stamp stamp2) { - // TODO + public Stamp foldStamp(Stamp s1, Stamp s2) { + FloatStamp stamp1 = (FloatStamp) s1; + FloatStamp stamp2 = (FloatStamp) s2; + Stamp folded = maybeFoldConstant(this, stamp1, stamp2); + if (folded != null) { + return folded; + } return stamp1.unrestricted(); } @@ -381,8 +453,13 @@ public class FloatStamp extends PrimitiveStamp { } @Override - public Stamp foldStamp(Stamp stamp1, Stamp stamp2) { - // TODO + public Stamp foldStamp(Stamp s1, Stamp s2) { + FloatStamp stamp1 = (FloatStamp) s1; + FloatStamp stamp2 = (FloatStamp) s2; + Stamp folded = maybeFoldConstant(this, stamp1, stamp2); + if (folded != null) { + return folded; + } return stamp1.unrestricted(); } @@ -418,9 +495,14 @@ public class FloatStamp extends PrimitiveStamp { } @Override - public Stamp foldStamp(Stamp a, Stamp b) { - // TODO - return a.unrestricted(); + public Stamp foldStamp(Stamp s1, Stamp s2) { + FloatStamp stamp1 = (FloatStamp) s1; + FloatStamp stamp2 = (FloatStamp) s2; + Stamp folded = maybeFoldConstant(this, stamp1, stamp2); + if (folded != null) { + return folded; + } + return stamp1.unrestricted(); } @Override @@ -450,17 +532,24 @@ public class FloatStamp extends PrimitiveStamp { assert a.getJavaKind() == b.getJavaKind(); switch (a.getJavaKind()) { case Float: - return JavaConstant.forFloat(a.asFloat() / b.asFloat()); + float floatDivisor = b.asFloat(); + return (floatDivisor == 0) ? null : JavaConstant.forFloat(a.asFloat() / floatDivisor); case Double: - return JavaConstant.forDouble(a.asDouble() / b.asDouble()); + double doubleDivisor = b.asDouble(); + return (doubleDivisor == 0) ? null : JavaConstant.forDouble(a.asDouble() / doubleDivisor); default: throw GraalError.shouldNotReachHere(); } } @Override - public Stamp foldStamp(Stamp stamp1, Stamp stamp2) { - // TODO + public Stamp foldStamp(Stamp s1, Stamp s2) { + FloatStamp stamp1 = (FloatStamp) s1; + FloatStamp stamp2 = (FloatStamp) s2; + Stamp folded = maybeFoldConstant(this, stamp1, stamp2); + if (folded != null) { + return folded; + } return stamp1.unrestricted(); } @@ -496,8 +585,13 @@ public class FloatStamp extends PrimitiveStamp { } @Override - public Stamp foldStamp(Stamp stamp1, Stamp stamp2) { - // TODO + public Stamp foldStamp(Stamp s1, Stamp s2) { + FloatStamp stamp1 = (FloatStamp) s1; + FloatStamp stamp2 = (FloatStamp) s2; + Stamp folded = maybeFoldConstant(this, stamp1, stamp2); + if (folded != null) { + return folded; + } return stamp1.unrestricted(); } }, @@ -521,6 +615,17 @@ public class FloatStamp extends PrimitiveStamp { @Override public Stamp foldStamp(Stamp s) { + FloatStamp stamp = (FloatStamp) s; + JavaConstant constant = stamp.asConstant(); + if (constant != null) { + Constant folded = foldConstant(constant); + if (folded != null) { + FloatStamp result = stampForConstant(folded); + if (result != null && result.isConstant()) { + return result; + } + } + } return s.unrestricted(); } }, @@ -547,7 +652,13 @@ public class FloatStamp extends PrimitiveStamp { } @Override - public Stamp foldStamp(Stamp stamp1, Stamp stamp2) { + public Stamp foldStamp(Stamp s1, Stamp s2) { + FloatStamp stamp1 = (FloatStamp) s1; + FloatStamp stamp2 = (FloatStamp) s2; + Stamp folded = maybeFoldConstant(this, stamp1, stamp2); + if (folded != null) { + return folded; + } return stamp1.unrestricted(); } @@ -576,7 +687,9 @@ public class FloatStamp extends PrimitiveStamp { case Float: int fa = Float.floatToRawIntBits(a.asFloat()); int fb = Float.floatToRawIntBits(b.asFloat()); - return JavaConstant.forFloat(Float.intBitsToFloat(fa | fb)); + float floatOr = Float.intBitsToFloat(fa | fb); + assert (fa | fb) == Float.floatToRawIntBits((floatOr)); + return JavaConstant.forFloat(floatOr); case Double: long da = Double.doubleToRawLongBits(a.asDouble()); long db = Double.doubleToRawLongBits(b.asDouble()); @@ -587,7 +700,13 @@ public class FloatStamp extends PrimitiveStamp { } @Override - public Stamp foldStamp(Stamp stamp1, Stamp stamp2) { + public Stamp foldStamp(Stamp s1, Stamp s2) { + FloatStamp stamp1 = (FloatStamp) s1; + FloatStamp stamp2 = (FloatStamp) s2; + Stamp folded = maybeFoldConstant(this, stamp1, stamp2); + if (folded != null) { + return folded; + } return stamp1.unrestricted(); } @@ -627,7 +746,13 @@ public class FloatStamp extends PrimitiveStamp { } @Override - public Stamp foldStamp(Stamp stamp1, Stamp stamp2) { + public Stamp foldStamp(Stamp s1, Stamp s2) { + FloatStamp stamp1 = (FloatStamp) s1; + FloatStamp stamp2 = (FloatStamp) s2; + Stamp folded = maybeFoldConstant(this, stamp1, stamp2); + if (folded != null) { + return folded; + } return stamp1.unrestricted(); } @@ -665,6 +790,10 @@ public class FloatStamp extends PrimitiveStamp { @Override public Stamp foldStamp(Stamp s) { FloatStamp stamp = (FloatStamp) s; + Stamp folded = maybeFoldConstant(this, stamp); + if (folded != null) { + return folded; + } if (stamp.isNaN()) { return stamp; } @@ -689,6 +818,11 @@ public class FloatStamp extends PrimitiveStamp { @Override public Stamp foldStamp(Stamp s) { + FloatStamp stamp = (FloatStamp) s; + Stamp folded = maybeFoldConstant(this, stamp); + if (folded != null) { + return folded; + } return s.unrestricted(); } }, diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/IntegerStamp.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/IntegerStamp.java index 36e40d54f8f..2ca05ce0a77 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/IntegerStamp.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/IntegerStamp.java @@ -597,6 +597,10 @@ public final class IntegerStamp extends PrimitiveStamp { public Stamp foldStamp(Stamp s) { IntegerStamp stamp = (IntegerStamp) s; int bits = stamp.getBits(); + if (stamp.lowerBound == stamp.upperBound) { + long value = CodeUtil.convert(-stamp.lowerBound(), stamp.getBits(), false); + return StampFactory.forInteger(stamp.getBits(), value, value); + } if (stamp.lowerBound() != CodeUtil.minValue(bits)) { // TODO(ls) check if the mask calculation is correct... return StampFactory.forInteger(bits, -stamp.upperBound(), -stamp.lowerBound()); @@ -624,6 +628,11 @@ public final class IntegerStamp extends PrimitiveStamp { int bits = a.getBits(); assert bits == b.getBits(); + if (a.lowerBound == a.upperBound && b.lowerBound == b.upperBound) { + long value = CodeUtil.convert(a.lowerBound() + b.lowerBound(), a.getBits(), false); + return StampFactory.forInteger(a.getBits(), value, value); + } + if (a.isUnrestricted()) { return a; } else if (b.isUnrestricted()) { @@ -711,6 +720,12 @@ public final class IntegerStamp extends PrimitiveStamp { int bits = a.getBits(); assert bits == b.getBits(); + + if (a.lowerBound == a.upperBound && b.lowerBound == b.upperBound) { + long value = CodeUtil.convert(a.lowerBound() * b.lowerBound(), a.getBits(), false); + return StampFactory.forInteger(a.getBits(), value, value); + } + // if a==0 or b==0 result of a*b is always 0 if (a.upMask() == 0) { return a; @@ -791,7 +806,7 @@ public final class IntegerStamp extends PrimitiveStamp { long maxPosB = b.upperBound(); // multiplication has shift semantics - long newUpMask = ~CodeUtil.mask(Long.numberOfTrailingZeros(a.upMask) + Long.numberOfTrailingZeros(b.upMask)) & CodeUtil.mask(bits); + long newUpMask = ~CodeUtil.mask(Math.min(64, Long.numberOfTrailingZeros(a.upMask) + Long.numberOfTrailingZeros(b.upMask))) & CodeUtil.mask(bits); if (a.canBePositive()) { if (b.canBePositive()) { @@ -1023,6 +1038,9 @@ public final class IntegerStamp extends PrimitiveStamp { PrimitiveConstant a = (PrimitiveConstant) const1; PrimitiveConstant b = (PrimitiveConstant) const2; assert a.getJavaKind() == b.getJavaKind(); + if (b.asLong() == 0) { + return null; + } return JavaConstant.forIntegerKind(a.getJavaKind(), a.asLong() / b.asLong()); } @@ -1031,9 +1049,12 @@ public final class IntegerStamp extends PrimitiveStamp { IntegerStamp a = (IntegerStamp) stamp1; IntegerStamp b = (IntegerStamp) stamp2; assert a.getBits() == b.getBits(); - if (b.isStrictlyPositive()) { - long newLowerBound = a.lowerBound() / b.upperBound(); - long newUpperBound = a.upperBound() / b.lowerBound(); + if (a.lowerBound == a.upperBound && b.lowerBound == b.upperBound && b.lowerBound != 0) { + long value = CodeUtil.convert(a.lowerBound() / b.lowerBound(), a.getBits(), false); + return StampFactory.forInteger(a.getBits(), value, value); + } else if (b.isStrictlyPositive()) { + long newLowerBound = a.lowerBound() < 0 ? a.lowerBound() / b.lowerBound() : a.lowerBound() / b.upperBound(); + long newUpperBound = a.upperBound() < 0 ? a.upperBound() / b.upperBound() : a.upperBound() / b.lowerBound(); return StampFactory.forInteger(a.getBits(), newLowerBound, newUpperBound); } else { return a.unrestricted(); @@ -1054,6 +1075,9 @@ public final class IntegerStamp extends PrimitiveStamp { PrimitiveConstant a = (PrimitiveConstant) const1; PrimitiveConstant b = (PrimitiveConstant) const2; assert a.getJavaKind() == b.getJavaKind(); + if (b.asLong() == 0) { + return null; + } return JavaConstant.forIntegerKind(a.getJavaKind(), a.asLong() % b.asLong()); } @@ -1062,6 +1086,12 @@ public final class IntegerStamp extends PrimitiveStamp { IntegerStamp a = (IntegerStamp) stamp1; IntegerStamp b = (IntegerStamp) stamp2; assert a.getBits() == b.getBits(); + + if (a.lowerBound == a.upperBound && b.lowerBound == b.upperBound && b.lowerBound != 0) { + long value = CodeUtil.convert(a.lowerBound() % b.lowerBound(), a.getBits(), false); + return StampFactory.forInteger(a.getBits(), value, value); + } + // zero is always possible long newLowerBound = Math.min(a.lowerBound(), 0); long newUpperBound = Math.max(a.upperBound(), 0); @@ -1364,6 +1394,10 @@ public final class IntegerStamp extends PrimitiveStamp { public Stamp foldStamp(Stamp input) { IntegerStamp stamp = (IntegerStamp) input; int bits = stamp.getBits(); + if (stamp.lowerBound == stamp.upperBound) { + long value = CodeUtil.convert(Math.abs(stamp.lowerBound()), stamp.getBits(), false); + return StampFactory.forInteger(stamp.getBits(), value, value); + } if (stamp.lowerBound() == CodeUtil.minValue(bits)) { return input.unrestricted(); } else { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/CheckGraalInvariants.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/CheckGraalInvariants.java index 10f0a91e853..7ddf8c24b10 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/CheckGraalInvariants.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/CheckGraalInvariants.java @@ -49,8 +49,8 @@ import org.graalvm.compiler.core.CompilerThreadFactory; import org.graalvm.compiler.core.common.LIRKind; import org.graalvm.compiler.core.common.type.ArithmeticOpTable; import org.graalvm.compiler.debug.DebugCloseable; -import org.graalvm.compiler.debug.DebugHandlersFactory; import org.graalvm.compiler.debug.DebugContext; +import org.graalvm.compiler.debug.DebugHandlersFactory; import org.graalvm.compiler.debug.GraalError; import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.graph.NodeClass; @@ -73,6 +73,8 @@ import org.graalvm.compiler.phases.util.Providers; import org.graalvm.compiler.phases.verify.VerifyBailoutUsage; import org.graalvm.compiler.phases.verify.VerifyCallerSensitiveMethods; import org.graalvm.compiler.phases.verify.VerifyDebugUsage; +import org.graalvm.compiler.phases.verify.VerifyGetOptionsUsage; +import org.graalvm.compiler.phases.verify.VerifyGraphAddUsage; import org.graalvm.compiler.phases.verify.VerifyInstanceOfUsage; import org.graalvm.compiler.phases.verify.VerifyUpdateUsages; import org.graalvm.compiler.phases.verify.VerifyUsageWithEquals; @@ -381,6 +383,8 @@ public class CheckGraalInvariants extends GraalCompilerTest { new VerifyUpdateUsages().apply(graph, context); new VerifyBailoutUsage().apply(graph, context); new VerifyInstanceOfUsage().apply(graph, context); + new VerifyGraphAddUsage().apply(graph, context); + new VerifyGetOptionsUsage().apply(graph, context); if (graph.method().isBridge()) { BridgeMethodUtils.getBridgedMethod(graph.method()); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTest14.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTest14.java new file mode 100644 index 00000000000..fbba0820ba8 --- /dev/null +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTest14.java @@ -0,0 +1,91 @@ +/* + * 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 org.graalvm.compiler.core.test; + +import org.graalvm.compiler.graph.iterators.NodeIterable; +import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.FixedGuardNode; +import org.graalvm.compiler.nodes.GuardNode; +import org.graalvm.compiler.nodes.LogicNode; +import org.graalvm.compiler.nodes.StructuredGraph; +import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions; +import org.graalvm.compiler.nodes.calc.IntegerBelowNode; +import org.graalvm.compiler.nodes.java.LoadIndexedNode; +import org.graalvm.compiler.nodes.memory.FloatingReadNode; +import org.graalvm.compiler.nodes.memory.ReadNode; +import org.graalvm.compiler.nodes.spi.LoweringTool; +import org.graalvm.compiler.phases.common.CanonicalizerPhase; +import org.graalvm.compiler.phases.common.FloatingReadPhase; +import org.graalvm.compiler.phases.common.IterativeConditionalEliminationPhase; +import org.graalvm.compiler.phases.common.LoweringPhase; +import org.graalvm.compiler.phases.tiers.PhaseContext; +import org.junit.Assert; +import org.junit.Test; + +import jdk.vm.ci.meta.DeoptimizationReason; + +/** + * Check that multiple bounds checks are correctly grouped together. + */ +public class ConditionalEliminationTest14 extends ConditionalEliminationTestBase { + + public static void test1Snippet(Object[] args) { + Object a5 = args[5]; + Object a7 = args[7]; + Object a6 = args[6]; + + /* + * The order of the conditions matters: The scheduler processes the floating reads for the + * array loads in the order of the conditions here, and we want the index 7 access to be + * processed before the index 6 access. + */ + if (a5 != null && a7 != null && a6 != null) { + sink1 = 1; + } + sink0 = 0; + } + + @Test + public void test1() { + StructuredGraph graph = parseEager("test1Snippet", AllowAssumptions.YES); + CanonicalizerPhase canonicalizer = new CanonicalizerPhase(); + PhaseContext context = new PhaseContext(getProviders()); + + /* Convert the LoadIndexNode to ReadNode with floating guards. */ + new LoweringPhase(canonicalizer, LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context); + /* Convert the ReadNode to FloatingReadNode. */ + new FloatingReadPhase().apply(graph); + /* Apply the phase that we want to test. */ + new IterativeConditionalEliminationPhase(canonicalizer, true).apply(graph, context); + + Assert.assertEquals("All guards must be floating", 0, graph.getNodes(FixedGuardNode.TYPE).count()); + Assert.assertEquals("All array accesses must have been lowered", 0, graph.getNodes().filter(LoadIndexedNode.class).count()); + Assert.assertEquals("All reads must be floating", 0, graph.getNodes().filter(ReadNode.class).count()); + Assert.assertEquals("Must have floating reads (3 array accesses, 1 array length)", 4, graph.getNodes().filter(FloatingReadNode.class).count()); + + NodeIterable boundsChecks = graph.getNodes(GuardNode.TYPE).filter(n -> ((GuardNode) n).getReason() == DeoptimizationReason.BoundsCheckException); + Assert.assertEquals("Must have only 1 bounds check remaining", 1, boundsChecks.count()); + LogicNode condition = boundsChecks.first().getCondition(); + Assert.assertTrue("Bounds check must check for array length 8", condition instanceof IntegerBelowNode && ((IntegerBelowNode) condition).getY().valueEquals(ConstantNode.forInt(8))); + } +} diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTestBase.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTestBase.java index 0140fe6e5de..dfc8aecff31 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTestBase.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTestBase.java @@ -27,12 +27,15 @@ import org.graalvm.compiler.nodes.ProxyNode; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions; import org.graalvm.compiler.nodes.spi.LoweringTool; +import org.graalvm.compiler.phases.OptimisticOptimizations; +import org.graalvm.compiler.phases.OptimisticOptimizations.Optimization; import org.graalvm.compiler.phases.common.CanonicalizerPhase; import org.graalvm.compiler.phases.common.ConditionalEliminationPhase; import org.graalvm.compiler.phases.common.ConvertDeoptimizeToGuardPhase; import org.graalvm.compiler.phases.common.IterativeConditionalEliminationPhase; import org.graalvm.compiler.phases.common.LoweringPhase; import org.graalvm.compiler.phases.schedule.SchedulePhase; +import org.graalvm.compiler.phases.tiers.HighTierContext; import org.graalvm.compiler.phases.tiers.PhaseContext; import org.junit.Assert; @@ -45,6 +48,15 @@ public class ConditionalEliminationTestBase extends GraalCompilerTest { protected static int sink1; protected static int sink2; + /** + * These tests assume all code paths in called routines are reachable so disable removal of dead + * code based on method profiles. + */ + @Override + protected HighTierContext getDefaultHighTierContext() { + return new HighTierContext(getProviders(), getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL.remove(Optimization.RemoveNeverExecutedCode)); + } + protected void testConditionalElimination(String snippet, String referenceSnippet) { testConditionalElimination(snippet, referenceSnippet, false, false); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/DumpPathTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/DumpPathTest.java new file mode 100644 index 00000000000..8380c9a4a5e --- /dev/null +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/DumpPathTest.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2017, 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 org.graalvm.compiler.core.test; + +import java.io.IOException; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.graalvm.compiler.debug.DebugOptions; +import org.graalvm.compiler.options.OptionKey; +import org.graalvm.compiler.options.OptionValues; +import org.graalvm.util.EconomicMap; +import org.junit.Test; + +/** + * Check that setting the dump path results in files ending up in the right directory with matching + * names. + */ +public class DumpPathTest extends GraalCompilerTest { + + public static Object snippet() { + return new String("snippet"); + } + + @Test + public void testDump() throws IOException { + Path dumpDirectoryPath = Files.createTempDirectory("DumpPathTest"); + String[] extensions = new String[]{".cfg", ".bgv", ".graph-strings"}; + EconomicMap, Object> overrides = OptionValues.newOptionMap(); + overrides.put(DebugOptions.DumpPath, dumpDirectoryPath.toString()); + overrides.put(DebugOptions.PrintGraphFile, true); + overrides.put(DebugOptions.PrintCanonicalGraphStrings, true); + overrides.put(DebugOptions.Dump, "*"); + + // Generate dump files. + test(new OptionValues(getInitialOptions(), overrides), "snippet"); + // Check that Ideal files got created, in the right place. + checkForFiles(dumpDirectoryPath, extensions); + + // Clean up the generated files. + scrubDirectory(dumpDirectoryPath); + } + + /** + * Check that the given directory contains file or directory names with all the given + * extensions. + */ + private static void checkForFiles(Path directoryPath, String[] extensions) throws IOException { + String[] paths = new String[extensions.length]; + try (DirectoryStream stream = Files.newDirectoryStream(directoryPath)) { + for (Path filePath : stream) { + String fileName = filePath.getFileName().toString(); + for (int i = 0; i < extensions.length; i++) { + String extension = extensions[i]; + if (fileName.endsWith(extensions[i])) { + assertTrue(paths[i] == null, "multiple files found for %s in %s", extension, directoryPath); + paths[i] = fileName.replace(extensions[i], ""); + } + } + } + } + for (int i = 0; i < paths.length; i++) { + assertTrue(paths[i] != null, "missing file for extension %s in %s", extensions[i], directoryPath); + } + // Ensure that all file names are the same. + for (int i = 1; i < paths.length; i++) { + assertTrue(paths[0].equals(paths[i]), paths[0] + " != " + paths[i]); + } + } + + /** + * Remove the temporary directory. + */ + private static void scrubDirectory(Path directoryPath) { + try { + try (DirectoryStream stream = Files.newDirectoryStream(directoryPath)) { + for (Path filePath : stream) { + if (Files.isRegularFile(filePath)) { + Files.delete(filePath); + } else if (Files.isDirectory(filePath)) { + scrubDirectory(filePath); + } + } + } + Files.delete(directoryPath); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } +} diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/FinalizableSubclassTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/FinalizableSubclassTest.java index e6e802ce4c1..c01acf87524 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/FinalizableSubclassTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/FinalizableSubclassTest.java @@ -72,7 +72,7 @@ public class FinalizableSubclassTest extends GraalCompilerTest { Assert.assertTrue(constructors.length == 1); final ResolvedJavaMethod javaMethod = getMetaAccess().lookupJavaMethod(constructors[0]); OptionValues options = getInitialOptions(); - StructuredGraph graph = new StructuredGraph.Builder(options, getDebugContext(options), allowAssumptions).method(javaMethod).build(); + StructuredGraph graph = new StructuredGraph.Builder(options, getDebugContext(options, null, javaMethod), allowAssumptions).method(javaMethod).build(); GraphBuilderConfiguration conf = GraphBuilderConfiguration.getSnippetDefault(getDefaultGraphBuilderPlugins()); new GraphBuilderPhase.Instance(getMetaAccess(), getProviders().getStampProvider(), getProviders().getConstantReflection(), getProviders().getConstantFieldProvider(), conf, diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/GraalCompilerTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/GraalCompilerTest.java index 00887afda2a..4941269fdb3 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/GraalCompilerTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/GraalCompilerTest.java @@ -389,7 +389,7 @@ public abstract class GraalCompilerTest extends GraalTest { * {@link DebugDumpHandler}s closed in {@link #afterTest()}. */ protected DebugContext getDebugContext() { - return getDebugContext(getInitialOptions()); + return getDebugContext(getInitialOptions(), null, null); } @Override @@ -862,7 +862,7 @@ public abstract class GraalCompilerTest extends GraalTest { Result actual = executeActual(options, method, receiver, args); profile = method.getProfilingInfo(); // profile can change after execution for (DeoptimizationReason reason : shouldNotDeopt) { - Assert.assertEquals((int) deoptCounts.get(reason), profile.getDeoptimizationCount(reason)); + Assert.assertEquals("wrong number of deopt counts for " + reason, (int) deoptCounts.get(reason), profile.getDeoptimizationCount(reason)); } return actual; } @@ -1216,15 +1216,15 @@ public abstract class GraalCompilerTest extends GraalTest { protected final Builder builder(ResolvedJavaMethod method, AllowAssumptions allowAssumptions) { OptionValues options = getInitialOptions(); - return new Builder(options, getDebugContext(options), allowAssumptions).method(method).compilationId(getCompilationId(method)); + return new Builder(options, getDebugContext(options, null, method), allowAssumptions).method(method).compilationId(getCompilationId(method)); } protected final Builder builder(ResolvedJavaMethod method, AllowAssumptions allowAssumptions, CompilationIdentifier compilationId, OptionValues options) { - return new Builder(options, getDebugContext(options), allowAssumptions).method(method).compilationId(compilationId); + return new Builder(options, getDebugContext(options, compilationId.toString(CompilationIdentifier.Verbosity.ID), method), allowAssumptions).method(method).compilationId(compilationId); } protected final Builder builder(ResolvedJavaMethod method, AllowAssumptions allowAssumptions, OptionValues options) { - return new Builder(options, getDebugContext(options), allowAssumptions).method(method).compilationId(getCompilationId(method)); + return new Builder(options, getDebugContext(options, null, method), allowAssumptions).method(method).compilationId(getCompilationId(method)); } protected PhaseSuite getDebugGraphBuilderSuite() { @@ -1234,6 +1234,7 @@ public abstract class GraalCompilerTest extends GraalTest { @SuppressWarnings("try") protected StructuredGraph parse(StructuredGraph.Builder builder, PhaseSuite graphBuilderSuite) { ResolvedJavaMethod javaMethod = builder.getMethod(); + builder.speculationLog(getSpeculationLog()); if (builder.getCancellable() == null) { builder.cancellable(getCancellable(javaMethod)); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/GraalDebugHandlersFactoryTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/GraalDebugHandlersFactoryTest.java index 723d127618a..42d058d4bd6 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/GraalDebugHandlersFactoryTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/GraalDebugHandlersFactoryTest.java @@ -31,8 +31,12 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.Comparator; -import org.graalvm.compiler.printer.GraalDebugHandlersFactory; +import org.graalvm.compiler.debug.DebugOptions; +import org.graalvm.compiler.debug.PathUtilities; +import org.graalvm.compiler.options.OptionKey; +import org.graalvm.compiler.options.OptionValues; import org.graalvm.compiler.test.AddExports; +import org.junit.Assume; import org.junit.Test; @AddExports("jdk.internal.vm.compiler/org.graalvm.compiler.printer") @@ -40,23 +44,28 @@ public class GraalDebugHandlersFactoryTest extends GraalCompilerTest { @Test public void createUniqueTest() throws Exception { - Field maxFileNameLengthField = GraalDebugHandlersFactory.class.getDeclaredField("MAX_FILE_NAME_LENGTH"); - maxFileNameLengthField.setAccessible(true); + Field maxFileNameLengthField = PathUtilities.class.getDeclaredField("MAX_FILE_NAME_LENGTH"); + try { + maxFileNameLengthField.setAccessible(true); + } catch (RuntimeException ex) { + Assume.assumeFalse("If InaccessibleObjectException is thrown, skip the test, we are on JDK9", ex.getClass().getSimpleName().equals("InaccessibleObjectException")); + } int maxFileNameLength = maxFileNameLengthField.getInt(null); - Method createUniqueMethod = GraalDebugHandlersFactory.class.getDeclaredMethod("createUnique", Path.class, String.class, String.class, String.class, boolean.class); + Method createUniqueMethod = PathUtilities.class.getDeclaredMethod("createUnique", OptionValues.class, OptionKey.class, String.class, String.class, String.class, boolean.class); createUniqueMethod.setAccessible(true); Path tmpDir = Files.createTempDirectory(Paths.get("."), "createUniqueTest"); + OptionValues options = new OptionValues(OptionValues.asMap(DebugOptions.DumpPath, tmpDir.toString())); try { for (boolean createDirectory : new boolean[]{true, false}) { for (String ext : new String[]{"", ".bgv", ".graph-strings"}) { for (int i = 0; i < maxFileNameLength + 5; i++) { String id = new String(new char[i]).replace('\0', 'i'); String label = ""; - createUniqueMethod.invoke(null, tmpDir, id, label, ext, createDirectory); + createUniqueMethod.invoke(null, options, null, id, label, ext, createDirectory); id = ""; label = new String(new char[i]).replace('\0', 'l'); - createUniqueMethod.invoke(null, tmpDir, id, label, ext, createDirectory); + createUniqueMethod.invoke(null, options, null, id, label, ext, createDirectory); } } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/GuardedIntrinsicTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/GuardedIntrinsicTest.java index fd0e6c2e956..a2a0f48ed2b 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/GuardedIntrinsicTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/GuardedIntrinsicTest.java @@ -144,6 +144,11 @@ public class GuardedIntrinsicTest extends GraalCompilerTest { public void test01() { Super inheritsHC = new Super(); Person overridesHC = new Person(0); + + // Ensure the profile for getSuperAge includes both receiver types + getSuperAge(inheritsHC); + getSuperAge(overridesHC); + test("getSuperAge", inheritsHC); test("getSuperAge", overridesHC); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/HashCodeTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/HashCodeTest.java index 6ee99a34f14..6891c666b7b 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/HashCodeTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/HashCodeTest.java @@ -22,6 +22,8 @@ */ package org.graalvm.compiler.core.test; +import java.util.HashMap; + import org.graalvm.compiler.core.phases.HighTier; import org.graalvm.compiler.core.phases.MidTier; import org.graalvm.compiler.nodes.InvokeNode; @@ -139,6 +141,10 @@ public class HashCodeTest extends GraalCompilerTest { public void test08() { initialize(Appendable.class); checkForGuardedIntrinsicPattern("hashCodeInterface"); + + // Ensure the profile for the dispatch in hashCodeSnippet01 + // has a receiver type that does not select Object.hashCode intrinsic + hashCodeSnippet01(new HashMap<>()); checkForGuardedIntrinsicPattern("hashCodeSnippet01"); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/MarkUnsafeAccessTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/MarkUnsafeAccessTest.java index 1d07d3b9375..2e747addf82 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/MarkUnsafeAccessTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/MarkUnsafeAccessTest.java @@ -25,6 +25,7 @@ package org.graalvm.compiler.core.test; import static java.nio.file.StandardOpenOption.READ; import static java.nio.file.StandardOpenOption.WRITE; +import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; @@ -33,22 +34,20 @@ import java.nio.channels.FileChannel.MapMode; import java.nio.file.Files; import java.nio.file.Path; -import jdk.vm.ci.code.InstalledCode; -import jdk.vm.ci.code.InvalidInstalledCodeException; -import jdk.vm.ci.meta.ResolvedJavaMethod; -import jdk.vm.ci.meta.ResolvedJavaType; - -import org.junit.Assert; -import org.junit.Assume; -import org.junit.Test; - -import sun.misc.Unsafe; - import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.phases.common.CanonicalizerPhase; import org.graalvm.compiler.phases.common.inlining.InliningPhase; import org.graalvm.compiler.phases.common.inlining.policy.InlineEverythingPolicy; import org.graalvm.compiler.phases.tiers.HighTierContext; +import org.junit.Assert; +import org.junit.Assume; +import org.junit.Test; + +import jdk.vm.ci.code.InstalledCode; +import jdk.vm.ci.code.InvalidInstalledCodeException; +import jdk.vm.ci.meta.ResolvedJavaMethod; +import jdk.vm.ci.meta.ResolvedJavaType; +import sun.misc.Unsafe; public class MarkUnsafeAccessTest extends GraalCompilerTest { @@ -170,7 +169,9 @@ public class MarkUnsafeAccessTest extends GraalCompilerTest { try { mbb.position(BLOCK_SIZE); getter.get(mbb); - System.currentTimeMillis(); // materialize async exception + + // Make a call that goes into native code to materialize async exception + new File("").exists(); } catch (InternalError e) { return; } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/MergeCanonicalizerTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/MergeCanonicalizerTest.java index 08f83bbe2cf..5c6605e4e6d 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/MergeCanonicalizerTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/MergeCanonicalizerTest.java @@ -26,12 +26,23 @@ import org.graalvm.compiler.debug.DebugContext; import org.graalvm.compiler.nodes.ReturnNode; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions; +import org.graalvm.compiler.phases.OptimisticOptimizations; +import org.graalvm.compiler.phases.OptimisticOptimizations.Optimization; import org.graalvm.compiler.phases.common.CanonicalizerPhase; +import org.graalvm.compiler.phases.tiers.HighTierContext; import org.graalvm.compiler.phases.tiers.PhaseContext; import org.junit.Test; public class MergeCanonicalizerTest extends GraalCompilerTest { + /** + * These tests assume all code paths are reachable so disable profile based dead code removal. + */ + @Override + protected HighTierContext getDefaultHighTierContext() { + return new HighTierContext(getProviders(), getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL.remove(Optimization.RemoveNeverExecutedCode)); + } + public static int staticField; private int field; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ProfilingInfoTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ProfilingInfoTest.java index 684d4a1c362..7d881183c7e 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ProfilingInfoTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ProfilingInfoTest.java @@ -24,15 +24,17 @@ package org.graalvm.compiler.core.test; import java.io.Serializable; +import org.graalvm.compiler.test.SubprocessUtil; +import org.junit.Assert; +import org.junit.Assume; +import org.junit.Test; + import jdk.vm.ci.meta.JavaTypeProfile; import jdk.vm.ci.meta.ProfilingInfo; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; import jdk.vm.ci.meta.TriState; -import org.junit.Assert; -import org.junit.Test; - /** * Tests profiling information provided by the runtime. *

@@ -40,7 +42,7 @@ import org.junit.Test; * information may be gathered for any given method. For example, HotSpot's advanced compilation * policy can decide to only gather partial profiles in a first level compilation (see * AdvancedThresholdPolicy::common(...) in advancedThresholdPolicy.cpp). Because of this, - * occasionally tests for {@link ProfilingInfo#getNullSeen(int)} can fail since HotSpot only set's + * occasionally tests for {@link ProfilingInfo#getNullSeen(int)} can fail since HotSpot only sets * the null_seen bit when doing full profiling. */ public class ProfilingInfoTest extends GraalCompilerTest { @@ -182,6 +184,14 @@ public class ProfilingInfoTest extends GraalCompilerTest { Assert.assertNull(typeProfile); } + public ProfilingInfoTest() { + // These tests are explicitly testing the profiling behavior of the + // interpreter. C1-based profiling differs slightly and when -Xcomp + // is present, profiles will be created by C1 compiled code, not the + // interpreter. + Assume.assumeTrue(!SubprocessUtil.getVMCommandLine().contains("-Xcomp")); + } + @Test public void testExceptionSeen() { // NullPointerException diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/SubWordReturnTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/SubWordReturnTest.java new file mode 100644 index 00000000000..d8e38370a3f --- /dev/null +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/SubWordReturnTest.java @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2017, 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 org.graalvm.compiler.core.test; + +import java.util.ArrayList; +import java.util.List; +import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.ResolvedJavaMethod; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +@RunWith(Parameterized.class) +public class SubWordReturnTest extends GraalCompilerTest { + + private final JavaKind kind; + private final int value; + + private final String generatedClassName; + private final String generatedClassNameInternal; + + private final String testMethodName; + + /** + * The {@link AsmLoader} generates a class looking like this for the types byte, short, int and + * char. + */ + static class ByteGetter { + + // private static int intField = 1000000; + + private static byte get() { + // GETSTATIC intField + // IRETURN + return 0; + } + + public static int testByteSnippet() { + return get(); + } + } + + @Parameters(name = "{0}, {1}") + public static List data() { + ArrayList ret = new ArrayList<>(); + for (int i : new int[]{1000000, 1000001, -1000000, -1}) { + ret.add(new Object[]{JavaKind.Boolean, i}); + ret.add(new Object[]{JavaKind.Byte, i}); + ret.add(new Object[]{JavaKind.Short, i}); + ret.add(new Object[]{JavaKind.Char, i}); + } + return ret; + } + + public SubWordReturnTest(JavaKind kind, int value) { + this.kind = kind; + this.value = value; + + this.generatedClassName = SubWordReturnTest.class.getName() + "$" + kind.toString() + "Getter"; + this.generatedClassNameInternal = generatedClassName.replace('.', '/'); + this.testMethodName = "test" + kind.name() + "Snippet"; + } + + @Test + public void test() throws ClassNotFoundException { + Class testClass = new AsmLoader(SubWordReturnTest.class.getClassLoader()).findClass(generatedClassName); + ResolvedJavaMethod method = getResolvedJavaMethod(testClass, testMethodName); + test(method, null); + } + + class AsmLoader extends ClassLoader implements Opcodes { + + Class loaded; + + AsmLoader(ClassLoader parent) { + super(parent); + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + if (name.equals(generatedClassName)) { + if (loaded == null) { + byte[] gen = generateClass(); + loaded = defineClass(name, gen, 0, gen.length); + } + return loaded; + } else { + return super.findClass(name); + } + } + + private byte[] generateClass() { + ClassWriter cw = new ClassWriter(0); + cw.visit(52, ACC_SUPER | ACC_PUBLIC, generatedClassNameInternal, null, "java/lang/Object", null); + + FieldVisitor intField = cw.visitField(ACC_PRIVATE | ACC_STATIC, "intField", "I", null, value); + intField.visitEnd(); + + MethodVisitor get = cw.visitMethod(ACC_PRIVATE | ACC_STATIC, "get", "()" + kind.getTypeChar(), null, null); + get.visitCode(); + get.visitFieldInsn(GETSTATIC, generatedClassNameInternal, "intField", "I"); + get.visitInsn(IRETURN); + get.visitMaxs(1, 0); + get.visitEnd(); + + MethodVisitor snippet = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, testMethodName, "()I", null, null); + snippet.visitCode(); + snippet.visitMethodInsn(INVOKESTATIC, generatedClassNameInternal, "get", "()" + kind.getTypeChar(), false); + snippet.visitInsn(IRETURN); + snippet.visitMaxs(1, 0); + snippet.visitEnd(); + + cw.visitEnd(); + return cw.toByteArray(); + } + } + +} diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/UnbalancedMonitorsTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/UnbalancedMonitorsTest.java index c7825439a07..eea935cfefe 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/UnbalancedMonitorsTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/UnbalancedMonitorsTest.java @@ -87,7 +87,7 @@ public class UnbalancedMonitorsTest extends GraalCompilerTest { ResolvedJavaMethod method = getResolvedJavaMethod(LOADER.findClass(INNER_CLASS_NAME), name); try { OptionValues options = getInitialOptions(); - StructuredGraph graph = new StructuredGraph.Builder(options, getDebugContext(options)).method(method).build(); + StructuredGraph graph = new StructuredGraph.Builder(options, getDebugContext(options, null, method)).method(method).build(); Plugins plugins = new Plugins(new InvocationPlugins()); GraphBuilderConfiguration graphBuilderConfig = GraphBuilderConfiguration.getDefault(plugins).withEagerResolving(true).withUnresolvedIsError(true); OptimisticOptimizations optimisticOpts = OptimisticOptimizations.NONE; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/UnsafeVirtualizationTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/UnsafeVirtualizationTest.java index 236be5cff40..9b09e2522b5 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/UnsafeVirtualizationTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/UnsafeVirtualizationTest.java @@ -32,9 +32,20 @@ import org.graalvm.compiler.phases.tiers.PhaseContext; import org.graalvm.compiler.virtual.phases.ea.PartialEscapePhase; import org.junit.Test; +import jdk.vm.ci.code.InstalledCode; +import jdk.vm.ci.meta.ResolvedJavaMethod; + public class UnsafeVirtualizationTest extends GraalCompilerTest { - public static class A { + public static class Base { + /* + * This padding ensure that the size of the Base class ends up as a multiple of 8, which + * makes the first field of the subclass 8-byte aligned. + */ + double padding; + } + + public static class A extends Base { int f1; int f2; } @@ -56,39 +67,96 @@ public class UnsafeVirtualizationTest extends GraalCompilerTest { AF2Offset = o2; } - public static int unsafeSnippet0(int i1, int i2) { + public static int unsafeSnippet1(double i1) { A a = new A(); - UNSAFE.putDouble(a, AF1Offset, i1 + i2); + UNSAFE.putDouble(a, AF1Offset, i1); return UNSAFE.getInt(a, AF1Offset) + UNSAFE.getInt(a, AF2Offset); } - public static int unsafeSnippet1(int i1, int i2) { + public static long unsafeSnippet2a(int i1) { A a = new A(); - UNSAFE.putDouble(a, AF1Offset, i1 + i2); + UNSAFE.putDouble(a, AF1Offset, i1); + a.f1 = i1; + return UNSAFE.getLong(a, AF1Offset); + } + + public static long unsafeSnippet2b(int i1) { + A a = new A(); + UNSAFE.putDouble(a, AF1Offset, i1); a.f2 = i1; - return (int) UNSAFE.getDouble(a, AF1Offset); + return UNSAFE.getLong(a, AF1Offset); + } + + public static long unsafeSnippet3a(int i1) { + A a = new A(); + UNSAFE.putDouble(a, AF1Offset, i1); + UNSAFE.putInt(a, AF1Offset, i1); + return UNSAFE.getLong(a, AF1Offset); + } + + public static long unsafeSnippet3b(int i1) { + A a = new A(); + UNSAFE.putDouble(a, AF1Offset, i1); + UNSAFE.putInt(a, AF2Offset, i1); + return UNSAFE.getLong(a, AF1Offset); + } + + public static int unsafeSnippet4(double i1) { + A a = new A(); + UNSAFE.putDouble(a, AF1Offset, i1); + UNSAFE.putDouble(a, AF1Offset, i1); + return UNSAFE.getInt(a, AF1Offset) + UNSAFE.getInt(a, AF2Offset); } @Test public void testUnsafePEA01() { - testPartialEscapeReadElimination(parseEager("unsafeSnippet0", AllowAssumptions.NO), false); - testPartialEscapeReadElimination(parseEager("unsafeSnippet0", AllowAssumptions.NO), true); + testPartialEscapeReadElimination("unsafeSnippet1", false, 1.0); + testPartialEscapeReadElimination("unsafeSnippet1", true, 1.0); } @Test public void testUnsafePEA02() { - testPartialEscapeReadElimination(parseEager("unsafeSnippet1", AllowAssumptions.NO), false); - testPartialEscapeReadElimination(parseEager("unsafeSnippet1", AllowAssumptions.NO), true); + testPartialEscapeReadElimination("unsafeSnippet2a", false, 1); + testPartialEscapeReadElimination("unsafeSnippet2a", true, 1); + + testPartialEscapeReadElimination("unsafeSnippet2b", false, 1); + testPartialEscapeReadElimination("unsafeSnippet2b", true, 1); } - public void testPartialEscapeReadElimination(StructuredGraph graph, boolean canonicalizeBefore) { + @Test + public void testUnsafePEA03() { + testPartialEscapeReadElimination("unsafeSnippet3a", false, 1); + testPartialEscapeReadElimination("unsafeSnippet3a", true, 1); + + testPartialEscapeReadElimination("unsafeSnippet3b", false, 1); + testPartialEscapeReadElimination("unsafeSnippet3b", true, 1); + } + + @Test + public void testUnsafePEA04() { + testPartialEscapeReadElimination("unsafeSnippet4", false, 1.0); + testPartialEscapeReadElimination("unsafeSnippet4", true, 1.0); + } + + public void testPartialEscapeReadElimination(String snippet, boolean canonicalizeBefore, Object... args) { + assert AF1Offset % 8 == 0 : "First of the two int-fields must be 8-byte aligned"; + + ResolvedJavaMethod method = getResolvedJavaMethod(snippet); + StructuredGraph graph = parseEager(snippet, AllowAssumptions.NO); OptionValues options = graph.getOptions(); PhaseContext context = getDefaultHighTierContext(); CanonicalizerPhase canonicalizer = new CanonicalizerPhase(); if (canonicalizeBefore) { canonicalizer.apply(graph, context); } + Result r = executeExpected(method, null, args); new PartialEscapePhase(true, true, canonicalizer, null, options).apply(graph, context); + try { + InstalledCode code = getCode(method, graph); + Object result = code.executeVarargs(args); + assertEquals(r, new Result(result, null)); + } catch (Throwable e) { + assertFalse(true, e.toString()); + } } - } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/EATestBase.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/EATestBase.java index f429d53ec52..55db5ec5b85 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/EATestBase.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/EATestBase.java @@ -77,7 +77,7 @@ public class EATestBase extends GraalCompilerTest { @Override public String toString() { - return "{" + x + "," + y + "}"; + return "{" + x + "," + y + "," + z + "}"; } @Override @@ -158,11 +158,19 @@ public class EATestBase extends GraalCompilerTest { context = getDefaultHighTierContext(); new InliningPhase(new CanonicalizerPhase()).apply(graph, context); new DeadCodeEliminationPhase().apply(graph); - new CanonicalizerPhase().apply(graph, context); + canonicalizeGraph(); new PartialEscapePhase(iterativeEscapeAnalysis, false, new CanonicalizerPhase(), null, graph.getOptions()).apply(graph, context); + postEACanonicalizeGraph(); returnNodes = graph.getNodes(ReturnNode.TYPE).snapshot(); } catch (Throwable e) { throw debug.handle(e); } } + + protected void postEACanonicalizeGraph() { + } + + protected void canonicalizeGraph() { + new CanonicalizerPhase().apply(graph, context); + } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/PEAAssertionsTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/PEAAssertionsTest.java index 8c3f70273a1..69c7cc21e68 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/PEAAssertionsTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/PEAAssertionsTest.java @@ -27,9 +27,20 @@ import org.junit.Test; import org.graalvm.compiler.api.directives.GraalDirectives; import org.graalvm.compiler.code.SourceStackTraceBailoutException; import org.graalvm.compiler.core.test.GraalCompilerTest; +import org.graalvm.compiler.phases.OptimisticOptimizations; +import org.graalvm.compiler.phases.OptimisticOptimizations.Optimization; +import org.graalvm.compiler.phases.tiers.HighTierContext; public class PEAAssertionsTest extends GraalCompilerTest { + /** + * These tests assume all code paths are reachable so disable profile based dead code removal. + */ + @Override + protected HighTierContext getDefaultHighTierContext() { + return new HighTierContext(getProviders(), getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL.remove(Optimization.RemoveNeverExecutedCode)); + } + public static Object field; public static void snippet1(int i) { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/TrufflePEATest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/TrufflePEATest.java index 81ee0d6eccc..b08c1adf12e 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/TrufflePEATest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/TrufflePEATest.java @@ -22,6 +22,8 @@ */ package org.graalvm.compiler.core.test.ea; +import java.lang.reflect.Field; + import org.graalvm.compiler.core.common.GraalOptions; import org.graalvm.compiler.core.test.GraalCompilerTest; import org.graalvm.compiler.nodes.StructuredGraph; @@ -33,9 +35,8 @@ import org.graalvm.compiler.phases.common.inlining.InliningPhase; import org.graalvm.compiler.phases.tiers.HighTierContext; import org.graalvm.compiler.virtual.phases.ea.PartialEscapePhase; import org.junit.Test; -import sun.misc.Unsafe; -import java.lang.reflect.Field; +import sun.misc.Unsafe; public class TrufflePEATest extends GraalCompilerTest { @@ -56,6 +57,7 @@ public class TrufflePEATest extends GraalCompilerTest { static class DynamicObject { int primitiveField0; int primitiveField1; + int primitiveField2; } private static final long offsetLong1 = Unsafe.ARRAY_LONG_BASE_OFFSET + Unsafe.ARRAY_LONG_INDEX_SCALE * 1; @@ -66,7 +68,15 @@ public class TrufflePEATest extends GraalCompilerTest { static { try { Field primitiveField0 = DynamicObject.class.getDeclaredField("primitiveField0"); - primitiveField0Offset = UNSAFE.objectFieldOffset(primitiveField0); + long offset = UNSAFE.objectFieldOffset(primitiveField0); + if (offset % 8 == 0) { + primitiveField0Offset = offset; + } else { + Field primitiveField1 = DynamicObject.class.getDeclaredField("primitiveField1"); + offset = UNSAFE.objectFieldOffset(primitiveField1); + assert offset % 8 == 0; + primitiveField0Offset = offset; + } } catch (NoSuchFieldException | SecurityException e) { throw new AssertionError(e); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/UnsafeEATest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/UnsafeEATest.java index 726f42adf8b..4ce92748330 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/UnsafeEATest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/UnsafeEATest.java @@ -22,14 +22,26 @@ */ package org.graalvm.compiler.core.test.ea; -import jdk.vm.ci.meta.JavaConstant; +import java.nio.ByteBuffer; +import org.graalvm.compiler.api.directives.GraalDirectives; +import org.graalvm.compiler.graph.Graph; +import org.graalvm.compiler.graph.Node; +import org.graalvm.compiler.nodes.NamedLocationIdentity; +import org.graalvm.compiler.nodes.PhiNode; +import org.graalvm.compiler.nodes.ValuePhiNode; +import org.graalvm.compiler.nodes.calc.UnpackEndianHalfNode; +import org.graalvm.compiler.nodes.extended.RawLoadNode; +import org.graalvm.compiler.nodes.extended.RawStoreNode; +import org.graalvm.compiler.nodes.extended.UnsafeAccessNode; +import org.graalvm.compiler.nodes.java.LoadFieldNode; +import org.graalvm.compiler.phases.common.CanonicalizerPhase; import org.junit.Assert; import org.junit.Test; -import org.graalvm.compiler.nodes.PhiNode; -import org.graalvm.compiler.nodes.ValuePhiNode; -import org.graalvm.compiler.nodes.java.LoadFieldNode; +import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.ResolvedJavaMethod; public class UnsafeEATest extends EATestBase { @@ -56,6 +68,64 @@ public class UnsafeEATest extends EATestBase { } } + @Override + protected void testEscapeAnalysis(String snippet, JavaConstant expectedConstantResult, boolean iterativeEscapeAnalysis) { + // Exercise both a graph containing UnsafeAccessNodes and one which has been possibly been + // canonicalized into AccessFieldNodes. + testingUnsafe = true; + super.testEscapeAnalysis(snippet, expectedConstantResult, iterativeEscapeAnalysis); + testingUnsafe = false; + super.testEscapeAnalysis(snippet, expectedConstantResult, iterativeEscapeAnalysis); + if (expectedConstantResult != null) { + // Check that a compiled version of this method returns the same value if we expect a + // constant result. + ResolvedJavaMethod method = getResolvedJavaMethod(snippet); + JavaKind[] javaKinds = method.getSignature().toParameterKinds(false); + Object[] args = new Object[javaKinds.length]; + int i = 0; + for (JavaKind k : javaKinds) { + args[i++] = JavaConstant.defaultForKind(k).asBoxedPrimitive(); + } + Result result = executeExpected(method, null, args); + assertTrue(result.returnValue.equals(expectedConstantResult.asBoxedPrimitive())); + } + } + + @Override + protected void canonicalizeGraph() { + if (testingUnsafe) { + // For testing purposes we'd like to ensure that our raw unsafe operations stay as + // unsafe nodes, so force them to appear to have LocationIdentity.any to disable + // transformation into field access nodes. + for (Node node : graph.getNodes().filter(x -> x instanceof UnsafeAccessNode).snapshot()) { + if (node instanceof RawStoreNode) { + RawStoreNode store = (RawStoreNode) node; + RawStoreNode newStore = graph.add(new RawStoreNode(store.object(), store.offset(), store.value(), store.accessKind(), NamedLocationIdentity.any(), + store.needsBarrier(), store.stateAfter(), true)); + graph.replaceFixedWithFixed(store, newStore); + } else if (node instanceof RawLoadNode) { + RawLoadNode load = (RawLoadNode) node; + RawLoadNode newLoad = graph.add(new RawLoadNode(load.object(), load.offset(), load.accessKind(), NamedLocationIdentity.any(), + true)); + graph.replaceFixedWithFixed(load, newLoad); + } + } + } + super.canonicalizeGraph(); + } + + @Override + protected void postEACanonicalizeGraph() { + // Simplify any UnpackEndianHalfNode so we end up with constants. + Graph.Mark mark = graph.getMark(); + for (UnpackEndianHalfNode node : graph.getNodes().filter(UnpackEndianHalfNode.class)) { + node.lower(getTarget().arch.getByteOrder()); + } + new CanonicalizerPhase().applyIncremental(graph, context, mark); + } + + private boolean testingUnsafe; + @Test public void testSimpleInt() { testEscapeAnalysis("testSimpleIntSnippet", JavaConstant.forInt(101), false); @@ -89,6 +159,82 @@ public class UnsafeEATest extends EATestBase { return UNSAFE.getDouble(x, fieldOffset1); } + @Test + public void testSimpleDoubleOverwriteWithInt() { + testEscapeAnalysis("testSimpleDoubleOverwriteWithIntSnippet", JavaConstant.forInt(10), false); + } + + public static int testSimpleDoubleOverwriteWithIntSnippet() { + TestClassInt x = new TestClassInt(); + UNSAFE.putDouble(x, fieldOffset1, 10.1); + UNSAFE.putInt(x, fieldOffset1, 10); + return UNSAFE.getInt(x, fieldOffset1); + } + + @Test + public void testSimpleDoubleOverwriteWithSecondInt() { + ByteBuffer bb = ByteBuffer.allocate(8).order(getTarget().arch.getByteOrder()); + bb.putDouble(10.1); + int value = bb.getInt(4); + + testEscapeAnalysis("testSimpleDoubleOverwriteWithSecondIntSnippet", JavaConstant.forInt(value), false); + } + + public static int testSimpleDoubleOverwriteWithSecondIntSnippet() { + TestClassInt x = new TestClassInt(); + UNSAFE.putDouble(x, fieldOffset1, 10.1); + UNSAFE.putInt(x, fieldOffset1, 10); + return UNSAFE.getInt(x, fieldOffset2); + } + + @Test + public void testSimpleDoubleOverwriteWithFirstInt() { + ByteBuffer bb = ByteBuffer.allocate(8).order(getTarget().arch.getByteOrder()); + bb.putDouble(10.1); + int value = bb.getInt(0); + + testEscapeAnalysis("testSimpleDoubleOverwriteWithFirstIntSnippet", JavaConstant.forInt(value), false); + } + + public static int testSimpleDoubleOverwriteWithFirstIntSnippet() { + TestClassInt x = new TestClassInt(); + UNSAFE.putDouble(x, fieldOffset1, 10.1); + UNSAFE.putInt(x, fieldOffset2, 10); + return UNSAFE.getInt(x, fieldOffset1); + } + + @Test + public void testSimpleLongOverwriteWithSecondInt() { + ByteBuffer bb = ByteBuffer.allocate(8).order(getTarget().arch.getByteOrder()); + bb.putLong(0, 0x1122334455667788L); + int value = bb.getInt(4); + + testEscapeAnalysis("testSimpleLongOverwriteWithSecondIntSnippet", JavaConstant.forInt(value), false); + } + + public static int testSimpleLongOverwriteWithSecondIntSnippet() { + TestClassInt x = new TestClassInt(); + UNSAFE.putLong(x, fieldOffset1, 0x1122334455667788L); + UNSAFE.putInt(x, fieldOffset1, 10); + return UNSAFE.getInt(x, fieldOffset2); + } + + @Test + public void testSimpleLongOverwriteWithFirstInt() { + ByteBuffer bb = ByteBuffer.allocate(8).order(getTarget().arch.getByteOrder()); + bb.putLong(0, 0x1122334455667788L); + int value = bb.getInt(0); + + testEscapeAnalysis("testSimpleLongOverwriteWithFirstIntSnippet", JavaConstant.forInt(value), false); + } + + public static int testSimpleLongOverwriteWithFirstIntSnippet() { + TestClassInt x = new TestClassInt(); + UNSAFE.putLong(x, fieldOffset1, 0x1122334455667788L); + UNSAFE.putInt(x, fieldOffset2, 10); + return UNSAFE.getInt(x, fieldOffset1); + } + @Test public void testMergedDouble() { testEscapeAnalysis("testMergedDoubleSnippet", null, false); @@ -111,6 +257,32 @@ public class UnsafeEATest extends EATestBase { return UNSAFE.getDouble(x, fieldOffset1); } + static class ExtendedTestClassInt extends TestClassInt { + public long l; + } + + @Test + public void testMergedVirtualObjects() { + testEscapeAnalysis("testMergedVirtualObjectsSnippet", null, false); + } + + public static TestClassInt testMergedVirtualObjectsSnippet(int value) { + TestClassInt x; + if (value == 1) { + x = new TestClassInt(); + UNSAFE.putDouble(x, fieldOffset1, 10); + } else { + x = new TestClassInt(); + UNSAFE.putInt(x, fieldOffset1, 0); + } + UNSAFE.putInt(x, fieldOffset1, 0); + if (value == 2) { + UNSAFE.putInt(x, fieldOffset2, 0); + } + GraalDirectives.deoptimizeAndInvalidate(); + return x; + } + @Test public void testMaterializedDouble() { test("testMaterializedDoubleSnippet"); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/inlining/NestedLoopEffectsPhaseComplexityTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/inlining/NestedLoopEffectsPhaseComplexityTest.java index 10505f2de92..0b1468bd7d1 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/inlining/NestedLoopEffectsPhaseComplexityTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/inlining/NestedLoopEffectsPhaseComplexityTest.java @@ -146,7 +146,7 @@ public class NestedLoopEffectsPhaseComplexityTest extends GraalCompilerTest { private StructuredGraph parseBytecodes(ResolvedJavaMethod method, HighTierContext context, CanonicalizerPhase canonicalizer) { OptionValues options = getInitialOptions(); - StructuredGraph newGraph = new StructuredGraph.Builder(options, getDebugContext(options), AllowAssumptions.NO).method(method).build(); + StructuredGraph newGraph = new StructuredGraph.Builder(options, getDebugContext(options, null, method), AllowAssumptions.NO).method(method).build(); context.getGraphBuilderSuite().apply(newGraph, context); new DeadCodeEliminationPhase(Optional).apply(newGraph); canonicalizer.apply(newGraph, context); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/GraalCompilerOptions.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/GraalCompilerOptions.java index 44adbe6c112..f19c83ac810 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/GraalCompilerOptions.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/GraalCompilerOptions.java @@ -47,7 +47,7 @@ public class GraalCompilerOptions { public static final EnumOptionKey CompilationFailureAction = new EnumOptionKey<>(ExceptionAction.Diagnose); @Option(help = "The maximum number of compilation failures or bailouts to handle with the action specified " + "by CompilationFailureAction or CompilationBailoutAction before changing to a less verbose action.", type = OptionType.User) - public static final OptionKey MaxCompilationProblemsPerAction = new OptionKey<>(5); + public static final OptionKey MaxCompilationProblemsPerAction = new OptionKey<>(2); @Option(help = "Alias for CompilationFailureAction=ExitVM.", type = OptionType.User) public static final OptionKey ExitVMOnException = new OptionKey<>(false); // @formatter:on diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/gen/DebugInfoBuilder.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/gen/DebugInfoBuilder.java index a48abd8cb62..63dd347b546 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/gen/DebugInfoBuilder.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/gen/DebugInfoBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -132,8 +132,17 @@ public class DebugInfoBuilder { slotKinds[pos] = toSlotKind(value); pos++; } else { - assert currentField.values().get(i - 1).getStackKind() == JavaKind.Double || currentField.values().get(i - 1).getStackKind() == JavaKind.Long : vobjNode + " " + i + " " + - currentField.values().get(i - 1); + assert value.getStackKind() == JavaKind.Illegal; + ValueNode previousValue = currentField.values().get(i - 1); + assert (previousValue != null && previousValue.getStackKind().needsTwoSlots()) : vobjNode + " " + i + + " " + previousValue + " " + currentField.values().snapshot(); + if (previousValue == null || !previousValue.getStackKind().needsTwoSlots()) { + // Don't allow the IllegalConstant to leak into the debug info + JavaKind entryKind = vobjNode.entryKind(i); + values[pos] = JavaConstant.defaultForKind(entryKind.getStackKind()); + slotKinds[pos] = entryKind.getStackKind(); + pos++; + } } } if (pos != entryCount) { @@ -164,19 +173,19 @@ public class DebugInfoBuilder { if (!type.isArray()) { ResolvedJavaField[] fields = type.getInstanceFields(true); int fieldIndex = 0; - for (int i = 0; i < values.length; i++) { - ResolvedJavaField field = fields[fieldIndex++]; - JavaKind valKind = slotKinds[i].getStackKind(); + for (int valueIndex = 0; valueIndex < values.length; valueIndex++, fieldIndex++) { + ResolvedJavaField field = fields[fieldIndex]; + JavaKind valKind = slotKinds[valueIndex].getStackKind(); JavaKind fieldKind = storageKind(field.getType()); - if (fieldKind == JavaKind.Object) { - assert valKind.isObject() : field + ": " + valKind + " != " + fieldKind; + if ((valKind == JavaKind.Double || valKind == JavaKind.Long) && fieldKind == JavaKind.Int) { + assert fieldIndex + 1 < fields.length : String.format("Not enough fields for fieldIndex = %d valueIndex = %d %s %s", fieldIndex, valueIndex, Arrays.toString(fields), + Arrays.toString(values)); + assert storageKind(fields[fieldIndex + 1].getType()) == JavaKind.Int : String.format("fieldIndex = %d valueIndex = %d %s %s %s", fieldIndex, valueIndex, + storageKind(fields[fieldIndex + 1].getType()), Arrays.toString(fields), + Arrays.toString(values)); + fieldIndex++; } else { - if ((valKind == JavaKind.Double || valKind == JavaKind.Long) && fieldKind == JavaKind.Int) { - assert storageKind(fields[fieldIndex].getType()) == JavaKind.Int; - fieldIndex++; - } else { - assert valKind == fieldKind.getStackKind() : field + ": " + valKind + " != " + fieldKind; - } + assert valKind == fieldKind.getStackKind() : field + ": " + valKind + " != " + fieldKind; } } assert fields.length == fieldIndex : type + ": fields=" + Arrays.toString(fields) + ", field values=" + Arrays.toString(values); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/gen/NodeLIRBuilder.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/gen/NodeLIRBuilder.java index b5ca686a857..fffe121e077 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/gen/NodeLIRBuilder.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/gen/NodeLIRBuilder.java @@ -59,6 +59,7 @@ import org.graalvm.compiler.lir.StandardOp.LabelOp; import org.graalvm.compiler.lir.SwitchStrategy; import org.graalvm.compiler.lir.Variable; import org.graalvm.compiler.lir.debug.LIRGenerationDebugContext; +import org.graalvm.compiler.lir.framemap.FrameMapBuilder; import org.graalvm.compiler.lir.gen.LIRGenerator; import org.graalvm.compiler.lir.gen.LIRGenerator.Options; import org.graalvm.compiler.lir.gen.LIRGeneratorTool; @@ -577,9 +578,9 @@ public abstract class NodeLIRBuilder implements NodeLIRBuilderTool, LIRGeneratio @Override public void emitInvoke(Invoke x) { LoweredCallTargetNode callTarget = (LoweredCallTargetNode) x.callTarget(); - CallingConvention invokeCc = gen.getResult().getFrameMapBuilder().getRegisterConfig().getCallingConvention(callTarget.callType(), x.asNode().stamp().javaType(gen.getMetaAccess()), - callTarget.signature(), gen); - gen.getResult().getFrameMapBuilder().callsMethod(invokeCc); + FrameMapBuilder frameMapBuilder = gen.getResult().getFrameMapBuilder(); + CallingConvention invokeCc = frameMapBuilder.getRegisterConfig().getCallingConvention(callTarget.callType(), x.asNode().stamp().javaType(gen.getMetaAccess()), callTarget.signature(), gen); + frameMapBuilder.callsMethod(invokeCc); Value[] parameters = visitInvokeArguments(invokeCc, callTarget.arguments()); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/target/Backend.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/target/Backend.java index c5d39002411..820a221e2e7 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/target/Backend.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/target/Backend.java @@ -195,9 +195,12 @@ public abstract class Backend implements TargetProvider, ValueKindFactory parentOutput; /** * Stores the {@link MetricKey} values. @@ -111,6 +116,19 @@ public final class DebugContext implements AutoCloseable { return immutable.scopesEnabled; } + public GraphOutput buildOutput(GraphOutput.Builder builder) throws IOException { + if (parentOutput != null) { + return builder.build(parentOutput); + } else { + if (sharedChannel == null) { + sharedChannel = new IgvDumpChannel(() -> getDumpPath(".bgv", false), immutable.options); + } + final GraphOutput output = builder.build(sharedChannel); + parentOutput = output; + return output; + } + } + /** * The immutable configuration that can be shared between {@link DebugContext} objects. */ @@ -323,6 +341,14 @@ public final class DebugContext implements AutoCloseable { String compilableName = compilable instanceof JavaMethod ? ((JavaMethod) compilable).format("%H.%n(%p)%R") : String.valueOf(compilable); return identifier + ":" + compilableName; } + + final String getLabel() { + if (compilable instanceof JavaMethod) { + JavaMethod method = (JavaMethod) compilable; + return method.format("%h.%n(%p)%r"); + } + return String.valueOf(compilable); + } } private final Description description; @@ -394,6 +420,20 @@ public final class DebugContext implements AutoCloseable { } } + public Path getDumpPath(String extension, boolean directory) { + try { + String id = description == null ? null : description.identifier; + String label = description == null ? null : description.getLabel(); + Path result = PathUtilities.createUnique(immutable.options, DumpPath, id, label, extension, directory); + if (ShowDumpFiles.getValue(immutable.options)) { + TTY.println("Dumping debug output to %s", result.toAbsolutePath().toString()); + } + return result; + } catch (IOException ex) { + throw rethrowSilently(RuntimeException.class, ex); + } + } + /** * A special dump level that indicates the dumping machinery is enabled but no dumps will be * produced except through other options. @@ -2043,4 +2083,9 @@ public final class DebugContext implements AutoCloseable { } out.println(); } + + @SuppressWarnings({"unused", "unchecked"}) + private static E rethrowSilently(Class type, Throwable ex) throws E { + throw (E) ex; + } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugHandlersFactory.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugHandlersFactory.java index 62fb5668198..169a4418e32 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugHandlersFactory.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugHandlersFactory.java @@ -35,6 +35,9 @@ public interface DebugHandlersFactory { /** * Creates {@link DebugHandler}s based on {@code options}. + * + * @param options options to control type and name of the channel + * @return list of debug handers that have been created */ List createHandlers(OptionValues options); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugOptions.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugOptions.java index 5aee4ccecb0..0df3e7311b1 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugOptions.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugOptions.java @@ -128,8 +128,6 @@ public class DebugOptions { public static final OptionKey PrintGraphProbabilities = new OptionKey<>(false); @Option(help = "Enable dumping to the IdealGraphVisualizer.", type = OptionType.Debug) public static final OptionKey PrintGraph = new OptionKey<>(true); - @Option(help = "Dump graphs in binary format instead of XML format.", type = OptionType.Debug) - public static final OptionKey PrintBinaryGraphs = new OptionKey<>(true); @Option(help = "Print graphs to files instead of sending them over the network.", type = OptionType.Debug) public static final OptionKey PrintGraphFile = new OptionKey<>(false); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/IgvDumpChannel.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/IgvDumpChannel.java new file mode 100644 index 00000000000..e7dc72c7ab7 --- /dev/null +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/IgvDumpChannel.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2017, 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 org.graalvm.compiler.debug; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.ClosedByInterruptException; +import java.nio.channels.FileChannel; +import java.nio.channels.SocketChannel; +import java.nio.channels.WritableByteChannel; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.function.Supplier; +import static org.graalvm.compiler.debug.DebugOptions.PrintBinaryGraphPort; +import static org.graalvm.compiler.debug.DebugOptions.PrintGraphHost; +import org.graalvm.compiler.options.OptionValues; + +final class IgvDumpChannel implements WritableByteChannel { + private final Supplier pathProvider; + private final OptionValues options; + private WritableByteChannel sharedChannel; + private boolean closed; + + IgvDumpChannel(Supplier pathProvider, OptionValues options) { + this.pathProvider = pathProvider; + this.options = options; + } + + @Override + public int write(ByteBuffer src) throws IOException { + return channel().write(src); + } + + @Override + public boolean isOpen() { + return !closed; + } + + @Override + public void close() throws IOException { + } + + void realClose() throws IOException { + closed = true; + if (sharedChannel != null) { + sharedChannel.close(); + sharedChannel = null; + } + } + + WritableByteChannel channel() throws IOException { + if (closed) { + throw new IOException(); + } + if (sharedChannel == null) { + if (DebugOptions.PrintGraphFile.getValue(options)) { + sharedChannel = createFileChannel(pathProvider); + } else { + sharedChannel = createNetworkChannel(pathProvider, options); + } + } + return sharedChannel; + } + + private static WritableByteChannel createNetworkChannel(Supplier pathProvider, OptionValues options) throws IOException { + String host = PrintGraphHost.getValue(options); + int port = PrintBinaryGraphPort.getValue(options); + try { + WritableByteChannel channel = SocketChannel.open(new InetSocketAddress(host, port)); + TTY.println("Connected to the IGV on %s:%d", host, port); + return channel; + } catch (ClosedByInterruptException | InterruptedIOException e) { + /* + * Interrupts should not count as errors because they may be caused by a cancelled Graal + * compilation. ClosedByInterruptException occurs if the SocketChannel could not be + * opened. InterruptedIOException occurs if new Socket(..) was interrupted. + */ + return null; + } catch (IOException e) { + if (!DebugOptions.PrintGraphFile.hasBeenSet(options)) { + return createFileChannel(pathProvider); + } else { + throw new IOException(String.format("Could not connect to the IGV on %s:%d", host, port), e); + } + } + } + + private static WritableByteChannel createFileChannel(Supplier pathProvider) throws IOException { + Path path = pathProvider.get(); + try { + return FileChannel.open(path, StandardOpenOption.WRITE, StandardOpenOption.CREATE); + } catch (IOException e) { + throw new IOException(String.format("Failed to open %s to dump IGV graphs", path), e); + } + } + +} diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/PathUtilities.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/PathUtilities.java index 54bb4e78144..e9f1e216faf 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/PathUtilities.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/PathUtilities.java @@ -22,12 +22,13 @@ */ package org.graalvm.compiler.debug; +import java.io.File; import java.io.IOException; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.HashMap; -import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import org.graalvm.compiler.options.OptionKey; @@ -39,54 +40,6 @@ import org.graalvm.compiler.options.OptionValues; public class PathUtilities { private static final AtomicLong globalTimeStamp = new AtomicLong(); - /** - * This generates a per thread persistent id to aid mapping related dump files with each other. - */ - private static final ThreadLocal threadDumpId = new ThreadLocal<>(); - private static final AtomicInteger dumpId = new AtomicInteger(); - - static class PerThreadSequence { - final int threadID; - HashMap sequences = new HashMap<>(2); - - PerThreadSequence(int threadID) { - this.threadID = threadID; - } - - String generateID(String extension) { - Integer box = sequences.get(extension); - if (box == null) { - sequences.put(extension, 1); - return Integer.toString(threadID); - } else { - sequences.put(extension, box + 1); - return Integer.toString(threadID) + '-' + box; - } - } - } - - private static String getThreadDumpId(String extension) { - PerThreadSequence id = threadDumpId.get(); - if (id == null) { - id = new PerThreadSequence(dumpId.incrementAndGet()); - threadDumpId.set(id); - } - return id.generateID(extension); - } - - /** - * Prepends a period (i.e., {@code '.'}) to an non-null, non-empty string representation a file - * extension if the string does not already start with a period. - * - * @return {@code ext} unmodified if it is null, empty or already starts with a period other - * {@code "." + ext} - */ - public static String formatExtension(String ext) { - if (ext == null || ext.length() == 0) { - return ""; - } - return "." + ext; - } /** * Gets a time stamp for the current process. This method will always return the same value for @@ -99,43 +52,6 @@ public class PathUtilities { return globalTimeStamp.get(); } - /** - * Generates a {@link Path} using the format "%s-%d_%d%s" with the {@code baseNameOption}, a - * {@link #getGlobalTimeStamp() global timestamp} , {@link #getThreadDumpId a per thread unique - * id} and an optional {@code extension}. - * - * @return the output file path or null if the flag is null - */ - public static Path getPath(OptionValues options, OptionKey baseNameOption, String extension) throws IOException { - return getPath(options, baseNameOption, extension, true); - } - - /** - * Generate a {@link Path} using the format "%s-%d_%s" with the {@code baseNameOption}, a - * {@link #getGlobalTimeStamp() global timestamp} and an optional {@code extension} . - * - * @return the output file path or null if the flag is null - */ - public static Path getPathGlobal(OptionValues options, OptionKey baseNameOption, String extension) throws IOException { - return getPath(options, baseNameOption, extension, false); - } - - private static Path getPath(OptionValues options, OptionKey baseNameOption, String extension, boolean includeThreadId) throws IOException { - if (baseNameOption.getValue(options) == null) { - return null; - } - String ext = formatExtension(extension); - final String name = includeThreadId - ? String.format("%s-%d_%s%s", baseNameOption.getValue(options), getGlobalTimeStamp(), getThreadDumpId(ext), ext) - : String.format("%s-%d%s", baseNameOption.getValue(options), getGlobalTimeStamp(), ext); - Path result = Paths.get(name); - if (result.isAbsolute()) { - return result; - } - Path dumpDir = DebugOptions.getDumpDirectory(options); - return dumpDir.resolve(name).normalize(); - } - /** * Gets a value based on {@code name} that can be passed to {@link Paths#get(String, String...)} * without causing an {@link InvalidPathException}. @@ -145,21 +61,80 @@ public class PathUtilities { */ public static String sanitizeFileName(String name) { try { - Paths.get(name); - return name; + Path path = Paths.get(name); + if (path.getNameCount() == 0) { + return name; + } } catch (InvalidPathException e) { // fall through } StringBuilder buf = new StringBuilder(name.length()); for (int i = 0; i < name.length(); i++) { char c = name.charAt(i); - try { - Paths.get(String.valueOf(c)); - } catch (InvalidPathException e) { - buf.append('_'); + if (c != File.separatorChar && c != ' ' && !Character.isISOControl(c)) { + try { + Paths.get(String.valueOf(c)); + buf.append(c); + continue; + } catch (InvalidPathException e) { + } } - buf.append(c); + buf.append('_'); } return buf.toString(); } + + /** + * A maximum file name length supported by most file systems. There is no platform independent + * way to get this in Java. + */ + private static final int MAX_FILE_NAME_LENGTH = 255; + + private static final String ELLIPSIS = "..."; + + static Path createUnique(OptionValues options, OptionKey baseNameOption, String id, String label, String ext, boolean createDirectory) throws IOException { + String uniqueTag = ""; + int dumpCounter = 1; + String prefix; + if (id == null) { + prefix = baseNameOption.getValue(options); + int slash = prefix.lastIndexOf(File.separatorChar); + prefix = prefix.substring(slash + 1); + } else { + prefix = id; + } + for (;;) { + int fileNameLengthWithoutLabel = uniqueTag.length() + ext.length() + prefix.length() + "[]".length(); + int labelLengthLimit = MAX_FILE_NAME_LENGTH - fileNameLengthWithoutLabel; + String fileName; + if (labelLengthLimit < ELLIPSIS.length()) { + // This means `id` is very long + String suffix = uniqueTag + ext; + int idLengthLimit = Math.min(MAX_FILE_NAME_LENGTH - suffix.length(), prefix.length()); + fileName = sanitizeFileName(prefix.substring(0, idLengthLimit) + suffix); + } else { + if (label == null) { + fileName = sanitizeFileName(prefix + uniqueTag + ext); + } else { + String adjustedLabel = label; + if (label.length() > labelLengthLimit) { + adjustedLabel = label.substring(0, labelLengthLimit - ELLIPSIS.length()) + ELLIPSIS; + } + fileName = sanitizeFileName(prefix + '[' + adjustedLabel + ']' + uniqueTag + ext); + } + } + Path dumpDir = DebugOptions.getDumpDirectory(options); + Path result = Paths.get(dumpDir.toString(), fileName); + try { + if (createDirectory) { + return Files.createDirectory(result); + } else { + return Files.createFile(result); + } + } catch (FileAlreadyExistsException e) { + uniqueTag = "_" + dumpCounter++; + } + } + } + } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph.test/src/org/graalvm/compiler/graph/test/graphio/GraphSnippetTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph.test/src/org/graalvm/compiler/graph/test/graphio/GraphSnippetTest.java new file mode 100644 index 00000000000..b3c790e3926 --- /dev/null +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph.test/src/org/graalvm/compiler/graph/test/graphio/GraphSnippetTest.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2017, 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 org.graalvm.compiler.graph.test.graphio; + +import java.io.File; +import java.lang.reflect.Method; +import static org.junit.Assert.assertTrue; +import org.junit.Assume; +import org.junit.Test; + +public class GraphSnippetTest { + @Test + public void dumpTheFile() throws Exception { + Class snippets = null; + try { + snippets = Class.forName("org.graalvm.graphio.GraphSnippets"); + } catch (ClassNotFoundException notFound) { + Assume.assumeNoException("The snippets class has to be around", notFound); + } + Method dump = null; + try { + dump = snippets.getDeclaredMethod("dump", File.class); + dump.setAccessible(true); + } catch (RuntimeException ex) { + Assume.assumeTrue("Only run the test, if the method is accessible", dump != null && dump.isAccessible()); + } + File diamond = File.createTempFile("diamond", ".bgv"); + dump.invoke(null, diamond); + assertTrue("File .bgv created: " + diamond, diamond.length() > 50); + } +} diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph.test/src/org/graalvm/compiler/graph/test/graphio/NodeEncodingTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph.test/src/org/graalvm/compiler/graph/test/graphio/NodeEncodingTest.java new file mode 100644 index 00000000000..a5e4c6862ea --- /dev/null +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph.test/src/org/graalvm/compiler/graph/test/graphio/NodeEncodingTest.java @@ -0,0 +1,311 @@ +/* + * Copyright (c) 2017, 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 org.graalvm.compiler.graph.test.graphio; + +import java.io.ByteArrayOutputStream; +import java.nio.channels.Channels; +import java.nio.channels.WritableByteChannel; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import org.graalvm.graphio.GraphOutput; +import org.graalvm.graphio.GraphStructure; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import org.junit.Before; +import org.junit.Test; + +public final class NodeEncodingTest { + + private ByteArrayOutputStream out; + + @Before + public void initOutput() { + out = new ByteArrayOutputStream(); + } + + @Test + public void version40TheNodeIsntDumpedWithItsID() throws Exception { + runTheNodeIsntDumpedWithItsID(true); + } + + @Test + public void defaultVersionTheNodeIsntDumpedWithItsID() throws Exception { + runTheNodeIsntDumpedWithItsID(false); + } + + private void runTheNodeIsntDumpedWithItsID(boolean explicitVersion) throws Exception { + WritableByteChannel w = Channels.newChannel(out); + MockGraph graph = new MockGraph(); + MockNodeClass clazz = new MockNodeClass("clazz"); + MockNode node = new MockNode(clazz, 33); // random value otherwise not found in the stream + try (GraphOutput dump = explicitVersion ? GraphOutput.newBuilder(new MockStructure()).protocolVersion(4, 0).build(w) : GraphOutput.newBuilder(new MockStructure()).build(w)) { + dump.beginGroup(graph, "test1", "t1", null, 0, Collections.singletonMap("node", node)); + dump.endGroup(); + } + + assertEquals("Node is always requested", 1, node.nodeRequested); + assertEquals("Nobody asks for id of a node in version 4.0", 0, node.idTested); + assertByte(false, out.toByteArray(), 33); + assertEquals("Node class of the node has been requested", 1, node.nodeClassRequested); + assertEquals("Node class template name stored", 1, clazz.nameTemplateQueried); + assertFalse("No to string ops", node.toStringRequested); + } + + @Test + public void dumpingNodeInVersion10() throws Exception { + runTheNodeIsTreatedAsString(true); + } + + private void runTheNodeIsTreatedAsString(boolean explicitVersion) throws Exception { + WritableByteChannel w = Channels.newChannel(out); + MockGraph graph = new MockGraph(); + MockNodeClass clazz = new MockNodeClass("clazz"); + MockNode node = new MockNode(clazz, 33); // random value otherwise not found in the stream + try (GraphOutput dump = explicitVersion ? GraphOutput.newBuilder(new MockStructure()).protocolVersion(1, 0).build(w) : GraphOutput.newBuilder(new MockStructure()).build(w)) { + dump.beginGroup(graph, "test1", "t1", null, 0, Collections.singletonMap("node", node)); + dump.endGroup(); + } + + assertEquals("Node is always requested", 1, node.nodeRequested); + assertEquals("Nobody asks for id of a node in version 1.0", 0, node.idTested); + assertByte(false, out.toByteArray(), 33); + assertEquals("Node class was needed to find out it is not a NodeClass instance", 1, node.nodeClassRequested); + assertEquals("Node class template name wasn't needed however", 0, clazz.nameTemplateQueried); + assertTrue("Node sent as a string version 1.0", node.toStringRequested); + } + + @Test + public void dumpingNodeInVersion15() throws Exception { + runTheNodeIsTreatedPoolEntry(true); + } + + private void runTheNodeIsTreatedPoolEntry(boolean explicitVersion) throws Exception { + WritableByteChannel w = Channels.newChannel(out); + MockGraph graph = new MockGraph(); + MockNodeClass clazz = new MockNodeClass("clazz"); + MockNode node = new MockNode(clazz, 33); // random value otherwise not found in the stream + try (GraphOutput dump = explicitVersion ? GraphOutput.newBuilder(new MockStructure()).protocolVersion(5, 0).build(w) : GraphOutput.newBuilder(new MockStructure()).build(w)) { + dump.beginGroup(graph, "test1", "t1", null, 0, Collections.singletonMap("node", node)); + dump.endGroup(); + } + + assertEquals("Node is always requested", 1, node.nodeRequested); + assertEquals("Id of our node is requested in version 5.0", 1, node.idTested); + assertByte(true, out.toByteArray(), 33); + assertTrue("Node class was needed at least once", 1 <= node.nodeClassRequested); + assertEquals("Node class template name sent to server", 1, clazz.nameTemplateQueried); + assertFalse("Node.toString() isn't needed", node.toStringRequested); + } + + @Test + public void dumpingNodeTwiceInVersion4() throws Exception { + WritableByteChannel w = Channels.newChannel(out); + MockGraph graph = new MockGraph(); + MockNodeClass clazz = new MockNodeClass("clazz"); + MockNode node = new MockNode(clazz, 33); // random value otherwise not found in the stream + try (GraphOutput dump = GraphOutput.newBuilder(new MockStructure()).protocolVersion(4, 0).build(w)) { + Map props = new LinkedHashMap<>(); + props.put("node1", node); + props.put("node2", node); + props.put("node3", node); + dump.beginGroup(graph, "test1", "t1", null, 0, props); + dump.endGroup(); + } + + assertEquals("Node requested three times", 3, node.nodeRequested); + assertEquals("Nobody asks for id of a node in version 4.0", 0, node.idTested); + // check there is no encoded string for object #3 + assertByte(false, out.toByteArray(), 1, 0, 3); + assertEquals("Node class of the node has been requested three times", 3, node.nodeClassRequested); + assertEquals("Node class template name stored", 1, clazz.nameTemplateQueried); + assertFalse("No to string ops", node.toStringRequested); + } + + private static void assertByte(boolean shouldBeFound, byte[] arr, int... value) { + boolean found = false; + int at = 0; + for (int i = 0; i < arr.length; i++) { + if (arr[i] == value[at]) { + if (++at == value.length) { + found = true; + break; + } + } else { + at = 0; + } + } + if (shouldBeFound == found) { + return; + } + if (shouldBeFound) { + fail("Value " + value + " not found in\n" + Arrays.toString(arr)); + } else { + fail("Value " + value + " surprisingly found in\n" + Arrays.toString(arr)); + } + } + + private static final class MockStructure implements GraphStructure { + + @Override + public MockGraph graph(MockGraph currentGraph, Object obj) { + return obj instanceof MockGraph ? (MockGraph) obj : null; + } + + @Override + public Iterable nodes(MockGraph graph) { + return Collections.emptyList(); + } + + @Override + public int nodesCount(MockGraph graph) { + return 0; + } + + @Override + public int nodeId(MockNode node) { + node.idTested++; + return node.id; + } + + @Override + public boolean nodeHasPredecessor(MockNode node) { + return false; + } + + @Override + public void nodeProperties(MockGraph graph, MockNode node, Map properties) { + } + + @Override + public MockNode node(Object obj) { + if (obj instanceof MockNode) { + ((MockNode) obj).nodeRequested++; + return (MockNode) obj; + } + return null; + } + + @Override + public MockNodeClass nodeClass(Object obj) { + if (obj instanceof MockNode) { + ((MockNode) obj).nodeClassRequested++; + } + return obj instanceof MockNodeClass ? (MockNodeClass) obj : null; + } + + @Override + public MockNodeClass classForNode(MockNode n) { + n.nodeClassRequested++; + return n.clazz; + } + + @Override + public String nameTemplate(MockNodeClass nodeClass) { + nodeClass.nameTemplateQueried++; + return ""; + } + + @Override + public Object nodeClassType(MockNodeClass nodeClass) { + return nodeClass.getClass(); + } + + @Override + public MockNodeClass portInputs(MockNodeClass nodeClass) { + return nodeClass; + } + + @Override + public MockNodeClass portOutputs(MockNodeClass nodeClass) { + return nodeClass; + } + + @Override + public int portSize(MockNodeClass port) { + return 0; + } + + @Override + public boolean edgeDirect(MockNodeClass port, int index) { + return false; + } + + @Override + public String edgeName(MockNodeClass port, int index) { + return null; + } + + @Override + public Object edgeType(MockNodeClass port, int index) { + return null; + } + + @Override + public Collection edgeNodes(MockGraph graph, MockNode node, MockNodeClass port, int index) { + return null; + } + } + + private static final class MockGraph { + + } + + private static final class MockNode { + final MockNodeClass clazz; + final int id; + int idTested; + int nodeClassRequested; + int nodeRequested; + boolean toStringRequested; + + MockNode(MockNodeClass clazz, int id) { + this.clazz = clazz; + this.id = id; + } + + @Override + public String toString() { + this.toStringRequested = true; + return "MockNode{" + "id=" + id + ", class=" + clazz + '}'; + } + } + + private static final class MockNodeClass { + final String name; + int nameTemplateQueried; + + MockNodeClass(String name) { + this.name = name; + } + + @Override + public String toString() { + return "MockNodeClass{" + "name=" + name + '}'; + } + } +} diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/Graph.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/Graph.java index 509f11841f6..092a84a3aff 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/Graph.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/Graph.java @@ -514,30 +514,61 @@ public class Graph { /** * A node was added to a graph. */ - NODE_ADDED; + NODE_ADDED, + + /** + * A node was removed from the graph. + */ + NODE_REMOVED; } /** * Client interested in one or more node related events. */ - public interface NodeEventListener { + public abstract static class NodeEventListener { /** - * Default handler for events. + * A method called when a change event occurs. + * + * This method dispatches the event to user-defined triggers. The methods that change the + * graph (typically in Graph and Node) must call this method to dispatch the event. * * @param e an event * @param node the node related to {@code e} */ - default void event(NodeEvent e, Node node) { + final void event(NodeEvent e, Node node) { + switch (e) { + case INPUT_CHANGED: + inputChanged(node); + break; + case ZERO_USAGES: + usagesDroppedToZero(node); + break; + case NODE_ADDED: + nodeAdded(node); + break; + case NODE_REMOVED: + nodeRemoved(node); + break; + } + changed(e, node); } /** - * Notifies this listener of a change in a node's inputs. + * Notifies this listener about any change event in the graph. + * + * @param e an event + * @param node the node related to {@code e} + */ + public void changed(NodeEvent e, Node node) { + } + + /** + * Notifies this listener about a change in a node's inputs. * * @param node a node who has had one of its inputs changed */ - default void inputChanged(Node node) { - event(NodeEvent.INPUT_CHANGED, node); + public void inputChanged(Node node) { } /** @@ -545,8 +576,7 @@ public class Graph { * * @param node a node whose {@link Node#usages()} just became empty */ - default void usagesDroppedToZero(Node node) { - event(NodeEvent.ZERO_USAGES, node); + public void usagesDroppedToZero(Node node) { } /** @@ -554,8 +584,15 @@ public class Graph { * * @param node a node that was just added to the graph */ - default void nodeAdded(Node node) { - event(NodeEvent.NODE_ADDED, node); + public void nodeAdded(Node node) { + } + + /** + * Notifies this listener of a removed node. + * + * @param node + */ + public void nodeRemoved(Node node) { } } @@ -583,7 +620,7 @@ public class Graph { } } - private static class ChainedNodeEventListener implements NodeEventListener { + private static class ChainedNodeEventListener extends NodeEventListener { NodeEventListener head; NodeEventListener next; @@ -595,20 +632,32 @@ public class Graph { @Override public void nodeAdded(Node node) { - head.nodeAdded(node); - next.nodeAdded(node); + head.event(NodeEvent.NODE_ADDED, node); + next.event(NodeEvent.NODE_ADDED, node); } @Override public void inputChanged(Node node) { - head.inputChanged(node); - next.inputChanged(node); + head.event(NodeEvent.INPUT_CHANGED, node); + next.event(NodeEvent.INPUT_CHANGED, node); } @Override public void usagesDroppedToZero(Node node) { - head.usagesDroppedToZero(node); - next.usagesDroppedToZero(node); + head.event(NodeEvent.ZERO_USAGES, node); + next.event(NodeEvent.ZERO_USAGES, node); + } + + @Override + public void nodeRemoved(Node node) { + head.event(NodeEvent.NODE_REMOVED, node); + next.event(NodeEvent.NODE_REMOVED, node); + } + + @Override + public void changed(NodeEvent e, Node node) { + head.event(e, node); + next.event(e, node); } } @@ -1023,7 +1072,7 @@ public class Graph { updateNodeCaches(node); if (nodeEventListener != null) { - nodeEventListener.nodeAdded(node); + nodeEventListener.event(NodeEvent.NODE_ADDED, node); } afterRegister(node); } @@ -1085,6 +1134,10 @@ public class Graph { nodes[node.id] = null; nodesDeletedSinceLastCompression++; + if (nodeEventListener != null) { + nodeEventListener.event(NodeEvent.NODE_ADDED, node); + } + // nodes aren't removed from the type cache here - they will be removed during iteration } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/Node.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/Node.java index fa468561191..0f0c41299af 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/Node.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/Node.java @@ -752,7 +752,7 @@ public abstract class Node implements Cloneable, Formattable, NodeInterface { assert !graph.isFrozen(); NodeEventListener listener = graph.nodeEventListener; if (listener != null) { - listener.inputChanged(node); + listener.event(Graph.NodeEvent.INPUT_CHANGED, node); } } } @@ -762,7 +762,7 @@ public abstract class Node implements Cloneable, Formattable, NodeInterface { assert !graph.isFrozen(); NodeEventListener listener = graph.nodeEventListener; if (listener != null && node.isAlive()) { - listener.usagesDroppedToZero(node); + listener.event(Graph.NodeEvent.ZERO_USAGES, node); } } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotNodeLIRBuilder.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotNodeLIRBuilder.java index 07ea09963ea..7865e886a87 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotNodeLIRBuilder.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotNodeLIRBuilder.java @@ -183,8 +183,7 @@ public class AArch64HotSpotNodeLIRBuilder extends AArch64NodeLIRBuilder implemen sig[i] = node.arguments().get(i).stamp().javaType(gen.getMetaAccess()); } - Value[] parameters = visitInvokeArguments(gen.getResult().getFrameMapBuilder().getRegisterConfig().getCallingConvention(HotSpotCallingConventionType.JavaCall, null, sig, gen), - node.arguments()); + Value[] parameters = visitInvokeArguments(gen.getRegisterConfig().getCallingConvention(HotSpotCallingConventionType.JavaCall, null, sig, gen), node.arguments()); append(new AArch64BreakpointOp(parameters)); } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotAddressLowering.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotAddressLowering.java index a26ed27391c..70bf4dae8c9 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotAddressLowering.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotAddressLowering.java @@ -32,7 +32,6 @@ import org.graalvm.compiler.core.amd64.AMD64AddressLowering; import org.graalvm.compiler.core.amd64.AMD64AddressNode; import org.graalvm.compiler.core.common.CompressEncoding; import org.graalvm.compiler.core.common.LIRKind; -import org.graalvm.compiler.core.common.NumUtil; import org.graalvm.compiler.core.common.type.ObjectStamp; import org.graalvm.compiler.core.common.type.StampFactory; import org.graalvm.compiler.debug.CounterKey; @@ -44,6 +43,7 @@ import org.graalvm.compiler.hotspot.nodes.type.KlassPointerStamp; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.CompressionNode; import org.graalvm.compiler.nodes.CompressionNode.CompressionOp; +import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.FloatingNode; import org.graalvm.compiler.nodes.spi.LIRLowerable; @@ -93,76 +93,76 @@ public class AMD64HotSpotAddressLowering extends AMD64AddressLowering { } @Override - protected boolean improve(DebugContext debug, AMD64AddressNode addr) { - - boolean result = false; - - while (super.improve(debug, addr)) { - result = true; + protected boolean improve(StructuredGraph graph, DebugContext debug, AMD64AddressNode addr, boolean isBaseNegated, boolean isIndexNegated) { + if (super.improve(graph, debug, addr, isBaseNegated, isIndexNegated)) { + return true; } if (addr.getScale() == Scale.Times1) { if (addr.getIndex() instanceof CompressionNode) { - if (improveUncompression(addr, (CompressionNode) addr.getIndex(), addr.getBase())) { + if (improveUncompression(addr, (CompressionNode) addr.getIndex(), addr.getBase(), isBaseNegated, isIndexNegated)) { counterFoldedUncompressDuringAddressLowering.increment(debug); return true; } } if (addr.getBase() instanceof CompressionNode) { - if (improveUncompression(addr, (CompressionNode) addr.getBase(), addr.getIndex())) { + if (improveUncompression(addr, (CompressionNode) addr.getBase(), addr.getIndex(), isBaseNegated, isIndexNegated)) { counterFoldedUncompressDuringAddressLowering.increment(debug); return true; } } } - return result; + return false; } - private boolean improveUncompression(AMD64AddressNode addr, CompressionNode compression, ValueNode other) { - if (compression.getOp() == CompressionOp.Uncompress) { - CompressEncoding encoding = compression.getEncoding(); - Scale scale = Scale.fromShift(encoding.getShift()); - if (scale == null) { + @Override + protected boolean mightBeOptimized(ValueNode value) { + return super.mightBeOptimized(value) || value instanceof CompressionNode; + } + + private boolean improveUncompression(AMD64AddressNode addr, CompressionNode compression, ValueNode other, boolean isBaseNegated, boolean isIndexNegated) { + if (isBaseNegated || isIndexNegated || compression.getOp() != CompressionOp.Uncompress) { + return false; + } + + CompressEncoding encoding = compression.getEncoding(); + Scale scale = Scale.fromShift(encoding.getShift()); + if (scale == null) { + return false; + } + + if (heapBaseRegister != null && encoding.getBase() == heapBase) { + if ((!generatePIC || compression.stamp() instanceof ObjectStamp) && other == null) { + // With PIC it is only legal to do for oops since the base value may be + // different at runtime. + ValueNode base = compression.graph().unique(new HeapBaseNode(heapBaseRegister)); + addr.setBase(base); + } else { return false; } - - if (heapBaseRegister != null && encoding.getBase() == heapBase) { - if ((!generatePIC || compression.stamp() instanceof ObjectStamp) && other == null) { - // With PIC it is only legal to do for oops since the base value may be - // different at runtime. - ValueNode base = compression.graph().unique(new HeapBaseNode(heapBaseRegister)); + } else if (encoding.getBase() != 0 || (generatePIC && compression.stamp() instanceof KlassPointerStamp)) { + if (generatePIC) { + if (other == null) { + ValueNode base = compression.graph().unique(new GraalHotSpotVMConfigNode(config, config.MARKID_NARROW_KLASS_BASE_ADDRESS, JavaKind.Long)); addr.setBase(base); } else { return false; } - } else if (encoding.getBase() != 0 || (generatePIC && compression.stamp() instanceof KlassPointerStamp)) { - if (generatePIC) { - if (other == null) { - ValueNode base = compression.graph().unique(new GraalHotSpotVMConfigNode(config, config.MARKID_NARROW_KLASS_BASE_ADDRESS, JavaKind.Long)); - addr.setBase(base); - } else { - return false; - } - } else { - long disp = addr.getDisplacement() + encoding.getBase(); - if (NumUtil.isInt(disp)) { - addr.setDisplacement((int) disp); - addr.setBase(other); - } else { - return false; - } - } } else { - addr.setBase(other); + if (updateDisplacement(addr, encoding.getBase(), isBaseNegated)) { + addr.setBase(other); + } else { + return false; + } } - - addr.setScale(scale); - addr.setIndex(compression.getValue()); - return true; } else { - return false; + addr.setBase(other); } + + addr.setScale(scale); + addr.setIndex(compression.getValue()); + return true; } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotLIRGenerator.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotLIRGenerator.java index 22844e93006..3a26ff997a7 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotLIRGenerator.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotLIRGenerator.java @@ -39,7 +39,6 @@ import java.util.List; import org.graalvm.compiler.asm.amd64.AMD64Address.Scale; import org.graalvm.compiler.core.amd64.AMD64ArithmeticLIRGenerator; import org.graalvm.compiler.core.amd64.AMD64LIRGenerator; -import org.graalvm.compiler.core.amd64.AMD64LIRKindTool; import org.graalvm.compiler.core.amd64.AMD64MoveFactoryBase.BackupSlotProvider; import org.graalvm.compiler.core.common.CompressEncoding; import org.graalvm.compiler.core.common.LIRKind; @@ -116,7 +115,7 @@ public class AMD64HotSpotLIRGenerator extends AMD64LIRGenerator implements HotSp } private AMD64HotSpotLIRGenerator(HotSpotProviders providers, GraalHotSpotVMConfig config, LIRGenerationResult lirGenRes, BackupSlotProvider backupSlotProvider) { - this(new AMD64LIRKindTool(), new AMD64HotSpotArithmeticLIRGenerator(), new AMD64HotSpotMoveFactory(backupSlotProvider), providers, config, lirGenRes); + this(new AMD64HotSpotLIRKindTool(), new AMD64HotSpotArithmeticLIRGenerator(), new AMD64HotSpotMoveFactory(backupSlotProvider), providers, config, lirGenRes); } protected AMD64HotSpotLIRGenerator(LIRKindTool lirKindTool, AMD64ArithmeticLIRGenerator arithmeticLIRGen, MoveFactory moveFactory, HotSpotProviders providers, GraalHotSpotVMConfig config, @@ -363,7 +362,7 @@ public class AMD64HotSpotLIRGenerator extends AMD64LIRGenerator implements HotSp Stub stub = getStub(); if (destroysRegisters) { if (stub != null && stub.preservesRegisters()) { - Register[] savedRegisters = getResult().getFrameMapBuilder().getRegisterConfig().getAllocatableRegisters().toArray(); + Register[] savedRegisters = getRegisterConfig().getAllocatableRegisters().toArray(); save = emitSaveAllRegisters(savedRegisters, true); } } @@ -567,28 +566,29 @@ public class AMD64HotSpotLIRGenerator extends AMD64LIRGenerator implements HotSp @Override public Value emitCompress(Value pointer, CompressEncoding encoding, boolean nonNull) { LIRKind inputKind = pointer.getValueKind(LIRKind.class); - assert inputKind.getPlatformKind() == AMD64Kind.QWORD; + LIRKindTool lirKindTool = getLIRKindTool(); + assert inputKind.getPlatformKind() == lirKindTool.getObjectKind().getPlatformKind(); if (inputKind.isReference(0)) { // oop - Variable result = newVariable(LIRKind.reference(AMD64Kind.DWORD)); - append(new AMD64HotSpotMove.CompressPointer(result, asAllocatable(pointer), getProviders().getRegisters().getHeapBaseRegister().asValue(), encoding, nonNull)); + Variable result = newVariable(lirKindTool.getNarrowOopKind()); + append(new AMD64Move.CompressPointer(result, asAllocatable(pointer), getProviders().getRegisters().getHeapBaseRegister().asValue(), encoding, nonNull, getLIRKindTool())); return result; } else { // metaspace pointer - Variable result = newVariable(LIRKind.value(AMD64Kind.DWORD)); + Variable result = newVariable(lirKindTool.getNarrowPointerKind()); AllocatableValue base = Value.ILLEGAL; OptionValues options = getResult().getLIR().getOptions(); if (encoding.hasBase() || GeneratePIC.getValue(options)) { if (GeneratePIC.getValue(options)) { - Variable baseAddress = newVariable(LIRKind.value(AMD64Kind.QWORD)); + Variable baseAddress = newVariable(lirKindTool.getWordKind()); AMD64HotSpotMove.BaseMove move = new AMD64HotSpotMove.BaseMove(baseAddress, config); append(move); base = baseAddress; } else { - base = emitLoadConstant(LIRKind.value(AMD64Kind.QWORD), JavaConstant.forLong(encoding.getBase())); + base = emitLoadConstant(lirKindTool.getWordKind(), JavaConstant.forLong(encoding.getBase())); } } - append(new AMD64HotSpotMove.CompressPointer(result, asAllocatable(pointer), base, encoding, nonNull)); + append(new AMD64Move.CompressPointer(result, asAllocatable(pointer), base, encoding, nonNull, getLIRKindTool())); return result; } } @@ -596,35 +596,37 @@ public class AMD64HotSpotLIRGenerator extends AMD64LIRGenerator implements HotSp @Override public Value emitUncompress(Value pointer, CompressEncoding encoding, boolean nonNull) { LIRKind inputKind = pointer.getValueKind(LIRKind.class); - assert inputKind.getPlatformKind() == AMD64Kind.DWORD; + LIRKindTool lirKindTool = getLIRKindTool(); + assert inputKind.getPlatformKind() == lirKindTool.getNarrowOopKind().getPlatformKind(); if (inputKind.isReference(0)) { // oop - Variable result = newVariable(LIRKind.reference(AMD64Kind.QWORD)); - append(new AMD64HotSpotMove.UncompressPointer(result, asAllocatable(pointer), getProviders().getRegisters().getHeapBaseRegister().asValue(), encoding, nonNull)); + Variable result = newVariable(lirKindTool.getObjectKind()); + append(new AMD64Move.UncompressPointer(result, asAllocatable(pointer), getProviders().getRegisters().getHeapBaseRegister().asValue(), encoding, nonNull, lirKindTool)); return result; } else { // metaspace pointer - Variable result = newVariable(LIRKind.value(AMD64Kind.QWORD)); + LIRKind uncompressedKind = lirKindTool.getWordKind(); + Variable result = newVariable(uncompressedKind); AllocatableValue base = Value.ILLEGAL; OptionValues options = getResult().getLIR().getOptions(); if (encoding.hasBase() || GeneratePIC.getValue(options)) { if (GeneratePIC.getValue(options)) { - Variable baseAddress = newVariable(LIRKind.value(AMD64Kind.QWORD)); + Variable baseAddress = newVariable(uncompressedKind); AMD64HotSpotMove.BaseMove move = new AMD64HotSpotMove.BaseMove(baseAddress, config); append(move); base = baseAddress; } else { - base = emitLoadConstant(LIRKind.value(AMD64Kind.QWORD), JavaConstant.forLong(encoding.getBase())); + base = emitLoadConstant(uncompressedKind, JavaConstant.forLong(encoding.getBase())); } } - append(new AMD64HotSpotMove.UncompressPointer(result, asAllocatable(pointer), base, encoding, nonNull)); + append(new AMD64Move.UncompressPointer(result, asAllocatable(pointer), base, encoding, nonNull, lirKindTool)); return result; } } @Override public void emitNullCheck(Value address, LIRFrameState state) { - if (address.getValueKind().getPlatformKind() == AMD64Kind.DWORD) { + if (address.getValueKind().getPlatformKind() == getLIRKindTool().getNarrowOopKind().getPlatformKind()) { CompressEncoding encoding = config.getOopEncoding(); Value uncompressed; if (encoding.getShift() <= 3) { @@ -635,9 +637,9 @@ public class AMD64HotSpotLIRGenerator extends AMD64LIRGenerator implements HotSp uncompressed = emitUncompress(address, encoding, false); } append(new AMD64Move.NullCheckOp(asAddressValue(uncompressed), state)); - } else { - super.emitNullCheck(address, state); + return; } + super.emitNullCheck(address, state); } @Override diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotLIRKindTool.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotLIRKindTool.java new file mode 100644 index 00000000000..095a2a2e056 --- /dev/null +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotLIRKindTool.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2017, 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 org.graalvm.compiler.hotspot.amd64; + +import jdk.vm.ci.amd64.AMD64Kind; +import org.graalvm.compiler.core.amd64.AMD64LIRKindTool; +import org.graalvm.compiler.core.common.LIRKind; + +public class AMD64HotSpotLIRKindTool extends AMD64LIRKindTool { + @Override + public LIRKind getNarrowOopKind() { + return LIRKind.reference(AMD64Kind.DWORD); + } + + @Override + public LIRKind getNarrowPointerKind() { + return LIRKind.value(AMD64Kind.DWORD); + } +} diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotMove.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotMove.java index a5c75f5958b..4440756c428 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotMove.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotMove.java @@ -24,16 +24,13 @@ package org.graalvm.compiler.hotspot.amd64; import static org.graalvm.compiler.core.common.GraalOptions.GeneratePIC; import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.HINT; -import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.ILLEGAL; import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.REG; import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.STACK; import static jdk.vm.ci.code.ValueUtil.asRegister; import static jdk.vm.ci.code.ValueUtil.isRegister; import static jdk.vm.ci.code.ValueUtil.isStackSlot; -import org.graalvm.compiler.asm.Label; import org.graalvm.compiler.asm.amd64.AMD64Address; -import org.graalvm.compiler.asm.amd64.AMD64Assembler.ConditionFlag; import org.graalvm.compiler.core.common.CompressEncoding; import org.graalvm.compiler.asm.amd64.AMD64MacroAssembler; import org.graalvm.compiler.debug.GraalError; @@ -41,10 +38,8 @@ import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig; import org.graalvm.compiler.lir.LIRInstructionClass; import org.graalvm.compiler.lir.StandardOp.LoadConstantOp; import org.graalvm.compiler.lir.amd64.AMD64LIRInstruction; -import org.graalvm.compiler.lir.amd64.AMD64Move; import org.graalvm.compiler.lir.asm.CompilationResultBuilder; -import jdk.vm.ci.amd64.AMD64Kind; import jdk.vm.ci.code.Register; import jdk.vm.ci.hotspot.HotSpotMetaspaceConstant; import jdk.vm.ci.hotspot.HotSpotObjectConstant; @@ -180,91 +175,6 @@ public class AMD64HotSpotMove { } } - public static final class CompressPointer extends AMD64LIRInstruction { - public static final LIRInstructionClass TYPE = LIRInstructionClass.create(CompressPointer.class); - - private final CompressEncoding encoding; - private final boolean nonNull; - - @Def({REG, HINT}) protected AllocatableValue result; - @Use({REG}) protected AllocatableValue input; - @Alive({REG, ILLEGAL}) protected AllocatableValue baseRegister; - - public CompressPointer(AllocatableValue result, AllocatableValue input, AllocatableValue baseRegister, CompressEncoding encoding, boolean nonNull) { - super(TYPE); - this.result = result; - this.input = input; - this.baseRegister = baseRegister; - this.encoding = encoding; - this.nonNull = nonNull; - } - - @Override - public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) { - AMD64Move.move(AMD64Kind.QWORD, crb, masm, result, input); - - Register resReg = asRegister(result); - if (encoding.hasBase() || GeneratePIC.getValue(crb.getOptions())) { - Register baseReg = asRegister(baseRegister); - if (!nonNull) { - masm.testq(resReg, resReg); - masm.cmovq(ConditionFlag.Equal, resReg, baseReg); - } - masm.subq(resReg, baseReg); - } - - if (encoding.hasShift()) { - masm.shrq(resReg, encoding.getShift()); - } - } - } - - public static final class UncompressPointer extends AMD64LIRInstruction { - public static final LIRInstructionClass TYPE = LIRInstructionClass.create(UncompressPointer.class); - - private final CompressEncoding encoding; - private final boolean nonNull; - - @Def({REG, HINT}) protected AllocatableValue result; - @Use({REG}) protected AllocatableValue input; - @Alive({REG, ILLEGAL}) protected AllocatableValue baseRegister; - - public UncompressPointer(AllocatableValue result, AllocatableValue input, AllocatableValue baseRegister, CompressEncoding encoding, boolean nonNull) { - super(TYPE); - this.result = result; - this.input = input; - this.baseRegister = baseRegister; - this.encoding = encoding; - this.nonNull = nonNull; - } - - @Override - public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) { - AMD64Move.move(AMD64Kind.DWORD, crb, masm, result, input); - - Register resReg = asRegister(result); - if (encoding.getShift() != 0) { - masm.shlq(resReg, encoding.getShift()); - } - - if (encoding.hasBase() || GeneratePIC.getValue(crb.getOptions())) { - if (nonNull) { - masm.addq(resReg, asRegister(baseRegister)); - } else { - if (!encoding.hasShift()) { - // if encoding.shift != 0, the flags are already set by the shlq - masm.testq(resReg, resReg); - } - - Label done = new Label(); - masm.jccb(ConditionFlag.Equal, done); - masm.addq(resReg, asRegister(baseRegister)); - masm.bind(done); - } - } - } - } - public static void decodeKlassPointer(CompilationResultBuilder crb, AMD64MacroAssembler masm, Register register, Register scratch, AMD64Address address, GraalHotSpotVMConfig config) { CompressEncoding encoding = config.getKlassEncoding(); masm.movl(register, address); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotNodeLIRBuilder.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotNodeLIRBuilder.java index a81cb6d222e..93a2c360fa8 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotNodeLIRBuilder.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotNodeLIRBuilder.java @@ -189,8 +189,7 @@ public class AMD64HotSpotNodeLIRBuilder extends AMD64NodeLIRBuilder implements H sig[i] = node.arguments().get(i).stamp().javaType(gen.getMetaAccess()); } - Value[] parameters = visitInvokeArguments(gen.getResult().getFrameMapBuilder().getRegisterConfig().getCallingConvention(HotSpotCallingConventionType.JavaCall, null, sig, gen), - node.arguments()); + Value[] parameters = visitInvokeArguments(gen.getRegisterConfig().getCallingConvention(HotSpotCallingConventionType.JavaCall, null, sig, gen), node.arguments()); append(new AMD64BreakpointOp(parameters)); } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotMove.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotMove.java index d6f9bdffaea..2175bec863c 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotMove.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotMove.java @@ -113,8 +113,7 @@ public class SPARCHotSpotMove { } else { register = asRegister(result); } - int bytes = result.getPlatformKind().getSizeInBytes(); - loadFromConstantTable(crb, masm, bytes, asRegister(constantTableBase), constant, register, SPARCDelayedControlTransfer.DUMMY); + int bytes = loadFromConstantTable(crb, masm, asRegister(constantTableBase), constant, register, SPARCDelayedControlTransfer.DUMMY); if (isStack) { masm.st(register, (SPARCAddress) crb.asAddress(result), bytes); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotNodeLIRBuilder.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotNodeLIRBuilder.java index 414058f9e67..09dc9cf5f42 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotNodeLIRBuilder.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotNodeLIRBuilder.java @@ -162,8 +162,7 @@ public class SPARCHotSpotNodeLIRBuilder extends SPARCNodeLIRBuilder implements H sig[i] = node.arguments().get(i).stamp().javaType(gen.getMetaAccess()); } - Value[] parameters = visitInvokeArguments(gen.getResult().getFrameMapBuilder().getRegisterConfig().getCallingConvention(HotSpotCallingConventionType.JavaCall, null, sig, gen), - node.arguments()); + Value[] parameters = visitInvokeArguments(gen.getRegisterConfig().getCallingConvention(HotSpotCallingConventionType.JavaCall, null, sig, gen), node.arguments()); append(new SPARCBreakpointOp(parameters)); } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotStrategySwitchOp.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotStrategySwitchOp.java index f4e2c741782..333f8569970 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotStrategySwitchOp.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotStrategySwitchOp.java @@ -77,8 +77,7 @@ final class SPARCHotSpotStrategySwitchOp extends SPARCControlFlow.StrategySwitch boolean canUseShortBranch = masm.hasFeature(CPUFeature.CBCOND) && SPARCControlFlow.isShortBranch(masm, cbCondPosition, hint, target); Register scratchRegister = asRegister(scratch); - final int byteCount = constant.isCompressed() ? 4 : 8; - loadFromConstantTable(crb, masm, byteCount, asRegister(constantTableBase), constant, scratchRegister, SPARCDelayedControlTransfer.DUMMY); + loadFromConstantTable(crb, masm, asRegister(constantTableBase), constant, scratchRegister, SPARCDelayedControlTransfer.DUMMY); if (canUseShortBranch) { CBCOND.emit(masm, conditionFlag, conditionCode == CC.Xcc, keyRegister, scratchRegister, target); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/ArrayCopyIntrinsificationTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/ArrayCopyIntrinsificationTest.java index 952dd2bc08e..21b69d4c082 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/ArrayCopyIntrinsificationTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/ArrayCopyIntrinsificationTest.java @@ -160,7 +160,7 @@ public class ArrayCopyIntrinsificationTest extends GraalCompilerTest { } /** - * Tests {@link ArrayCopySnippets#checkcastArraycopyWork}. + * Tests {@link ArrayCopySnippets#arraycopyCheckcastSnippet}. */ @Test public void testArrayStoreException() { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CompilationWrapperTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CompilationWrapperTest.java index 669d8d746ab..4f53585d4d4 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CompilationWrapperTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CompilationWrapperTest.java @@ -122,7 +122,7 @@ public class CompilationWrapperTest extends GraalCompilerTest { * Tests compilation requested by Truffle. */ @Test - public void testTruffleCompilation() throws IOException, InterruptedException { + public void testTruffleCompilation1() throws IOException, InterruptedException { testHelper(Collections.emptyList(), Arrays.asList( "-Dgraal.CompilationFailureAction=ExitVM", @@ -130,6 +130,22 @@ public class CompilationWrapperTest extends GraalCompilerTest { "org.graalvm.compiler.truffle.test.SLTruffleGraalTestSuite", "test"); } + /** + * Tests that TruffleCompilationExceptionsAreFatal works as expected. + */ + @Test + public void testTruffleCompilation2() throws IOException, InterruptedException { + Probe[] probes = { + new Probe("Exiting VM due to TruffleCompilationExceptionsAreFatal=true", 1), + }; + testHelper(Arrays.asList(probes), + Arrays.asList( + "-Dgraal.CompilationFailureAction=Silent", + "-Dgraal.TruffleCompilationExceptionsAreFatal=true", + "-Dgraal.CrashAt=root test1"), + "org.graalvm.compiler.truffle.test.SLTruffleGraalTestSuite", "test"); + } + private static final boolean VERBOSE = Boolean.getBoolean(CompilationWrapperTest.class.getSimpleName() + ".verbose"); private static void testHelper(List initialProbes, List extraVmArgs, String... mainClassAndArgs) throws IOException, InterruptedException { @@ -149,14 +165,17 @@ public class CompilationWrapperTest extends GraalCompilerTest { } List probes = new ArrayList<>(initialProbes); - Probe diagnosticProbe = new Probe("Graal diagnostic output saved in ", 1); - probes.add(diagnosticProbe); - probes.add(new Probe("Forced crash after compiling", Integer.MAX_VALUE) { - @Override - String test() { - return actualOccurrences > 0 ? null : "expected at least 1 occurrence"; - } - }); + Probe diagnosticProbe = null; + if (!extraVmArgs.contains("-Dgraal.TruffleCompilationExceptionsAreFatal=true")) { + diagnosticProbe = new Probe("Graal diagnostic output saved in ", 1); + probes.add(diagnosticProbe); + probes.add(new Probe("Forced crash after compiling", Integer.MAX_VALUE) { + @Override + String test() { + return actualOccurrences > 0 ? null : "expected at least 1 occurrence"; + } + }); + } for (String line : proc.output) { for (Probe probe : probes) { @@ -171,38 +190,42 @@ public class CompilationWrapperTest extends GraalCompilerTest { Assert.fail(String.format("Did not find expected occurences of '%s' in output of command: %s%n%s", probe.substring, error, proc)); } } + if (diagnosticProbe != null) { + String line = diagnosticProbe.lastMatchingLine; + int substringStart = line.indexOf(diagnosticProbe.substring); + int substringLength = diagnosticProbe.substring.length(); + String diagnosticOutputZip = line.substring(substringStart + substringLength).trim(); - String diagnosticOutputZip = diagnosticProbe.lastMatchingLine.substring(diagnosticProbe.substring.length()).trim(); + List dumpPathEntries = Arrays.asList(dumpPath.list()); - List dumpPathEntries = Arrays.asList(dumpPath.list()); - - File zip = new File(diagnosticOutputZip).getAbsoluteFile(); - Assert.assertTrue(zip.toString(), zip.exists()); - Assert.assertTrue(zip + " not in " + dumpPathEntries, dumpPathEntries.contains(zip.getName())); - try { - int bgv = 0; - int cfg = 0; - ZipFile dd = new ZipFile(diagnosticOutputZip); - List entries = new ArrayList<>(); - for (Enumeration e = dd.entries(); e.hasMoreElements();) { - ZipEntry ze = e.nextElement(); - String name = ze.getName(); - entries.add(name); - if (name.endsWith(".bgv")) { - bgv++; - } else if (name.endsWith(".cfg")) { - cfg++; + File zip = new File(diagnosticOutputZip).getAbsoluteFile(); + Assert.assertTrue(zip.toString(), zip.exists()); + Assert.assertTrue(zip + " not in " + dumpPathEntries, dumpPathEntries.contains(zip.getName())); + try { + int bgv = 0; + int cfg = 0; + ZipFile dd = new ZipFile(diagnosticOutputZip); + List entries = new ArrayList<>(); + for (Enumeration e = dd.entries(); e.hasMoreElements();) { + ZipEntry ze = e.nextElement(); + String name = ze.getName(); + entries.add(name); + if (name.endsWith(".bgv")) { + bgv++; + } else if (name.endsWith(".cfg")) { + cfg++; + } } + if (bgv == 0) { + Assert.fail(String.format("Expected at least one .bgv file in %s: %s%n%s", diagnosticOutputZip, entries, proc)); + } + if (cfg == 0) { + Assert.fail(String.format("Expected at least one .cfg file in %s: %s", diagnosticOutputZip, entries)); + } + } finally { + zip.delete(); + dumpPath.delete(); } - if (bgv == 0) { - Assert.fail(String.format("Expected at least one .bgv file in %s: %s%n%s", diagnosticOutputZip, entries, proc)); - } - if (cfg == 0) { - Assert.fail(String.format("Expected at least one .cfg file in %s: %s", diagnosticOutputZip, entries)); - } - } finally { - zip.delete(); - dumpPath.delete(); } } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/ExplicitExceptionTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/ExplicitExceptionTest.java index 44dbfe55e67..0b533e5abe5 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/ExplicitExceptionTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/ExplicitExceptionTest.java @@ -22,23 +22,44 @@ */ package org.graalvm.compiler.hotspot.test; -import org.junit.Test; - import org.graalvm.compiler.core.test.GraalCompilerTest; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.extended.ForeignCallNode; import org.graalvm.compiler.options.OptionValues; +import org.junit.Assume; +import org.junit.Test; import jdk.vm.ci.code.InstalledCode; +import jdk.vm.ci.meta.ProfilingInfo; import jdk.vm.ci.meta.ResolvedJavaMethod; +import jdk.vm.ci.meta.TriState; public class ExplicitExceptionTest extends GraalCompilerTest { private int expectedForeignCallCount; + /** + * Determines if profiling info for {@code method} indicates an exception was thrown somewhere + * in the method. In the case of the {@code -Xcomp} VM option, interpreter execution can be + * skipped altogether and other execution engines (e.g., C1) may not record seen exceptions in a + * method profile. + */ + private static boolean exceptionWasSeen(ResolvedJavaMethod method) { + ProfilingInfo profilingInfo = method.getProfilingInfo(); + if (profilingInfo != null) { + for (int i = 0; i < profilingInfo.getCodeSize(); i++) { + if (profilingInfo.getExceptionSeen(i) == TriState.TRUE) { + return true; + } + } + } + return false; + } + @Override protected InstalledCode getCode(ResolvedJavaMethod method, StructuredGraph graph, boolean forceCompile, boolean installAsDefault, OptionValues options) { InstalledCode installedCode = super.getCode(method, graph, forceCompile, installAsDefault, options); + Assume.assumeTrue(exceptionWasSeen(method)); assertDeepEquals(expectedForeignCallCount, lastCompiledGraph.getNodes().filter(ForeignCallNode.class).count()); return installedCode; } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/GraalOSRLockTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/GraalOSRLockTest.java index bff37edeb79..6b47720d1ac 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/GraalOSRLockTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/GraalOSRLockTest.java @@ -24,11 +24,13 @@ package org.graalvm.compiler.hotspot.test; import java.lang.management.ManagementFactory; import java.lang.management.MonitorInfo; +import java.lang.management.RuntimeMXBean; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import org.graalvm.compiler.api.directives.GraalDirectives; import org.graalvm.compiler.core.phases.HighTier; @@ -44,6 +46,7 @@ import org.junit.Test; import jdk.vm.ci.meta.ResolvedJavaMethod; import org.junit.Assume; +import org.junit.BeforeClass; /** * Test on-stack-replacement with locks. @@ -51,13 +54,28 @@ import org.junit.Assume; public class GraalOSRLockTest extends GraalOSRTestBase { private static boolean TestInSeparateThread = false; + private static final String COMPILE_ONLY_FLAG = "-Xcomp"; - public GraalOSRLockTest() { + @BeforeClass + public static void checkVMArguments() { try { Class.forName("java.lang.management.ManagementFactory"); } catch (ClassNotFoundException ex) { Assume.assumeNoException("cannot check for monitors without java.management JDK9 module", ex); } + /* + * Note: The -Xcomp execution mode of the VM will stop most of the OSR test cases from + * working as every method is compiled at level3 (followed by level4 on the second + * invocation). The tests in this class are written in a way that they expect a method to be + * executed at the invocation BCI with the interpreter and then perform an OSR to an + * installed nmethod at a given BCI. + * + */ + RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean(); + List arguments = runtimeMxBean.getInputArguments(); + for (String arg : arguments) { + Assume.assumeFalse(arg.equals(COMPILE_ONLY_FLAG)); + } } // testing only @@ -438,7 +456,7 @@ public class GraalOSRLockTest extends GraalOSRTestBase { } GraalDirectives.controlFlowAnchor(); if (!GraalDirectives.inCompiledCode()) { - throw new Error("Must part of compiled code"); + throw new Error("Must be part of compiled code"); } return ret; } @@ -449,11 +467,11 @@ public class GraalOSRLockTest extends GraalOSRTestBase { ReturnValue ret = ReturnValue.FAILURE; synchronized (lock) { synchronized (lock1) { - for (int i = 1; i < limit; i++) { + for (int i = 1; i < 10 * limit; i++) { GraalDirectives.blackhole(i); - if (i % 1001 == 0) { + if (i % 33 == 0) { ret = ReturnValue.SUCCESS; - if (GraalDirectives.inCompiledCode() && i + 33 > (limit)) { + if (GraalDirectives.inCompiledCode() && i + 33 > (10 * limit)) { GraalDirectives.blackhole(ret); System.gc(); } @@ -462,7 +480,7 @@ public class GraalOSRLockTest extends GraalOSRTestBase { } GraalDirectives.controlFlowAnchor(); if (!GraalDirectives.inCompiledCode()) { - throw new Error("Must part of compiled code"); + throw new Error("Must be part of compiled code already hereeeeee"); } else { // lock 1 must be free if (isMonitorLockHeld(lock1)) { @@ -519,7 +537,7 @@ public class GraalOSRLockTest extends GraalOSRTestBase { } GraalDirectives.controlFlowAnchor(); if (!GraalDirectives.inCompiledCode()) { - throw new Error("Must part of compiled code"); + throw new Error("Must be part of compiled code"); } return ret; } @@ -543,7 +561,7 @@ public class GraalOSRLockTest extends GraalOSRTestBase { } GraalDirectives.controlFlowAnchor(); if (!GraalDirectives.inCompiledCode()) { - throw new Error("Must part of compiled code"); + throw new Error("Must be part of compiled code"); } return ret; } @@ -568,7 +586,7 @@ public class GraalOSRLockTest extends GraalOSRTestBase { } GraalDirectives.controlFlowAnchor(); if (!GraalDirectives.inCompiledCode()) { - throw new Error("Must part of compiled code"); + throw new Error("Must be part of compiled code"); } return ret; } @@ -646,7 +664,7 @@ public class GraalOSRLockTest extends GraalOSRTestBase { synchronized (monitor) { GraalDirectives.controlFlowAnchor(); if (!GraalDirectives.inCompiledCode()) { - throw new Error("Must part of compiled code"); + throw new Error("Must be part of compiled code"); } } return ret; @@ -670,7 +688,7 @@ public class GraalOSRLockTest extends GraalOSRTestBase { } GraalDirectives.controlFlowAnchor(); if (!GraalDirectives.inCompiledCode()) { - throw new Error("Must part of compiled code"); + throw new Error("Must be part of compiled code"); } return ret; } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/GraalOSRTestBase.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/GraalOSRTestBase.java index f0d53bb23d7..d73449cd9dd 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/GraalOSRTestBase.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/GraalOSRTestBase.java @@ -35,7 +35,9 @@ import org.graalvm.compiler.debug.DebugContext; import org.graalvm.compiler.debug.GraalError; import org.graalvm.compiler.debug.TTY; import org.graalvm.compiler.hotspot.CompilationTask; +import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig; import org.graalvm.compiler.hotspot.HotSpotGraalCompiler; +import org.graalvm.compiler.hotspot.HotSpotGraalRuntimeProvider; import org.graalvm.compiler.java.BciBlockMapping; import org.graalvm.compiler.java.BciBlockMapping.BciBlock; import org.graalvm.compiler.nodes.StructuredGraph; @@ -76,6 +78,13 @@ public abstract class GraalOSRTestBase extends GraalCompilerTest { HotSpotCompilationRequest request = new HotSpotCompilationRequest((HotSpotResolvedJavaMethod) method, bci, jvmciEnv); HotSpotGraalCompiler compiler = (HotSpotGraalCompiler) runtime.getCompiler(); CompilationTask task = new CompilationTask(runtime, compiler, request, true, true, debug.getOptions()); + if (method instanceof HotSpotResolvedJavaMethod) { + HotSpotGraalRuntimeProvider graalRuntime = compiler.getGraalRuntime(); + GraalHotSpotVMConfig config = graalRuntime.getVMConfig(); + if (((HotSpotResolvedJavaMethod) method).hasCodeAtLevel(bci, config.compilationLevelFullOptimization)) { + return; + } + } HotSpotCompilationRequestResult result = task.runCompilation(debug); if (result.getFailure() != null) { throw new GraalError(result.getFailureMessage()); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/WriteBarrierVerificationTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/WriteBarrierVerificationTest.java index 4c3a48468c4..10298107ad4 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/WriteBarrierVerificationTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/WriteBarrierVerificationTest.java @@ -37,7 +37,6 @@ import org.graalvm.compiler.hotspot.nodes.SerialArrayRangeWriteBarrier; import org.graalvm.compiler.hotspot.nodes.SerialWriteBarrier; import org.graalvm.compiler.hotspot.phases.WriteBarrierAdditionPhase; import org.graalvm.compiler.hotspot.phases.WriteBarrierVerificationPhase; -import org.graalvm.compiler.hotspot.replacements.arraycopy.UnsafeArrayCopyNode; import org.graalvm.compiler.nodes.AbstractBeginNode; import org.graalvm.compiler.nodes.AbstractMergeNode; import org.graalvm.compiler.nodes.FieldLocationIdentity; @@ -629,12 +628,6 @@ public class WriteBarrierVerificationTest extends HotSpotGraalCompilerTest { System.arraycopy(a, 0, b, 0, a.length); } - @Test - public void test61() { - GraphPredicate checkForUnsafeArrayCopy = graph -> graph.getNodes().filter(UnsafeArrayCopyNode.class).count() > 0 ? 1 : 0; - testPredicate("test13Snippet", checkForUnsafeArrayCopy, new int[]{}); - } - private interface GraphPredicate { int apply(StructuredGraph graph); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfig.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfig.java index f314bbd584f..15e709c3056 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfig.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfig.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2016, 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 @@ -280,14 +280,19 @@ public class GraalHotSpotVMConfig extends HotSpotVMConfigAccess { } if (offset == -1) { try { - offset = getFieldOffset(name, Integer.class, "OopHandle"); + offset = getFieldOffset(name, Integer.class, "jobject"); isHandle = true; } catch (JVMCIError e) { - + try { + // JDK-8186777 + offset = getFieldOffset(name, Integer.class, "OopHandle"); + isHandle = true; + } catch (JVMCIError e2) { + } } } if (offset == -1) { - throw new JVMCIError("cannot get offset of field " + name + " with type oop or OopHandle"); + throw new JVMCIError("cannot get offset of field " + name + " with type oop, jobject or OopHandle"); } classMirrorOffset = offset; classMirrorIsHandle = isHandle; @@ -648,6 +653,8 @@ public class GraalHotSpotVMConfig extends HotSpotVMConfigAccess { public final long heapEndAddress = getFieldValue("CompilerToVM::Data::_heap_end_addr", Long.class, "HeapWord**"); public final long heapTopAddress = getFieldValue("CompilerToVM::Data::_heap_top_addr", Long.class, isJDK8 ? "HeapWord**" : "HeapWord* volatile*"); + public final boolean cmsIncrementalMode = getFlag("CMSIncrementalMode", Boolean.class, false); + public final long inlineCacheMissStub = getFieldValue("CompilerToVM::Data::SharedRuntime_ic_miss_stub", Long.class, "address"); public final long handleWrongMethodStub = getFieldValue("CompilerToVM::Data::SharedRuntime_handle_wrong_method_stub", Long.class, "address"); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotBackend.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotBackend.java index 3a6d2ec302b..5191305b3e7 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotBackend.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotBackend.java @@ -266,6 +266,8 @@ public abstract class HotSpotBackend extends Backend implements FrameMap.Referen unsafeArraycopyStub(HotSpotBackend.UNSAFE_ARRAYCOPY, srcAddr, dstAddr, size); } + public static final ForeignCallDescriptor GENERIC_ARRAYCOPY = new ForeignCallDescriptor("generic_arraycopy", int.class, Word.class, int.class, Word.class, int.class, int.class); + @NodeIntrinsic(ForeignCallNode.class) private static native void unsafeArraycopyStub(@ConstantNodeParameter ForeignCallDescriptor descriptor, Word srcAddr, Word dstAddr, Word size); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalCompiler.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalCompiler.java index c77d3eebf18..c160cc0878c 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalCompiler.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalCompiler.java @@ -284,7 +284,7 @@ public class HotSpotGraalCompiler implements GraalJVMCICompiler { public Object mbean() { if (graalRuntime instanceof HotSpotGraalRuntime) { - return ((HotSpotGraalRuntime)graalRuntime).mbean(); + return ((HotSpotGraalRuntime) graalRuntime).getMBean(); } return null; } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalCompilerFactory.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalCompilerFactory.java index b4b8e41890f..7146ad77271 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalCompilerFactory.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalCompilerFactory.java @@ -26,6 +26,8 @@ import static jdk.vm.ci.common.InitTimer.timer; import static org.graalvm.compiler.hotspot.HotSpotGraalOptionValues.GRAAL_OPTION_PROPERTY_PREFIX; import java.io.PrintStream; +import java.util.Map; +import java.util.Collections; import org.graalvm.compiler.debug.MethodFilter; import org.graalvm.compiler.options.Option; @@ -190,4 +192,11 @@ public final class HotSpotGraalCompilerFactory extends HotSpotJVMCICompilerFacto } return level; } + + public Map mbeans() { + HotSpotGraalCompiler compiler = createCompiler(HotSpotJVMCIRuntime.runtime()); + String name = "org.graalvm.compiler.hotspot:type=Options"; + Object bean = ((HotSpotGraalRuntime) compiler.getGraalRuntime()).getMBean(); + return Collections.singletonMap(name, bean); + } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalMBean.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalMBean.java index c32e26808f1..2d83476c62f 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalMBean.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalMBean.java @@ -283,10 +283,8 @@ public final class HotSpotGraalMBean implements javax.management.DynamicMBean { @Override public javax.management.MBeanInfo getMBeanInfo() { List attrs = new ArrayList<>(); - if (registered != null) { - for (OptionDescriptor descr : allOptionDescriptors()) { - attrs.add(new javax.management.MBeanAttributeInfo(descr.getName(), descr.getType().getName(), descr.getHelp(), true, true, false)); - } + for (OptionDescriptor descr : allOptionDescriptors()) { + attrs.add(new javax.management.MBeanAttributeInfo(descr.getName(), descr.getType().getName(), descr.getHelp(), true, true, false)); } javax.management.MBeanOperationInfo[] ops = { new javax.management.MBeanOperationInfo("dumpMethod", "Enable IGV dumps for provided method", new javax.management.MBeanParameterInfo[]{ diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalRuntime.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalRuntime.java index c56d8105219..e14b268e885 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalRuntime.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalRuntime.java @@ -317,7 +317,7 @@ public final class HotSpotGraalRuntime implements HotSpotGraalRuntimeProvider { return compilationProblemsPerAction; } - final Object mbean() { + Object getMBean() { return mBean; } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/DefaultHotSpotLoweringProvider.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/DefaultHotSpotLoweringProvider.java index 268dce0937a..d3c57cfe2c0 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/DefaultHotSpotLoweringProvider.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/DefaultHotSpotLoweringProvider.java @@ -28,8 +28,8 @@ import static org.graalvm.compiler.core.common.GraalOptions.InlineVTableStubs; import static org.graalvm.compiler.core.common.GraalOptions.OmitHotExceptionStacktrace; import static org.graalvm.compiler.hotspot.meta.HotSpotForeignCallsProviderImpl.OSR_MIGRATION_END; import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.CLASS_KLASS_LOCATION; -import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.CLASS_MIRROR_LOCATION; import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.CLASS_MIRROR_HANDLE_LOCATION; +import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.CLASS_MIRROR_LOCATION; import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.COMPRESSED_HUB_LOCATION; import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.DISPLACED_MARK_WORD_LOCATION; import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.HUB_LOCATION; @@ -41,6 +41,7 @@ import static org.graalvm.word.LocationIdentity.any; import java.lang.ref.Reference; import org.graalvm.compiler.api.directives.GraalDirectives; +import org.graalvm.compiler.core.common.CompressEncoding; import org.graalvm.compiler.core.common.GraalOptions; import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor; import org.graalvm.compiler.core.common.spi.ForeignCallsProvider; @@ -90,10 +91,8 @@ import org.graalvm.compiler.hotspot.replacements.UnsafeLoadSnippets; import org.graalvm.compiler.hotspot.replacements.WriteBarrierSnippets; import org.graalvm.compiler.hotspot.replacements.aot.ResolveConstantSnippets; import org.graalvm.compiler.hotspot.replacements.arraycopy.ArrayCopyNode; -import org.graalvm.compiler.hotspot.replacements.arraycopy.ArrayCopySlowPathNode; +import org.graalvm.compiler.hotspot.replacements.arraycopy.ArrayCopyWithSlowPathNode; import org.graalvm.compiler.hotspot.replacements.arraycopy.ArrayCopySnippets; -import org.graalvm.compiler.hotspot.replacements.arraycopy.ArrayCopyUnrollNode; -import org.graalvm.compiler.hotspot.replacements.arraycopy.UnsafeArrayCopySnippets; import org.graalvm.compiler.hotspot.replacements.profiling.ProfileSnippets; import org.graalvm.compiler.hotspot.word.KlassPointer; import org.graalvm.compiler.nodes.AbstractBeginNode; @@ -193,7 +192,7 @@ public class DefaultHotSpotLoweringProvider extends DefaultJavaLoweringProvider public DefaultHotSpotLoweringProvider(HotSpotGraalRuntimeProvider runtime, MetaAccessProvider metaAccess, ForeignCallsProvider foreignCalls, HotSpotRegistersProvider registers, HotSpotConstantReflectionProvider constantReflection, TargetDescription target) { - super(metaAccess, foreignCalls, target); + super(metaAccess, foreignCalls, target, runtime.getVMConfig().useCompressedOops); this.runtime = runtime; this.registers = registers; this.constantReflection = constantReflection; @@ -216,7 +215,6 @@ public class DefaultHotSpotLoweringProvider extends DefaultJavaLoweringProvider hashCodeSnippets = new HashCodeSnippets.Templates(options, factories, providers, target); resolveConstantSnippets = new ResolveConstantSnippets.Templates(options, factories, providers, target); profileSnippets = new ProfileSnippets.Templates(options, factories, providers, target); - providers.getReplacements().registerSnippetTemplateCache(new UnsafeArrayCopySnippets.Templates(options, factories, providers, target)); } public MonitorSnippets.Templates getMonitorSnippets() { @@ -315,10 +313,8 @@ public class DefaultHotSpotLoweringProvider extends DefaultJavaLoweringProvider } } else if (n instanceof ArrayCopyNode) { arraycopySnippets.lower((ArrayCopyNode) n, tool); - } else if (n instanceof ArrayCopySlowPathNode) { - arraycopySnippets.lower((ArrayCopySlowPathNode) n, tool); - } else if (n instanceof ArrayCopyUnrollNode) { - arraycopySnippets.lower((ArrayCopyUnrollNode) n, tool); + } else if (n instanceof ArrayCopyWithSlowPathNode) { + arraycopySnippets.lower((ArrayCopyWithSlowPathNode) n, tool); } else if (n instanceof G1PreWriteBarrier) { writeBarrierSnippets.lower((G1PreWriteBarrier) n, registers, tool); } else if (n instanceof G1PostWriteBarrier) { @@ -495,20 +491,18 @@ public class DefaultHotSpotLoweringProvider extends DefaultJavaLoweringProvider } } - @Override - protected Stamp loadStamp(Stamp stamp, JavaKind kind, boolean compressible) { - if (kind == JavaKind.Object && compressible && runtime.getVMConfig().useCompressedOops) { - return HotSpotNarrowOopStamp.compressed((ObjectStamp) stamp, runtime.getVMConfig().getOopEncoding()); - } - return super.loadStamp(stamp, kind, compressible); + private CompressEncoding getOopEncoding() { + return runtime.getVMConfig().getOopEncoding(); } @Override - protected ValueNode implicitLoadConvert(JavaKind kind, ValueNode value, boolean compressible) { - if (kind == JavaKind.Object && compressible && runtime.getVMConfig().useCompressedOops) { - return new HotSpotCompressionNode(CompressionOp.Uncompress, value, runtime.getVMConfig().getOopEncoding()); - } - return super.implicitLoadConvert(kind, value, compressible); + protected Stamp loadCompressedStamp(ObjectStamp stamp) { + return HotSpotNarrowOopStamp.compressed(stamp, getOopEncoding()); + } + + @Override + protected ValueNode newCompressionNode(CompressionOp op, ValueNode value) { + return new HotSpotCompressionNode(op, value, getOopEncoding()); } @Override @@ -518,14 +512,6 @@ public class DefaultHotSpotLoweringProvider extends DefaultJavaLoweringProvider return ConstantNode.forConstant(base, metaAccess, graph); } - @Override - protected ValueNode implicitStoreConvert(JavaKind kind, ValueNode value, boolean compressible) { - if (kind == JavaKind.Object && compressible && runtime.getVMConfig().useCompressedOops) { - return new HotSpotCompressionNode(CompressionOp.Compress, value, runtime.getVMConfig().getOopEncoding()); - } - return super.implicitStoreConvert(kind, value, compressible); - } - @Override protected ValueNode createReadArrayComponentHub(StructuredGraph graph, ValueNode arrayHub, FixedNode anchor) { /* @@ -800,4 +786,9 @@ public class DefaultHotSpotLoweringProvider extends DefaultJavaLoweringProvider public int arrayLengthOffset() { return runtime.getVMConfig().arrayOopDescLengthOffset(); } + + @Override + protected final JavaKind getStorageKind(ResolvedJavaField field) { + return field.getJavaKind(); + } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java index 61fbadca3b6..5e1504ab14c 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java @@ -39,6 +39,7 @@ import java.util.zip.CRC32; import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; import org.graalvm.compiler.bytecode.BytecodeProvider; import org.graalvm.compiler.core.common.spi.ForeignCallsProvider; +import org.graalvm.compiler.core.common.type.StampFactory; import org.graalvm.compiler.debug.GraalError; import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig; import org.graalvm.compiler.hotspot.nodes.CurrentJavaThreadNode; @@ -68,6 +69,7 @@ import org.graalvm.compiler.nodes.NamedLocationIdentity; import org.graalvm.compiler.nodes.PiNode; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.AddNode; +import org.graalvm.compiler.nodes.calc.IntegerConvertNode; import org.graalvm.compiler.nodes.calc.LeftShiftNode; import org.graalvm.compiler.nodes.graphbuilderconf.ForeignCallPlugin; import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; @@ -316,8 +318,8 @@ public class HotSpotGraphBuilderPlugins { private static boolean readMetaspaceConstantPoolElement(GraphBuilderContext b, ValueNode constantPoolOop, ValueNode index, JavaKind elementKind, WordTypes wordTypes, GraalHotSpotVMConfig config) { ValueNode constants = getMetaspaceConstantPool(b, constantPoolOop, wordTypes, config); int shift = CodeUtil.log2(wordTypes.getWordKind().getByteCount()); - ValueNode scaledIndex = b.add(new LeftShiftNode(index, b.add(ConstantNode.forInt(shift)))); - ValueNode offset = b.add(new AddNode(scaledIndex, b.add(ConstantNode.forInt(config.constantPoolSize)))); + ValueNode scaledIndex = b.add(new LeftShiftNode(IntegerConvertNode.convert(index, StampFactory.forKind(JavaKind.Long)), b.add(ConstantNode.forInt(shift)))); + ValueNode offset = b.add(new AddNode(scaledIndex, b.add(ConstantNode.forLong(config.constantPoolSize)))); AddressNode elementAddress = b.add(new OffsetAddressNode(constants, offset)); boolean notCompressible = false; ValueNode elementValue = WordOperationPlugin.readOp(b, elementKind, elementAddress, NamedLocationIdentity.getArrayLocation(elementKind), BarrierType.NONE, notCompressible); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotHostForeignCallsProvider.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotHostForeignCallsProvider.java index 36618783bfc..e18d2fde16d 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotHostForeignCallsProvider.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotHostForeignCallsProvider.java @@ -34,6 +34,7 @@ import static org.graalvm.compiler.hotspot.HotSpotBackend.DECRYPT_WITH_ORIGINAL_ import static org.graalvm.compiler.hotspot.HotSpotBackend.ENCRYPT; import static org.graalvm.compiler.hotspot.HotSpotBackend.ENCRYPT_BLOCK; import static org.graalvm.compiler.hotspot.HotSpotBackend.EXCEPTION_HANDLER; +import static org.graalvm.compiler.hotspot.HotSpotBackend.GENERIC_ARRAYCOPY; import static org.graalvm.compiler.hotspot.HotSpotBackend.IC_MISS_HANDLER; import static org.graalvm.compiler.hotspot.HotSpotBackend.INITIALIZE_KLASS_BY_SYMBOL; import static org.graalvm.compiler.hotspot.HotSpotBackend.INVOCATION_EVENT; @@ -85,7 +86,6 @@ import static org.graalvm.compiler.hotspot.stubs.NewArrayStub.NEW_ARRAY_C; import static org.graalvm.compiler.hotspot.stubs.NewInstanceStub.NEW_INSTANCE_C; import static org.graalvm.compiler.hotspot.stubs.StubUtil.VM_MESSAGE_C; import static org.graalvm.compiler.hotspot.stubs.UnwindExceptionToCallerStub.EXCEPTION_HANDLER_FOR_RETURN_ADDRESS; -import static org.graalvm.compiler.nodes.NamedLocationIdentity.any; import static org.graalvm.compiler.nodes.java.ForeignCallDescriptors.REGISTER_FINALIZER; import static org.graalvm.compiler.replacements.Log.LOG_OBJECT; import static org.graalvm.compiler.replacements.Log.LOG_PRIMITIVE; @@ -97,6 +97,7 @@ import static org.graalvm.compiler.replacements.nodes.UnaryMathIntrinsicNode.Una import static org.graalvm.compiler.replacements.nodes.UnaryMathIntrinsicNode.UnaryOperation.LOG10; import static org.graalvm.compiler.replacements.nodes.UnaryMathIntrinsicNode.UnaryOperation.SIN; import static org.graalvm.compiler.replacements.nodes.UnaryMathIntrinsicNode.UnaryOperation.TAN; +import static org.graalvm.word.LocationIdentity.any; import java.util.EnumMap; @@ -213,7 +214,7 @@ public abstract class HotSpotHostForeignCallsProvider extends HotSpotForeignCall // c_rarg4 - oop ckval (super_klass) // return: 0 = success, n = number of copied elements xor'd with -1. ForeignCallDescriptor desc = new ForeignCallDescriptor(name, int.class, Word.class, Word.class, Word.class, Word.class, Word.class); - LocationIdentity killed = NamedLocationIdentity.getArrayLocation(JavaKind.Object); + LocationIdentity killed = NamedLocationIdentity.any(); registerForeignCall(desc, routine, NativeCall, DESTROYS_REGISTERS, LEAF_NOFP, NOT_REEXECUTABLE, killed); checkcastArraycopyDescriptors[uninit ? 1 : 0] = desc; } @@ -333,6 +334,7 @@ public abstract class HotSpotHostForeignCallsProvider extends HotSpotForeignCall registerCheckcastArraycopyDescriptor(true, c.checkcastArraycopyUninit); registerCheckcastArraycopyDescriptor(false, c.checkcastArraycopy); + registerForeignCall(GENERIC_ARRAYCOPY, c.genericArraycopy, NativeCall, DESTROYS_REGISTERS, LEAF_NOFP, NOT_REEXECUTABLE, NamedLocationIdentity.any()); registerForeignCall(UNSAFE_ARRAYCOPY, c.unsafeArraycopy, NativeCall, DESTROYS_REGISTERS, LEAF_NOFP, NOT_REEXECUTABLE, NamedLocationIdentity.any()); if (c.useMultiplyToLenIntrinsic()) { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/ResolveDynamicConstantNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/ResolveDynamicConstantNode.java index 813055bd6d1..f8a8f92e9e6 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/ResolveDynamicConstantNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/ResolveDynamicConstantNode.java @@ -28,6 +28,7 @@ import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_16; import org.graalvm.word.LocationIdentity; import org.graalvm.compiler.core.common.type.Stamp; import org.graalvm.compiler.graph.NodeClass; +import org.graalvm.compiler.nodeinfo.InputType; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.DeoptimizingFixedWithNextNode; import org.graalvm.compiler.nodes.ValueNode; @@ -35,7 +36,7 @@ import org.graalvm.compiler.nodes.memory.MemoryCheckpoint; import org.graalvm.compiler.nodes.spi.Lowerable; import org.graalvm.compiler.nodes.spi.LoweringTool; -@NodeInfo(cycles = CYCLES_4, size = SIZE_16) +@NodeInfo(cycles = CYCLES_4, size = SIZE_16, allowedUsageTypes = {InputType.Memory}) public class ResolveDynamicConstantNode extends DeoptimizingFixedWithNextNode implements Lowerable, MemoryCheckpoint.Single { public static final NodeClass TYPE = NodeClass.create(ResolveDynamicConstantNode.class); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/profiling/ProfileBranchNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/profiling/ProfileBranchNode.java index d7f475463c3..eb916a17acc 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/profiling/ProfileBranchNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/profiling/ProfileBranchNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 @@ -71,6 +71,15 @@ public class ProfileBranchNode extends ProfileWithNotificationNode { return branchCondition != null; } + @Override + protected boolean canBeMergedWith(ProfileNode p) { + if (p instanceof ProfileBranchNode) { + ProfileBranchNode that = (ProfileBranchNode) p; + return this.method.equals(that.method) && this.bci == that.bci; + } + return false; + } + /** * Gathers all the {@link ProfileBranchNode}s that are inputs to the * {@linkplain StructuredGraph#getNodes() live nodes} in a given graph. diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/profiling/ProfileInvokeNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/profiling/ProfileInvokeNode.java index 03f12cdfd08..9ee5c23f81f 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/profiling/ProfileInvokeNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/profiling/ProfileInvokeNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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,6 +37,15 @@ public class ProfileInvokeNode extends ProfileWithNotificationNode { super(TYPE, method, freqLog, probabilityLog); } + @Override + protected boolean canBeMergedWith(ProfileNode p) { + if (p instanceof ProfileInvokeNode) { + ProfileInvokeNode that = (ProfileInvokeNode) p; + return this.method.equals(that.method); + } + return false; + } + /** * Gathers all the {@link ProfileInvokeNode}s that are inputs to the * {@linkplain StructuredGraph#getNodes() live nodes} in a given graph. diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/profiling/ProfileNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/profiling/ProfileNode.java index c5c09718e6b..9b6d447ca09 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/profiling/ProfileNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/profiling/ProfileNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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,11 +24,17 @@ package org.graalvm.compiler.hotspot.nodes.profiling; import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_IGNORED; import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_IGNORED; +import static org.graalvm.compiler.nodes.util.GraphUtil.removeFixedWithUnusedInputs; import org.graalvm.compiler.core.common.type.StampFactory; +import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.graph.iterators.NodeIterable; +import org.graalvm.compiler.graph.spi.Simplifiable; +import org.graalvm.compiler.graph.spi.SimplifierTool; import org.graalvm.compiler.nodeinfo.NodeInfo; +import org.graalvm.compiler.nodes.AbstractMergeNode; +import org.graalvm.compiler.nodes.ControlSplitNode; import org.graalvm.compiler.nodes.DeoptimizingFixedWithNextNode; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; @@ -41,7 +47,7 @@ import org.graalvm.compiler.options.OptionType; import jdk.vm.ci.meta.ResolvedJavaMethod; @NodeInfo(cycles = CYCLES_IGNORED, cyclesRationale = "profiling should be ignored", size = SIZE_IGNORED, sizeRationale = "profiling should be ignored") -public class ProfileNode extends DeoptimizingFixedWithNextNode implements Lowerable { +public abstract class ProfileNode extends DeoptimizingFixedWithNextNode implements Simplifiable, Lowerable { public static class Options { @Option(help = "Control probabilistic profiling on AMD64", type = OptionType.Expert)// public static final OptionKey ProbabilisticProfiling = new OptionKey<>(true); @@ -54,19 +60,21 @@ public class ProfileNode extends DeoptimizingFixedWithNextNode implements Lowera // Only used if ProbabilisticProfiling == true and may be ignored by lowerer. @OptionalInput protected ValueNode random; - // logarithm base 2 of the profile probability + // Logarithm base 2 of the profile probability. protected int probabilityLog; + // Step value to add to the profile counter. + protected int step; + protected ProfileNode(NodeClass c, ResolvedJavaMethod method, int probabilityLog) { super(c, StampFactory.forVoid()); this.method = method; this.probabilityLog = probabilityLog; + this.step = 1; } public ProfileNode(ResolvedJavaMethod method, int probabilityLog) { - super(TYPE, StampFactory.forVoid()); - this.method = method; - this.probabilityLog = probabilityLog; + this(TYPE, method, probabilityLog); } @Override @@ -92,6 +100,14 @@ public class ProfileNode extends DeoptimizingFixedWithNextNode implements Lowera this.random = r; } + public int getStep() { + return step; + } + + public void setStep(int s) { + step = s; + } + /** * Get the logarithm base 2 of the profile probability. */ @@ -106,4 +122,25 @@ public class ProfileNode extends DeoptimizingFixedWithNextNode implements Lowera public static NodeIterable getProfileNodes(StructuredGraph graph) { return graph.getNodes().filter(ProfileNode.class); } + + protected abstract boolean canBeMergedWith(ProfileNode p); + + @Override + public void simplify(SimplifierTool tool) { + for (Node p = predecessor(); p != null; p = p.predecessor()) { + // Terminate search when we hit a control split or merge. + if (p instanceof ControlSplitNode || p instanceof AbstractMergeNode) { + break; + } + if (p instanceof ProfileNode) { + ProfileNode that = (ProfileNode) p; + if (this.canBeMergedWith(that)) { + that.setStep(this.getStep() + that.getStep()); + removeFixedWithUnusedInputs(this); + tool.addToWorkList(that); + break; + } + } + } + } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/profiling/ProfileWithNotificationNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/profiling/ProfileWithNotificationNode.java index d0fdcbc2867..1d9281a34da 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/profiling/ProfileWithNotificationNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/profiling/ProfileWithNotificationNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 @@ -28,7 +28,7 @@ import org.graalvm.compiler.nodeinfo.NodeInfo; import jdk.vm.ci.meta.ResolvedJavaMethod; @NodeInfo -public class ProfileWithNotificationNode extends ProfileNode { +public abstract class ProfileWithNotificationNode extends ProfileNode { public static final NodeClass TYPE = NodeClass.create(ProfileWithNotificationNode.class); protected int freqLog; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/OnStackReplacementPhase.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/OnStackReplacementPhase.java index df14d7adc37..116ef56436b 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/OnStackReplacementPhase.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/OnStackReplacementPhase.java @@ -22,10 +22,16 @@ */ package org.graalvm.compiler.hotspot.phases; +import static jdk.vm.ci.meta.SpeculationLog.SpeculationReason; import static org.graalvm.compiler.phases.common.DeadCodeEliminationPhase.Optionality.Required; +import jdk.vm.ci.meta.DeoptimizationAction; +import jdk.vm.ci.meta.DeoptimizationReason; +import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.JavaKind; import org.graalvm.compiler.core.common.PermanentBailoutException; import org.graalvm.compiler.core.common.cfg.Loop; +import org.graalvm.compiler.core.common.type.ObjectStamp; import org.graalvm.compiler.core.common.type.Stamp; import org.graalvm.compiler.debug.CounterKey; import org.graalvm.compiler.debug.DebugContext; @@ -37,13 +43,15 @@ import org.graalvm.compiler.loop.phases.LoopTransformations; import org.graalvm.compiler.nodeinfo.InputType; import org.graalvm.compiler.nodeinfo.Verbosity; import org.graalvm.compiler.nodes.AbstractBeginNode; -import org.graalvm.compiler.nodes.AbstractLocalNode; import org.graalvm.compiler.nodes.EntryMarkerNode; import org.graalvm.compiler.nodes.EntryProxyNode; +import org.graalvm.compiler.nodes.FixedGuardNode; import org.graalvm.compiler.nodes.FixedNode; import org.graalvm.compiler.nodes.FrameState; +import org.graalvm.compiler.nodes.LogicNode; import org.graalvm.compiler.nodes.LoopBeginNode; import org.graalvm.compiler.nodes.ParameterNode; +import org.graalvm.compiler.nodes.PiNode; import org.graalvm.compiler.nodes.StartNode; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; @@ -53,6 +61,7 @@ import org.graalvm.compiler.nodes.extended.OSRLockNode; import org.graalvm.compiler.nodes.extended.OSRMonitorEnterNode; import org.graalvm.compiler.nodes.extended.OSRStartNode; import org.graalvm.compiler.nodes.java.AccessMonitorNode; +import org.graalvm.compiler.nodes.java.InstanceOfNode; import org.graalvm.compiler.nodes.java.MonitorEnterNode; import org.graalvm.compiler.nodes.java.MonitorExitNode; import org.graalvm.compiler.nodes.java.MonitorIdNode; @@ -172,15 +181,32 @@ public class OnStackReplacementPhase extends Phase { if (value instanceof EntryProxyNode) { EntryProxyNode proxy = (EntryProxyNode) value; /* - * we need to drop the stamp since the types we see during OSR may be too precise - * (if a branch was not parsed for example). + * We need to drop the stamp since the types we see during OSR may be too precise + * (if a branch was not parsed for example). In cases when this is possible, we + * insert a guard and narrow the OSRLocal stamp at its usages. */ - Stamp s = proxy.stamp().unrestricted(); - AbstractLocalNode osrLocal = null; + Stamp narrowedStamp = proxy.value().stamp(); + Stamp unrestrictedStamp = proxy.stamp().unrestricted(); + ValueNode osrLocal; if (i >= localsSize) { - osrLocal = graph.addOrUnique(new OSRLockNode(i - localsSize, s)); + osrLocal = graph.addOrUnique(new OSRLockNode(i - localsSize, unrestrictedStamp)); } else { - osrLocal = graph.addOrUnique(new OSRLocalNode(i, s)); + osrLocal = graph.addOrUnique(new OSRLocalNode(i, unrestrictedStamp)); + } + // Speculate on the OSRLocal stamps that could be more precise. + OSRLocalSpeculationReason reason = new OSRLocalSpeculationReason(osrState.bci, narrowedStamp, i); + if (graph.getSpeculationLog().maySpeculate(reason) && osrLocal instanceof OSRLocalNode && value.getStackKind().equals(JavaKind.Object) && !narrowedStamp.isUnrestricted()) { + // Add guard. + LogicNode check = graph.addOrUniqueWithInputs(InstanceOfNode.createHelper((ObjectStamp) narrowedStamp, osrLocal, null, null)); + JavaConstant constant = graph.getSpeculationLog().speculate(reason); + FixedGuardNode guard = graph.add(new FixedGuardNode(check, DeoptimizationReason.OptimizedTypeCheckViolated, DeoptimizationAction.InvalidateRecompile, constant, false)); + graph.addAfterFixed(osrStart, guard); + + // Replace with a more specific type at usages. + // We know that we are at the root, + // so we need to replace the proxy in the state. + proxy.replaceAtMatchingUsages(osrLocal, n -> n == osrState); + osrLocal = graph.addOrUnique(new PiNode(osrLocal, narrowedStamp, guard)); } proxy.replaceAndDelete(osrLocal); } else { @@ -268,4 +294,30 @@ public class OnStackReplacementPhase extends Phase { public float codeSizeIncrease() { return 5.0f; } + + private static class OSRLocalSpeculationReason implements SpeculationReason { + private int bci; + private Stamp speculatedStamp; + private int localIndex; + + OSRLocalSpeculationReason(int bci, Stamp speculatedStamp, int localIndex) { + this.bci = bci; + this.speculatedStamp = speculatedStamp; + this.localIndex = localIndex; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof OSRLocalSpeculationReason) { + OSRLocalSpeculationReason that = (OSRLocalSpeculationReason) obj; + return this.bci == that.bci && this.speculatedStamp.equals(that.speculatedStamp) && this.localIndex == that.localIndex; + } + return false; + } + + @Override + public int hashCode() { + return (bci << 16) ^ speculatedStamp.hashCode() ^ localIndex; + } + } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/HotSpotReplacementsUtil.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/HotSpotReplacementsUtil.java index 37e7c1826a8..29a508882d8 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/HotSpotReplacementsUtil.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/HotSpotReplacementsUtil.java @@ -669,6 +669,11 @@ public class HotSpotReplacementsUtil { return config.useG1GC; } + @Fold + public static boolean useCMSIncrementalMode(@InjectedParameter GraalHotSpotVMConfig config) { + return config.cmsIncrementalMode; + } + @Fold public static boolean useCompressedOops(@InjectedParameter GraalHotSpotVMConfig config) { return config.useCompressedOops; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/HubGetClassNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/HubGetClassNode.java index c96dc5c3654..2084027388d 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/HubGetClassNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/HubGetClassNode.java @@ -71,7 +71,7 @@ public final class HubGetClassNode extends FloatingNode implements Lowerable, Ca return null; } else { MetaAccessProvider metaAccess = tool.getMetaAccess(); - if (metaAccess != null && hub.isConstant() && !GraalOptions.ImmutableCode.getValue(graph().getOptions())) { + if (metaAccess != null && hub.isConstant() && !GraalOptions.ImmutableCode.getValue(tool.getOptions())) { ResolvedJavaType exactType = tool.getConstantReflection().asJavaType(hub.asConstant()); if (exactType != null) { return ConstantNode.forConstant(tool.getConstantReflection().asJavaClass(exactType), metaAccess); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/IdentityHashCodeNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/IdentityHashCodeNode.java index 2577987d090..a28f4559f67 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/IdentityHashCodeNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/IdentityHashCodeNode.java @@ -67,7 +67,7 @@ public class IdentityHashCodeNode extends FixedWithNextNode implements Canonical if (object.isConstant()) { assert object.stamp() instanceof AbstractObjectStamp; JavaConstant c = (JavaConstant) object.asConstant(); - if (ImmutableCode.getValue(getOptions())) { + if (ImmutableCode.getValue(tool.getOptions())) { return this; } JavaConstant identityHashCode = null; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/ArrayCopySnippets.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/ArrayCopySnippets.java index bf68c43c48f..a8c59ade39b 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/ArrayCopySnippets.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/ArrayCopySnippets.java @@ -28,11 +28,12 @@ import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil. import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.arrayBaseOffset; import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.arrayClassElementOffset; import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.arrayIndexScale; -import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.layoutHelperElementTypePrimitiveInPlace; import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.loadHub; import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.readLayoutHelper; import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.superCheckOffsetOffset; -import static org.graalvm.compiler.nodes.extended.BranchProbabilityNode.FAST_PATH_PROBABILITY; +import static org.graalvm.compiler.nodes.extended.BranchProbabilityNode.FREQUENT_PROBABILITY; +import static org.graalvm.compiler.nodes.extended.BranchProbabilityNode.LIKELY_PROBABILITY; +import static org.graalvm.compiler.nodes.extended.BranchProbabilityNode.NOT_FREQUENT_PROBABILITY; import static org.graalvm.compiler.nodes.extended.BranchProbabilityNode.SLOW_PATH_PROBABILITY; import static org.graalvm.compiler.nodes.extended.BranchProbabilityNode.probability; @@ -47,10 +48,8 @@ import org.graalvm.compiler.debug.DebugHandlersFactory; import org.graalvm.compiler.debug.GraalError; import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.hotspot.meta.HotSpotProviders; -import org.graalvm.compiler.hotspot.nodes.type.KlassPointerStamp; import org.graalvm.compiler.hotspot.word.KlassPointer; import org.graalvm.compiler.nodes.CallTargetNode; -import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.DeoptimizeNode; import org.graalvm.compiler.nodes.Invoke; import org.graalvm.compiler.nodes.InvokeNode; @@ -65,8 +64,10 @@ import org.graalvm.compiler.nodes.spi.LoweringTool; import org.graalvm.compiler.nodes.type.StampTool; import org.graalvm.compiler.nodes.util.GraphUtil; import org.graalvm.compiler.options.OptionValues; +import org.graalvm.compiler.replacements.ReplacementsUtil; import org.graalvm.compiler.replacements.SnippetCounter; import org.graalvm.compiler.replacements.SnippetCounter.Group; +import org.graalvm.compiler.replacements.SnippetIntegerHistogram; import org.graalvm.compiler.replacements.SnippetTemplate; import org.graalvm.compiler.replacements.SnippetTemplate.Arguments; import org.graalvm.compiler.replacements.SnippetTemplate.SnippetInfo; @@ -79,16 +80,208 @@ import org.graalvm.word.LocationIdentity; import org.graalvm.word.WordFactory; import jdk.vm.ci.code.TargetDescription; -import jdk.vm.ci.hotspot.HotSpotResolvedObjectType; import jdk.vm.ci.meta.DeoptimizationAction; import jdk.vm.ci.meta.DeoptimizationReason; -import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; public class ArrayCopySnippets implements Snippets { + private enum ArrayCopyTypeCheck { + UNDEFINED_ARRAY_TYPE_CHECK, + // we know that both objects are arrays and have the same type + NO_ARRAY_TYPE_CHECK, + // can be used when we know that one of the objects is a primitive array + HUB_BASED_ARRAY_TYPE_CHECK, + // must be used when we don't have sufficient information to use one of the others + LAYOUT_HELPER_BASED_ARRAY_TYPE_CHECK + } + + @Snippet + public static void arraycopyZeroLengthSnippet(Object src, int srcPos, Object dest, int destPos, int length, @ConstantParameter ArrayCopyTypeCheck arrayTypeCheck, + @ConstantParameter Counters counters) { + Object nonNullSrc = GraalDirectives.guardingNonNull(src); + Object nonNullDest = GraalDirectives.guardingNonNull(dest); + checkArrayTypes(nonNullSrc, nonNullDest, arrayTypeCheck); + checkLimits(nonNullSrc, srcPos, nonNullDest, destPos, length, counters); + counters.zeroLengthStaticCounter.inc(); + } + + @Snippet + public static void arraycopyExactSnippet(Object src, int srcPos, Object dest, int destPos, int length, @ConstantParameter ArrayCopyTypeCheck arrayTypeCheck, + @ConstantParameter JavaKind elementKind, @ConstantParameter SnippetCounter elementKindCounter, @ConstantParameter SnippetCounter elementKindCopiedCounter, + @ConstantParameter Counters counters) { + Object nonNullSrc = GraalDirectives.guardingNonNull(src); + Object nonNullDest = GraalDirectives.guardingNonNull(dest); + checkArrayTypes(nonNullSrc, nonNullDest, arrayTypeCheck); + checkLimits(nonNullSrc, srcPos, nonNullDest, destPos, length, counters); + incrementLengthCounter(length, counters); + + elementKindCounter.inc(); + elementKindCopiedCounter.add(length); + ArrayCopyCallNode.arraycopy(nonNullSrc, srcPos, nonNullDest, destPos, length, elementKind); + } + + @Snippet + public static void arraycopyUnrolledSnippet(Object src, int srcPos, Object dest, int destPos, int length, @ConstantParameter ArrayCopyTypeCheck arrayTypeCheck, + @ConstantParameter JavaKind elementKind, @ConstantParameter int unrolledLength, @ConstantParameter Counters counters) { + Object nonNullSrc = GraalDirectives.guardingNonNull(src); + Object nonNullDest = GraalDirectives.guardingNonNull(dest); + checkArrayTypes(nonNullSrc, nonNullDest, arrayTypeCheck); + checkLimits(nonNullSrc, srcPos, nonNullDest, destPos, length, counters); + incrementLengthCounter(length, counters); + + unrolledArraycopyWork(nonNullSrc, srcPos, nonNullDest, destPos, unrolledLength, elementKind); + } + + @Snippet + public static void arraycopyCheckcastSnippet(Object src, int srcPos, Object dest, int destPos, int length, @ConstantParameter ArrayCopyTypeCheck arrayTypeCheck, + @ConstantParameter Counters counters, @ConstantParameter SnippetInfo workSnippet, @ConstantParameter JavaKind elementKind) { + Object nonNullSrc = GraalDirectives.guardingNonNull(src); + Object nonNullDest = GraalDirectives.guardingNonNull(dest); + checkArrayTypes(nonNullSrc, nonNullDest, arrayTypeCheck); + checkLimits(nonNullSrc, srcPos, nonNullDest, destPos, length, counters); + incrementLengthCounter(length, counters); + + ArrayCopyWithSlowPathNode.arraycopy(nonNullSrc, srcPos, nonNullDest, destPos, length, workSnippet, elementKind); + } + + @Snippet + public static void arraycopyGenericSnippet(Object src, int srcPos, Object dest, int destPos, int length, @ConstantParameter ArrayCopyTypeCheck arrayTypeCheck, @ConstantParameter Counters counters, + @ConstantParameter SnippetInfo workSnippet, @ConstantParameter JavaKind elementKind) { + Object nonNullSrc = GraalDirectives.guardingNonNull(src); + Object nonNullDest = GraalDirectives.guardingNonNull(dest); + checkArrayTypes(nonNullSrc, nonNullDest, arrayTypeCheck); + checkLimits(nonNullSrc, srcPos, nonNullDest, destPos, length, counters); + incrementLengthCounter(length, counters); + + ArrayCopyWithSlowPathNode.arraycopy(nonNullSrc, srcPos, nonNullDest, destPos, length, workSnippet, elementKind); + } + + @Snippet + public static void arraycopyNativeSnippet(Object src, int srcPos, Object dest, int destPos, int length, @ConstantParameter Counters counters) { + // all checks are done in the native method, so no need to emit additional checks here + incrementLengthCounter(length, counters); + counters.systemArraycopyCounter.inc(); + counters.systemArraycopyCopiedCounter.add(length); + + System.arraycopy(src, srcPos, dest, destPos, length); + } + + @Fold + static LocationIdentity getArrayLocation(JavaKind kind) { + return NamedLocationIdentity.getArrayLocation(kind); + } + + private static void unrolledArraycopyWork(Object nonNullSrc, int srcPos, Object nonNullDest, int destPos, int length, JavaKind elementKind) { + int scale = arrayIndexScale(elementKind); + int arrayBaseOffset = arrayBaseOffset(elementKind); + LocationIdentity arrayLocation = getArrayLocation(elementKind); + + long sourceOffset = arrayBaseOffset + (long) srcPos * scale; + long destOffset = arrayBaseOffset + (long) destPos * scale; + long position = 0; + long delta = scale; + if (probability(NOT_FREQUENT_PROBABILITY, nonNullSrc == nonNullDest && srcPos < destPos)) { + // bad aliased case so we need to copy the array from back to front + position = (long) (length - 1) * scale; + delta = -delta; + } + + // the length was already checked before - we can emit unconditional instructions + ExplodeLoopNode.explodeLoop(); + for (int iteration = 0; iteration < length; iteration++) { + Object value = RawLoadNode.load(nonNullSrc, sourceOffset + position, elementKind, arrayLocation); + RawStoreNode.storeObject(nonNullDest, destOffset + position, value, elementKind, arrayLocation, false); + position += delta; + } + } + + @Snippet(allowPartialIntrinsicArgumentMismatch = true) + public static void checkcastArraycopyWithSlowPathWork(Object src, int srcPos, Object dest, int destPos, int length, @ConstantParameter Counters counters) { + if (probability(FREQUENT_PROBABILITY, length > 0)) { + Object nonNullSrc = PiNode.asNonNullObject(src); + Object nonNullDest = PiNode.asNonNullObject(dest); + KlassPointer srcKlass = loadHub(nonNullSrc); + KlassPointer destKlass = loadHub(nonNullDest); + if (probability(LIKELY_PROBABILITY, srcKlass == destKlass)) { + // no storecheck required. + counters.objectCheckcastSameTypeCounter.inc(); + counters.objectCheckcastSameTypeCopiedCounter.add(length); + ArrayCopyCallNode.arraycopyObjectKillsAny(nonNullSrc, srcPos, nonNullDest, destPos, length); + } else { + KlassPointer destElemKlass = destKlass.readKlassPointer(arrayClassElementOffset(INJECTED_VMCONFIG), OBJ_ARRAY_KLASS_ELEMENT_KLASS_LOCATION); + Word superCheckOffset = WordFactory.signed(destElemKlass.readInt(superCheckOffsetOffset(INJECTED_VMCONFIG), KLASS_SUPER_CHECK_OFFSET_LOCATION)); + + counters.objectCheckcastDifferentTypeCounter.inc(); + counters.objectCheckcastDifferentTypeCopiedCounter.add(length); + + int copiedElements = CheckcastArrayCopyCallNode.checkcastArraycopy(nonNullSrc, srcPos, nonNullDest, destPos, length, superCheckOffset, destElemKlass, false); + if (probability(SLOW_PATH_PROBABILITY, copiedElements != 0)) { + /* + * the stub doesn't throw the ArrayStoreException, but returns the number of + * copied elements (xor'd with -1). + */ + copiedElements ^= -1; + System.arraycopy(nonNullSrc, srcPos + copiedElements, nonNullDest, destPos + copiedElements, length - copiedElements); + } + } + } + } + + @Snippet(allowPartialIntrinsicArgumentMismatch = true) + public static void genericArraycopyWithSlowPathWork(Object src, int srcPos, Object dest, int destPos, int length, @ConstantParameter Counters counters) { + if (probability(FREQUENT_PROBABILITY, length > 0)) { + counters.genericArraycopyDifferentTypeCounter.inc(); + counters.genericArraycopyDifferentTypeCopiedCounter.add(length); + int copiedElements = GenericArrayCopyCallNode.genericArraycopy(src, srcPos, dest, destPos, length); + if (probability(SLOW_PATH_PROBABILITY, copiedElements != 0)) { + /* + * the stub doesn't throw the ArrayStoreException, but returns the number of copied + * elements (xor'd with -1). + */ + copiedElements ^= -1; + System.arraycopy(src, srcPos + copiedElements, dest, destPos + copiedElements, length - copiedElements); + } + } + } + + private static void incrementLengthCounter(int length, Counters counters) { + counters.lengthHistogram.inc(length); + } + + private static void checkLimits(Object src, int srcPos, Object dest, int destPos, int length, Counters counters) { + if (probability(SLOW_PATH_PROBABILITY, srcPos < 0) || + probability(SLOW_PATH_PROBABILITY, destPos < 0) || + probability(SLOW_PATH_PROBABILITY, length < 0) || + probability(SLOW_PATH_PROBABILITY, srcPos > ArrayLengthNode.arrayLength(src) - length) || + probability(SLOW_PATH_PROBABILITY, destPos > ArrayLengthNode.arrayLength(dest) - length)) { + counters.checkAIOOBECounter.inc(); + DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.RuntimeConstraint); + } + counters.checkSuccessCounter.inc(); + } + + private static void checkArrayTypes(Object nonNullSrc, Object nonNullDest, ArrayCopyTypeCheck arrayTypeCheck) { + if (arrayTypeCheck == ArrayCopyTypeCheck.NO_ARRAY_TYPE_CHECK) { + // nothing to do + } else if (arrayTypeCheck == ArrayCopyTypeCheck.HUB_BASED_ARRAY_TYPE_CHECK) { + KlassPointer srcHub = loadHub(nonNullSrc); + KlassPointer destHub = loadHub(nonNullDest); + if (probability(SLOW_PATH_PROBABILITY, srcHub != destHub)) { + DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.RuntimeConstraint); + } + } else if (arrayTypeCheck == ArrayCopyTypeCheck.LAYOUT_HELPER_BASED_ARRAY_TYPE_CHECK) { + KlassPointer srcHub = loadHub(nonNullSrc); + KlassPointer destHub = loadHub(nonNullDest); + checkArrayType(srcHub); + checkArrayType(destHub); + } else { + ReplacementsUtil.staticAssert(false, "unknown array type check"); + } + } + private static int checkArrayType(KlassPointer nonNullHub) { int layoutHelper = readLayoutHelper(nonNullHub); if (probability(SLOW_PATH_PROBABILITY, layoutHelper >= 0)) { @@ -97,528 +290,228 @@ public class ArrayCopySnippets implements Snippets { return layoutHelper; } - private static void checkLimits(Object src, int srcPos, Object dest, int destPos, int length, Counters counters) { - if (probability(SLOW_PATH_PROBABILITY, srcPos < 0)) { - counters.checkAIOOBECounter.inc(); - DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.RuntimeConstraint); - } - if (probability(SLOW_PATH_PROBABILITY, destPos < 0)) { - counters.checkAIOOBECounter.inc(); - DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.RuntimeConstraint); - } - if (probability(SLOW_PATH_PROBABILITY, length < 0)) { - counters.checkAIOOBECounter.inc(); - DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.RuntimeConstraint); - } - if (probability(SLOW_PATH_PROBABILITY, srcPos > ArrayLengthNode.arrayLength(src) - length)) { - counters.checkAIOOBECounter.inc(); - DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.RuntimeConstraint); - } - if (probability(SLOW_PATH_PROBABILITY, destPos > ArrayLengthNode.arrayLength(dest) - length)) { - counters.checkAIOOBECounter.inc(); - DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.RuntimeConstraint); - } - counters.checkSuccessCounter.inc(); - } - - @Snippet - public static void arraycopyZeroLengthIntrinsic(Object src, int srcPos, Object dest, int destPos, int length, @ConstantParameter Counters counters) { - Object nonNullSrc = GraalDirectives.guardingNonNull(src); - Object nonNullDest = GraalDirectives.guardingNonNull(dest); - KlassPointer srcHub = loadHub(nonNullSrc); - KlassPointer destHub = loadHub(nonNullDest); - checkArrayType(srcHub); - checkArrayType(destHub); - checkLimits(nonNullSrc, srcPos, nonNullDest, destPos, length, counters); - counters.zeroLengthStaticCounter.inc(); - } - - @Snippet - public static void arraycopyExactIntrinsic(Object src, int srcPos, Object dest, int destPos, int length, @ConstantParameter JavaKind elementKind, @ConstantParameter SnippetCounter counter, - @ConstantParameter SnippetCounter copiedCounter, @ConstantParameter Counters counters) { - Object nonNullSrc = GraalDirectives.guardingNonNull(src); - Object nonNullDest = GraalDirectives.guardingNonNull(dest); - checkLimits(nonNullSrc, srcPos, nonNullDest, destPos, length, counters); - counter.inc(); - copiedCounter.add(length); - ArrayCopyCallNode.arraycopy(nonNullSrc, srcPos, nonNullDest, destPos, length, elementKind); - if (length == 0) { - counters.zeroLengthDynamicCounter.inc(); - } else { - counters.nonZeroLengthDynamicCounter.inc(); - counters.nonZeroLengthDynamicCopiedCounter.add(length); - } - } - - /** - * This intrinsic is useful for the case where we know something statically about one of the - * inputs but not the other. - */ - @Snippet - public static void arraycopyPredictedExactIntrinsic(Object src, int srcPos, Object dest, int destPos, int length, @ConstantParameter JavaKind elementKind, - @ConstantParameter SnippetCounter counter, @ConstantParameter SnippetCounter copiedCounter, @ConstantParameter Counters counters) { - Object nonNullSrc = GraalDirectives.guardingNonNull(src); - Object nonNullDest = GraalDirectives.guardingNonNull(dest); - KlassPointer srcHub = loadHub(nonNullSrc); - KlassPointer destHub = loadHub(nonNullDest); - if (probability(SLOW_PATH_PROBABILITY, srcHub != destHub)) { - DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.RuntimeConstraint); - } - checkLimits(nonNullSrc, srcPos, nonNullDest, destPos, length, counters); - counter.inc(); - copiedCounter.add(length); - ArrayCopyCallNode.arraycopy(nonNullSrc, srcPos, nonNullDest, destPos, length, elementKind); - if (length == 0) { - counters.zeroLengthDynamicCounter.inc(); - } else { - counters.nonZeroLengthDynamicCounter.inc(); - counters.nonZeroLengthDynamicCopiedCounter.add(length); - } - } - - @Snippet - public static void arraycopyPredictedObjectWork(Object nonNullSrc, int srcPos, Object nonNullDest, int destPos, int length, KlassPointer objectArrayKlass, - @ConstantParameter SnippetCounter counter, @ConstantParameter SnippetCounter copiedCounter, @ConstantParameter Counters counters) { - if (length > 0) { - 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); - counters.predictedObjectArrayCopyFastPathCounter.inc(); - counters.predictedObjectArrayCopyFastPathCopiedCounter.add(length); - ArrayCopyCallNode.arraycopyObjectKillsAny(nonNullSrc, srcPos, nonNullDest, destPos, length); - } else { - counters.predictedObjectArrayCopySlowPathCounter.inc(); - counters.predictedObjectArrayCopySlowPathCopiedCounter.add(length); - System.arraycopy(nonNullSrc, srcPos, nonNullDest, destPos, length); - } - } - } - - /** - * This is the basic template for the full arraycopy checks, including a check that the - * underlying type is really an array type. - */ - @Snippet - public static void arraycopySlowPathIntrinsic(Object src, int srcPos, Object dest, int destPos, int length, KlassPointer predictedKlass, @ConstantParameter JavaKind elementKind, - @ConstantParameter SnippetInfo slowPath, @ConstantParameter Object slowPathArgument, @ConstantParameter Counters counters) { - Object nonNullSrc = GraalDirectives.guardingNonNull(src); - Object nonNullDest = GraalDirectives.guardingNonNull(dest); - KlassPointer srcHub = loadHub(nonNullSrc); - KlassPointer destHub = loadHub(nonNullDest); - checkArrayType(srcHub); - checkArrayType(destHub); - checkLimits(nonNullSrc, srcPos, nonNullDest, destPos, length, counters); - if (length == 0) { - counters.zeroLengthDynamicCounter.inc(); - } else { - counters.nonZeroLengthDynamicCounter.inc(); - counters.nonZeroLengthDynamicCopiedCounter.add(length); - } - ArrayCopySlowPathNode.arraycopy(nonNullSrc, srcPos, nonNullDest, destPos, length, predictedKlass, elementKind, slowPath, slowPathArgument); - } - - /** - * Snippet for unrolled arraycopy. - */ - @Snippet - public static void arraycopyUnrolledIntrinsic(Object src, int srcPos, Object dest, int destPos, int length, @ConstantParameter int unrolledLength, @ConstantParameter JavaKind elementKind, - @ConstantParameter Counters counters) { - Object nonNullSrc = GraalDirectives.guardingNonNull(src); - Object nonNullDest = GraalDirectives.guardingNonNull(dest); - checkLimits(nonNullSrc, srcPos, nonNullDest, destPos, length, counters); - if (length == 0) { - counters.zeroLengthDynamicCounter.inc(); - } else { - counters.nonZeroLengthDynamicCounter.inc(); - counters.nonZeroLengthDynamicCopiedCounter.add(length); - } - ArrayCopyUnrollNode.arraycopy(nonNullSrc, srcPos, nonNullDest, destPos, length, unrolledLength, elementKind); - } - - @Snippet - public static void checkcastArraycopyWork(Object nonNullSrc, int srcPos, Object nonNullDest, int destPos, int length, @ConstantParameter Counters counters) { - if (length > 0) { - KlassPointer destKlass = loadHub(nonNullDest); - KlassPointer srcKlass = loadHub(nonNullSrc); - if (probability(SLOW_PATH_PROBABILITY, srcKlass == destKlass)) { - // no storecheck required. - counters.objectCheckcastSameTypeCounter.inc(); - counters.objectCheckcastSameTypeCopiedCounter.add(length); - ArrayCopyCallNode.arraycopyObjectKillsAny(nonNullSrc, srcPos, nonNullDest, destPos, length); - } else { - KlassPointer destElemKlass = destKlass.readKlassPointer(arrayClassElementOffset(INJECTED_VMCONFIG), OBJ_ARRAY_KLASS_ELEMENT_KLASS_LOCATION); - Word superCheckOffset = WordFactory.signed(destElemKlass.readInt(superCheckOffsetOffset(INJECTED_VMCONFIG), KLASS_SUPER_CHECK_OFFSET_LOCATION)); - counters.objectCheckcastCounter.inc(); - counters.objectCheckcastCopiedCounter.add(length); - int copiedElements = CheckcastArrayCopyCallNode.checkcastArraycopy(nonNullSrc, srcPos, nonNullDest, destPos, length, superCheckOffset, destElemKlass, false); - if (copiedElements != 0) { - /* - * the checkcast stub doesn't throw the ArrayStoreException, but returns the - * number of copied elements (xor'd with -1). - */ - copiedElements ^= -1; - System.arraycopy(nonNullSrc, srcPos + copiedElements, nonNullDest, destPos + copiedElements, length - copiedElements); - } - } - } - } - - @Snippet - public static void arraycopyGeneric(Object src, int srcPos, Object dest, int destPos, int length, @ConstantParameter Counters counters) { - Object nonNullSrc = GraalDirectives.guardingNonNull(src); - Object nonNullDest = GraalDirectives.guardingNonNull(dest); - KlassPointer srcHub = loadHub(nonNullSrc); - KlassPointer destHub = loadHub(nonNullDest); - if (probability(FAST_PATH_PROBABILITY, srcHub.equal(destHub)) && probability(FAST_PATH_PROBABILITY, nonNullSrc != nonNullDest)) { - int layoutHelper = checkArrayType(srcHub); - final boolean isObjectArray = ((layoutHelper & layoutHelperElementTypePrimitiveInPlace(INJECTED_VMCONFIG)) == 0); - checkLimits(nonNullSrc, srcPos, nonNullDest, destPos, length, counters); - if (probability(FAST_PATH_PROBABILITY, isObjectArray)) { - counters.genericObjectExactCallCounter.inc(); - counters.genericObjectExactCallCopiedCounter.add(length); - ArrayCopyCallNode.disjointArraycopy(nonNullSrc, srcPos, nonNullDest, destPos, length, JavaKind.Object); - } else { - counters.genericPrimitiveCallCounter.inc(); - counters.genericPrimitiveCallCopiedCounter.add(length); - UnsafeArrayCopyNode.arraycopyPrimitive(nonNullSrc, srcPos, nonNullDest, destPos, length, layoutHelper); - } - } else { - counters.systemArraycopyCounter.inc(); - counters.systemArraycopyCopiedCounter.add(length); - System.arraycopy(nonNullSrc, srcPos, nonNullDest, destPos, length); - } - } - - @Fold - static LocationIdentity getArrayLocation(JavaKind kind) { - return NamedLocationIdentity.getArrayLocation(kind); - } - - @Snippet - public static void arraycopyUnrolledWork(Object nonNullSrc, int srcPos, Object nonNullDest, int destPos, @ConstantParameter int length, @ConstantParameter JavaKind elementKind) { - final int scale = arrayIndexScale(elementKind); - int arrayBaseOffset = arrayBaseOffset(elementKind); - LocationIdentity arrayLocation = getArrayLocation(elementKind); - if (nonNullSrc == nonNullDest && srcPos < destPos) { // bad aliased case - long start = (long) (length - 1) * scale; - long i = start; - ExplodeLoopNode.explodeLoop(); - for (int iteration = 0; iteration < length; iteration++) { - if (i >= 0) { - Object a = RawLoadNode.load(nonNullSrc, arrayBaseOffset + i + (long) srcPos * scale, elementKind, arrayLocation); - RawStoreNode.storeObject(nonNullDest, arrayBaseOffset + i + (long) destPos * scale, a, elementKind, arrayLocation, false); - i -= scale; - } - } - } else { - long end = (long) length * scale; - long i = 0; - ExplodeLoopNode.explodeLoop(); - for (int iteration = 0; iteration < length; iteration++) { - if (i < end) { - Object a = RawLoadNode.load(nonNullSrc, arrayBaseOffset + i + (long) srcPos * scale, elementKind, arrayLocation); - RawStoreNode.storeObject(nonNullDest, arrayBaseOffset + i + (long) destPos * scale, a, elementKind, arrayLocation, false); - i += scale; - } - } - } - } - static class Counters { final SnippetCounter checkSuccessCounter; final SnippetCounter checkAIOOBECounter; - final SnippetCounter objectCheckcastCounter; - final SnippetCounter objectCheckcastSameTypeCounter; - final SnippetCounter predictedObjectArrayCopySlowPathCounter; - final SnippetCounter predictedObjectArrayCopyFastPathCounter; - - final SnippetCounter genericPrimitiveCallCounter; - final SnippetCounter genericObjectExactCallCounter; - final SnippetCounter systemArraycopyCounter; - final SnippetCounter zeroLengthStaticCounter; - final SnippetCounter zeroLengthDynamicCounter; - final SnippetCounter nonZeroLengthDynamicCounter; + final SnippetIntegerHistogram lengthHistogram; - final SnippetCounter nonZeroLengthDynamicCopiedCounter; - final SnippetCounter genericPrimitiveCallCopiedCounter; - final SnippetCounter genericObjectExactCallCopiedCounter; + final SnippetCounter systemArraycopyCounter; final SnippetCounter systemArraycopyCopiedCounter; - final SnippetCounter objectCheckcastCopiedCounter; + final SnippetCounter genericArraycopyDifferentTypeCopiedCounter; + final SnippetCounter genericArraycopyDifferentTypeCounter; + final SnippetCounter objectCheckcastSameTypeCopiedCounter; - final SnippetCounter predictedObjectArrayCopySlowPathCopiedCounter; - final SnippetCounter predictedObjectArrayCopyFastPathCopiedCounter; + final SnippetCounter objectCheckcastSameTypeCounter; + final SnippetCounter objectCheckcastDifferentTypeCopiedCounter; + final SnippetCounter objectCheckcastDifferentTypeCounter; final EnumMap arraycopyCallCounters = new EnumMap<>(JavaKind.class); - final EnumMap arraycopyCounters = new EnumMap<>(JavaKind.class); - final EnumMap arraycopyCallCopiedCounters = new EnumMap<>(JavaKind.class); - final EnumMap arraycopyCopiedCounters = new EnumMap<>(JavaKind.class); Counters(SnippetCounter.Group.Factory factory) { final Group checkCounters = factory.createSnippetCounterGroup("System.arraycopy checkInputs"); - final Group counters = factory.createSnippetCounterGroup("System.arraycopy"); - final Group copiedCounters = factory.createSnippetCounterGroup("System.arraycopy copied elements"); - final Group lengthCounters = factory.createSnippetCounterGroup("System.arraycopy 0-length checks"); + final Group callCounters = factory.createSnippetCounterGroup("System.arraycopy calls"); + final Group copiedElementsCounters = factory.createSnippetCounterGroup("System.arraycopy copied elements"); + final Group lengthCounters = factory.createSnippetCounterGroup("System.arraycopy with 0-length"); checkSuccessCounter = new SnippetCounter(checkCounters, "checkSuccess", "checkSuccess"); checkAIOOBECounter = new SnippetCounter(checkCounters, "checkAIOOBE", "checkAIOOBE"); - objectCheckcastCounter = new SnippetCounter(counters, "Object[]{non-exact}", "arraycopy for non-exact Object[] arrays"); - objectCheckcastSameTypeCounter = new SnippetCounter(counters, "Object[]{same-type}", "arraycopy call for src.klass == dest.klass Object[] arrays"); - predictedObjectArrayCopySlowPathCounter = new SnippetCounter(counters, "Object[]{slow-path}", "used System.arraycopy slow path for predicted Object[] arrays"); - predictedObjectArrayCopyFastPathCounter = new SnippetCounter(counters, "Object[]{fast-path}", "used oop_arraycopy for predicted Object[] arrays"); - genericPrimitiveCallCounter = new SnippetCounter(counters, "genericPrimitive", "generic arraycopy snippet for primitive arrays"); - genericObjectExactCallCounter = new SnippetCounter(counters, "genericObjectExact", "generic arraycopy snippet for special object arrays"); - systemArraycopyCounter = new SnippetCounter(counters, "genericObject", "call to System.arraycopy"); + zeroLengthStaticCounter = new SnippetCounter(lengthCounters, "0-length copy static", "calls where the length is statically 0"); + lengthHistogram = new SnippetIntegerHistogram(lengthCounters, 2, "length", "length"); - zeroLengthStaticCounter = new SnippetCounter(lengthCounters, "0-lengthcopy static", "arraycopy where the length is statically 0"); - zeroLengthDynamicCounter = new SnippetCounter(lengthCounters, "0-lengthcopy dynamically", "arraycopy where the length is dynamically 0"); - nonZeroLengthDynamicCounter = new SnippetCounter(lengthCounters, "non-0-lengthcopy dynamically", "arraycopy where the length is dynamically not zero"); + systemArraycopyCounter = new SnippetCounter(callCounters, "native System.arraycopy", "JNI-based System.arraycopy call"); + systemArraycopyCopiedCounter = new SnippetCounter(copiedElementsCounters, "native System.arraycopy", "JNI-based System.arraycopy call"); - nonZeroLengthDynamicCopiedCounter = new SnippetCounter(copiedCounters, "non-0-lengthcopy dynamically", "arraycopy where the length is dynamically not zero"); - genericPrimitiveCallCopiedCounter = new SnippetCounter(copiedCounters, "genericPrimitive", "generic arraycopy snippet for primitive arrays"); - genericObjectExactCallCopiedCounter = new SnippetCounter(copiedCounters, "genericObjectExact", "generic arraycopy snippet for special object arrays"); - systemArraycopyCopiedCounter = new SnippetCounter(copiedCounters, "genericObject", "call to System.arraycopy"); + genericArraycopyDifferentTypeCounter = new SnippetCounter(callCounters, "generic[] stub", "generic arraycopy stub"); + genericArraycopyDifferentTypeCopiedCounter = new SnippetCounter(copiedElementsCounters, "generic[] stub", "generic arraycopy stub"); - objectCheckcastCopiedCounter = new SnippetCounter(copiedCounters, "Object[]{non-exact}", "arraycopy for non-exact Object[] arrays"); - objectCheckcastSameTypeCopiedCounter = new SnippetCounter(copiedCounters, "Object[]{same-type}", "arraycopy call for src.klass == dest.klass Object[] arrays"); - predictedObjectArrayCopySlowPathCopiedCounter = new SnippetCounter(copiedCounters, "Object[]{slow-path}", - "used System.arraycopy slow path for predicted Object[] arrays"); - predictedObjectArrayCopyFastPathCopiedCounter = new SnippetCounter(copiedCounters, "Object[]{fast-path}", "used oop_arraycopy for predicted Object[] arrays"); - createArraycopyCounter(JavaKind.Byte, counters, copiedCounters); - createArraycopyCounter(JavaKind.Boolean, counters, copiedCounters); - createArraycopyCounter(JavaKind.Char, counters, copiedCounters); - createArraycopyCounter(JavaKind.Short, counters, copiedCounters); - createArraycopyCounter(JavaKind.Int, counters, copiedCounters); - createArraycopyCounter(JavaKind.Long, counters, copiedCounters); - createArraycopyCounter(JavaKind.Float, counters, copiedCounters); - createArraycopyCounter(JavaKind.Double, counters, copiedCounters); - createArraycopyCounter(JavaKind.Object, counters, copiedCounters); + objectCheckcastSameTypeCounter = new SnippetCounter(callCounters, "checkcast object[] (same-type)", "checkcast object[] stub but src.klass == dest.klass Object[] arrays"); + objectCheckcastSameTypeCopiedCounter = new SnippetCounter(copiedElementsCounters, "checkcast object[] (same-type)", "checkcast object[] stub but src.klass == dest.klass Object[] arrays"); + objectCheckcastDifferentTypeCounter = new SnippetCounter(callCounters, "checkcast object[] (store-check)", "checkcast object[] stub with store check"); + objectCheckcastDifferentTypeCopiedCounter = new SnippetCounter(copiedElementsCounters, "checkcast object[] (store-check)", "checkcast object[] stub with store check"); + + createArraycopyCounter(JavaKind.Byte, callCounters, copiedElementsCounters); + createArraycopyCounter(JavaKind.Boolean, callCounters, copiedElementsCounters); + createArraycopyCounter(JavaKind.Char, callCounters, copiedElementsCounters); + createArraycopyCounter(JavaKind.Short, callCounters, copiedElementsCounters); + createArraycopyCounter(JavaKind.Int, callCounters, copiedElementsCounters); + createArraycopyCounter(JavaKind.Long, callCounters, copiedElementsCounters); + createArraycopyCounter(JavaKind.Float, callCounters, copiedElementsCounters); + createArraycopyCounter(JavaKind.Double, callCounters, copiedElementsCounters); + createArraycopyCounter(JavaKind.Object, callCounters, copiedElementsCounters); } void createArraycopyCounter(JavaKind kind, Group counters, Group copiedCounters) { - arraycopyCallCounters.put(kind, new SnippetCounter(counters, kind + "[]{stub}", "arraycopy call for " + kind + "[] arrays")); - arraycopyCounters.put(kind, new SnippetCounter(counters, kind + "[]{inline}", "inline arraycopy for " + kind + "[] arrays")); - - arraycopyCallCopiedCounters.put(kind, new SnippetCounter(copiedCounters, kind + "[]{stub}", "arraycopy call for " + kind + "[] arrays")); - arraycopyCopiedCounters.put(kind, new SnippetCounter(copiedCounters, kind + "[]{inline}", "inline arraycopy for " + kind + "[] arrays")); + arraycopyCallCounters.put(kind, new SnippetCounter(counters, kind + "[] stub", "arraycopy call for " + kind + "[] arrays")); + arraycopyCallCopiedCounters.put(kind, new SnippetCounter(copiedCounters, kind + "[] stub", "arraycopy call for " + kind + "[] arrays")); } } public static class Templates extends SnippetTemplate.AbstractTemplates { + private final SnippetInfo arraycopyGenericSnippet = snippet("arraycopyGenericSnippet"); + private final SnippetInfo arraycopyUnrolledSnippet = snippet("arraycopyUnrolledSnippet"); + private final SnippetInfo arraycopyExactSnippet = snippet("arraycopyExactSnippet"); + private final SnippetInfo arraycopyZeroLengthSnippet = snippet("arraycopyZeroLengthSnippet"); + private final SnippetInfo arraycopyCheckcastSnippet = snippet("arraycopyCheckcastSnippet"); + private final SnippetInfo arraycopyNativeSnippet = snippet("arraycopyNativeSnippet"); + + private final SnippetInfo checkcastArraycopyWithSlowPathWork = snippet("checkcastArraycopyWithSlowPathWork"); + private final SnippetInfo genericArraycopyWithSlowPathWork = snippet("genericArraycopyWithSlowPathWork"); + + private ResolvedJavaMethod originalArraycopy; + private final Counters counters; public Templates(OptionValues options, Iterable factories, SnippetCounter.Group.Factory factory, HotSpotProviders providers, TargetDescription target) { super(options, factories, providers, providers.getSnippetReflection(), target); this.counters = new Counters(factory); } - private ResolvedJavaMethod originalArraycopy() throws GraalError { - if (originalArraycopy == null) { - Method method; - try { - method = System.class.getDeclaredMethod("arraycopy", Object.class, int.class, Object.class, int.class, int.class); - } catch (NoSuchMethodException | SecurityException e) { - throw new GraalError(e); - } - originalArraycopy = providers.getMetaAccess().lookupJavaMethod(method); - } - return originalArraycopy; - } - - private ResolvedJavaMethod originalArraycopy; - - private final SnippetInfo checkcastArraycopyWorkSnippet = snippet("checkcastArraycopyWork"); - private final SnippetInfo arraycopyGenericSnippet = snippet("arraycopyGeneric"); - - private final SnippetInfo arraycopySlowPathIntrinsicSnippet = snippet("arraycopySlowPathIntrinsic"); - private final SnippetInfo arraycopyUnrolledIntrinsicSnippet = snippet("arraycopyUnrolledIntrinsic"); - private final SnippetInfo arraycopyExactIntrinsicSnippet = snippet("arraycopyExactIntrinsic"); - private final SnippetInfo arraycopyZeroLengthIntrinsicSnippet = snippet("arraycopyZeroLengthIntrinsic"); - private final SnippetInfo arraycopyPredictedExactIntrinsicSnippet = snippet("arraycopyPredictedExactIntrinsic"); - private final SnippetInfo arraycopyPredictedObjectWorkSnippet = snippet("arraycopyPredictedObjectWork"); - - private final SnippetInfo arraycopyUnrolledWorkSnippet = snippet("arraycopyUnrolledWork"); - - private final Counters counters; - protected SnippetInfo snippet(String methodName) { SnippetInfo info = snippet(ArrayCopySnippets.class, methodName, LocationIdentity.any()); info.setOriginalMethod(originalArraycopy()); return info; } - public static JavaKind selectComponentKind(BasicArrayCopyNode arraycopy) { - return selectComponentKind(arraycopy, true); - } + public void lower(ArrayCopyNode arraycopy, LoweringTool tool) { + JavaKind elementKind = selectComponentKind(arraycopy); + SnippetInfo snippetInfo; + ArrayCopyTypeCheck arrayTypeCheck; - public static JavaKind selectComponentKind(BasicArrayCopyNode arraycopy, boolean exact) { ResolvedJavaType srcType = StampTool.typeOrNull(arraycopy.getSource().stamp()); ResolvedJavaType destType = StampTool.typeOrNull(arraycopy.getDestination().stamp()); - - if (srcType == null || !srcType.isArray() || destType == null || !destType.isArray()) { - if (!exact) { - JavaKind component = getComponentKind(srcType); - if (component != null) { - return component; - } - return getComponentKind(destType); - } - return null; - } - if (exact) { - if (!destType.getComponentType().isAssignableFrom(srcType.getComponentType())) { - return null; - } - if (!arraycopy.isExact()) { - return null; - } - } - return srcType.getComponentType().getJavaKind(); - } - - private static JavaKind getComponentKind(ResolvedJavaType type) { - if (type != null && type.isArray()) { - return type.getComponentType().getJavaKind(); - } - return null; - } - - private static boolean shouldUnroll(ValueNode length) { - return length.isConstant() && length.asJavaConstant().asInt() <= 8 && length.asJavaConstant().asInt() != 0; - } - - public void lower(ArrayCopyNode arraycopy, LoweringTool tool) { - JavaKind componentKind = selectComponentKind(arraycopy); - SnippetInfo snippetInfo = null; - SnippetInfo slowPathSnippetInfo = null; - Object slowPathArgument = null; - - if (arraycopy.getLength().isConstant() && arraycopy.getLength().asJavaConstant().asLong() == 0) { - snippetInfo = arraycopyZeroLengthIntrinsicSnippet; - } else if (arraycopy.isExact()) { - snippetInfo = arraycopyExactIntrinsicSnippet; - if (shouldUnroll(arraycopy.getLength())) { - snippetInfo = arraycopyUnrolledIntrinsicSnippet; - } + if (!canBeArray(srcType) || !canBeArray(destType)) { + // at least one of the objects is definitely not an array - use the native call + // right away as the copying will fail anyways + snippetInfo = arraycopyNativeSnippet; + arrayTypeCheck = ArrayCopyTypeCheck.UNDEFINED_ARRAY_TYPE_CHECK; } else { - if (componentKind == JavaKind.Object) { - ResolvedJavaType srcType = StampTool.typeOrNull(arraycopy.getSource().stamp()); - ResolvedJavaType destType = StampTool.typeOrNull(arraycopy.getDestination().stamp()); - ResolvedJavaType srcComponentType = srcType == null ? null : srcType.getComponentType(); - ResolvedJavaType destComponentType = destType == null ? null : destType.getComponentType(); - if (srcComponentType != null && destComponentType != null && !srcComponentType.isPrimitive() && !destComponentType.isPrimitive()) { - snippetInfo = arraycopySlowPathIntrinsicSnippet; - slowPathSnippetInfo = checkcastArraycopyWorkSnippet; - slowPathArgument = LocationIdentity.any(); - /* - * Because this snippet has to use Sysytem.arraycopy as a slow path, we must - * pretend to kill any() so clear the componentKind. - */ - componentKind = null; - } - } - if (componentKind == null && snippetInfo == null) { - JavaKind predictedKind = selectComponentKind(arraycopy, false); - if (predictedKind != null) { - /* - * At least one array is of a known type requiring no store checks, so - * assume the other is of the same type. Generally this is working around - * deficiencies in our propagation of type information. - */ - componentKind = predictedKind; - if (predictedKind == JavaKind.Object) { - snippetInfo = arraycopySlowPathIntrinsicSnippet; - slowPathSnippetInfo = arraycopyPredictedObjectWorkSnippet; - slowPathArgument = predictedKind; - componentKind = null; - } else { - snippetInfo = arraycopyPredictedExactIntrinsicSnippet; - } - } - } - if (snippetInfo == null) { + ResolvedJavaType srcComponentType = srcType == null ? null : srcType.getComponentType(); + ResolvedJavaType destComponentType = destType == null ? null : destType.getComponentType(); + + if (arraycopy.isExact()) { + // there is a sufficient type match - we don't need any additional type checks + snippetInfo = arraycopyExactSnippet; + arrayTypeCheck = ArrayCopyTypeCheck.NO_ARRAY_TYPE_CHECK; + } else if (srcComponentType == null && destComponentType == null) { + // we don't know anything about the types - use the generic copying snippetInfo = arraycopyGenericSnippet; + arrayTypeCheck = ArrayCopyTypeCheck.LAYOUT_HELPER_BASED_ARRAY_TYPE_CHECK; + } else if (srcComponentType != null && destComponentType != null) { + if (!srcComponentType.isPrimitive() && !destComponentType.isPrimitive()) { + // it depends on the array content if the copy succeeds - we need + // a type check for every store + snippetInfo = arraycopyCheckcastSnippet; + arrayTypeCheck = ArrayCopyTypeCheck.NO_ARRAY_TYPE_CHECK; + } else { + // one object is an object array, the other one is a primitive array. + // this copy will always fail - use the native call right away + assert !srcComponentType.equals(destComponentType) : "must be handled by arraycopy.isExact()"; + snippetInfo = arraycopyNativeSnippet; + arrayTypeCheck = ArrayCopyTypeCheck.UNDEFINED_ARRAY_TYPE_CHECK; + } + } else { + ResolvedJavaType nonNullComponentType = srcComponentType != null ? srcComponentType : destComponentType; + if (nonNullComponentType.isPrimitive()) { + // one involved object is a primitive array - we can safely assume that we + // are copying primitive arrays + snippetInfo = arraycopyExactSnippet; + arrayTypeCheck = ArrayCopyTypeCheck.HUB_BASED_ARRAY_TYPE_CHECK; + elementKind = nonNullComponentType.getJavaKind(); + } else { + // one involved object is an object array - we can safely assume that we are + // copying object arrays that might require a store check + snippetInfo = arraycopyCheckcastSnippet; + arrayTypeCheck = ArrayCopyTypeCheck.LAYOUT_HELPER_BASED_ARRAY_TYPE_CHECK; + } } } + + // a few special cases that are easier to handle when all other variables already have a + // value + if (arraycopy.getLength().isConstant() && arraycopy.getLength().asJavaConstant().asLong() == 0) { + snippetInfo = arraycopyZeroLengthSnippet; + } else if (snippetInfo == arraycopyExactSnippet && shouldUnroll(arraycopy.getLength())) { + snippetInfo = arraycopyUnrolledSnippet; + } + + // create the snippet Arguments args = new Arguments(snippetInfo, arraycopy.graph().getGuardsStage(), tool.getLoweringStage()); args.add("src", arraycopy.getSource()); args.add("srcPos", arraycopy.getSourcePosition()); args.add("dest", arraycopy.getDestination()); args.add("destPos", arraycopy.getDestinationPosition()); args.add("length", arraycopy.getLength()); - if (snippetInfo == arraycopyUnrolledIntrinsicSnippet) { - args.addConst("unrolledLength", arraycopy.getLength().asJavaConstant().asInt()); - args.addConst("elementKind", componentKind != null ? componentKind : JavaKind.Illegal); - } else if (snippetInfo == arraycopySlowPathIntrinsicSnippet) { - ValueNode predictedKlass = null; - if (slowPathArgument == arraycopyPredictedObjectWorkSnippet) { - HotSpotResolvedObjectType arrayClass = (HotSpotResolvedObjectType) tool.getMetaAccess().lookupJavaType(Object[].class); - predictedKlass = ConstantNode.forConstant(KlassPointerStamp.klassNonNull(), arrayClass.klass(), tool.getMetaAccess(), arraycopy.graph()); - } else { - predictedKlass = ConstantNode.forConstant(KlassPointerStamp.klassAlwaysNull(), JavaConstant.NULL_POINTER, tool.getMetaAccess(), arraycopy.graph()); - } - args.add("predictedKlass", predictedKlass); - args.addConst("elementKind", componentKind != null ? componentKind : JavaKind.Illegal); - args.addConst("slowPath", slowPathSnippetInfo); - assert slowPathArgument != null; - args.addConst("slowPathArgument", slowPathArgument); - } else if (snippetInfo == arraycopyExactIntrinsicSnippet || snippetInfo == arraycopyPredictedExactIntrinsicSnippet) { - assert componentKind != null; - args.addConst("elementKind", componentKind); - args.addConst("counter", counters.arraycopyCallCounters.get(componentKind)); - args.addConst("copiedCounter", counters.arraycopyCallCopiedCounters.get(componentKind)); + if (snippetInfo != arraycopyNativeSnippet) { + assert arrayTypeCheck != ArrayCopyTypeCheck.UNDEFINED_ARRAY_TYPE_CHECK; + args.addConst("arrayTypeCheck", arrayTypeCheck); } + if (snippetInfo == arraycopyUnrolledSnippet) { + args.addConst("elementKind", elementKind != null ? elementKind : JavaKind.Illegal); + args.addConst("unrolledLength", arraycopy.getLength().asJavaConstant().asInt()); + } + if (snippetInfo == arraycopyExactSnippet) { + assert elementKind != null; + args.addConst("elementKind", elementKind); + args.addConst("elementKindCounter", counters.arraycopyCallCounters.get(elementKind)); + args.addConst("elementKindCopiedCounter", counters.arraycopyCallCopiedCounters.get(elementKind)); + } + args.addConst("counters", counters); + if (snippetInfo == arraycopyCheckcastSnippet) { + args.addConst("workSnippet", checkcastArraycopyWithSlowPathWork); + args.addConst("elementKind", JavaKind.Illegal); + } + if (snippetInfo == arraycopyGenericSnippet) { + args.addConst("workSnippet", genericArraycopyWithSlowPathWork); + args.addConst("elementKind", JavaKind.Illegal); + } + + instantiate(args, arraycopy); + } + + public void lower(ArrayCopyWithSlowPathNode arraycopy, LoweringTool tool) { + StructuredGraph graph = arraycopy.graph(); + if (!graph.getGuardsStage().areFrameStatesAtDeopts()) { + // if an arraycopy contains a slow path, we can't lower it right away + return; + } + + SnippetInfo snippetInfo = arraycopy.getSnippet(); + Arguments args = new Arguments(snippetInfo, graph.getGuardsStage(), tool.getLoweringStage()); + args.add("src", arraycopy.getSource()); + args.add("srcPos", arraycopy.getSourcePosition()); + args.add("dest", arraycopy.getDestination()); + args.add("destPos", arraycopy.getDestinationPosition()); + args.add("length", arraycopy.getLength()); args.addConst("counters", counters); instantiate(args, arraycopy); } - public void lower(ArrayCopySlowPathNode arraycopy, LoweringTool tool) { - StructuredGraph graph = arraycopy.graph(); - if (!graph.getGuardsStage().areFrameStatesAtDeopts()) { - // Can't be lowered yet - return; - } - SnippetInfo snippetInfo = arraycopy.getSnippet(); - Arguments args = new Arguments(snippetInfo, graph.getGuardsStage(), tool.getLoweringStage()); - args.add("nonNullSrc", arraycopy.getSource()); - args.add("srcPos", arraycopy.getSourcePosition()); - args.add("nonNullDest", arraycopy.getDestination()); - args.add("destPos", arraycopy.getDestinationPosition()); - if (snippetInfo == arraycopyUnrolledWorkSnippet) { - args.addConst("length", ((Integer) arraycopy.getArgument()).intValue()); - args.addConst("elementKind", arraycopy.getElementKind()); - } else { - args.add("length", arraycopy.getLength()); - } - if (snippetInfo == arraycopyPredictedObjectWorkSnippet) { - args.add("objectArrayKlass", arraycopy.getPredictedKlass()); - args.addConst("counter", counters.arraycopyCallCounters.get(JavaKind.Object)); - args.addConst("copiedCounter", counters.arraycopyCallCopiedCounters.get(JavaKind.Object)); - args.addConst("counters", counters); - } - instantiate(args, arraycopy); + private static boolean canBeArray(ResolvedJavaType type) { + return type == null || type.isJavaLangObject() || type.isArray(); } - public void lower(ArrayCopyUnrollNode arraycopy, LoweringTool tool) { - StructuredGraph graph = arraycopy.graph(); - if (!graph.getGuardsStage().areFrameStatesAtDeopts()) { - // Can't be lowered yet - return; + public static JavaKind selectComponentKind(BasicArrayCopyNode arraycopy) { + ResolvedJavaType srcType = StampTool.typeOrNull(arraycopy.getSource().stamp()); + ResolvedJavaType destType = StampTool.typeOrNull(arraycopy.getDestination().stamp()); + + if (srcType == null || !srcType.isArray() || destType == null || !destType.isArray()) { + return null; } - SnippetInfo snippetInfo = arraycopyUnrolledWorkSnippet; - Arguments args = new Arguments(snippetInfo, graph.getGuardsStage(), tool.getLoweringStage()); - args.add("nonNullSrc", arraycopy.getSource()); - args.add("srcPos", arraycopy.getSourcePosition()); - args.add("nonNullDest", arraycopy.getDestination()); - args.add("destPos", arraycopy.getDestinationPosition()); - args.addConst("length", arraycopy.getUnrollLength()); - args.addConst("elementKind", arraycopy.getElementKind()); - template(graph.getDebug(), args).instantiate(providers.getMetaAccess(), arraycopy, SnippetTemplate.DEFAULT_REPLACER, args); + if (!destType.getComponentType().isAssignableFrom(srcType.getComponentType())) { + return null; + } + if (!arraycopy.isExact()) { + return null; + } + return srcType.getComponentType().getJavaKind(); + } + + private static boolean shouldUnroll(ValueNode length) { + return length.isConstant() && length.asJavaConstant().asInt() <= 8 && length.asJavaConstant().asInt() != 0; } /** @@ -650,8 +543,8 @@ public class ArrayCopySnippets implements Snippets { newInvoke.setStateAfter(arraycopy.stateAfter()); } graph.replaceFixedWithFixed((InvokeNode) invoke.asNode(), newInvoke); - } else if (originalNode instanceof ArrayCopySlowPathNode) { - ArrayCopySlowPathNode slowPath = (ArrayCopySlowPathNode) replacements.get(originalNode); + } else if (originalNode instanceof ArrayCopyWithSlowPathNode) { + ArrayCopyWithSlowPathNode slowPath = (ArrayCopyWithSlowPathNode) replacements.get(originalNode); assert arraycopy.stateAfter() != null : arraycopy; assert slowPath.stateAfter() == arraycopy.stateAfter(); slowPath.setBci(arraycopy.getBci()); @@ -659,5 +552,18 @@ public class ArrayCopySnippets implements Snippets { } GraphUtil.killCFG(arraycopy); } + + private ResolvedJavaMethod originalArraycopy() throws GraalError { + if (originalArraycopy == null) { + Method method; + try { + method = System.class.getDeclaredMethod("arraycopy", Object.class, int.class, Object.class, int.class, int.class); + } catch (NoSuchMethodException | SecurityException e) { + throw new GraalError(e); + } + originalArraycopy = providers.getMetaAccess().lookupJavaMethod(method); + } + return originalArraycopy; + } } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/ArrayCopyUnrollNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/ArrayCopyUnrollNode.java deleted file mode 100644 index 7bd998024d3..00000000000 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/ArrayCopyUnrollNode.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (c) 2015, 2016, 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 org.graalvm.compiler.hotspot.replacements.arraycopy; - -import jdk.vm.ci.meta.JavaKind; - -import static org.graalvm.word.LocationIdentity.any; - -import org.graalvm.compiler.core.common.type.StampFactory; -import org.graalvm.compiler.graph.NodeClass; -import org.graalvm.compiler.nodeinfo.InputType; -import org.graalvm.compiler.nodeinfo.NodeInfo; -import org.graalvm.compiler.nodes.NamedLocationIdentity; -import org.graalvm.compiler.nodes.ValueNode; -import org.graalvm.compiler.nodes.extended.ArrayRangeWriteNode; -import org.graalvm.compiler.nodes.memory.MemoryAccess; -import org.graalvm.compiler.nodes.memory.MemoryCheckpoint; -import org.graalvm.compiler.nodes.memory.MemoryNode; -import org.graalvm.compiler.nodes.spi.Lowerable; -import org.graalvm.compiler.nodes.spi.LoweringTool; -import org.graalvm.word.LocationIdentity; - -@NodeInfo(allowedUsageTypes = InputType.Memory) -public class ArrayCopyUnrollNode extends ArrayRangeWriteNode implements MemoryCheckpoint.Single, Lowerable, MemoryAccess { - - public static final NodeClass TYPE = NodeClass.create(ArrayCopyUnrollNode.class); - - @Input protected ValueNode src; - @Input protected ValueNode srcPos; - @Input protected ValueNode dest; - @Input protected ValueNode destPos; - @Input protected ValueNode length; - - private JavaKind elementKind; - - private int unrolledLength; - - @OptionalInput(InputType.Memory) private MemoryNode lastLocationAccess; - - public ArrayCopyUnrollNode(ValueNode src, ValueNode srcPos, ValueNode dest, ValueNode destPos, ValueNode length, int unrolledLength, JavaKind elementKind) { - super(TYPE, StampFactory.forKind(JavaKind.Void)); - this.src = src; - this.srcPos = srcPos; - this.dest = dest; - this.destPos = destPos; - this.length = length; - this.unrolledLength = unrolledLength; - assert elementKind != null && elementKind != JavaKind.Illegal; - this.elementKind = elementKind; - } - - public ValueNode getSource() { - return src; - } - - public ValueNode getSourcePosition() { - return srcPos; - } - - public ValueNode getDestination() { - return dest; - } - - public ValueNode getDestinationPosition() { - return destPos; - } - - @Override - public ValueNode getLength() { - return length; - } - - @Override - public ValueNode getArray() { - return dest; - } - - @Override - public ValueNode getIndex() { - return destPos; - } - - @Override - public boolean isObjectArray() { - return elementKind == JavaKind.Object; - } - - @Override - public boolean isInitialization() { - return false; - } - - @NodeIntrinsic - public static native void arraycopy(Object nonNullSrc, int srcPos, Object nonNullDest, int destPos, int length, @ConstantNodeParameter int unrolledLength, - @ConstantNodeParameter JavaKind elementKind); - - public int getUnrollLength() { - return unrolledLength; - } - - public JavaKind getElementKind() { - return elementKind; - } - - @Override - public LocationIdentity getLocationIdentity() { - if (elementKind != null) { - return NamedLocationIdentity.getArrayLocation(elementKind); - } - return any(); - } - - @Override - public void lower(LoweringTool tool) { - tool.getLowerer().lower(this, tool); - } - - @Override - public MemoryNode getLastLocationAccess() { - return lastLocationAccess; - } - - @Override - public void setLastLocationAccess(MemoryNode lla) { - updateUsagesInterface(lastLocationAccess, lla); - lastLocationAccess = lla; - } -} diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/ArrayCopySlowPathNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/ArrayCopyWithSlowPathNode.java similarity index 54% rename from src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/ArrayCopySlowPathNode.java rename to src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/ArrayCopyWithSlowPathNode.java index 99591564e3d..0afcdee6c1c 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/ArrayCopySlowPathNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/ArrayCopyWithSlowPathNode.java @@ -22,76 +22,39 @@ */ package org.graalvm.compiler.hotspot.replacements.arraycopy; -import jdk.vm.ci.code.BytecodeFrame; -import jdk.vm.ci.meta.JavaKind; - -import static org.graalvm.word.LocationIdentity.any; - import org.graalvm.compiler.graph.NodeClass; -import org.graalvm.compiler.hotspot.word.KlassPointer; import org.graalvm.compiler.nodeinfo.InputType; import org.graalvm.compiler.nodeinfo.NodeInfo; -import org.graalvm.compiler.nodes.NamedLocationIdentity; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.type.StampTool; import org.graalvm.compiler.replacements.SnippetTemplate; import org.graalvm.compiler.replacements.nodes.BasicArrayCopyNode; -import org.graalvm.word.LocationIdentity; + +import jdk.vm.ci.code.BytecodeFrame; +import jdk.vm.ci.meta.JavaKind; @NodeInfo(allowedUsageTypes = InputType.Memory) -public final class ArrayCopySlowPathNode extends BasicArrayCopyNode { +public final class ArrayCopyWithSlowPathNode extends BasicArrayCopyNode { - public static final NodeClass TYPE = NodeClass.create(ArrayCopySlowPathNode.class); + public static final NodeClass TYPE = NodeClass.create(ArrayCopyWithSlowPathNode.class); private final SnippetTemplate.SnippetInfo snippet; - /** - * Extra context for the slow path snippet. - */ - private final Object argument; - - /** - * AOT compilation requires klass constants to be exposed after the first lowering to be handled - * automatically. Lowering for {@link ArrayCopySlowPathNode}, with snippet == - * {@link ArrayCopySnippets#arraycopyPredictedObjectWork}, requires a klass of Object[]. For - * other snippets {@link #predictedKlass} is a null constant. - */ - @Input protected ValueNode predictedKlass; - - public ArrayCopySlowPathNode(ValueNode src, ValueNode srcPos, ValueNode dest, ValueNode destPos, ValueNode length, ValueNode predictedKlass, JavaKind elementKind, - SnippetTemplate.SnippetInfo snippet, Object argument) { + public ArrayCopyWithSlowPathNode(ValueNode src, ValueNode srcPos, ValueNode dest, ValueNode destPos, ValueNode length, SnippetTemplate.SnippetInfo snippet, JavaKind elementKind) { super(TYPE, src, srcPos, dest, destPos, length, elementKind, BytecodeFrame.INVALID_FRAMESTATE_BCI); assert StampTool.isPointerNonNull(src) && StampTool.isPointerNonNull(dest) : "must have been null checked"; this.snippet = snippet; - this.argument = argument; - this.predictedKlass = predictedKlass; } @NodeIntrinsic - public static native void arraycopy(Object nonNullSrc, int srcPos, Object nonNullDest, int destPos, int length, KlassPointer predictedKlass, - @ConstantNodeParameter JavaKind elementKind, @ConstantNodeParameter SnippetTemplate.SnippetInfo snippet, @ConstantNodeParameter Object argument); + public static native void arraycopy(Object nonNullSrc, int srcPos, Object nonNullDest, int destPos, int length, @ConstantNodeParameter SnippetTemplate.SnippetInfo snippet, + @ConstantNodeParameter JavaKind elementKind); public SnippetTemplate.SnippetInfo getSnippet() { return snippet; } - public Object getArgument() { - return argument; - } - - @Override - public LocationIdentity getLocationIdentity() { - if (elementKind != null) { - return NamedLocationIdentity.getArrayLocation(elementKind); - } - return any(); - } - public void setBci(int bci) { this.bci = bci; } - - public ValueNode getPredictedKlass() { - return predictedKlass; - } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/CheckcastArrayCopyCallNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/CheckcastArrayCopyCallNode.java index 8853e1a3b66..e90d486a98d 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/CheckcastArrayCopyCallNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/CheckcastArrayCopyCallNode.java @@ -23,12 +23,13 @@ //JaCoCo Exclude package org.graalvm.compiler.hotspot.replacements.arraycopy; -import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_UNKNOWN; -import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_UNKNOWN; import static jdk.vm.ci.hotspot.HotSpotJVMCIRuntimeProvider.getArrayBaseOffset; import static jdk.vm.ci.hotspot.HotSpotJVMCIRuntimeProvider.getArrayIndexScale; +import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_UNKNOWN; +import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_UNKNOWN; import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor; +import org.graalvm.compiler.core.common.type.PrimitiveStamp; import org.graalvm.compiler.core.common.type.StampFactory; import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.hotspot.HotSpotGraalRuntimeProvider; @@ -114,8 +115,9 @@ public final class CheckcastArrayCopyCallNode extends AbstractMemoryCheckpoint i graph().addBeforeFixed(this, basePtr); int shift = CodeUtil.log2(getArrayIndexScale(JavaKind.Object)); - ValueNode scaledIndex = graph().unique(new LeftShiftNode(pos, ConstantNode.forInt(shift, graph()))); - ValueNode offset = graph().unique(new AddNode(scaledIndex, ConstantNode.forInt(getArrayBaseOffset(JavaKind.Object), graph()))); + ValueNode extendedPos = IntegerConvertNode.convert(pos, StampFactory.forKind(runtime.getTarget().wordJavaKind), graph()); + ValueNode scaledIndex = graph().unique(new LeftShiftNode(extendedPos, ConstantNode.forInt(shift, graph()))); + ValueNode offset = graph().unique(new AddNode(scaledIndex, ConstantNode.forIntegerBits(PrimitiveStamp.getBits(scaledIndex.stamp()), getArrayBaseOffset(JavaKind.Object), graph()))); return graph().unique(new OffsetAddressNode(basePtr, offset)); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/GenericArrayCopyCallNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/GenericArrayCopyCallNode.java new file mode 100644 index 00000000000..1017b07c2b2 --- /dev/null +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/GenericArrayCopyCallNode.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2014, 2016, 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. + */ +//JaCoCo Exclude +package org.graalvm.compiler.hotspot.replacements.arraycopy; + +import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_UNKNOWN; +import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_UNKNOWN; + +import org.graalvm.compiler.core.common.type.StampFactory; +import org.graalvm.compiler.graph.NodeClass; +import org.graalvm.compiler.hotspot.HotSpotBackend; +import org.graalvm.compiler.hotspot.HotSpotGraalRuntimeProvider; +import org.graalvm.compiler.hotspot.nodes.GetObjectAddressNode; +import org.graalvm.compiler.nodeinfo.InputType; +import org.graalvm.compiler.nodeinfo.NodeInfo; +import org.graalvm.compiler.nodes.StructuredGraph; +import org.graalvm.compiler.nodes.ValueNode; +import org.graalvm.compiler.nodes.calc.IntegerConvertNode; +import org.graalvm.compiler.nodes.extended.ForeignCallNode; +import org.graalvm.compiler.nodes.memory.AbstractMemoryCheckpoint; +import org.graalvm.compiler.nodes.memory.MemoryCheckpoint; +import org.graalvm.compiler.nodes.spi.Lowerable; +import org.graalvm.compiler.nodes.spi.LoweringTool; +import org.graalvm.word.LocationIdentity; + +import jdk.vm.ci.meta.JavaKind; + +@NodeInfo(allowedUsageTypes = {InputType.Memory, InputType.Value}, cycles = CYCLES_UNKNOWN, size = SIZE_UNKNOWN) +public final class GenericArrayCopyCallNode extends AbstractMemoryCheckpoint implements Lowerable, MemoryCheckpoint.Single { + + public static final NodeClass TYPE = NodeClass.create(GenericArrayCopyCallNode.class); + @Input ValueNode src; + @Input ValueNode srcPos; + @Input ValueNode dest; + @Input ValueNode destPos; + @Input ValueNode length; + + protected final HotSpotGraalRuntimeProvider runtime; + + protected GenericArrayCopyCallNode(@InjectedNodeParameter HotSpotGraalRuntimeProvider runtime, ValueNode src, ValueNode srcPos, ValueNode dest, ValueNode destPos, ValueNode length) { + super(TYPE, StampFactory.forKind(JavaKind.Int)); + this.src = src; + this.srcPos = srcPos; + this.dest = dest; + this.destPos = destPos; + this.length = length; + this.runtime = runtime; + } + + public ValueNode getSource() { + return src; + } + + public ValueNode getSourcePosition() { + return srcPos; + } + + public ValueNode getDestination() { + return dest; + } + + public ValueNode getDestinationPosition() { + return destPos; + } + + public ValueNode getLength() { + return length; + } + + @Override + public void lower(LoweringTool tool) { + if (graph().getGuardsStage().areFrameStatesAtDeopts()) { + StructuredGraph graph = graph(); + ValueNode srcAddr = objectAddress(getSource()); + ValueNode destAddr = objectAddress(getDestination()); + ForeignCallNode call = graph.add(new ForeignCallNode(runtime.getHostBackend().getForeignCalls(), HotSpotBackend.GENERIC_ARRAYCOPY, srcAddr, srcPos, destAddr, destPos, length)); + call.setStateAfter(stateAfter()); + graph.replaceFixedWithFixed(this, call); + } + } + + private ValueNode objectAddress(ValueNode obj) { + GetObjectAddressNode result = graph().add(new GetObjectAddressNode(obj)); + graph().addBeforeFixed(this, result); + return result; + } + + private ValueNode wordValue(ValueNode value) { + if (value.stamp().getStackKind() != runtime.getTarget().wordJavaKind) { + return IntegerConvertNode.convert(value, StampFactory.forKind(runtime.getTarget().wordJavaKind), graph()); + } + return value; + } + + @Override + public LocationIdentity getLocationIdentity() { + return LocationIdentity.any(); + } + + @NodeIntrinsic + public static native int genericArraycopy(Object src, int srcPos, Object dest, int destPos, int length); +} diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/UnsafeArrayCopyNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/UnsafeArrayCopyNode.java deleted file mode 100644 index 83c7c4f9f10..00000000000 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/UnsafeArrayCopyNode.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (c) 2013, 2016, 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 org.graalvm.compiler.hotspot.replacements.arraycopy; - -import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_256; -import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_64; -import static org.graalvm.word.LocationIdentity.any; - -import org.graalvm.compiler.core.common.type.StampFactory; -import org.graalvm.compiler.graph.NodeClass; -import org.graalvm.compiler.nodeinfo.InputType; -import org.graalvm.compiler.nodeinfo.NodeInfo; -import org.graalvm.compiler.nodes.NamedLocationIdentity; -import org.graalvm.compiler.nodes.ValueNode; -import org.graalvm.compiler.nodes.extended.ArrayRangeWriteNode; -import org.graalvm.compiler.nodes.memory.MemoryAccess; -import org.graalvm.compiler.nodes.memory.MemoryCheckpoint; -import org.graalvm.compiler.nodes.memory.MemoryNode; -import org.graalvm.compiler.nodes.spi.Lowerable; -import org.graalvm.compiler.nodes.spi.LoweringTool; -import org.graalvm.compiler.replacements.SnippetTemplate.Arguments; -import org.graalvm.word.LocationIdentity; - -import jdk.vm.ci.meta.JavaKind; - -@NodeInfo(allowedUsageTypes = {InputType.Memory}, cycles = CYCLES_256, size = SIZE_64) -public final class UnsafeArrayCopyNode extends ArrayRangeWriteNode implements Lowerable, MemoryCheckpoint.Single, MemoryAccess { - - public static final NodeClass TYPE = NodeClass.create(UnsafeArrayCopyNode.class); - @Input ValueNode src; - @Input ValueNode srcPos; - @Input ValueNode dest; - @Input ValueNode destPos; - @Input ValueNode length; - @OptionalInput ValueNode layoutHelper; - - @OptionalInput(InputType.Memory) MemoryNode lastLocationAccess; - - protected JavaKind elementKind; - - public UnsafeArrayCopyNode(ValueNode src, ValueNode srcPos, ValueNode dest, ValueNode destPos, ValueNode length, ValueNode layoutHelper, JavaKind elementKind) { - super(TYPE, StampFactory.forVoid()); - assert layoutHelper == null || elementKind == null; - this.src = src; - this.srcPos = srcPos; - this.dest = dest; - this.destPos = destPos; - this.length = length; - this.layoutHelper = layoutHelper; - this.elementKind = elementKind; - } - - public UnsafeArrayCopyNode(ValueNode src, ValueNode srcPos, ValueNode dest, ValueNode destPos, ValueNode length, JavaKind elementKind) { - this(src, srcPos, dest, destPos, length, null, elementKind); - } - - public UnsafeArrayCopyNode(ValueNode src, ValueNode srcPos, ValueNode dest, ValueNode destPos, ValueNode length, ValueNode layoutHelper) { - this(src, srcPos, dest, destPos, length, layoutHelper, null); - } - - @Override - public ValueNode getArray() { - return dest; - } - - @Override - public ValueNode getIndex() { - return destPos; - } - - @Override - public ValueNode getLength() { - return length; - } - - @Override - public boolean isObjectArray() { - return elementKind == JavaKind.Object; - } - - @Override - public boolean isInitialization() { - return false; - } - - public JavaKind getElementKind() { - return elementKind; - } - - @Override - public void lower(LoweringTool tool) { - if (graph().getGuardsStage().areFrameStatesAtDeopts()) { - UnsafeArrayCopySnippets.Templates templates = tool.getReplacements().getSnippetTemplateCache(UnsafeArrayCopySnippets.Templates.class); - templates.lower(this, tool); - } - } - - public void addSnippetArguments(Arguments args) { - args.add("src", src); - args.add("srcPos", srcPos); - args.add("dest", dest); - args.add("destPos", destPos); - args.add("length", length); - if (layoutHelper != null) { - args.add("layoutHelper", layoutHelper); - } - } - - @Override - public LocationIdentity getLocationIdentity() { - if (elementKind != null) { - return NamedLocationIdentity.getArrayLocation(elementKind); - } - return any(); - } - - @Override - public MemoryNode getLastLocationAccess() { - return lastLocationAccess; - } - - @Override - public void setLastLocationAccess(MemoryNode lla) { - updateUsagesInterface(lastLocationAccess, lla); - lastLocationAccess = lla; - } - - @NodeIntrinsic - public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length, @ConstantNodeParameter JavaKind elementKind); - - @NodeIntrinsic - public static native void arraycopyPrimitive(Object src, int srcPos, Object dest, int destPos, int length, int layoutHelper); -} diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/UnsafeArrayCopySnippets.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/UnsafeArrayCopySnippets.java deleted file mode 100644 index 71d63706deb..00000000000 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/UnsafeArrayCopySnippets.java +++ /dev/null @@ -1,324 +0,0 @@ -/* - * Copyright (c) 2013, 2016, 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 org.graalvm.compiler.hotspot.replacements.arraycopy; - -import static jdk.vm.ci.hotspot.HotSpotJVMCIRuntimeProvider.getArrayIndexScale; -import static org.graalvm.compiler.hotspot.GraalHotSpotVMConfig.INJECTED_VMCONFIG; -import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.arrayBaseOffset; -import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.arrayIndexScale; -import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.layoutHelperHeaderSizeMask; -import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.layoutHelperHeaderSizeShift; -import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.layoutHelperLog2ElementSizeMask; -import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.layoutHelperLog2ElementSizeShift; -import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.runtime; -import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.wordSize; -import static org.graalvm.compiler.nodes.extended.BranchProbabilityNode.NOT_FREQUENT_PROBABILITY; -import static org.graalvm.compiler.nodes.extended.BranchProbabilityNode.probability; -import static org.graalvm.compiler.replacements.SnippetTemplate.DEFAULT_REPLACER; -import static org.graalvm.word.LocationIdentity.any; - -import org.graalvm.compiler.api.replacements.Fold; -import org.graalvm.compiler.api.replacements.Snippet; -import org.graalvm.compiler.core.common.NumUtil; -import org.graalvm.compiler.debug.DebugHandlersFactory; -import org.graalvm.compiler.hotspot.meta.HotSpotProviders; -import org.graalvm.compiler.hotspot.phases.WriteBarrierAdditionPhase; -import org.graalvm.compiler.nodes.NamedLocationIdentity; -import org.graalvm.compiler.nodes.extended.RawLoadNode; -import org.graalvm.compiler.nodes.extended.RawStoreNode; -import org.graalvm.compiler.nodes.extended.UnsafeCopyNode; -import org.graalvm.compiler.nodes.spi.LoweringTool; -import org.graalvm.compiler.options.OptionValues; -import org.graalvm.compiler.replacements.SnippetTemplate; -import org.graalvm.compiler.replacements.SnippetTemplate.AbstractTemplates; -import org.graalvm.compiler.replacements.SnippetTemplate.Arguments; -import org.graalvm.compiler.replacements.SnippetTemplate.SnippetInfo; -import org.graalvm.compiler.replacements.Snippets; -import org.graalvm.compiler.word.ObjectAccess; -import org.graalvm.word.LocationIdentity; -import org.graalvm.word.UnsignedWord; -import org.graalvm.word.WordFactory; - -import jdk.vm.ci.code.TargetDescription; -import jdk.vm.ci.meta.JavaKind; - -/** - * As opposed to {@link ArrayCopySnippets}, these Snippets do not perform store checks. - */ -public class UnsafeArrayCopySnippets implements Snippets { - - private static final boolean supportsUnalignedMemoryAccess = runtime().getHostJVMCIBackend().getTarget().arch.supportsUnalignedMemoryAccess(); - - private static final JavaKind VECTOR_KIND = JavaKind.Long; - private static final long VECTOR_SIZE = getArrayIndexScale(VECTOR_KIND); - - private static void vectorizedCopy(Object src, int srcPos, Object dest, int destPos, int length, JavaKind baseKind, LocationIdentity locationIdentity) { - int arrayBaseOffset = arrayBaseOffset(baseKind); - int elementSize = arrayIndexScale(baseKind); - long byteLength = (long) length * elementSize; - long srcOffset = (long) srcPos * elementSize; - long destOffset = (long) destPos * elementSize; - - long preLoopBytes; - long mainLoopBytes; - long postLoopBytes; - - // We can easily vectorize the loop if both offsets have the same alignment. - if (byteLength >= VECTOR_SIZE && (srcOffset % VECTOR_SIZE) == (destOffset % VECTOR_SIZE)) { - preLoopBytes = NumUtil.roundUp(arrayBaseOffset + srcOffset, VECTOR_SIZE) - (arrayBaseOffset + srcOffset); - postLoopBytes = (byteLength - preLoopBytes) % VECTOR_SIZE; - mainLoopBytes = byteLength - preLoopBytes - postLoopBytes; - } else { - // Does the architecture support unaligned memory accesses? - if (supportsUnalignedMemoryAccess) { - preLoopBytes = byteLength % VECTOR_SIZE; - mainLoopBytes = byteLength - preLoopBytes; - postLoopBytes = 0; - } else { - // No. Let's do element-wise copying. - preLoopBytes = byteLength; - mainLoopBytes = 0; - postLoopBytes = 0; - } - } - - if (probability(NOT_FREQUENT_PROBABILITY, src == dest) && probability(NOT_FREQUENT_PROBABILITY, srcPos < destPos)) { - // bad aliased case - srcOffset += byteLength; - destOffset += byteLength; - - // Post-loop - for (long i = 0; i < postLoopBytes; i += elementSize) { - srcOffset -= elementSize; - destOffset -= elementSize; - UnsafeCopyNode.copy(src, arrayBaseOffset + srcOffset, dest, arrayBaseOffset + destOffset, baseKind, locationIdentity); - } - // Main-loop - for (long i = 0; i < mainLoopBytes; i += VECTOR_SIZE) { - srcOffset -= VECTOR_SIZE; - destOffset -= VECTOR_SIZE; - UnsafeCopyNode.copy(src, arrayBaseOffset + srcOffset, dest, arrayBaseOffset + destOffset, VECTOR_KIND, locationIdentity); - } - // Pre-loop - for (long i = 0; i < preLoopBytes; i += elementSize) { - srcOffset -= elementSize; - destOffset -= elementSize; - UnsafeCopyNode.copy(src, arrayBaseOffset + srcOffset, dest, arrayBaseOffset + destOffset, baseKind, locationIdentity); - } - } else { - // Pre-loop - for (long i = 0; i < preLoopBytes; i += elementSize) { - UnsafeCopyNode.copy(src, arrayBaseOffset + srcOffset, dest, arrayBaseOffset + destOffset, baseKind, locationIdentity); - srcOffset += elementSize; - destOffset += elementSize; - } - // Main-loop - for (long i = 0; i < mainLoopBytes; i += VECTOR_SIZE) { - UnsafeCopyNode.copy(src, arrayBaseOffset + srcOffset, dest, arrayBaseOffset + destOffset, VECTOR_KIND, locationIdentity); - srcOffset += VECTOR_SIZE; - destOffset += VECTOR_SIZE; - } - // Post-loop - for (long i = 0; i < postLoopBytes; i += elementSize) { - UnsafeCopyNode.copy(src, arrayBaseOffset + srcOffset, dest, arrayBaseOffset + destOffset, baseKind, locationIdentity); - srcOffset += elementSize; - destOffset += elementSize; - } - } - } - - @Fold - static LocationIdentity getArrayLocation(JavaKind kind) { - return NamedLocationIdentity.getArrayLocation(kind); - } - - @Snippet - public static void arraycopyByte(byte[] src, int srcPos, byte[] dest, int destPos, int length) { - JavaKind kind = JavaKind.Byte; - vectorizedCopy(src, srcPos, dest, destPos, length, kind, getArrayLocation(kind)); - } - - @Snippet - public static void arraycopyBoolean(boolean[] src, int srcPos, boolean[] dest, int destPos, int length) { - JavaKind kind = JavaKind.Boolean; - vectorizedCopy(src, srcPos, dest, destPos, length, kind, getArrayLocation(kind)); - } - - @Snippet - public static void arraycopyChar(char[] src, int srcPos, char[] dest, int destPos, int length) { - JavaKind kind = JavaKind.Char; - vectorizedCopy(src, srcPos, dest, destPos, length, kind, getArrayLocation(kind)); - } - - @Snippet - public static void arraycopyShort(short[] src, int srcPos, short[] dest, int destPos, int length) { - JavaKind kind = JavaKind.Short; - vectorizedCopy(src, srcPos, dest, destPos, length, kind, getArrayLocation(kind)); - } - - @Snippet - public static void arraycopyInt(int[] src, int srcPos, int[] dest, int destPos, int length) { - JavaKind kind = JavaKind.Int; - vectorizedCopy(src, srcPos, dest, destPos, length, kind, getArrayLocation(kind)); - } - - @Snippet - public static void arraycopyFloat(float[] src, int srcPos, float[] dest, int destPos, int length) { - JavaKind kind = JavaKind.Float; - vectorizedCopy(src, srcPos, dest, destPos, length, kind, getArrayLocation(kind)); - } - - @Snippet - public static void arraycopyLong(long[] src, int srcPos, long[] dest, int destPos, int length) { - JavaKind kind = JavaKind.Long; - vectorizedCopy(src, srcPos, dest, destPos, length, kind, getArrayLocation(kind)); - } - - @Snippet - public static void arraycopyDouble(double[] src, int srcPos, double[] dest, int destPos, int length) { - JavaKind kind = JavaKind.Double; - /* - * TODO atomicity problem on 32-bit architectures: The JVM spec requires double values to be - * copied atomically, but not long values. For example, on Intel 32-bit this code is not - * atomic as long as the vector kind remains Kind.Long. - */ - vectorizedCopy(src, srcPos, dest, destPos, length, kind, getArrayLocation(kind)); - } - - /** - * For this kind, Object, we want to avoid write barriers between writes, but instead have them - * at the end of the snippet. This is done by using {@link RawStoreNode}, and rely on - * {@link WriteBarrierAdditionPhase} to put write barriers after the {@link UnsafeArrayCopyNode} - * with kind Object. - */ - @Snippet - public static void arraycopyObject(Object[] src, int srcPos, Object[] dest, int destPos, int length) { - JavaKind kind = JavaKind.Object; - final int scale = arrayIndexScale(kind); - int arrayBaseOffset = arrayBaseOffset(kind); - LocationIdentity arrayLocation = getArrayLocation(kind); - if (src == dest && srcPos < destPos) { // bad aliased case - long start = (long) (length - 1) * scale; - for (long i = start; i >= 0; i -= scale) { - Object a = RawLoadNode.load(src, arrayBaseOffset + i + (long) srcPos * scale, kind, arrayLocation); - RawStoreNode.storeObject(dest, arrayBaseOffset + i + (long) destPos * scale, a, kind, getArrayLocation(kind), false); - } - } else { - long end = (long) length * scale; - for (long i = 0; i < end; i += scale) { - Object a = RawLoadNode.load(src, arrayBaseOffset + i + (long) srcPos * scale, kind, arrayLocation); - RawStoreNode.storeObject(dest, arrayBaseOffset + i + (long) destPos * scale, a, kind, getArrayLocation(kind), false); - } - } - } - - @Snippet - public static void arraycopyPrimitive(Object src, int srcPos, Object dest, int destPos, int length, int layoutHelper) { - int log2ElementSize = (layoutHelper >> layoutHelperLog2ElementSizeShift(INJECTED_VMCONFIG)) & layoutHelperLog2ElementSizeMask(INJECTED_VMCONFIG); - int headerSize = (layoutHelper >> layoutHelperHeaderSizeShift(INJECTED_VMCONFIG)) & layoutHelperHeaderSizeMask(INJECTED_VMCONFIG); - - UnsignedWord vectorSize = WordFactory.unsigned(VECTOR_SIZE); - UnsignedWord srcOffset = WordFactory.unsigned(srcPos).shiftLeft(log2ElementSize).add(headerSize); - UnsignedWord destOffset = WordFactory.unsigned(destPos).shiftLeft(log2ElementSize).add(headerSize); - UnsignedWord destStart = destOffset; - UnsignedWord destEnd = destOffset.add(WordFactory.unsigned(length).shiftLeft(log2ElementSize)); - - UnsignedWord destVectorEnd = null; - UnsignedWord nonVectorBytes = null; - UnsignedWord sizeInBytes = WordFactory.unsigned(length).shiftLeft(log2ElementSize); - if (supportsUnalignedMemoryAccess) { - nonVectorBytes = sizeInBytes.unsignedRemainder(vectorSize); - destVectorEnd = destEnd; - } else { - boolean inPhase = srcOffset.and((int) VECTOR_SIZE - 1).equal(destOffset.and((int) VECTOR_SIZE - 1)); - boolean hasAtLeastOneVector = sizeInBytes.aboveOrEqual(vectorSize); - // We must have at least one full vector, otherwise we must copy each byte separately - if (hasAtLeastOneVector && inPhase) { // If in phase, we can vectorize - nonVectorBytes = vectorSize.subtract(destStart.unsignedRemainder(vectorSize)); - } else { // fallback is byte-wise - nonVectorBytes = sizeInBytes; - } - destVectorEnd = destEnd.subtract(destEnd.unsignedRemainder(vectorSize)); - } - - UnsignedWord destNonVectorEnd = destStart.add(nonVectorBytes); - while (destOffset.belowThan(destNonVectorEnd)) { - ObjectAccess.writeByte(dest, destOffset, ObjectAccess.readByte(src, srcOffset, any()), any()); - destOffset = destOffset.add(1); - srcOffset = srcOffset.add(1); - } - // Unsigned destVectorEnd = destEnd.subtract(destEnd.unsignedRemainder(8)); - while (destOffset.belowThan(destVectorEnd)) { - ObjectAccess.writeWord(dest, destOffset, ObjectAccess.readWord(src, srcOffset, any()), any()); - destOffset = destOffset.add(wordSize()); - srcOffset = srcOffset.add(wordSize()); - } - // Do the last bytes each when it is required to have absolute alignment. - while (!supportsUnalignedMemoryAccess && destOffset.belowThan(destEnd)) { - ObjectAccess.writeByte(dest, destOffset, ObjectAccess.readByte(src, srcOffset, any()), any()); - destOffset = destOffset.add(1); - srcOffset = srcOffset.add(1); - } - } - - public static class Templates extends AbstractTemplates { - - private final SnippetInfo[] arraycopySnippets; - private final SnippetInfo genericPrimitiveSnippet; - - public Templates(OptionValues options, Iterable factories, HotSpotProviders providers, TargetDescription target) { - super(options, factories, providers, providers.getSnippetReflection(), target); - - arraycopySnippets = new SnippetInfo[JavaKind.values().length]; - arraycopySnippets[JavaKind.Boolean.ordinal()] = snippet(UnsafeArrayCopySnippets.class, "arraycopyBoolean"); - arraycopySnippets[JavaKind.Byte.ordinal()] = snippet(UnsafeArrayCopySnippets.class, "arraycopyByte"); - arraycopySnippets[JavaKind.Short.ordinal()] = snippet(UnsafeArrayCopySnippets.class, "arraycopyShort"); - arraycopySnippets[JavaKind.Char.ordinal()] = snippet(UnsafeArrayCopySnippets.class, "arraycopyChar"); - arraycopySnippets[JavaKind.Int.ordinal()] = snippet(UnsafeArrayCopySnippets.class, "arraycopyInt"); - arraycopySnippets[JavaKind.Long.ordinal()] = snippet(UnsafeArrayCopySnippets.class, "arraycopyLong"); - arraycopySnippets[JavaKind.Float.ordinal()] = snippet(UnsafeArrayCopySnippets.class, "arraycopyFloat"); - arraycopySnippets[JavaKind.Double.ordinal()] = snippet(UnsafeArrayCopySnippets.class, "arraycopyDouble"); - arraycopySnippets[JavaKind.Object.ordinal()] = snippet(UnsafeArrayCopySnippets.class, "arraycopyObject"); - - genericPrimitiveSnippet = snippet(UnsafeArrayCopySnippets.class, "arraycopyPrimitive"); - } - - public void lower(UnsafeArrayCopyNode node, LoweringTool tool) { - JavaKind elementKind = node.getElementKind(); - SnippetInfo snippet; - if (elementKind == null) { - // primitive array of unknown kind - snippet = genericPrimitiveSnippet; - } else { - snippet = arraycopySnippets[elementKind.ordinal()]; - assert snippet != null : "arraycopy snippet for " + elementKind.name() + " not found"; - } - - Arguments args = new Arguments(snippet, node.graph().getGuardsStage(), tool.getLoweringStage()); - node.addSnippetArguments(args); - - SnippetTemplate template = template(node.getDebug(), args); - template.instantiate(providers.getMetaAccess(), node, DEFAULT_REPLACER, args); - } - } -} diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/profiling/ProbabilisticProfileSnippets.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/profiling/ProbabilisticProfileSnippets.java index a287f15dfaf..a13ef3c69f3 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/profiling/ProbabilisticProfileSnippets.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/profiling/ProbabilisticProfileSnippets.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 @@ -54,6 +54,7 @@ import org.graalvm.compiler.replacements.SnippetTemplate.Arguments; import org.graalvm.compiler.replacements.SnippetTemplate.SnippetInfo; import org.graalvm.compiler.replacements.Snippets; +import jdk.vm.ci.code.CodeUtil; import jdk.vm.ci.code.TargetDescription; public class ProbabilisticProfileSnippets implements Snippets { @@ -64,22 +65,22 @@ public class ProbabilisticProfileSnippets implements Snippets { } @Snippet - public static int notificationMask(int freqLog, int probLog) { - int probabilityMask = (1 << probLog) - 1; + public static int notificationMask(int freqLog, int probLog, int stepLog) { int frequencyMask = (1 << freqLog) - 1; - return frequencyMask & ~probabilityMask; + int stepMask = (1 << (stepLog + probLog)) - 1; + return frequencyMask & ~stepMask; } @NodeIntrinsic(ForeignCallNode.class) public static native void methodInvocationEvent(@ConstantNodeParameter ForeignCallDescriptor descriptor, MethodCountersPointer counters); @Snippet - public static void profileMethodEntryWithProbability(MethodCountersPointer counters, int random, @ConstantParameter int freqLog, @ConstantParameter int probLog) { + public static void profileMethodEntryWithProbability(MethodCountersPointer counters, int random, int step, int stepLog, @ConstantParameter int freqLog, @ConstantParameter int probLog) { if (probability(1.0 / (1 << probLog), shouldProfile(probLog, random))) { - int counterValue = counters.readInt(config(INJECTED_VMCONFIG).invocationCounterOffset) + (config(INJECTED_VMCONFIG).invocationCounterIncrement << probLog); + int counterValue = counters.readInt(config(INJECTED_VMCONFIG).invocationCounterOffset) + ((config(INJECTED_VMCONFIG).invocationCounterIncrement * step) << probLog); counters.writeInt(config(INJECTED_VMCONFIG).invocationCounterOffset, counterValue); if (freqLog >= 0) { - int mask = notificationMask(freqLog, probLog); + int mask = notificationMask(freqLog, probLog, stepLog); if (probability(SLOW_PATH_PROBABILITY, (counterValue & (mask << config(INJECTED_VMCONFIG).invocationCounterShift)) == 0)) { methodInvocationEvent(HotSpotBackend.INVOCATION_EVENT, counters); } @@ -91,11 +92,12 @@ public class ProbabilisticProfileSnippets implements Snippets { public static native void methodBackedgeEvent(@ConstantNodeParameter ForeignCallDescriptor descriptor, MethodCountersPointer counters, int bci, int targetBci); @Snippet - public static void profileBackedgeWithProbability(MethodCountersPointer counters, int random, @ConstantParameter int freqLog, @ConstantParameter int probLog, int bci, int targetBci) { + public static void profileBackedgeWithProbability(MethodCountersPointer counters, int random, int step, int stepLog, @ConstantParameter int freqLog, @ConstantParameter int probLog, int bci, + int targetBci) { if (probability(1.0 / (1 << probLog), shouldProfile(probLog, random))) { - int counterValue = counters.readInt(config(INJECTED_VMCONFIG).backedgeCounterOffset) + (config(INJECTED_VMCONFIG).invocationCounterIncrement << probLog); + int counterValue = counters.readInt(config(INJECTED_VMCONFIG).backedgeCounterOffset) + ((config(INJECTED_VMCONFIG).invocationCounterIncrement * step) << probLog); counters.writeInt(config(INJECTED_VMCONFIG).backedgeCounterOffset, counterValue); - int mask = notificationMask(freqLog, probLog); + int mask = notificationMask(freqLog, probLog, stepLog); if (probability(SLOW_PATH_PROBABILITY, (counterValue & (mask << config(INJECTED_VMCONFIG).invocationCounterShift)) == 0)) { methodBackedgeEvent(HotSpotBackend.BACKEDGE_EVENT, counters, bci, targetBci); } @@ -103,10 +105,11 @@ public class ProbabilisticProfileSnippets implements Snippets { } @Snippet - public static void profileConditionalBackedgeWithProbability(MethodCountersPointer counters, int random, @ConstantParameter int freqLog, @ConstantParameter int probLog, boolean branchCondition, + public static void profileConditionalBackedgeWithProbability(MethodCountersPointer counters, int random, int step, int stepLog, @ConstantParameter int freqLog, + @ConstantParameter int probLog, boolean branchCondition, int bci, int targetBci) { if (branchCondition) { - profileBackedgeWithProbability(counters, random, freqLog, probLog, bci, targetBci); + profileBackedgeWithProbability(counters, random, step, stepLog, freqLog, probLog, bci, targetBci); } } @@ -124,6 +127,8 @@ public class ProbabilisticProfileSnippets implements Snippets { StructuredGraph graph = profileNode.graph(); LoadMethodCountersNode counters = graph.unique(new LoadMethodCountersNode(profileNode.getProfiledMethod())); + ConstantNode step = ConstantNode.forInt(profileNode.getStep(), graph); + ConstantNode stepLog = ConstantNode.forInt(CodeUtil.log2(profileNode.getStep()), graph); if (profileNode instanceof ProfileBranchNode) { // Backedge event @@ -132,8 +137,11 @@ public class ProbabilisticProfileSnippets implements Snippets { Arguments args = new Arguments(snippet, graph.getGuardsStage(), tool.getLoweringStage()); ConstantNode bci = ConstantNode.forInt(profileBranchNode.bci(), graph); ConstantNode targetBci = ConstantNode.forInt(profileBranchNode.targetBci(), graph); + args.add("counters", counters); args.add("random", profileBranchNode.getRandom()); + args.add("step", step); + args.add("stepLog", stepLog); args.addConst("freqLog", profileBranchNode.getNotificationFreqLog()); args.addConst("probLog", profileBranchNode.getProbabilityLog()); if (profileBranchNode.hasCondition()) { @@ -148,8 +156,11 @@ public class ProbabilisticProfileSnippets implements Snippets { ProfileInvokeNode profileInvokeNode = (ProfileInvokeNode) profileNode; // Method invocation event Arguments args = new Arguments(profileMethodEntryWithProbability, graph.getGuardsStage(), tool.getLoweringStage()); + args.add("counters", counters); args.add("random", profileInvokeNode.getRandom()); + args.add("step", step); + args.add("stepLog", stepLog); args.addConst("freqLog", profileInvokeNode.getNotificationFreqLog()); args.addConst("probLog", profileInvokeNode.getProbabilityLog()); SnippetTemplate template = template(graph.getDebug(), args); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/profiling/ProfileSnippets.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/profiling/ProfileSnippets.java index 1c5f05e1a13..79d21ab15ee 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/profiling/ProfileSnippets.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/profiling/ProfileSnippets.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 @@ -54,6 +54,7 @@ import org.graalvm.compiler.replacements.SnippetTemplate.Arguments; import org.graalvm.compiler.replacements.SnippetTemplate.SnippetInfo; import org.graalvm.compiler.replacements.Snippets; +import jdk.vm.ci.code.CodeUtil; import jdk.vm.ci.code.TargetDescription; public class ProfileSnippets implements Snippets { @@ -61,12 +62,19 @@ public class ProfileSnippets implements Snippets { public static native void methodInvocationEvent(@ConstantNodeParameter ForeignCallDescriptor descriptor, MethodCountersPointer counters); @Snippet - public static void profileMethodEntry(MethodCountersPointer counters, @ConstantParameter int freqLog) { - int counterValue = counters.readInt(config(INJECTED_VMCONFIG).invocationCounterOffset) + config(INJECTED_VMCONFIG).invocationCounterIncrement; + protected static int notificationMask(int freqLog, int stepLog) { + int stepMask = (1 << stepLog) - 1; + int frequencyMask = (1 << freqLog) - 1; + return frequencyMask & ~stepMask; + } + + @Snippet + public static void profileMethodEntry(MethodCountersPointer counters, int step, int stepLog, @ConstantParameter int freqLog) { + int counterValue = counters.readInt(config(INJECTED_VMCONFIG).invocationCounterOffset) + config(INJECTED_VMCONFIG).invocationCounterIncrement * step; counters.writeInt(config(INJECTED_VMCONFIG).invocationCounterOffset, counterValue); if (freqLog >= 0) { - final int frequencyMask = (1 << freqLog) - 1; - if (probability(SLOW_PATH_PROBABILITY, (counterValue & (frequencyMask << config(INJECTED_VMCONFIG).invocationCounterShift)) == 0)) { + final int mask = notificationMask(freqLog, stepLog); + if (probability(SLOW_PATH_PROBABILITY, (counterValue & (mask << config(INJECTED_VMCONFIG).invocationCounterShift)) == 0)) { methodInvocationEvent(HotSpotBackend.INVOCATION_EVENT, counters); } } @@ -76,19 +84,19 @@ public class ProfileSnippets implements Snippets { public static native void methodBackedgeEvent(@ConstantNodeParameter ForeignCallDescriptor descriptor, MethodCountersPointer counters, int bci, int targetBci); @Snippet - public static void profileBackedge(MethodCountersPointer counters, @ConstantParameter int freqLog, int bci, int targetBci) { - int counterValue = counters.readInt(config(INJECTED_VMCONFIG).backedgeCounterOffset) + config(INJECTED_VMCONFIG).invocationCounterIncrement; + public static void profileBackedge(MethodCountersPointer counters, int step, int stepLog, @ConstantParameter int freqLog, int bci, int targetBci) { + int counterValue = counters.readInt(config(INJECTED_VMCONFIG).backedgeCounterOffset) + config(INJECTED_VMCONFIG).invocationCounterIncrement * step; counters.writeInt(config(INJECTED_VMCONFIG).backedgeCounterOffset, counterValue); - final int frequencyMask = (1 << freqLog) - 1; - if (probability(SLOW_PATH_PROBABILITY, (counterValue & (frequencyMask << config(INJECTED_VMCONFIG).invocationCounterShift)) == 0)) { + final int mask = notificationMask(freqLog, stepLog); + if (probability(SLOW_PATH_PROBABILITY, (counterValue & (mask << config(INJECTED_VMCONFIG).invocationCounterShift)) == 0)) { methodBackedgeEvent(HotSpotBackend.BACKEDGE_EVENT, counters, bci, targetBci); } } @Snippet - public static void profileConditionalBackedge(MethodCountersPointer counters, @ConstantParameter int freqLog, boolean branchCondition, int bci, int targetBci) { + public static void profileConditionalBackedge(MethodCountersPointer counters, int step, int stepLog, @ConstantParameter int freqLog, boolean branchCondition, int bci, int targetBci) { if (branchCondition) { - profileBackedge(counters, freqLog, bci, targetBci); + profileBackedge(counters, step, stepLog, freqLog, bci, targetBci); } } @@ -104,6 +112,8 @@ public class ProfileSnippets implements Snippets { public void lower(ProfileNode profileNode, LoweringTool tool) { StructuredGraph graph = profileNode.graph(); LoadMethodCountersNode counters = graph.unique(new LoadMethodCountersNode(profileNode.getProfiledMethod())); + ConstantNode step = ConstantNode.forInt(profileNode.getStep(), graph); + ConstantNode stepLog = ConstantNode.forInt(CodeUtil.log2(profileNode.getStep()), graph); if (profileNode instanceof ProfileBranchNode) { // Backedge event @@ -113,6 +123,8 @@ public class ProfileSnippets implements Snippets { ConstantNode bci = ConstantNode.forInt(profileBranchNode.bci(), graph); ConstantNode targetBci = ConstantNode.forInt(profileBranchNode.targetBci(), graph); args.add("counters", counters); + args.add("step", step); + args.add("stepLog", stepLog); args.addConst("freqLog", profileBranchNode.getNotificationFreqLog()); if (profileBranchNode.hasCondition()) { args.add("branchCondition", profileBranchNode.branchCondition()); @@ -127,6 +139,8 @@ public class ProfileSnippets implements Snippets { // Method invocation event Arguments args = new Arguments(profileMethodEntry, graph.getGuardsStage(), tool.getLoweringStage()); args.add("counters", counters); + args.add("step", step); + args.add("stepLog", stepLog); args.addConst("freqLog", profileInvokeNode.getNotificationFreqLog()); SnippetTemplate template = template(graph.getDebug(), args); template.instantiate(providers.getMetaAccess(), profileNode, DEFAULT_REPLACER, args); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/NewArrayStub.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/NewArrayStub.java index d7018a1ef75..b5cbb0e5585 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/NewArrayStub.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/NewArrayStub.java @@ -33,6 +33,7 @@ import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil. import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.layoutHelperLog2ElementSizeShift; import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.readLayoutHelper; import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.registerAsWord; +import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.useCMSIncrementalMode; import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.wordSize; import static org.graalvm.compiler.hotspot.replacements.NewObjectSnippets.MAX_ARRAY_FAST_PATH_ALLOCATION_LENGTH; import static org.graalvm.compiler.hotspot.replacements.NewObjectSnippets.formatArray; @@ -122,7 +123,7 @@ public class NewArrayStub extends SnippetStub { // check that array length is small enough for fast path. Word thread = registerAsWord(threadRegister); boolean inlineContiguousAllocationSupported = GraalHotSpotVMConfigNode.inlineContiguousAllocationSupported(); - if (inlineContiguousAllocationSupported && length >= 0 && length <= MAX_ARRAY_FAST_PATH_ALLOCATION_LENGTH) { + if (inlineContiguousAllocationSupported && !useCMSIncrementalMode(INJECTED_VMCONFIG) && length >= 0 && length <= MAX_ARRAY_FAST_PATH_ALLOCATION_LENGTH) { Word memory = refillAllocate(thread, intArrayHub, sizeInBytes, logging(options)); if (memory.notEqual(0)) { if (logging(options)) { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/NewInstanceStub.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/NewInstanceStub.java index 91a5acab1f1..9ebcee8bce7 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/NewInstanceStub.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/NewInstanceStub.java @@ -53,6 +53,7 @@ import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil. import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.tlabRefillWasteLimitOffset; import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.tlabSlowAllocationsOffset; import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.tlabStats; +import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.useCMSIncrementalMode; import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.useG1GC; import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.useTLAB; import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.wordSize; @@ -147,7 +148,7 @@ public class NewInstanceStub extends SnippetStub { */ Word thread = registerAsWord(threadRegister); boolean inlineContiguousAllocationSupported = GraalHotSpotVMConfigNode.inlineContiguousAllocationSupported(); - if (!forceSlowPath(options) && inlineContiguousAllocationSupported) { + if (!forceSlowPath(options) && inlineContiguousAllocationSupported && !useCMSIncrementalMode(INJECTED_VMCONFIG)) { if (isInstanceKlassFullyInitialized(hub)) { int sizeInBytes = readLayoutHelper(hub); Word memory = refillAllocate(thread, intArrayHub, sizeInBytes, logging(options)); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BytecodeParser.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BytecodeParser.java index 4c23ddbc6ea..8a8299098fe 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BytecodeParser.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BytecodeParser.java @@ -342,7 +342,7 @@ import org.graalvm.compiler.nodes.calc.AddNode; import org.graalvm.compiler.nodes.calc.AndNode; import org.graalvm.compiler.nodes.calc.CompareNode; import org.graalvm.compiler.nodes.calc.ConditionalNode; -import org.graalvm.compiler.nodes.calc.DivNode; +import org.graalvm.compiler.nodes.calc.FloatDivNode; import org.graalvm.compiler.nodes.calc.FloatConvertNode; import org.graalvm.compiler.nodes.calc.IntegerBelowNode; import org.graalvm.compiler.nodes.calc.IntegerEqualsNode; @@ -374,7 +374,6 @@ import org.graalvm.compiler.nodes.extended.MembarNode; import org.graalvm.compiler.nodes.extended.StateSplitProxyNode; import org.graalvm.compiler.nodes.extended.ValueAnchorNode; import org.graalvm.compiler.nodes.graphbuilderconf.ClassInitializationPlugin; -import org.graalvm.compiler.nodes.graphbuilderconf.InvokeDynamicPlugin; import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration; import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.BytecodeExceptionMode; import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext; @@ -383,6 +382,7 @@ import org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin.InlineInfo import org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext; import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin; import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins.InvocationPluginReceiver; +import org.graalvm.compiler.nodes.graphbuilderconf.InvokeDynamicPlugin; import org.graalvm.compiler.nodes.graphbuilderconf.NodePlugin; import org.graalvm.compiler.nodes.graphbuilderconf.ProfilingPlugin; import org.graalvm.compiler.nodes.java.ArrayLengthNode; @@ -435,6 +435,7 @@ import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; import jdk.vm.ci.meta.Signature; import jdk.vm.ci.meta.TriState; +import org.graalvm.compiler.core.common.type.IntegerStamp; /** * The {@code GraphBuilder} class parses the bytecode of a method and builds the IR graph. @@ -1036,7 +1037,7 @@ public class BytecodeParser implements GraphBuilderContext { deopt.updateNodeSourcePosition(() -> createBytecodePosition()); } - private AbstractBeginNode handleException(ValueNode exceptionObject, int bci) { + private AbstractBeginNode handleException(ValueNode exceptionObject, int bci, boolean deoptimizeOnly) { assert bci == BytecodeFrame.BEFORE_BCI || bci == bci() : "invalid bci"; debug.log("Creating exception dispatch edges at %d, exception object=%s, exception seen=%s", bci, exceptionObject, (profilingInfo == null ? "" : profilingInfo.getExceptionSeen(bci))); @@ -1058,8 +1059,12 @@ public class BytecodeParser implements GraphBuilderContext { this.controlFlowSplit = true; FixedWithNextNode finishedDispatch = finishInstruction(dispatchBegin, dispatchState); - createHandleExceptionTarget(finishedDispatch, bci, dispatchState); - + if (deoptimizeOnly) { + DeoptimizeNode deoptimizeNode = graph.add(new DeoptimizeNode(DeoptimizationAction.None, DeoptimizationReason.TransferToInterpreter)); + dispatchBegin.setNext(BeginNode.begin(deoptimizeNode)); + } else { + createHandleExceptionTarget(finishedDispatch, bci, dispatchState); + } return dispatchBegin; } @@ -1111,7 +1116,7 @@ public class BytecodeParser implements GraphBuilderContext { } protected ValueNode genFloatDiv(ValueNode x, ValueNode y) { - return DivNode.create(x, y); + return FloatDivNode.create(x, y); } protected ValueNode genFloatRem(ValueNode x, ValueNode y) { @@ -1215,7 +1220,7 @@ public class BytecodeParser implements GraphBuilderContext { ValueNode exception = frameState.pop(JavaKind.Object); FixedGuardNode nullCheck = append(new FixedGuardNode(graph.addOrUniqueWithInputs(IsNullNode.create(exception)), NullCheckException, InvalidateReprofile, true)); ValueNode nonNullException = graph.maybeAddOrUnique(PiNode.create(exception, exception.stamp().join(objectNonNull()), nullCheck)); - lastInstr.setNext(handleException(nonNullException, bci())); + lastInstr.setNext(handleException(nonNullException, bci(), false)); } protected LogicNode createInstanceOf(TypeReference type, ValueNode object) { @@ -1275,12 +1280,12 @@ public class BytecodeParser implements GraphBuilderContext { } BytecodeExceptionNode exception = graph.add(new BytecodeExceptionNode(metaAccess, NullPointerException.class)); AbstractBeginNode falseSucc = graph.add(new BeginNode()); - ValueNode nonNullReceiver = graph.addOrUnique(PiNode.create(receiver, objectNonNull(), falseSucc)); + ValueNode nonNullReceiver = graph.addOrUniqueWithInputs(PiNode.create(receiver, objectNonNull(), falseSucc)); append(new IfNode(graph.addOrUniqueWithInputs(IsNullNode.create(receiver)), exception, falseSucc, SLOW_PATH_PROBABILITY)); lastInstr = falseSucc; exception.setStateAfter(createFrameState(bci(), exception)); - exception.setNext(handleException(exception, bci())); + exception.setNext(handleException(exception, bci(), false)); EXPLICIT_EXCEPTIONS.increment(debug); return nonNullReceiver; } @@ -1292,7 +1297,7 @@ public class BytecodeParser implements GraphBuilderContext { lastInstr = trueSucc; exception.setStateAfter(createFrameState(bci(), exception)); - exception.setNext(handleException(exception, bci())); + exception.setNext(handleException(exception, bci(), false)); } protected ValueNode genArrayLength(ValueNode x) { @@ -1532,8 +1537,8 @@ public class BytecodeParser implements GraphBuilderContext { @Override public void handleReplacedInvoke(CallTargetNode callTarget, JavaKind resultType) { BytecodeParser intrinsicCallSiteParser = getNonIntrinsicAncestor(); - boolean withExceptionEdge = intrinsicCallSiteParser == null ? !omitInvokeExceptionEdge(null) : !intrinsicCallSiteParser.omitInvokeExceptionEdge(null); - createNonInlinedInvoke(withExceptionEdge, bci(), callTarget, resultType); + ExceptionEdgeAction exceptionEdgeAction = intrinsicCallSiteParser == null ? getActionForInvokeExceptionEdge(null) : intrinsicCallSiteParser.getActionForInvokeExceptionEdge(null); + createNonInlinedInvoke(exceptionEdgeAction, bci(), callTarget, resultType); } protected Invoke appendInvoke(InvokeKind initialInvokeKind, ResolvedJavaMethod initialTargetMethod, ValueNode[] args) { @@ -1603,7 +1608,7 @@ public class BytecodeParser implements GraphBuilderContext { int invokeBci = bci(); JavaTypeProfile profile = getProfileForInvoke(invokeKind); - boolean withExceptionEdge = !omitInvokeExceptionEdge(inlineInfo); + ExceptionEdgeAction edgeAction = getActionForInvokeExceptionEdge(inlineInfo); boolean partialIntrinsicExit = false; if (intrinsicContext != null && intrinsicContext.isCallToOriginal(targetMethod)) { partialIntrinsicExit = true; @@ -1614,7 +1619,7 @@ public class BytecodeParser implements GraphBuilderContext { // must use the same context as the call to the intrinsic. invokeBci = intrinsicCallSiteParser.bci(); profile = intrinsicCallSiteParser.getProfileForInvoke(invokeKind); - withExceptionEdge = !intrinsicCallSiteParser.omitInvokeExceptionEdge(inlineInfo); + edgeAction = intrinsicCallSiteParser.getActionForInvokeExceptionEdge(inlineInfo); } else { // We are parsing the intrinsic for the root compilation or for inlining, // This call is a partial intrinsic exit, and we do not have profile information @@ -1624,7 +1629,7 @@ public class BytecodeParser implements GraphBuilderContext { assert intrinsicContext.isPostParseInlined(); invokeBci = BytecodeFrame.UNKNOWN_BCI; profile = null; - withExceptionEdge = graph.method().getAnnotation(Snippet.class) == null; + edgeAction = graph.method().getAnnotation(Snippet.class) == null ? ExceptionEdgeAction.INCLUDE_AND_HANDLE : ExceptionEdgeAction.OMIT; } if (originalMethod.isStatic()) { @@ -1637,10 +1642,10 @@ public class BytecodeParser implements GraphBuilderContext { Signature sig = originalMethod.getSignature(); returnType = sig.getReturnType(method.getDeclaringClass()); resultType = sig.getReturnKind(); - assert checkPartialIntrinsicExit(intrinsicCallSiteParser == null ? null : intrinsicCallSiteParser.currentInvoke.args, args); + assert intrinsicContext.allowPartialIntrinsicArgumentMismatch() || checkPartialIntrinsicExit(intrinsicCallSiteParser == null ? null : intrinsicCallSiteParser.currentInvoke.args, args); targetMethod = originalMethod; } - Invoke invoke = createNonInlinedInvoke(withExceptionEdge, invokeBci, args, targetMethod, invokeKind, resultType, returnType, profile); + Invoke invoke = createNonInlinedInvoke(edgeAction, invokeBci, args, targetMethod, invokeKind, resultType, returnType, profile); if (partialIntrinsicExit) { // This invoke must never be later inlined as it might select the intrinsic graph. // Until there is a mechanism to guarantee that any late inlining will not select @@ -1698,14 +1703,14 @@ public class BytecodeParser implements GraphBuilderContext { } else { for (int i = 0; i < recursiveArgs.length; i++) { ValueNode arg = GraphUtil.unproxify(recursiveArgs[i]); - assert arg instanceof ParameterNode && ((ParameterNode) arg).index() == i : String.format("argument %d of call denoting partial intrinsic exit should be a %s with index %d, not %s", i, - ParameterNode.class.getSimpleName(), i, arg); + assert arg instanceof ParameterNode && ((ParameterNode) arg).index() == i : String.format("argument %d of call denoting partial intrinsic exit should be a %s with index %d, not %s", + i, ParameterNode.class.getSimpleName(), i, arg); } } return true; } - protected Invoke createNonInlinedInvoke(boolean withExceptionEdge, int invokeBci, ValueNode[] invokeArgs, ResolvedJavaMethod targetMethod, + protected Invoke createNonInlinedInvoke(ExceptionEdgeAction exceptionEdge, int invokeBci, ValueNode[] invokeArgs, ResolvedJavaMethod targetMethod, InvokeKind invokeKind, JavaKind resultType, JavaType returnType, JavaTypeProfile profile) { StampPair returnStamp = graphBuilderConfig.getPlugins().getOverridingStamp(this, returnType, false); @@ -1714,7 +1719,7 @@ public class BytecodeParser implements GraphBuilderContext { } MethodCallTargetNode callTarget = graph.add(createMethodCallTarget(invokeKind, targetMethod, invokeArgs, returnStamp, profile)); - Invoke invoke = createNonInlinedInvoke(withExceptionEdge, invokeBci, callTarget, resultType); + Invoke invoke = createNonInlinedInvoke(exceptionEdge, invokeBci, callTarget, resultType); for (InlineInvokePlugin plugin : graphBuilderConfig.getPlugins().getInlineInvokePlugins()) { plugin.notifyNotInlined(this, targetMethod, invoke); @@ -1723,11 +1728,11 @@ public class BytecodeParser implements GraphBuilderContext { return invoke; } - protected Invoke createNonInlinedInvoke(boolean withExceptionEdge, int invokeBci, CallTargetNode callTarget, JavaKind resultType) { - if (!withExceptionEdge) { + protected Invoke createNonInlinedInvoke(ExceptionEdgeAction exceptionEdge, int invokeBci, CallTargetNode callTarget, JavaKind resultType) { + if (exceptionEdge == ExceptionEdgeAction.OMIT) { return createInvoke(invokeBci, callTarget, resultType); } else { - Invoke invoke = createInvokeWithException(invokeBci, callTarget, resultType); + Invoke invoke = createInvokeWithException(invokeBci, callTarget, resultType, exceptionEdge); AbstractBeginNode beginNode = graph.add(KillingBeginNode.create(LocationIdentity.any())); invoke.setNext(beginNode); lastInstr = beginNode; @@ -1736,20 +1741,29 @@ public class BytecodeParser implements GraphBuilderContext { } /** - * If the method returns true, the invocation of the given {@link MethodCallTargetNode call - * target} does not need an exception edge. + * Describes what should be done with the exception edge of an invocation. The edge can be + * omitted or included. An included edge can handle the exception or transfer execution to the + * interpreter for handling (deoptimize). */ - protected boolean omitInvokeExceptionEdge(InlineInfo lastInlineInfo) { + protected enum ExceptionEdgeAction { + OMIT, + INCLUDE_AND_HANDLE, + INCLUDE_AND_DEOPTIMIZE + } + + protected ExceptionEdgeAction getActionForInvokeExceptionEdge(InlineInfo lastInlineInfo) { if (lastInlineInfo == InlineInfo.DO_NOT_INLINE_WITH_EXCEPTION) { - return false; + return ExceptionEdgeAction.INCLUDE_AND_HANDLE; } else if (lastInlineInfo == InlineInfo.DO_NOT_INLINE_NO_EXCEPTION) { - return true; + return ExceptionEdgeAction.OMIT; + } else if (lastInlineInfo == InlineInfo.DO_NOT_INLINE_DEOPTIMIZE_ON_EXCEPTION) { + return ExceptionEdgeAction.INCLUDE_AND_DEOPTIMIZE; } else if (graphBuilderConfig.getBytecodeExceptionMode() == BytecodeExceptionMode.CheckAll) { - return false; + return ExceptionEdgeAction.INCLUDE_AND_HANDLE; } else if (graphBuilderConfig.getBytecodeExceptionMode() == BytecodeExceptionMode.ExplicitOnly) { - return false; + return ExceptionEdgeAction.INCLUDE_AND_HANDLE; } else if (graphBuilderConfig.getBytecodeExceptionMode() == BytecodeExceptionMode.OmitAll) { - return true; + return ExceptionEdgeAction.OMIT; } else { assert graphBuilderConfig.getBytecodeExceptionMode() == BytecodeExceptionMode.Profile; // be conservative if information was not recorded (could result in endless @@ -1759,12 +1773,12 @@ public class BytecodeParser implements GraphBuilderContext { if (profilingInfo != null) { TriState exceptionSeen = profilingInfo.getExceptionSeen(bci()); if (exceptionSeen == TriState.FALSE) { - return true; + return ExceptionEdgeAction.OMIT; } } } } - return false; + return ExceptionEdgeAction.INCLUDE_AND_HANDLE; } } @@ -1887,7 +1901,7 @@ public class BytecodeParser implements GraphBuilderContext { if (newProfile != profile) { if (newProfile.getTypes().length == 0) { // All profiled types select the intrinsic so - // emit a fixed guard instead of a if-then-else. + // emit a fixed guard instead of an if-then-else. lastInstr = append(new FixedGuardNode(compare, TypeCheckedInliningViolated, InvalidateReprofile, false)); return new IntrinsicGuard(currentLastInstr, intrinsicReceiver, mark, null, null); } @@ -1966,7 +1980,7 @@ public class BytecodeParser implements GraphBuilderContext { } lastInstr = intrinsicGuard.nonIntrinsicBranch; - createNonInlinedInvoke(omitInvokeExceptionEdge(null), bci(), args, targetMethod, invokeKind, resultType, returnType, intrinsicGuard.profile); + createNonInlinedInvoke(getActionForInvokeExceptionEdge(null), bci(), args, targetMethod, invokeKind, resultType, returnType, intrinsicGuard.profile); EndNode nonIntrinsicEnd = append(new EndNode()); AbstractMergeNode mergeNode = graph.add(new MergeNode()); @@ -2303,7 +2317,7 @@ public class BytecodeParser implements GraphBuilderContext { if (calleeBeforeUnwindNode != null) { ValueNode calleeUnwindValue = parser.getUnwindValue(); assert calleeUnwindValue != null; - calleeBeforeUnwindNode.setNext(handleException(calleeUnwindValue, bci())); + calleeBeforeUnwindNode.setNext(handleException(calleeUnwindValue, bci(), false)); } } } @@ -2319,7 +2333,7 @@ public class BytecodeParser implements GraphBuilderContext { return invoke; } - protected InvokeWithExceptionNode createInvokeWithException(int invokeBci, CallTargetNode callTarget, JavaKind resultType) { + protected InvokeWithExceptionNode createInvokeWithException(int invokeBci, CallTargetNode callTarget, JavaKind resultType, ExceptionEdgeAction exceptionEdgeAction) { if (currentBlock != null && stream.nextBCI() > currentBlock.endBci) { /* * Clear non-live locals early so that the exception handler entry gets the cleared @@ -2328,7 +2342,7 @@ public class BytecodeParser implements GraphBuilderContext { frameState.clearNonLiveLocals(currentBlock, liveness, false); } - AbstractBeginNode exceptionEdge = handleException(null, bci()); + AbstractBeginNode exceptionEdge = handleException(null, bci(), exceptionEdgeAction == ExceptionEdgeAction.INCLUDE_AND_DEOPTIMIZE); InvokeWithExceptionNode invoke = append(new InvokeWithExceptionNode(callTarget, exceptionEdge, invokeBci)); frameState.pushReturn(resultType, invoke); invoke.setStateAfter(createFrameState(stream.nextBCI(), invoke)); @@ -2359,20 +2373,43 @@ public class BytecodeParser implements GraphBuilderContext { } } + ValueNode realReturnVal = processReturnValue(returnVal, returnKind); + frameState.setRethrowException(false); frameState.clearStack(); - beforeReturn(returnVal, returnKind); + beforeReturn(realReturnVal, returnKind); if (parent == null) { - append(new ReturnNode(returnVal)); + append(new ReturnNode(realReturnVal)); } else { if (returnDataList == null) { returnDataList = new ArrayList<>(); } - returnDataList.add(new ReturnToCallerData(returnVal, lastInstr)); + returnDataList.add(new ReturnToCallerData(realReturnVal, lastInstr)); lastInstr = null; } } + private ValueNode processReturnValue(ValueNode value, JavaKind kind) { + JavaKind returnKind = method.getSignature().getReturnKind(); + if (kind != returnKind) { + // sub-word integer + assert returnKind.isNumericInteger() && returnKind.getStackKind() == JavaKind.Int; + IntegerStamp stamp = (IntegerStamp) value.stamp(); + + // the bytecode verifier doesn't check that the value is in the correct range + if (stamp.lowerBound() < returnKind.getMinValue() || returnKind.getMaxValue() < stamp.upperBound()) { + ValueNode narrow = append(genNarrow(value, returnKind.getBitCount())); + if (returnKind.isUnsigned()) { + return append(genZeroExtend(narrow, 32)); + } else { + return append(genSignExtend(narrow, 32)); + } + } + } + + return value; + } + private void beforeReturn(ValueNode x, JavaKind kind) { if (graph.method() != null && graph.method().isJavaLangObjectInit()) { /* @@ -2794,6 +2831,8 @@ public class BytecodeParser implements GraphBuilderContext { } private void createExceptionDispatch(ExceptionDispatchBlock block) { + lastInstr = finishInstruction(lastInstr, frameState); + assert frameState.stackSize() == 1 : frameState; if (block.handler.isCatchAll()) { assert block.getSuccessorCount() == 1; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/FrameStateBuilder.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/FrameStateBuilder.java index f045d25a6bd..b924c25f85c 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/FrameStateBuilder.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/FrameStateBuilder.java @@ -207,7 +207,7 @@ public final class FrameStateBuilder implements SideEffectsState { receiver = new ParameterNode(javaIndex, receiverStamp); } - locals[javaIndex] = graph.addOrUnique(receiver); + locals[javaIndex] = graph.addOrUniqueWithInputs(receiver); javaIndex = 1; index = 1; } @@ -241,7 +241,7 @@ public final class FrameStateBuilder implements SideEffectsState { param = new ParameterNode(index, stamp); } - locals[javaIndex] = graph.addOrUnique(param); + locals[javaIndex] = graph.addOrUniqueWithInputs(param); javaIndex++; if (kind.needsTwoSlots()) { locals[javaIndex] = TWO_SLOT_MARKER; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.jtt/src/org/graalvm/compiler/jtt/optimize/ConditionalElimination02.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.jtt/src/org/graalvm/compiler/jtt/optimize/ConditionalElimination02.java index e975bd8d3de..3d709ee1792 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.jtt/src/org/graalvm/compiler/jtt/optimize/ConditionalElimination02.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.jtt/src/org/graalvm/compiler/jtt/optimize/ConditionalElimination02.java @@ -29,6 +29,9 @@ import jdk.vm.ci.meta.DeoptimizationReason; import org.junit.Test; import org.graalvm.compiler.jtt.JTTTest; +import org.graalvm.compiler.phases.OptimisticOptimizations; +import org.graalvm.compiler.phases.OptimisticOptimizations.Optimization; +import org.graalvm.compiler.phases.tiers.HighTierContext; public class ConditionalElimination02 extends JTTTest { @@ -59,6 +62,14 @@ public class ConditionalElimination02 extends JTTTest { return -1; } + /** + * These tests assume all code paths are reachable so disable profile based dead code removal. + */ + @Override + protected HighTierContext getDefaultHighTierContext() { + return new HighTierContext(getProviders(), getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL.remove(Optimization.RemoveNeverExecutedCode)); + } + @Test public void run0() throws Throwable { runTest(EnumSet.of(DeoptimizationReason.NullCheckException), "test", new A(5), false, false); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.jtt/src/org/graalvm/compiler/jtt/optimize/Fold_Double04.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.jtt/src/org/graalvm/compiler/jtt/optimize/Fold_Double04.java new file mode 100644 index 00000000000..f2a73a25357 --- /dev/null +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.jtt/src/org/graalvm/compiler/jtt/optimize/Fold_Double04.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2017, 2017, 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 org.graalvm.compiler.jtt.optimize; + +import org.junit.Test; + +import org.graalvm.compiler.jtt.JTTTest; + +/* + * Tests constant folding of double operations. + */ +public class Fold_Double04 extends JTTTest { + + // Contrived check whether both arguments are the same kind of zero + public static boolean test(double x, double y) { + if (x == 0) { + if (1 / x == Double.NEGATIVE_INFINITY) { + return 1 / y == Double.NEGATIVE_INFINITY; + } else { + return 1 / y == Double.POSITIVE_INFINITY; + } + } + return false; + } + + @Test + public void run0() throws Throwable { + runTest("test", -0d, -0d); + } + + @Test + public void run1() throws Throwable { + runTest("test", -0d, 0d); + } + + @Test + public void run2() throws Throwable { + runTest("test", 0d, -0d); + } + + @Test + public void run3() throws Throwable { + runTest("test", 0d, 0d); + } + +} diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.jtt/src/org/graalvm/compiler/jtt/optimize/Fold_Float03.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.jtt/src/org/graalvm/compiler/jtt/optimize/Fold_Float03.java new file mode 100644 index 00000000000..74e8fe8d434 --- /dev/null +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.jtt/src/org/graalvm/compiler/jtt/optimize/Fold_Float03.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2017, 2017, 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 org.graalvm.compiler.jtt.optimize; + +import org.junit.Test; + +import org.graalvm.compiler.jtt.JTTTest; + +/* + * Tests constant folding of float operations. + */ +public class Fold_Float03 extends JTTTest { + + // Contrived check whether both arguments are the same kind of zero + public static boolean test(float x, float y) { + if (x == 0) { + if (1 / x == Float.NEGATIVE_INFINITY) { + return 1 / y == Float.NEGATIVE_INFINITY; + } else { + return 1 / y == Float.POSITIVE_INFINITY; + } + } + return false; + } + + @Test + public void run0() throws Throwable { + runTest("test", -0f, -0f); + } + + @Test + public void run1() throws Throwable { + runTest("test", -0f, 0f); + } + + @Test + public void run2() throws Throwable { + runTest("test", 0f, -0f); + } + + @Test + public void run3() throws Throwable { + runTest("test", 0f, 0f); + } + +} diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.amd64/src/org/graalvm/compiler/lir/amd64/AMD64ArrayEqualsOp.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.amd64/src/org/graalvm/compiler/lir/amd64/AMD64ArrayEqualsOp.java index a9f8e9221cd..8ba05753b81 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.amd64/src/org/graalvm/compiler/lir/amd64/AMD64ArrayEqualsOp.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.amd64/src/org/graalvm/compiler/lir/amd64/AMD64ArrayEqualsOp.java @@ -37,6 +37,7 @@ import org.graalvm.compiler.asm.amd64.AMD64Assembler.OperandSize; import org.graalvm.compiler.asm.amd64.AMD64Assembler.SSEOp; import org.graalvm.compiler.asm.amd64.AMD64MacroAssembler; import org.graalvm.compiler.core.common.LIRKind; +import org.graalvm.compiler.core.common.NumUtil; import org.graalvm.compiler.lir.LIRInstructionClass; import org.graalvm.compiler.lir.Opcode; import org.graalvm.compiler.lir.asm.CompilationResultBuilder; @@ -132,12 +133,19 @@ public final class AMD64ArrayEqualsOp extends AMD64LIRInstruction { masm.leaq(array2, new AMD64Address(asRegister(array2Value), arrayBaseOffset)); // Get array length in bytes. - masm.imull(length, asRegister(lengthValue), arrayIndexScale); + masm.movl(length, asRegister(lengthValue)); + + if (arrayIndexScale > 1) { + masm.shll(length, NumUtil.log2Ceil(arrayIndexScale)); // scale length + } + masm.movl(result, length); // copy if (supportsAVX2(crb.target)) { emitAVXCompare(crb, masm, result, array1, array2, length, trueLabel, falseLabel); } else if (supportsSSE41(crb.target)) { + // this code is used for AVX as well because our backend correctly ensures that + // VEX-prefixed instructions are emitted if AVX is supported emitSSE41Compare(crb, masm, result, array1, array2, length, trueLabel, falseLabel); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.amd64/src/org/graalvm/compiler/lir/amd64/AMD64ControlFlow.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.amd64/src/org/graalvm/compiler/lir/amd64/AMD64ControlFlow.java index 41cf3c9a6f3..c3c7c84aef0 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.amd64/src/org/graalvm/compiler/lir/amd64/AMD64ControlFlow.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.amd64/src/org/graalvm/compiler/lir/amd64/AMD64ControlFlow.java @@ -22,21 +22,21 @@ */ package org.graalvm.compiler.lir.amd64; +import static jdk.vm.ci.code.ValueUtil.asRegister; +import static jdk.vm.ci.code.ValueUtil.isRegister; import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.CONST; import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.HINT; import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.ILLEGAL; import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.REG; import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.STACK; -import static jdk.vm.ci.code.ValueUtil.asRegister; -import static jdk.vm.ci.code.ValueUtil.isRegister; import org.graalvm.compiler.asm.Label; -import org.graalvm.compiler.core.common.NumUtil; import org.graalvm.compiler.asm.amd64.AMD64Address; import org.graalvm.compiler.asm.amd64.AMD64Address.Scale; import org.graalvm.compiler.asm.amd64.AMD64Assembler.ConditionFlag; import org.graalvm.compiler.asm.amd64.AMD64MacroAssembler; import org.graalvm.compiler.code.CompilationResult.JumpTable; +import org.graalvm.compiler.core.common.NumUtil; import org.graalvm.compiler.core.common.calc.Condition; import org.graalvm.compiler.debug.GraalError; import org.graalvm.compiler.lir.LIRInstructionClass; @@ -312,6 +312,42 @@ public class AMD64ControlFlow { } } + @Opcode("SETcc") + public static final class CondSetOp extends AMD64LIRInstruction { + public static final LIRInstructionClass TYPE = LIRInstructionClass.create(CondSetOp.class); + @Def({REG, HINT}) protected Value result; + private final ConditionFlag condition; + + public CondSetOp(Variable result, Condition condition) { + super(TYPE); + this.result = result; + this.condition = intCond(condition); + } + + @Override + public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) { + setcc(masm, result, condition); + } + } + + @Opcode("SETcc") + public static final class FloatCondSetOp extends AMD64LIRInstruction { + public static final LIRInstructionClass TYPE = LIRInstructionClass.create(FloatCondSetOp.class); + @Def({REG, HINT}) protected Value result; + private final ConditionFlag condition; + + public FloatCondSetOp(Variable result, Condition condition) { + super(TYPE); + this.result = result; + this.condition = floatCond(condition); + } + + @Override + public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) { + setcc(masm, result, condition); + } + } + @Opcode("CMOVE") public static final class CondMoveOp extends AMD64LIRInstruction { public static final LIRInstructionClass TYPE = LIRInstructionClass.create(CondMoveOp.class); @@ -418,6 +454,21 @@ public class AMD64ControlFlow { } } + private static void setcc(AMD64MacroAssembler masm, Value result, ConditionFlag cond) { + switch ((AMD64Kind) result.getPlatformKind()) { + case BYTE: + case WORD: + case DWORD: + masm.setl(cond, asRegister(result)); + break; + case QWORD: + masm.setq(cond, asRegister(result)); + break; + default: + throw GraalError.shouldNotReachHere(); + } + } + private static ConditionFlag intCond(Condition cond) { switch (cond) { case EQ: @@ -464,6 +515,10 @@ public class AMD64ControlFlow { } } + public static boolean trueOnUnordered(Condition condition) { + return trueOnUnordered(floatCond(condition)); + } + private static boolean trueOnUnordered(ConditionFlag condition) { switch (condition) { case AboveEqual: diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.amd64/src/org/graalvm/compiler/lir/amd64/AMD64Move.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.amd64/src/org/graalvm/compiler/lir/amd64/AMD64Move.java index 8f5785159a1..38fdd263a6c 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.amd64/src/org/graalvm/compiler/lir/amd64/AMD64Move.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.amd64/src/org/graalvm/compiler/lir/amd64/AMD64Move.java @@ -22,8 +22,11 @@ */ package org.graalvm.compiler.lir.amd64; +import static org.graalvm.compiler.asm.amd64.AMD64Assembler.ConditionFlag.Equal; +import static org.graalvm.compiler.core.common.GraalOptions.GeneratePIC; import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.COMPOSITE; import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.HINT; +import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.ILLEGAL; import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.REG; import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.STACK; import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.UNINITIALIZED; @@ -35,12 +38,16 @@ import static jdk.vm.ci.code.ValueUtil.asRegister; import static jdk.vm.ci.code.ValueUtil.isRegister; import static jdk.vm.ci.code.ValueUtil.isStackSlot; +import org.graalvm.compiler.asm.Label; +import org.graalvm.compiler.core.common.CompressEncoding; +import org.graalvm.compiler.core.common.LIRKind; import org.graalvm.compiler.core.common.NumUtil; import org.graalvm.compiler.asm.amd64.AMD64Address; import org.graalvm.compiler.asm.amd64.AMD64Assembler.AMD64MIOp; import org.graalvm.compiler.asm.amd64.AMD64Assembler.AMD64MOp; import org.graalvm.compiler.asm.amd64.AMD64Assembler.OperandSize; import org.graalvm.compiler.asm.amd64.AMD64MacroAssembler; +import org.graalvm.compiler.core.common.spi.LIRKindTool; import org.graalvm.compiler.core.common.type.DataPointerConstant; import org.graalvm.compiler.debug.GraalError; import org.graalvm.compiler.lir.LIRFrameState; @@ -740,4 +747,110 @@ public class AMD64Move { throw GraalError.shouldNotReachHere("Unknown result Kind: " + result.getPlatformKind()); } } + + public abstract static class Pointer extends AMD64LIRInstruction { + protected final LIRKindTool lirKindTool; + protected final CompressEncoding encoding; + protected final boolean nonNull; + + @Def({REG, HINT}) private AllocatableValue result; + @Use({REG}) private AllocatableValue input; + @Alive({REG, ILLEGAL}) private AllocatableValue baseRegister; + + protected Pointer(LIRInstructionClass type, AllocatableValue result, AllocatableValue input, AllocatableValue baseRegister, CompressEncoding encoding, boolean nonNull, + LIRKindTool lirKindTool) { + super(type); + this.result = result; + this.input = input; + this.baseRegister = baseRegister; + this.encoding = encoding; + this.nonNull = nonNull; + this.lirKindTool = lirKindTool; + } + + protected boolean hasBase(CompilationResultBuilder crb) { + return GeneratePIC.getValue(crb.getOptions()) || encoding.hasBase(); + } + + protected final Register getResultRegister() { + return asRegister(result); + } + + protected final Register getBaseRegister() { + return asRegister(baseRegister); + } + + protected final int getShift() { + return encoding.getShift(); + } + + protected final void move(LIRKind kind, CompilationResultBuilder crb, AMD64MacroAssembler masm) { + AMD64Move.move((AMD64Kind) kind.getPlatformKind(), crb, masm, result, input); + } + } + + public static final class CompressPointer extends Pointer { + public static final LIRInstructionClass TYPE = LIRInstructionClass.create(CompressPointer.class); + + public CompressPointer(AllocatableValue result, AllocatableValue input, AllocatableValue baseRegister, CompressEncoding encoding, boolean nonNull, LIRKindTool lirKindTool) { + super(TYPE, result, input, baseRegister, encoding, nonNull, lirKindTool); + } + + @Override + public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) { + move(lirKindTool.getObjectKind(), crb, masm); + + Register resReg = getResultRegister(); + if (hasBase(crb)) { + Register baseReg = getBaseRegister(); + if (!nonNull) { + masm.testq(resReg, resReg); + masm.cmovq(Equal, resReg, baseReg); + } + masm.subq(resReg, baseReg); + } + + int shift = getShift(); + if (shift != 0) { + masm.shrq(resReg, shift); + } + } + } + + public static final class UncompressPointer extends Pointer { + public static final LIRInstructionClass TYPE = LIRInstructionClass.create(UncompressPointer.class); + + public UncompressPointer(AllocatableValue result, AllocatableValue input, AllocatableValue baseRegister, CompressEncoding encoding, boolean nonNull, LIRKindTool lirKindTool) { + super(TYPE, result, input, baseRegister, encoding, nonNull, lirKindTool); + } + + @Override + public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) { + move(lirKindTool.getNarrowOopKind(), crb, masm); + + Register resReg = getResultRegister(); + int shift = getShift(); + if (shift != 0) { + masm.shlq(resReg, shift); + } + + if (hasBase(crb)) { + Register baseReg = getBaseRegister(); + if (nonNull) { + masm.addq(resReg, baseReg); + return; + } + + if (shift == 0) { + // if encoding.shift != 0, the flags are already set by the shlq + masm.testq(resReg, resReg); + } + + Label done = new Label(); + masm.jccb(Equal, done); + masm.addq(resReg, baseReg); + masm.bind(done); + } + } + } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.sparc/src/org/graalvm/compiler/lir/sparc/SPARCLoadConstantTableBaseOp.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.sparc/src/org/graalvm/compiler/lir/sparc/SPARCLoadConstantTableBaseOp.java index bb47d833eae..0eaedd1da7c 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.sparc/src/org/graalvm/compiler/lir/sparc/SPARCLoadConstantTableBaseOp.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.sparc/src/org/graalvm/compiler/lir/sparc/SPARCLoadConstantTableBaseOp.java @@ -55,8 +55,8 @@ import jdk.vm.ci.meta.AllocatableValue; * this case absolute addressing (without using the base pointer is used). See also: * CodeInstaller::pd_patch_DataSectionReference * - * @see SPARCMove#loadFromConstantTable(CompilationResultBuilder, SPARCMacroAssembler, int, - * Register, jdk.vm.ci.meta.Constant, Register, SPARCDelayedControlTransfer) + * @see SPARCMove#loadFromConstantTable(CompilationResultBuilder, SPARCMacroAssembler, Register, + * jdk.vm.ci.meta.Constant, Register, SPARCDelayedControlTransfer) */ public class SPARCLoadConstantTableBaseOp extends SPARCLIRInstruction { public static final LIRInstructionClass TYPE = LIRInstructionClass.create(SPARCLoadConstantTableBaseOp.class); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.sparc/src/org/graalvm/compiler/lir/sparc/SPARCMove.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.sparc/src/org/graalvm/compiler/lir/sparc/SPARCMove.java index b35e9b55750..22dedeaefe0 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.sparc/src/org/graalvm/compiler/lir/sparc/SPARCMove.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.sparc/src/org/graalvm/compiler/lir/sparc/SPARCMove.java @@ -53,6 +53,7 @@ import org.graalvm.compiler.asm.sparc.SPARCAddress; import org.graalvm.compiler.asm.sparc.SPARCAssembler; import org.graalvm.compiler.asm.sparc.SPARCMacroAssembler; import org.graalvm.compiler.asm.sparc.SPARCMacroAssembler.ScratchRegister; +import org.graalvm.compiler.code.DataSection.Data; import org.graalvm.compiler.core.common.LIRKind; import org.graalvm.compiler.core.common.type.DataPointerConstant; import org.graalvm.compiler.debug.GraalError; @@ -116,11 +117,11 @@ public class SPARCMove { public static final LIRInstructionClass TYPE = LIRInstructionClass.create(LoadConstantFromTable.class); public static final SizeEstimate SIZE = SizeEstimate.create(1, 8); - private Constant constant; + private JavaConstant constant; @Def({REG, STACK}) AllocatableValue result; @Use({REG}) private AllocatableValue constantTableBase; - public LoadConstantFromTable(Constant constant, AllocatableValue constantTableBase, AllocatableValue result) { + public LoadConstantFromTable(JavaConstant constant, AllocatableValue constantTableBase, AllocatableValue result) { super(TYPE, SIZE); this.constant = constant; this.result = result; @@ -134,11 +135,11 @@ public class SPARCMove { Register baseRegister = asRegister(constantTableBase); if (isRegister(result)) { Register resultRegister = asRegister(result); - loadFromConstantTable(crb, masm, byteCount, baseRegister, constant, resultRegister, getDelayedControlTransfer()); + loadFromConstantTable(crb, masm, baseRegister, constant, resultRegister, getDelayedControlTransfer()); } else if (isStackSlot(result)) { try (ScratchRegister scratch = masm.getScratchRegister()) { Register scratchRegister = scratch.getRegister(); - loadFromConstantTable(crb, masm, byteCount, baseRegister, constant, scratchRegister, getDelayedControlTransfer()); + loadFromConstantTable(crb, masm, baseRegister, constant, scratchRegister, getDelayedControlTransfer()); StackSlot slot = asStackSlot(result); reg2stack(crb, masm, slot, scratchRegister.asValue(), getDelayedControlTransfer()); } @@ -642,7 +643,6 @@ public class SPARCMove { boolean hasVIS1 = cpuFeatures.contains(CPUFeature.VIS1); boolean hasVIS3 = cpuFeatures.contains(CPUFeature.VIS3); Register resultRegister = asRegister(result); - int byteCount = result.getPlatformKind().getSizeInBytes(); switch (input.getJavaKind().getStackKind()) { case Int: if (input.isDefaultForKind()) { @@ -655,7 +655,7 @@ public class SPARCMove { if (constantTableBase.equals(g0)) { throw GraalError.shouldNotReachHere(); } else { - loadFromConstantTable(crb, masm, byteCount, constantTableBase, input, resultRegister, delaySlotLir); + loadFromConstantTable(crb, masm, constantTableBase, input, resultRegister, delaySlotLir); } } break; @@ -667,7 +667,7 @@ public class SPARCMove { delaySlotLir.emitControlTransfer(crb, masm); masm.or(g0, (int) input.asLong(), resultRegister); } else { - loadFromConstantTable(crb, masm, byteCount, constantTableBase, input, resultRegister, delaySlotLir); + loadFromConstantTable(crb, masm, constantTableBase, input, resultRegister, delaySlotLir); } break; case Float: { @@ -683,7 +683,7 @@ public class SPARCMove { masm.movwtos(scratch, resultRegister); } else { // First load the address into the scratch register - loadFromConstantTable(crb, masm, byteCount, constantTableBase, input, resultRegister, delaySlotLir); + loadFromConstantTable(crb, masm, constantTableBase, input, resultRegister, delaySlotLir); } } break; @@ -700,7 +700,7 @@ public class SPARCMove { delaySlotLir.emitControlTransfer(crb, masm); masm.movxtod(scratch, resultRegister); } else { - loadFromConstantTable(crb, masm, byteCount, constantTableBase, input, resultRegister, delaySlotLir); + loadFromConstantTable(crb, masm, constantTableBase, input, resultRegister, delaySlotLir); } } break; @@ -710,7 +710,7 @@ public class SPARCMove { delaySlotLir.emitControlTransfer(crb, masm); masm.clr(resultRegister); } else { - loadFromConstantTable(crb, masm, byteCount, constantTableBase, input, resultRegister, delaySlotLir); + loadFromConstantTable(crb, masm, constantTableBase, input, resultRegister, delaySlotLir); } break; default: @@ -768,25 +768,30 @@ public class SPARCMove { * patterns used for small constant sections (<8k) and large constant sections (>=8k). The * generated patterns by this method must be understood by * CodeInstaller::pd_patch_DataSectionReference (jvmciCodeInstaller_sparc.cpp). + * + * @return the number of bytes loaded from the constant table */ - public static void loadFromConstantTable(CompilationResultBuilder crb, SPARCMacroAssembler masm, int byteCount, Register constantTableBase, Constant input, Register dest, + public static int loadFromConstantTable(CompilationResultBuilder crb, SPARCMacroAssembler masm, Register constantTableBase, Constant input, Register dest, SPARCDelayedControlTransfer delaySlotInstruction) { SPARCAddress address; ScratchRegister scratch = null; try { + Data data = crb.createDataItem(input); + int size = data.getSize(); if (masm.isImmediateConstantLoad()) { address = new SPARCAddress(constantTableBase, 0); // Make delayed only, when using immediate constant load. delaySlotInstruction.emitControlTransfer(crb, masm); - crb.recordDataReferenceInCode(input, byteCount); + crb.recordDataReferenceInCode(data, size); } else { scratch = masm.getScratchRegister(); Register sr = scratch.getRegister(); - crb.recordDataReferenceInCode(input, byteCount); + crb.recordDataReferenceInCode(data, size); masm.sethix(0, sr, true); address = new SPARCAddress(sr, 0); } - masm.ld(address, dest, byteCount, false); + masm.ld(address, dest, size, false); + return size; } finally { if (scratch != null) { scratch.close(); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/LIRValueUtil.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/LIRValueUtil.java index f0f9e6e9826..f0ec11125fe 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/LIRValueUtil.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/LIRValueUtil.java @@ -69,6 +69,16 @@ public final class LIRValueUtil { return asConstantValue(value).getJavaConstant(); } + public static boolean isIntConstant(Value value, long expected) { + if (isJavaConstant(value)) { + JavaConstant javaConstant = asJavaConstant(value); + if (javaConstant != null && javaConstant.getJavaKind().isNumericInteger()) { + return javaConstant.asLong() == expected; + } + } + return false; + } + public static boolean isStackSlotValue(Value value) { assert value != null; return value instanceof StackSlot || value instanceof VirtualStackSlot; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/RedundantMoveElimination.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/RedundantMoveElimination.java index 26434f7e526..69fbbf4eebf 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/RedundantMoveElimination.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/RedundantMoveElimination.java @@ -30,6 +30,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; +import jdk.vm.ci.code.RegisterConfig; import org.graalvm.compiler.core.common.LIRKind; import org.graalvm.compiler.core.common.cfg.AbstractBlockBase; import org.graalvm.compiler.debug.CounterKey; @@ -138,8 +139,8 @@ public final class RedundantMoveElimination extends PostAllocationOptimizationPh private void doOptimize(LIR lir) { DebugContext debug = lir.getDebug(); try (Indent indent = debug.logAndIndent("eliminate redundant moves")) { - - callerSaveRegs = frameMap.getRegisterConfig().getCallerSaveRegisters(); + RegisterConfig registerConfig = frameMap.getRegisterConfig(); + callerSaveRegs = registerConfig.getCallerSaveRegisters(); initBlockData(lir); @@ -147,7 +148,7 @@ public final class RedundantMoveElimination extends PostAllocationOptimizationPh // Unallocatable registers should never be optimized. eligibleRegs = new int[numRegs]; Arrays.fill(eligibleRegs, -1); - for (Register reg : frameMap.getRegisterConfig().getAllocatableRegisters()) { + for (Register reg : registerConfig.getAllocatableRegisters()) { if (reg.number < numRegs) { eligibleRegs[reg.number] = reg.number; } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/SaveCalleeSaveRegisters.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/SaveCalleeSaveRegisters.java index 976b618f8d9..cd0a15dff48 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/SaveCalleeSaveRegisters.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/SaveCalleeSaveRegisters.java @@ -31,7 +31,6 @@ import org.graalvm.compiler.lir.LIRInsertionBuffer; import org.graalvm.compiler.lir.LIRInstruction; import org.graalvm.compiler.lir.StandardOp; import org.graalvm.compiler.lir.Variable; -import org.graalvm.compiler.lir.framemap.FrameMapBuilder; import org.graalvm.compiler.lir.gen.LIRGenerationResult; import org.graalvm.compiler.lir.gen.LIRGeneratorTool; import org.graalvm.compiler.lir.phases.PreAllocationOptimizationPhase; @@ -48,8 +47,7 @@ public class SaveCalleeSaveRegisters extends PreAllocationOptimizationPhase { @Override protected void run(TargetDescription target, LIRGenerationResult lirGenRes, PreAllocationOptimizationContext context) { - FrameMapBuilder frameMapBuilder = lirGenRes.getFrameMapBuilder(); - RegisterArray calleeSaveRegisters = frameMapBuilder.getCodeCache().getRegisterConfig().getCalleeSaveRegisters(); + RegisterArray calleeSaveRegisters = lirGenRes.getRegisterConfig().getCalleeSaveRegisters(); if (calleeSaveRegisters == null || calleeSaveRegisters.size() == 0) { return; } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/asm/CompilationResultBuilder.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/asm/CompilationResultBuilder.java index 36dd827fc3b..fec7aa0466a 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/asm/CompilationResultBuilder.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/asm/CompilationResultBuilder.java @@ -80,10 +80,10 @@ import jdk.vm.ci.meta.Value; */ public class CompilationResultBuilder { - // @formatter:off - @Option(help = "Include the LIR as comments with the final assembly.", type = OptionType.Debug) - public static final OptionKey PrintLIRWithAssembly = new OptionKey<>(false); - // @formatter:on + public static class Options { + @Option(help = "Include the LIR as comments with the final assembly.", type = OptionType.Debug) // + public static final OptionKey PrintLIRWithAssembly = new OptionKey<>(false); + } private static class ExceptionInfo { @@ -295,13 +295,24 @@ public class CompilationResultBuilder { public AbstractAddress recordDataReferenceInCode(Constant constant, int alignment) { assert constant != null; debug.log("Constant reference in code: pos = %d, data = %s", asm.position(), constant); + Data data = createDataItem(constant); + data.updateAlignment(alignment); + return recordDataSectionReference(data); + } + + public AbstractAddress recordDataReferenceInCode(Data data, int alignment) { + assert data != null; + data.updateAlignment(alignment); + return recordDataSectionReference(data); + } + + public Data createDataItem(Constant constant) { Data data = dataCache.get(constant); if (data == null) { data = dataBuilder.createDataItem(constant); dataCache.put(constant, data); } - data.updateAlignment(alignment); - return recordDataSectionReference(data); + return data; } public AbstractAddress recordDataReferenceInCode(byte[] data, int alignment) { @@ -472,7 +483,7 @@ public class CompilationResultBuilder { if (block == null) { return; } - boolean emitComment = debug.isDumpEnabled(DebugContext.BASIC_LEVEL) || PrintLIRWithAssembly.getValue(getOptions()); + boolean emitComment = debug.isDumpEnabled(DebugContext.BASIC_LEVEL) || Options.PrintLIRWithAssembly.getValue(getOptions()); if (emitComment) { blockComment(String.format("block B%d %s", block.getId(), block.getLoop())); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/gen/ArithmeticLIRGenerator.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/gen/ArithmeticLIRGenerator.java index 4a80de0705c..28df230967b 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/gen/ArithmeticLIRGenerator.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/gen/ArithmeticLIRGenerator.java @@ -50,8 +50,19 @@ public abstract class ArithmeticLIRGenerator implements ArithmeticLIRGeneratorTo protected abstract Variable emitAdd(LIRKind resultKind, Value a, Value b, boolean setFlags); + protected abstract Variable emitSub(LIRKind resultKind, Value a, Value b, boolean setFlags); + @Override public final Variable emitAdd(Value aVal, Value bVal, boolean setFlags) { + return emitAddOrSub(aVal, bVal, setFlags, true); + } + + @Override + public final Variable emitSub(Value aVal, Value bVal, boolean setFlags) { + return emitAddOrSub(aVal, bVal, setFlags, false); + } + + private Variable emitAddOrSub(Value aVal, Value bVal, boolean setFlags, boolean isAdd) { LIRKind resultKind; Value a = aVal; Value b = bVal; @@ -90,47 +101,7 @@ public abstract class ArithmeticLIRGenerator implements ArithmeticLIRGeneratorTo resultKind = LIRKind.combine(a, b); } - return emitAdd(resultKind, a, b, setFlags); + return isAdd ? emitAdd(resultKind, a, b, setFlags) : emitSub(resultKind, a, b, setFlags); } - protected abstract Variable emitSub(LIRKind resultKind, Value a, Value b, boolean setFlags); - - @Override - public final Variable emitSub(Value aVal, Value bVal, boolean setFlags) { - LIRKind resultKind; - Value a = aVal; - Value b = bVal; - - if (isNumericInteger(a.getPlatformKind())) { - LIRKind aKind = a.getValueKind(LIRKind.class); - LIRKind bKind = b.getValueKind(LIRKind.class); - assert a.getPlatformKind() == b.getPlatformKind(); - - if (aKind.isUnknownReference()) { - resultKind = aKind; - } else if (bKind.isUnknownReference()) { - resultKind = bKind; - } - - if (aKind.isValue() && bKind.isValue()) { - resultKind = aKind; - } else if (bKind.isValue()) { - if (aKind.isDerivedReference()) { - resultKind = aKind; - } else { - AllocatableValue allocatable = getLIRGen().asAllocatable(a); - resultKind = aKind.makeDerivedReference(allocatable); - a = allocatable; - } - } else if (aKind.isDerivedReference() && bKind.isDerivedReference() && aKind.getDerivedReferenceBase().equals(bKind.getDerivedReferenceBase())) { - resultKind = LIRKind.value(a.getPlatformKind()); - } else { - resultKind = aKind.makeUnknownReference(); - } - } else { - resultKind = LIRKind.combine(a, b); - } - - return emitSub(resultKind, a, b, setFlags); - } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/gen/LIRGenerationResult.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/gen/LIRGenerationResult.java index e4eb673740c..7d6922adaab 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/gen/LIRGenerationResult.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/gen/LIRGenerationResult.java @@ -22,6 +22,7 @@ */ package org.graalvm.compiler.lir.gen; +import jdk.vm.ci.code.RegisterConfig; import org.graalvm.compiler.core.common.CompilationIdentifier; import org.graalvm.compiler.core.common.CompilationIdentifier.Verbosity; import org.graalvm.compiler.debug.DebugContext; @@ -123,6 +124,10 @@ public class LIRGenerationResult { return frameMap; } + public final RegisterConfig getRegisterConfig() { + return frameMapBuilder.getRegisterConfig(); + } + public LIR getLIR() { return lir; } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/gen/LIRGenerator.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/gen/LIRGenerator.java index 0c92168d8fe..383927856ee 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/gen/LIRGenerator.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/gen/LIRGenerator.java @@ -34,6 +34,7 @@ import static org.graalvm.compiler.lir.LIRValueUtil.isVirtualStackSlot; import java.util.ArrayList; import java.util.List; +import jdk.vm.ci.code.RegisterConfig; import org.graalvm.compiler.asm.Label; import org.graalvm.compiler.core.common.LIRKind; import org.graalvm.compiler.core.common.calc.Condition; @@ -200,9 +201,14 @@ public abstract class LIRGenerator implements LIRGeneratorTool { return new Variable(valueKind, ((VariableProvider) res.getLIR()).nextVariable()); } + @Override + public RegisterConfig getRegisterConfig() { + return res.getRegisterConfig(); + } + @Override public RegisterAttributes attributes(Register register) { - return res.getFrameMapBuilder().getRegisterConfig().getAttributesMap()[register.number]; + return getRegisterConfig().getAttributesMap()[register.number]; } @Override @@ -228,7 +234,7 @@ public abstract class LIRGenerator implements LIRGeneratorTool { if (moveFactory.canInlineConstant(constant)) { return new ConstantValue(toRegisterKind(kind), constant); } else { - return emitLoadConstant(kind, constant); + return emitLoadConstant(toRegisterKind(kind), constant); } } @@ -289,7 +295,7 @@ public abstract class LIRGenerator implements LIRGeneratorTool { */ @Override public AllocatableValue resultOperandFor(JavaKind javaKind, ValueKind valueKind) { - Register reg = res.getFrameMapBuilder().getRegisterConfig().getReturnRegister(javaKind); + Register reg = getRegisterConfig().getReturnRegister(javaKind); assert target().arch.canStoreValue(reg.getRegisterCategory(), valueKind.getPlatformKind()) : reg.getRegisterCategory() + " " + valueKind.getPlatformKind(); return reg.asValue(valueKind); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/gen/LIRGeneratorTool.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/gen/LIRGeneratorTool.java index 6533ba63180..c82c7e7ec33 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/gen/LIRGeneratorTool.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/gen/LIRGeneratorTool.java @@ -22,6 +22,7 @@ */ package org.graalvm.compiler.lir.gen; +import jdk.vm.ci.code.RegisterConfig; import org.graalvm.compiler.core.common.CompressEncoding; import org.graalvm.compiler.core.common.LIRKind; import org.graalvm.compiler.core.common.calc.Condition; @@ -109,6 +110,8 @@ public interface LIRGeneratorTool extends DiagnosticLIRGeneratorTool, ValueKindF LIRGenerationResult getResult(); + RegisterConfig getRegisterConfig(); + boolean hasBlockEnd(AbstractBlockBase block); MoveFactory getMoveFactory(); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.test/src/org/graalvm/compiler/loop/test/LoopPartialUnrollTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.test/src/org/graalvm/compiler/loop/test/LoopPartialUnrollTest.java index 8a93d77bf93..4b411ca7aa3 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.test/src/org/graalvm/compiler/loop/test/LoopPartialUnrollTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.test/src/org/graalvm/compiler/loop/test/LoopPartialUnrollTest.java @@ -214,7 +214,7 @@ public class LoopPartialUnrollTest extends GraalCompilerTest { } }; ResolvedJavaMethod method = getResolvedJavaMethod(name); - OptionValues options = new OptionValues(getInitialOptions(), DefaultLoopPolicies.UnrollMaxIterations, 2); + OptionValues options = new OptionValues(getInitialOptions(), DefaultLoopPolicies.Options.UnrollMaxIterations, 2); StructuredGraph graph = parse(builder(method, StructuredGraph.AllowAssumptions.YES, id, options), getEagerGraphBuilderSuite()); try (DebugContext.Scope buildScope = graph.getDebug().scope(name, method, graph)) { MidTierContext context = new MidTierContext(getProviders(), getTargetProvider(), OptimisticOptimizations.ALL, null); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/CountedLoopInfo.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/CountedLoopInfo.java index 4e4c12b7e64..ae6bb412278 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/CountedLoopInfo.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/CountedLoopInfo.java @@ -83,7 +83,11 @@ public class CountedLoopInfo { range = add(graph, range, oneDirection); } // round-away-from-zero divison: (range + stride -/+ 1) / stride - ValueNode denominator = add(graph, sub(graph, range, oneDirection), iv.strideNode()); + ValueNode denominator = range; + if (!oneDirection.stamp().equals(iv.strideNode().stamp())) { + ValueNode subedRanged = sub(graph, range, oneDirection); + denominator = add(graph, subedRanged, iv.strideNode()); + } ValueNode div = divBefore(graph, loop.entryPoint(), denominator, iv.strideNode()); if (assumePositive) { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/DefaultLoopPolicies.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/DefaultLoopPolicies.java index e25c955027b..ada82be03ef 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/DefaultLoopPolicies.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/DefaultLoopPolicies.java @@ -55,16 +55,19 @@ import org.graalvm.compiler.options.OptionValues; import jdk.vm.ci.meta.MetaAccessProvider; public class DefaultLoopPolicies implements LoopPolicies { - @Option(help = "", type = OptionType.Expert) public static final OptionKey LoopUnswitchMaxIncrease = new OptionKey<>(500); - @Option(help = "", type = OptionType.Expert) public static final OptionKey LoopUnswitchTrivial = new OptionKey<>(10); - @Option(help = "", type = OptionType.Expert) public static final OptionKey LoopUnswitchFrequencyBoost = new OptionKey<>(10.0); - @Option(help = "", type = OptionType.Expert) public static final OptionKey FullUnrollMaxNodes = new OptionKey<>(300); - @Option(help = "", type = OptionType.Expert) public static final OptionKey FullUnrollMaxIterations = new OptionKey<>(600); - @Option(help = "", type = OptionType.Expert) public static final OptionKey ExactFullUnrollMaxNodes = new OptionKey<>(1200); - @Option(help = "", type = OptionType.Expert) public static final OptionKey ExactPartialUnrollMaxNodes = new OptionKey<>(200); + public static class Options { + @Option(help = "", type = OptionType.Expert) public static final OptionKey LoopUnswitchMaxIncrease = new OptionKey<>(500); + @Option(help = "", type = OptionType.Expert) public static final OptionKey LoopUnswitchTrivial = new OptionKey<>(10); + @Option(help = "", type = OptionType.Expert) public static final OptionKey LoopUnswitchFrequencyBoost = new OptionKey<>(10.0); - @Option(help = "", type = OptionType.Expert) public static final OptionKey UnrollMaxIterations = new OptionKey<>(16); + @Option(help = "", type = OptionType.Expert) public static final OptionKey FullUnrollMaxNodes = new OptionKey<>(300); + @Option(help = "", type = OptionType.Expert) public static final OptionKey FullUnrollMaxIterations = new OptionKey<>(600); + @Option(help = "", type = OptionType.Expert) public static final OptionKey ExactFullUnrollMaxNodes = new OptionKey<>(1200); + @Option(help = "", type = OptionType.Expert) public static final OptionKey ExactPartialUnrollMaxNodes = new OptionKey<>(200); + + @Option(help = "", type = OptionType.Expert) public static final OptionKey UnrollMaxIterations = new OptionKey<>(16); + } @Override public boolean shouldPeel(LoopEx loop, ControlFlowGraph cfg, MetaAccessProvider metaAccess) { @@ -87,10 +90,10 @@ public class DefaultLoopPolicies implements LoopPolicies { OptionValues options = loop.entryPoint().getOptions(); CountedLoopInfo counted = loop.counted(); long maxTrips = counted.constantMaxTripCount(); - int maxNodes = (counted.isExactTripCount() && counted.isConstantExactTripCount()) ? ExactFullUnrollMaxNodes.getValue(options) : FullUnrollMaxNodes.getValue(options); + int maxNodes = (counted.isExactTripCount() && counted.isConstantExactTripCount()) ? Options.ExactFullUnrollMaxNodes.getValue(options) : Options.FullUnrollMaxNodes.getValue(options); maxNodes = Math.min(maxNodes, Math.max(0, MaximumDesiredSize.getValue(options) - loop.loopBegin().graph().getNodeCount())); int size = Math.max(1, loop.size() - 1 - loop.loopBegin().phis().count()); - if (maxTrips <= FullUnrollMaxIterations.getValue(options) && size * (maxTrips - 1) <= maxNodes) { + if (maxTrips <= Options.FullUnrollMaxIterations.getValue(options) && size * (maxTrips - 1) <= maxNodes) { // check whether we're allowed to unroll this loop return loop.canDuplicateLoop(); } else { @@ -106,7 +109,7 @@ public class DefaultLoopPolicies implements LoopPolicies { return false; } OptionValues options = loop.entryPoint().getOptions(); - int maxNodes = ExactPartialUnrollMaxNodes.getValue(options); + int maxNodes = Options.ExactPartialUnrollMaxNodes.getValue(options); maxNodes = Math.min(maxNodes, Math.max(0, MaximumDesiredSize.getValue(options) - loop.loopBegin().graph().getNodeCount())); int size = Math.max(1, loop.size() - 1 - loop.loopBegin().phis().count()); int unrollFactor = loopBegin.getUnrollFactor(); @@ -118,7 +121,7 @@ public class DefaultLoopPolicies implements LoopPolicies { } loopBegin.setLoopOrigFrequency(loopFrequency); } - int maxUnroll = UnrollMaxIterations.getValue(options); + int maxUnroll = Options.UnrollMaxIterations.getValue(options); // Now correct size for the next unroll. UnrollMaxIterations == 1 means perform the // pre/main/post transformation but don't actually unroll the main loop. size += size; @@ -190,9 +193,9 @@ public class DefaultLoopPolicies implements LoopPolicies { CountingClosure stateNodesCount = new CountingClosure(); double loopFrequency = loop.loopBegin().loopFrequency(); OptionValues options = loop.loopBegin().getOptions(); - int maxDiff = LoopUnswitchTrivial.getValue(options) + (int) (LoopUnswitchFrequencyBoost.getValue(options) * (loopFrequency - 1.0 + phis)); + int maxDiff = Options.LoopUnswitchTrivial.getValue(options) + (int) (Options.LoopUnswitchFrequencyBoost.getValue(options) * (loopFrequency - 1.0 + phis)); - maxDiff = Math.min(maxDiff, LoopUnswitchMaxIncrease.getValue(options)); + maxDiff = Math.min(maxDiff, Options.LoopUnswitchMaxIncrease.getValue(options)); int remainingGraphSpace = MaximumDesiredSize.getValue(options) - graph.getNodeCount(); maxDiff = Math.min(maxDiff, remainingGraphSpace); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodeinfo/src/org/graalvm/compiler/nodeinfo/NodeCycles.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodeinfo/src/org/graalvm/compiler/nodeinfo/NodeCycles.java index 13857f3f4e3..34057c7631e 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodeinfo/src/org/graalvm/compiler/nodeinfo/NodeCycles.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodeinfo/src/org/graalvm/compiler/nodeinfo/NodeCycles.java @@ -68,6 +68,10 @@ public enum NodeCycles { this.value = value; } + public boolean isValueKnown() { + return this != NodeCycles.CYCLES_UNKNOWN && this != NodeCycles.CYCLES_UNSET; + } + public static final int IGNORE_CYCLES_CONTRACT_FACTOR = 0xFFFF; public static NodeCycles compute(NodeCycles base, int opCount) { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/IntegerStampTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/IntegerStampTest.java index bf22e803e0e..04832ecd1a3 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/IntegerStampTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/IntegerStampTest.java @@ -552,4 +552,21 @@ public class IntegerStampTest extends GraphTest { return result.longValue(); } } + + @Test + public void testDiv() { + testDiv(32, Integer.MIN_VALUE, Integer.MAX_VALUE); + testDiv(64, Long.MIN_VALUE, Long.MAX_VALUE); + } + + private static void testDiv(int bits, long min, long max) { + BinaryOp div = IntegerStamp.OPS.getDiv(); + assertEquals(IntegerStamp.create(bits, -50, 50), div.foldStamp(IntegerStamp.create(bits, -100, 100), IntegerStamp.create(bits, 2, 5))); + assertEquals(IntegerStamp.create(bits, 20, 500), div.foldStamp(IntegerStamp.create(bits, 100, 1000), IntegerStamp.create(bits, 2, 5))); + assertEquals(IntegerStamp.create(bits, -500, -20), div.foldStamp(IntegerStamp.create(bits, -1000, -100), IntegerStamp.create(bits, 2, 5))); + assertEquals(IntegerStamp.create(bits, min, max), div.foldStamp(IntegerStamp.create(bits, min, max), IntegerStamp.create(bits, 1, max))); + assertEquals(IntegerStamp.create(bits, -100, 100), div.foldStamp(IntegerStamp.create(bits, -100, 100), IntegerStamp.create(bits, 1, max))); + assertEquals(IntegerStamp.create(bits, 0, 1000), div.foldStamp(IntegerStamp.create(bits, 100, 1000), IntegerStamp.create(bits, 1, max))); + assertEquals(IntegerStamp.create(bits, -1000, 0), div.foldStamp(IntegerStamp.create(bits, -1000, -100), IntegerStamp.create(bits, 1, max))); + } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/PrimitiveStampBoundaryTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/PrimitiveStampBoundaryTest.java new file mode 100644 index 00000000000..c8ed8a5816d --- /dev/null +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/PrimitiveStampBoundaryTest.java @@ -0,0 +1,326 @@ +/* + * 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 + * 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 org.graalvm.compiler.nodes.test; + +import java.util.EnumSet; +import java.util.HashSet; + +import org.graalvm.compiler.core.common.calc.FloatConvert; +import org.graalvm.compiler.core.common.calc.FloatConvertCategory; +import org.graalvm.compiler.core.common.type.ArithmeticOpTable; +import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp; +import org.graalvm.compiler.core.common.type.ArithmeticOpTable.IntegerConvertOp; +import org.graalvm.compiler.core.common.type.ArithmeticOpTable.ShiftOp; +import org.graalvm.compiler.core.common.type.FloatStamp; +import org.graalvm.compiler.core.common.type.IntegerStamp; +import org.graalvm.compiler.core.common.type.PrimitiveStamp; +import org.graalvm.compiler.core.common.type.Stamp; +import org.graalvm.compiler.test.GraalTest; +import org.junit.Test; + +import jdk.vm.ci.meta.Constant; +import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.JavaKind; + +/** + * Exercise the various stamp folding operations by generating ranges from a set of boundary values + * and then ensuring that the values that produced those ranges are in the resulting stamp. + */ +public class PrimitiveStampBoundaryTest extends GraalTest { + + static long[] longBoundaryValues = {Long.MIN_VALUE, Long.MIN_VALUE + 1, Integer.MIN_VALUE, Integer.MIN_VALUE + 1, -1, 0, 1, Integer.MAX_VALUE - 1, Integer.MAX_VALUE, Long.MAX_VALUE - 1, + Long.MAX_VALUE}; + + static int[] shiftBoundaryValues = {-128, -1, 0, 1, 4, 8, 16, 31, 63, 128}; + + static HashSet shiftStamps; + static HashSet integerTestStamps; + static HashSet floatTestStamps; + + static { + shiftStamps = new HashSet<>(); + for (long v1 : shiftBoundaryValues) { + for (long v2 : shiftBoundaryValues) { + shiftStamps.add(IntegerStamp.create(32, Math.min(v1, v2), Math.max(v1, v2))); + } + } + + integerTestStamps = new HashSet<>(); + for (long v1 : longBoundaryValues) { + for (long v2 : longBoundaryValues) { + if (v2 == (int) v2 && v1 == (int) v1) { + integerTestStamps.add(IntegerStamp.create(32, Math.min(v1, v2), Math.max(v1, v2))); + } + integerTestStamps.add(IntegerStamp.create(64, Math.min(v1, v2), Math.max(v1, v2))); + } + } + } + + static double[] doubleBoundaryValues = {Double.NEGATIVE_INFINITY, Double.MIN_VALUE, Float.NEGATIVE_INFINITY, Float.MIN_VALUE, + Long.MIN_VALUE, Long.MIN_VALUE + 1, Integer.MIN_VALUE, Integer.MIN_VALUE + 1, -1, 0, 1, + Integer.MAX_VALUE - 1, Integer.MAX_VALUE, Long.MAX_VALUE - 1, Long.MAX_VALUE, + Float.MAX_VALUE, Float.POSITIVE_INFINITY, Double.MAX_VALUE, Double.POSITIVE_INFINITY}; + + static double[] doubleSpecialValues = {Double.NaN, -0.0, -0.0F, Float.NaN}; + + static { + floatTestStamps = new HashSet<>(); + + for (double d1 : doubleBoundaryValues) { + for (double d2 : doubleBoundaryValues) { + float f1 = (float) d2; + float f2 = (float) d1; + if (d2 == f1 && d1 == f2) { + generateFloatingStamps(new FloatStamp(32, Math.min(f2, f1), Math.max(f2, f1), true)); + generateFloatingStamps(new FloatStamp(32, Math.min(f2, f1), Math.max(f2, f1), false)); + } + generateFloatingStamps(new FloatStamp(64, Math.min(d1, d2), Math.max(d1, d2), true)); + generateFloatingStamps(new FloatStamp(64, Math.min(d1, d2), Math.max(d1, d2), false)); + } + } + } + + private static void generateFloatingStamps(FloatStamp floatStamp) { + floatTestStamps.add(floatStamp); + for (double d : doubleSpecialValues) { + FloatStamp newStamp = (FloatStamp) floatStamp.meet(floatStampForConstant(d, floatStamp.getBits())); + if (!newStamp.isUnrestricted()) { + floatTestStamps.add(newStamp); + } + } + } + + @Test + public void testConvertBoundaryValues() { + testConvertBoundaryValues(IntegerStamp.OPS.getSignExtend(), 32, 64, integerTestStamps); + testConvertBoundaryValues(IntegerStamp.OPS.getZeroExtend(), 32, 64, integerTestStamps); + testConvertBoundaryValues(IntegerStamp.OPS.getNarrow(), 64, 32, integerTestStamps); + } + + private static void testConvertBoundaryValues(IntegerConvertOp op, int inputBits, int resultBits, HashSet stamps) { + for (PrimitiveStamp stamp : stamps) { + if (inputBits == stamp.getBits()) { + Stamp lower = boundaryStamp(stamp, false); + Stamp upper = boundaryStamp(stamp, true); + checkConvertOperation(op, inputBits, resultBits, op.foldStamp(inputBits, resultBits, stamp), lower); + checkConvertOperation(op, inputBits, resultBits, op.foldStamp(inputBits, resultBits, stamp), upper); + } + } + } + + private static void checkConvertOperation(IntegerConvertOp op, int inputBits, int resultBits, Stamp result, Stamp v1stamp) { + Stamp folded = op.foldStamp(inputBits, resultBits, v1stamp); + assertTrue(folded.asConstant() != null, "should constant fold %s %s %s", op, v1stamp, folded); + assertTrue(result.meet(folded).equals(result), "result out of range %s %s %s %s %s", op, v1stamp, folded, result, result.meet(folded)); + } + + @Test + public void testFloatConvertBoundaryValues() { + for (FloatConvert op : EnumSet.allOf(FloatConvert.class)) { + ArithmeticOpTable.FloatConvertOp floatConvert = IntegerStamp.OPS.getFloatConvert(op); + if (floatConvert == null) { + continue; + } + assert op.getCategory() == FloatConvertCategory.IntegerToFloatingPoint : op; + testConvertBoundaryValues(floatConvert, op.getInputBits(), integerTestStamps); + } + for (FloatConvert op : EnumSet.allOf(FloatConvert.class)) { + ArithmeticOpTable.FloatConvertOp floatConvert = FloatStamp.OPS.getFloatConvert(op); + if (floatConvert == null) { + continue; + } + assert op.getCategory() == FloatConvertCategory.FloatingPointToInteger || op.getCategory() == FloatConvertCategory.FloatingPointToFloatingPoint : op; + testConvertBoundaryValues(floatConvert, op.getInputBits(), floatTestStamps); + } + } + + private static void testConvertBoundaryValues(ArithmeticOpTable.FloatConvertOp op, int bits, HashSet stamps) { + for (PrimitiveStamp stamp : stamps) { + if (bits == stamp.getBits()) { + Stamp lower = boundaryStamp(stamp, false); + Stamp upper = boundaryStamp(stamp, true); + checkConvertOperation(op, op.foldStamp(stamp), lower); + checkConvertOperation(op, op.foldStamp(stamp), upper); + } + } + } + + private static void checkConvertOperation(ArithmeticOpTable.FloatConvertOp op, Stamp result, Stamp v1stamp) { + Stamp folded = op.foldStamp(v1stamp); + assertTrue(folded.asConstant() != null, "should constant fold %s %s %s", op, v1stamp, folded); + assertTrue(result.meet(folded).equals(result), "result out of range %s %s %s %s %s", op, v1stamp, folded, result, result.meet(folded)); + } + + @Test + public void testShiftBoundaryValues() { + for (ShiftOp op : IntegerStamp.OPS.getShiftOps()) { + testShiftBoundaryValues(op, integerTestStamps, shiftStamps); + } + } + + private static void testShiftBoundaryValues(ShiftOp shiftOp, HashSet stamps, HashSet shifts) { + for (PrimitiveStamp testStamp : stamps) { + if (testStamp instanceof IntegerStamp) { + IntegerStamp stamp = (IntegerStamp) testStamp; + for (IntegerStamp shiftStamp : shifts) { + IntegerStamp foldedStamp = (IntegerStamp) shiftOp.foldStamp(stamp, shiftStamp); + checkShiftOperation(stamp.getBits(), shiftOp, foldedStamp, stamp.lowerBound(), shiftStamp.lowerBound()); + checkShiftOperation(stamp.getBits(), shiftOp, foldedStamp, stamp.lowerBound(), shiftStamp.upperBound()); + checkShiftOperation(stamp.getBits(), shiftOp, foldedStamp, stamp.upperBound(), shiftStamp.lowerBound()); + checkShiftOperation(stamp.getBits(), shiftOp, foldedStamp, stamp.upperBound(), shiftStamp.upperBound()); + } + } + } + } + + private static void checkShiftOperation(int bits, ShiftOp op, IntegerStamp result, long v1, long v2) { + IntegerStamp v1stamp = IntegerStamp.create(bits, v1, v1); + IntegerStamp v2stamp = IntegerStamp.create(32, v2, v2); + IntegerStamp folded = (IntegerStamp) op.foldStamp(v1stamp, v2stamp); + Constant constant = op.foldConstant(JavaConstant.forPrimitiveInt(bits, v1), (int) v2); + assertTrue(constant != null); + assertTrue(folded.asConstant() != null, "should constant fold %s %s %s %s", op, v1stamp, v2stamp, folded); + assertTrue(result.meet(folded).equals(result), "result out of range %s %s %s %s %s %s", op, v1stamp, v2stamp, folded, result, result.meet(folded)); + } + + private static void checkBinaryOperation(ArithmeticOpTable.BinaryOp op, Stamp result, Stamp v1stamp, Stamp v2stamp) { + Stamp folded = op.foldStamp(v1stamp, v2stamp); + Constant constant = op.foldConstant(v1stamp.asConstant(), v2stamp.asConstant()); + if (constant != null) { + Constant constant2 = folded.asConstant(); + if (constant2 == null && v1stamp instanceof FloatStamp) { + JavaConstant c = (JavaConstant) constant; + assertTrue((c.getJavaKind() == JavaKind.Double && Double.isNaN(c.asDouble())) || + (c.getJavaKind() == JavaKind.Float && Float.isNaN(c.asFloat()))); + } else { + assertTrue(constant2 != null, "should constant fold %s %s %s %s", op, v1stamp, v2stamp, folded); + if (!constant.equals(constant2)) { + op.foldConstant(v1stamp.asConstant(), v2stamp.asConstant()); + op.foldStamp(v1stamp, v2stamp); + } + assertTrue(constant.equals(constant2), "should produce same constant %s %s %s %s %s", op, v1stamp, v2stamp, constant, constant2); + } + assertTrue(result.meet(folded).equals(result), "result out of range %s %s %s %s %s %s", op, v1stamp, v2stamp, folded, result, result.meet(folded)); + } + } + + @Test + public void testBinaryBoundaryValues() { + for (BinaryOp op : IntegerStamp.OPS.getBinaryOps()) { + if (op != null) { + testBinaryBoundaryValues(op, integerTestStamps); + } + } + for (BinaryOp op : FloatStamp.OPS.getBinaryOps()) { + if (op != null) { + testBinaryBoundaryValues(op, floatTestStamps); + } + } + } + + private static Stamp boundaryStamp(Stamp v1, boolean upper) { + if (v1 instanceof IntegerStamp) { + IntegerStamp istamp = (IntegerStamp) v1; + long bound = upper ? istamp.upperBound() : istamp.lowerBound(); + return IntegerStamp.create(istamp.getBits(), bound, bound); + } else if (v1 instanceof FloatStamp) { + FloatStamp floatStamp = (FloatStamp) v1; + double bound = upper ? floatStamp.upperBound() : floatStamp.lowerBound(); + int bits = floatStamp.getBits(); + return floatStampForConstant(bound, bits); + } else { + throw new InternalError("unexpected stamp type " + v1); + } + } + + private static FloatStamp floatStampForConstant(double bound, int bits) { + if (bits == 32) { + float fbound = (float) bound; + return new FloatStamp(bits, fbound, fbound, !Float.isNaN(fbound)); + } else { + return new FloatStamp(bits, bound, bound, !Double.isNaN(bound)); + } + } + + private static void testBinaryBoundaryValues(ArithmeticOpTable.BinaryOp op, HashSet stamps) { + for (PrimitiveStamp v1 : stamps) { + for (PrimitiveStamp v2 : stamps) { + if (v1.getBits() == v2.getBits() && v1.getClass() == v2.getClass()) { + Stamp result = op.foldStamp(v1, v2); + Stamp v1lower = boundaryStamp(v1, false); + Stamp v1upper = boundaryStamp(v1, true); + Stamp v2lower = boundaryStamp(v2, false); + Stamp v2upper = boundaryStamp(v2, true); + checkBinaryOperation(op, result, v1lower, v2lower); + checkBinaryOperation(op, result, v1lower, v2upper); + checkBinaryOperation(op, result, v1upper, v2lower); + checkBinaryOperation(op, result, v1upper, v2upper); + } + } + } + } + + @Test + public void testUnaryBoundaryValues() { + for (ArithmeticOpTable.UnaryOp op : IntegerStamp.OPS.getUnaryOps()) { + if (op != null) { + testUnaryBoundaryValues(op, integerTestStamps); + } + } + for (ArithmeticOpTable.UnaryOp op : FloatStamp.OPS.getUnaryOps()) { + if (op != null) { + testUnaryBoundaryValues(op, floatTestStamps); + } + } + } + + private static void testUnaryBoundaryValues(ArithmeticOpTable.UnaryOp op, HashSet stamps) { + for (PrimitiveStamp v1 : stamps) { + Stamp result = op.foldStamp(v1); + checkUnaryOperation(op, result, boundaryStamp(v1, false)); + checkUnaryOperation(op, result, boundaryStamp(v1, true)); + } + } + + private static void checkUnaryOperation(ArithmeticOpTable.UnaryOp op, Stamp result, Stamp v1stamp) { + Stamp folded = op.foldStamp(v1stamp); + Constant v1constant = v1stamp.asConstant(); + if (v1constant != null) { + Constant constant = op.foldConstant(v1constant); + if (constant != null) { + Constant constant2 = folded.asConstant(); + if (constant2 == null && v1stamp instanceof FloatStamp) { + JavaConstant c = (JavaConstant) constant; + assertTrue((c.getJavaKind() == JavaKind.Double && Double.isNaN(c.asDouble())) || + (c.getJavaKind() == JavaKind.Float && Float.isNaN(c.asFloat()))); + } else { + assertTrue(constant2 != null, "should constant fold %s %s %s", op, v1stamp, folded); + assertTrue(constant.equals(constant2), "should produce same constant %s %s %s %s", op, v1stamp, constant, constant2); + } + } + } else { + assert v1stamp instanceof FloatStamp; + } + assertTrue(result.meet(folded).equals(result), "result out of range %s %s %s %s %s", op, v1stamp, folded, result, result.meet(folded)); + } +} diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/CompressionNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/CompressionNode.java index f5fb7981032..6f9a420af59 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/CompressionNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/CompressionNode.java @@ -122,9 +122,9 @@ public abstract class CompressionNode extends UnaryNode implements ConvertNode, // We always want uncompressed constants return this; } - int stableDimension = ((ConstantNode) forValue).getStableDimension(); - boolean isDefaultStable = ((ConstantNode) forValue).isDefaultStable(); - return ConstantNode.forConstant(stamp(), convert(forValue.asConstant(), tool.getConstantReflection()), stableDimension, isDefaultStable, tool.getMetaAccess()); + + ConstantNode constant = (ConstantNode) forValue; + return ConstantNode.forConstant(stamp(), convert(constant.getValue(), tool.getConstantReflection()), constant.getStableDimension(), constant.isDefaultStable(), tool.getMetaAccess()); } else if (forValue instanceof CompressionNode) { CompressionNode other = (CompressionNode) forValue; if (op != other.op && encoding.equals(other.encoding)) { @@ -136,22 +136,22 @@ public abstract class CompressionNode extends UnaryNode implements ConvertNode, @Override public void generate(NodeLIRBuilderTool gen) { - LIRGeneratorTool hsGen = gen.getLIRGeneratorTool(); boolean nonNull; - if (getValue().stamp() instanceof AbstractObjectStamp) { - nonNull = StampTool.isPointerNonNull(getValue().stamp()); + if (value.stamp() instanceof AbstractObjectStamp) { + nonNull = StampTool.isPointerNonNull(value.stamp()); } else { // metaspace pointers are never null nonNull = true; } + LIRGeneratorTool tool = gen.getLIRGeneratorTool(); Value result; switch (op) { case Compress: - result = hsGen.emitCompress(gen.operand(getValue()), encoding, nonNull); + result = tool.emitCompress(gen.operand(value), encoding, nonNull); break; case Uncompress: - result = hsGen.emitUncompress(gen.operand(getValue()), encoding, nonNull); + result = tool.emitUncompress(gen.operand(value), encoding, nonNull); break; default: throw GraalError.shouldNotReachHere(); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ConstantNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ConstantNode.java index 73c807f7495..4dd797fe32c 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ConstantNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ConstantNode.java @@ -54,7 +54,7 @@ import jdk.vm.ci.meta.PrimitiveConstant; /** * The {@code ConstantNode} represents a {@link Constant constant}. */ -@NodeInfo(nameTemplate = "C({p#rawvalue})", cycles = CYCLES_0, size = SIZE_1) +@NodeInfo(nameTemplate = "C({p#rawvalue}) {p#stampKind}", cycles = CYCLES_0, size = SIZE_1) public final class ConstantNode extends FloatingNode implements LIRLowerable { public static final NodeClass TYPE = NodeClass.create(ConstantNode.class); @@ -518,13 +518,14 @@ public final class ConstantNode extends FloatingNode implements LIRLowerable { public Map getDebugProperties(Map map) { Map properties = super.getDebugProperties(map); properties.put("rawvalue", value.toValueString()); + properties.put("stampKind", stamp.unrestricted().toString()); return properties; } @Override public String toString(Verbosity verbosity) { if (verbosity == Verbosity.Name) { - return super.toString(Verbosity.Name) + "(" + value.toValueString() + ")"; + return super.toString(Verbosity.Name) + "(" + value.toValueString() + ", " + stamp().unrestricted().toString() + ")"; } else { return super.toString(verbosity); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/IfNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/IfNode.java index dba8f7295b2..616b1de0ac1 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/IfNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/IfNode.java @@ -22,7 +22,7 @@ */ package org.graalvm.compiler.nodes; -import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_2; +import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_1; import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_2; import java.util.ArrayList; @@ -74,7 +74,7 @@ import jdk.vm.ci.meta.PrimitiveConstant; * The {@code IfNode} represents a branch that can go one of two directions depending on the outcome * of a comparison. */ -@NodeInfo(cycles = CYCLES_2, size = SIZE_2, sizeRationale = "2 jmps") +@NodeInfo(cycles = CYCLES_1, size = SIZE_2, sizeRationale = "2 jmps") public final class IfNode extends ControlSplitNode implements Simplifiable, LIRLowerable { public static final NodeClass TYPE = NodeClass.create(IfNode.class); @@ -375,7 +375,7 @@ public final class IfNode extends ControlSplitNode implements Simplifiable, LIRL } // Falsify the reference check. - setCondition(graph().addOrUnique(LogicConstantNode.contradiction())); + setCondition(graph().addOrUniqueWithInputs(LogicConstantNode.contradiction())); return true; } @@ -726,10 +726,11 @@ public final class IfNode extends ControlSplitNode implements Simplifiable, LIRL protected void removeThroughFalseBranch(SimplifierTool tool, AbstractMergeNode merge) { AbstractBeginNode trueBegin = trueSuccessor(); + LogicNode conditionNode = condition(); graph().removeSplitPropagate(this, trueBegin); tool.addToWorkList(trueBegin); - if (condition() != null) { - GraphUtil.tryKillUnused(condition()); + if (conditionNode != null) { + GraphUtil.tryKillUnused(conditionNode); } if (merge.isAlive() && merge.forwardEndCount() > 1) { for (FixedNode end : merge.forwardEnds()) { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/PhiNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/PhiNode.java index 8ee5706389f..2d517d17ba5 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/PhiNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/PhiNode.java @@ -132,12 +132,24 @@ public abstract class PhiNode extends FloatingNode implements Canonicalizable { } str.append(valueAt(i) == null ? "-" : valueAt(i).toString(Verbosity.Id)); } + String description = valueDescription(); + if (description.length() > 0) { + str.append(", ").append(description); + } return super.toString(Verbosity.Name) + "(" + str + ")"; } else { return super.toString(verbosity); } } + /** + * String describing the kind of value this Phi merges. Used by {@link #toString(Verbosity)} and + * dumping. + */ + protected String valueDescription() { + return ""; + } + public void addInput(ValueNode x) { assert !(x instanceof ValuePhiNode) || ((ValuePhiNode) x).merge() instanceof LoopBeginNode || ((ValuePhiNode) x).merge() != this.merge(); assert !(this instanceof ValuePhiNode) || x.stamp().isCompatible(stamp()); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/PiNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/PiNode.java index 55a2cd06297..2b0207c53b5 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/PiNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/PiNode.java @@ -59,14 +59,14 @@ import jdk.vm.ci.meta.ResolvedJavaType; * * In contrast to a {@link GuardedValueNode}, a {@link PiNode} is useless as soon as the type of its * input is as narrow or narrower than the {@link PiNode}'s type. The {@link PiNode}, and therefore - * also the scheduling restriction enforced by the anchor, will go away. + * also the scheduling restriction enforced by the guard, will go away. */ @NodeInfo(cycles = CYCLES_0, size = SIZE_0) public class PiNode extends FloatingGuardedNode implements LIRLowerable, Virtualizable, IterableNodeType, Canonicalizable, ValueProxy { public static final NodeClass TYPE = NodeClass.create(PiNode.class); @Input ValueNode object; - protected final Stamp piStamp; + protected Stamp piStamp; public ValueNode object() { return object; @@ -84,12 +84,12 @@ public class PiNode extends FloatingGuardedNode implements LIRLowerable, Virtual this(object, stamp, null); } - public PiNode(ValueNode object, Stamp stamp, ValueNode anchor) { - this(TYPE, object, stamp, (GuardingNode) anchor); + public PiNode(ValueNode object, Stamp stamp, ValueNode guard) { + this(TYPE, object, stamp, (GuardingNode) guard); } - public PiNode(ValueNode object, ValueNode anchor) { - this(object, AbstractPointerStamp.pointerNonNull(object.stamp()), anchor); + public PiNode(ValueNode object, ValueNode guard) { + this(object, AbstractPointerStamp.pointerNonNull(object.stamp()), guard); } public PiNode(ValueNode object, ResolvedJavaType toType, boolean exactType, boolean nonNull) { @@ -104,29 +104,29 @@ public class PiNode extends FloatingGuardedNode implements LIRLowerable, Virtual return new PiNode(object, stamp); } - public static ValueNode create(ValueNode object, Stamp stamp, ValueNode anchor) { - ValueNode value = canonical(object, stamp, (GuardingNode) anchor); + public static ValueNode create(ValueNode object, Stamp stamp, ValueNode guard) { + ValueNode value = canonical(object, stamp, (GuardingNode) guard); if (value != null) { return value; } - return new PiNode(object, stamp, anchor); + return new PiNode(object, stamp, guard); } - public static ValueNode create(ValueNode object, ValueNode anchor) { + public static ValueNode create(ValueNode object, ValueNode guard) { Stamp stamp = AbstractPointerStamp.pointerNonNull(object.stamp()); - ValueNode value = canonical(object, stamp, (GuardingNode) anchor); + ValueNode value = canonical(object, stamp, (GuardingNode) guard); if (value != null) { return value; } - return new PiNode(object, stamp, anchor); + return new PiNode(object, stamp, guard); } @SuppressWarnings("unused") - public static boolean intrinsify(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode object, ValueNode anchor) { + public static boolean intrinsify(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode object, ValueNode guard) { Stamp stamp = AbstractPointerStamp.pointerNonNull(object.stamp()); - ValueNode value = canonical(object, stamp, (GuardingNode) anchor); + ValueNode value = canonical(object, stamp, (GuardingNode) guard); if (value == null) { - value = new PiNode(object, stamp, anchor); + value = new PiNode(object, stamp, guard); } b.push(JavaKind.Object, b.append(value)); return true; @@ -147,6 +147,11 @@ public class PiNode extends FloatingGuardedNode implements LIRLowerable, Virtual return piStamp; } + public void strengthenPiStamp(Stamp newPiStamp) { + assert this.piStamp.join(newPiStamp).equals(newPiStamp) : "stamp can only improve"; + this.piStamp = newPiStamp; + } + @Override public void generate(NodeLIRBuilderTool generator) { if (generator.hasOperand(object)) { @@ -256,17 +261,17 @@ public class PiNode extends FloatingGuardedNode implements LIRLowerable, Virtual /** * Changes the stamp of an object and ensures the newly stamped value is non-null and does not - * float above a given anchor. + * float above a given guard. */ @NodeIntrinsic - public static native Object piCastNonNull(Object object, GuardingNode anchor); + public static native Object piCastNonNull(Object object, GuardingNode guard); /** * Changes the stamp of an object and ensures the newly stamped value is non-null and does not - * float above a given anchor. + * float above a given guard. */ @NodeIntrinsic - public static native Class piCastNonNullClass(Class type, GuardingNode anchor); + public static native Class piCastNonNullClass(Class type, GuardingNode guard); /** * Changes the stamp of an object to represent a given type and to indicate that the object is diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ValuePhiNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ValuePhiNode.java index 71f1357a493..8a60c072cdd 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ValuePhiNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ValuePhiNode.java @@ -22,6 +22,8 @@ */ package org.graalvm.compiler.nodes; +import java.util.Map; + import org.graalvm.compiler.core.common.type.Stamp; import org.graalvm.compiler.core.common.type.StampFactory; import org.graalvm.compiler.graph.NodeClass; @@ -35,7 +37,7 @@ import org.graalvm.util.CollectionsUtil; /** * Value {@link PhiNode}s merge data flow values at control flow merges. */ -@NodeInfo(nameTemplate = "Phi({i#values})") +@NodeInfo(nameTemplate = "Phi({i#values}, {p#valueDescription})") public class ValuePhiNode extends PhiNode implements ArrayLengthProvider { public static final NodeClass TYPE = NodeClass.create(ValuePhiNode.class); @@ -113,4 +115,16 @@ public class ValuePhiNode extends PhiNode implements ArrayLengthProvider { } return super.verify(); } + + @Override + protected String valueDescription() { + return stamp().unrestricted().toString(); + } + + @Override + public Map getDebugProperties(Map map) { + Map properties = super.getDebugProperties(map); + properties.put("valueDescription", valueDescription()); + return properties; + } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/BinaryArithmeticNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/BinaryArithmeticNode.java index 87fb4e0ca69..7593daecd74 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/BinaryArithmeticNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/BinaryArithmeticNode.java @@ -91,7 +91,9 @@ public abstract class BinaryArithmeticNode extends BinaryNode implements Ari public static ConstantNode tryConstantFold(BinaryOp op, ValueNode forX, ValueNode forY, Stamp stamp) { if (forX.isConstant() && forY.isConstant()) { Constant ret = op.foldConstant(forX.asConstant(), forY.asConstant()); - return ConstantNode.forPrimitive(stamp, ret); + if (ret != null) { + return ConstantNode.forPrimitive(stamp, ret); + } } return null; } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ConditionalNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ConditionalNode.java index e1161d63417..c06eea37c9b 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ConditionalNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ConditionalNode.java @@ -22,7 +22,7 @@ */ package org.graalvm.compiler.nodes.calc; -import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_0; +import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_1; import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_2; import static org.graalvm.compiler.nodes.calc.CompareNode.createCompareNode; @@ -47,10 +47,10 @@ import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; import jdk.vm.ci.meta.JavaConstant; /** - * The {@code ConditionalNode} class represents a comparison that yields one of two values. Note - * that these nodes are not built directly from the bytecode but are introduced by canonicalization. + * The {@code ConditionalNode} class represents a comparison that yields one of two (eagerly + * evaluated) values. */ -@NodeInfo(cycles = CYCLES_0, size = SIZE_2) +@NodeInfo(cycles = CYCLES_1, size = SIZE_2) public final class ConditionalNode extends FloatingNode implements Canonicalizable, LIRLowerable { public static final NodeClass TYPE = NodeClass.create(ConditionalNode.class); @@ -116,7 +116,6 @@ public final class ConditionalNode extends FloatingNode implements Canonicalizab valueStamp = valueStamp.join(bounds); } } - } return updateStamp(valueStamp); } @@ -145,49 +144,10 @@ public final class ConditionalNode extends FloatingNode implements Canonicalizab } public static ValueNode canonicalizeConditional(LogicNode condition, ValueNode trueValue, ValueNode falseValue, Stamp stamp) { - // this optimizes the case where a value from the range 0 - 1 is mapped to the range 0 - 1 - if (trueValue.isConstant() && falseValue.isConstant() && trueValue.stamp() instanceof IntegerStamp && falseValue.stamp() instanceof IntegerStamp) { - long constTrueValue = trueValue.asJavaConstant().asLong(); - long constFalseValue = falseValue.asJavaConstant().asLong(); - if (condition instanceof IntegerEqualsNode) { - IntegerEqualsNode equals = (IntegerEqualsNode) condition; - if (equals.getY().isConstant() && equals.getX().stamp() instanceof IntegerStamp) { - IntegerStamp equalsXStamp = (IntegerStamp) equals.getX().stamp(); - if (equalsXStamp.upMask() == 1) { - long equalsY = equals.getY().asJavaConstant().asLong(); - if (equalsY == 0) { - if (constTrueValue == 0 && constFalseValue == 1) { - // return x when: x == 0 ? 0 : 1; - return IntegerConvertNode.convertUnsigned(equals.getX(), stamp); - } else if (constTrueValue == 1 && constFalseValue == 0) { - // negate a boolean value via xor - return IntegerConvertNode.convertUnsigned(XorNode.create(equals.getX(), ConstantNode.forIntegerStamp(equals.getX().stamp(), 1)), stamp); - } - } else if (equalsY == 1) { - if (constTrueValue == 1 && constFalseValue == 0) { - // return x when: x == 1 ? 1 : 0; - return IntegerConvertNode.convertUnsigned(equals.getX(), stamp); - } else if (constTrueValue == 0 && constFalseValue == 1) { - // negate a boolean value via xor - return IntegerConvertNode.convertUnsigned(XorNode.create(equals.getX(), ConstantNode.forIntegerStamp(equals.getX().stamp(), 1)), stamp); - } - } - } - } - } else if (condition instanceof IntegerTestNode) { - // replace IntegerTestNode with AndNode for the following patterns: - // (value & 1) == 0 ? 0 : 1 - // (value & 1) == 1 ? 1 : 0 - IntegerTestNode integerTestNode = (IntegerTestNode) condition; - if (integerTestNode.getY().isConstant()) { - assert integerTestNode.getX().stamp() instanceof IntegerStamp; - long testY = integerTestNode.getY().asJavaConstant().asLong(); - if (testY == 1 && constTrueValue == 0 && constFalseValue == 1) { - return IntegerConvertNode.convertUnsigned(AndNode.create(integerTestNode.getX(), integerTestNode.getY()), stamp); - } - } - } + if (trueValue == falseValue) { + return trueValue; } + if (condition instanceof CompareNode && ((CompareNode) condition).isIdentityComparison()) { // optimize the pattern (x == y) ? x : y CompareNode compare = (CompareNode) condition; @@ -195,25 +155,87 @@ public final class ConditionalNode extends FloatingNode implements Canonicalizab return falseValue; } } - if (trueValue == falseValue) { - 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); + if (trueValue.stamp() instanceof IntegerStamp) { + // check if the conditional is redundant + if (condition instanceof IntegerLessThanNode) { + IntegerLessThanNode lessThan = (IntegerLessThanNode) condition; + IntegerStamp falseValueStamp = (IntegerStamp) falseValue.stamp(); + IntegerStamp trueValueStamp = (IntegerStamp) trueValue.stamp(); + if (lessThan.getX() == trueValue && lessThan.getY() == falseValue) { + // return "x" for "x < y ? x : y" in case that we know "x <= y" + if (trueValueStamp.upperBound() <= falseValueStamp.lowerBound()) { + return trueValue; + } + } else if (lessThan.getX() == falseValue && lessThan.getY() == trueValue) { + // return "x" for "x < y ? y : x" in case that we know "x <= y" + if (falseValueStamp.upperBound() <= trueValueStamp.lowerBound()) { + return falseValue; + } + } + } + + // this optimizes the case where a value from the range 0 - 1 is mapped to the + // range 0 - 1 + if (trueValue.isConstant() && falseValue.isConstant()) { + long constTrueValue = trueValue.asJavaConstant().asLong(); + long constFalseValue = falseValue.asJavaConstant().asLong(); + if (condition instanceof IntegerEqualsNode) { + IntegerEqualsNode equals = (IntegerEqualsNode) condition; + if (equals.getY().isConstant() && equals.getX().stamp() instanceof IntegerStamp) { + IntegerStamp equalsXStamp = (IntegerStamp) equals.getX().stamp(); + if (equalsXStamp.upMask() == 1) { + long equalsY = equals.getY().asJavaConstant().asLong(); + if (equalsY == 0) { + if (constTrueValue == 0 && constFalseValue == 1) { + // return x when: x == 0 ? 0 : 1; + return IntegerConvertNode.convertUnsigned(equals.getX(), stamp); + } else if (constTrueValue == 1 && constFalseValue == 0) { + // negate a boolean value via xor + return IntegerConvertNode.convertUnsigned(XorNode.create(equals.getX(), ConstantNode.forIntegerStamp(equals.getX().stamp(), 1)), stamp); + } + } else if (equalsY == 1) { + if (constTrueValue == 1 && constFalseValue == 0) { + // return x when: x == 1 ? 1 : 0; + return IntegerConvertNode.convertUnsigned(equals.getX(), stamp); + } else if (constTrueValue == 0 && constFalseValue == 1) { + // negate a boolean value via xor + return IntegerConvertNode.convertUnsigned(XorNode.create(equals.getX(), ConstantNode.forIntegerStamp(equals.getX().stamp(), 1)), stamp); + } + } + } + } + } else if (condition instanceof IntegerTestNode) { + // replace IntegerTestNode with AndNode for the following patterns: + // (value & 1) == 0 ? 0 : 1 + // (value & 1) == 1 ? 1 : 0 + IntegerTestNode integerTestNode = (IntegerTestNode) condition; + if (integerTestNode.getY().isConstant()) { + assert integerTestNode.getX().stamp() instanceof IntegerStamp; + long testY = integerTestNode.getY().asJavaConstant().asLong(); + if (testY == 1 && constTrueValue == 0 && constFalseValue == 1) { + return IntegerConvertNode.convertUnsigned(AndNode.create(integerTestNode.getX(), integerTestNode.getY()), stamp); + } + } + } + } + + if (condition instanceof IntegerLessThanNode) { + /* + * 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); + } } } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/DivNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatDivNode.java similarity index 69% rename from src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/DivNode.java rename to src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatDivNode.java index e7b0fa20ca3..195241a9c2a 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/DivNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatDivNode.java @@ -25,6 +25,7 @@ package org.graalvm.compiler.nodes.calc; import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_32; import org.graalvm.compiler.core.common.type.ArithmeticOpTable; +import org.graalvm.compiler.core.common.type.FloatStamp; import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp; import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Div; import org.graalvm.compiler.core.common.type.Stamp; @@ -36,21 +37,20 @@ import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; -import jdk.vm.ci.code.CodeUtil; import jdk.vm.ci.meta.Constant; -import jdk.vm.ci.meta.PrimitiveConstant; @NodeInfo(shortName = "/", cycles = CYCLES_32) -public class DivNode extends BinaryArithmeticNode

{ +public class FloatDivNode extends BinaryArithmeticNode
{ - public static final NodeClass TYPE = NodeClass.create(DivNode.class); + public static final NodeClass TYPE = NodeClass.create(FloatDivNode.class); - public DivNode(ValueNode x, ValueNode y) { - super(TYPE, ArithmeticOpTable::getDiv, x, y); + public FloatDivNode(ValueNode x, ValueNode y) { + this(TYPE, x, y); } - protected DivNode(NodeClass c, ValueNode x, ValueNode y) { + protected FloatDivNode(NodeClass c, ValueNode x, ValueNode y) { super(c, ArithmeticOpTable::getDiv, x, y); + assert stamp instanceof FloatStamp; } public static ValueNode create(ValueNode x, ValueNode y) { @@ -73,33 +73,14 @@ public class DivNode extends BinaryArithmeticNode
{ return canonical(this, getOp(forX, forY), forX, forY); } - private static ValueNode canonical(DivNode self, BinaryOp
op, ValueNode forX, ValueNode forY) { + private static ValueNode canonical(FloatDivNode self, BinaryOp
op, ValueNode forX, ValueNode forY) { if (forY.isConstant()) { Constant c = forY.asConstant(); if (op.isNeutral(c)) { return forX; } - if (c instanceof PrimitiveConstant && ((PrimitiveConstant) c).getJavaKind().isNumericInteger()) { - long i = ((PrimitiveConstant) c).asLong(); - boolean signFlip = false; - if (i < 0) { - i = -i; - signFlip = true; - } - ValueNode divResult = null; - if (CodeUtil.isPowerOf2(i)) { - divResult = new RightShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(i))); - } - if (divResult != null) { - if (signFlip) { - return NegateNode.create(divResult); - } else { - return divResult; - } - } - } } - return self != null ? self : new DivNode(forX, forY); + return self != null ? self : new FloatDivNode(forX, forY); } @Override diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerConvertNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerConvertNode.java index cf9e8df7aa1..90bfdfde901 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerConvertNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerConvertNode.java @@ -129,7 +129,7 @@ public abstract class IntegerConvertNode extends UnaryNode implements A ValueNode convert = convert(input, stamp, false); if (!convert.isAlive()) { assert !convert.isDeleted(); - convert = graph.addOrUnique(convert); + convert = graph.addOrUniqueWithInputs(convert); } return convert; } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerLessThanNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerLessThanNode.java index 3c43b3ee629..bce2a2c39f3 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerLessThanNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerLessThanNode.java @@ -52,7 +52,7 @@ import org.graalvm.compiler.options.OptionValues; @NodeInfo(shortName = "<") public final class IntegerLessThanNode extends IntegerLowerThanNode { public static final NodeClass TYPE = NodeClass.create(IntegerLessThanNode.class); - public static final LessThanOp OP = new LessThanOp(); + private static final LessThanOp OP = new LessThanOp(); public IntegerLessThanNode(ValueNode x, ValueNode y) { super(TYPE, x, y, OP); @@ -203,49 +203,52 @@ public final class IntegerLessThanNode extends IntegerLowerThanNode { } } - int bits = ((IntegerStamp) forX.stamp()).getBits(); - assert ((IntegerStamp) forY.stamp()).getBits() == bits; - long min = OP.minValue(bits); - long xResidue = 0; - ValueNode left = null; - JavaConstant leftCst = null; - if (forX instanceof AddNode) { - AddNode xAdd = (AddNode) forX; - if (xAdd.getY().isJavaConstant()) { - long xCst = xAdd.getY().asJavaConstant().asLong(); - xResidue = xCst - min; - left = xAdd.getX(); - } - } else if (forX.isJavaConstant()) { - leftCst = forX.asJavaConstant(); - } - if (left != null || leftCst != null) { - long yResidue = 0; - ValueNode right = null; - JavaConstant rightCst = null; - if (forY instanceof AddNode) { - AddNode yAdd = (AddNode) forY; - if (yAdd.getY().isJavaConstant()) { - long yCst = yAdd.getY().asJavaConstant().asLong(); - yResidue = yCst - min; - right = yAdd.getX(); + if (forX.stamp() instanceof IntegerStamp) { + assert forY.stamp() instanceof IntegerStamp; + int bits = ((IntegerStamp) forX.stamp()).getBits(); + assert ((IntegerStamp) forY.stamp()).getBits() == bits; + long min = OP.minValue(bits); + long xResidue = 0; + ValueNode left = null; + JavaConstant leftCst = null; + if (forX instanceof AddNode) { + AddNode xAdd = (AddNode) forX; + if (xAdd.getY().isJavaConstant()) { + long xCst = xAdd.getY().asJavaConstant().asLong(); + xResidue = xCst - min; + left = xAdd.getX(); } - } else if (forY.isJavaConstant()) { - rightCst = forY.asJavaConstant(); + } else if (forX.isJavaConstant()) { + leftCst = forX.asJavaConstant(); } - if (right != null || rightCst != null) { - if ((xResidue == 0 && left != null) || (yResidue == 0 && right != null)) { - if (left == null) { - left = ConstantNode.forIntegerBits(bits, leftCst.asLong() - min); - } else if (xResidue != 0) { - left = AddNode.create(left, ConstantNode.forIntegerBits(bits, xResidue)); + if (left != null || leftCst != null) { + long yResidue = 0; + ValueNode right = null; + JavaConstant rightCst = null; + if (forY instanceof AddNode) { + AddNode yAdd = (AddNode) forY; + if (yAdd.getY().isJavaConstant()) { + long yCst = yAdd.getY().asJavaConstant().asLong(); + yResidue = yCst - min; + right = yAdd.getX(); } - if (right == null) { - right = ConstantNode.forIntegerBits(bits, rightCst.asLong() - min); - } else if (yResidue != 0) { - right = AddNode.create(right, ConstantNode.forIntegerBits(bits, yResidue)); + } else if (forY.isJavaConstant()) { + rightCst = forY.asJavaConstant(); + } + if (right != null || rightCst != null) { + if ((xResidue == 0 && left != null) || (yResidue == 0 && right != null)) { + if (left == null) { + left = ConstantNode.forIntegerBits(bits, leftCst.asLong() - min); + } else if (xResidue != 0) { + left = AddNode.create(left, ConstantNode.forIntegerBits(bits, xResidue)); + } + if (right == null) { + right = ConstantNode.forIntegerBits(bits, rightCst.asLong() - min); + } else if (yResidue != 0) { + right = AddNode.create(right, ConstantNode.forIntegerBits(bits, yResidue)); + } + return new IntegerBelowNode(left, right); } - return new IntegerBelowNode(left, right); } } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IsNullNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IsNullNode.java index 7524ef0dfb1..68b5700559b 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IsNullNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IsNullNode.java @@ -121,7 +121,8 @@ public final class IsNullNode extends UnaryOpLogicNode implements LIRLowerable, @Override public Stamp getSucceedingStampForValue(boolean negated) { - AbstractPointerStamp pointerStamp = (AbstractPointerStamp) getValue().stamp(); + // Ignore any more precise input stamp since canonicalization will skip through PiNodes + AbstractPointerStamp pointerStamp = (AbstractPointerStamp) getValue().stamp().unrestricted(); return negated ? pointerStamp.asNonNull() : pointerStamp.asAlwaysNull(); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignedDivNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignedDivNode.java index 79fc9beffd1..c8ba664abad 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignedDivNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignedDivNode.java @@ -63,29 +63,9 @@ public class SignedDivNode extends IntegerDivRemNode implements LIRLowerable { return ConstantNode.forIntegerStamp(stamp(), forX.asJavaConstant().asLong() / y); } else if (forY.isConstant()) { long c = forY.asJavaConstant().asLong(); - if (c == 1) { - return forX; - } - if (c == -1) { - return NegateNode.create(forX); - } - long abs = Math.abs(c); - if (CodeUtil.isPowerOf2(abs) && forX.stamp() instanceof IntegerStamp) { - ValueNode dividend = forX; - IntegerStamp stampX = (IntegerStamp) forX.stamp(); - int log2 = CodeUtil.log2(abs); - // no rounding if dividend is positive or if its low bits are always 0 - if (stampX.canBeNegative() || (stampX.upMask() & (abs - 1)) != 0) { - int bits = PrimitiveStamp.getBits(stamp()); - RightShiftNode sign = new RightShiftNode(forX, ConstantNode.forInt(bits - 1)); - UnsignedRightShiftNode round = new UnsignedRightShiftNode(sign, ConstantNode.forInt(bits - log2)); - dividend = BinaryArithmeticNode.add(dividend, round); - } - RightShiftNode shift = new RightShiftNode(dividend, ConstantNode.forInt(log2)); - if (c < 0) { - return NegateNode.create(shift); - } - return shift; + ValueNode v = canonical(forX, c); + if (v != null) { + return v; } } @@ -113,6 +93,34 @@ public class SignedDivNode extends IntegerDivRemNode implements LIRLowerable { return this; } + public static ValueNode canonical(ValueNode forX, long c) { + if (c == 1) { + return forX; + } + if (c == -1) { + return NegateNode.create(forX); + } + long abs = Math.abs(c); + if (CodeUtil.isPowerOf2(abs) && forX.stamp() instanceof IntegerStamp) { + ValueNode dividend = forX; + IntegerStamp stampX = (IntegerStamp) forX.stamp(); + int log2 = CodeUtil.log2(abs); + // no rounding if dividend is positive or if its low bits are always 0 + if (stampX.canBeNegative() || (stampX.upMask() & (abs - 1)) != 0) { + int bits = PrimitiveStamp.getBits(forX.stamp()); + RightShiftNode sign = new RightShiftNode(forX, ConstantNode.forInt(bits - 1)); + UnsignedRightShiftNode round = new UnsignedRightShiftNode(sign, ConstantNode.forInt(bits - log2)); + dividend = BinaryArithmeticNode.add(dividend, round); + } + RightShiftNode shift = new RightShiftNode(dividend, ConstantNode.forInt(log2)); + if (c < 0) { + return NegateNode.create(shift); + } + return shift; + } + return null; + } + @Override public void generate(NodeLIRBuilderTool gen) { gen.setResult(this, gen.getLIRGeneratorTool().getArithmetic().emitDiv(gen.operand(getX()), gen.operand(getY()), gen.state(this))); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignedRemNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignedRemNode.java index deb5c18d3e5..4dbfd845b54 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignedRemNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignedRemNode.java @@ -72,13 +72,14 @@ public class SignedRemNode extends IntegerDivRemNode implements LIRLowerable { return ConstantNode.forIntegerStamp(stamp(), 0); } else if (CodeUtil.isPowerOf2(constY)) { if (xStamp.isPositive()) { + // x & (y - 1) return new AndNode(forX, ConstantNode.forIntegerStamp(stamp(), constY - 1)); } else if (xStamp.isNegative()) { + // -((-x) & (y - 1)) return new NegateNode(new AndNode(new NegateNode(forX), ConstantNode.forIntegerStamp(stamp(), constY - 1))); } else { - return new ConditionalNode(IntegerLessThanNode.create(forX, ConstantNode.forIntegerStamp(forX.stamp(), 0)), - new NegateNode(new AndNode(new NegateNode(forX), ConstantNode.forIntegerStamp(stamp(), constY - 1))), - new AndNode(forX, ConstantNode.forIntegerStamp(stamp(), constY - 1))); + // x - ((x / y) << log2(y)) + return SubNode.create(forX, LeftShiftNode.create(SignedDivNode.canonical(forX, constY), ConstantNode.forInt(CodeUtil.log2(constY)))); } } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnaryNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnaryNode.java index 542dfaa234a..66a8ee32342 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnaryNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnaryNode.java @@ -45,6 +45,11 @@ public abstract class UnaryNode extends FloatingNode implements Canonicalizable. return value; } + public void setValue(ValueNode value) { + updateUsages(this.value, value); + this.value = value; + } + /** * Creates a new UnaryNode instance. * diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnpackEndianHalfNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnpackEndianHalfNode.java new file mode 100644 index 00000000000..ad7cfdc9932 --- /dev/null +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnpackEndianHalfNode.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2017, 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 org.graalvm.compiler.nodes.calc; + +import java.nio.ByteOrder; + +import org.graalvm.compiler.core.common.type.StampFactory; +import org.graalvm.compiler.graph.Node; +import org.graalvm.compiler.graph.NodeClass; +import org.graalvm.compiler.graph.spi.CanonicalizerTool; +import org.graalvm.compiler.nodeinfo.NodeCycles; +import org.graalvm.compiler.nodeinfo.NodeInfo; +import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.ValueNode; +import org.graalvm.compiler.nodes.spi.Lowerable; +import org.graalvm.compiler.nodes.spi.LoweringTool; + +import jdk.vm.ci.meta.JavaKind; + +/** + * Produces the platform dependent first or second half of a long or double value as an int. + */ +@NodeInfo(cycles = NodeCycles.CYCLES_2) +public final class UnpackEndianHalfNode extends UnaryNode implements Lowerable { + public static final NodeClass TYPE = NodeClass.create(UnpackEndianHalfNode.class); + + private final boolean firstHalf; + + protected UnpackEndianHalfNode(ValueNode value, boolean firstHalf) { + super(TYPE, StampFactory.forKind(JavaKind.Int), value); + assert value.getStackKind() == JavaKind.Double || value.getStackKind() == JavaKind.Long : "unexpected kind " + value.getStackKind(); + this.firstHalf = firstHalf; + } + + public static ValueNode create(ValueNode value, boolean firstHalf) { + if (value.isConstant() && value.asConstant().isDefaultForKind()) { + return ConstantNode.defaultForKind(JavaKind.Int); + } + return new UnpackEndianHalfNode(value, firstHalf); + } + + public boolean isFirstHalf() { + return firstHalf; + } + + @Override + public Node canonical(CanonicalizerTool tool, ValueNode forValue) { + if (forValue.isConstant() && forValue.asConstant().isDefaultForKind()) { + return ConstantNode.defaultForKind(stamp.getStackKind()); + } + return this; + } + + @Override + public void lower(LoweringTool tool) { + tool.getLowerer().lower(this, tool); + } + + public void lower(ByteOrder byteOrder) { + ValueNode result = value; + if (value.getStackKind() == JavaKind.Double) { + result = graph().unique(new ReinterpretNode(JavaKind.Long, value)); + } + if ((byteOrder == ByteOrder.BIG_ENDIAN) == firstHalf) { + result = graph().unique(new UnsignedRightShiftNode(result, ConstantNode.forInt(32, graph()))); + } + result = IntegerConvertNode.convert(result, StampFactory.forKind(JavaKind.Int), graph()); + replaceAtUsagesAndDelete(result); + } +} diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/cfg/ControlFlowGraph.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/cfg/ControlFlowGraph.java index 7f268788351..bb4918a96c4 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/cfg/ControlFlowGraph.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/cfg/ControlFlowGraph.java @@ -592,7 +592,11 @@ public final class ControlFlowGraph implements AbstractControlFlowGraph { for (Block block : reversePostOrder) { AbstractBeginNode beginNode = block.getBeginNode(); if (beginNode instanceof LoopBeginNode) { - Loop loop = new HIRLoop(block.getLoop(), loops.size(), block); + Loop parent = block.getLoop(); + Loop loop = new HIRLoop(parent, loops.size(), block); + if (parent != null) { + parent.getChildren().add(loop); + } loops.add(loop); block.setLoop(loop); loop.getBlocks().add(block); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/BranchProbabilityNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/BranchProbabilityNode.java index ac74a474147..cc3e63d14f8 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/BranchProbabilityNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/BranchProbabilityNode.java @@ -82,6 +82,9 @@ public final class BranchProbabilityNode extends FloatingNode implements Simplif @Override public void simplify(SimplifierTool tool) { + if (!hasUsages()) { + return; + } if (probability.isConstant()) { double probabilityValue = probability.asJavaConstant().asDouble(); if (probabilityValue < 0.0) { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/RawLoadNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/RawLoadNode.java index 63e6dfcffe2..de715c7e2c3 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/RawLoadNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/RawLoadNode.java @@ -79,7 +79,7 @@ public class RawLoadNode extends UnsafeAccessNode implements Lowerable, Virtuali super(TYPE, stamp, object, offset, accessKind, locationIdentity, false); } - public RawLoadNode(NodeClass c, ValueNode object, ValueNode offset, JavaKind accessKind, LocationIdentity locationIdentity) { + protected RawLoadNode(NodeClass c, ValueNode object, ValueNode offset, JavaKind accessKind, LocationIdentity locationIdentity) { super(c, StampFactory.forKind(accessKind.getStackKind()), object, offset, accessKind, locationIdentity, false); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/RawStoreNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/RawStoreNode.java index b9d3f71dc07..720654f5a82 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/RawStoreNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/RawStoreNode.java @@ -29,7 +29,6 @@ import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_1; import org.graalvm.compiler.core.common.type.StampFactory; import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.nodeinfo.NodeInfo; -import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.FrameState; import org.graalvm.compiler.nodes.StateSplit; import org.graalvm.compiler.nodes.ValueNode; @@ -39,12 +38,10 @@ import org.graalvm.compiler.nodes.spi.Lowerable; import org.graalvm.compiler.nodes.spi.LoweringTool; import org.graalvm.compiler.nodes.spi.Virtualizable; import org.graalvm.compiler.nodes.spi.VirtualizerTool; -import org.graalvm.compiler.nodes.type.StampTool; import org.graalvm.compiler.nodes.virtual.VirtualObjectNode; import org.graalvm.word.LocationIdentity; import jdk.vm.ci.meta.Assumptions; -import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.ResolvedJavaField; @@ -123,41 +120,8 @@ public final class RawStoreNode extends UnsafeAccessNode implements StateSplit, if (indexValue.isConstant()) { long off = indexValue.asJavaConstant().asLong(); int entryIndex = virtual.entryIndexForOffset(off, accessKind()); - if (entryIndex != -1) { - JavaKind entryKind = virtual.entryKind(entryIndex); - boolean canVirtualize = entryKind == accessKind() || (entryKind == accessKind().getStackKind() && !StampTool.typeOrNull(object()).isArray()); - if (!canVirtualize) { - /* - * Special case: If the entryKind is long, allow arbitrary kinds as long as - * a value of the same kind is already there. This can only happen if some - * other node initialized the entry with a value of a different kind. One - * example where this happens is the Truffle NewFrameNode. - */ - ValueNode entry = tool.getEntry(virtual, entryIndex); - if (entryKind == JavaKind.Long && entry.getStackKind() == value.getStackKind()) { - canVirtualize = true; - } - } - if (canVirtualize) { - tool.setVirtualEntry(virtual, entryIndex, value(), true); - tool.delete(); - } else { - /* - * Special case: Allow storing a single long or double value into two - * consecutive int slots. - */ - if ((accessKind() == JavaKind.Long || accessKind() == JavaKind.Double) && entryKind == JavaKind.Int) { - int nextIndex = virtual.entryIndexForOffset(off + 4, entryKind); - if (nextIndex != -1) { - JavaKind nextKind = virtual.entryKind(nextIndex); - if (nextKind == JavaKind.Int) { - tool.setVirtualEntry(virtual, entryIndex, value(), true); - tool.setVirtualEntry(virtual, nextIndex, ConstantNode.forConstant(JavaConstant.forIllegal(), tool.getMetaAccessProvider(), graph()), true); - tool.delete(); - } - } - } - } + if (entryIndex != -1 && tool.setVirtualEntry(virtual, entryIndex, value(), accessKind(), off)) { + tool.delete(); } } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/GraphBuilderContext.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/GraphBuilderContext.java index 3d5578eaa50..09b4cc72c89 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/GraphBuilderContext.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/GraphBuilderContext.java @@ -280,7 +280,7 @@ public interface GraphBuilderContext extends GraphBuilderTool { ObjectStamp receiverStamp = (ObjectStamp) value.stamp(); Stamp stamp = receiverStamp.join(objectNonNull()); FixedGuardNode fixedGuard = append(new FixedGuardNode(condition, NullCheckException, action, true)); - ValueNode nonNullReceiver = getGraph().addOrUnique(PiNode.create(value, stamp, fixedGuard)); + ValueNode nonNullReceiver = getGraph().addOrUniqueWithInputs(PiNode.create(value, stamp, 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. diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/InlineInvokePlugin.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/InlineInvokePlugin.java index 73ac712a8f8..a85d3589ea0 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/InlineInvokePlugin.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/InlineInvokePlugin.java @@ -53,6 +53,12 @@ public interface InlineInvokePlugin extends GraphBuilderPlugin { */ public static final InlineInfo DO_NOT_INLINE_NO_EXCEPTION = new InlineInfo(null, null); + /** + * Denotes a call site must not be inlined and the execution should be transferred to + * interpreter in case of an exception. + */ + public static final InlineInfo DO_NOT_INLINE_DEOPTIMIZE_ON_EXCEPTION = new InlineInfo(null, null); + private final ResolvedJavaMethod methodToInline; private final BytecodeProvider intrinsicBytecodeProvider; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/IntrinsicContext.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/IntrinsicContext.java index dedabd6099a..f06676378ef 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/IntrinsicContext.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/IntrinsicContext.java @@ -66,6 +66,34 @@ public class IntrinsicContext { */ final BytecodeProvider bytecodeProvider; + final CompilationContext compilationContext; + + final boolean allowPartialIntrinsicArgumentMismatch; + + public IntrinsicContext(ResolvedJavaMethod method, ResolvedJavaMethod intrinsic, BytecodeProvider bytecodeProvider, CompilationContext compilationContext) { + this(method, intrinsic, bytecodeProvider, compilationContext, false); + } + + public IntrinsicContext(ResolvedJavaMethod method, ResolvedJavaMethod intrinsic, BytecodeProvider bytecodeProvider, CompilationContext compilationContext, + boolean allowPartialIntrinsicArgumentMismatch) { + this.method = method; + this.intrinsic = intrinsic; + this.bytecodeProvider = bytecodeProvider; + assert bytecodeProvider != null; + this.compilationContext = compilationContext; + this.allowPartialIntrinsicArgumentMismatch = allowPartialIntrinsicArgumentMismatch; + assert !isCompilationRoot() || method.hasBytecodes() : "Cannot root compile intrinsic for native or abstract method " + method.format("%H.%n(%p)"); + } + + /** + * A partial intrinsic exits by (effectively) calling the intrinsified method. Normally, this + * call must use exactly the same arguments as the call that is being intrinsified. This allows + * to override this behavior. + */ + public boolean allowPartialIntrinsicArgumentMismatch() { + return allowPartialIntrinsicArgumentMismatch; + } + /** * Gets the method being intrinsified. */ @@ -96,17 +124,6 @@ public class IntrinsicContext { return method.equals(targetMethod) || intrinsic.equals(targetMethod); } - final CompilationContext compilationContext; - - public IntrinsicContext(ResolvedJavaMethod method, ResolvedJavaMethod intrinsic, BytecodeProvider bytecodeProvider, CompilationContext compilationContext) { - this.method = method; - this.intrinsic = intrinsic; - this.bytecodeProvider = bytecodeProvider; - assert bytecodeProvider != null; - 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); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/InstanceOfNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/InstanceOfNode.java index b9aa170b065..e63cffefe88 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/InstanceOfNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/InstanceOfNode.java @@ -49,6 +49,8 @@ import org.graalvm.compiler.nodes.type.StampTool; import jdk.vm.ci.meta.JavaTypeProfile; import jdk.vm.ci.meta.TriState; +import java.util.Objects; + /** * The {@code InstanceOfNode} represents an instanceof test. */ @@ -56,7 +58,7 @@ import jdk.vm.ci.meta.TriState; public class InstanceOfNode extends UnaryOpLogicNode implements Lowerable, Virtualizable { public static final NodeClass TYPE = NodeClass.create(InstanceOfNode.class); - protected final ObjectStamp checkedStamp; + private ObjectStamp checkedStamp; private JavaTypeProfile profile; @OptionalInput(Anchor) protected AnchoringNode anchor; @@ -126,7 +128,9 @@ public class InstanceOfNode extends UnaryOpLogicNode implements Lowerable, Virtu // The check will always succeed, the union of the two stamps is equal to the // checked stamp. return LogicConstantNode.tautology(); - } else if (checkedStamp.type().equals(meetStamp.type()) && checkedStamp.isExactType() == meetStamp.isExactType() && checkedStamp.alwaysNull() == meetStamp.alwaysNull()) { + } else if (checkedStamp.alwaysNull()) { + return IsNullNode.create(object); + } else if (Objects.equals(checkedStamp.type(), meetStamp.type()) && checkedStamp.isExactType() == meetStamp.isExactType() && checkedStamp.alwaysNull() == meetStamp.alwaysNull()) { assert checkedStamp.nonNull() != inputStamp.nonNull(); // The only difference makes the null-ness of the value => simplify the check. if (checkedStamp.nonNull()) { @@ -135,8 +139,8 @@ public class InstanceOfNode extends UnaryOpLogicNode implements Lowerable, Virtu return IsNullNode.create(object); } } + assert checkedStamp.type() != null; } - return null; } @@ -204,4 +208,13 @@ public class InstanceOfNode extends UnaryOpLogicNode implements Lowerable, Virtu public AnchoringNode getAnchor() { return anchor; } + + public ObjectStamp getCheckedStamp() { + return checkedStamp; + } + + public void strengthenCheckedStamp(ObjectStamp newCheckedStamp) { + assert this.checkedStamp.join(newCheckedStamp).equals(newCheckedStamp) : "stamp can only improve"; + this.checkedStamp = newCheckedStamp; + } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LoadFieldNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LoadFieldNode.java index 0373f894197..5ff55d2a65f 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LoadFieldNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LoadFieldNode.java @@ -25,7 +25,6 @@ package org.graalvm.compiler.nodes.java; import static org.graalvm.compiler.graph.iterators.NodePredicates.isNotA; import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_2; -import jdk.vm.ci.meta.ConstantReflectionProvider; import org.graalvm.compiler.core.common.spi.ConstantFieldProvider; import org.graalvm.compiler.core.common.type.Stamp; import org.graalvm.compiler.core.common.type.StampFactory; @@ -47,15 +46,16 @@ import org.graalvm.compiler.nodes.type.StampTool; import org.graalvm.compiler.nodes.util.ConstantFoldUtil; import org.graalvm.compiler.nodes.virtual.VirtualInstanceNode; import org.graalvm.compiler.nodes.virtual.VirtualObjectNode; +import org.graalvm.compiler.options.OptionValues; import jdk.vm.ci.meta.Assumptions; +import jdk.vm.ci.meta.ConstantReflectionProvider; import jdk.vm.ci.meta.DeoptimizationAction; import jdk.vm.ci.meta.DeoptimizationReason; import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.MetaAccessProvider; import jdk.vm.ci.meta.ResolvedJavaField; -import org.graalvm.compiler.options.OptionValues; /** * The {@code LoadFieldNode} represents a read of a static or instance field. @@ -150,7 +150,7 @@ public final class LoadFieldNode extends AccessFieldNode implements Canonicaliza } public ConstantNode asConstant(CanonicalizerTool tool, JavaConstant constant) { - return ConstantFoldUtil.tryConstantFold(tool.getConstantFieldProvider(), tool.getConstantReflection(), tool.getMetaAccess(), field(), constant, getOptions()); + return ConstantFoldUtil.tryConstantFold(tool.getConstantFieldProvider(), tool.getConstantReflection(), tool.getMetaAccess(), field(), constant, tool.getOptions()); } private static PhiNode asPhi(ConstantFieldProvider constantFields, ConstantReflectionProvider constantReflection, diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/StoreFieldNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/StoreFieldNode.java index 343d7d3c614..84c26fa1b9e 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/StoreFieldNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/StoreFieldNode.java @@ -29,7 +29,6 @@ import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.nodeinfo.InputType; import org.graalvm.compiler.nodeinfo.NodeCycles; import org.graalvm.compiler.nodeinfo.NodeInfo; -import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.FrameState; import org.graalvm.compiler.nodes.StateSplit; import org.graalvm.compiler.nodes.ValueNode; @@ -38,7 +37,6 @@ import org.graalvm.compiler.nodes.spi.VirtualizerTool; import org.graalvm.compiler.nodes.virtual.VirtualInstanceNode; import org.graalvm.compiler.nodes.virtual.VirtualObjectNode; -import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.ResolvedJavaField; /** @@ -90,15 +88,8 @@ public final class StoreFieldNode extends AccessFieldNode implements StateSplit, VirtualInstanceNode virtual = (VirtualInstanceNode) alias; int fieldIndex = virtual.fieldIndex(field()); if (fieldIndex != -1) { - ValueNode existing = tool.getEntry((VirtualObjectNode) alias, fieldIndex); - if (existing.stamp().isCompatible(value().stamp())) { - tool.setVirtualEntry(virtual, fieldIndex, value(), false); - tool.delete(); - } else { - // stamp of the value is not compatible with the value in the virtualizer - // can only happen with unsafe two slot writes on one slot fields - assert existing instanceof ConstantNode && ((ConstantNode) existing).asConstant().equals(JavaConstant.forIllegal()) : value.stamp() + " vs " + existing.stamp(); - } + tool.setVirtualEntry(virtual, fieldIndex, value()); + tool.delete(); } } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/StoreIndexedNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/StoreIndexedNode.java index 04e6fb4aeaf..b5e55e7bf47 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/StoreIndexedNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/StoreIndexedNode.java @@ -89,7 +89,7 @@ public final class StoreIndexedNode extends AccessIndexedNode implements StateSp ResolvedJavaType componentType = virtual.type().getComponentType(); if (componentType.isPrimitive() || StampTool.isPointerAlwaysNull(value) || componentType.getSuperclass() == null || (StampTool.typeReferenceOrNull(value) != null && componentType.isAssignableFrom(StampTool.typeOrNull(value)))) { - tool.setVirtualEntry(virtual, idx, value(), false); + tool.setVirtualEntry(virtual, idx, value()); tool.delete(); } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/MemoryPhiNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/MemoryPhiNode.java index fadb2cad9a9..5e7f2a30819 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/MemoryPhiNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/MemoryPhiNode.java @@ -62,4 +62,9 @@ public final class MemoryPhiNode extends PhiNode implements MemoryNode { public NodeInputList values() { return values; } + + @Override + protected String valueDescription() { + return locationIdentity.toString(); + } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/address/OffsetAddressNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/address/OffsetAddressNode.java index f0be982e4d0..345bef6cd99 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/address/OffsetAddressNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/address/OffsetAddressNode.java @@ -22,6 +22,7 @@ */ package org.graalvm.compiler.nodes.memory.address; +import org.graalvm.compiler.core.common.type.AbstractPointerStamp; import org.graalvm.compiler.core.common.type.IntegerStamp; import org.graalvm.compiler.core.common.type.Stamp; import org.graalvm.compiler.graph.Node; @@ -52,6 +53,9 @@ public class OffsetAddressNode extends AddressNode implements Canonicalizable { super(TYPE); this.base = base; this.offset = offset; + + assert base != null && (base.stamp() instanceof AbstractPointerStamp || IntegerStamp.getBits(base.stamp()) == 64) && + offset != null && IntegerStamp.getBits(offset.stamp()) == 64 : "both values must have 64 bits"; } @Override @@ -62,6 +66,7 @@ public class OffsetAddressNode extends AddressNode implements Canonicalizable { public void setBase(ValueNode base) { updateUsages(this.base, base); this.base = base; + assert base != null && (base.stamp() instanceof AbstractPointerStamp || IntegerStamp.getBits(base.stamp()) == 64); } public ValueNode getOffset() { @@ -71,6 +76,7 @@ public class OffsetAddressNode extends AddressNode implements Canonicalizable { public void setOffset(ValueNode offset) { updateUsages(this.offset, offset); this.offset = offset; + assert offset != null && IntegerStamp.getBits(offset.stamp()) == 64; } @Override diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/spi/VirtualizerTool.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/spi/VirtualizerTool.java index e7acfc7bec4..785675adde5 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/spi/VirtualizerTool.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/spi/VirtualizerTool.java @@ -25,6 +25,7 @@ package org.graalvm.compiler.nodes.spi; import java.util.List; import org.graalvm.compiler.debug.DebugContext; +import org.graalvm.compiler.debug.GraalError; import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.java.MonitorIdNode; @@ -91,9 +92,17 @@ public interface VirtualizerTool { * * @param index the index to be set. * @param value the new value for the given index. - * @param unsafe if true, then mismatching value {@link JavaKind}s will be accepted. + * @param accessKind the kind of the store which might be different than + * {@link VirtualObjectNode#entryKind(int)}. + * @return true if the operation was permitted */ - void setVirtualEntry(VirtualObjectNode virtualObject, int index, ValueNode value, boolean unsafe); + boolean setVirtualEntry(VirtualObjectNode virtualObject, int index, ValueNode value, JavaKind accessKind, long offset); + + default void setVirtualEntry(VirtualObjectNode virtualObject, int index, ValueNode value) { + if (!setVirtualEntry(virtualObject, index, value, null, 0)) { + throw new GraalError("unexpected failure when updating virtual entry"); + } + } ValueNode getEntry(VirtualObjectNode virtualObject, int index); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/util/GraphUtil.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/util/GraphUtil.java index 083fa733f84..4990cc79e70 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/util/GraphUtil.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/util/GraphUtil.java @@ -242,7 +242,7 @@ public class GraphUtil { EconomicSet collectedUnusedNodes = unusedNodes = EconomicSet.create(Equivalence.IDENTITY); nodeEventScope = node.graph().trackNodeEvents(new Graph.NodeEventListener() { @Override - public void event(Graph.NodeEvent e, Node n) { + public void changed(Graph.NodeEvent e, Node n) { if (e == Graph.NodeEvent.ZERO_USAGES && isFloatingNode(n) && !(n instanceof GuardNode)) { collectedUnusedNodes.add(n); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/util/JavaConstantFormattable.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/util/JavaConstantFormattable.java new file mode 100644 index 00000000000..61f44e8d08f --- /dev/null +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/util/JavaConstantFormattable.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2017, 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 org.graalvm.compiler.nodes.util; + +/** + * Performs special formatting of values involving {@link jdk.vm.ci.meta.JavaConstant JavaConstants} + * when they are being dumped. + */ +public interface JavaConstantFormattable { + String format(JavaConstantFormatter formatter); +} diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/util/JavaConstantFormatter.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/util/JavaConstantFormatter.java new file mode 100644 index 00000000000..5f475d8aacf --- /dev/null +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/util/JavaConstantFormatter.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2017, 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 org.graalvm.compiler.nodes.util; + +import jdk.vm.ci.meta.JavaConstant; + +public interface JavaConstantFormatter { + String format(JavaConstant constant); +} diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/virtual/VirtualArrayNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/virtual/VirtualArrayNode.java index 19b9c608fc1..65f644b73dc 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/virtual/VirtualArrayNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/virtual/VirtualArrayNode.java @@ -37,7 +37,7 @@ import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.ResolvedJavaType; import sun.misc.Unsafe; -@NodeInfo(nameTemplate = "VirtualArray {p#componentType/s}[{p#length}]") +@NodeInfo(nameTemplate = "VirtualArray({p#objectId}) {p#componentType/s}[{p#length}]") public class VirtualArrayNode extends VirtualObjectNode implements ArrayLengthProvider { public static final NodeClass TYPE = NodeClass.create(VirtualArrayNode.class); @@ -76,7 +76,7 @@ public class VirtualArrayNode extends VirtualObjectNode implements ArrayLengthPr @Override public String toString(Verbosity verbosity) { if (verbosity == Verbosity.Name) { - return super.toString(Verbosity.Name) + " " + componentType.getName() + "[" + length + "]"; + return super.toString(Verbosity.Name) + "(" + getObjectId() + ") " + componentType.getName() + "[" + length + "]"; } else { return super.toString(verbosity); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/virtual/VirtualInstanceNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/virtual/VirtualInstanceNode.java index 0591a2d6b54..bee87d12029 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/virtual/VirtualInstanceNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/virtual/VirtualInstanceNode.java @@ -32,7 +32,7 @@ import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.ResolvedJavaType; -@NodeInfo(nameTemplate = "VirtualInstance {p#type/s}") +@NodeInfo(nameTemplate = "VirtualInstance({p#objectId}) {p#type/s}") public class VirtualInstanceNode extends VirtualObjectNode { public static final NodeClass TYPE = NodeClass.create(VirtualInstanceNode.class); @@ -78,7 +78,7 @@ public class VirtualInstanceNode extends VirtualObjectNode { @Override public String toString(Verbosity verbosity) { if (verbosity == Verbosity.Name) { - return super.toString(Verbosity.Name) + " " + type.toJavaName(false); + return super.toString(Verbosity.Name) + "(" + getObjectId() + ") " + type.toJavaName(false); } else { return super.toString(verbosity); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options.processor/src/org/graalvm/compiler/options/processor/OptionProcessor.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options.processor/src/org/graalvm/compiler/options/processor/OptionProcessor.java index 7749a77cb39..797a127bbf6 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options.processor/src/org/graalvm/compiler/options/processor/OptionProcessor.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options.processor/src/org/graalvm/compiler/options/processor/OptionProcessor.java @@ -394,6 +394,9 @@ public class OptionProcessor extends AbstractProcessor { options = new OptionsInfo(topDeclaringType); map.put(topDeclaringType, options); } + if (!element.getEnclosingElement().getSimpleName().toString().endsWith("Options")) { + processingEnv.getMessager().printMessage(Kind.ERROR, "Option declaring classes must have a name that ends with 'Options'", element.getEnclosingElement()); + } processElement(element, options); } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ConditionalEliminationPhase.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ConditionalEliminationPhase.java index 276643ae150..3530b938204 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ConditionalEliminationPhase.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ConditionalEliminationPhase.java @@ -248,7 +248,7 @@ public class ConditionalEliminationPhase extends BasePhase { /** * Tests which may be eliminated because post dominating tests to prove a broader condition. */ - private Deque pendingTests; + private Deque pendingTests; public Instance(StructuredGraph graph, BlockMap> blockToNodes, PhaseContext context) { this.graph = graph; @@ -555,7 +555,8 @@ public class ConditionalEliminationPhase extends BasePhase { } } if (guard instanceof DeoptimizingGuard) { - pendingTests.push(new PendingTest(condition, (DeoptimizingGuard) guard)); + assert ((DeoptimizingGuard) guard).getCondition() == condition; + pendingTests.push((DeoptimizingGuard) guard); } registerCondition(condition, negated, guard); } @@ -628,15 +629,16 @@ public class ConditionalEliminationPhase extends BasePhase { } protected boolean foldPendingTest(DeoptimizingGuard thisGuard, ValueNode original, Stamp newStamp, GuardRewirer rewireGuardFunction) { - for (PendingTest pending : pendingTests) { + for (DeoptimizingGuard pendingGuard : pendingTests) { + LogicNode pendingCondition = pendingGuard.getCondition(); TriState result = TriState.UNKNOWN; - if (pending.condition instanceof UnaryOpLogicNode) { - UnaryOpLogicNode unaryLogicNode = (UnaryOpLogicNode) pending.condition; + if (pendingCondition instanceof UnaryOpLogicNode) { + UnaryOpLogicNode unaryLogicNode = (UnaryOpLogicNode) pendingCondition; if (unaryLogicNode.getValue() == original) { result = unaryLogicNode.tryFold(newStamp); } - } else if (pending.condition instanceof BinaryOpLogicNode) { - BinaryOpLogicNode binaryOpLogicNode = (BinaryOpLogicNode) pending.condition; + } else if (pendingCondition instanceof BinaryOpLogicNode) { + BinaryOpLogicNode binaryOpLogicNode = (BinaryOpLogicNode) pendingCondition; ValueNode x = binaryOpLogicNode.getX(); ValueNode y = binaryOpLogicNode.getY(); if (x == original) { @@ -659,7 +661,7 @@ public class ConditionalEliminationPhase extends BasePhase { */ InputFilter v = new InputFilter(original); thisGuard.getCondition().applyInputs(v); - if (v.ok && foldGuard(thisGuard, pending.guard, newStamp, rewireGuardFunction)) { + if (v.ok && foldGuard(thisGuard, pendingGuard, newStamp, rewireGuardFunction)) { return true; } } @@ -1026,16 +1028,6 @@ public class ConditionalEliminationPhase extends BasePhase { boolean rewire(GuardingNode guard, boolean result, Stamp guardedValueStamp, ValueNode newInput); } - protected static class PendingTest { - private final LogicNode condition; - private final DeoptimizingGuard guard; - - public PendingTest(LogicNode condition, DeoptimizingGuard guard) { - this.condition = condition; - this.guard = guard; - } - } - protected static final class InfoElement { private final Stamp stamp; private final GuardingNode guard; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/FixReadsPhase.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/FixReadsPhase.java index a83fc06422d..b1379d9b0ca 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/FixReadsPhase.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/FixReadsPhase.java @@ -221,7 +221,12 @@ public class FixReadsPhase extends BasePhase { EconomicMap endMap = endMaps.get(node); MapCursor entries = endMap.getEntries(); while (entries.advance()) { - if (registerNewValueStamp(entries.getKey(), entries.getValue())) { + ValueNode value = entries.getKey(); + if (value.isDeleted()) { + // nodes from this map can be deleted when a loop dies + continue; + } + if (registerNewValueStamp(value, entries.getValue())) { counterBetterMergedStamps.increment(debug); } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ProfileCompiledMethodsPhase.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ProfileCompiledMethodsPhase.java index 6e8cd9de1b9..c395bcca3fc 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ProfileCompiledMethodsPhase.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ProfileCompiledMethodsPhase.java @@ -48,7 +48,7 @@ import org.graalvm.compiler.nodes.UnwindNode; import org.graalvm.compiler.nodes.VirtualState; import org.graalvm.compiler.nodes.calc.BinaryNode; import org.graalvm.compiler.nodes.calc.ConvertNode; -import org.graalvm.compiler.nodes.calc.DivNode; +import org.graalvm.compiler.nodes.calc.FloatDivNode; import org.graalvm.compiler.nodes.calc.IntegerDivRemNode; import org.graalvm.compiler.nodes.calc.MulNode; import org.graalvm.compiler.nodes.calc.NotNode; @@ -162,7 +162,7 @@ public class ProfileCompiledMethodsPhase extends Phase { return 2; } else if (node instanceof LogicNode || node instanceof ConvertNode || node instanceof BinaryNode || node instanceof NotNode) { return 1; - } else if (node instanceof IntegerDivRemNode || node instanceof DivNode || node instanceof RemNode) { + } else if (node instanceof IntegerDivRemNode || node instanceof FloatDivNode || node instanceof RemNode) { return 10; } else if (node instanceof MulNode) { return 3; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/elem/InlineableGraph.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/elem/InlineableGraph.java index d7334e35baa..35d076543a6 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/elem/InlineableGraph.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/elem/InlineableGraph.java @@ -43,7 +43,6 @@ import org.graalvm.compiler.phases.common.inlining.InliningUtil; import org.graalvm.compiler.phases.graph.FixedNodeProbabilityCache; import org.graalvm.compiler.phases.tiers.HighTierContext; -import jdk.vm.ci.meta.Constant; import jdk.vm.ci.meta.ResolvedJavaMethod; /** @@ -159,11 +158,11 @@ public class InlineableGraph implements Inlineable { if (param.usages().isNotEmpty()) { ValueNode arg = args.get(param.index()); if (arg.isConstant()) { - Constant constant = arg.asConstant(); + ConstantNode constant = (ConstantNode) arg; parameterUsages = trackParameterUsages(param, parameterUsages); // collect param usages before replacing the param param.replaceAtUsagesAndDelete(graph.unique( - ConstantNode.forConstant(arg.stamp(), constant, ((ConstantNode) arg).getStableDimension(), ((ConstantNode) arg).isDefaultStable(), context.getMetaAccess()))); + ConstantNode.forConstant(arg.stamp(), constant.getValue(), constant.getStableDimension(), constant.isDefaultStable(), context.getMetaAccess()))); // param-node gone, leaving a gap in the sequence given by param.index() } else { Stamp impro = improvedStamp(arg, param); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/util/HashSetNodeEventListener.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/util/HashSetNodeEventListener.java index 57c13252ff1..eef9f035006 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/util/HashSetNodeEventListener.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/util/HashSetNodeEventListener.java @@ -37,7 +37,7 @@ import org.graalvm.util.EconomicSet; * A simple {@link NodeEventListener} implementation that accumulates event nodes in a * {@link HashSet}. */ -public class HashSetNodeEventListener implements NodeEventListener { +public class HashSetNodeEventListener extends NodeEventListener { private final EconomicSet nodes; private final Set filter; @@ -68,7 +68,7 @@ public class HashSetNodeEventListener implements NodeEventListener { } @Override - public void event(NodeEvent e, Node node) { + public void changed(NodeEvent e, Node node) { if (filter.contains(e)) { nodes.add(node); if (node instanceof IndirectCanonicalization) { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/BasePhase.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/BasePhase.java index 7f2574950b9..927b94ede62 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/BasePhase.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/BasePhase.java @@ -258,7 +258,7 @@ public abstract class BasePhase implements PhaseSizeContract { return false; } - private final class GraphChangeListener implements NodeEventListener { + private final class GraphChangeListener extends NodeEventListener { boolean changed; private StructuredGraph graph; private Mark mark; @@ -269,7 +269,7 @@ public abstract class BasePhase implements PhaseSizeContract { } @Override - public void event(NodeEvent e, Node node) { + public void changed(NodeEvent e, Node node) { if (!graph.isNew(mark, node) && node.isAlive()) { if (e == NodeEvent.INPUT_CHANGED || e == NodeEvent.ZERO_USAGES) { changed = true; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/schedule/SchedulePhase.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/schedule/SchedulePhase.java index c2b10b5a349..1b896801e58 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/schedule/SchedulePhase.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/schedule/SchedulePhase.java @@ -109,7 +109,7 @@ public final class SchedulePhase extends Phase { if (immutableGraph && Assertions.assertionsEnabled()) { return graph.trackNodeEvents(new NodeEventListener() { @Override - public void event(NodeEvent e, Node node) { + public void changed(NodeEvent e, Node node) { assert false : "graph changed: " + e + " on node " + node; } }); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/verify/VerifyGetOptionsUsage.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/verify/VerifyGetOptionsUsage.java new file mode 100644 index 00000000000..9018846e367 --- /dev/null +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/verify/VerifyGetOptionsUsage.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2017, 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 org.graalvm.compiler.phases.verify; + +import java.lang.reflect.MalformedParametersException; +import java.lang.reflect.Method; + +import org.graalvm.compiler.graph.Node; +import org.graalvm.compiler.graph.spi.CanonicalizerTool; +import org.graalvm.compiler.nodes.StructuredGraph; +import org.graalvm.compiler.nodes.java.MethodCallTargetNode; +import org.graalvm.compiler.phases.VerifyPhase; +import org.graalvm.compiler.phases.tiers.PhaseContext; + +import jdk.vm.ci.meta.MetaAccessProvider; +import jdk.vm.ci.meta.ResolvedJavaMethod; +import jdk.vm.ci.meta.ResolvedJavaType; + +/** + * {@link Node#getOptions()} is unsafe for use during canonicalization so try to verify that it + * isn't used when a {@link CanonicalizerTool} is available in the arguments. This is slightly more + * general but since there are several canonical methods with varying signatures this covers more + * cases. + */ +public class VerifyGetOptionsUsage extends VerifyPhase { + static Method lookupMethod(Class klass, String name) { + for (Method m : klass.getDeclaredMethods()) { + if (m.getName().equals(name)) { + return m; + } + } + throw new InternalError(); + } + + @Override + protected boolean verify(StructuredGraph graph, PhaseContext context) { + MetaAccessProvider metaAccess = context.getMetaAccess(); + ResolvedJavaType canonicalizerToolClass = metaAccess.lookupJavaType(CanonicalizerTool.class); + boolean hasTool = false; + try { + for (ResolvedJavaMethod.Parameter parameter : graph.method().getParameters()) { + if (parameter.getType().getName().equals(canonicalizerToolClass.getName())) { + hasTool = true; + break; + } + } + } catch (MalformedParametersException e) { + // Lambdas sometimes have malformed parameters so ignore this. + } + if (hasTool) { + ResolvedJavaMethod getOptionsMethod = metaAccess.lookupJavaMethod(lookupMethod(Node.class, "getOptions")); + for (MethodCallTargetNode t : graph.getNodes(MethodCallTargetNode.TYPE)) { + ResolvedJavaMethod callee = t.targetMethod(); + if (callee.equals(getOptionsMethod)) { + if (hasTool) { + throw new VerificationError("Must use CanonicalizerTool.getOptions() instead of Node.getOptions() in method '%s' of class '%s'.", + graph.method().getName(), graph.method().getDeclaringClass().getName()); + } + } + } + } + + return true; + } + +} diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/verify/VerifyGraphAddUsage.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/verify/VerifyGraphAddUsage.java new file mode 100644 index 00000000000..17d7dbaaaf6 --- /dev/null +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/verify/VerifyGraphAddUsage.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2017, 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 org.graalvm.compiler.phases.verify; + +import jdk.vm.ci.meta.ResolvedJavaMethod; +import jdk.vm.ci.meta.ResolvedJavaType; +import org.graalvm.compiler.debug.GraalError; +import org.graalvm.compiler.graph.Graph; +import org.graalvm.compiler.graph.Node; +import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.Invoke; +import org.graalvm.compiler.nodes.ParameterNode; +import org.graalvm.compiler.nodes.PiNode; +import org.graalvm.compiler.nodes.StructuredGraph; +import org.graalvm.compiler.nodes.ValueNode; +import org.graalvm.compiler.nodes.ValuePhiNode; +import org.graalvm.compiler.nodes.ValueProxyNode; +import org.graalvm.compiler.nodes.java.MethodCallTargetNode; +import org.graalvm.compiler.nodes.java.NewInstanceNode; +import org.graalvm.compiler.nodes.spi.LoweringProvider; +import org.graalvm.compiler.phases.VerifyPhase; +import org.graalvm.compiler.phases.tiers.PhaseContext; +import org.graalvm.util.EconomicSet; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +public class VerifyGraphAddUsage extends VerifyPhase { + private static final Method ADD_OR_UNIQUE; + private static final Method CONSTRUCTOR_NEW_INSTANCE; + private static final EconomicSet> ALLOWED_CLASSES = EconomicSet.create(); + + static { + try { + ADD_OR_UNIQUE = Graph.class.getDeclaredMethod("addOrUnique", Node.class); + CONSTRUCTOR_NEW_INSTANCE = Constructor.class.getDeclaredMethod("newInstance", Object[].class); + } catch (NoSuchMethodException e) { + throw new GraalError(e); + } + + ALLOWED_CLASSES.add(Graph.class); + ALLOWED_CLASSES.add(LoweringProvider.class); + } + + @Override + protected boolean verify(StructuredGraph graph, PhaseContext context) { + boolean allowed = false; + for (Class cls : ALLOWED_CLASSES) { + ResolvedJavaType declaringClass = graph.method().getDeclaringClass(); + if (context.getMetaAccess().lookupJavaType(cls).isAssignableFrom(declaringClass)) { + allowed = true; + } + } + if (!allowed) { + ResolvedJavaMethod addOrUniqueMethod = context.getMetaAccess().lookupJavaMethod(ADD_OR_UNIQUE); + for (MethodCallTargetNode t : graph.getNodes(MethodCallTargetNode.TYPE)) { + ResolvedJavaMethod callee = t.targetMethod(); + if (callee.equals(addOrUniqueMethod)) { + ValueNode nodeArgument = t.arguments().get(1); + EconomicSet seen = EconomicSet.create(); + checkNonFactory(graph, seen, context, nodeArgument); + } + } + } + + return true; + } + + private void checkNonFactory(StructuredGraph graph, EconomicSet seen, PhaseContext context, ValueNode node) { + if (seen.contains(node)) { + return; + } + seen.add(node); + + // Check where the value came from recursively, or if it is allowed. + if (node instanceof ValuePhiNode) { + for (ValueNode input : ((ValuePhiNode) node).values()) { + checkNonFactory(graph, seen, context, input); + } + } else if (node instanceof PiNode) { + checkNonFactory(graph, seen, context, ((PiNode) node).object()); + } else if (node instanceof ParameterNode) { + return; + } else if (node instanceof ConstantNode) { + return; + } else if (node instanceof ValueProxyNode) { + checkNonFactory(graph, seen, context, ((ValueProxyNode) node).value()); + } else if (node instanceof Invoke && ((Invoke) node).callTarget().targetMethod().equals(context.getMetaAccess().lookupJavaMethod(CONSTRUCTOR_NEW_INSTANCE))) { + return; + } else if (!(node instanceof NewInstanceNode)) { + // In all other cases, the argument must be a new instance. + throw new VerificationError("Must add node '%s' with inputs in method '%s' of class '%s'.", + node, graph.method().getName(), graph.method().getDeclaringClass().getName()); + } + } + +} diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/BasicIdealGraphPrinter.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/BasicIdealGraphPrinter.java deleted file mode 100644 index 1e853dfe31c..00000000000 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/BasicIdealGraphPrinter.java +++ /dev/null @@ -1,366 +0,0 @@ -/* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package org.graalvm.compiler.printer; - -import java.io.BufferedOutputStream; -import java.io.OutputStream; -import java.io.PrintStream; -import java.io.UnsupportedEncodingException; -import java.nio.charset.Charset; -import java.util.Map; -import java.util.Map.Entry; - -/** - * Elementary, generic generator of Ideal Graph Visualizer input for use in printers for specific - * data structures. - */ -class BasicIdealGraphPrinter { - - /** - * Edge between two nodes. - */ - protected static class Edge { - - final String from; - final int fromIndex; - final String to; - final int toIndex; - final String label; - - public Edge(String from, int fromIndex, String to, int toIndex, String label) { - assert (from != null && to != null); - this.from = from; - this.fromIndex = fromIndex; - this.to = to; - this.toIndex = toIndex; - this.label = label; - } - - @Override - public int hashCode() { - int h = from.hashCode() ^ to.hashCode(); - h = 3 * h + fromIndex; - h = 5 * h + toIndex; - if (label != null) { - h ^= label.hashCode(); - } - return h; - } - - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } - if (obj instanceof Edge) { - Edge other = (Edge) obj; - return from.equals(other.from) && fromIndex == other.fromIndex && to.equals(other.to) && toIndex == other.toIndex && - (label == other.label || (label != null && label.equals(other.label))); - } - return false; - } - } - - private final PrintStream stream; - - /** - * Creates a new {@link IdealGraphPrinter} that writes to the specified output stream. - */ - protected BasicIdealGraphPrinter(OutputStream stream) { - try { - OutputStream buffered; - if (stream instanceof BufferedOutputStream) { - buffered = stream; - } else { - buffered = new BufferedOutputStream(stream, 256 * 1024); - } - this.stream = new PrintStream(buffered, false, Charset.defaultCharset().name()); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } - } - - /** - * Flushes any buffered output. - */ - protected void flush() { - stream.flush(); - } - - /** - * Starts a new graph document. - */ - protected void begin() { - stream.println(""); - } - - protected void beginGroup() { - stream.println(""); - } - - protected void beginMethod(String name, String shortName, int bci) { - stream.printf(" %n", escape(name), escape(shortName), bci); - } - - protected void beginBytecodes() { - stream.println(" \n"); - } - - protected void printBytecodes(String disassembly) { - beginBytecodes(); - stream.println(disassembly); - endBytecodes(); - } - - protected void endMethod() { - stream.println(" "); - } - - protected void beginGraph(String title) { - stream.printf(" %n", escape(title)); - } - - protected void beginProperties() { - stream.print(""); - } - - protected void printProperty(String name, String value) { - stream.printf("

%s

", escape(name), escape(value)); - } - - protected void endProperties() { - stream.print("
"); - } - - protected void printProperties(Map properties) { - beginProperties(); - for (Entry entry : properties.entrySet()) { - printProperty(entry.getKey(), entry.getValue()); - } - endProperties(); - } - - protected void beginNodes() { - stream.println(" "); - } - - protected void beginNode(String id) { - stream.printf(" ", escape(id)); - } - - protected void endNode() { - stream.println(" "); - } - - protected void printNode(String id, Map properties) { - beginNode(id); - if (properties != null) { - printProperties(properties); - } - endNode(); - } - - protected void endNodes() { - stream.println(" "); - } - - protected void beginEdges() { - stream.println(" "); - } - - protected void printEdge(Edge edge) { - stream.printf(" %n", escape(edge.from), edge.fromIndex, escape(edge.to), edge.toIndex, escape(edge.label)); - } - - protected void endEdges() { - stream.println(" "); - } - - protected void beginControlFlow() { - stream.println(" "); - } - - protected void beginBlock(String name) { - stream.printf(" %n", escape(name)); - } - - protected void beginSuccessors() { - stream.println(" "); - } - - protected void printSuccessor(String name) { - stream.printf(" %n", escape(name)); - } - - protected void endSuccessors() { - stream.println(" "); - } - - protected void beginBlockNodes() { - stream.println(" "); - } - - protected void printBlockNode(String nodeId) { - stream.printf(" %n", escape(nodeId)); - } - - protected void endBlockNodes() { - stream.println(" "); - } - - protected void endBlock() { - stream.println(" "); - } - - protected void endControlFlow() { - stream.println(" "); - } - - protected void endGraph() { - stream.println("
"); - } - - /** - * Ends the current group. - */ - public void endGroup() { - stream.println("
"); - } - - /** - * Finishes the graph document and flushes the output stream. - */ - protected void end() { - stream.println("
"); - flush(); - } - - public void close() { - end(); - stream.close(); - } - - public boolean isValid() { - return !stream.checkError(); - } - - private static String escape(String s) { - StringBuilder str = null; - for (int i = 0; i < s.length(); i++) { - char c = s.charAt(i); - switch (c) { - case '&': - case '<': - case '>': - case '"': - case '\'': - if (str == null) { - str = new StringBuilder(); - str.append(s, 0, i); - } - switch (c) { - case '&': - str.append("&"); - break; - case '<': - str.append("<"); - break; - case '>': - str.append(">"); - break; - case '"': - str.append("""); - break; - case '\'': - str.append("'"); - break; - default: - assert false; - } - break; - case '\u0000': - case '\u0001': - case '\u0002': - case '\u0003': - case '\u0004': - case '\u0005': - case '\u0006': - case '\u0007': - case '\u0008': - case '\u000b': - case '\u000c': - case '\u000e': - case '\u000f': - case '\u0010': - case '\u0011': - case '\u0012': - case '\u0013': - case '\u0014': - case '\u0015': - case '\u0016': - case '\u0017': - case '\u0018': - case '\u0019': - case '\u001a': - case '\u001b': - case '\u001c': - case '\u001d': - case '\u001e': - case '\u001f': - if (str == null) { - str = new StringBuilder(); - str.append(s, 0, i); - } - str.append("'0x").append(Integer.toHexString(c)); - break; - default: - if (str != null) { - str.append(c); - } - break; - } - } - if (str == null) { - return s; - } else { - return str.toString(); - } - } -} diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/BinaryGraphPrinter.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/BinaryGraphPrinter.java index 03ebcce69d6..f40d78c6fc5 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/BinaryGraphPrinter.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/BinaryGraphPrinter.java @@ -26,18 +26,17 @@ import static org.graalvm.compiler.graph.Edges.Type.Inputs; import static org.graalvm.compiler.graph.Edges.Type.Successors; import java.io.IOException; -import java.nio.channels.WritableByteChannel; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Map; -import jdk.vm.ci.meta.ResolvedJavaField; import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; import org.graalvm.compiler.bytecode.Bytecode; import org.graalvm.compiler.core.common.cfg.BlockMap; +import org.graalvm.compiler.debug.DebugContext; import org.graalvm.compiler.debug.DebugOptions; import org.graalvm.compiler.graph.CachedGraph; import org.graalvm.compiler.graph.Edges; @@ -46,6 +45,7 @@ import org.graalvm.compiler.graph.InputEdges; import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.graph.NodeMap; +import org.graalvm.compiler.graph.NodeSourcePosition; import org.graalvm.compiler.nodes.AbstractBeginNode; import org.graalvm.compiler.nodes.AbstractEndNode; import org.graalvm.compiler.nodes.AbstractMergeNode; @@ -55,15 +55,11 @@ import org.graalvm.compiler.nodes.ControlSplitNode; import org.graalvm.compiler.nodes.FixedNode; import org.graalvm.compiler.nodes.PhiNode; import org.graalvm.compiler.nodes.ProxyNode; +import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.VirtualState; import org.graalvm.compiler.nodes.cfg.Block; import org.graalvm.compiler.nodes.cfg.ControlFlowGraph; -import jdk.vm.ci.meta.ResolvedJavaMethod; -import jdk.vm.ci.meta.ResolvedJavaType; -import jdk.vm.ci.meta.Signature; -import org.graalvm.compiler.debug.DebugContext; -import org.graalvm.compiler.graph.NodeSourcePosition; -import org.graalvm.compiler.nodes.StructuredGraph; +import org.graalvm.compiler.nodes.util.JavaConstantFormattable; import org.graalvm.compiler.phases.schedule.SchedulePhase; import org.graalvm.graphio.GraphBlocks; import org.graalvm.graphio.GraphElements; @@ -71,6 +67,11 @@ import org.graalvm.graphio.GraphOutput; import org.graalvm.graphio.GraphStructure; import org.graalvm.graphio.GraphTypes; +import jdk.vm.ci.meta.ResolvedJavaField; +import jdk.vm.ci.meta.ResolvedJavaMethod; +import jdk.vm.ci.meta.ResolvedJavaType; +import jdk.vm.ci.meta.Signature; + public class BinaryGraphPrinter implements GraphStructure, Edges>, GraphBlocks, @@ -79,8 +80,8 @@ public class BinaryGraphPrinter implements private final SnippetReflectionProvider snippetReflection; private final GraphOutput output; - public BinaryGraphPrinter(WritableByteChannel channel, SnippetReflectionProvider snippetReflection) throws IOException { - this.output = GraphOutput.newBuilder(this).blocks(this).elements(this).types(this).build(channel); + public BinaryGraphPrinter(DebugContext ctx, SnippetReflectionProvider snippetReflection) throws IOException { + this.output = ctx.buildOutput(GraphOutput.newBuilder(this).protocolVersion(5, 0).blocks(this).elements(this).types(this)); this.snippetReflection = snippetReflection; } @@ -115,17 +116,24 @@ public class BinaryGraphPrinter implements } } + @Override + public Node node(Object obj) { + return obj instanceof Node ? (Node) obj : null; + } + @Override public NodeClass nodeClass(Object obj) { if (obj instanceof NodeClass) { return (NodeClass) obj; } - if (obj instanceof Node) { - return ((Node) obj).getNodeClass(); - } return null; } + @Override + public NodeClass classForNode(Node node) { + return node.getNodeClass(); + } + @Override public Object nodeClassType(NodeClass node) { return node.getJavaClass(); @@ -257,6 +265,13 @@ public class BinaryGraphPrinter implements } props.put("category", "floating"); } + if (getSnippetReflectionProvider() != null) { + for (Map.Entry prop : props.entrySet()) { + if (prop.getValue() instanceof JavaConstantFormattable) { + props.put(prop.getKey(), ((JavaConstantFormattable) prop.getValue()).format(this)); + } + } + } } private Object getBlockForNode(Node node, NodeMap nodeToBlocks) { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/CFGPrinterObserver.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/CFGPrinterObserver.java index 6b91eb09e17..ac408784cfd 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/CFGPrinterObserver.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/CFGPrinterObserver.java @@ -23,13 +23,13 @@ package org.graalvm.compiler.printer; import static org.graalvm.compiler.debug.DebugOptions.PrintCFG; -import static org.graalvm.compiler.printer.GraalDebugHandlersFactory.createDumpPath; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -153,8 +153,8 @@ public class CFGPrinterObserver implements DebugDumpHandler { if (cfgPrinter == null) { try { - Graph graph = debug.contextLookupTopdown(Graph.class); - cfgFile = createDumpPath(options, graph, "cfg", false).toFile(); + Path dumpFile = debug.getDumpPath(".cfg", false); + cfgFile = dumpFile.toFile(); OutputStream out = new BufferedOutputStream(new FileOutputStream(cfgFile)); cfgPrinter = new CFGPrinter(out); } catch (IOException e) { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/CanonicalStringGraphPrinter.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/CanonicalStringGraphPrinter.java index f062850d20d..147dfd8b44a 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/CanonicalStringGraphPrinter.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/CanonicalStringGraphPrinter.java @@ -26,7 +26,6 @@ import static org.graalvm.compiler.debug.DebugOptions.CanonicalGraphStringsCheck import static org.graalvm.compiler.debug.DebugOptions.CanonicalGraphStringsExcludeVirtuals; import static org.graalvm.compiler.debug.DebugOptions.CanonicalGraphStringsRemoveIdentities; import static org.graalvm.compiler.debug.DebugOptions.PrintCanonicalGraphStringFlavor; -import static org.graalvm.compiler.printer.GraalDebugHandlersFactory.sanitizedFileName; import java.io.BufferedWriter; import java.io.FileWriter; @@ -44,6 +43,7 @@ import java.util.regex.Pattern; import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; import org.graalvm.compiler.core.common.Fields; import org.graalvm.compiler.debug.DebugContext; +import org.graalvm.compiler.debug.PathUtilities; import org.graalvm.compiler.graph.Graph; import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.graph.NodeMap; @@ -260,11 +260,11 @@ public class CanonicalStringGraphPrinter implements GraphPrinter { private StructuredGraph currentGraph; private Path currentDirectory; - private Path getDirectory(StructuredGraph graph) throws IOException { + private Path getDirectory(DebugContext debug, StructuredGraph graph) { if (graph == currentGraph) { return currentDirectory; } - currentDirectory = GraalDebugHandlersFactory.createDumpPath(graph.getOptions(), graph, "graph-strings", true); + currentDirectory = debug.getDumpPath(".graph-strings", true); currentGraph = graph; return currentDirectory; } @@ -274,9 +274,9 @@ public class CanonicalStringGraphPrinter implements GraphPrinter { if (graph instanceof StructuredGraph) { OptionValues options = graph.getOptions(); StructuredGraph structuredGraph = (StructuredGraph) graph; - Path outDirectory = getDirectory(structuredGraph); + Path outDirectory = getDirectory(debug, structuredGraph); String title = String.format("%03d-%s.txt", id, String.format(format, simplifyClassArgs(args))); - Path filePath = outDirectory.resolve(sanitizedFileName(title)); + Path filePath = outDirectory.resolve(PathUtilities.sanitizeFileName(title)); try (PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(filePath.toFile())))) { switch (PrintCanonicalGraphStringFlavor.getValue(options)) { case 1: diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/GraalDebugHandlersFactory.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/GraalDebugHandlersFactory.java index 01ade572f42..b788848ef22 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/GraalDebugHandlersFactory.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/GraalDebugHandlersFactory.java @@ -22,44 +22,18 @@ */ package org.graalvm.compiler.printer; -import static org.graalvm.compiler.debug.DebugOptions.PrintBinaryGraphPort; -import static org.graalvm.compiler.debug.DebugOptions.PrintBinaryGraphs; -import static org.graalvm.compiler.debug.DebugOptions.PrintGraphHost; -import static org.graalvm.compiler.debug.DebugOptions.PrintXmlGraphPort; -import static org.graalvm.compiler.debug.DebugOptions.ShowDumpFiles; - -import java.io.File; -import java.io.IOException; -import java.io.InterruptedIOException; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.nio.channels.ClosedByInterruptException; -import java.nio.channels.FileChannel; -import java.nio.channels.SocketChannel; -import java.nio.file.FileAlreadyExistsException; -import java.nio.file.Files; -import java.nio.file.InvalidPathException; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; -import org.graalvm.compiler.core.common.CompilationIdentifier; -import org.graalvm.compiler.debug.Assertions; import org.graalvm.compiler.debug.DebugContext; import org.graalvm.compiler.debug.DebugDumpHandler; import org.graalvm.compiler.debug.DebugHandler; import org.graalvm.compiler.debug.DebugHandlersFactory; import org.graalvm.compiler.debug.DebugOptions; import org.graalvm.compiler.debug.TTY; -import org.graalvm.compiler.debug.PathUtilities; -import org.graalvm.compiler.graph.Graph; import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.nodeinfo.Verbosity; -import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.util.GraphUtil; import org.graalvm.compiler.options.OptionValues; import org.graalvm.compiler.serviceprovider.ServiceProvider; @@ -80,17 +54,13 @@ public class GraalDebugHandlersFactory implements DebugHandlersFactory { @Override public List createHandlers(OptionValues options) { List handlers = new ArrayList<>(); - if (DebugOptions.PrintGraphFile.getValue(options)) { - handlers.add(new GraphPrinterDumpHandler((graph) -> createFilePrinter(graph, options, snippetReflection))); - } else { - handlers.add(new GraphPrinterDumpHandler((graph) -> createNetworkPrinter(graph, options, snippetReflection))); - } + handlers.add(new GraphPrinterDumpHandler((debug, graph) -> new BinaryGraphPrinter(debug, snippetReflection))); if (DebugOptions.PrintCanonicalGraphStrings.getValue(options)) { - handlers.add(new GraphPrinterDumpHandler((graph) -> createStringPrinter(snippetReflection))); + handlers.add(new GraphPrinterDumpHandler((debug, graph) -> createStringPrinter(snippetReflection))); } handlers.add(new NodeDumper()); if (DebugOptions.PrintCFG.getValue(options) || DebugOptions.PrintBackendCFG.getValue(options)) { - if (DebugOptions.PrintBinaryGraphs.getValue(options) && DebugOptions.PrintCFG.getValue(options)) { + if (DebugOptions.PrintCFG.getValue(options)) { TTY.out.println("Complete C1Visualizer dumping slows down PrintBinaryGraphs: use -Dgraal.PrintCFG=false to disable it"); } handlers.add(new CFGPrinterObserver()); @@ -119,162 +89,4 @@ public class GraalDebugHandlersFactory implements DebugHandlersFactory { return new CanonicalStringGraphPrinter(snippetReflection); } - public static String sanitizedFileName(String n) { - /* - * First ensure that the name does not contain the directory separator (which would be - * considered a valid path). - */ - String name = n.replace(File.separatorChar, '_'); - - try { - Paths.get(name); - return name; - } catch (InvalidPathException e) { - // fall through - } - StringBuilder buf = new StringBuilder(name.length()); - for (int i = 0; i < name.length(); i++) { - char c = name.charAt(i); - try { - Paths.get(String.valueOf(c)); - } catch (InvalidPathException e) { - buf.append('_'); - } - buf.append(c); - } - return buf.toString(); - } - - private static GraphPrinter createNetworkPrinter(Graph graph, OptionValues options, SnippetReflectionProvider snippetReflection) throws IOException { - String host = PrintGraphHost.getValue(options); - int port = PrintBinaryGraphs.getValue(options) ? PrintBinaryGraphPort.getValue(options) : PrintXmlGraphPort.getValue(options); - try { - GraphPrinter printer; - if (DebugOptions.PrintBinaryGraphs.getValue(options)) { - printer = new BinaryGraphPrinter(SocketChannel.open(new InetSocketAddress(host, port)), snippetReflection); - } else { - printer = new IdealGraphPrinter(new Socket(host, port).getOutputStream(), true, snippetReflection); - } - TTY.println("Connected to the IGV on %s:%d", host, port); - return printer; - } catch (ClosedByInterruptException | InterruptedIOException e) { - /* - * Interrupts should not count as errors because they may be caused by a cancelled Graal - * compilation. ClosedByInterruptException occurs if the SocketChannel could not be - * opened. InterruptedIOException occurs if new Socket(..) was interrupted. - */ - return null; - } catch (IOException e) { - if (!DebugOptions.PrintGraphFile.hasBeenSet(options)) { - return createFilePrinter(graph, options, snippetReflection); - } else { - throw new IOException(String.format("Could not connect to the IGV on %s:%d", host, port), e); - } - } - } - - private static final AtomicInteger unknownCompilationId = new AtomicInteger(); - - /** - * Creates a new file or directory for dumping based on a given graph and a file extension. - * - * @param graph a base path name is derived from {@code graph} - * @param extension a suffix which if non-null and non-empty added to the end of the returned - * path separated by a {@code "."} - * @param createDirectory specifies if this is a request to create a directory instead of a file - * @return the created directory or file - * @throws IOException if there was an error creating the directory or file - */ - static Path createDumpPath(OptionValues options, Graph graph, String extension, boolean createDirectory) throws IOException { - CompilationIdentifier compilationId = CompilationIdentifier.INVALID_COMPILATION_ID; - String id = null; - String label = null; - if (graph instanceof StructuredGraph) { - StructuredGraph sgraph = (StructuredGraph) graph; - label = getGraphName(sgraph); - compilationId = sgraph.compilationId(); - if (compilationId == CompilationIdentifier.INVALID_COMPILATION_ID) { - id = graph.getClass().getSimpleName() + "-" + sgraph.graphId(); - } else { - id = compilationId.toString(CompilationIdentifier.Verbosity.ID); - } - } else { - label = graph == null ? null : graph.name != null ? graph.name : graph.toString(); - id = "UnknownCompilation-" + unknownCompilationId.incrementAndGet(); - } - String ext = PathUtilities.formatExtension(extension); - Path result = createUnique(DebugOptions.getDumpDirectory(options), id, label, ext, createDirectory); - if (ShowDumpFiles.getValue(options) || Assertions.assertionsEnabled()) { - TTY.println("Dumping debug output to %s", result.toAbsolutePath().toString()); - } - return result; - } - - /** - * A maximum file name length supported by most file systems. There is no platform independent - * way to get this in Java. - */ - private static final int MAX_FILE_NAME_LENGTH = 255; - - private static final String ELLIPSIS = "..."; - - private static Path createUnique(Path dumpDir, String id, String label, String ext, boolean createDirectory) throws IOException { - String timestamp = ""; - for (;;) { - int fileNameLengthWithoutLabel = timestamp.length() + ext.length() + id.length() + "[]".length(); - int labelLengthLimit = MAX_FILE_NAME_LENGTH - fileNameLengthWithoutLabel; - String fileName; - if (labelLengthLimit < ELLIPSIS.length()) { - // This means `id` is very long - String suffix = timestamp + ext; - int idLengthLimit = Math.min(MAX_FILE_NAME_LENGTH - suffix.length(), id.length()); - fileName = sanitizedFileName(id.substring(0, idLengthLimit) + suffix); - } else { - if (label == null) { - fileName = sanitizedFileName(id + timestamp + ext); - } else { - String adjustedLabel = label; - if (label.length() > labelLengthLimit) { - adjustedLabel = label.substring(0, labelLengthLimit - ELLIPSIS.length()) + ELLIPSIS; - } - fileName = sanitizedFileName(id + '[' + adjustedLabel + ']' + timestamp + ext); - } - } - Path result = dumpDir.resolve(fileName); - try { - if (createDirectory) { - return Files.createDirectory(result); - } else { - return Files.createFile(result); - } - } catch (FileAlreadyExistsException e) { - timestamp = "_" + Long.toString(System.currentTimeMillis()); - } - } - } - - private static String getGraphName(StructuredGraph graph) { - if (graph.name != null) { - return graph.name; - } else if (graph.method() != null) { - return graph.method().format("%h.%n(%p)").replace(" ", ""); - } else { - return graph.toString(); - } - } - - private static GraphPrinter createFilePrinter(Graph graph, OptionValues options, SnippetReflectionProvider snippetReflection) throws IOException { - Path path = createDumpPath(options, graph, PrintBinaryGraphs.getValue(options) ? "bgv" : "gv.xml", false); - try { - GraphPrinter printer; - if (DebugOptions.PrintBinaryGraphs.getValue(options)) { - printer = new BinaryGraphPrinter(FileChannel.open(path, StandardOpenOption.WRITE), snippetReflection); - } else { - printer = new IdealGraphPrinter(Files.newOutputStream(path), true, snippetReflection); - } - return printer; - } catch (IOException e) { - throw new IOException(String.format("Failed to open %s to dump IGV graphs", path), e); - } - } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/GraphPrinter.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/GraphPrinter.java index 194e86e53e6..979dd3e85ee 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/GraphPrinter.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/GraphPrinter.java @@ -35,6 +35,7 @@ import org.graalvm.compiler.debug.DebugContext.Scope; import org.graalvm.compiler.graph.Graph; import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.StructuredGraph; +import org.graalvm.compiler.nodes.util.JavaConstantFormatter; import org.graalvm.compiler.phases.schedule.SchedulePhase; import org.graalvm.compiler.serviceprovider.JDK9Method; @@ -46,7 +47,7 @@ import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.runtime.JVMCI; import jdk.vm.ci.services.Services; -interface GraphPrinter extends Closeable { +interface GraphPrinter extends Closeable, JavaConstantFormatter { /** * Starts a new group of graphs with the given name, short name and method byte code index (BCI) @@ -116,30 +117,44 @@ interface GraphPrinter extends Closeable { return true; } } + if (c.getClassLoader() == GraphPrinter.class.getClassLoader()) { + return true; + } return false; } + /** + * Use the real {@link Object#toString()} method for {@link JavaConstant JavaConstants} that are + * wrapping trusted types, other just return the results of {@link JavaConstant#toString()}. + */ + @Override + default String format(JavaConstant constant) { + SnippetReflectionProvider snippetReflection = getSnippetReflectionProvider(); + if (snippetReflection != null) { + if (constant.getJavaKind() == JavaKind.Object) { + Object obj = snippetReflection.asObject(Object.class, constant); + if (obj != null) { + return GraphPrinter.constantToString(obj); + } + } + } + return constant.toString(); + } + /** * Sets or updates the {@code "rawvalue"} and {@code "toString"} properties in {@code props} for * {@code cn} if it's a boxed Object value and {@code snippetReflection} can access the raw * value. */ default void updateStringPropertiesForConstant(Map props, ConstantNode cn) { - SnippetReflectionProvider snippetReflection = getSnippetReflectionProvider(); - if (snippetReflection != null && cn.getValue() instanceof JavaConstant) { - JavaConstant constant = (JavaConstant) cn.getValue(); - if (constant.getJavaKind() == JavaKind.Object) { - Object obj = snippetReflection.asObject(Object.class, constant); - if (obj != null) { - String toString = GraphPrinter.constantToString(obj); - String rawvalue = GraphPrinter.truncate(toString); - // Overwrite the value inserted by - // ConstantNode.getDebugProperties() - props.put("rawvalue", rawvalue); - if (!rawvalue.equals(toString)) { - props.put("toString", toString); - } - } + if (cn.isJavaConstant() && cn.getStackKind().isObject()) { + String toString = format(cn.asJavaConstant()); + String rawvalue = GraphPrinter.truncate(toString); + // Overwrite the value inserted by + // ConstantNode.getDebugProperties() + props.put("rawvalue", rawvalue); + if (!rawvalue.equals(toString)) { + props.put("toString", toString); } } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/GraphPrinterDumpHandler.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/GraphPrinterDumpHandler.java index e1bb20c91b4..a875ca30b01 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/GraphPrinterDumpHandler.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/GraphPrinterDumpHandler.java @@ -52,8 +52,8 @@ import jdk.vm.ci.meta.ResolvedJavaMethod; //JaCoCo Exclude /** - * Observes compilation events and uses {@link IdealGraphPrinter} to generate a graph representation - * that can be inspected with the Graph Visualizer. + * Observes compilation events and uses {@link BinaryGraphPrinter} to generate a graph + * representation that can be inspected with the Graph Visualizer. */ public class GraphPrinterDumpHandler implements DebugDumpHandler { @@ -69,7 +69,7 @@ public class GraphPrinterDumpHandler implements DebugDumpHandler { @FunctionalInterface public interface GraphPrinterSupplier { - GraphPrinter get(Graph graph) throws IOException; + GraphPrinter get(DebugContext ctx, Graph graph) throws IOException; } /** @@ -93,7 +93,7 @@ public class GraphPrinterDumpHandler implements DebugDumpHandler { } } - private void ensureInitialized(Graph graph) { + private void ensureInitialized(DebugContext ctx, Graph graph) { if (printer == null) { if (failuresCount >= FAILURE_LIMIT) { return; @@ -102,7 +102,7 @@ public class GraphPrinterDumpHandler implements DebugDumpHandler { inlineContextMap = new WeakHashMap<>(); DebugContext debug = graph.getDebug(); try { - printer = printerSupplier.get(graph); + printer = printerSupplier.get(ctx, graph); } catch (IOException e) { handleException(debug, e); } @@ -123,7 +123,7 @@ public class GraphPrinterDumpHandler implements DebugDumpHandler { OptionValues options = debug.getOptions(); if (object instanceof Graph && DebugOptions.PrintGraph.getValue(options)) { final Graph graph = (Graph) object; - ensureInitialized(graph); + ensureInitialized(debug, graph); if (printer == null) { return; } @@ -168,11 +168,13 @@ public class GraphPrinterDumpHandler implements DebugDumpHandler { // Save inline context for next dump. previousInlineContext = inlineContext; + // Capture before creating the sandbox + String currentScopeName = debug.getCurrentScopeName(); try (DebugContext.Scope s = debug.sandbox("PrintingGraph", null)) { // Finally, output the graph. Map properties = new HashMap<>(); properties.put("graph", graph.toString()); - properties.put("scope", debug.getCurrentScopeName()); + properties.put("scope", currentScopeName); if (graph instanceof StructuredGraph) { properties.put("compilationIdentifier", ((StructuredGraph) graph).compilationId()); try { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/IdealGraphPrinter.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/IdealGraphPrinter.java deleted file mode 100644 index 493e62df1a5..00000000000 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/IdealGraphPrinter.java +++ /dev/null @@ -1,356 +0,0 @@ -/* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package org.graalvm.compiler.printer; - -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; -import org.graalvm.compiler.bytecode.BytecodeDisassembler; -import org.graalvm.compiler.debug.DebugContext; -import org.graalvm.compiler.debug.DebugOptions; -import org.graalvm.compiler.graph.Graph; -import org.graalvm.compiler.graph.Node; -import org.graalvm.compiler.graph.NodeMap; -import org.graalvm.compiler.graph.Position; -import org.graalvm.compiler.nodeinfo.Verbosity; -import org.graalvm.compiler.nodes.AbstractMergeNode; -import org.graalvm.compiler.nodes.BeginNode; -import org.graalvm.compiler.nodes.ConstantNode; -import org.graalvm.compiler.nodes.EndNode; -import org.graalvm.compiler.nodes.ParameterNode; -import org.graalvm.compiler.nodes.PhiNode; -import org.graalvm.compiler.nodes.StateSplit; -import org.graalvm.compiler.nodes.StructuredGraph; -import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult; -import org.graalvm.compiler.nodes.cfg.Block; -import org.graalvm.compiler.nodes.cfg.ControlFlowGraph; -import org.graalvm.compiler.options.OptionValues; -import org.graalvm.compiler.phases.schedule.SchedulePhase; -import org.graalvm.util.EconomicSet; -import org.graalvm.util.Equivalence; - -import jdk.vm.ci.meta.ResolvedJavaMethod; - -/** - * Generates a representation of {@link Graph Graphs} that can be visualized and inspected with the - * Ideal Graph Visualizer. - */ -public class IdealGraphPrinter extends BasicIdealGraphPrinter implements GraphPrinter { - - private final boolean tryToSchedule; - private final SnippetReflectionProvider snippetReflection; - - /** - * Creates a new {@link IdealGraphPrinter} that writes to the specified output stream. - * - * @param tryToSchedule If false, no scheduling is done, which avoids exceptions for - * non-schedulable graphs. - */ - public IdealGraphPrinter(OutputStream stream, boolean tryToSchedule, SnippetReflectionProvider snippetReflection) { - super(stream); - this.snippetReflection = snippetReflection; - this.begin(); - this.tryToSchedule = tryToSchedule; - } - - @Override - public SnippetReflectionProvider getSnippetReflectionProvider() { - return snippetReflection; - } - - /** - * Starts a new group of graphs with the given name, short name and method byte code index (BCI) - * as properties. - */ - @Override - public void beginGroup(DebugContext debug, String name, String shortName, ResolvedJavaMethod method, int bci, Map properties) { - beginGroup(); - beginProperties(); - printProperty("name", name); - if (properties != null) { - for (Entry entry : properties.entrySet()) { - printProperty(entry.getKey().toString(), entry.getValue().toString()); - } - } - endProperties(); - beginMethod(name, shortName, bci); - if (method != null && method.getCode() != null) { - printBytecodes(new BytecodeDisassembler(false).disassemble(method)); - } - endMethod(); - } - - /** - * Prints an entire {@link Graph} with the specified title, optionally using short names for - * nodes. - */ - @Override - public void print(DebugContext debug, Graph graph, Map properties, int id, String format, Object... args) { - String title = id + ": " + String.format(format, simplifyClassArgs(args)); - beginGraph(title); - EconomicSet noBlockNodes = EconomicSet.create(Equivalence.IDENTITY); - ScheduleResult schedule = null; - if (graph instanceof StructuredGraph) { - StructuredGraph structuredGraph = (StructuredGraph) graph; - schedule = structuredGraph.getLastSchedule(); - if (schedule == null && tryToSchedule) { - OptionValues options = graph.getOptions(); - if (DebugOptions.PrintGraphWithSchedule.getValue(options)) { - try { - SchedulePhase schedulePhase = new SchedulePhase(options); - schedulePhase.apply(structuredGraph); - schedule = structuredGraph.getLastSchedule(); - } catch (Throwable t) { - } - } - } - } - ControlFlowGraph cfg = schedule == null ? null : schedule.getCFG(); - - if (properties != null) { - beginProperties(); - for (Entry entry : properties.entrySet()) { - printProperty(entry.getKey().toString(), entry.getValue().toString()); - } - endProperties(); - } - - beginNodes(); - List edges = printNodes(graph, cfg == null ? null : cfg.getNodeToBlock(), noBlockNodes); - endNodes(); - - beginEdges(); - for (Edge edge : edges) { - printEdge(edge); - } - endEdges(); - - if (cfg != null && cfg.getBlocks() != null) { - beginControlFlow(); - for (Block block : cfg.getBlocks()) { - printBlock(graph, block, cfg.getNodeToBlock()); - } - printNoBlock(noBlockNodes); - endControlFlow(); - } - - endGraph(); - flush(); - } - - private List printNodes(Graph graph, NodeMap nodeToBlock, EconomicSet noBlockNodes) { - ArrayList edges = new ArrayList<>(); - - NodeMap>> colors = graph.createNodeMap(); - NodeMap>> colorsToString = graph.createNodeMap(); - NodeMap> bits = graph.createNodeMap(); - - for (Node node : graph.getNodes()) { - - beginNode(node.toString(Verbosity.Id)); - beginProperties(); - printProperty("idx", node.toString(Verbosity.Id)); - - Map props = node.getDebugProperties(); - if (!props.containsKey("name") || props.get("name").toString().trim().length() == 0) { - String name = node.toString(Verbosity.Name); - printProperty("name", name); - } - printProperty("class", node.getClass().getSimpleName()); - - Block block = nodeToBlock == null || nodeToBlock.isNew(node) ? null : nodeToBlock.get(node); - if (block != null) { - printProperty("block", Integer.toString(block.getId())); - // if (!(node instanceof PhiNode || node instanceof FrameState || node instanceof - // ParameterNode) && !block.nodes().contains(node)) { - // printProperty("notInOwnBlock", "true"); - // } - } else { - printProperty("block", "noBlock"); - noBlockNodes.add(node); - } - - Set> nodeColors = colors.get(node); - if (nodeColors != null) { - for (Entry color : nodeColors) { - String name = color.getKey(); - Integer value = color.getValue(); - printProperty(name, Integer.toString(value)); - } - } - Set> nodeColorStrings = colorsToString.get(node); - if (nodeColorStrings != null) { - for (Entry color : nodeColorStrings) { - String name = color.getKey(); - String value = color.getValue(); - printProperty(name, value); - } - } - Set nodeBits = bits.get(node); - if (nodeBits != null) { - for (String bit : nodeBits) { - printProperty(bit, "true"); - } - } - if (node instanceof BeginNode) { - printProperty("shortName", "B"); - } else if (node.getClass() == EndNode.class) { - printProperty("shortName", "E"); - } else if (node instanceof ConstantNode) { - ConstantNode cn = (ConstantNode) node; - updateStringPropertiesForConstant(props, cn); - } - if (node.predecessor() != null) { - printProperty("hasPredecessor", "true"); - } - - try { - printProperty("NodeCost-Size", node.estimatedNodeSize().toString()); - printProperty("NodeCost-Cycles", node.estimatedNodeCycles().toString()); - } catch (Throwable t) { - props.put("node-cost-exception", t.getMessage()); - } - - for (Entry entry : props.entrySet()) { - String key = entry.getKey().toString(); - Object value = entry.getValue(); - String valueString; - if (value == null) { - valueString = "null"; - } else { - Class type = value.getClass(); - if (type.isArray()) { - if (!type.getComponentType().isPrimitive()) { - valueString = Arrays.toString((Object[]) value); - } else if (type.getComponentType() == Integer.TYPE) { - valueString = Arrays.toString((int[]) value); - } else if (type.getComponentType() == Double.TYPE) { - valueString = Arrays.toString((double[]) value); - } else { - valueString = toString(); - } - } else { - valueString = value.toString(); - } - } - printProperty(key, valueString); - } - - endProperties(); - endNode(); - - // successors - int fromIndex = 0; - for (Position position : node.successorPositions()) { - Node successor = position.get(node); - if (successor != null) { - edges.add(new Edge(node.toString(Verbosity.Id), fromIndex, successor.toString(Verbosity.Id), 0, position.getName())); - } - fromIndex++; - } - - // inputs - int toIndex = 1; - for (Position position : node.inputPositions()) { - Node input = position.get(node); - if (input != null) { - edges.add(new Edge(input.toString(Verbosity.Id), input.successors().count(), node.toString(Verbosity.Id), toIndex, position.getName())); - } - toIndex++; - } - } - - return edges; - } - - private void printBlock(Graph graph, Block block, NodeMap nodeToBlock) { - beginBlock(Integer.toString(block.getId())); - beginSuccessors(); - for (Block sux : block.getSuccessors()) { - if (sux != null) { - printSuccessor(Integer.toString(sux.getId())); - } - } - endSuccessors(); - beginBlockNodes(); - - EconomicSet nodes = EconomicSet.create(Equivalence.IDENTITY); - - if (nodeToBlock != null) { - for (Node n : graph.getNodes()) { - Block blk = nodeToBlock.isNew(n) ? null : nodeToBlock.get(n); - if (blk == block) { - nodes.add(n); - } - } - } - - if (nodes.size() > 0) { - // if this is the first block: add all locals to this block - if (block.getBeginNode() == ((StructuredGraph) graph).start()) { - for (Node node : graph.getNodes()) { - if (node instanceof ParameterNode) { - nodes.add(node); - } - } - } - - EconomicSet snapshot = EconomicSet.create(Equivalence.IDENTITY, nodes); - // add all framestates and phis to their blocks - for (Node node : snapshot) { - if (node instanceof StateSplit && ((StateSplit) node).stateAfter() != null) { - nodes.add(((StateSplit) node).stateAfter()); - } - if (node instanceof AbstractMergeNode) { - for (PhiNode phi : ((AbstractMergeNode) node).phis()) { - nodes.add(phi); - } - } - } - - for (Node node : nodes) { - printBlockNode(node.toString(Verbosity.Id)); - } - } - endBlockNodes(); - endBlock(); - } - - private void printNoBlock(EconomicSet noBlockNodes) { - if (!noBlockNodes.isEmpty()) { - beginBlock("noBlock"); - beginBlockNodes(); - for (Node node : noBlockNodes) { - printBlockNode(node.toString(Verbosity.Id)); - } - endBlockNodes(); - endBlock(); - } - } -} diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64StringIndexOfNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64StringIndexOfNode.java index 87dedbd92e8..67e858a0cdb 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64StringIndexOfNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64StringIndexOfNode.java @@ -22,13 +22,13 @@ */ package org.graalvm.compiler.replacements.amd64; -import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_256; import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_64; import org.graalvm.compiler.core.common.type.StampFactory; import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.graph.NodeInputList; import org.graalvm.compiler.nodeinfo.InputType; +import org.graalvm.compiler.nodeinfo.NodeCycles; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.FixedWithNextNode; import org.graalvm.compiler.nodes.NamedLocationIdentity; @@ -44,7 +44,7 @@ import org.graalvm.word.Pointer; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.Value; -@NodeInfo(size = SIZE_64, cycles = CYCLES_256) +@NodeInfo(size = SIZE_64, cycles = NodeCycles.CYCLES_UNKNOWN) public class AMD64StringIndexOfNode extends FixedWithNextNode implements LIRLowerable, MemoryAccess { public static final NodeClass TYPE = NodeClass.create(AMD64StringIndexOfNode.class); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/ConstantBindingParameterPlugin.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/ConstantBindingParameterPlugin.java index 9a1fecea546..09622018bed 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/ConstantBindingParameterPlugin.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/ConstantBindingParameterPlugin.java @@ -63,7 +63,7 @@ public class ConstantBindingParameterPlugin implements ParameterPlugin { * This is a node from another graph, so copy over extra state into a new * ConstantNode. */ - constantNode = ConstantNode.forConstant(stamp.getTrustedStamp(), otherCon.asConstant(), otherCon.getStableDimension(), otherCon.isDefaultStable(), metaAccess); + constantNode = ConstantNode.forConstant(stamp.getTrustedStamp(), otherCon.getValue(), otherCon.getStableDimension(), otherCon.isDefaultStable(), metaAccess); } else { constantNode = otherCon; } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/DefaultJavaLoweringProvider.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/DefaultJavaLoweringProvider.java index 0c8f8ba2859..dd0746320f3 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/DefaultJavaLoweringProvider.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/DefaultJavaLoweringProvider.java @@ -33,6 +33,7 @@ import static org.graalvm.compiler.nodes.NamedLocationIdentity.ARRAY_LENGTH_LOCA import static org.graalvm.compiler.nodes.java.ArrayLengthNode.readArrayLength; import static org.graalvm.compiler.nodes.util.GraphUtil.skipPiWhileNonNull; +import java.nio.ByteOrder; import java.util.ArrayList; import java.util.BitSet; import java.util.List; @@ -43,6 +44,7 @@ import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor; import org.graalvm.compiler.core.common.spi.ForeignCallsProvider; import org.graalvm.compiler.core.common.type.IntegerStamp; +import org.graalvm.compiler.core.common.type.ObjectStamp; import org.graalvm.compiler.core.common.type.Stamp; import org.graalvm.compiler.core.common.type.StampFactory; import org.graalvm.compiler.core.common.type.TypeReference; @@ -50,6 +52,7 @@ import org.graalvm.compiler.debug.DebugHandlersFactory; import org.graalvm.compiler.debug.GraalError; import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.nodeinfo.InputType; +import org.graalvm.compiler.nodes.CompressionNode.CompressionOp; import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.FieldLocationIdentity; import org.graalvm.compiler.nodes.FixedNode; @@ -69,6 +72,7 @@ import org.graalvm.compiler.nodes.calc.NarrowNode; import org.graalvm.compiler.nodes.calc.RightShiftNode; import org.graalvm.compiler.nodes.calc.SignExtendNode; import org.graalvm.compiler.nodes.calc.SubNode; +import org.graalvm.compiler.nodes.calc.UnpackEndianHalfNode; import org.graalvm.compiler.nodes.calc.ZeroExtendNode; import org.graalvm.compiler.nodes.debug.VerifyHeapNode; import org.graalvm.compiler.nodes.extended.BoxNode; @@ -150,14 +154,16 @@ public abstract class DefaultJavaLoweringProvider implements LoweringProvider { protected final MetaAccessProvider metaAccess; protected final ForeignCallsProvider foreignCalls; protected final TargetDescription target; + private final boolean useCompressedOops; private BoxingSnippets.Templates boxingSnippets; private ConstantStringIndexOfSnippets.Templates indexOfSnippets; - public DefaultJavaLoweringProvider(MetaAccessProvider metaAccess, ForeignCallsProvider foreignCalls, TargetDescription target) { + public DefaultJavaLoweringProvider(MetaAccessProvider metaAccess, ForeignCallsProvider foreignCalls, TargetDescription target, boolean useCompressedOops) { this.metaAccess = metaAccess; this.foreignCalls = foreignCalls; this.target = target; + this.useCompressedOops = useCompressedOops; } public void initialize(OptionValues options, Iterable factories, SnippetCounter.Group.Factory factory, Providers providers, SnippetReflectionProvider snippetReflection) { @@ -218,11 +224,18 @@ public abstract class DefaultJavaLoweringProvider implements LoweringProvider { lowerBinaryMath((BinaryMathIntrinsicNode) n, tool); } else if (n instanceof StringIndexOfNode) { lowerIndexOf((StringIndexOfNode) n); + } else if (n instanceof UnpackEndianHalfNode) { + lowerSecondHalf((UnpackEndianHalfNode) n); } else { throw GraalError.shouldNotReachHere("Node implementing Lowerable not handled: " + n); } } + private void lowerSecondHalf(UnpackEndianHalfNode n) { + ByteOrder byteOrder = target.arch.getByteOrder(); + n.lower(byteOrder); + } + private void lowerIndexOf(StringIndexOfNode n) { if (n.getArgument(3).isConstant()) { SnippetLowering lowering = new SnippetLowering() { @@ -326,19 +339,21 @@ public abstract class DefaultJavaLoweringProvider implements LoweringProvider { } } + protected abstract JavaKind getStorageKind(ResolvedJavaField field); + protected void lowerLoadFieldNode(LoadFieldNode loadField, LoweringTool tool) { assert loadField.getStackKind() != JavaKind.Illegal; StructuredGraph graph = loadField.graph(); ResolvedJavaField field = loadField.field(); ValueNode object = loadField.isStatic() ? staticFieldBase(graph, field) : loadField.object(); object = createNullCheckedValue(object, loadField, tool); - Stamp loadStamp = loadStamp(loadField.stamp(), field.getJavaKind()); + Stamp loadStamp = loadStamp(loadField.stamp(), getStorageKind(field)); AddressNode address = createFieldAddress(graph, object, field); assert address != null : "Field that is loaded must not be eliminated: " + field.getDeclaringClass().toJavaName(true) + "." + field.getName(); ReadNode memoryRead = graph.add(new ReadNode(address, fieldLocationIdentity(field), loadStamp, fieldLoadBarrierType(field))); - ValueNode readValue = implicitLoadConvert(graph, field.getJavaKind(), memoryRead); + ValueNode readValue = implicitLoadConvert(graph, getStorageKind(field), memoryRead); loadField.replaceAtUsages(readValue); graph.replaceFixed(loadField, memoryRead); @@ -355,7 +370,7 @@ public abstract class DefaultJavaLoweringProvider implements LoweringProvider { ResolvedJavaField field = storeField.field(); ValueNode object = storeField.isStatic() ? staticFieldBase(graph, field) : storeField.object(); object = createNullCheckedValue(object, storeField, tool); - ValueNode value = implicitStoreConvert(graph, storeField.field().getJavaKind(), storeField.value()); + ValueNode value = implicitStoreConvert(graph, getStorageKind(storeField.field()), storeField.value()); AddressNode address = createFieldAddress(graph, object, field); assert address != null; @@ -651,9 +666,7 @@ public abstract class DefaultJavaLoweringProvider implements LoweringProvider { protected void lowerJavaWriteNode(JavaWriteNode write) { StructuredGraph graph = write.graph(); - JavaKind valueKind = write.getWriteKind(); - ValueNode value = implicitStoreConvert(graph, valueKind, write.value(), write.isCompressible()); - + ValueNode value = implicitStoreConvert(graph, write.getWriteKind(), write.value(), write.isCompressible()); WriteNode memoryWrite = graph.add(new WriteNode(write.getAddress(), write.getLocationIdentity(), value, write.getBarrierType())); memoryWrite.setStateAfter(write.stateAfter()); graph.replaceFixedWithFixed(write, memoryWrite); @@ -918,10 +931,20 @@ public abstract class DefaultJavaLoweringProvider implements LoweringProvider { return loadStamp(stamp, kind, true); } + private boolean useCompressedOops(JavaKind kind, boolean compressible) { + return kind == JavaKind.Object && compressible && useCompressedOops; + } + + protected abstract Stamp loadCompressedStamp(ObjectStamp stamp); + /** * @param compressible whether the stamp should be compressible */ protected Stamp loadStamp(Stamp stamp, JavaKind kind, boolean compressible) { + if (useCompressedOops(kind, compressible)) { + return loadCompressedStamp((ObjectStamp) stamp); + } + switch (kind) { case Boolean: case Byte: @@ -949,10 +972,16 @@ public abstract class DefaultJavaLoweringProvider implements LoweringProvider { return ret; } + protected abstract ValueNode newCompressionNode(CompressionOp op, ValueNode value); + /** - * @param compressible whether the covert should be compressible + * @param compressible whether the convert should be compressible */ protected ValueNode implicitLoadConvert(JavaKind kind, ValueNode value, boolean compressible) { + if (useCompressedOops(kind, compressible)) { + return newCompressionNode(CompressionOp.Uncompress, value); + } + switch (kind) { case Byte: case Short: @@ -984,6 +1013,10 @@ public abstract class DefaultJavaLoweringProvider implements LoweringProvider { * @param compressible whether the covert should be compressible */ protected ValueNode implicitStoreConvert(JavaKind kind, ValueNode value, boolean compressible) { + if (useCompressedOops(kind, compressible)) { + return newCompressionNode(CompressionOp.Compress, value); + } + switch (kind) { case Boolean: case Byte: @@ -1011,9 +1044,8 @@ public abstract class DefaultJavaLoweringProvider implements LoweringProvider { LogicNode boundsCheck = IntegerBelowNode.create(n.index(), arrayLength); if (boundsCheck.isTautology()) { return null; - } else { - return tool.createGuard(n, graph.addOrUniqueWithInputs(boundsCheck), BoundsCheckException, InvalidateReprofile); } + return tool.createGuard(n, graph.addOrUniqueWithInputs(boundsCheck), BoundsCheckException, InvalidateReprofile); } protected GuardingNode createNullCheck(ValueNode object, FixedNode before, LoweringTool tool) { @@ -1027,9 +1059,8 @@ public abstract class DefaultJavaLoweringProvider implements LoweringProvider { GuardingNode nullCheck = createNullCheck(object, before, tool); if (nullCheck == null) { return object; - } else { - return before.graph().maybeAddOrUnique(PiNode.create(object, (object.stamp()).join(StampFactory.objectNonNull()), (ValueNode) nullCheck)); } + return before.graph().maybeAddOrUnique(PiNode.create(object, (object.stamp()).join(StampFactory.objectNonNull()), (ValueNode) nullCheck)); } @Override diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/GraphKit.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/GraphKit.java index 5a2be59ea7f..99d6ff7d413 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/GraphKit.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/GraphKit.java @@ -75,7 +75,6 @@ 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; /** @@ -252,6 +251,10 @@ public class GraphKit implements GraphBuilderTool { return new MethodCallTargetNode(invokeKind, targetMethod, args, returnStamp, null); } + protected final JavaKind asKind(JavaType type) { + return wordTypes != null ? wordTypes.asKind(type) : type.getJavaKind(); + } + /** * Determines if a given set of arguments is compatible with the signature of a given method. * @@ -267,14 +270,12 @@ public class GraphKit implements GraphBuilderTool { } int argIndex = 0; if (!isStatic) { - ResolvedJavaType expectedType = method.getDeclaringClass(); - JavaKind expected = wordTypes == null ? expectedType.getJavaKind() : wordTypes.asKind(expectedType); + JavaKind expected = asKind(method.getDeclaringClass()); JavaKind actual = args[argIndex++].stamp().getStackKind(); assert expected == actual : graph + ": wrong kind of value for receiver argument of call to " + method + " [" + actual + " != " + expected + "]"; } for (int i = 0; i != signature.getParameterCount(false); i++) { - JavaType expectedType = signature.getParameterType(i, method.getDeclaringClass()); - JavaKind expected = wordTypes == null ? expectedType.getJavaKind().getStackKind() : wordTypes.asKind(expectedType).getStackKind(); + JavaKind expected = asKind(signature.getParameterType(i, method.getDeclaringClass())).getStackKind(); JavaKind actual = args[argIndex++].stamp().getStackKind(); if (expected != actual) { throw new AssertionError(graph + ": wrong kind of value for argument " + i + " of call to " + method + " [" + actual + " != " + expected + "]"); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/ReplacementsImpl.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/ReplacementsImpl.java index e5173c70cc7..c569e541320 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/ReplacementsImpl.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/ReplacementsImpl.java @@ -433,13 +433,14 @@ public class ReplacementsImpl implements Replacements, InlineInvokePlugin { } IntrinsicContext initialIntrinsicContext = null; - if (method.getAnnotation(Snippet.class) == null) { + Snippet snippetAnnotation = method.getAnnotation(Snippet.class); + if (snippetAnnotation == null) { // Post-parse inlined intrinsic initialIntrinsicContext = new IntrinsicContext(substitutedMethod, method, bytecodeProvider, INLINE_AFTER_PARSING); } else { // Snippet ResolvedJavaMethod original = substitutedMethod != null ? substitutedMethod : method; - initialIntrinsicContext = new IntrinsicContext(original, method, bytecodeProvider, INLINE_AFTER_PARSING); + initialIntrinsicContext = new IntrinsicContext(original, method, bytecodeProvider, INLINE_AFTER_PARSING, snippetAnnotation.allowPartialIntrinsicArgumentMismatch()); } createGraphBuilder(metaAccess, replacements.providers.getStampProvider(), replacements.providers.getConstantReflection(), replacements.providers.getConstantFieldProvider(), config, diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/SnippetCounter.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/SnippetCounter.java index b52ad3881a7..8b3276fecaa 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/SnippetCounter.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/SnippetCounter.java @@ -93,12 +93,7 @@ public final class SnippetCounter implements Comparable { */ @Override public int compareTo(SnippetCounter o) { - if (value > o.value) { - return -1; - } else if (o.value < value) { - return 1; - } - return 0; + return Long.signum(o.value - value); } private final Group group; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/SnippetIntegerHistogram.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/SnippetIntegerHistogram.java new file mode 100644 index 00000000000..9a00d6157da --- /dev/null +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/SnippetIntegerHistogram.java @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2017, 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 org.graalvm.compiler.replacements; + +/** + * A histogram that can (only) be {@linkplain #inc(long) incremented} from within a snippet for + * gathering snippet specific metrics. + */ +public final class SnippetIntegerHistogram { + private final SnippetCounter.Group group; + private final String name; + + private final SnippetCounter counter0; + private final SnippetCounter counter1; + private final SnippetCounter counter2; + private final SnippetCounter counter3; + private final SnippetCounter counter4; + private final SnippetCounter counter5; + private final SnippetCounter counter6; + private final SnippetCounter counter7; + private final SnippetCounter counter8; + private final SnippetCounter counter9; + private final SnippetCounter counter10; + + private final int counter0UpperBound; + private final int counter1UpperBound; + private final int counter2UpperBound; + private final int counter3UpperBound; + private final int counter4UpperBound; + private final int counter5UpperBound; + private final int counter6UpperBound; + private final int counter7UpperBound; + private final int counter8UpperBound; + private final int counter9UpperBound; + + public SnippetIntegerHistogram(SnippetCounter.Group group, int log2StepLength, String name, String description) { + assert log2StepLength > 0; + + this.group = group; + this.name = name; + + int lowerBound = 0; + counter0UpperBound = 0; + counter0 = createCounter(group, name, description, lowerBound, counter0UpperBound); + + lowerBound = counter0UpperBound + 1; + counter1UpperBound = Math.max(1, lowerBound - 1) << log2StepLength; + counter1 = createCounter(group, name, description, lowerBound, counter1UpperBound); + + lowerBound = counter1UpperBound + 1; + counter2UpperBound = Math.max(1, lowerBound - 1) << log2StepLength; + counter2 = createCounter(group, name, description, lowerBound, counter2UpperBound); + + lowerBound = counter2UpperBound + 1; + counter3UpperBound = Math.max(1, lowerBound - 1) << log2StepLength; + counter3 = createCounter(group, name, description, lowerBound, counter3UpperBound); + + lowerBound = counter3UpperBound + 1; + counter4UpperBound = Math.max(1, lowerBound - 1) << log2StepLength; + counter4 = createCounter(group, name, description, lowerBound, counter4UpperBound); + + lowerBound = counter4UpperBound + 1; + counter5UpperBound = Math.max(1, lowerBound - 1) << log2StepLength; + counter5 = createCounter(group, name, description, lowerBound, counter5UpperBound); + + lowerBound = counter5UpperBound + 1; + counter6UpperBound = Math.max(1, lowerBound - 1) << log2StepLength; + counter6 = createCounter(group, name, description, lowerBound, counter6UpperBound); + + lowerBound = counter6UpperBound + 1; + counter7UpperBound = Math.max(1, lowerBound - 1) << log2StepLength; + counter7 = createCounter(group, name, description, lowerBound, counter7UpperBound); + + lowerBound = counter7UpperBound + 1; + counter8UpperBound = Math.max(1, lowerBound - 1) << log2StepLength; + counter8 = createCounter(group, name, description, lowerBound, counter8UpperBound); + + lowerBound = counter8UpperBound + 1; + counter9UpperBound = Math.max(1, lowerBound - 1) << log2StepLength; + counter9 = createCounter(group, name, description, lowerBound, counter9UpperBound); + + lowerBound = counter9UpperBound + 1; + counter10 = createCounter(group, name, description, lowerBound, Long.MAX_VALUE); + } + + private static SnippetCounter createCounter(SnippetCounter.Group group, String name, String description, long lowerBound, long upperBound) { + if (group != null) { + SnippetCounter snippetCounter = new SnippetCounter(group, name + "[" + lowerBound + ", " + upperBound + "]", description); + return snippetCounter; + } + return null; + } + + /** + * Increments the value of the matching histogram element. This method can only be used in a + * snippet on a compile-time constant {@link SnippetIntegerHistogram} object. + */ + public void inc(long value) { + if (group != null) { + if (value <= counter0UpperBound) { + counter0.inc(); + } else if (value <= counter1UpperBound) { + counter1.inc(); + } else if (value <= counter2UpperBound) { + counter2.inc(); + } else if (value <= counter3UpperBound) { + counter3.inc(); + } else if (value <= counter4UpperBound) { + counter4.inc(); + } else if (value <= counter5UpperBound) { + counter5.inc(); + } else if (value <= counter6UpperBound) { + counter6.inc(); + } else if (value <= counter7UpperBound) { + counter7.inc(); + } else if (value <= counter8UpperBound) { + counter8.inc(); + } else if (value <= counter9UpperBound) { + counter9.inc(); + } else { + counter10.inc(); + } + } + } + + @Override + public String toString() { + if (group != null) { + return "SnippetHistogram-" + group.name + ":" + name; + } + return super.toString(); + } +} diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/ArrayEqualsNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/ArrayEqualsNode.java index bf2edb8abaf..a08ff3182cc 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/ArrayEqualsNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/ArrayEqualsNode.java @@ -23,15 +23,15 @@ package org.graalvm.compiler.replacements.nodes; import static org.graalvm.compiler.nodeinfo.InputType.Memory; -import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_1024; -import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_1024; import org.graalvm.compiler.core.common.type.StampFactory; import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.graph.spi.Canonicalizable; import org.graalvm.compiler.graph.spi.CanonicalizerTool; +import org.graalvm.compiler.nodeinfo.NodeCycles; import org.graalvm.compiler.nodeinfo.NodeInfo; +import org.graalvm.compiler.nodeinfo.NodeSize; import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.FixedWithNextNode; import org.graalvm.compiler.nodes.NamedLocationIdentity; @@ -57,7 +57,7 @@ import jdk.vm.ci.meta.Value; /** * Compares two arrays with the same length. */ -@NodeInfo(cycles = CYCLES_1024, size = SIZE_1024) +@NodeInfo(cycles = NodeCycles.CYCLES_UNKNOWN, size = NodeSize.SIZE_128) public final class ArrayEqualsNode extends FixedWithNextNode implements LIRLowerable, Canonicalizable, Virtualizable, MemoryAccess { public static final NodeClass TYPE = NodeClass.create(ArrayEqualsNode.class); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BasicArrayCopyNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BasicArrayCopyNode.java index 59750cc6454..0c5e6a17724 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BasicArrayCopyNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BasicArrayCopyNode.java @@ -24,7 +24,6 @@ package org.graalvm.compiler.replacements.nodes; import static org.graalvm.compiler.nodeinfo.InputType.Memory; import static org.graalvm.compiler.nodeinfo.InputType.State; -import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_256; import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_64; import static org.graalvm.word.LocationIdentity.any; @@ -32,6 +31,7 @@ import org.graalvm.compiler.core.common.type.StampFactory; import org.graalvm.compiler.debug.DebugContext; import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.graph.NodeInputList; +import org.graalvm.compiler.nodeinfo.NodeCycles; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.DeoptimizingNode; @@ -56,7 +56,7 @@ import jdk.vm.ci.code.BytecodeFrame; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.ResolvedJavaType; -@NodeInfo(cycles = CYCLES_256, size = SIZE_64) +@NodeInfo(cycles = NodeCycles.CYCLES_UNKNOWN, size = SIZE_64) public class BasicArrayCopyNode extends AbstractMemoryCheckpoint implements Virtualizable, MemoryCheckpoint.Single, MemoryAccess, Lowerable, DeoptimizingNode.DeoptDuring { public static final NodeClass TYPE = NodeClass.create(BasicArrayCopyNode.class); @@ -215,7 +215,7 @@ public class BasicArrayCopyNode extends AbstractMemoryCheckpoint implements Virt return; } for (int i = 0; i < len; i++) { - tool.setVirtualEntry(destVirtual, destPosInt + i, tool.getEntry(srcVirtual, srcPosInt + i), false); + tool.setVirtualEntry(destVirtual, destPosInt + i, tool.getEntry(srcVirtual, srcPosInt + i)); } tool.delete(); DebugContext debug = getDebug(); @@ -235,7 +235,7 @@ public class BasicArrayCopyNode extends AbstractMemoryCheckpoint implements Virt for (int i = 0; i < len; i++) { LoadIndexedNode load = new LoadIndexedNode(graph().getAssumptions(), srcAlias, ConstantNode.forInt(i + srcPosInt, graph()), destComponentType.getJavaKind()); tool.addNode(load); - tool.setVirtualEntry(destVirtual, destPosInt + i, load, false); + tool.setVirtualEntry(destVirtual, destPosInt + i, load); } tool.delete(); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BasicObjectCloneNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BasicObjectCloneNode.java index c1c49af5da7..9261266c0eb 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BasicObjectCloneNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BasicObjectCloneNode.java @@ -22,7 +22,6 @@ */ package org.graalvm.compiler.replacements.nodes; -import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_8; import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_8; import java.util.Collections; @@ -32,6 +31,7 @@ import org.graalvm.compiler.core.common.type.Stamp; import org.graalvm.compiler.core.common.type.StampFactory; import org.graalvm.compiler.core.common.type.StampPair; import org.graalvm.compiler.graph.NodeClass; +import org.graalvm.compiler.nodeinfo.NodeCycles; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind; import org.graalvm.compiler.nodes.ValueNode; @@ -49,7 +49,7 @@ import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; -@NodeInfo(cycles = CYCLES_8, size = SIZE_8) +@NodeInfo(cycles = NodeCycles.CYCLES_UNKNOWN, size = SIZE_8) public abstract class BasicObjectCloneNode extends MacroStateSplitNode implements VirtualizableAllocation, ArrayLengthProvider { public static final NodeClass TYPE = NodeClass.create(BasicObjectCloneNode.class); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BinaryMathIntrinsicNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BinaryMathIntrinsicNode.java index bff7c320f02..2e68bc7e6b8 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BinaryMathIntrinsicNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BinaryMathIntrinsicNode.java @@ -37,7 +37,7 @@ import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.BinaryNode; -import org.graalvm.compiler.nodes.calc.DivNode; +import org.graalvm.compiler.nodes.calc.FloatDivNode; import org.graalvm.compiler.nodes.calc.MulNode; import org.graalvm.compiler.nodes.calc.SqrtNode; import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable; @@ -141,7 +141,7 @@ public final class BinaryMathIntrinsicNode extends BinaryNode implements Arithme // x**-1 = 1/x if (yValue == -1.0D) { - return new DivNode(ConstantNode.forDouble(1), x); + return new FloatDivNode(ConstantNode.forDouble(1), x); } // x**2 = x*x diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.test/src/org/graalvm/compiler/test/GraalTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.test/src/org/graalvm/compiler/test/GraalTest.java index 695d4d2ccbd..256e1ee6123 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.test/src/org/graalvm/compiler/test/GraalTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.test/src/org/graalvm/compiler/test/GraalTest.java @@ -36,6 +36,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import jdk.vm.ci.meta.ResolvedJavaMethod; import org.graalvm.compiler.debug.DebugHandlersFactory; import org.graalvm.compiler.debug.DebugContext; import org.graalvm.compiler.debug.DebugDumpHandler; @@ -392,6 +393,20 @@ public class GraalTest { * {@link DebugDumpHandler}s closed in {@link #afterTest()}. */ protected DebugContext getDebugContext(OptionValues options) { + return getDebugContext(options, null, null); + } + + /** + * Gets a {@link DebugContext} object corresponding to {@code options}, creating a new one if + * none currently exists.Debug contexts created by this method will have their + * {@link DebugDumpHandler}s closed in {@link #afterTest()}. + * + * @param options currently active options + * @param id identification of the compilation or {@code null} + * @param method method to use for a proper description of the context or {@code null} + * @return configured context for compilation + */ + protected DebugContext getDebugContext(OptionValues options, String id, ResolvedJavaMethod method) { List cached = cachedDebugs.get(); if (cached == null) { cached = new ArrayList<>(); @@ -402,7 +417,13 @@ public class GraalTest { return debug; } } - DebugContext debug = DebugContext.create(options, NO_DESCRIPTION, NO_GLOBAL_METRIC_VALUES, DEFAULT_LOG_STREAM, getDebugHandlersFactories()); + final DebugContext.Description descr; + if (method == null) { + descr = NO_DESCRIPTION; + } else { + descr = new DebugContext.Description(method, id == null ? method.getName() : id); + } + DebugContext debug = DebugContext.create(options, descr, NO_GLOBAL_METRIC_VALUES, DEFAULT_LOG_STREAM, getDebugHandlersFactories()); cached.add(debug); return debug; } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/EffectList.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/EffectList.java index 42d73c5715b..db38a0497c9 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/EffectList.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/EffectList.java @@ -171,9 +171,14 @@ public class EffectList implements Iterable { } public void apply(StructuredGraph graph, ArrayList obsoleteNodes, boolean cfgKills) { + boolean message = false; for (int i = 0; i < size(); i++) { Effect effect = effects[i]; if (effect.isCfgKill() == cfgKills) { + if (!message) { + message = true; + debug.log(cfgKills ? " ==== cfg kill effects" : " ==== effects"); + } try { effect.apply(graph, obsoleteNodes); } catch (Throwable t) { @@ -202,7 +207,7 @@ public class EffectList implements Iterable { // Inner classes could capture the EffectList itself. continue; } - str.append(first ? "" : ", ").append(format(object)); + str.append(first ? "" : ", ").append(field.getName()).append("=").append(format(object)); first = false; } catch (SecurityException | IllegalAccessException e) { throw new RuntimeException(e); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/EffectsClosure.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/EffectsClosure.java index c5336c81c7a..fcdc2185b32 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/EffectsClosure.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/EffectsClosure.java @@ -184,7 +184,6 @@ public abstract class EffectsClosure> e }; ReentrantBlockIterator.apply(closure, cfg.getStartBlock()); for (GraphEffectList effects : effectList) { - debug.log(" ==== effects"); effects.apply(graph, obsoleteNodes, false); } /* @@ -193,7 +192,6 @@ public abstract class EffectsClosure> e * indexes. */ for (GraphEffectList effects : effectList) { - debug.log(" ==== cfg kill effects"); effects.apply(graph, obsoleteNodes, true); } debug.dump(DebugContext.DETAILED_LEVEL, graph, "After applying effects"); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/EffectsPhase.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/EffectsPhase.java index a3df229bfeb..5a6d0937579 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/EffectsPhase.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/EffectsPhase.java @@ -99,8 +99,8 @@ public abstract class EffectsPhase extends B closure.applyEffects(); } - if (debug.isDumpEnabled(DebugContext.INFO_LEVEL)) { - debug.dump(DebugContext.DETAILED_LEVEL, graph, "%s iteration", getName()); + if (debug.isDumpEnabled(DebugContext.VERBOSE_LEVEL)) { + debug.dump(DebugContext.VERBOSE_LEVEL, graph, "%s iteration", getName()); } new DeadCodeEliminationPhase(Required).apply(graph); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/ObjectState.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/ObjectState.java index 95fa92769b6..aed90d161cf 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/ObjectState.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/ObjectState.java @@ -68,6 +68,7 @@ public class ObjectState { } public ObjectState(ValueNode[] entries, LockState locks, boolean ensureVirtualized) { + assert checkIllegalValues(entries); this.entries = entries; this.locks = locks; this.ensureVirtualized = ensureVirtualized; @@ -92,6 +93,30 @@ public class ObjectState { return new ObjectState(this); } + /** + * Ensure that if an {@link JavaConstant#forIllegal() illegal value} is seen that the previous + * value is a double word value. + */ + public static boolean checkIllegalValues(ValueNode[] values) { + if (values != null) { + for (int v = 1; v < values.length; v++) { + checkIllegalValue(values, v); + } + } + return true; + } + + /** + * Ensure that if an {@link JavaConstant#forIllegal() illegal value} is seen that the previous + * value is a double word value. + */ + public static boolean checkIllegalValue(ValueNode[] values, int v) { + if (v > 0 && values[v].isConstant() && values[v].asConstant().equals(JavaConstant.forIllegal())) { + assert values[v - 1].getStackKind().needsTwoSlots(); + } + return true; + } + public EscapeObjectState createEscapeObjectState(DebugContext debug, VirtualObjectNode virtual) { GET_ESCAPED_OBJECT_STATE.increment(debug); if (cachedState == null) { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PartialEscapeClosure.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PartialEscapeClosure.java index e00e407ff78..f88b86b763b 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PartialEscapeClosure.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PartialEscapeClosure.java @@ -854,6 +854,7 @@ public abstract class PartialEscapeClosure closure, Assumptions assumptions, OptionValues options, DebugContext debug, LoweringProvider loweringProvider) { @@ -125,17 +130,81 @@ class VirtualizerToolImpl implements VirtualizerTool, CanonicalizerTool { } @Override - public void setVirtualEntry(VirtualObjectNode virtual, int index, ValueNode value, boolean unsafe) { + public boolean setVirtualEntry(VirtualObjectNode virtual, int index, ValueNode value, JavaKind theAccessKind, long offset) { ObjectState obj = state.getObjectState(virtual); assert obj.isVirtual() : "not virtual: " + obj; ValueNode newValue; + JavaKind entryKind = virtual.entryKind(index); + JavaKind accessKind = theAccessKind != null ? theAccessKind : entryKind; if (value == null) { newValue = null; } else { newValue = closure.getAliasAndResolve(state, value); - assert unsafe || obj.getEntry(index) == null || obj.getEntry(index).getStackKind() == newValue.getStackKind() || (isObjectEntry(obj.getEntry(index)) && isObjectEntry(newValue)); } - state.setEntry(virtual.getObjectId(), index, newValue); + getDebug().log(DebugContext.DETAILED_LEVEL, "Setting entry %d in virtual object %s %s results in %s", index, virtual.getObjectId(), virtual, state.getObjectState(virtual.getObjectId())); + ValueNode oldValue = getEntry(virtual, index); + boolean canVirtualize = entryKind == accessKind || (entryKind == accessKind.getStackKind() && virtual instanceof VirtualInstanceNode); + if (!canVirtualize) { + if (entryKind == JavaKind.Long && oldValue.getStackKind() == newValue.getStackKind() && oldValue.getStackKind().isPrimitive()) { + /* + * Special case: If the entryKind is long, allow arbitrary kinds as long as a value + * of the same kind is already there. This can only happen if some other node + * initialized the entry with a value of a different kind. One example where this + * happens is the Truffle NewFrameNode. + */ + getDebug().log(DebugContext.DETAILED_LEVEL, "virtualizing %s with primitive of kind %s in long entry ", current, oldValue.getStackKind()); + canVirtualize = true; + } else if (entryKind == JavaKind.Int && (accessKind == JavaKind.Long || accessKind == JavaKind.Double) && offset % 8 == 0) { + /* + * Special case: Allow storing a single long or double value into two consecutive + * int slots. + */ + int nextIndex = virtual.entryIndexForOffset(offset + 4, JavaKind.Int); + if (nextIndex != -1) { + canVirtualize = true; + assert nextIndex == index + 1 : "expected to be sequential"; + getDebug().log(DebugContext.DETAILED_LEVEL, "virtualizing %s for double word stored in two ints", current); + } + } + } + + if (canVirtualize) { + getDebug().log(DebugContext.DETAILED_LEVEL, "virtualizing %s for entryKind %s and access kind %s", current, entryKind, accessKind); + state.setEntry(virtual.getObjectId(), index, newValue); + if (entryKind == JavaKind.Int) { + if (accessKind.needsTwoSlots()) { + // Storing double word value two int slots + assert virtual.entryKind(index + 1) == JavaKind.Int; + state.setEntry(virtual.getObjectId(), index + 1, getIllegalConstant()); + } else if (oldValue.getStackKind() == JavaKind.Double || oldValue.getStackKind() == JavaKind.Long) { + // Splitting double word constant by storing over it with an int + getDebug().log(DebugContext.DETAILED_LEVEL, "virtualizing %s producing second half of double word value %s", current, oldValue); + ValueNode secondHalf = UnpackEndianHalfNode.create(oldValue, false); + addNode(secondHalf); + state.setEntry(virtual.getObjectId(), index + 1, secondHalf); + } + } + if (oldValue.isConstant() && oldValue.asConstant().equals(JavaConstant.forIllegal())) { + // Storing into second half of double, so replace previous value + ValueNode previous = getEntry(virtual, index - 1); + getDebug().log(DebugContext.DETAILED_LEVEL, "virtualizing %s producing first half of double word value %s", current, previous); + ValueNode firstHalf = UnpackEndianHalfNode.create(previous, true); + addNode(firstHalf); + state.setEntry(virtual.getObjectId(), index - 1, firstHalf); + } + return true; + } + // Should only occur if there are mismatches between the entry and access kind + assert entryKind != accessKind; + return false; + } + + private ValueNode getIllegalConstant() { + if (illegalConstant == null) { + illegalConstant = ConstantNode.forConstant(JavaConstant.forIllegal(), getMetaAccessProvider()); + addNode(illegalConstant); + } + return illegalConstant; } @Override @@ -149,10 +218,6 @@ class VirtualizerToolImpl implements VirtualizerTool, CanonicalizerTool { return state.getObjectState(virtualObject).getEnsureVirtualized(); } - private static boolean isObjectEntry(ValueNode value) { - return value.getStackKind() == JavaKind.Object || value instanceof VirtualObjectNode; - } - @Override public void replaceWithVirtual(VirtualObjectNode virtual) { closure.addVirtualAlias(virtual, current); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.graphio/src/org/graalvm/graphio/GraphOutput.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.graphio/src/org/graalvm/graphio/GraphOutput.java index 24b4cc10302..b08671ebd43 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.graphio/src/org/graalvm/graphio/GraphOutput.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.graphio/src/org/graalvm/graphio/GraphOutput.java @@ -113,11 +113,29 @@ public final class GraphOutput implements Closeable { private GraphElements elements = null; private GraphTypes types = DefaultGraphTypes.DEFAULT; private GraphBlocks blocks = DefaultGraphBlocks.empty(); + private int major = 4; + private int minor = 0; Builder(GraphStructure structure) { this.structure = structure; } + /** + * Chooses which version of the protocol to use. The default version is 4.0 + * (when the {@link GraphOutput} & co. classes were introduced). The default can be changed + * to other known versions manually by calling this method. + * + * @param majorVersion by default 4, newer version may be known + * @param minorVersion usually 0 + * @return this builder + * @since 0.28 + */ + public Builder protocolVersion(int majorVersion, int minorVersion) { + this.major = majorVersion; + this.minor = minorVersion; + return this; + } + /** * Associates different implementation of types. * @@ -161,7 +179,28 @@ public final class GraphOutput implements Closeable { * @throws IOException if something goes wrong when writing to the channel */ public GraphOutput build(WritableByteChannel channel) throws IOException { - ProtocolImpl p = new ProtocolImpl<>(structure, types, blocks, elements, channel); + ProtocolImpl p = new ProtocolImpl<>(major, minor, structure, types, blocks, elements, channel); + return new GraphOutput<>(p); + } + + /** + * Support for nesting heterogenous graphs. The newly created output uses all the interfaces + * currently associated with this builder, but shares with {@code parent} the output + * {@code channel}, internal constant pool and {@link #protocolVersion(int, int) protocol + * version}. + *

+ * Both GraphOutput (the {@code parent} and the returned one) has to be used in + * synchronization - e.g. only one + * {@link #beginGroup(java.lang.Object, java.lang.String, java.lang.String, java.lang.Object, int, java.util.Map) + * begin}, {@link #endGroup() end} of group or + * {@link #print(java.lang.Object, java.util.Map, int, java.lang.String, java.lang.Object...) + * printing} can be on at a given moment. + * + * @param parent the output to inherit {@code channel} and protocol version from + * @return new output sharing {@code channel} and other internals with {@code parent} + */ + public GraphOutput build(GraphOutput parent) { + ProtocolImpl p = new ProtocolImpl<>(parent.printer, structure, types, blocks, elements); return new GraphOutput<>(p); } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.graphio/src/org/graalvm/graphio/GraphProtocol.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.graphio/src/org/graalvm/graphio/GraphProtocol.java index fd63dd7b586..e0fb0a3f804 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.graphio/src/org/graalvm/graphio/GraphProtocol.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.graphio/src/org/graalvm/graphio/GraphProtocol.java @@ -52,6 +52,7 @@ abstract class GraphProtocol 4) { - throw new IllegalArgumentException(); - } - if (major == 4 && minor > 0) { - throw new IllegalArgumentException(); + GraphProtocol(WritableByteChannel channel, int major, int minor) throws IOException { + if (major > 5 || (major == 5 && minor > 0)) { + throw new IllegalArgumentException("Unrecognized version " + major + "." + minor); } this.versionMajor = major; this.versionMinor = minor; @@ -93,6 +87,14 @@ abstract class GraphProtocol parent) { + this.versionMajor = parent.versionMajor; + this.versionMinor = parent.versionMinor; + this.constantPool = parent.constantPool; + this.buffer = parent.buffer; + this.channel = parent.channel; + } + @SuppressWarnings("all") public final void print(Graph graph, Map properties, int id, String format, Object... args) throws IOException { writeByte(BEGIN_GRAPH); @@ -137,8 +139,32 @@ abstract class GraphProtocolnull if it is not a node object, non-null otherwise + */ + protected abstract Node findNode(Object obj); + + /** + * Determines whether the provided object is node class or not. + * + * @param obj object to check + * @return {@code null} if {@code obj} does not represent a NodeClass otherwise the NodeClass + * represented by {@code obj} + */ protected abstract NodeClass findNodeClass(Object obj); + /** + * Returns the NodeClass for a given Node {@code obj}. + * + * @param obj instance of node + * @return non-{@code null} instance of the node's class object + */ + protected abstract NodeClass findClassForNode(Node obj); + /** * Find a Java class. The returned object must be acceptable by * {@link #findJavaTypeName(java.lang.Object)} and return valid name for the class. @@ -239,7 +265,7 @@ abstract class GraphProtocol || findEnumOrdinal(object) >= 0) { - writeByte(POOL_ENUM); - } else if (object instanceof Class || findJavaTypeName(object) != null) { - writeByte(POOL_CLASS); - } else if (findJavaField(object) != null) { + if (findJavaField(object) != null) { writeByte(POOL_FIELD); } else if (findSignature(object) != null) { writeByte(POOL_SIGNATURE); } else if (versionMajor >= 4 && findNodeSourcePosition(object) != null) { writeByte(POOL_NODE_SOURCE_POSITION); } else { + final Node node = findNode(object); + if (versionMajor == 4 && node != null) { + object = classForNode(node); + } if (findNodeClass(object) != null) { writeByte(POOL_NODE_CLASS); + } else if (versionMajor >= 5 && node != null) { + writeByte(POOL_NODE); } else if (findMethod(object) != null) { writeByte(POOL_METHOD); } else { - writeByte(POOL_STRING); + if (object instanceof Enum || findEnumOrdinal(object) >= 0) { + writeByte(POOL_ENUM); + } else if (object instanceof Class || findJavaTypeName(object) != null) { + writeByte(POOL_CLASS); + } else { + writeByte(POOL_STRING); + } } } writeShort(id.charValue()); @@ -383,10 +418,7 @@ abstract class GraphProtocol= 0) { - writeByte(POOL_ENUM); - writePoolObject(findEnumClass(object)); - writeInt(enumOrdinal); - } else if ((field = findJavaField(object)) != null) { + if ((field = findJavaField(object)) != null) { writeByte(POOL_FIELD); writePoolObject(findFieldDeclaringClass(field)); writePoolObject(findFieldName(field)); @@ -535,6 +559,18 @@ abstract class GraphProtocol= 5) { + writeByte(POOL_NODE); + writeInt(findNodeId(node)); + writePoolObject(classForNode(node)); + return; + } + if (versionMajor == 4) { + object = classForNode(node); + } + } NodeClass nodeClass = findNodeClass(object); if (nodeClass != null) { writeByte(POOL_NODE_CLASS); @@ -553,8 +589,27 @@ abstract class GraphProtocol= 0) { + writeByte(POOL_ENUM); + writePoolObject(findEnumClass(object)); + writeInt(enumOrdinal); + } else { + writeByte(POOL_STRING); + writeString(object.toString()); + } return; } writeByte(POOL_METHOD); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.graphio/src/org/graalvm/graphio/GraphSnippets.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.graphio/src/org/graalvm/graphio/GraphSnippets.java new file mode 100644 index 00000000000..3fb3c7f8e07 --- /dev/null +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.graphio/src/org/graalvm/graphio/GraphSnippets.java @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2017, 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 org.graalvm.graphio; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.channels.FileChannel; +import java.nio.channels.WritableByteChannel; +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +final class GraphSnippets { + static GraphStructure acmeGraphStructure() { + // @formatter:off + // BEGIN: org.graalvm.graphio.GraphSnippets#acmeGraphStructure + class AcmeGraphStructure implements + GraphStructure { + + @Override + public AcmeGraph graph(AcmeGraph currentGraph, Object obj) { + return obj instanceof AcmeGraph ? (AcmeGraph) obj : null; + } + + @Override + public Iterable nodes(AcmeGraph graph) { + return graph.allNodes(); + } + + @Override + public int nodesCount(AcmeGraph graph) { + return graph.allNodes().size(); + } + + @Override + public int nodeId(AcmeNode node) { + return node.id; + } + + @Override + public boolean nodeHasPredecessor(AcmeNode node) { + return node.id > 0; + } + + @Override + public void nodeProperties( + AcmeGraph graph, AcmeNode node, Map properties + ) { + properties.put("id", node.id); + } + + @Override + public AcmeNodeType nodeClass(Object obj) { + return obj instanceof AcmeNodeType ? (AcmeNodeType) obj : null; + } + + @Override + public AcmeNode node(Object obj) { + return obj instanceof AcmeNode ? (AcmeNode) obj : null; + } + + @Override + public AcmeNodeType classForNode(AcmeNode node) { + // we have only one type of nodes + return AcmeNodeType.STANDARD; + } + + + @Override + public String nameTemplate(AcmeNodeType nodeClass) { + return "Acme ({p#id})"; + } + + @Override + public Object nodeClassType(AcmeNodeType nodeClass) { + return nodeClass.getClass(); + } + + @Override + public AcmePorts portInputs(AcmeNodeType nodeClass) { + return AcmePorts.INPUT; + } + + @Override + public AcmePorts portOutputs(AcmeNodeType nodeClass) { + return AcmePorts.OUTPUT; + } + + @Override + public int portSize(AcmePorts port) { + return port == AcmePorts.OUTPUT ? 1 : 0; + } + + @Override + public boolean edgeDirect(AcmePorts port, int index) { + return false; + } + + @Override + public String edgeName(AcmePorts port, int index) { + return port.name(); + } + + @Override + public Object edgeType(AcmePorts port, int index) { + return port; + } + + @Override + public Collection edgeNodes( + AcmeGraph graph, AcmeNode node, AcmePorts port, int index + ) { + if (port == AcmePorts.OUTPUT) { + return node.outgoing.targets; + } + return null; + } + } + + // END: org.graalvm.graphio.GraphSnippets#acmeGraphStructure + + return new AcmeGraphStructure(); + } + + // BEGIN: org.graalvm.graphio.GraphSnippets#buildOutput + static GraphOutput buildOutput(WritableByteChannel channel) + throws IOException { + return GraphOutput.newBuilder(acmeGraphStructure()). + // use the latest version; currently 5.0 + protocolVersion(5, 0). + build(channel); + } + // END: org.graalvm.graphio.GraphSnippets#buildOutput + + // BEGIN: org.graalvm.graphio.GraphSnippets#buildAll + static GraphOutput buildAll(WritableByteChannel channel) + throws IOException { + GraphBlocks graphBlocks = acmeBlocks(); + GraphElements graphElements = acmeElements(); + GraphTypes graphTypes = acmeTypes(); + + return GraphOutput.newBuilder(acmeGraphStructure()). + protocolVersion(5, 0). + blocks(graphBlocks). + elements(graphElements). + types(graphTypes). + build(channel); + } + // END: org.graalvm.graphio.GraphSnippets#buildAll + + private static GraphTypes acmeTypes() { + GraphTypes graphTypes = null; + // in real world don't return null + return graphTypes; + } + + private static GraphElements acmeElements() { + GraphElements graphElements = null; + // in real world don't return null + return graphElements; + } + + private static GraphBlocks acmeBlocks() { + GraphBlocks graphBlocks = null; + // in real world don't return null + return graphBlocks; + } + + private static class AcmeGraph { + final AcmeNode root; + + AcmeGraph(AcmeNode root) { + this.root = root; + } + + Set allNodes() { + return allNodes(root, new LinkedHashSet<>()); + } + + private static Set allNodes(AcmeNode node, Set collectTo) { + if (collectTo.add(node)) { + for (AcmeNode target : node.outgoing.targets) { + allNodes(target, collectTo); + } + } + return collectTo; + } + } + + private static class AcmeNode { + final int id; + final AcmeEdges outgoing; + + AcmeNode(int id) { + this.id = id; + this.outgoing = new AcmeEdges(); + } + + void linkTo(AcmeNode target) { + outgoing.targets.add(target); + } + } + + private enum AcmeNodeType { + STANDARD + } + + private enum AcmePorts { + INPUT, + OUTPUT; + } + + private static class AcmeEdges { + final Set targets; + + AcmeEdges() { + this.targets = new LinkedHashSet<>(); + } + } + + private static class AcmeBlocks { + } + + private static class AcmeMethod { + } + + private static class AcmeField { + } + + private static class AcmeSignature { + } + + private static class AcmeCodePosition { + } + + // BEGIN: org.graalvm.graphio.GraphSnippets#dump + static void dump(File toFile) throws IOException { + try ( + FileChannel ch = new FileOutputStream(toFile).getChannel(); + GraphOutput output = buildOutput(ch); + ) { + AcmeNode root = new AcmeNode(0); + AcmeNode n1 = new AcmeNode(1); + AcmeNode n2 = new AcmeNode(2); + AcmeNode n3 = new AcmeNode(3); + + root.linkTo(n1); + root.linkTo(n2); + n1.linkTo(n3); + n2.linkTo(n3); + + AcmeGraph diamondGraph = new AcmeGraph(root); + + output.beginGroup(diamondGraph, "Diamond", "dia", null, 0, null); + output.print(diamondGraph, null, 0, "Diamond graph #%d", 1); + output.endGroup(); + } + } + // END: org.graalvm.graphio.GraphSnippets#dump + +} diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.graphio/src/org/graalvm/graphio/GraphStructure.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.graphio/src/org/graalvm/graphio/GraphStructure.java index 7b950e42471..9c4ae934749 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.graphio/src/org/graalvm/graphio/GraphStructure.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.graphio/src/org/graalvm/graphio/GraphStructure.java @@ -38,9 +38,9 @@ import java.util.Map; */ public interface GraphStructure { /** - * Casts the provided object to graph, if possible. If the given object obj can be - * seen as a graph or sub-graph of a graph, then return the properly typed instance. Otherwise - * return null + * Casts {@code obj} to graph, if possible. If the given object obj can be seen as + * a graph or sub-graph of a graph, then return the properly typed instance. Otherwise return + * null * * @param currentGraph the currently processed graph * @param obj an object to check and view as a graph @@ -69,8 +69,8 @@ public interface GraphStructure { int nodesCount(G graph); /** - * Id of a node. Each node in the graph is uniquely identified by a integer value. If two nodes - * have the same id, then they shall be == to each other. + * Id of {@code node}. Each node in the graph is uniquely identified by an integer value. If two + * nodes have the same id, then they shall be == to each other. * * @param node the node to query for an id * @return the id of the node @@ -96,15 +96,34 @@ public interface GraphStructure { void nodeProperties(G graph, N node, Map properties); /** - * Finds the node class for the provided object, if possible. If the given object - * obj can be seen as an instance of node class or it is a node in this graph, - * return the properly typed instance of the node class. Otherwise return null + * Finds a node for {@code obj}, if possible. If the given object obj can be seen + * as an instance of node return the properly typed instance of the node class. Otherwise return + * null. + * + * @param obj an object to find node for + * @return appropriate graph object or null if the object doesn't represent a node + */ + N node(Object obj); + + /** + * Finds a node class for {@code obj}, if possible. If the given object obj can be + * seen as an instance of node class return the properly typed instance of the node class. + * Otherwise return null. * * @param obj an object to find node class for - * @return appropriate graph object or null if the object doesn't represent a graph + * @return appropriate graph object or null if the object doesn't represent a node + * class */ C nodeClass(Object obj); + /** + * Finds a node class for {@code node}. + * + * @param node an instance of node in this graph + * @return the node's node class, never null + */ + C classForNode(N node); + /** * The template used to build the name of nodes of this class. The template may use references * to inputs ({i#inputName}) and its properties ({p#propertyName}). diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.graphio/src/org/graalvm/graphio/ProtocolImpl.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.graphio/src/org/graalvm/graphio/ProtocolImpl.java index a7d486af4dc..a6ef36838e1 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.graphio/src/org/graalvm/graphio/ProtocolImpl.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.graphio/src/org/graalvm/graphio/ProtocolImpl.java @@ -34,9 +34,18 @@ final class ProtocolImpl blocks; private final GraphElements elements; - ProtocolImpl(GraphStructure structure, GraphTypes enums, GraphBlocks blocks, + ProtocolImpl(int major, int minor, GraphStructure structure, GraphTypes enums, GraphBlocks blocks, GraphElements elements, WritableByteChannel channel) throws IOException { - super(channel); + super(channel, major, minor); + this.structure = structure; + this.types = enums; + this.blocks = blocks; + this.elements = elements; + } + + ProtocolImpl(GraphProtocol parent, GraphStructure structure, GraphTypes enums, GraphBlocks blocks, + GraphElements elements) { + super(parent); this.structure = structure; this.types = enums; this.blocks = blocks; @@ -48,11 +57,21 @@ final class ProtocolImplIGV via a socket or a file. This package allows one to easily encode + * any graph-like data structure and send it for visualization to + * OracleLab's Ideal Graph Visualizer tool. Assuming you already have your own data + * structure that contains nodes and edges among them, creating a + * {@link org.graalvm.graphio.GraphOutput} specialized for your data is a matter of implementing a + * single interface: + * + * {@link org.graalvm.graphio.GraphSnippets#acmeGraphStructure} + * + * The {@link org.graalvm.graphio.GraphStructure} interface defines the set of operations that are + * needed by the graph protocol to encode a graph into the IGV expected format. The + * graph structure is implemented as a so called + * singletonizer API pattern: there is no + * need to change your data structures or implement some special interfaces - everything needed is + * provided by implementing the {@link org.graalvm.graphio.GraphStructure} operations. + *

+ * The next step is to turn this graph structure into an instance of + * {@link org.graalvm.graphio.GraphOutput}. To do so use the associated + * {@link org.graalvm.graphio.GraphOutput.Builder builder} just like shown in the following method: + * + * {@link org.graalvm.graphio.GraphSnippets#buildOutput} + * + * Now you are ready to dump your graph into IGV. Where to obtain the right channel? One + * option is to create a {@link java.nio.channels.FileChannel} and dump the data into a file + * (preferrably with .bgv extension). The other is to open a socket to port + * 4445 (the default port IGV listens to) and dump the data there. Here is an + * example: + * + * {@link org.graalvm.graphio.GraphSnippets#dump} + * + * Call the {@code dump} method with pointer to file {@code diamond.bgv} and then you can open the + * file in IGV. The result will look like this: + *

+ * + *

+ * You can verify the behavior directly in the IGV by downloading + * diamond.bgv file generated from the above diamond structure + * graph. + *

+ * The primary IGV focus is on graphs used by Graal compiler. As such they aren't plain + * graphs, but contain various compiler oriented attributes: + *

    + *
  • {@linkplain org.graalvm.graphio.GraphBlocks code blocks} information
  • + *
  • {@linkplain org.graalvm.graphio.GraphElements method and fields} information
  • + *
  • Advanced support for {@linkplain org.graalvm.graphio.GraphTypes recognizing types}
  • + *
+ * all these additional interfaces ({@link org.graalvm.graphio.GraphBlocks}, + * {@link org.graalvm.graphio.GraphElements} and {@link org.graalvm.graphio.GraphTypes}) are + * optional - they don't have to be provided. As such they can be specified via + * {@link org.graalvm.graphio.GraphOutput.Builder} instance methods, which may, but need not be + * called at all. Here is an example: + * + * {@link org.graalvm.graphio.GraphSnippets#buildAll} + * + * All these interfaces follow the + * singletonizer API pattern again - e.g. + * no need to change your existing data structures, just implement the operations provided by the + * interfaces you pass into the builder. By combining these interfaces together you can get as rich, + * colorful, source linked graphs as Graal compiler produces to describe its optimizations. + */ +package org.graalvm.graphio; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.word/.checkstyle_checks.xml b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.word/.checkstyle_checks.xml new file mode 100644 index 00000000000..44c996f7eea --- /dev/null +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.word/.checkstyle_checks.xml @@ -0,0 +1,240 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +