# HG changeset patch # User Michael Van De Vanter # Date 1387344402 28800 # Node ID 9c88b11382403a2378903db10ea555360e6b556c # Parent 69d2e4baa2154bd5fb1dccdb89817aa11a58b43d# Parent 430c9f08728d9efa37f4311aa712e969f9e5e254 Merge with 430c9f08728d9efa37f4311aa712e969f9e5e254 diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.amd64/src/com/oracle/graal/amd64/AMD64.java --- a/graal/com.oracle.graal.amd64/src/com/oracle/graal/amd64/AMD64.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.amd64/src/com/oracle/graal/amd64/AMD64.java Tue Dec 17 21:26:42 2013 -0800 @@ -26,6 +26,7 @@ import static com.oracle.graal.api.code.Register.*; import java.nio.*; +import java.util.*; import com.oracle.graal.api.code.*; import com.oracle.graal.api.code.Register.RegisterCategory; @@ -111,22 +112,23 @@ // @formatter:on - private final int supportedSSEVersion; - private final int supportedAVXVersion; - - public AMD64(int supportedSSEVersion, int supportedAVXVersion) { - super("AMD64", 8, ByteOrder.LITTLE_ENDIAN, true, allRegisters, LOAD_STORE | STORE_STORE, 1, r15.encoding + 1, 8); - assert supportedSSEVersion >= 2; - this.supportedSSEVersion = supportedSSEVersion; - this.supportedAVXVersion = supportedAVXVersion; + /** + * Basic set of CPU features mirroring what is returned from the cpuid instruction. + */ + public static enum CPUFeature { + SSE, SSE2, SSE3, SSE4, SSE4a, SSE4_1, SSE4_2, SSSE3, POPCNT, LZCNT, AVX, AVX2, ERMS, AMD_3DNOW_PREFETCH, AES, } - public int getSupportedSSEVersion() { - return supportedSSEVersion; + private final EnumSet features; + + public AMD64(EnumSet features) { + super("AMD64", 8, ByteOrder.LITTLE_ENDIAN, true, allRegisters, LOAD_STORE | STORE_STORE, 1, r15.encoding + 1, 8); + this.features = features; + assert features.contains(CPUFeature.SSE2) : "minimum config for x64"; } - public int getSupportedAVXVersion() { - return supportedAVXVersion; + public EnumSet getFeatures() { + return features; } @Override diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/ExceptionHandler.java --- a/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/ExceptionHandler.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/ExceptionHandler.java Tue Dec 17 21:26:42 2013 -0800 @@ -111,4 +111,9 @@ public String toString() { return "ExceptionHandler"; } + + @Override + public int hashCode() { + return catchTypeCPI ^ endBCI ^ handlerBCI; + } } diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.asm.amd64/src/com/oracle/graal/asm/amd64/AMD64Assembler.java --- a/graal/com.oracle.graal.asm.amd64/src/com/oracle/graal/asm/amd64/AMD64Assembler.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.asm.amd64/src/com/oracle/graal/asm/amd64/AMD64Assembler.java Tue Dec 17 21:26:42 2013 -0800 @@ -174,6 +174,10 @@ this.frameRegister = registerConfig == null ? null : registerConfig.getFrameRegister(); } + private boolean supports(CPUFeature feature) { + return ((AMD64) target.arch).getFeatures().contains(feature); + } + private static int encode(Register r) { assert r.encoding < 16 && r.encoding >= 0 : "encoding out of range: " + r.encoding; return r.encoding & 0x7; @@ -450,6 +454,7 @@ } public final void bsrq(Register dst, Register src) { + assert !supports(CPUFeature.LZCNT); int encode = prefixqAndEncode(dst.encoding, src.encoding); emitByte(0x0F); emitByte(0xBD); @@ -457,6 +462,7 @@ } public final void bsrq(Register dst, AMD64Address src) { + assert !supports(CPUFeature.LZCNT); prefixq(src, dst); emitByte(0x0F); emitByte(0xBD); @@ -464,6 +470,7 @@ } public final void bsrl(Register dst, Register src) { + assert !supports(CPUFeature.LZCNT); int encode = prefixAndEncode(dst.encoding, src.encoding); emitByte(0x0F); emitByte(0xBD); @@ -471,6 +478,7 @@ } public final void bsrl(Register dst, AMD64Address src) { + assert !supports(CPUFeature.LZCNT); prefix(src, dst); emitByte(0x0F); emitByte(0xBD); @@ -909,7 +917,7 @@ public final void movb(AMD64Address dst, Register src) { assert src.getRegisterCategory() == AMD64.CPU : "must have byte register"; - prefix(dst, src); // , true) + prefix(dst, src, true); emitByte(0x88); emitOperandHelper(src, dst); } @@ -1405,6 +1413,7 @@ } public final void popcntl(Register dst, AMD64Address src) { + assert supports(CPUFeature.POPCNT); emitByte(0xF3); prefix(src, dst); emitByte(0x0F); @@ -1413,6 +1422,7 @@ } public final void popcntl(Register dst, Register src) { + assert supports(CPUFeature.POPCNT); emitByte(0xF3); int encode = prefixAndEncode(dst.encoding, src.encoding); emitByte(0x0F); @@ -1421,6 +1431,7 @@ } public final void popcntq(Register dst, AMD64Address src) { + assert supports(CPUFeature.POPCNT); emitByte(0xF3); prefixq(src, dst); emitByte(0x0F); @@ -1429,6 +1440,7 @@ } public final void popcntq(Register dst, Register src) { + assert supports(CPUFeature.POPCNT); emitByte(0xF3); int encode = prefixqAndEncode(dst.encoding, src.encoding); emitByte(0x0F); @@ -1446,6 +1458,14 @@ emitByte(0x50 | encode); } + public void pushfq() { + emitByte(0x9c); + } + + public void popfq() { + emitByte(0x9D); + } + public final void ret(int imm16) { if (imm16 == 0) { emitByte(0xC3); @@ -1875,6 +1895,10 @@ } private void prefix(AMD64Address adr, Register reg) { + prefix(adr, reg, false); + } + + private void prefix(AMD64Address adr, Register reg, boolean byteinst) { if (reg.encoding < 8) { if (needsRex(adr.getBase())) { if (needsRex(adr.getIndex())) { @@ -1885,7 +1909,7 @@ } else { if (needsRex(adr.getIndex())) { emitByte(Prefix.REXX); - } else if (reg.encoding >= 4) { + } else if (byteinst && reg.encoding >= 4) { emitByte(Prefix.REX); } } @@ -2279,7 +2303,11 @@ subq(dst, imm32, false); } - public final void subq(Register dst, int imm32, boolean force32Imm) { + public final void subqWide(Register dst, int imm32) { + subq(dst, imm32, true); + } + + private void subq(Register dst, int imm32, boolean force32Imm) { prefixqAndEncode(dst.encoding); emitArith(0x81, 0xE8, dst, imm32, force32Imm); } @@ -2537,28 +2565,28 @@ } void prefetchr(AMD64Address src) { - // assert(VM_Version::supports_3dnow_prefetch(), "must support"); + assert supports(CPUFeature.AMD_3DNOW_PREFETCH); prefetchPrefix(src); emitByte(0x0D); emitOperandHelper(0, src); } public void prefetcht0(AMD64Address src) { - // NOT_LP64(assert(VM_Version::supports_sse(), "must support")); + assert supports(CPUFeature.SSE); prefetchPrefix(src); emitByte(0x18); emitOperandHelper(1, src); } public void prefetcht1(AMD64Address src) { - // NOT_LP64(assert(VM_Version::supports_sse(), "must support")); + assert supports(CPUFeature.SSE); prefetchPrefix(src); emitByte(0x18); emitOperandHelper(2, src); } public void prefetcht2(AMD64Address src) { - // NOT_LP64(assert(VM_Version::supports_sse(), "must support")); + assert supports(CPUFeature.SSE); prefix(src); emitByte(0x0f); emitByte(0x18); @@ -2566,7 +2594,7 @@ } public void prefetchw(AMD64Address src) { - // assert(VM_Version::supports_3dnow_prefetch(), "must support"); + assert supports(CPUFeature.AMD_3DNOW_PREFETCH); prefix(src); emitByte(0x0f); emitByte(0x0D); diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.compiler.hsail.test.infra/src/com/oracle/graal/compiler/hsail/test/infra/KernelTester.java --- a/graal/com.oracle.graal.compiler.hsail.test.infra/src/com/oracle/graal/compiler/hsail/test/infra/KernelTester.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.compiler.hsail.test.infra/src/com/oracle/graal/compiler/hsail/test/infra/KernelTester.java Tue Dec 17 21:26:42 2013 -0800 @@ -280,10 +280,13 @@ } /** - * This isEqualsFP method allows subclass to override what FP equality means for this particular - * unit test. + * Tests two floating point values for equality. */ public boolean isEqualsFP(double first, double second) { + // Special case for checking whether expected and actual values are both NaNs. + if (Double.isNaN(first) && Double.isNaN(second)) { + return true; + } return first == second; } diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/DoubleAbsTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/DoubleAbsTest.java Tue Dec 17 21:26:42 2013 -0800 @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.graal.compiler.hsail.test; + +import org.junit.Test; +import com.oracle.graal.compiler.hsail.test.infra.*; + +/** + * Tests intrinsic for calls to Math.abs(double). Generates a abs_f64 instruction. + */ +public class DoubleAbsTest extends GraalKernelTester { + + static final int num = 40; + // Output array storing the results of calling Math.abs(). + @Result protected double[] outArray = new double[num]; + + /** + * The static "kernel" method we will be testing. This method calls Math.abs() on an element of + * an input array and writes the result to the corresponding index of an output array. By + * convention the gid is the last parameter. + * + * @param out the output array. + * @param ina the input array. + * @param gid the parameter used to index into the input and output arrays. + */ + public static void run(double[] out, double[] ina, int gid) { + out[gid] = Math.abs(ina[gid]); + } + + /** + * Tests the HSAIL code generated for this unit test by comparing the result of executing this + * code with the result of executing a sequential Java version of this unit test. + */ + @Test + public void test() { + super.testGeneratedHsail(); + } + + /** + * Initializes the input and output arrays passed to the run routine. + * + * @param in the input array. + */ + void setupArrays(double[] in) { + for (int i = 0; i < num; i++) { + // Include positive and negative values as well as corner cases. + if (i == 1) { + in[i] = Double.NaN; + } else if (i == 2) { + in[i] = Double.NEGATIVE_INFINITY; + } else if (i == 3) { + in[i] = Double.POSITIVE_INFINITY; + } else if (i == 4) { + in[i] = -0.0; + } else { + in[i] = i < num / 2 ? i : -i; + } + outArray[i] = 0; + } + } + + /** + * Dispatches the HSAIL kernel for this test case. + */ + @Override + public void runTest() { + double[] inArray = new double[num]; + setupArrays(inArray); + dispatchMethodKernel(num, outArray, inArray); + } +} diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/DoubleCeilTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/DoubleCeilTest.java Tue Dec 17 21:26:42 2013 -0800 @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.graal.compiler.hsail.test; + +import org.junit.Test; +import com.oracle.graal.compiler.hsail.test.infra.*; + +/** + * Tests intrinsic for calls to Math.ceil(). Generates a ceil_f64 instruction. + */ +public class DoubleCeilTest extends GraalKernelTester { + + static final int num = 40; + // Output array storing the results. + @Result protected double[] outArray = new double[num]; + + /** + * The static "kernel" method we will be testing. This method calls Math.ceil() on an element of + * an input array and writes the result to the corresponding index of an output array. By + * convention the gid is the last parameter. + * + * @param out the output array. + * @param ina the input array. + * @param gid the parameter used to index into the input and output arrays. + */ + public static void run(double[] out, double[] ina, int gid) { + out[gid] = Math.ceil(ina[gid]); + } + + /** + * Tests the HSAIL code generated for this unit test by comparing the result of executing this + * code with the result of executing a sequential Java version of this unit test. + */ + @Test + public void test() { + super.testGeneratedHsail(); + } + + /** + * Initializes the input and output arrays passed to the run routine. + * + * @param in the input array. + */ + void setupArrays(double[] in) { + // Initialize arrays with a mix of positive and negativ values and any corner cases. + for (int i = 0; i < num; i++) { + if (i == 0) { + in[i] = 0.0; + } else if (i == 1) { + in[i] = -0.0; + } else if (i == 2) { + in[i] = Double.NaN; + } else if (i == 3) { + in[i] = Double.NEGATIVE_INFINITY; + } else if (i == 4) { + in[i] = Double.POSITIVE_INFINITY; + } else { + in[i] = i < num / 2 ? i + 0.5 : -i - 0.5; + } + outArray[i] = 0; + } + } + + /** + * Dispatches the HSAIL kernel for this test case. + */ + @Override + public void runTest() { + double[] inArray = new double[num]; + setupArrays(inArray); + dispatchMethodKernel(num, outArray, inArray); + } +} diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/DoubleFloorTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/DoubleFloorTest.java Tue Dec 17 21:26:42 2013 -0800 @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.graal.compiler.hsail.test; + +import org.junit.Test; +import com.oracle.graal.compiler.hsail.test.infra.*; + +/** + * Tests intrinsic for calls to Math.floor(). Generates a floor_f64 instruction. + */ +public class DoubleFloorTest extends GraalKernelTester { + + static final int num = 40; + // Output array storing the results. + @Result protected double[] outArray = new double[num]; + + /** + * The static "kernel" method we will be testing. This method calls Math.floor() on an element + * of an input array and writes the result to the corresponding index of an output array. By + * convention the gid is the last parameter. + * + * @param out the output array. + * @param ina the input array. + * @param gid the parameter used to index into the input and output arrays. + */ + public static void run(double[] out, double[] ina, int gid) { + out[gid] = Math.floor(ina[gid]); + } + + /** + * Tests the HSAIL code generated for this unit test by comparing the result of executing this + * code with the result of executing a sequential Java version of this unit test. + */ + @Test + public void test() { + super.testGeneratedHsail(); + } + + /** + * Initializes the input and output arrays passed to the run routine. + * + * @param in the input array. + */ + void setupArrays(double[] in) { + // Initialize input array with a mix of positive and negative values and any corner cases. + for (int i = 0; i < num; i++) { + if (i == 0) { + in[i] = 0.0; + } else if (i == 1) { + in[i] = -0.0; + } else if (i == 2) { + in[i] = Double.NaN; + } else if (i == 3) { + in[i] = Double.NEGATIVE_INFINITY; + } else if (i == 4) { + in[i] = Double.POSITIVE_INFINITY; + } else { + in[i] = i < num / 2 ? i + 0.5 : -i - 0.5; + } + outArray[i] = 0; + } + } + + /** + * Dispatches the HSAIL kernel for this test case. + */ + @Override + public void runTest() { + double[] inArray = new double[num]; + setupArrays(inArray); + dispatchMethodKernel(num, outArray, inArray); + } +} diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/DoubleRintTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/DoubleRintTest.java Tue Dec 17 21:26:42 2013 -0800 @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.graal.compiler.hsail.test; + +import org.junit.*; + +import com.oracle.graal.compiler.hsail.test.infra.*; + +/** + * Tests intrinsic for Math.rint(). Generates an rint_f64 instruction. + */ +public class DoubleRintTest extends GraalKernelTester { + + static final int size = 64; + @Result double[] out = new double[size]; + + /** + * The static "kernel" method we will be testing. This method calls Math.rint() on an element of + * an input array and writes the result to the corresponding index of an output array. By + * convention the gid is the last parameter. + * + * @param out the output array. + * @param in the input array. + * @param gid the parameter used to index into the input and output arrays. + */ + public static void run(double[] out, double[] in, int gid) { + out[gid] = Math.rint(in[gid]); + } + + /** + * Initialize input arrays. + * + * @param in the input array + */ + void setupArrays(double[] in) { + // Initialize input array with a mix of positive and negative values and corner cases. + for (int i = 0; i < size; i++) { + if (i == 1) { + in[i] = Double.NaN; + } else if (i == 2) { + in[i] = Double.NEGATIVE_INFINITY; + } else if (i == 3) { + in[i] = Double.POSITIVE_INFINITY; + + } else if (i == 4) { + in[i] = 0.0; + } else if (i == 5) { + in[i] = -0.0; + } else { + in[i] = i < size / 2 ? i + 0.5 : -i - 0.6; + } + out[i] = 0; + } + } + + /** + * Dispatches the HSAIL kernel for this test case. + */ + @Override + public void runTest() { + double[] inArray = new double[size]; + setupArrays(inArray); + dispatchMethodKernel(size, out, inArray); + } + + /** + * Tests the HSAIL code generated for this unit test by comparing the result of executing this + * code with the result of executing a sequential Java version of this unit test. + */ + @Test + public void test() { + testGeneratedHsail(); + } +} diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/DoubleSqrtTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/DoubleSqrtTest.java Tue Dec 17 21:26:42 2013 -0800 @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.graal.compiler.hsail.test; + +import org.junit.*; + +import com.oracle.graal.compiler.hsail.test.infra.*; + +/** + * Tests intrinsic for call to Math.sqrt(double). Generates a sqrt_f64 instruction. + */ +public class DoubleSqrtTest extends GraalKernelTester { + + static final int size = 64; + @Result double[] out = new double[size]; + + /** + * The static "kernel" method we will be testing. This method calls Math.sqrt() on an element of + * an input array and writes the result to the corresponding index of an output array. By + * convention the gid is the last parameter. + * + * @param out the output array. + * @param in the input array. + * @param gid the parameter used to index into the input and output arrays. + */ + public static void run(double[] in, double[] out, int gid) { + out[gid] = Math.sqrt(in[gid]); + } + + /** + * Initializes the input and output arrays passed to the run routine. + * + * @param in the input array. + */ + void setupArrays(double[] in) { + for (int i = 0; i < size; i++) { + // Include positive and negative values as well as corner cases. + if (i == 1) { + in[i] = Double.NaN; + } else if (i == 2) { + in[i] = Double.NEGATIVE_INFINITY; + } else if (i == 3) { + in[i] = Double.POSITIVE_INFINITY; + } else if (i == 4) { + in[i] = -0.0; + } else if (i > 5 && i < 10) { + in[i] = i + 0.5; + } else { + in[i] = i < size / 2 ? i : -i; + } + out[i] = 0; + } + } + + /** + * Dispatches the HSAIL kernel for this test case. + */ + @Override + public void runTest() { + double[] inArray = new double[size]; + setupArrays(inArray); + dispatchMethodKernel(size, inArray, out); + } + + /** + * Tests the HSAIL code generated for this unit test by comparing the result of executing this + * code with the result of executing a sequential Java version of this unit test. + */ + @Test + public void test() { + testGeneratedHsail(); + } +} diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/FloatAbsTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/FloatAbsTest.java Tue Dec 17 21:26:42 2013 -0800 @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.graal.compiler.hsail.test; + +import org.junit.Test; +import com.oracle.graal.compiler.hsail.test.infra.*; + +/** + * Tests intrinsic for calls to Math.abs(float). Generates an abs_f32 instruction. + */ +public class FloatAbsTest extends GraalKernelTester { + + static final int num = 40; + // Output array storing the results of calling Math.abs(). + @Result protected float[] outArray = new float[num]; + + /** + * The static "kernel" method we will be testing. This method calls Math.abs() on an element of + * an input array and writes the result to the corresponding index of an output array. By + * convention the gid is the last parameter. + * + * @param out the output array. + * @param ina the input array. + * @param gid the parameter used to index into the input and output arrays. + */ + public static void run(float[] out, float[] ina, int gid) { + out[gid] = Math.abs(ina[gid]); + } + + /** + * Tests the HSAIL code generated for this unit test by comparing the result of executing this + * code with the result of executing a sequential Java version of this unit test. + */ + @Test + public void test() { + super.testGeneratedHsail(); + } + + /** + * Initializes the input and output arrays passed to the run routine. + * + * @param in the input array. + */ + void setupArrays(float[] in) { + for (int i = 0; i < num; i++) { + // Initialize array with positive and negative values as well as corner cases. + if (i == 1) { + in[i] = Float.NaN; + } else if (i == 2) { + in[i] = Float.NEGATIVE_INFINITY; + } else if (i == 3) { + in[i] = Float.POSITIVE_INFINITY; + } else if (i == 4) { + in[i] = -0; + } else { + in[i] = i < num / 2 ? i : -i; + } + outArray[i] = 0; + } + } + + /** + * Dispatches the HSAIL kernel for this test case. + */ + @Override + public void runTest() { + float[] inArray = new float[num]; + setupArrays(inArray); + dispatchMethodKernel(num, outArray, inArray); + } +} diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/FloatDivPrecisionTest.java --- a/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/FloatDivPrecisionTest.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/FloatDivPrecisionTest.java Tue Dec 17 21:26:42 2013 -0800 @@ -47,10 +47,6 @@ } - /** - * Allows subclass to override what FP equality means for this particular unit test. - * - */ @Override public boolean isEqualsFP(double first, double second) { return Math.abs(first - second) == 0; diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/FloatSqrtTest.java --- a/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/FloatSqrtTest.java Tue Dec 17 20:22:45 2013 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package com.oracle.graal.compiler.hsail.test; - -import org.junit.*; - -import com.oracle.graal.compiler.hsail.test.infra.*; - -/** - * Tests floating point square root. - */ -public class FloatSqrtTest extends GraalKernelTester { - - static final int size = 64; - float[] input = new float[size]; - @Result float[] output = new float[size]; - { - for (int i = 0; i < size; i++) { - input[i] = i; - output[i] = -1.0f; - } - - } - - public static void run(float[] input1, float[] output1, int gid) { - output1[gid] = (float) Math.sqrt(input1[gid]); - } - - @Override - public void runTest() { - dispatchMethodKernel(size, input, output); - } - - @Test - public void test() { - testGeneratedHsail(); - } -} diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/IntAbsTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/IntAbsTest.java Tue Dec 17 21:26:42 2013 -0800 @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.graal.compiler.hsail.test; + +import org.junit.Test; +import com.oracle.graal.compiler.hsail.test.infra.*; + +/** + * Tests intrinsic for call to Math.abs(int). Generates an abs_s32 instruction. + */ +public class IntAbsTest extends GraalKernelTester { + + static final int num = 20; + // Output array storing the results of negation operations. + @Result protected int[] outArray = new int[num]; + + /** + * The static "kernel" method we will be testing. This method calls Math.abs( ) on an element of + * an input array and writes the result to the corresponding index of an output array. By + * convention the gid is the last parameter. + * + * @param out the output array. + * @param ina the input array. + * @param gid the parameter used to index into the input and output arrays. + */ + public static void run(int[] out, int[] ina, int gid) { + out[gid] = Math.abs(ina[gid]); + } + + /** + * Tests the HSAIL code generated for this unit test by comparing the result of executing this + * code with the result of executing a sequential Java version of this unit test. + */ + @Test + public void test() { + super.testGeneratedHsail(); + } + + /** + * Initializes the input and output arrays passed to the run routine. + * + * @param in the input array. + */ + void setupArrays(int[] in) { + // initialize input array with a mix of positive and negative values and any corner cases. + for (int i = 0; i < num; i++) { + if (i == 1) { + in[i] = Integer.MIN_VALUE; + } else { + in[i] = i < num / 2 ? i : -i; + } + outArray[i] = 0; + } + } + + /** + * Dispatches the HSAIL kernel for this test case. + */ + @Override + public void runTest() { + int[] inArray = new int[num]; + setupArrays(inArray); + dispatchMethodKernel(num, outArray, inArray); + } +} diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/LongAbsTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/LongAbsTest.java Tue Dec 17 21:26:42 2013 -0800 @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.graal.compiler.hsail.test; + +import org.junit.Test; +import com.oracle.graal.compiler.hsail.test.infra.*; + +/** + * Tests intrinsic for call to Math.abs(long). Generates an abs_s64 instruction. + */ +public class LongAbsTest extends GraalKernelTester { + + static final int num = 20; + // Output array storing the results of the operations. + @Result protected long[] outArray = new long[num]; + + /** + * The static "kernel" method we will be testing. This method calls Math.abs( ) on an element of + * an input array and writes the result to the corresponding index of an output array. By + * convention the gid is the last parameter. + * + * @param out the output array. + * @param ina the input array. + * @param gid the parameter used to index into the input and output arrays. + */ + public static void run(long[] out, long[] ina, int gid) { + out[gid] = Math.abs(ina[gid]); + } + + /** + * Tests the HSAIL code generated for this unit test by comparing the result of executing this + * code with the result of executing a sequential Java version of this unit test. + */ + @Test + public void test() { + super.testGeneratedHsail(); + } + + /** + * Initializes the input and output arrays passed to the run routine. + * + * @param in the input array. + */ + void setupArrays(long[] in) { + // Initialize input array with a mix of positive and negative values and any corner cases. + for (int i = 0; i < num; i++) { + if (i == 1) { + in[i] = Long.MIN_VALUE; + } else { + in[i] = i < num / 2 ? i : -i; + } + outArray[i] = 0; + } + } + + /** + * Dispatches the HSAIL kernel for this test case. + */ + @Override + public void runTest() { + long[] inArray = new long[num]; + setupArrays(inArray); + dispatchMethodKernel(num, outArray, inArray); + } +} diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.compiler.hsail/src/com/oracle/graal/compiler/hsail/HSAILLIRGenerator.java --- a/graal/com.oracle.graal.compiler.hsail/src/com/oracle/graal/compiler/hsail/HSAILLIRGenerator.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.compiler.hsail/src/com/oracle/graal/compiler/hsail/HSAILLIRGenerator.java Tue Dec 17 21:26:42 2013 -0800 @@ -646,11 +646,61 @@ throw GraalInternalError.unimplemented(); } + /** + * Emits the LIR code for the {@link HSAILArithmetic#ABS} operation. + * + * @param input the source operand + * @return Value representing the result of the operation + */ @Override public Value emitMathAbs(Value input) { - throw GraalInternalError.unimplemented(); + Variable result = newVariable(input.getPlatformKind()); + append(new Op1Stack(ABS, result, input)); + return result; + } + + /** + * Emits the LIR code for the {@link HSAILArithmetic#CEIL} operation. + * + * @param input the source operand + * @return Value representing the result of the operation + */ + public Value emitMathCeil(Value input) { + Variable result = newVariable(input.getPlatformKind()); + append(new Op1Stack(CEIL, result, input)); + return result; } + /** + * Emits the LIR code for the {@link HSAILArithmetic#FLOOR} operation. + * + * @param input the source operand + * @return Value representing the result of the operation + */ + public Value emitMathFloor(Value input) { + Variable result = newVariable(input.getPlatformKind()); + append(new Op1Stack(FLOOR, result, input)); + return result; + } + + /** + * Emits the LIR code for the {@link HSAILArithmetic#RINT} operation. + * + * @param input the source operand + * @return Value representing the result of the operation + */ + public Value emitMathRint(Value input) { + Variable result = newVariable(input.getPlatformKind()); + append(new Op1Stack(RINT, result, input)); + return result; + } + + /** + * Emits the LIR code for the {@link HSAILArithmetic#SQRT} operation. + * + * @param input the source operand + * @return value representing the result of the operation + */ @Override public Value emitMathSqrt(Value input) { Variable result = newVariable(input.getPlatformKind()); diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/CommonedConstantsTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/CommonedConstantsTest.java Tue Dec 17 21:26:42 2013 -0800 @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2012, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.compiler.test; + +import java.lang.reflect.*; + +import org.junit.*; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.phases.common.*; + +/** + * Tests any optimization that commons loads of non-inlineable constants. + */ +public class CommonedConstantsTest extends GraalCompilerTest { + + public static final String[] array = {"1", "2", null}; + + // A method where a constant is used on the normal and exception edge of a non-inlined call. + // The dominating block of both usages is the block containing the call. + public static Object test0Snippet(String[] arr, int i) { + Object result = null; + try { + result = Array.get(arr, i); + } catch (ArrayIndexOutOfBoundsException e) { + result = array[0]; + } + if (result == null) { + result = array[2]; + } + return result; + } + + @Test + public void test0() { + // Ensure the exception path is profiled + ResolvedJavaMethod javaMethod = getMetaAccess().lookupJavaMethod(getMethod("test0Snippet")); + javaMethod.reprofile(); + test0Snippet(array, array.length); + + test("test0Snippet", array, 0); + test("test0Snippet", array, 2); + test("test0Snippet", array, 3); + test("test0Snippet", array, 1); + } + + public static final char[] alphabet = "abcdefghijklmnopqrstuvwxyz".toCharArray(); + + static int noninlineLength(char[] s) { + return s.length; + } + + /** + * A constant with usages before and after a non-inlined call. + */ + public static int test1Snippet(String s) { + if (s == null) { + return noninlineLength(alphabet) + 1; + } + char[] sChars = s.toCharArray(); + int count = 0; + for (int i = 0; i < alphabet.length && i < sChars.length; i++) { + if (alphabet[i] == sChars[i]) { + count++; + } + } + return count; + } + + @Test + public void test1() { + getSuites().getHighTier().findPhase(AbstractInliningPhase.class).remove(); + test1Snippet(new String(alphabet)); + + test("test1Snippet", (Object) null); + test("test1Snippet", "test1Snippet"); + test("test1Snippet", ""); + } + + /** + * A constant with only usage in a loop. + */ + public static int test2Snippet(String s) { + char[] sChars = s.toCharArray(); + int count = 0; + for (int i = 0; i < alphabet.length && i < sChars.length; i++) { + if (alphabet[i] == sChars[i]) { + count++; + } + } + return count; + } + + @Test + public void test2() { + assert getSuites().getHighTier().findPhase(AbstractInliningPhase.class).hasNext(); + test2Snippet(new String(alphabet)); + + test("test2Snippet", (Object) null); + test("test2Snippet", "test1Snippet"); + test("test2Snippet", ""); + } +} diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/MemoryScheduleTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/MemoryScheduleTest.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/MemoryScheduleTest.java Tue Dec 17 21:26:42 2013 -0800 @@ -87,7 +87,7 @@ @Test public void testSimple() { for (TestMode mode : TestMode.values()) { - SchedulePhase schedule = getFinalSchedule("testSimpleSnippet", mode, MemoryScheduling.OPTIMAL); + SchedulePhase schedule = getFinalSchedule("testSimpleSnippet", mode); StructuredGraph graph = schedule.getCFG().graph; assertReadAndWriteInSameBlock(schedule, true); assertOrderedAfterSchedule(schedule, graph.getNodes().filter(FloatingReadNode.class).first(), graph.getNodes().filter(WriteNode.class).first()); @@ -112,7 +112,7 @@ @Test public void testSplit1() { for (TestMode mode : TestMode.values()) { - SchedulePhase schedule = getFinalSchedule("testSplit1Snippet", mode, MemoryScheduling.OPTIMAL); + SchedulePhase schedule = getFinalSchedule("testSplit1Snippet", mode); assertReadWithinStartBlock(schedule, true); assertReadWithinReturnBlock(schedule, false); } @@ -135,7 +135,7 @@ @Test public void testSplit2() { - SchedulePhase schedule = getFinalSchedule("testSplit2Snippet", TestMode.WITHOUT_FRAMESTATES, MemoryScheduling.OPTIMAL); + SchedulePhase schedule = getFinalSchedule("testSplit2Snippet", TestMode.WITHOUT_FRAMESTATES); assertReadWithinStartBlock(schedule, false); assertReadWithinReturnBlock(schedule, true); } @@ -159,7 +159,7 @@ @Test public void testLoop1() { - SchedulePhase schedule = getFinalSchedule("testLoop1Snippet", TestMode.WITHOUT_FRAMESTATES, MemoryScheduling.OPTIMAL); + SchedulePhase schedule = getFinalSchedule("testLoop1Snippet", TestMode.WITHOUT_FRAMESTATES); assertEquals(6, schedule.getCFG().getBlocks().length); assertReadWithinStartBlock(schedule, true); assertReadWithinReturnBlock(schedule, false); @@ -184,7 +184,7 @@ @Test public void testLoop2() { - SchedulePhase schedule = getFinalSchedule("testLoop2Snippet", TestMode.WITHOUT_FRAMESTATES, MemoryScheduling.OPTIMAL); + SchedulePhase schedule = getFinalSchedule("testLoop2Snippet", TestMode.WITHOUT_FRAMESTATES); assertEquals(6, schedule.getCFG().getBlocks().length); assertReadWithinStartBlock(schedule, false); assertReadWithinReturnBlock(schedule, true); @@ -206,7 +206,7 @@ @Test public void testLoop3() { - SchedulePhase schedule = getFinalSchedule("testLoop3Snippet", TestMode.WITHOUT_FRAMESTATES, MemoryScheduling.OPTIMAL); + SchedulePhase schedule = getFinalSchedule("testLoop3Snippet", TestMode.WITHOUT_FRAMESTATES); assertEquals(7, schedule.getCFG().getBlocks().length); assertReadWithinStartBlock(schedule, true); assertReadWithinReturnBlock(schedule, false); @@ -218,7 +218,7 @@ @Test public void testStringReplace() { - getFinalSchedule("testStringReplaceSnippet", TestMode.INLINED_WITHOUT_FRAMESTATES, MemoryScheduling.OPTIMAL); + getFinalSchedule("testStringReplaceSnippet", TestMode.INLINED_WITHOUT_FRAMESTATES); test("testStringReplaceSnippet", "acbaaa"); } @@ -242,7 +242,7 @@ @Test public void testLoop5() { - SchedulePhase schedule = getFinalSchedule("testLoop5Snippet", TestMode.WITHOUT_FRAMESTATES, MemoryScheduling.OPTIMAL); + SchedulePhase schedule = getFinalSchedule("testLoop5Snippet", TestMode.WITHOUT_FRAMESTATES); assertEquals(7, schedule.getCFG().getBlocks().length); assertReadWithinStartBlock(schedule, false); assertReadWithinReturnBlock(schedule, false); @@ -258,7 +258,7 @@ @Test public void testArrayCopy() { - SchedulePhase schedule = getFinalSchedule("testArrayCopySnippet", TestMode.INLINED_WITHOUT_FRAMESTATES, MemoryScheduling.OPTIMAL); + SchedulePhase schedule = getFinalSchedule("testArrayCopySnippet", TestMode.INLINED_WITHOUT_FRAMESTATES); StructuredGraph graph = schedule.getCFG().getStartBlock().getBeginNode().graph(); ReturnNode ret = graph.getNodes().filter(ReturnNode.class).first(); assertTrue(ret.result() instanceof FloatingReadNode); @@ -279,7 +279,7 @@ @Test public void testIfRead1() { - SchedulePhase schedule = getFinalSchedule("testIfRead1Snippet", TestMode.WITHOUT_FRAMESTATES, MemoryScheduling.OPTIMAL); + SchedulePhase schedule = getFinalSchedule("testIfRead1Snippet", TestMode.WITHOUT_FRAMESTATES); assertEquals(4, schedule.getCFG().getBlocks().length); assertReadWithinStartBlock(schedule, true); assertReadAndWriteInSameBlock(schedule, false); @@ -300,7 +300,7 @@ @Test public void testIfRead2() { - SchedulePhase schedule = getFinalSchedule("testIfRead2Snippet", TestMode.WITHOUT_FRAMESTATES, MemoryScheduling.OPTIMAL); + SchedulePhase schedule = getFinalSchedule("testIfRead2Snippet", TestMode.WITHOUT_FRAMESTATES); assertEquals(4, schedule.getCFG().getBlocks().length); assertEquals(1, schedule.getCFG().graph.getNodes().filter(FloatingReadNode.class).count()); assertReadWithinStartBlock(schedule, false); @@ -322,7 +322,7 @@ @Test public void testIfRead3() { - SchedulePhase schedule = getFinalSchedule("testIfRead3Snippet", TestMode.WITHOUT_FRAMESTATES, MemoryScheduling.OPTIMAL); + SchedulePhase schedule = getFinalSchedule("testIfRead3Snippet", TestMode.WITHOUT_FRAMESTATES); assertEquals(4, schedule.getCFG().getBlocks().length); assertReadWithinStartBlock(schedule, false); assertReadWithinReturnBlock(schedule, true); @@ -343,7 +343,7 @@ @Test public void testIfRead4() { - SchedulePhase schedule = getFinalSchedule("testIfRead4Snippet", TestMode.WITHOUT_FRAMESTATES, MemoryScheduling.OPTIMAL); + SchedulePhase schedule = getFinalSchedule("testIfRead4Snippet", TestMode.WITHOUT_FRAMESTATES); assertEquals(4, schedule.getCFG().getBlocks().length); assertReadWithinStartBlock(schedule, false); assertReadWithinReturnBlock(schedule, false); @@ -362,7 +362,7 @@ @Test public void testIfRead5() { - SchedulePhase schedule = getFinalSchedule("testIfRead5Snippet", TestMode.WITHOUT_FRAMESTATES, MemoryScheduling.OPTIMAL); + SchedulePhase schedule = getFinalSchedule("testIfRead5Snippet", TestMode.WITHOUT_FRAMESTATES); assertEquals(4, schedule.getCFG().getBlocks().length); assertReadWithinStartBlock(schedule, false); assertReadWithinReturnBlock(schedule, true); @@ -388,7 +388,7 @@ @Test public void testBlockSchedule() { - SchedulePhase schedule = getFinalSchedule("testBlockScheduleSnippet", TestMode.WITHOUT_FRAMESTATES, MemoryScheduling.OPTIMAL); + SchedulePhase schedule = getFinalSchedule("testBlockScheduleSnippet", TestMode.WITHOUT_FRAMESTATES); StructuredGraph graph = schedule.getCFG().graph; NodeIterable writeNodes = graph.getNodes().filter(WriteNode.class); @@ -447,7 +447,7 @@ @Test public void testProxy1() { - SchedulePhase schedule = getFinalSchedule("testProxy1Snippet", TestMode.WITHOUT_FRAMESTATES, MemoryScheduling.OPTIMAL); + SchedulePhase schedule = getFinalSchedule("testProxy1Snippet", TestMode.WITHOUT_FRAMESTATES); assertReadWithinStartBlock(schedule, true); // read of container.a should be in start block /* * read of container.b for increment operation should be in return block. TODO: not sure @@ -473,7 +473,7 @@ @Test public void testProxy2() { - SchedulePhase schedule = getFinalSchedule("testProxy2Snippet", TestMode.WITHOUT_FRAMESTATES, MemoryScheduling.OPTIMAL); + SchedulePhase schedule = getFinalSchedule("testProxy2Snippet", TestMode.WITHOUT_FRAMESTATES); assertReadWithinStartBlock(schedule, false); assertReadWithinReturnBlock(schedule, false); } @@ -496,7 +496,7 @@ @Test public void testStringHashCode() { - SchedulePhase schedule = getFinalSchedule("testStringHashCodeSnippet", TestMode.WITHOUT_FRAMESTATES, MemoryScheduling.OPTIMAL); + SchedulePhase schedule = getFinalSchedule("testStringHashCodeSnippet", TestMode.WITHOUT_FRAMESTATES); assertReadWithinStartBlock(schedule, true); assertReadWithinReturnBlock(schedule, false); @@ -528,7 +528,7 @@ @Test public void testLoop4() { - SchedulePhase schedule = getFinalSchedule("testLoop4Snippet", TestMode.WITHOUT_FRAMESTATES, MemoryScheduling.OPTIMAL); + SchedulePhase schedule = getFinalSchedule("testLoop4Snippet", TestMode.WITHOUT_FRAMESTATES); assertReadWithinStartBlock(schedule, false); assertReadWithinReturnBlock(schedule, false); } @@ -573,6 +573,10 @@ assertTrue(!(inSame ^ schedule.getCFG().blockFor(read) == schedule.getCFG().blockFor(write))); } + private SchedulePhase getFinalSchedule(final String snippet, final TestMode mode) { + return getFinalSchedule(snippet, mode, MemoryScheduling.OPTIMAL); + } + private SchedulePhase getFinalSchedule(final String snippet, final TestMode mode, final MemoryScheduling memsched) { return getFinalSchedule(snippet, mode, memsched, SchedulingStrategy.LATEST_OUT_OF_LOOPS); } diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java Tue Dec 17 21:26:42 2013 -0800 @@ -262,14 +262,13 @@ for (Block b : lir.linearScanOrder()) { emitBlock(lirGen, b); } + lirGen.beforeRegisterAllocation(); Debug.dump(lir, "After LIR generation"); } catch (Throwable e) { throw Debug.handle(e); } - lirGen.beforeRegisterAllocation(); - try (Scope s = Debug.scope("Allocator")) { if (backend.shouldAllocateRegisters()) { new LinearScan(target, lir, lirGen, frameMap).allocate(); diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalDebugConfig.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalDebugConfig.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalDebugConfig.java Tue Dec 17 21:26:42 2013 -0800 @@ -110,6 +110,10 @@ return isEnabled(logFilter); } + public boolean isLogEnabledForMethod() { + return isEnabledForMethod(logFilter); + } + public boolean isMeterEnabled() { return isEnabled(meterFilter); } @@ -118,6 +122,10 @@ return isEnabled(dumpFilter); } + public boolean isDumpEnabledForMethod() { + return isEnabledForMethod(dumpFilter); + } + public boolean isTimeEnabled() { return isEnabled(timerFilter); } @@ -130,6 +138,10 @@ return checkDebugFilter(Debug.currentScope(), filter) && checkMethodFilter(); } + private boolean isEnabledForMethod(DebugFilter filter) { + return filter != null && checkMethodFilter(); + } + private static boolean checkDebugFilter(String currentScope, DebugFilter filter) { return filter != null && filter.matches(currentScope); } diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/Interval.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/Interval.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/Interval.java Tue Dec 17 21:26:42 2013 -0800 @@ -418,7 +418,8 @@ /** * The {@linkplain RegisterValue register} or {@linkplain StackSlot spill slot} assigned to this - * interval. + * interval. In case of a spilled interval which is re-materialized this is + * {@link Value#ILLEGAL}. */ private AllocatableValue location; @@ -498,6 +499,17 @@ */ private Interval locationHint; + /** + * The value with which a spilled child interval can be re-materialized. Currently this must be + * a Constant. + */ + private Constant materializedValue; + + /** + * The number of times {@link #addMaterializationValue(Constant)} is called. + */ + private int numMaterializationValuesAdded; + void assignLocation(AllocatableValue newLocation) { if (isRegister(newLocation)) { assert this.location == null : "cannot re-assign location for " + this; @@ -505,6 +517,8 @@ this.location = asRegister(newLocation).asValue(kind); return; } + } else if (isIllegal(newLocation)) { + assert canMaterialize(); } else { assert this.location == null || isRegister(this.location) : "cannot re-assign location for " + this; assert isStackSlot(newLocation); @@ -621,7 +635,7 @@ // returns true if this interval has a shadow copy on the stack that is always correct boolean alwaysInMemory() { - return splitParent().spillState == SpillState.StoreAtDefinition || splitParent().spillState == SpillState.StartInMemory; + return (splitParent().spillState == SpillState.StoreAtDefinition || splitParent().spillState == SpillState.StartInMemory) && !canMaterialize(); } void removeFirstUsePos() { @@ -693,6 +707,34 @@ currentSplitChild = this; } + /** + * Sets the value which is used for re-materialization. + */ + void addMaterializationValue(Constant value) { + if (numMaterializationValuesAdded == 0) { + materializedValue = value; + } else { + // Interval is defined on multiple places -> no materialization is possible. + materializedValue = null; + } + numMaterializationValuesAdded++; + } + + /** + * Returns true if this interval can be re-materialized when spilled. This means that no + * spill-moves are needed. Instead of restore-moves the materializeValue is restored. + */ + public boolean canMaterialize() { + return getMaterializedValue() != null; + } + + /** + * Returns the value which is moved to a register instead of a restore-move from stack. + */ + public Constant getMaterializedValue() { + return splitParent().materializedValue; + } + int calcTo() { assert first != Range.EndMarker : "interval has no range"; @@ -864,12 +906,24 @@ } } + private RegisterPriority adaptPriority(RegisterPriority priority) { + /* + * In case of re-materialized values we require that use-operands are registers, because we + * don't have the value at a stack location. (Note that ShouldHaveRegister means that the + * operand can also be a StackSlot). + */ + if (priority == RegisterPriority.ShouldHaveRegister && canMaterialize()) { + return RegisterPriority.MustHaveRegister; + } + return priority; + } + // Note: use positions are sorted descending . first use has highest index int firstUsage(RegisterPriority minRegisterPriority) { assert isVariable(operand) : "cannot access use positions for fixed intervals"; for (int i = usePosList.size() - 1; i >= 0; --i) { - RegisterPriority registerPriority = usePosList.registerPriority(i); + RegisterPriority registerPriority = adaptPriority(usePosList.registerPriority(i)); if (registerPriority.greaterEqual(minRegisterPriority)) { return usePosList.usePos(i); } @@ -882,7 +936,7 @@ for (int i = usePosList.size() - 1; i >= 0; --i) { int usePos = usePosList.usePos(i); - if (usePos >= from && usePosList.registerPriority(i).greaterEqual(minRegisterPriority)) { + if (usePos >= from && adaptPriority(usePosList.registerPriority(i)).greaterEqual(minRegisterPriority)) { return usePos; } } @@ -894,7 +948,7 @@ for (int i = usePosList.size() - 1; i >= 0; --i) { int usePos = usePosList.usePos(i); - if (usePos >= from && usePosList.registerPriority(i) == exactRegisterPriority) { + if (usePos >= from && adaptPriority(usePosList.registerPriority(i)) == exactRegisterPriority) { return usePos; } } @@ -910,7 +964,7 @@ if (usePos > from) { return prev; } - if (usePosList.registerPriority(i).greaterEqual(minRegisterPriority)) { + if (adaptPriority(usePosList.registerPriority(i)).greaterEqual(minRegisterPriority)) { prev = usePos; } } @@ -1181,6 +1235,10 @@ buf.append(usePosList.usePos(i)).append(':').append(usePosList.registerPriority(i)); prev = usePosList.usePos(i); } - return buf.append("} spill-state{").append(spillState()).append("}").toString(); + buf.append("} spill-state{").append(spillState()).append("}"); + if (canMaterialize()) { + buf.append(" (remat:").append(getMaterializedValue().toString()).append(")"); + } + return buf.toString(); } } diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/LinearScan.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/LinearScan.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/LinearScan.java Tue Dec 17 21:26:42 2013 -0800 @@ -263,11 +263,11 @@ } }; - static final IntervalPredicate IS_OOP_INTERVAL = new IntervalPredicate() { + static final IntervalPredicate IS_STACK_INTERVAL = new IntervalPredicate() { @Override public boolean apply(Interval i) { - return !isRegister(i.operand) && i.kind() == Kind.Object; + return !isRegister(i.operand); } }; @@ -282,7 +282,9 @@ void assignSpillSlot(Interval interval) { // assign the canonical spill slot of the parent (if a part of the interval // is already spilled) or allocate a new spill slot - if (interval.spillSlot() != null) { + if (interval.canMaterialize()) { + interval.assignLocation(Value.ILLEGAL); + } else if (interval.spillSlot() != null) { interval.assignLocation(interval.spillSlot()); } else { StackSlot slot = frameMap.allocateSpillSlot(interval.kind()); @@ -519,9 +521,7 @@ // called once before assignment of register numbers void eliminateSpillMoves() { - if (getTraceLevel() >= 3) { - TTY.println(" Eliminating unnecessary spill moves"); - } + Indent indent = Debug.logAndIndent("Eliminating unnecessary spill moves"); // collect all intervals that must be stored after their definition. // the list is sorted by Interval.spillDefinitionPos @@ -553,9 +553,7 @@ if (!isRegister(curInterval.location()) && curInterval.alwaysInMemory()) { // move target is a stack slot that is always correct, so eliminate // instruction - if (getTraceLevel() >= 4) { - TTY.println("eliminating move from interval %d to %d", operandNumber(move.getInput()), operandNumber(move.getResult())); - } + indent.log("eliminating move from interval %d to %d", operandNumber(move.getInput()), operandNumber(move.getResult())); instructions.set(j, null); // null-instructions are deleted by assignRegNum } @@ -565,25 +563,23 @@ assert interval == Interval.EndMarker || (interval.isSplitParent() && interval.spillState() == SpillState.StoreAtDefinition) : "invalid interval"; while (interval != Interval.EndMarker && interval.spillDefinitionPos() == opId) { - if (!insertionBuffer.initialized()) { - // prepare insertion buffer (appended when all instructions of the block - // are processed) - insertionBuffer.init(instructions); - } - - AllocatableValue fromLocation = interval.location(); - AllocatableValue toLocation = canonicalSpillOpr(interval); + if (!interval.canMaterialize()) { + if (!insertionBuffer.initialized()) { + // prepare insertion buffer (appended when all instructions of the + // block are processed) + insertionBuffer.init(instructions); + } - assert isRegister(fromLocation) : "from operand must be a register but is: " + fromLocation + " toLocation=" + toLocation + " spillState=" + interval.spillState(); - assert isStackSlot(toLocation) : "to operand must be a stack slot"; - - insertionBuffer.append(j + 1, ir.spillMoveFactory.createMove(toLocation, fromLocation)); + AllocatableValue fromLocation = interval.location(); + AllocatableValue toLocation = canonicalSpillOpr(interval); - if (getTraceLevel() >= 4) { - StackSlot slot = interval.spillSlot(); - TTY.println("inserting move after definition of interval %d to stack slot %s at opId %d", interval.operandNumber, slot, opId); + assert isRegister(fromLocation) : "from operand must be a register but is: " + fromLocation + " toLocation=" + toLocation + " spillState=" + interval.spillState(); + assert isStackSlot(toLocation) : "to operand must be a stack slot"; + + insertionBuffer.append(j + 1, ir.spillMoveFactory.createMove(toLocation, fromLocation)); + + indent.log("inserting move after definition of interval %d to stack slot %s at opId %d", interval.operandNumber, interval.spillSlot(), opId); } - interval = interval.next; } } @@ -595,9 +591,10 @@ } // end of block iteration assert interval == Interval.EndMarker : "missed an interval"; + indent.outdent(); } - private void checkIntervals(Interval interval) { + private static void checkIntervals(Interval interval) { Interval prev = null; Interval temp = interval; while (temp != Interval.EndMarker) { @@ -607,13 +604,11 @@ assert temp.spillDefinitionPos() >= prev.spillDefinitionPos() : "when intervals are sorted by from : then they must also be sorted by spillDefinitionPos"; } - assert temp.spillSlot() != null : "interval has no spill slot assigned"; + assert temp.spillSlot() != null || temp.canMaterialize() : "interval has no spill slot assigned"; assert temp.spillDefinitionPos() >= temp.from() : "invalid order"; assert temp.spillDefinitionPos() <= temp.from() + 2 : "only intervals defined once at their start-pos can be optimized"; - if (traceLevel >= 4) { - TTY.println("interval %d (from %d to %d) must be stored at %d", temp.operandNumber, temp.from(), temp.to(), temp.spillDefinitionPos()); - } + Debug.log("interval %d (from %d to %d) must be stored at %d", temp.operandNumber, temp.from(), temp.to(), temp.spillDefinitionPos()); prev = temp; temp = temp.next; @@ -695,6 +690,8 @@ // iterate all blocks for (final Block block : sortedBlocks) { + Indent indent = Debug.logAndIndent("compute local live sets for block %d", block.getId()); + final BitSet liveGen = new BitSet(liveSize); final BitSet liveKill = new BitSet(liveSize); @@ -713,9 +710,7 @@ int operandNum = operandNumber(operand); if (!liveKill.get(operandNum)) { liveGen.set(operandNum); - if (getTraceLevel() >= 4) { - TTY.println(" Setting liveGen for operand %d at instruction %d", operandNum, op.id()); - } + Debug.log("liveGen for operand %d", operandNum); } if (block.getLoop() != null) { intervalInLoop.setBit(operandNum, block.getLoop().index); @@ -735,9 +730,7 @@ int operandNum = operandNumber(operand); if (!liveKill.get(operandNum)) { liveGen.set(operandNum); - if (getTraceLevel() >= 4) { - TTY.println(" Setting liveGen for LIR opId %d, operand %d because of state for %s", op.id(), operandNum, op); - } + Debug.log("liveGen in state for operand %d", operandNum); } return operand; } @@ -749,6 +742,7 @@ if (isVariable(operand)) { int varNum = operandNumber(operand); liveKill.set(varNum); + Debug.log("liveKill for operand %d", varNum); if (block.getLoop() != null) { intervalInLoop.setBit(varNum, block.getLoop().index); } @@ -764,6 +758,7 @@ } }; + Indent indent2 = indent.logAndIndent("handle op %d", op.id()); op.forEachInput(useProc); op.forEachAlive(useProc); // Add uses of live locals from interpreter's point of view for proper debug @@ -771,17 +766,19 @@ op.forEachState(stateProc); op.forEachTemp(defProc); op.forEachOutput(defProc); + indent2.outdent(); } // end of instruction iteration - blockData.get(block).liveGen = liveGen; - blockData.get(block).liveKill = liveKill; - blockData.get(block).liveIn = new BitSet(liveSize); - blockData.get(block).liveOut = new BitSet(liveSize); + BlockData blockSets = blockData.get(block); + blockSets.liveGen = liveGen; + blockSets.liveKill = liveKill; + blockSets.liveIn = new BitSet(liveSize); + blockSets.liveOut = new BitSet(liveSize); - if (getTraceLevel() >= 4) { - TTY.println("liveGen B%d %s", block.getId(), blockData.get(block).liveGen); - TTY.println("liveKill B%d %s", block.getId(), blockData.get(block).liveKill); - } + indent.log("liveGen B%d %s", block.getId(), blockSets.liveGen); + indent.log("liveKill B%d %s", block.getId(), blockSets.liveKill); + + indent.outdent(); } // end of block iteration } @@ -814,6 +811,7 @@ * {@link BlockData#liveIn} and {@link BlockData#liveOut}) for each block. */ void computeGlobalLiveSets() { + Indent indent = Debug.logAndIndent("compute global live sets"); int numBlocks = blockCount(); boolean changeOccurred; boolean changeOccurredInBlock; @@ -825,9 +823,12 @@ do { changeOccurred = false; + Indent indent2 = indent.logAndIndent("new iteration %d", iterationCount); + // iterate all blocks in reverse order for (int i = numBlocks - 1; i >= 0; i--) { Block block = blockAt(i); + BlockData blockSets = blockData.get(block); changeOccurredInBlock = false; @@ -844,10 +845,10 @@ liveOut.clear(); } - if (!blockData.get(block).liveOut.equals(liveOut)) { + if (!blockSets.liveOut.equals(liveOut)) { // A change occurred. Swap the old and new live out sets to avoid copying. - BitSet temp = blockData.get(block).liveOut; - blockData.get(block).liveOut = liveOut; + BitSet temp = blockSets.liveOut; + blockSets.liveOut = liveOut; liveOut = temp; changeOccurred = true; @@ -860,15 +861,13 @@ // !liveKill(block)) // note: liveIn has to be computed only in first iteration or if liveOut has // changed! - BitSet liveIn = blockData.get(block).liveIn; + BitSet liveIn = blockSets.liveIn; liveIn.clear(); - liveIn.or(blockData.get(block).liveOut); - liveIn.andNot(blockData.get(block).liveKill); - liveIn.or(blockData.get(block).liveGen); - } + liveIn.or(blockSets.liveOut); + liveIn.andNot(blockSets.liveKill); + liveIn.or(blockSets.liveGen); - if (getTraceLevel() >= 4) { - traceLiveness(changeOccurredInBlock, iterationCount, block); + indent2.log("block %d: livein = %s, liveout = %s", block.getId(), liveIn, blockSets.liveOut); } } iterationCount++; @@ -876,6 +875,7 @@ if (changeOccurred && iterationCount > 50) { throw new BailoutException("too many iterations in computeGlobalLiveSets"); } + indent2.outdent(); } while (changeOccurred); if (DetailedAsserts.getValue()) { @@ -888,55 +888,53 @@ if (DetailedAsserts.getValue()) { reportFailure(numBlocks); } - - TTY.println("preds=" + startBlock.getPredecessorCount() + ", succs=" + startBlock.getSuccessorCount()); - TTY.println("startBlock-ID: " + startBlock.getId()); - // bailout if this occurs in product mode. throw new GraalInternalError("liveIn set of first block must be empty: " + blockData.get(startBlock).liveIn); } + indent.outdent(); } private void reportFailure(int numBlocks) { - TTY.println(gen.getGraph().toString()); - TTY.println("Error: liveIn set of first block must be empty (when this fails, variables are used before they are defined):"); + Indent indent = Debug.logAndIndent("report failure"); + indent.log("graph: %s", gen.getGraph()); + indent.log("Error: liveIn set of first block must be empty (when this fails, variables are used before they are defined):"); BitSet startBlockLiveIn = blockData.get(ir.cfg.getStartBlock()).liveIn; for (int operandNum = startBlockLiveIn.nextSetBit(0); operandNum >= 0; operandNum = startBlockLiveIn.nextSetBit(operandNum + 1)) { Value operand = operandFor(operandNum); - TTY.println(" var %d; operand=%s; node=%s", operandNum, operand.toString(), gen.valueForOperand(operand)); + indent.log(" var %d; operand=%s; node=%s", operandNum, operand, gen.valueForOperand(operand)); } // print some additional information to simplify debugging for (int operandNum = startBlockLiveIn.nextSetBit(0); operandNum >= 0; operandNum = startBlockLiveIn.nextSetBit(operandNum + 1)) { Value operand = operandFor(operandNum); - TTY.println("---- Detailed information for var %d; operand=%s; node=%s ----", operandNum, operand.toString(), gen.valueForOperand(operand)); + final Indent indent2 = indent.logAndIndent("---- Detailed information for var %d; operand=%s; node=%s ----", operandNum, operand, gen.valueForOperand(operand)); Deque definedIn = new ArrayDeque<>(); HashSet usedIn = new HashSet<>(); for (Block block : sortedBlocks) { if (blockData.get(block).liveGen.get(operandNum)) { usedIn.add(block); - TTY.println("used in block B%d {", block.getId()); + indent2.log("used in block B%d {", block.getId()); for (LIRInstruction ins : ir.lir(block)) { - TTY.println(" " + ins.id() + ": " + ins.toString()); + indent2.log(" %d: %s", ins.id(), ins); ins.forEachState(new ValueProcedure() { @Override public Value doValue(Value liveStateOperand) { - TTY.println(" operand=" + liveStateOperand); + indent2.log(" operand=" + liveStateOperand); return liveStateOperand; } }); } - TTY.println("}"); + indent2.log("}"); } if (blockData.get(block).liveKill.get(operandNum)) { definedIn.add(block); - TTY.println("defined in block B%d {", block.getId()); + indent2.log("defined in block B%d {", block.getId()); for (LIRInstruction ins : ir.lir(block)) { - TTY.println(" " + ins.id() + ": " + ins.toString()); + indent2.log(" %d: %s", ins.id(), ins); } - TTY.println("}"); + indent2.log("}"); } } @@ -957,12 +955,13 @@ } } } - TTY.print("**** offending usages are in: "); + indent.log("**** offending usages are in: "); for (Block block : usedIn) { - TTY.print("B%d ", block.getId()); + indent2.log("B%d ", block.getId()); } - TTY.println(); + indent2.outdent(); } + indent.outdent(); } private void verifyLiveness() { @@ -977,21 +976,10 @@ } } - private void traceLiveness(boolean changeOccurredInBlock, int iterationCount, Block block) { - char c = iterationCount == 0 || changeOccurredInBlock ? '*' : ' '; - TTY.print("(%d) liveIn%c B%d ", iterationCount, c, block.getId()); - TTY.println(blockData.get(block).liveIn.toString()); - TTY.print("(%d) liveOut%c B%d ", iterationCount, c, block.getId()); - TTY.println(blockData.get(block).liveOut.toString()); - } - void addUse(AllocatableValue operand, int from, int to, RegisterPriority registerPriority, PlatformKind kind) { if (!isProcessed(operand)) { return; } - if (getTraceLevel() >= 2 && kind == null) { - TTY.println(" use %s from %d to %d (%s)", operand, from, to, registerPriority.name()); - } Interval interval = getOrCreateInterval(operand); if (kind != Kind.Illegal) { @@ -1002,15 +990,14 @@ // Register use position at even instruction id. interval.addUsePos(to & ~1, registerPriority); + + Debug.log("add use: %s, from %d to %d (%s)", interval, from, to, registerPriority.name()); } void addTemp(AllocatableValue operand, int tempPos, RegisterPriority registerPriority, PlatformKind kind) { if (!isProcessed(operand)) { return; } - if (getTraceLevel() >= 2) { - TTY.println(" temp %s tempPos %d (%s)", operand, tempPos, RegisterPriority.MustHaveRegister.name()); - } Interval interval = getOrCreateInterval(operand); if (kind != Kind.Illegal) { @@ -1019,19 +1006,20 @@ interval.addRange(tempPos, tempPos + 1); interval.addUsePos(tempPos, registerPriority); + interval.addMaterializationValue(null); + + Debug.log("add temp: %s tempPos %d (%s)", interval, tempPos, RegisterPriority.MustHaveRegister.name()); } boolean isProcessed(Value operand) { return !isRegister(operand) || attributes(asRegister(operand)).isAllocatable(); } - void addDef(AllocatableValue operand, int defPos, RegisterPriority registerPriority, PlatformKind kind) { + void addDef(AllocatableValue operand, LIRInstruction op, RegisterPriority registerPriority, PlatformKind kind) { if (!isProcessed(operand)) { return; } - if (getTraceLevel() >= 2) { - TTY.println(" def %s defPos %d (%s)", operand, defPos, registerPriority.name()); - } + int defPos = op.id(); Interval interval = getOrCreateInterval(operand); if (kind != Kind.Illegal) { @@ -1050,9 +1038,7 @@ // also add register priority for dead intervals interval.addRange(defPos, defPos + 1); interval.addUsePos(defPos, registerPriority); - if (getTraceLevel() >= 2) { - TTY.println("Warning: def of operand %s at %d occurs without use", operand, defPos); - } + Debug.log("Warning: def of operand %s at %d occurs without use", operand, defPos); } changeSpillDefinitionPos(interval, defPos); @@ -1060,6 +1046,9 @@ // detection of method-parameters and roundfp-results interval.setSpillState(SpillState.StartInMemory); } + interval.addMaterializationValue(gen.getMaterializedValue(op, operand)); + + Debug.log("add def: %s defPos %d (%s)", interval, defPos, registerPriority.name()); } /** @@ -1153,6 +1142,9 @@ } void buildIntervals() { + + Indent indent = Debug.logAndIndent("build intervals"); + intervalsSize = operandSize(); intervals = new Interval[intervalsSize + INITIAL_SPLIT_INTERVALS_CAPACITY]; @@ -1161,7 +1153,10 @@ // iterate all blocks in reverse order for (int i = blockCount() - 1; i >= 0; i--) { + Block block = blockAt(i); + Indent indent2 = indent.logAndIndent("handle block %d", block.getId()); + List instructions = ir.lir(block); final int blockFrom = getFirstLirInstructionId(block); int blockTo = getLastLirInstructionId(block); @@ -1174,9 +1169,7 @@ for (int operandNum = live.nextSetBit(0); operandNum >= 0; operandNum = live.nextSetBit(operandNum + 1)) { assert live.get(operandNum) : "should not stop here otherwise"; AllocatableValue operand = operandFor(operandNum); - if (getTraceLevel() >= 2) { - TTY.println("live in %s to %d", operand, blockTo + 2); - } + indent.log("live in %d: %s", operandNum, operand); addUse(operand, blockFrom, blockTo + 2, RegisterPriority.None, Kind.Illegal); @@ -1195,6 +1188,8 @@ final LIRInstruction op = instructions.get(j); final int opId = op.id(); + Indent indent3 = indent2.logAndIndent("handle inst %d: %s", opId, op); + // add a temp range for each register if operation destroys caller-save registers if (op.destroysCallerSavedRegisters()) { for (Register r : callerSaveRegs) { @@ -1202,9 +1197,7 @@ addTemp(r.asValue(), opId, RegisterPriority.None, Kind.Illegal); } } - if (getTraceLevel() >= 4) { - TTY.println("operation destroys all caller-save registers"); - } + indent.log("operation destroys all caller-save registers"); } op.forEachOutput(new ValueProcedure() { @@ -1212,7 +1205,7 @@ @Override public Value doValue(Value operand, OperandMode mode, EnumSet flags) { if (isVariableOrRegister(operand)) { - addDef((AllocatableValue) operand, opId, registerPriorityOfOutputOperand(op), operand.getPlatformKind()); + addDef((AllocatableValue) operand, op, registerPriorityOfOutputOperand(op), operand.getPlatformKind()); addRegisterHint(op, operand, mode, flags, true); } return operand; @@ -1270,7 +1263,10 @@ // special steps for some instructions (especially moves) handleMethodArguments(op); + indent3.outdent(); + } // end of instruction iteration + indent2.outdent(); } // end of block iteration // add the range [0, 1] to all fixed intervals. @@ -1280,6 +1276,7 @@ interval.addRange(0, 1); } } + indent.outdent(); } // * Phase 5: actual register allocation @@ -1431,6 +1428,7 @@ }; public void allocateRegisters() { + Indent indent = Debug.logAndIndent("allocate registers"); Interval precoloredIntervals; Interval notPrecoloredIntervals; @@ -1442,6 +1440,7 @@ LinearScanWalker lsw = new LinearScanWalker(this, precoloredIntervals, notPrecoloredIntervals); lsw.walk(); lsw.finishAllocation(); + indent.outdent(); } // * Phase 6: resolve data flow @@ -1546,6 +1545,8 @@ * have been split. */ void resolveDataFlow() { + Indent indent = Debug.logAndIndent("resolve data flow"); + int numBlocks = blockCount(); MoveResolver moveResolver = new MoveResolver(this); BitSet blockCompleted = new BitSet(numBlocks); @@ -1608,6 +1609,7 @@ } } } + indent.outdent(); } // * Phase 7: assign register numbers back to LIR @@ -1652,15 +1654,18 @@ interval = splitChildAtOpId(interval, opId, mode); } + if (isIllegal(interval.location()) && interval.canMaterialize()) { + return interval.getMaterializedValue(); + } return interval.location(); } - IntervalWalker initComputeOopMaps() { + protected IntervalWalker initIntervalWalker(IntervalPredicate predicate) { // setup lists of potential oops for walking Interval oopIntervals; Interval nonOopIntervals; - oopIntervals = createUnhandledLists(IS_OOP_INTERVAL, null).first; + oopIntervals = createUnhandledLists(predicate, null).first; // intervals that have no oops inside need not to be processed. // to ensure a walking until the last instruction id, add a dummy interval @@ -1671,7 +1676,11 @@ return new IntervalWalker(this, oopIntervals, nonOopIntervals); } - void computeOopMap(IntervalWalker iw, LIRInstruction op, BitSet registerRefMap, BitSet frameRefMap) { + /** + * Visits all intervals for a frame state. The frame state use this information to build the OOP + * maps. + */ + void markFrameLocations(IntervalWalker iw, LIRInstruction op, LIRFrameState info) { if (getTraceLevel() >= 3) { TTY.println("creating oop map at opId %d", op.id()); } @@ -1694,11 +1703,11 @@ // moves, any intervals which end at this instruction are included // in the oop map since we may safepoint while doing the patch // before we've consumed the inputs. - if (op.id() < interval.currentTo()) { + if (op.id() < interval.currentTo() && !isIllegal(interval.location())) { // caller-save registers must not be included into oop-maps at calls assert !op.destroysCallerSavedRegisters() || !isRegister(operand) || !isCallerSave(operand) : "interval is in a caller-save register at a call . register will be overwritten"; - frameMap.setReference(interval.location(), registerRefMap, frameRefMap); + info.markLocation(interval.location(), frameMap); // Spill optimization: when the stack value is guaranteed to be always correct, // then it must be added to the oop map even if the interval is currently in a @@ -1707,7 +1716,7 @@ assert interval.spillDefinitionPos() > 0 : "position not set correctly"; assert interval.spillSlot() != null : "no spill slot assigned"; assert !isRegister(interval.operand) : "interval is on stack : so stack slot is registered twice"; - frameMap.setReference(interval.spillSlot(), registerRefMap, frameRefMap); + info.markLocation(interval.spillSlot(), frameMap); } } } @@ -1718,9 +1727,8 @@ } private void computeDebugInfo(IntervalWalker iw, final LIRInstruction op, LIRFrameState info) { - BitSet registerRefMap = op.destroysCallerSavedRegisters() && callKillsRegisters ? null : frameMap.initRegisterRefMap(); - BitSet frameRefMap = frameMap.initFrameRefMap(); - computeOopMap(iw, op, registerRefMap, frameRefMap); + info.initDebugInfo(frameMap, !op.destroysCallerSavedRegisters() || !callKillsRegisters); + markFrameLocations(iw, op, info); info.forEachState(new ValueProcedure() { @@ -1750,12 +1758,11 @@ // the intervals // if the interval is not live, colorLirOperand will cause an assert on failure Value result = colorLirOperand((Variable) operand, tempOpId, mode); - assert !hasCall(tempOpId) || isStackSlot(result) || !isCallerSave(result) : "cannot have caller-save register operands at calls"; + assert !hasCall(tempOpId) || isStackSlot(result) || isConstant(result) || !isCallerSave(result) : "cannot have caller-save register operands at calls"; return result; } }); - - info.finish(registerRefMap, frameRefMap); + info.finish(op, frameMap); } private void assignLocations(List instructions, final IntervalWalker iw) { @@ -1811,7 +1818,7 @@ } private void assignLocations() { - IntervalWalker iw = initComputeOopMaps(); + IntervalWalker iw = initIntervalWalker(IS_STACK_INTERVAL); for (Block block : sortedBlocks) { assignLocations(ir.lir(block), iw); } @@ -1819,6 +1826,8 @@ public void allocate() { + Indent indent = Debug.logAndIndent(false, "allocate %s", gen.getGraph().method()); + try (Scope s = Debug.scope("LifetimeAnalysis")) { numberInstructions(); printLir("Before register allocation", true); @@ -1869,11 +1878,21 @@ printLir("After register number assignment", true); EdgeMoveOptimizer.optimize(ir); ControlFlowOptimizer.optimize(ir); + + /* + * Temporarily disabled because of problem in specjvm2008. TODO: fix the problem and + * re-enable it. + * + * RedundantMoveElimination.optimize(ir, frameMap, gen.getGraph().method()); + */ + NullCheckOptimizer.optimize(ir, target.implicitNullCheckLimit); printLir("After control flow optimization", false); } catch (Throwable e) { throw Debug.handle(e); } + + indent.outdent(); } void printIntervals(String label) { @@ -1998,7 +2017,7 @@ } Value l1 = i1.location(); Value l2 = i2.location(); - if (i1.intersects(i2) && (l1.equals(l2))) { + if (i1.intersects(i2) && !isIllegal(l1) && (l1.equals(l2))) { if (DetailedAsserts.getValue()) { TTY.println("Intervals %d and %d overlap and have the same register assigned", i1.operandNumber, i2.operandNumber); TTY.println(i1.logString(this)); diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/LinearScanWalker.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/LinearScanWalker.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/LinearScanWalker.java Tue Dec 17 21:26:42 2013 -0800 @@ -399,66 +399,52 @@ // 1) the left part has already a location assigned // 2) the right part is sorted into to the unhandled-list void splitBeforeUsage(Interval interval, int minSplitPos, int maxSplitPos) { - if (getTraceLevel() >= 2) { - TTY.println("----- splitting interval: "); - } - if (getTraceLevel() >= 4) { - TTY.println(interval.logString(allocator)); - } - if (getTraceLevel() >= 2) { - TTY.println(" between %d and %d", minSplitPos, maxSplitPos); - } + + try (Indent indent = Debug.logAndIndent("splitting interval %s between %d and %d", interval, minSplitPos, maxSplitPos)) { - assert interval.from() < minSplitPos : "cannot split at start of interval"; - assert currentPosition < minSplitPos : "cannot split before current position"; - assert minSplitPos <= maxSplitPos : "invalid order"; - assert maxSplitPos <= interval.to() : "cannot split after end of interval"; + assert interval.from() < minSplitPos : "cannot split at start of interval"; + assert currentPosition < minSplitPos : "cannot split before current position"; + assert minSplitPos <= maxSplitPos : "invalid order"; + assert maxSplitPos <= interval.to() : "cannot split after end of interval"; + + int optimalSplitPos = findOptimalSplitPos(interval, minSplitPos, maxSplitPos, true); - int optimalSplitPos = findOptimalSplitPos(interval, minSplitPos, maxSplitPos, true); - - assert minSplitPos <= optimalSplitPos && optimalSplitPos <= maxSplitPos : "out of range"; - assert optimalSplitPos <= interval.to() : "cannot split after end of interval"; - assert optimalSplitPos > interval.from() : "cannot split at start of interval"; + assert minSplitPos <= optimalSplitPos && optimalSplitPos <= maxSplitPos : "out of range"; + assert optimalSplitPos <= interval.to() : "cannot split after end of interval"; + assert optimalSplitPos > interval.from() : "cannot split at start of interval"; - if (optimalSplitPos == interval.to() && interval.nextUsage(RegisterPriority.MustHaveRegister, minSplitPos) == Integer.MAX_VALUE) { - // the split position would be just before the end of the interval - // . no split at all necessary - if (getTraceLevel() >= 4) { - TTY.println(" no split necessary because optimal split position is at end of interval"); + if (optimalSplitPos == interval.to() && interval.nextUsage(RegisterPriority.MustHaveRegister, minSplitPos) == Integer.MAX_VALUE) { + // the split position would be just before the end of the interval + // . no split at all necessary + indent.log("no split necessary because optimal split position is at end of interval"); + return; } - return; - } - // must calculate this before the actual split is performed and before split position is - // moved to odd opId - boolean moveNecessary = !allocator.isBlockBegin(optimalSplitPos) && !interval.hasHoleBetween(optimalSplitPos - 1, optimalSplitPos); + // must calculate this before the actual split is performed and before split position is + // moved to odd opId + boolean moveNecessary = !allocator.isBlockBegin(optimalSplitPos) && !interval.hasHoleBetween(optimalSplitPos - 1, optimalSplitPos); - if (!allocator.isBlockBegin(optimalSplitPos)) { - // move position before actual instruction (odd opId) - optimalSplitPos = (optimalSplitPos - 1) | 1; - } + if (!allocator.isBlockBegin(optimalSplitPos)) { + // move position before actual instruction (odd opId) + optimalSplitPos = (optimalSplitPos - 1) | 1; + } - if (getTraceLevel() >= 4) { - TTY.println(" splitting at position %d", optimalSplitPos); - } - assert allocator.isBlockBegin(optimalSplitPos) || (optimalSplitPos % 2 == 1) : "split pos must be odd when not on block boundary"; - assert !allocator.isBlockBegin(optimalSplitPos) || (optimalSplitPos % 2 == 0) : "split pos must be even on block boundary"; + indent.log("splitting at position %d", optimalSplitPos); - Interval splitPart = interval.split(optimalSplitPos, allocator); + assert allocator.isBlockBegin(optimalSplitPos) || (optimalSplitPos % 2 == 1) : "split pos must be odd when not on block boundary"; + assert !allocator.isBlockBegin(optimalSplitPos) || (optimalSplitPos % 2 == 0) : "split pos must be even on block boundary"; - splitPart.setInsertMoveWhenActivated(moveNecessary); + Interval splitPart = interval.split(optimalSplitPos, allocator); - assert splitPart.from() >= currentInterval.currentFrom() : "cannot append new interval before current walk position"; - unhandledLists.addToListSortedByStartAndUsePositions(RegisterBinding.Any, splitPart); + splitPart.setInsertMoveWhenActivated(moveNecessary); - if (getTraceLevel() >= 2) { - TTY.println(" split interval in two parts (insertMoveWhenActivated: %b)", moveNecessary); - } - if (getTraceLevel() >= 2) { - TTY.print(" "); - TTY.println(interval.logString(allocator)); - TTY.print(" "); - TTY.println(splitPart.logString(allocator)); + assert splitPart.from() >= currentInterval.currentFrom() : "cannot append new interval before current walk position"; + unhandledLists.addToListSortedByStartAndUsePositions(RegisterBinding.Any, splitPart); + + if (Debug.isLogEnabled()) { + indent.log("left interval %s: %s", moveNecessary ? " " : "", interval.logString(allocator)); + indent.log("right interval %s: %s", moveNecessary ? "(move)" : "", splitPart.logString(allocator)); + } } } @@ -472,11 +458,7 @@ int maxSplitPos = currentPosition; int minSplitPos = Math.max(interval.previousUsage(RegisterPriority.ShouldHaveRegister, maxSplitPos) + 1, interval.from()); - if (getTraceLevel() >= 2) { - TTY.print("----- splitting and spilling interval: "); - TTY.println(interval.logString(allocator)); - TTY.println(" between %d and %d", minSplitPos, maxSplitPos); - } + Indent indent = Debug.logAndIndent("splitting and spilling interval %s between %d and %d", interval, minSplitPos, maxSplitPos); assert interval.state == State.Active : "why spill interval that is not active?"; assert interval.from() <= minSplitPos : "cannot split before start of interval"; @@ -486,33 +468,31 @@ if (minSplitPos == interval.from()) { // the whole interval is never used, so spill it entirely to memory - if (getTraceLevel() >= 2) { - TTY.println(" spilling entire interval because split pos is at beginning of interval"); - TTY.println(" use positions: " + interval.usePosList().size()); - } - assert interval.firstUsage(RegisterPriority.ShouldHaveRegister) > currentPosition : "interval must not have use position before currentPosition"; + + try (Indent indent2 = indent.logAndIndent("spilling entire interval because split pos is at beginning of interval (use positions: %d)", interval.usePosList().size())) { - allocator.assignSpillSlot(interval); - allocator.changeSpillState(interval, minSplitPos); + assert interval.firstUsage(RegisterPriority.ShouldHaveRegister) > currentPosition : "interval must not have use position before currentPosition"; + + allocator.assignSpillSlot(interval); + allocator.changeSpillState(interval, minSplitPos); - // Also kick parent intervals out of register to memory when they have no use - // position. This avoids short interval in register surrounded by intervals in - // memory . avoid useless moves from memory to register and back - Interval parent = interval; - while (parent != null && parent.isSplitChild()) { - parent = parent.getSplitChildBeforeOpId(parent.from()); + // Also kick parent intervals out of register to memory when they have no use + // position. This avoids short interval in register surrounded by intervals in + // memory . avoid useless moves from memory to register and back + Interval parent = interval; + while (parent != null && parent.isSplitChild()) { + parent = parent.getSplitChildBeforeOpId(parent.from()); - if (isRegister(parent.location())) { - if (parent.firstUsage(RegisterPriority.ShouldHaveRegister) == Integer.MAX_VALUE) { - // parent is never used, so kick it out of its assigned register - if (getTraceLevel() >= 4) { - TTY.println(" kicking out interval %d out of its register because it is never used", parent.operandNumber); + if (isRegister(parent.location())) { + if (parent.firstUsage(RegisterPriority.ShouldHaveRegister) == Integer.MAX_VALUE) { + // parent is never used, so kick it out of its assigned register + indent2.log("kicking out interval %d out of its register because it is never used", parent.operandNumber); + allocator.assignSpillSlot(parent); + } else { + // do not go further back because the register is actually used by + // the interval + parent = null; } - allocator.assignSpillSlot(parent); - } else { - // do not go further back because the register is actually used by the - // interval - parent = null; } } } @@ -530,35 +510,30 @@ optimalSplitPos = (optimalSplitPos - 1) | 1; } - if (getTraceLevel() >= 4) { - TTY.println(" splitting at position %d", optimalSplitPos); - } - assert allocator.isBlockBegin(optimalSplitPos) || (optimalSplitPos % 2 == 1) : "split pos must be odd when not on block boundary"; - assert !allocator.isBlockBegin(optimalSplitPos) || (optimalSplitPos % 2 == 0) : "split pos must be even on block boundary"; + try (Indent indent2 = indent.logAndIndent("splitting at position %d", optimalSplitPos)) { + assert allocator.isBlockBegin(optimalSplitPos) || (optimalSplitPos % 2 == 1) : "split pos must be odd when not on block boundary"; + assert !allocator.isBlockBegin(optimalSplitPos) || (optimalSplitPos % 2 == 0) : "split pos must be even on block boundary"; - Interval spilledPart = interval.split(optimalSplitPos, allocator); - allocator.assignSpillSlot(spilledPart); - allocator.changeSpillState(spilledPart, optimalSplitPos); + Interval spilledPart = interval.split(optimalSplitPos, allocator); + allocator.assignSpillSlot(spilledPart); + allocator.changeSpillState(spilledPart, optimalSplitPos); - if (!allocator.isBlockBegin(optimalSplitPos)) { - if (getTraceLevel() >= 4) { - TTY.println(" inserting move from interval %d to %d", interval.operandNumber, spilledPart.operandNumber); + if (!allocator.isBlockBegin(optimalSplitPos)) { + indent2.log("inserting move from interval %d to %d", interval.operandNumber, spilledPart.operandNumber); + insertMove(optimalSplitPos, interval, spilledPart); } - insertMove(optimalSplitPos, interval, spilledPart); - } - // the currentSplitChild is needed later when moves are inserted for reloading - assert spilledPart.currentSplitChild() == interval : "overwriting wrong currentSplitChild"; - spilledPart.makeCurrentSplitChild(); + // the currentSplitChild is needed later when moves are inserted for reloading + assert spilledPart.currentSplitChild() == interval : "overwriting wrong currentSplitChild"; + spilledPart.makeCurrentSplitChild(); - if (getTraceLevel() >= 2) { - TTY.println(" split interval in two parts"); - TTY.print(" "); - TTY.println(interval.logString(allocator)); - TTY.print(" "); - TTY.println(spilledPart.logString(allocator)); + if (Debug.isLogEnabled()) { + indent2.log("left interval: %s", interval.logString(allocator)); + indent2.log("spilled interval : %s", spilledPart.logString(allocator)); + } } } + indent.outdent(); } void splitStackInterval(Interval interval) { @@ -883,13 +858,8 @@ Interval interval = currentInterval; boolean result = true; - if (getTraceLevel() >= 2) { - TTY.println("+++++ activating interval " + interval.logString(allocator)); - } - - if (getTraceLevel() >= 4) { - TTY.println(" splitParent: %s, insertMoveWhenActivated: %b", interval.splitParent().operandNumber, interval.insertMoveWhenActivated()); - } + Indent indent = Debug.logAndIndent("activating interval %s, splitParent: %s, insertMoveWhenActivated: %b", interval.logString(allocator), interval.splitParent().operandNumber, + interval.insertMoveWhenActivated()); final Value operand = interval.operand; if (interval.location() != null && isStackSlot(interval.location())) { @@ -940,6 +910,8 @@ } interval.makeCurrentSplitChild(); + indent.outdent(); + return result; // true = interval is moved to active list } diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/MoveResolver.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/MoveResolver.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/MoveResolver.java Tue Dec 17 21:26:42 2013 -0800 @@ -199,9 +199,7 @@ insertionBuffer.append(insertIdx, allocator.ir.spillMoveFactory.createMove(toOpr, fromOpr)); - if (allocator.getTraceLevel() >= 4) { - TTY.println("MoveResolver: inserted move from %d (%s) to %d (%s)", fromInterval.operandNumber, fromInterval.location(), toInterval.operandNumber, toInterval.location()); - } + Debug.log("insert move from %s to %s at %d", fromInterval, toInterval, insertIdx); } private void insertMove(Value fromOpr, Interval toInterval) { @@ -211,9 +209,7 @@ AllocatableValue toOpr = toInterval.operand; insertionBuffer.append(insertIdx, allocator.ir.spillMoveFactory.createMove(toOpr, fromOpr)); - if (allocator.getTraceLevel() >= 4) { - TTY.print("MoveResolver: inserted move from constant %s to %d (%s)", fromOpr, toInterval.operandNumber, toInterval.location()); - } + Debug.log("insert move from value %s to %s at %d", fromOpr, toInterval, insertIdx); } private void resolveMappings() { @@ -283,9 +279,7 @@ } spillInterval.assignLocation(spillSlot); - if (allocator.getTraceLevel() >= 4) { - TTY.println("created new Interval %s for spilling", spillInterval.operand); - } + Debug.log("created new Interval for spilling: %s", spillInterval); // insert a move from register to stack and update the mapping insertMove(fromInterval, spillInterval); @@ -325,9 +319,18 @@ } void addMapping(Interval fromInterval, Interval toInterval) { - if (allocator.getTraceLevel() >= 4) { - TTY.println("MoveResolver: adding mapping from interval %d (%s) to interval %d (%s)", fromInterval.operandNumber, fromInterval.location(), toInterval.operandNumber, toInterval.location()); + + if (isIllegal(toInterval.location()) && toInterval.canMaterialize()) { + Debug.log("no store to rematerializable interval %s needed", toInterval); + return; } + if (isIllegal(fromInterval.location()) && fromInterval.canMaterialize()) { + // Instead of a reload, re-materialize the value + Value rematValue = fromInterval.getMaterializedValue(); + addMapping(rematValue, toInterval); + return; + } + Debug.log("add move mapping from %s to %s", fromInterval, toInterval); assert !fromInterval.operand.equals(toInterval.operand) : "from and to interval equal: " + fromInterval; assert fromInterval.kind() == toInterval.kind(); @@ -337,9 +340,8 @@ } void addMapping(Value fromOpr, Interval toInterval) { - if (allocator.getTraceLevel() >= 4) { - TTY.println("MoveResolver: adding mapping from %s to %d (%s)", fromOpr, toInterval.operandNumber, toInterval.location()); - } + Debug.log("add move mapping from %s to %s", fromOpr, toInterval); + assert isConstant(fromOpr) : "only for constants"; mappingFrom.add(null); diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/LIRGenerator.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/LIRGenerator.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/LIRGenerator.java Tue Dec 17 21:26:42 2013 -0800 @@ -28,6 +28,7 @@ import static com.oracle.graal.lir.LIR.*; import static com.oracle.graal.lir.LIRValueUtil.*; import static com.oracle.graal.nodes.ConstantNode.*; +import static com.oracle.graal.phases.GraalOptions.*; import java.util.*; import java.util.Map.Entry; @@ -39,9 +40,7 @@ import com.oracle.graal.debug.*; import com.oracle.graal.graph.*; import com.oracle.graal.lir.*; -import com.oracle.graal.lir.StandardOp.BlockEndOp; -import com.oracle.graal.lir.StandardOp.JumpOp; -import com.oracle.graal.lir.StandardOp.LabelOp; +import com.oracle.graal.lir.StandardOp.*; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.PhiNode.PhiType; import com.oracle.graal.nodes.calc.*; @@ -82,10 +81,75 @@ private final boolean printIRWithLIR; /** - * Maps constants the variables within the scope of a single block to avoid loading a constant - * more than once per block. + * Handle for an operation that loads a constant into a variable. The operation starts in the + * first block where the constant is used but will eventually be + * {@linkplain LIRGenerator#insertConstantLoads() moved} to a block dominating all usages of the + * constant. */ - private Map constantsLoadedInCurrentBlock; + public static class LoadConstant implements Comparable { + /** + * The index of {@link #op} within {@link #block}'s instruction list or -1 if {@code op} is + * to be moved to a dominator block. + */ + int index; + + /** + * The operation that loads the constant. + */ + private final LIRInstruction op; + + /** + * The block that does or will contain {@link #op}. This is initially the block where the + * first usage of the constant is seen during LIR generation. + */ + private Block block; + + /** + * The variable into which the constant is loaded. + */ + private final Variable variable; + + public LoadConstant(Variable variable, Block block, int index, LIRInstruction op) { + this.variable = variable; + this.block = block; + this.index = index; + this.op = op; + } + + /** + * Sorts {@link LoadConstant} objects according to their enclosing blocks. This is used to + * group loads per block in {@link LIRGenerator#insertConstantLoads()}. + */ + public int compareTo(LoadConstant o) { + if (block.getId() < o.block.getId()) { + return -1; + } + if (block.getId() > o.block.getId()) { + return 1; + } + return 0; + } + + @Override + public String toString() { + return block + "#" + op; + } + + /** + * Removes the {@link #op} from its original location if it is still at that location. + */ + public void unpin(LIR lir) { + if (index >= 0) { + // Replace the move with a filler op so that the operation + // list does not need to be adjusted. + List instructions = lir.lir(block); + instructions.set(index, new NoOp(null, -1)); + index = -1; + } + } + } + + private Map constantLoads; private ValueNode currentInstruction; private ValueNode lastInstructionPrinted; // Debugging only @@ -123,6 +187,19 @@ this.printIRWithLIR = Options.PrintIRWithLIR.getValue(); } + /** + * Returns a value for a interval definition, which can be used for re-materialization. + * + * @param op An instruction which defines a value + * @param operand The destination operand of the instruction + * @return Returns the value which is moved to the instruction and which can be reused at all + * reload-locations in case the interval of this instruction is spilled. Currently this + * can only be a {@link Constant}. + */ + public Constant getMaterializedValue(LIRInstruction op, Value operand) { + return null; + } + @SuppressWarnings("hiding") protected DebugInfoBuilder createDebugInfoBuilder(NodeMap nodeOperands) { return new DebugInfoBuilder(nodeOperands); @@ -174,7 +251,7 @@ if (nodeOperands == null) { return null; } - Value operand = !node.isExternal() ? nodeOperands.get(node) : null; + Value operand = nodeOperands.get(node); if (operand == null) { return getConstantOperand(node); } @@ -186,18 +263,27 @@ Constant value = node.asConstant(); if (value != null) { if (canInlineConstant(value)) { - return !node.isExternal() ? setResult(node, value) : value; + return setResult(node, value); } else { Variable loadedValue; - if (constantsLoadedInCurrentBlock == null) { - constantsLoadedInCurrentBlock = new HashMap<>(); - loadedValue = null; + if (constantLoads == null) { + constantLoads = new HashMap<>(); + } + LoadConstant load = constantLoads.get(value); + if (load == null) { + int index = lir.lir(currentBlock).size(); + loadedValue = emitMove(value); + LIRInstruction op = lir.lir(currentBlock).get(index); + constantLoads.put(value, new LoadConstant(loadedValue, currentBlock, index, op)); } else { - loadedValue = constantsLoadedInCurrentBlock.get(value); - } - if (loadedValue == null) { - loadedValue = emitMove(value); - constantsLoadedInCurrentBlock.put(value, loadedValue); + Block dominator = ControlFlowGraph.commonDominator(load.block, currentBlock); + loadedValue = load.variable; + if (dominator != load.block) { + load.unpin(lir); + } else { + assert load.block != currentBlock || load.index < lir.lir(currentBlock).size(); + } + load.block = dominator; } return loadedValue; } @@ -350,7 +436,6 @@ } currentBlock = block; - resetLoadedConstants(); // set up the list of LIR instructions assert lir.lir(block) == null : "LIR list already computed for this block"; @@ -414,12 +499,6 @@ } } - private void resetLoadedConstants() { - if (constantsLoadedInCurrentBlock != null && !constantsLoadedInCurrentBlock.isEmpty()) { - constantsLoadedInCurrentBlock.clear(); - } - } - protected abstract boolean peephole(ValueNode valueNode); private boolean hasBlockEnd(Block block) { @@ -795,10 +874,87 @@ @Override public void beforeRegisterAllocation() { + insertConstantLoads(); } /** - * Gets an garbage vale for a given kind. + * Moves deferred {@linkplain LoadConstant loads} of constants into blocks dominating all usages + * of the constant. Any operations inserted into a block are guaranteed to be immediately prior + * to the first control flow instruction near the end of the block. + */ + private void insertConstantLoads() { + if (constantLoads != null) { + // Remove loads where all usages are in the same block. + for (Iterator> iter = constantLoads.entrySet().iterator(); iter.hasNext();) { + LoadConstant lc = iter.next().getValue(); + + // Move loads of constant outside of loops + if (OptScheduleOutOfLoops.getValue()) { + Block outOfLoopDominator = lc.block; + while (outOfLoopDominator.getLoop() != null) { + outOfLoopDominator = outOfLoopDominator.getDominator(); + } + if (outOfLoopDominator != lc.block) { + lc.unpin(lir); + lc.block = outOfLoopDominator; + } + } + + if (lc.index != -1) { + assert lir.lir(lc.block).get(lc.index) == lc.op; + iter.remove(); + } + } + if (constantLoads.isEmpty()) { + return; + } + + // Sorting groups the loads per block. + LoadConstant[] groupedByBlock = constantLoads.values().toArray(new LoadConstant[constantLoads.size()]); + Arrays.sort(groupedByBlock); + + int groupBegin = 0; + while (true) { + int groupEnd = groupBegin + 1; + Block block = groupedByBlock[groupBegin].block; + while (groupEnd < groupedByBlock.length && groupedByBlock[groupEnd].block == block) { + groupEnd++; + } + int groupSize = groupEnd - groupBegin; + + List ops = lir.lir(block); + int lastIndex = ops.size() - 1; + assert ops.get(lastIndex) instanceof BlockEndOp; + int insertionIndex = lastIndex; + for (int i = Math.max(0, lastIndex - MAX_EXCEPTION_EDGE_OP_DISTANCE_FROM_END); i < lastIndex; i++) { + if (getExceptionEdge(ops.get(i)) != null) { + insertionIndex = i; + break; + } + } + + if (groupSize == 1) { + ops.add(insertionIndex, groupedByBlock[groupBegin].op); + } else { + assert groupSize > 1; + List moves = new ArrayList<>(groupSize); + for (int i = groupBegin; i < groupEnd; i++) { + moves.add(groupedByBlock[i].op); + } + ops.addAll(insertionIndex, moves); + } + + if (groupEnd == groupedByBlock.length) { + break; + } + groupBegin = groupEnd; + } + constantLoads = null; + } + } + + /** + * Gets a garbage value for a given kind. */ protected Constant zapValueForKind(PlatformKind kind) { long dead = 0xDEADDEADDEADDEADL; diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.debug/src/com/oracle/graal/debug/Debug.java --- a/graal/com.oracle.graal.debug/src/com/oracle/graal/debug/Debug.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.debug/src/com/oracle/graal/debug/Debug.java Tue Dec 17 21:26:42 2013 -0800 @@ -69,6 +69,17 @@ return ENABLED; } + public static boolean isDumpEnabledForMethod() { + if (!ENABLED) { + return false; + } + DebugConfig config = DebugScope.getConfig(); + if (config == null) { + return false; + } + return config.isDumpEnabledForMethod(); + } + public static boolean isDumpEnabled() { return ENABLED && DebugScope.getInstance().isDumpEnabled(); } @@ -81,6 +92,17 @@ return ENABLED && DebugScope.getInstance().isTimeEnabled(); } + public static boolean isLogEnabledForMethod() { + if (!ENABLED) { + return false; + } + DebugConfig config = DebugScope.getConfig(); + if (config == null) { + return false; + } + return config.isLogEnabledForMethod(); + } + public static boolean isLogEnabled() { return ENABLED && DebugScope.getInstance().isLogEnabled(); } @@ -431,6 +453,10 @@ return isLogEnabled; } + public boolean isLogEnabledForMethod() { + return isLogEnabled; + } + @Override public boolean isMeterEnabled() { return isMeterEnabled; @@ -441,6 +467,10 @@ return isDumpEnabled; } + public boolean isDumpEnabledForMethod() { + return isDumpEnabled; + } + @Override public boolean isTimeEnabled() { return isTimerEnabled; diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.debug/src/com/oracle/graal/debug/DebugConfig.java --- a/graal/com.oracle.graal.debug/src/com/oracle/graal/debug/DebugConfig.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.debug/src/com/oracle/graal/debug/DebugConfig.java Tue Dec 17 21:26:42 2013 -0800 @@ -36,6 +36,12 @@ boolean isLogEnabled(); /** + * Determines if logging can be enabled in the current method, regardless of the + * {@linkplain Debug#currentScope() current debug scope}. + */ + boolean isLogEnabledForMethod(); + + /** * Determines if metering is enabled in the {@linkplain Debug#currentScope() current debug * scope}. * @@ -52,6 +58,12 @@ boolean isDumpEnabled(); /** + * Determines if dumping can be enabled in the current method, regardless of the + * {@linkplain Debug#currentScope() current debug scope}. + */ + boolean isDumpEnabledForMethod(); + + /** * Adds an object the context used by this configuration to do filtering. */ void addToContext(Object o); diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.debug/src/com/oracle/graal/debug/DelegatingDebugConfig.java --- a/graal/com.oracle.graal.debug/src/com/oracle/graal/debug/DelegatingDebugConfig.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.debug/src/com/oracle/graal/debug/DelegatingDebugConfig.java Tue Dec 17 21:26:42 2013 -0800 @@ -38,6 +38,10 @@ return delegate.isLogEnabled(); } + public boolean isLogEnabledForMethod() { + return delegate.isLogEnabledForMethod(); + } + @Override public boolean isMeterEnabled() { return delegate.isMeterEnabled(); @@ -48,6 +52,10 @@ return delegate.isDumpEnabled(); } + public boolean isDumpEnabledForMethod() { + return delegate.isDumpEnabledForMethod(); + } + @Override public boolean isTimeEnabled() { return delegate.isTimeEnabled(); diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Graph.java --- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Graph.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Graph.java Tue Dec 17 21:26:42 2013 -0800 @@ -75,11 +75,6 @@ NodeChangedListener usagesDroppedToZeroListener; private final HashMap cachedNodes = new HashMap<>(); - /** - * Determines if external nodes will use {@link Graph}'s canonicalization cache. - */ - public static final boolean CacheExternalNodesInGraph = Boolean.parseBoolean(System.getProperty("graal.cacheExternalNodesInGraph", "false")); - private static final class CacheEntry { private final Node node; @@ -347,24 +342,6 @@ return uniqueHelper(node, true); } - /** - * Looks for a node similar to {@code node}. If not found, {@code node} is added to a - * cache in this graph used to canonicalize nodes. - *

- * Note that node must implement {@link ValueNumberable} and must be an - * {@linkplain Node#isExternal() external} node. - * - * @return a node similar to {@code node} if one exists, otherwise {@code node} - */ - public T uniqueExternal(T node) { - assert node.isExternal() : node; - assert node instanceof ValueNumberable : node; - if (!CacheExternalNodesInGraph) { - return node; - } - return uniqueHelper(node, false); - } - @SuppressWarnings("unchecked") T uniqueHelper(T node, boolean addIfMissing) { assert node.getNodeClass().valueNumberable(); @@ -381,14 +358,13 @@ } void putNodeIntoCache(Node node) { - assert node.isExternal() || node.graph() == this || node.graph() == null; + assert node.graph() == this || node.graph() == null; assert node.getNodeClass().valueNumberable(); assert node.getNodeClass().isLeafNode() : node.getClass(); cachedNodes.put(new CacheEntry(node), node); } Node findNodeInCache(Node node) { - assert !node.isExternal() || CacheExternalNodesInGraph; CacheEntry key = new CacheEntry(node); Node result = cachedNodes.get(key); if (result != null && result.isDeleted()) { @@ -589,7 +565,7 @@ * ordering between the nodes within the list. */ public boolean maybeCompress() { - if (Debug.isEnabled()) { + if (Debug.isDumpEnabledForMethod() || Debug.isLogEnabledForMethod()) { return false; } int liveNodeCount = getNodeCount(); @@ -785,7 +761,6 @@ } void register(Node node) { - assert !node.isExternal(); assert node.id() == Node.INITIAL_ID; if (nodes.length == nodesSize) { nodes = Arrays.copyOf(nodes, (nodesSize * 2) + 1); diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Node.java --- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Node.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Node.java Tue Dec 17 21:26:42 2013 -0800 @@ -157,11 +157,9 @@ } /** - * Gets the graph context of this node. This must not be called for {@linkplain #isExternal() - * external} nodes. + * Gets the graph context of this node. */ public Graph graph() { - assert !isExternal() : "external node has no graph: " + this; return graph; } @@ -288,15 +286,6 @@ } /** - * Determines if this node has a {@linkplain #graph() graph} context or is external to any - * graph. The {@link #graph()} method must only be called on nodes for which this method returns - * true. - */ - public boolean isExternal() { - return false; - } - - /** * Finds the index of the last non-null entry in a node array. The search assumes that all * non-null entries precede the first null entry in the array. * @@ -531,7 +520,6 @@ assert assertFalse(other == this, "cannot replace a node with itself"); assert assertFalse(isDeleted(), "cannot replace deleted node"); assert assertTrue(other == null || !other.isDeleted(), "cannot replace with deleted node %s", other); - assert assertTrue(other == null || other.isExternal() || other.graph() == graph, "cannot replace with node in different graph: %s", other == null || other.isExternal() ? null : other.graph()); return true; } @@ -588,7 +576,7 @@ assert assertFalse(isDeleted(), "cannot clear inputs of deleted node"); for (Node input : inputs()) { - if (input.recordsUsages() && !input.isExternal()) { + if (input.recordsUsages()) { removeThisFromUsages(input); if (input.usages().isEmpty()) { NodeChangedListener listener = graph.usagesDroppedToZeroListener; @@ -637,7 +625,6 @@ } public final Node copyWithInputs() { - assert !isExternal(); Node newNode = clone(graph); NodeClass clazz = getNodeClass(); clazz.copyInputs(this, newNode); @@ -676,7 +663,6 @@ } final Node clone(Graph into, boolean clearInputsAndSuccessors) { - assert !isExternal(); NodeClass nodeClass = getNodeClass(); if (nodeClass.valueNumberable() && nodeClass.isLeafNode()) { Node otherNode = into.findNodeInCache(this); @@ -711,23 +697,6 @@ return newNode; } - /** - * Determines if a given node is {@linkplain Graph#uniqueExternal(Node) unique} within a given - * graph if the node is non-null and {@linkplain #isExternal() external}. - * - * @param node node to check - * @param graph graph context to use - * @return true if node is null, not external or unique within {@code graph} otherwise raises a - * {@link VerificationError} - */ - public static boolean verifyUniqueIfExternal(Node node, Graph graph) { - if (node != null && node.isExternal() && Graph.CacheExternalNodesInGraph) { - Node cached = graph.findNodeInCache(node); - node.assertTrue(cached == node, "external node does not match canonical node %s", cached); - } - return true; - } - protected void afterClone(@SuppressWarnings("unused") Node other) { } @@ -736,8 +705,6 @@ assertTrue(graph() != null, "null graph"); for (Node input : inputs()) { assertTrue(!input.recordsUsages() || input.usages().contains(this), "missing usage in input %s", input); - assert verifyUniqueIfExternal(input, graph()); - assertTrue(input.isExternal() || input.graph() == graph(), "mismatching graph in input %s", input); } for (Node successor : successors()) { assertTrue(successor.predecessor() == this, "missing predecessor in %s (actual: %s)", successor, successor.predecessor()); @@ -791,9 +758,7 @@ } /** - * Nodes always use an {@linkplain System#identityHashCode(Object) identity} hash code. For this - * reason, {@linkplain #isExternal() external} nodes should still be {@link Graph#unique unique} - * within the context of a graph. + * Nodes always use an {@linkplain System#identityHashCode(Object) identity} hash code. */ @Override public final int hashCode() { @@ -801,8 +766,7 @@ } /** - * Equality tests must rely solely on identity. For this reason, {@linkplain #isExternal() - * external} nodes should still be {@link Graph#unique unique} within the context of a graph. + * Equality tests must rely solely on identity. */ @Override public final boolean equals(Object obj) { diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeClass.java --- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeClass.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeClass.java Tue Dec 17 21:26:42 2013 -0800 @@ -937,7 +937,6 @@ if (input != null) { Node newInput = duplicationReplacement.replacement(input, true); node.updateUsages(null, newInput); - assert Node.verifyUniqueIfExternal(newInput, node.graph()); assert newInput == null || fieldTypes.get(inputOffsets[index]).isAssignableFrom(newInput.getClass()) : "Can not assign " + newInput.getClass() + " to " + fieldTypes.get(inputOffsets[index]) + " in " + node; putNode(node, inputOffsets[index], newInput); @@ -994,7 +993,6 @@ Node oldNode = list.get(i); if (oldNode != null) { Node newNode = duplicationReplacement.replacement(oldNode, true); - assert Node.verifyUniqueIfExternal(newNode, node.graph()); result.set(i, newNode); } } @@ -1079,7 +1077,6 @@ } public boolean replaceFirstInput(Node node, Node old, Node other) { - assert Node.verifyUniqueIfExternal(other, node.graph()); int index = 0; while (index < directInputCount) { Node input = getNode(node, inputOffsets[index]); @@ -1384,9 +1381,6 @@ InplaceUpdateClosure replacementClosure = new InplaceUpdateClosure() { public Node replacement(Node node, boolean isInput) { - if (node.isExternal() && node instanceof ValueNumberable) { - return graph.uniqueExternal(node); - } Node target = newNodes.get(node); if (target == null) { Node replacement = node; diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackend.java --- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackend.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackend.java Tue Dec 17 21:26:42 2013 -0800 @@ -83,8 +83,8 @@ * @param isVerifiedEntryPoint specifies if the code buffer is currently at the verified entry * point */ - protected static void emitStackOverflowCheck(CompilationResultBuilder crb, int stackShadowPages, boolean afterFrameInit, boolean isVerifiedEntryPoint) { - if (stackShadowPages > 0) { + protected static void emitStackOverflowCheck(CompilationResultBuilder crb, int pagesToBang, boolean afterFrameInit, boolean isVerifiedEntryPoint) { + if (pagesToBang > 0) { AMD64MacroAssembler asm = (AMD64MacroAssembler) crb.asm; int frameSize = crb.frameMap.frameSize(); @@ -92,7 +92,7 @@ int lastFramePage = frameSize / unsafe.pageSize(); // emit multiple stack bangs for methods with frames larger than a page for (int i = 0; i <= lastFramePage; i++) { - int disp = (i + stackShadowPages) * unsafe.pageSize(); + int disp = (i + pagesToBang) * unsafe.pageSize(); if (afterFrameInit) { disp -= frameSize; } @@ -141,12 +141,12 @@ } } else { int verifiedEntryPointOffset = asm.codeBuffer.position(); - if (!isStub && stackShadowPages > 0) { - emitStackOverflowCheck(crb, stackShadowPages, false, true); + if (!isStub && pagesToBang > 0) { + emitStackOverflowCheck(crb, pagesToBang, false, true); assert asm.codeBuffer.position() - verifiedEntryPointOffset >= PATCHED_VERIFIED_ENTRY_POINT_INSTRUCTION_SIZE; } if (!isStub && asm.codeBuffer.position() == verifiedEntryPointOffset) { - asm.subq(rsp, frameSize, true); + asm.subqWide(rsp, frameSize); assert asm.codeBuffer.position() - verifiedEntryPointOffset >= PATCHED_VERIFIED_ENTRY_POINT_INSTRUCTION_SIZE; } else { asm.decrementq(rsp, frameSize); @@ -278,7 +278,7 @@ * @param installedCodeOwner see {@link Backend#emitCode} */ public void emitCodeBody(ResolvedJavaMethod installedCodeOwner, CompilationResultBuilder crb, LIRGenerator lirGen) { - lirGen.lir.emitCode(crb); + crb.emit(lirGen.lir); } /** diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackendFactory.java --- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackendFactory.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackendFactory.java Tue Dec 17 21:26:42 2013 -0800 @@ -37,7 +37,40 @@ public class AMD64HotSpotBackendFactory implements HotSpotBackendFactory { protected Architecture createArchitecture(HotSpotVMConfig config) { - return new AMD64(config.useSSE, config.useAVX); + return new AMD64(computeFeatures(config)); + } + + protected EnumSet computeFeatures(HotSpotVMConfig config) { + // Configure the feature set using the HotSpot flag settings. + EnumSet features = EnumSet.noneOf(AMD64.CPUFeature.class); + assert config.useSSE >= 2 : "minimum config for x64"; + features.add(AMD64.CPUFeature.SSE); + features.add(AMD64.CPUFeature.SSE2); + if (config.useSSE > 2) { + features.add(AMD64.CPUFeature.SSE3); + } + if (config.useSSE > 3) { + features.add(AMD64.CPUFeature.SSE4); + } + if (config.useAVX > 0) { + features.add(AMD64.CPUFeature.AVX); + } + if (config.useAVX > 1) { + features.add(AMD64.CPUFeature.AVX2); + } + if (config.useCountLeadingZerosInstruction) { + features.add(AMD64.CPUFeature.LZCNT); + } + if (config.usePopCountInstruction) { + features.add(AMD64.CPUFeature.POPCNT); + } + if (config.useAESIntrinsics) { + features.add(AMD64.CPUFeature.AES); + } + if (config.allocatePrefetchInstr == 3) { + features.add(AMD64.CPUFeature.AMD_3DNOW_PREFETCH); + } + return features; } protected TargetDescription createTarget(HotSpotVMConfig config) { diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerator.java --- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerator.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerator.java Tue Dec 17 21:26:42 2013 -0800 @@ -48,7 +48,7 @@ import com.oracle.graal.hotspot.nodes.*; import com.oracle.graal.hotspot.stubs.*; import com.oracle.graal.lir.*; -import com.oracle.graal.lir.StandardOp.PlaceholderOp; +import com.oracle.graal.lir.StandardOp.NoOp; import com.oracle.graal.lir.StandardOp.SaveRegistersOp; import com.oracle.graal.lir.amd64.*; import com.oracle.graal.lir.amd64.AMD64ControlFlow.CondMoveOp; @@ -93,14 +93,14 @@ */ class SaveRbp { - final PlaceholderOp placeholder; + final NoOp placeholder; /** * The slot reserved for saving RBP. */ final StackSlot reservedSlot; - public SaveRbp(PlaceholderOp placeholder) { + public SaveRbp(NoOp placeholder) { this.placeholder = placeholder; this.reservedSlot = frameMap.allocateSpillSlot(Kind.Long); assert reservedSlot.getRawOffset() == -16 : reservedSlot.getRawOffset(); @@ -172,7 +172,7 @@ emitIncomingValues(params); - saveRbp = new SaveRbp(new PlaceholderOp(currentBlock, lir.lir(currentBlock).size())); + saveRbp = new SaveRbp(new NoOp(currentBlock, lir.lir(currentBlock).size())); append(saveRbp.placeholder); for (LocalNode local : graph.getNodes(LocalNode.class)) { @@ -433,6 +433,7 @@ @Override public void beforeRegisterAllocation() { + super.beforeRegisterAllocation(); boolean hasDebugInfo = lir.hasDebugInfo(); AllocatableValue savedRbp = saveRbp.finalize(hasDebugInfo); if (hasDebugInfo) { diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotRegisterConfig.java --- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotRegisterConfig.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotRegisterConfig.java Tue Dec 17 21:26:42 2013 -0800 @@ -40,6 +40,13 @@ private final Register[] allocatable; + /** + * The same as {@link #allocatable}, except if parameter registers are removed with the + * {@link #RegisterPressure} option. The caller saved registers always include all parameter + * registers. + */ + private final Register[] callerSaved; + private final HashMap categorized = new HashMap<>(); private final RegisterAttributes[] attributesMap; @@ -129,12 +136,20 @@ csl = null; allocatable = initAllocatable(config.useCompressedOops); + Set callerSaveSet = new HashSet<>(); + Collections.addAll(callerSaveSet, allocatable); + Collections.addAll(callerSaveSet, xmmParameterRegisters); + Collections.addAll(callerSaveSet, javaGeneralParameterRegisters); + Collections.addAll(callerSaveSet, nativeGeneralParameterRegisters); + callerSaved = callerSaveSet.toArray(new Register[callerSaveSet.size()]); + assert callerSaved.length == allocatable.length || RegisterPressure.getValue() != null; + attributesMap = RegisterAttributes.createMap(this, AMD64.allRegisters); } @Override public Register[] getCallerSaveRegisters() { - return getAllocatableRegisters(); + return callerSaved; } @Override diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotSafepointOp.java --- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotSafepointOp.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotSafepointOp.java Tue Dec 17 21:26:42 2013 -0800 @@ -42,20 +42,24 @@ public class AMD64HotSpotSafepointOp extends AMD64LIRInstruction { @State protected LIRFrameState state; - @Temp({OperandFlag.REG}) private AllocatableValue temp; + @Temp({OperandFlag.REG, OperandFlag.ILLEGAL}) private AllocatableValue temp; private final HotSpotVMConfig config; public AMD64HotSpotSafepointOp(LIRFrameState state, HotSpotVMConfig config, LIRGeneratorTool tool) { this.state = state; this.config = config; - temp = tool.newVariable(tool.target().wordKind); + if (isPollingPageFar(config)) { + temp = tool.newVariable(tool.target().wordKind); + } else { + // Don't waste a register if it's unneeded + temp = Value.ILLEGAL; + } } @Override public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler asm) { - RegisterValue scratch = (RegisterValue) temp; - emitCode(crb, asm, config, false, state, scratch.getRegister()); + emitCode(crb, asm, config, false, state, temp instanceof RegisterValue ? ((RegisterValue) temp).getRegister() : null); } /** @@ -76,7 +80,7 @@ if (state != null) { crb.recordInfopoint(pos, state, InfopointReason.SAFEPOINT); } - asm.movq(scratch, new AMD64Address(scratch)); + asm.testl(rax, new AMD64Address(scratch)); } else { crb.recordMark(atReturn ? MARK_POLL_RETURN_NEAR : MARK_POLL_NEAR); if (state != null) { @@ -84,7 +88,7 @@ } // The C++ code transforms the polling page offset into an RIP displacement // to the real address at that offset in the polling page. - asm.movq(scratch, new AMD64Address(rip, 0)); + asm.testl(rax, new AMD64Address(rip, 0)); } } } diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotBackend.java --- a/graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotBackend.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotBackend.java Tue Dec 17 21:26:42 2013 -0800 @@ -41,6 +41,8 @@ import com.oracle.graal.lir.asm.*; import com.oracle.graal.lir.hsail.*; import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.spi.Replacements; +import com.oracle.graal.replacements.hsail.*; /** * HSAIL specific backend. @@ -63,12 +65,23 @@ return true; } + /** + * Completes the initialization of the HSAIL backend. This includes initializing the providers + * and registering any method substitutions specified by the HSAIL backend. + */ @Override public void completeInitialization() { final HotSpotProviders providers = getProviders(); HotSpotVMConfig config = getRuntime().getConfig(); + // Initialize the lowering provider. final HotSpotLoweringProvider lowerer = (HotSpotLoweringProvider) providers.getLowerer(); lowerer.initialize(providers, config); + + // Register the replacements used by the HSAIL backend. + Replacements replacements = providers.getReplacements(); + + // Register the substitutions for java.lang.Math routines. + replacements.registerSubstitutions(HSAILMathSubstitutions.class); } /** @@ -284,7 +297,7 @@ } } // Prologue done, Emit code for the LIR. - lirGen.lir.emitCode(crb); + crb.emit(lirGen.lir); // Now that code is emitted go back and figure out what the upper Bound stack size was. long maxStackSize = ((HSAILAssembler) crb.asm).upperBoundStackSize(); String spillsegStringFinal; diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.hotspot.ptx/src/com/oracle/graal/hotspot/ptx/PTXHotSpotBackend.java --- a/graal/com.oracle.graal.hotspot.ptx/src/com/oracle/graal/hotspot/ptx/PTXHotSpotBackend.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.hotspot.ptx/src/com/oracle/graal/hotspot/ptx/PTXHotSpotBackend.java Tue Dec 17 21:26:42 2013 -0800 @@ -284,7 +284,7 @@ } // Emit code for the LIR try { - lirGen.lir.emitCode(crb); + crb.emit(lirGen.lir); } catch (GraalInternalError e) { e.printStackTrace(); // TODO : Better error handling needs to be done once diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotBackend.java --- a/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotBackend.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotBackend.java Tue Dec 17 21:26:42 2013 -0800 @@ -81,15 +81,15 @@ * @param afterFrameInit specifies if the stack pointer has already been adjusted to allocate * the current frame */ - protected static void emitStackOverflowCheck(CompilationResultBuilder crb, int stackShadowPages, boolean afterFrameInit) { - if (stackShadowPages > 0) { + protected static void emitStackOverflowCheck(CompilationResultBuilder crb, int pagesToBang, boolean afterFrameInit) { + if (pagesToBang > 0) { SPARCMacroAssembler masm = (SPARCMacroAssembler) crb.asm; final int frameSize = crb.frameMap.totalFrameSize(); if (frameSize > 0) { int lastFramePage = frameSize / unsafe.pageSize(); // emit multiple stack bangs for methods with frames larger than a page for (int i = 0; i <= lastFramePage; i++) { - int disp = (i + stackShadowPages) * unsafe.pageSize(); + int disp = (i + pagesToBang) * unsafe.pageSize(); if (afterFrameInit) { disp -= frameSize; } @@ -124,8 +124,8 @@ final int frameSize = crb.frameMap.totalFrameSize(); SPARCMacroAssembler masm = (SPARCMacroAssembler) crb.asm; - if (!isStub && stackShadowPages > 0) { - emitStackOverflowCheck(crb, stackShadowPages, false); + if (!isStub && pagesToBang > 0) { + emitStackOverflowCheck(crb, pagesToBang, false); } new Save(sp, -frameSize, sp).emit(masm); @@ -207,7 +207,7 @@ crb.recordMark(Marks.MARK_VERIFIED_ENTRY); // Emit code for the LIR - lirGen.lir.emitCode(crb); + crb.emit(lirGen.lir); HotSpotFrameContext frameContext = (HotSpotFrameContext) crb.frameContext; HotSpotForeignCallsProvider foreignCalls = getProviders().getForeignCalls(); diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/CompileTheWorldTest.java --- a/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/CompileTheWorldTest.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/CompileTheWorldTest.java Tue Dec 17 21:26:42 2013 -0800 @@ -28,6 +28,7 @@ import com.oracle.graal.compiler.test.*; import com.oracle.graal.hotspot.*; +import com.oracle.graal.hotspot.CompileTheWorld.Config; /** * Tests {@link CompileTheWorld} functionality. @@ -39,7 +40,7 @@ boolean originalSetting = ExitVMOnException.getValue(); // Compile a couple classes in rt.jar String file = System.getProperty("java.home") + "/lib/rt.jar"; - new CompileTheWorld(file, 1, 5, false).compile(); + new CompileTheWorld(file, new Config(null), 1, 5, false).compile(); ExitVMOnException.setValue(originalSetting); } diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompilationTask.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompilationTask.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompilationTask.java Tue Dec 17 21:26:42 2013 -0800 @@ -47,7 +47,7 @@ import com.oracle.graal.phases.*; import com.oracle.graal.phases.tiers.*; -public final class CompilationTask implements Runnable { +public class CompilationTask implements Runnable { public static final ThreadLocal withinEnqueue = new ThreadLocal() { @@ -77,7 +77,7 @@ return new CompilationTask(backend, plan, optimisticOpts, profilingInfo, method, entryBCI, id); } - private CompilationTask(HotSpotBackend backend, PhasePlan plan, OptimisticOptimizations optimisticOpts, ProfilingInfo profilingInfo, HotSpotResolvedJavaMethod method, int entryBCI, int id) { + protected CompilationTask(HotSpotBackend backend, PhasePlan plan, OptimisticOptimizations optimisticOpts, ProfilingInfo profilingInfo, HotSpotResolvedJavaMethod method, int entryBCI, int id) { assert id >= 0; this.backend = backend; this.plan = plan; @@ -104,7 +104,7 @@ public void run() { withinEnqueue.set(Boolean.FALSE); try { - runCompilation(); + runCompilation(true); } finally { if (method.currentTask() == this) { method.setCurrentTask(null); @@ -120,7 +120,11 @@ public static final DebugTimer CodeInstallationTime = Debug.timer("CodeInstallation"); - public void runCompilation() { + protected Suites getSuites(HotSpotProviders providers) { + return providers.getSuites().getDefaultSuites(); + } + + public void runCompilation(boolean clearFromCompilationQueue) { /* * no code must be outside this try/finally because it could happen otherwise that * clearQueuedForCompilation() is not executed @@ -164,7 +168,7 @@ } InlinedBytecodes.add(method.getCodeSize()); CallingConvention cc = getCallingConvention(providers.getCodeCache(), Type.JavaCallee, graph.method(), false); - Suites suites = providers.getSuites().getDefaultSuites(); + Suites suites = getSuites(providers); result = compileGraph(graph, cc, method, providers, backend, backend.getTarget(), graphCache, plan, optimisticOpts, profilingInfo, method.getSpeculationLog(), suites, true, new CompilationResult(), CompilationResultBuilderFactory.Default); @@ -211,8 +215,10 @@ c2vm.notifyCompilationStatistics(id, method, entryBCI != INVOCATION_ENTRY_BCI, (int) processedBytes, time, timeUnitsPerSecond, installedCode); } - assert method.isQueuedForCompilation(); - method.clearQueuedForCompilation(); + if (clearFromCompilationQueue) { + assert method.isQueuedForCompilation(); + method.clearQueuedForCompilation(); + } } } diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompileTheWorld.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompileTheWorld.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompileTheWorld.java Tue Dec 17 21:26:42 2013 -0800 @@ -22,7 +22,9 @@ */ package com.oracle.graal.hotspot; +import static com.oracle.graal.hotspot.CompileTheWorld.Options.*; import static com.oracle.graal.hotspot.HotSpotGraalRuntime.*; +import static com.oracle.graal.nodes.StructuredGraph.*; import static com.oracle.graal.phases.GraalOptions.*; import java.io.*; @@ -32,12 +34,17 @@ import java.util.jar.*; import com.oracle.graal.api.meta.*; +import com.oracle.graal.api.meta.ProfilingInfo.TriState; import com.oracle.graal.bytecode.*; import com.oracle.graal.debug.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.hotspot.HotSpotOptions.OptionConsumer; import com.oracle.graal.hotspot.bridge.*; import com.oracle.graal.hotspot.meta.*; -import com.oracle.graal.nodes.*; +import com.oracle.graal.options.*; +import com.oracle.graal.options.OptionValue.OverrideScope; import com.oracle.graal.phases.*; +import com.oracle.graal.phases.tiers.*; import com.oracle.graal.replacements.*; /** @@ -46,21 +53,109 @@ public final class CompileTheWorld { /** - * This is our magic token to trigger reading files from the boot class path. + * Magic token to trigger reading files from the boot class path. */ public static final String SUN_BOOT_CLASS_PATH = "sun.boot.class.path"; + public static class Options { + // @formatter:off + @Option(help = "Compile all methods in all classes on given class path") + public static final OptionValue CompileTheWorldClasspath = new OptionValue<>(SUN_BOOT_CLASS_PATH); + @Option(help = "Verbose CompileTheWorld operation") + public static final OptionValue CompileTheWorldVerbose = new OptionValue<>(true); + @Option(help = "The number of CompileTheWorld iterations to perform") + public static final OptionValue CompileTheWorldIterations = new OptionValue<>(1); + @Option(help = "First class to consider when using -XX:+CompileTheWorld") + public static final OptionValue CompileTheWorldStartAt = new OptionValue<>(1); + @Option(help = "Last class to consider when using -XX:+CompileTheWorld") + public static final OptionValue CompileTheWorldStopAt = new OptionValue<>(Integer.MAX_VALUE); + @Option(help = "Option value overrides to use during compile the world. For example, " + + "to disable inlining and partial escape analysis specify '-PartialEscapeAnalysis -Inline'. " + + "The format for each option is the same as on the command line just without the '-G:' prefix.") + public static final OptionValue CompileTheWorldConfig = new OptionValue<>(null); + // @formatter:on + + /** + * Overrides {@link #CompileTheWorldStartAt} and {@link #CompileTheWorldStopAt} from + * {@code -XX} HotSpot options of the same name if the latter have non-default values. + */ + static void overrideWithNativeOptions(HotSpotVMConfig c) { + if (c.compileTheWorldStartAt != 1) { + CompileTheWorldStartAt.setValue(c.compileTheWorldStartAt); + } + if (c.compileTheWorldStopAt != Integer.MAX_VALUE) { + CompileTheWorldStopAt.setValue(c.compileTheWorldStopAt); + } + } + } + + /** + * A mechanism for overriding Graal options that affect compilation. A {@link Config} object + * should be used in a try-with-resources statement to ensure overriding of options is scoped + * properly. For example: + * + *

+     *     Config config = ...;
+     *     try (AutoCloseable s = config == null ? null : config.apply()) {
+     *         // perform a Graal compilation
+     *     }
+     * 
+ */ + @SuppressWarnings("serial") + public static class Config extends HashMap, Object> implements AutoCloseable, OptionConsumer { + OverrideScope scope; + + /** + * Creates a {@link Config} object by parsing a set of space separated override options. + * + * @param options a space separated set of option value settings with each option setting in + * a format compatible with + * {@link HotSpotOptions#parseOption(String, OptionConsumer)}. Ignored if null. + */ + public Config(String options) { + if (options != null) { + for (String option : options.split("\\s+")) { + if (!HotSpotOptions.parseOption(option, this)) { + throw new GraalInternalError("Invalid option specified: %s", option); + } + } + } + } + + /** + * Applies the overrides represented by this object. The overrides are in effect until + * {@link #close()} is called on this object. + */ + Config apply() { + assert scope == null; + scope = OptionValue.override(this); + return this; + } + + public void close() { + assert scope != null; + scope.close(); + + scope = null; + + } + + public void set(OptionDescriptor desc, Object value) { + put(desc.getOptionValue(), value); + } + } + // Some runtime instances we need. private final HotSpotGraalRuntime runtime = runtime(); private final VMToCompilerImpl vmToCompiler = (VMToCompilerImpl) runtime.getVMToCompiler(); - /** List of Zip/Jar files to compile (see {@link GraalOptions#CompileTheWorld}. */ + /** List of Zip/Jar files to compile (see {@link #CompileTheWorldClasspath}. */ private final String files; - /** Class index to start compilation at (see {@link GraalOptions#CompileTheWorldStartAt}. */ + /** Class index to start compilation at (see {@link #CompileTheWorldStartAt}. */ private final int startAt; - /** Class index to stop compilation at (see {@link GraalOptions#CompileTheWorldStopAt}. */ + /** Class index to stop compilation at (see {@link #CompileTheWorldStopAt}. */ private final int stopAt; // Counters @@ -69,44 +164,35 @@ private long compileTime = 0; private boolean verbose; + private final Config config; /** - * Create a compile-the-world instance with default values from - * {@link GraalOptions#CompileTheWorld}, {@link GraalOptions#CompileTheWorldStartAt} and - * {@link GraalOptions#CompileTheWorldStopAt}. - */ - public CompileTheWorld() { - this(CompileTheWorld.getValue(), CompileTheWorldStartAt.getValue(), CompileTheWorldStopAt.getValue(), true); - } - - /** - * Create a compile-the-world instance. + * Creates a compile-the-world instance. * * @param files {@link File#pathSeparator} separated list of Zip/Jar files to compile * @param startAt index of the class file to start compilation at * @param stopAt index of the class file to stop compilation at */ - public CompileTheWorld(String files, int startAt, int stopAt, boolean verbose) { + public CompileTheWorld(String files, Config config, int startAt, int stopAt, boolean verbose) { this.files = files; this.startAt = startAt; this.stopAt = stopAt; this.verbose = verbose; + this.config = config; // We don't want the VM to exit when a method fails to compile... - ExitVMOnException.setValue(false); + config.put(ExitVMOnException, false); // ...but we want to see exceptions. - PrintBailout.setValue(true); - PrintStackTraceOnException.setValue(true); + config.put(PrintBailout, true); + config.put(PrintStackTraceOnException, true); } /** - * Compile all methods in all classes in the Zip/Jar files in - * {@link GraalOptions#CompileTheWorld}. If the GraalOptions.CompileTheWorld contains the magic - * token {@link CompileTheWorld#SUN_BOOT_CLASS_PATH} passed up from HotSpot we take the files - * from the boot class path. - * - * @throws Throwable + * Compiles all methods in all classes in the Zip/Jar archive files in + * {@link #CompileTheWorldClasspath}. If {@link #CompileTheWorldClasspath} contains the magic + * token {@link #SUN_BOOT_CLASS_PATH} passed up from HotSpot we take the files from the boot + * class path. */ public void compile() throws Throwable { if (SUN_BOOT_CLASS_PATH.equals(files)) { @@ -145,7 +231,7 @@ } /** - * Compile all methods in all classes in the Zip/Jar files passed. + * Compiles all methods in all classes in the Zip/Jar files passed. * * @param fileList {@link File#pathSeparator} separated list of Zip/Jar files to compile * @throws Throwable @@ -186,7 +272,7 @@ String className = je.getName().substring(0, je.getName().length() - ".class".length()); classFileCounter++; - try { + try (AutoCloseable s = config.apply()) { // Load and initialize class Class javaClass = Class.forName(className.replace('/', '.'), true, loader); @@ -229,16 +315,42 @@ } println(); - println("CompileTheWorld : Done (%d classes, %d methods, %d ms)", classFileCounter, compiledMethodsCounter, compileTime); + TTY.println("CompileTheWorld : Done (%d classes, %d methods, %d ms)", classFileCounter, compiledMethodsCounter, compileTime); } /** - * Helper method to schedule a method for compilation and gather some statistics. + * A compilation task that creates a fresh compilation suite for its compilation. This is + * required so that a CTW compilation can be {@linkplain Config configured} differently from a + * VM triggered compilation. + */ + static class CTWCompilationTask extends CompilationTask { + + CTWCompilationTask(HotSpotBackend backend, PhasePlan plan, OptimisticOptimizations optimisticOpts, ProfilingInfo profilingInfo, HotSpotResolvedJavaMethod method, int entryBCI, int id) { + super(backend, plan, optimisticOpts, profilingInfo, method, entryBCI, id); + } + + @Override + protected Suites getSuites(HotSpotProviders providers) { + return providers.getSuites().createSuites(); + } + } + + /** + * Compiles a method and gathers some statistics. */ private void compileMethod(HotSpotResolvedJavaMethod method) { try { long start = System.currentTimeMillis(); - vmToCompiler.compileMethod(method, StructuredGraph.INVOCATION_ENTRY_BCI, true); + + // Be optimistic and return false for exceptionSeen. + final ProfilingInfo profilingInfo = DefaultProfilingInfo.get(TriState.FALSE); + final OptimisticOptimizations optimisticOpts = new OptimisticOptimizations(profilingInfo); + int id = vmToCompiler.allocateCompileTaskId(); + HotSpotBackend backend = runtime.getHostBackend(); + PhasePlan phasePlan = vmToCompiler.createPhasePlan(backend.getProviders(), optimisticOpts, false); + CompilationTask task = new CTWCompilationTask(backend, phasePlan, optimisticOpts, profilingInfo, method, INVOCATION_ENTRY_BCI, id); + task.runCompilation(false); + compileTime += (System.currentTimeMillis() - start); compiledMethodsCounter++; method.reprofile(); // makes the method also not-entrant @@ -250,18 +362,16 @@ } /** - * Helper method for CompileTheWorld to determine if a method should be compiled (Cf. - * CompilationPolicy::can_be_compiled). + * Determines if a method should be compiled (Cf. CompilationPolicy::can_be_compiled). * * @return true if it can be compiled, false otherwise */ - private static boolean canBeCompiled(HotSpotResolvedJavaMethod javaMethod, int modifiers) { + private boolean canBeCompiled(HotSpotResolvedJavaMethod javaMethod, int modifiers) { if (Modifier.isAbstract(modifiers) || Modifier.isNative(modifiers)) { return false; } - // This number is from HotSpot: - final int hugeMethodLimit = 8000; - if (javaMethod.getCodeSize() > hugeMethodLimit) { + HotSpotVMConfig c = runtime.getConfig(); + if (c.dontCompileHugeMethods && javaMethod.getCodeSize() > c.hugeMethodLimit) { return false; } // Skip @Snippets for now diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotGraalRuntime.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotGraalRuntime.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotGraalRuntime.java Tue Dec 17 21:26:42 2013 -0800 @@ -24,6 +24,7 @@ import static com.oracle.graal.graph.UnsafeAccess.*; import static com.oracle.graal.hotspot.HotSpotGraalRuntime.Options.*; +import static com.oracle.graal.phases.GraalOptions.*; import java.lang.reflect.*; import java.util.*; @@ -231,24 +232,15 @@ initMirror(typeDouble); initMirror(typeVoid); - // Set some global options: - if (config.compileTheWorld) { - GraalOptions.CompileTheWorld.setValue(CompileTheWorld.SUN_BOOT_CLASS_PATH); - } - if (config.compileTheWorldStartAt != 1) { - GraalOptions.CompileTheWorldStartAt.setValue(config.compileTheWorldStartAt); - } - if (config.compileTheWorldStopAt != Integer.MAX_VALUE) { - GraalOptions.CompileTheWorldStopAt.setValue(config.compileTheWorldStopAt); - } + CompileTheWorld.Options.overrideWithNativeOptions(config); // Only set HotSpotPrintCompilation and HotSpotPrintInlining if they still have their // default value (false). - if (GraalOptions.HotSpotPrintCompilation.getValue() == false) { - GraalOptions.HotSpotPrintCompilation.setValue(config.printCompilation); + if (HotSpotPrintCompilation.getValue() == false) { + HotSpotPrintCompilation.setValue(config.printCompilation); } - if (GraalOptions.HotSpotPrintInlining.getValue() == false) { - GraalOptions.HotSpotPrintInlining.setValue(config.printInlining); + if (HotSpotPrintInlining.getValue() == false) { + HotSpotPrintInlining.setValue(config.printInlining); } if (Boolean.valueOf(System.getProperty("graal.printconfig"))) { diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotHostBackend.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotHostBackend.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotHostBackend.java Tue Dec 17 21:26:42 2013 -0800 @@ -40,11 +40,11 @@ /** * This will be 0 if stack banging is disabled. */ - protected final int stackShadowPages; + protected final int pagesToBang; public HotSpotHostBackend(HotSpotGraalRuntime runtime, HotSpotProviders providers) { super(runtime, providers); - this.stackShadowPages = runtime.getConfig().useStackBanging ? runtime.getConfig().stackShadowPages : 0; + this.pagesToBang = runtime.getConfig().useStackBanging ? runtime.getConfig().stackShadowPages : 0; } @Override diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotOptions.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotOptions.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotOptions.java Tue Dec 17 21:26:42 2013 -0800 @@ -103,6 +103,22 @@ // Called from VM code public static boolean setOption(String option) { + return parseOption(option, null); + } + + interface OptionConsumer { + void set(OptionDescriptor desc, Object value); + } + + /** + * Parses a given option value specification. + * + * @param option the specification of an option and its value + * @param setter the object to notify of the parsed option and value. If null, the + * {@link OptionValue#setValue(Object)} method of the specified option is called + * instead. + */ + public static boolean parseOption(String option, OptionConsumer setter) { if (option.length() == 0) { return false; } @@ -175,9 +191,13 @@ } if (value != null) { - OptionValue optionValue = desc.getOptionValue(); - optionValue.setValue(value); - // Logger.info("Set option " + desc.getName() + " to " + value); + if (setter != null) { + setter.set(desc, value); + } else { + OptionValue optionValue = desc.getOptionValue(); + optionValue.setValue(value); + // Logger.info("Set option " + desc.getName() + " to " + value); + } } else { Logger.info("Wrong value \"" + valueString + "\" for option " + optionName); return false; diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java Tue Dec 17 21:26:42 2013 -0800 @@ -662,6 +662,8 @@ @HotSpotVMFlag(name = "CompileTheWorld") @Stable public boolean compileTheWorld; @HotSpotVMFlag(name = "CompileTheWorldStartAt") @Stable public int compileTheWorldStartAt; @HotSpotVMFlag(name = "CompileTheWorldStopAt") @Stable public int compileTheWorldStopAt; + @HotSpotVMFlag(name = "DontCompileHugeMethods") @Stable public boolean dontCompileHugeMethods; + @HotSpotVMFlag(name = "HugeMethodLimit") @Stable public int hugeMethodLimit; @HotSpotVMFlag(name = "PrintCompilation") @Stable public boolean printCompilation; @HotSpotVMFlag(name = "PrintInlining") @Stable public boolean printInlining; @HotSpotVMFlag(name = "GraalUseFastLocking") @Stable public boolean useFastLocking; @@ -669,6 +671,7 @@ @HotSpotVMFlag(name = "UseTLAB") @Stable public boolean useTLAB; @HotSpotVMFlag(name = "UseBiasedLocking") @Stable public boolean useBiasedLocking; @HotSpotVMFlag(name = "UsePopCountInstruction") @Stable public boolean usePopCountInstruction; + @HotSpotVMFlag(name = "UseCountLeadingZerosInstruction") @Stable public boolean useCountLeadingZerosInstruction; @HotSpotVMFlag(name = "UseAESIntrinsics") @Stable public boolean useAESIntrinsics; @HotSpotVMFlag(name = "UseCRC32Intrinsics") @Stable public boolean useCRC32Intrinsics; @HotSpotVMFlag(name = "UseG1GC") @Stable public boolean useG1GC; diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVM.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVM.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVM.java Tue Dec 17 21:26:42 2013 -0800 @@ -73,11 +73,10 @@ * Used to implement {@link ResolvedJavaType#findUniqueConcreteMethod(ResolvedJavaMethod)}. * * @param metaspaceMethod the metaspace Method on which to based the search - * @param resultHolder the holder of the result is put in element 0 of this array * @return the metaspace Method result or 0 is there is no unique concrete method for * {@code metaspaceMethod} */ - long getUniqueConcreteMethod(long metaspaceMethod, HotSpotResolvedObjectType[] resultHolder); + long findUniqueConcreteMethod(long metaspaceMethod); /** * Used to determine if an interface has exactly one implementor. @@ -189,6 +188,8 @@ */ void notifyCompilationStatistics(int id, HotSpotResolvedJavaMethod method, boolean osr, int processedBytecodes, long time, long timeUnitsPerSecond, HotSpotInstalledCode installedCode); + void printCompilationStatistics(boolean perCompiler, boolean aggregate); + void resetCompilationStatistics(); void initializeConfiguration(HotSpotVMConfig config); diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVMImpl.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVMImpl.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVMImpl.java Tue Dec 17 21:26:42 2013 -0800 @@ -56,7 +56,7 @@ public native boolean isMethodCompilable(long metaspaceMethod); @Override - public native long getUniqueConcreteMethod(long metaspaceMethod, HotSpotResolvedObjectType[] resultHolder); + public native long findUniqueConcreteMethod(long metaspaceMethod); @Override public native ResolvedJavaType getUniqueImplementor(HotSpotResolvedObjectType interfaceType); @@ -154,6 +154,8 @@ public synchronized native void notifyCompilationStatistics(int id, HotSpotResolvedJavaMethod method, boolean osr, int processedBytecodes, long time, long timeUnitsPerSecond, HotSpotInstalledCode installedCode); + public synchronized native void printCompilationStatistics(boolean perCompiler, boolean aggregate); + public native void resetCompilationStatistics(); /** diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java Tue Dec 17 21:26:42 2013 -0800 @@ -25,6 +25,7 @@ import static com.oracle.graal.compiler.GraalDebugConfig.*; import static com.oracle.graal.graph.UnsafeAccess.*; +import static com.oracle.graal.hotspot.CompileTheWorld.Options.*; import static com.oracle.graal.hotspot.HotSpotGraalRuntime.*; import java.io.*; @@ -42,6 +43,7 @@ import com.oracle.graal.debug.internal.*; import com.oracle.graal.graph.*; import com.oracle.graal.hotspot.*; +import com.oracle.graal.hotspot.CompileTheWorld.Config; import com.oracle.graal.hotspot.debug.*; import com.oracle.graal.hotspot.meta.*; import com.oracle.graal.hotspot.phases.*; @@ -65,6 +67,10 @@ @Option(help = "Print compilation queue activity periodically") private static final OptionValue PrintQueue = new OptionValue<>(false); + @Option(help = "Interval in milliseconds at which to print compilation rate periodically. " + + "The compilation statistics are reset after each print out.") + private static final OptionValue PrintCompRate = new OptionValue<>(0); + @Option(help = "Print bootstrap progress and summary") private static final OptionValue PrintBootstrap = new OptionValue<>(true); @@ -97,6 +103,10 @@ this.runtime = runtime; } + public int allocateCompileTaskId() { + return compileTaskIds.incrementAndGet(); + } + public void startCompiler(boolean bootstrapEnabled) throws Throwable { FastNodeClassRegistry.initialize(); @@ -112,8 +122,6 @@ } } - runtime.getCompilerToVM(); - TTY.initialize(log); if (Log.getValue() == null && Meter.getValue() == null && Time.getValue() == null && Dump.getValue() == null) { @@ -178,6 +186,29 @@ t.start(); } + if (PrintCompRate.getValue() != 0) { + if (!runtime.getConfig().ciTime && !runtime.getConfig().ciTimeEach) { + TTY.println("PrintCompRate requires CITime or CITimeEach"); + } else { + Thread t = new Thread() { + + @Override + public void run() { + while (true) { + runtime.getCompilerToVM().printCompilationStatistics(true, false); + runtime.getCompilerToVM().resetCompilationStatistics(); + try { + Thread.sleep(PrintCompRate.getValue()); + } catch (InterruptedException e) { + } + } + } + }; + t.setDaemon(true); + t.start(); + } + } + BenchmarkCounters.initialize(runtime.getCompilerToVM()); compilerStartTime = System.nanoTime(); @@ -303,7 +334,14 @@ } public void compileTheWorld() throws Throwable { - new CompileTheWorld().compile(); + int iterations = CompileTheWorld.Options.CompileTheWorldIterations.getValue(); + for (int i = 0; i < iterations; i++) { + runtime.getCompilerToVM().resetCompilationStatistics(); + TTY.println("CompileTheWorld : iteration " + i); + CompileTheWorld ctw = new CompileTheWorld(CompileTheWorldClasspath.getValue(), new Config(CompileTheWorldConfig.getValue()), CompileTheWorldStartAt.getValue(), + CompileTheWorldStopAt.getValue(), CompileTheWorldVerbose.getValue()); + ctw.compile(); + } System.exit(0); } @@ -556,12 +594,12 @@ final ProfilingInfo profilingInfo = method.getCompilationProfilingInfo(osrCompilation); final OptimisticOptimizations optimisticOpts = new OptimisticOptimizations(profilingInfo); - int id = compileTaskIds.incrementAndGet(); + int id = allocateCompileTaskId(); HotSpotBackend backend = runtime.getHostBackend(); CompilationTask task = CompilationTask.create(backend, createPhasePlan(backend.getProviders(), optimisticOpts, osrCompilation), optimisticOpts, profilingInfo, method, entryBCI, id); if (blocking) { - task.runCompilation(); + task.runCompilation(true); } else { try { method.setCurrentTask(task); diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotMethodData.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotMethodData.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotMethodData.java Tue Dec 17 21:26:42 2013 -0800 @@ -397,7 +397,7 @@ @Override public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) { - return sb.append(format("count(%d)", getCounterValue(data, pos))); + return sb.append(format("count(%d) null_seen(%s) exception_seen(%s)", getCounterValue(data, pos), getNullSeen(data, pos), getExceptionSeen(data, pos))); } } @@ -524,7 +524,9 @@ public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) { RawItemProfile profile = getRawTypeProfile(data, pos); TriState nullSeen = getNullSeen(data, pos); - sb.append(format("count(%d) null_seen(%s) nonprofiled_count(%d) entries(%d)", getCounterValue(data, pos), nullSeen, getTypesNotRecordedExecutionCount(data, pos), profile.entries)); + TriState exceptionSeen = getExceptionSeen(data, pos); + sb.append(format("count(%d) null_seen(%s) exception_seen(%s) nonprofiled_count(%d) entries(%d)", getCounterValue(data, pos), nullSeen, exceptionSeen, + getTypesNotRecordedExecutionCount(data, pos), profile.entries)); for (int i = 0; i < profile.entries; i++) { long count = profile.counts[i]; sb.append(format("%n %s (%d, %4.2f)", MetaUtil.toJavaName(profile.items[i]), count, (double) count / profile.totalCount)); diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaField.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaField.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaField.java Tue Dec 17 21:26:42 2013 -0800 @@ -41,7 +41,7 @@ /** * Represents a field in a HotSpot type. */ -public class HotSpotResolvedJavaField extends CompilerObject implements ResolvedJavaField, LocationIdentity { +public class HotSpotResolvedJavaField extends CompilerObject implements ResolvedJavaField { // Must not conflict with any fields flags used by the VM - the assertion in the constructor // checks this assumption @@ -117,7 +117,7 @@ return false; } - private static final Set notEmbeddable = new HashSet<>(); + private static final List notEmbeddable = new ArrayList<>(); private static void addResolvedToSet(Field field) { MetaAccessProvider metaAccess = runtime().getHostProviders().getMetaAccess(); diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaMethod.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaMethod.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaMethod.java Tue Dec 17 21:26:42 2013 -0800 @@ -320,13 +320,22 @@ } public ResolvedJavaMethod uniqueConcreteMethod() { - HotSpotResolvedObjectType[] resultHolder = {null}; - long ucm = runtime().getCompilerToVM().getUniqueConcreteMethod(metaspaceMethod, resultHolder); - if (ucm != 0L) { - assert resultHolder[0] != null; - return resultHolder[0].createMethod(ucm); + if (holder.isInterface()) { + // Cannot trust interfaces. Because of: + // interface I { void foo(); } + // class A { public void foo() {} } + // class B extends A implements I { } + // class C extends B { public void foo() { } } + // class D extends B { } + // Would lead to identify C.foo() as the unique concrete method for I.foo() without + // seeing A.foo(). + return null; } - return null; + final long uniqueConcreteMethod = runtime().getCompilerToVM().findUniqueConcreteMethod(metaspaceMethod); + if (uniqueConcreteMethod == 0) { + return null; + } + return fromMetaspace(uniqueConcreteMethod); } @Override diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.java/src/com/oracle/graal/java/BciBlockMapping.java --- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/BciBlockMapping.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/BciBlockMapping.java Tue Dec 17 21:26:42 2013 -0800 @@ -434,7 +434,7 @@ predecessor.successors.add(sux); } - private final HashSet jsrVisited = new HashSet<>(); + private final ArrayList jsrVisited = new ArrayList<>(); private void createJsrAlternatives(Block block) { jsrVisited.add(block); diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java --- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java Tue Dec 17 21:26:42 2013 -0800 @@ -217,7 +217,7 @@ TTY.println(MetaUtil.indent(MetaUtil.profileToString(profilingInfo, method, CodeUtil.NEW_LINE), " ")); } - Indent indent = Debug.logAndIndent(false, "build graph for %s", method.toString()); + Indent indent = Debug.logAndIndent(false, "build graph for %s", method); // compute the block map, setup exception handlers and get the entrypoint(s) BciBlockMapping blockMap = createBlockMap(); diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64ControlFlow.java --- a/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64ControlFlow.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64ControlFlow.java Tue Dec 17 21:26:42 2013 -0800 @@ -73,12 +73,11 @@ @Override public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) { - int sourceIndex = crb.getCurrentBlockIndex(); - if (trueDestination.isCodeEmittingOrderSuccessorEdge(sourceIndex)) { + if (crb.isSuccessorEdge(trueDestination)) { jcc(masm, true, falseDestination); } else { jcc(masm, false, trueDestination); - if (!falseDestination.isCodeEmittingOrderSuccessorEdge(sourceIndex)) { + if (!crb.isSuccessorEdge(falseDestination)) { masm.jmp(falseDestination.label()); } } diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILArithmetic.java --- a/graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILArithmetic.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILArithmetic.java Tue Dec 17 21:26:42 2013 -0800 @@ -29,14 +29,17 @@ import com.oracle.graal.asm.hsail.*; import com.oracle.graal.graph.*; import com.oracle.graal.lir.*; -import com.oracle.graal.lir.asm.*; +import com.oracle.graal.lir.asm.CompilationResultBuilder; /** * Defines arithmetic instruction nodes. */ public enum HSAILArithmetic { + ABS, CALL, + CEIL, FDIV, + FLOOR, FREM, DADD, DDIV, @@ -97,6 +100,7 @@ LUSUB, LXOR, OADD, + RINT, SQRT, UNDEF; @@ -289,10 +293,33 @@ } } + /** + * Emits the HSAIL code for an arithmetic operation taking one input parameter. + * + * @param crb the CompilationResultBuilder + * @param masm the HSAIL assembler + * @param opcode the opcode of the arithmetic operation + * @param dst the destination + * @param src the source parameter + * @param info structure that stores the LIRFrameState. Used for exception handling. + */ + public static void emit(CompilationResultBuilder crb, HSAILAssembler masm, HSAILArithmetic opcode, Value dst, Value src, LIRFrameState info) { int exceptionOffset = -1; if (isRegister(src)) { switch (opcode) { + case ABS: + masm.emit("abs", dst, src); + break; + case CEIL: + masm.emit("ceil", dst, src); + break; + case FLOOR: + masm.emit("floor", dst, src); + break; + case RINT: + masm.emit("rint", dst, src); + break; case SQRT: masm.emit("sqrt", dst, src); break; @@ -304,14 +331,12 @@ break; case INOT: case LNOT: - // Emit the HSAIL instruction for a bitwise not. masm.emitForceBitwise("not", dst, src); break; case INEG: case LNEG: case FNEG: case DNEG: - // Emit the HSAIL instruction for a negate operation. masm.emit("neg", dst, src); break; default: diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILControlFlow.java --- a/graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILControlFlow.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILControlFlow.java Tue Dec 17 21:26:42 2013 -0800 @@ -188,14 +188,13 @@ @Override public void emitCode(CompilationResultBuilder crb, HSAILAssembler masm) { - int sourceIndex = crb.getCurrentBlockIndex(); - if (trueDestination.isCodeEmittingOrderSuccessorEdge(sourceIndex)) { + if (crb.isSuccessorEdge(trueDestination)) { HSAILCompare.emit(crb, masm, condition.negate(), x, y, z, !unordered); masm.cbr(masm.nameOf(falseDestination.label())); } else { HSAILCompare.emit(crb, masm, condition, x, y, z, unordered); masm.cbr(masm.nameOf(trueDestination.label())); - if (!falseDestination.isCodeEmittingOrderSuccessorEdge(sourceIndex)) { + if (!crb.isSuccessorEdge(falseDestination)) { masm.jmp(falseDestination.label()); } } diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.lir.ptx/src/com/oracle/graal/lir/ptx/PTXControlFlow.java --- a/graal/com.oracle.graal.lir.ptx/src/com/oracle/graal/lir/ptx/PTXControlFlow.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.lir.ptx/src/com/oracle/graal/lir/ptx/PTXControlFlow.java Tue Dec 17 21:26:42 2013 -0800 @@ -85,12 +85,11 @@ @Override public void emitCode(CompilationResultBuilder crb, PTXMacroAssembler masm) { - int sourceIndex = crb.getCurrentBlockIndex(); - if (trueDestination.isCodeEmittingOrderSuccessorEdge(sourceIndex)) { + if (crb.isSuccessorEdge(trueDestination)) { masm.bra(masm.nameOf(falseDestination.label()), predRegNum); } else { masm.bra(masm.nameOf(trueDestination.label())); - if (!falseDestination.isCodeEmittingOrderSuccessorEdge(sourceIndex)) { + if (!crb.isSuccessorEdge(falseDestination)) { masm.jmp(falseDestination.label()); } } diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.lir.sparc/src/com/oracle/graal/lir/sparc/SPARCControlFlow.java --- a/graal/com.oracle.graal.lir.sparc/src/com/oracle/graal/lir/sparc/SPARCControlFlow.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.lir.sparc/src/com/oracle/graal/lir/sparc/SPARCControlFlow.java Tue Dec 17 21:26:42 2013 -0800 @@ -73,18 +73,17 @@ @Override public void emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm) { - int sourceIndex = crb.getCurrentBlockIndex(); Label actualTarget; Condition actualCondition; boolean needJump; - if (trueDestination.isCodeEmittingOrderSuccessorEdge(sourceIndex)) { + if (crb.isSuccessorEdge(trueDestination)) { actualCondition = condition.negate(); actualTarget = falseDestination.label(); needJump = false; } else { actualCondition = condition; actualTarget = trueDestination.label(); - needJump = !falseDestination.isCodeEmittingOrderSuccessorEdge(sourceIndex); + needJump = !crb.isSuccessorEdge(falseDestination); } assert kind == Kind.Int || kind == Kind.Long || kind == Kind.Object; CC cc = kind == Kind.Int ? CC.Icc : CC.Xcc; diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LIR.java --- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LIR.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LIR.java Tue Dec 17 21:26:42 2013 -0800 @@ -25,10 +25,8 @@ import java.util.*; import com.oracle.graal.api.meta.*; -import com.oracle.graal.debug.*; -import com.oracle.graal.graph.*; +import com.oracle.graal.lir.LIRInstruction.StateProcedure; import com.oracle.graal.lir.StandardOp.BlockEndOp; -import com.oracle.graal.lir.asm.*; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.cfg.*; @@ -137,46 +135,6 @@ firstVariableNumber = num; } - public void emitCode(CompilationResultBuilder crb) { - crb.frameContext.enter(crb); - - // notifyBlocksOfSuccessors(); - - int index = 0; - for (Block b : codeEmittingOrder) { - crb.setCurrentBlockIndex(index++); - emitBlock(crb, b); - } - } - - private void emitBlock(CompilationResultBuilder crb, Block block) { - if (Debug.isDumpEnabled()) { - crb.blockComment(String.format("block B%d %s", block.getId(), block.getLoop())); - } - - for (LIRInstruction op : lir(block)) { - if (Debug.isDumpEnabled()) { - crb.blockComment(String.format("%d %s", op.id(), op)); - } - - try { - emitOp(crb, op); - } catch (GraalInternalError e) { - throw e.addContext("lir instruction", block + "@" + op.id() + " " + op + "\n" + codeEmittingOrder); - } - } - } - - private static void emitOp(CompilationResultBuilder crb, LIRInstruction op) { - try { - op.emitCode(crb); - } catch (AssertionError t) { - throw new GraalInternalError(t); - } catch (RuntimeException t) { - throw new GraalInternalError(t); - } - } - public void setHasArgInCallerFrame() { hasArgInCallerFrame = true; } @@ -189,15 +147,55 @@ return hasArgInCallerFrame; } + /** + * Gets the exception edge (if any) originating at a given operation. + */ + public static LabelRef getExceptionEdge(LIRInstruction op) { + final LabelRef[] exceptionEdge = {null}; + op.forEachState(new StateProcedure() { + @Override + protected void doState(LIRFrameState state) { + if (state.exceptionEdge != null) { + assert exceptionEdge[0] == null; + exceptionEdge[0] = state.exceptionEdge; + } + } + }); + return exceptionEdge[0]; + } + + /** + * The maximum distance an operation with an {@linkplain #getExceptionEdge(LIRInstruction) + * exception edge} can be from the last instruction of a LIR block. The value of 3 is based on a + * non-void call operation that has an exception edge. Such a call may move the result to + * another register and then spill it. + *

+ * The rationale for such a constant is to limit the search for an insertion point when adding + * move operations at the end of a block. Such moves must be inserted before all control flow + * instructions. + */ + public static final int MAX_EXCEPTION_EDGE_OP_DISTANCE_FROM_END = 3; + public static boolean verifyBlock(LIR lir, Block block) { List ops = lir.lir(block); if (ops.size() == 0) { - return true; + return false; } - for (LIRInstruction op : ops.subList(0, ops.size() - 1)) { + LIRInstruction opWithExceptionEdge = null; + int index = 0; + int lastIndex = ops.size() - 1; + for (LIRInstruction op : ops.subList(0, lastIndex)) { assert !(op instanceof BlockEndOp) : op.getClass(); + LabelRef exceptionEdge = getExceptionEdge(op); + if (exceptionEdge != null) { + assert opWithExceptionEdge == null : "multiple ops with an exception edge not allowed"; + opWithExceptionEdge = op; + int distanceFromEnd = lastIndex - index; + assert distanceFromEnd <= MAX_EXCEPTION_EDGE_OP_DISTANCE_FROM_END; + } + index++; } - LIRInstruction end = ops.get(ops.size() - 1); + LIRInstruction end = ops.get(lastIndex); assert end instanceof BlockEndOp : end.getClass(); return true; } @@ -210,7 +208,9 @@ for (Block pred : block.getPredecessors()) { assert blocks.contains(pred) : "missing predecessor from: " + block + "to: " + pred; } - verifyBlock(lir, block); + if (!verifyBlock(lir, block)) { + return false; + } } return true; } diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LIRFrameState.java --- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LIRFrameState.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LIRFrameState.java Tue Dec 17 21:26:42 2013 -0800 @@ -41,7 +41,7 @@ public final BytecodeFrame topFrame; private final VirtualObject[] virtualObjects; public final LabelRef exceptionEdge; - private DebugInfo debugInfo; + protected DebugInfo debugInfo; public LIRFrameState(BytecodeFrame topFrame, VirtualObject[] virtualObjects, LabelRef exceptionEdge) { this.topFrame = topFrame; @@ -109,8 +109,45 @@ } } - public void finish(BitSet registerRefMap, BitSet frameRefMap) { - debugInfo = new DebugInfo(topFrame, registerRefMap, frameRefMap); + /** + * Called by the register allocator before {@link #markLocation} to initialize the frame state. + * + * @param frameMap The frame map. + * @param canHaveRegisters True if there can be any register map entries. + */ + public void initDebugInfo(FrameMap frameMap, boolean canHaveRegisters) { + BitSet registerRefMap = (canHaveRegisters ? frameMap.initRegisterRefMap() : null); + debugInfo = new DebugInfo(topFrame, registerRefMap, frameMap.initFrameRefMap()); + } + + /** + * Called by the register allocator to mark the specified location as a reference in the + * reference map of the debug information. The tracked location can be a {@link RegisterValue} + * or a {@link StackSlot}. Note that a {@link Constant} is automatically tracked. + * + * @param location The location to be added to the reference map. + * @param frameMap The frame map. + */ + public void markLocation(Value location, FrameMap frameMap) { + if (location.getKind() == Kind.Object) { + if (isRegister(location)) { + debugInfo.getRegisterRefMap().set(asRegister(location).number); + } else if (isStackSlot(location)) { + int index = frameMap.indexForStackSlot(asStackSlot(location)); + debugInfo.getFrameRefMap().set(index); + } else { + assert isConstant(location); + } + } + } + + /** + * Called by the register allocator after all locations are marked. + * + * @param op The instruction to which this frame state belongs. + * @param frameMap The frame map. + */ + public void finish(LIRInstruction op, FrameMap frameMap) { } @Override diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LabelRef.java --- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LabelRef.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LabelRef.java Tue Dec 17 21:26:42 2013 -0800 @@ -22,8 +22,6 @@ */ package com.oracle.graal.lir; -import java.util.*; - import com.oracle.graal.asm.*; import com.oracle.graal.lir.StandardOp.BranchOp; import com.oracle.graal.lir.StandardOp.JumpOp; @@ -78,21 +76,8 @@ return ((StandardOp.LabelOp) lir.lir(getTargetBlock()).get(0)).getLabel(); } - /** - * Determines if the edge represented by this object is from a block to its lexical successor in - * the code emitting order of blocks. - * - * @param sourceIndex the index of this edge's {@linkplain #getSourceBlock() source} in the code - * emitting order - */ - public boolean isCodeEmittingOrderSuccessorEdge(int sourceIndex) { - List order = lir.codeEmittingOrder(); - assert order.get(sourceIndex) == block; - return sourceIndex < order.size() - 1 && order.get(sourceIndex + 1) == getTargetBlock(); - } - @Override public String toString() { - return suxIndex < block.getSuccessorCount() ? getTargetBlock().toString() : "?" + block + ":" + suxIndex + "?"; + return getSourceBlock() + " -> " + (suxIndex < block.getSuccessors().size() ? getTargetBlock() : "?"); } } diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.lir/src/com/oracle/graal/lir/RedundantMoveElimination.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/RedundantMoveElimination.java Tue Dec 17 21:26:42 2013 -0800 @@ -0,0 +1,462 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.lir; + +import static com.oracle.graal.api.code.ValueUtil.*; + +import java.util.*; + +import com.oracle.graal.api.code.*; +import com.oracle.graal.api.meta.*; +import com.oracle.graal.debug.*; +import com.oracle.graal.lir.StandardOp.MoveOp; +import com.oracle.graal.lir.LIRInstruction.*; +import com.oracle.graal.nodes.cfg.*; + +/** + * Removes move instructions, where the destination value is already in place. + */ +public final class RedundantMoveElimination { + + public static void optimize(LIR lir, FrameMap frameMap, ResolvedJavaMethod method) { + RedundantMoveElimination redundantMoveElimination = new RedundantMoveElimination(); + redundantMoveElimination.doOptimize(lir, frameMap, method); + } + + /** + * Holds the entry and exit states for each block for dataflow analysis. The state is an array + * with an element for each relevant location (register or stack slot). Each element holds the + * global number of the location's definition. A location definition is simply an output of an + * instruction. Note that because instructions can have multiple outputs it is not possible to + * use the instruction id for value numbering. In addition, the result of merging at block + * entries (= phi values) get unique value numbers. + * + * The value numbers also contain information if it is an object kind value or not: if the + * number is negative it is an object kind value. + */ + private static class BlockData { + + BlockData(int stateSize) { + entryState = new int[stateSize]; + exitState = new int[stateSize]; + } + + /* + * The state at block entry for global dataflow analysis. It contains a global value number + * for each location to optimize. + */ + int[] entryState; + + /* + * The state at block exit for global dataflow analysis. It contains a global value number + * for each location to optimize. + */ + int[] exitState; + + /* + * The starting number for global value numbering in this block. + */ + int entryValueNum; + } + + Map blockData = new HashMap<>(); + + Register[] callerSaveRegs; + + Map stackIndices = new HashMap<>(); + + int numRegs; + + /* + * Pseudo value for a not yet assigned location. + */ + static final int INIT_VALUE = 0; + + /** + * The main method doing the elimination of redundant moves. + */ + private void doOptimize(LIR lir, FrameMap frameMap, ResolvedJavaMethod method) { + + try (Indent indent = Debug.logAndIndent(false, "eliminate redundant moves in %s", method)) { + + callerSaveRegs = frameMap.registerConfig.getCallerSaveRegisters(); + + initBlockData(lir); + + solveDataFlow(lir); + + eliminateMoves(lir); + } + } + + private void initBlockData(LIR lir) { + + List blocks = lir.linearScanOrder(); + numRegs = 0; + + /* + * Search for relevant locations which can be optimized. These are register or stack slots + * which occur as destinations of move instructions. + */ + for (Block block : blocks) { + List instructions = lir.lir(block); + for (LIRInstruction op : instructions) { + if (isEligibleMove(op)) { + Value dest = ((MoveOp) op).getResult(); + if (isRegister(dest)) { + int regNum = ((RegisterValue) dest).getRegister().number; + if (regNum >= numRegs) { + numRegs = regNum + 1; + } + } else if (isStackSlot(dest)) { + StackSlot stackSlot = (StackSlot) dest; + if (!stackIndices.containsKey(stackSlot)) { + stackIndices.put(stackSlot, stackIndices.size()); + } + } + } + } + } + + /* + * Now we know the number of locations to optimize, so we can allocate the block states. + */ + int numLocations = numRegs + stackIndices.size(); + Debug.log("num locations = %d (regs = %d, stack = %d)", numLocations, numRegs, stackIndices.size()); + for (Block block : blocks) { + BlockData data = new BlockData(numLocations); + blockData.put(block, data); + } + } + + /** + * Calculates the entry and exit states for all basic blocks. + */ + private void solveDataFlow(LIR lir) { + + Indent indent = Debug.logAndIndent("solve data flow"); + + List blocks = lir.linearScanOrder(); + + /* + * Iterate until there are no more changes. + */ + int currentValueNum = 1; + boolean firstRound = true; + boolean changed; + do { + changed = false; + Indent indent2 = indent.logAndIndent("new iteration"); + + for (Block block : blocks) { + + BlockData data = blockData.get(block); + /* + * Initialize the number for global value numbering for this block. It is essential + * that the starting number for a block is consistent at all iterations and also in + * eliminateMoves(). + */ + if (firstRound) { + data.entryValueNum = currentValueNum; + } + int valueNum = data.entryValueNum; + assert valueNum > 0; + boolean newState = false; + + if (block == blocks.get(0) || block.isExceptionEntry()) { + /* + * The entry block has undefined values. And also exception handler blocks: the + * LinearScan can insert moves at the end of an exception handler predecessor + * block (after the invoke, which throws the exception), and in reality such + * moves are not in the control flow in case of an exception. So we assume a + * save default for exception handler blocks. + */ + indent2.log("kill all values at entry of block %d", block.getId()); + clearValues(data.entryState, valueNum); + } else { + /* + * Merge the states of predecessor blocks + */ + for (Block predecessor : block.getPredecessors()) { + BlockData predData = blockData.get(predecessor); + newState |= mergeState(data.entryState, predData.exitState, valueNum); + } + } + // Advance by the value numbers which are "consumed" by clearValues and mergeState + valueNum += data.entryState.length; + + if (newState || firstRound) { + + Indent indent3 = indent2.logAndIndent("update block %d", block.getId()); + + /* + * Derive the exit state from the entry state by iterating through all + * instructions of the block. + */ + int[] iterState = data.exitState; + copyState(iterState, data.entryState); + List instructions = lir.lir(block); + + for (LIRInstruction op : instructions) { + valueNum = updateState(iterState, op, valueNum); + } + changed = true; + indent3.outdent(); + } + if (firstRound) { + currentValueNum = valueNum; + } + } + firstRound = false; + indent2.outdent(); + + } while (changed); + + indent.outdent(); + } + + /** + * Deletes all move instructions where the target location already contains the source value. + */ + private void eliminateMoves(LIR lir) { + + Indent indent = Debug.logAndIndent("eliminate moves"); + + List blocks = lir.linearScanOrder(); + + for (Block block : blocks) { + + Indent indent2 = indent.logAndIndent("eliminate moves in block %d", block.getId()); + + List instructions = lir.lir(block); + BlockData data = blockData.get(block); + boolean hasDead = false; + + // Reuse the entry state for iteration, we don't need it later. + int[] iterState = data.entryState; + + // Add the values which are "consumed" by clearValues and mergeState in solveDataFlow + int valueNum = data.entryValueNum + data.entryState.length; + + int numInsts = instructions.size(); + for (int idx = 0; idx < numInsts; idx++) { + LIRInstruction op = instructions.get(idx); + if (isEligibleMove(op)) { + MoveOp moveOp = (MoveOp) op; + int sourceIdx = getStateIdx(moveOp.getInput()); + int destIdx = getStateIdx(moveOp.getResult()); + if (sourceIdx >= 0 && destIdx >= 0 && iterState[sourceIdx] == iterState[destIdx]) { + assert iterState[sourceIdx] != INIT_VALUE; + indent2.log("delete move %s", op); + instructions.set(idx, null); + hasDead = true; + } + } + // It doesn't harm if updateState is also called for a deleted move + valueNum = updateState(iterState, op, valueNum); + } + if (hasDead) { + instructions.removeAll(Collections.singleton(null)); + } + indent2.outdent(); + } + indent.outdent(); + } + + /** + * Updates the state for one instruction. + */ + private int updateState(final int[] state, LIRInstruction op, int initValueNum) { + + try (final Indent indent = Debug.logAndIndent("update state for op %s, initial value num = %d", op, initValueNum)) { + if (isEligibleMove(op)) { + /* + * Handle the special case of a move instruction + */ + MoveOp moveOp = (MoveOp) op; + int sourceIdx = getStateIdx(moveOp.getInput()); + int destIdx = getStateIdx(moveOp.getResult()); + if (sourceIdx >= 0 && destIdx >= 0) { + state[destIdx] = state[sourceIdx]; + indent.log("move value %d from %d to %d", state[sourceIdx], sourceIdx, destIdx); + return initValueNum; + } + } + + int valueNum = initValueNum; + + if (op.destroysCallerSavedRegisters()) { + indent.log("kill all caller save regs"); + + for (Register reg : callerSaveRegs) { + if (reg.number < numRegs) { + // Kind.Object is the save default + state[reg.number] = encodeValueNum(valueNum++, true); + } + } + } + + /* + * Value procedure for the instruction's output and temp values + */ + class OutputValueProc extends ValueProcedure { + + int opValueNum; + + OutputValueProc(int opValueNum) { + this.opValueNum = opValueNum; + } + + @Override + public Value doValue(Value operand, OperandMode mode, EnumSet flags) { + int stateIdx = getStateIdx(operand); + if (stateIdx >= 0) { + /* + * Assign a unique number to the output or temp location. + */ + state[stateIdx] = encodeValueNum(opValueNum++, operand.getKind() == Kind.Object); + indent.log("set def %d for register %s(%d): %d", opValueNum, operand, stateIdx, state[stateIdx]); + } + return operand; + } + } + + OutputValueProc outputValueProc = new OutputValueProc(valueNum); + op.forEachOutput(outputValueProc); + op.forEachTemp(outputValueProc); + valueNum = outputValueProc.opValueNum; + + if (op.hasState()) { + /* + * All instructions with framestates (mostly method calls), may do garbage + * collection. GC will rewrite all object references which are live at this point. + * So we can't rely on their values. + */ + indent.log("kill all object values"); + clearValuesOfKindObject(state, valueNum); + valueNum += state.length; + } + + return valueNum; + } + } + + /** + * The state merge function for dataflow joins. + */ + private static boolean mergeState(int[] dest, int[] source, int defNum) { + assert dest.length == source.length; + boolean changed = false; + for (int idx = 0; idx < source.length; idx++) { + int phiNum = defNum + idx; + if (dest[idx] != source[idx] && source[idx] != INIT_VALUE && dest[idx] != encodeValueNum(phiNum, isObjectValue(dest[idx]))) { + if (dest[idx] != INIT_VALUE) { + dest[idx] = encodeValueNum(phiNum, isObjectValue(dest[idx]) || isObjectValue(source[idx])); + } else { + dest[idx] = source[idx]; + } + changed = true; + } + } + return changed; + } + + private static void copyState(int[] dest, int[] source) { + assert dest.length == source.length; + for (int idx = 0; idx < source.length; idx++) { + dest[idx] = source[idx]; + } + } + + private static void clearValues(int[] state, int defNum) { + for (int idx = 0; idx < state.length; idx++) { + int phiNum = defNum + idx; + // Let the killed values assume to be object references: it's the save default. + state[idx] = encodeValueNum(phiNum, true); + } + } + + private static void clearValuesOfKindObject(int[] state, int defNum) { + for (int idx = 0; idx < state.length; idx++) { + int phiNum = defNum + idx; + if (isObjectValue(state[idx])) { + state[idx] = encodeValueNum(phiNum, true); + } + } + } + + /** + * Returns the index to the state arrays in BlockData for a specific location. + */ + private int getStateIdx(Value location) { + if (isRegister(location)) { + int regNum = ((RegisterValue) location).getRegister().number; + if (regNum < numRegs) { + return regNum; + } + } + if (isStackSlot(location)) { + StackSlot slot = (StackSlot) location; + Integer index = stackIndices.get(slot); + if (index != null) { + return index.intValue() + numRegs; + } + } + return -1; + } + + /** + * Encodes a value number + the is-object information to a number to be stored in a state. + */ + private static int encodeValueNum(int valueNum, boolean isObjectKind) { + assert valueNum > 0; + if (isObjectKind) { + return -valueNum; + } + return valueNum; + } + + /** + * Returns true if an encoded value number (which is stored in a state) refers to an object + * reference. + */ + private static boolean isObjectValue(int encodedValueNum) { + return encodedValueNum < 0; + } + + /** + * Returns true for a move instruction which is a candidate for elimination. + */ + private static boolean isEligibleMove(LIRInstruction op) { + if (op instanceof MoveOp) { + MoveOp moveOp = (MoveOp) op; + Value source = moveOp.getInput(); + Value dest = moveOp.getResult(); + /* + * Moves with mismatching kinds are not moves, but memory loads/stores! + */ + return source.getKind() == dest.getKind() && source.getPlatformKind() == dest.getPlatformKind() && source.getKind() != Kind.Illegal; + } + return false; + } +} diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.lir/src/com/oracle/graal/lir/StandardOp.java --- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/StandardOp.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/StandardOp.java Tue Dec 17 21:26:42 2013 -0800 @@ -113,7 +113,7 @@ @Override public void emitCode(CompilationResultBuilder crb) { - if (!destination.isCodeEmittingOrderSuccessorEdge(crb.getCurrentBlockIndex())) { + if (!crb.isSuccessorEdge(destination)) { crb.asm.jmp(destination.label()); } } @@ -174,9 +174,10 @@ } /** - * Placeholder for a LIR instruction that will be subsequently replaced. + * A LIR operation that does nothing. If the operation records its position, it can be + * subsequently {@linkplain #replace(LIR, LIRInstruction) replaced}. */ - public static class PlaceholderOp extends LIRInstruction { + public static class NoOp extends LIRInstruction { /** * The block in which this instruction is located. @@ -188,7 +189,7 @@ */ final int index; - public PlaceholderOp(Block block, int index) { + public NoOp(Block block, int index) { this.block = block; this.index = index; } @@ -199,7 +200,9 @@ @Override public void emitCode(CompilationResultBuilder crb) { - throw new GraalInternalError(this + " should have been replaced"); + if (block != null) { + throw new GraalInternalError(this + " should have been replaced"); + } } } } diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.lir/src/com/oracle/graal/lir/SwitchStrategy.java --- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/SwitchStrategy.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/SwitchStrategy.java Tue Dec 17 21:26:42 2013 -0800 @@ -116,9 +116,9 @@ } public void conditionalJumpOrDefault(int index, Condition condition, boolean canFallThrough) { - if (canFallThrough && defaultTarget.isCodeEmittingOrderSuccessorEdge(crb.getCurrentBlockIndex())) { + if (canFallThrough && crb.isSuccessorEdge(defaultTarget)) { conditionalJump(index, condition, keyTargets[index].label()); - } else if (canFallThrough && keyTargets[index].isCodeEmittingOrderSuccessorEdge(crb.getCurrentBlockIndex())) { + } else if (canFallThrough && crb.isSuccessorEdge(keyTargets[index])) { conditionalJump(index, condition.negate(), defaultTarget.label()); } else { conditionalJump(index, condition, keyTargets[index].label()); diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.lir/src/com/oracle/graal/lir/asm/CompilationResultBuilder.java --- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/asm/CompilationResultBuilder.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/asm/CompilationResultBuilder.java Tue Dec 17 21:26:42 2013 -0800 @@ -32,6 +32,7 @@ import com.oracle.graal.debug.*; import com.oracle.graal.graph.*; import com.oracle.graal.lir.*; +import com.oracle.graal.nodes.cfg.*; /** * Fills in a {@link CompilationResult} as its code is being assembled. @@ -59,7 +60,12 @@ public final FrameMap frameMap; /** - * The index of the block currently being processed in the code emitting block order. + * The LIR for which code is being generated. + */ + private LIR lir; + + /** + * The index of the block currently being emitted. */ private int currentBlockIndex; @@ -285,16 +291,58 @@ } /** - * Gets the index of the block currently being processed in the code emitting block order. + * Determines if a given edge from the block currently being emitted goes to its lexical + * successor. */ - public int getCurrentBlockIndex() { - return currentBlockIndex; + public boolean isSuccessorEdge(LabelRef edge) { + assert lir != null; + List order = lir.codeEmittingOrder(); + assert order.get(currentBlockIndex) == edge.getSourceBlock(); + return currentBlockIndex < order.size() - 1 && order.get(currentBlockIndex + 1) == edge.getTargetBlock(); } /** - * Sets the index of the block currently being processed in the code emitting block order. + * Emits code for {@code lir} in its {@linkplain LIR#codeEmittingOrder() code emitting order}. */ - public void setCurrentBlockIndex(int index) { - this.currentBlockIndex = index; + public void emit(@SuppressWarnings("hiding") LIR lir) { + assert this.lir == null; + assert currentBlockIndex == 0; + this.lir = lir; + this.currentBlockIndex = 0; + frameContext.enter(this); + for (Block b : lir.codeEmittingOrder()) { + emitBlock(b); + currentBlockIndex++; + } + this.lir = null; + this.currentBlockIndex = 0; + } + + private void emitBlock(Block block) { + if (Debug.isDumpEnabled()) { + blockComment(String.format("block B%d %s", block.getId(), block.getLoop())); + } + + for (LIRInstruction op : lir.lir(block)) { + if (Debug.isDumpEnabled()) { + blockComment(String.format("%d %s", op.id(), op)); + } + + try { + emitOp(this, op); + } catch (GraalInternalError e) { + throw e.addContext("lir instruction", block + "@" + op.id() + " " + op + "\n" + lir.codeEmittingOrder()); + } + } + } + + private static void emitOp(CompilationResultBuilder crb, LIRInstruction op) { + try { + op.emitCode(crb); + } catch (AssertionError t) { + throw new GraalInternalError(t); + } catch (RuntimeException t) { + throw new GraalInternalError(t); + } } } diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.loop/src/com/oracle/graal/loop/LoopEx.java --- a/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/LoopEx.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/LoopEx.java Tue Dec 17 21:26:42 2013 -0800 @@ -84,7 +84,7 @@ } public boolean isOutsideLoop(Node n) { - return n.isExternal() || !whole().contains(n); + return !whole().contains(n); } public LoopBeginNode loopBegin() { diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.loop/src/com/oracle/graal/loop/LoopFragment.java --- a/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/LoopFragment.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/LoopFragment.java Tue Dec 17 21:26:42 2013 -0800 @@ -71,10 +71,7 @@ @SuppressWarnings("unchecked") public New getDuplicatedNode(Old n) { assert isDuplicate(); - if (!n.isExternal()) { - return (New) duplicationMap.get(n); - } - return n; + return (New) duplicationMap.get(n); } protected void putDuplicatedNode(Old oldNode, New newNode) { diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ConstantNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ConstantNode.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ConstantNode.java Tue Dec 17 21:26:42 2013 -0800 @@ -22,8 +22,6 @@ */ package com.oracle.graal.nodes; -import static com.oracle.graal.graph.Graph.*; - import java.util.*; import com.oracle.graal.api.meta.*; @@ -68,40 +66,17 @@ return value; } - private static boolean ConstantNodesAreExternal = Boolean.parseBoolean(System.getProperty("graal.constantNodesAreExternal", "true")); - /** * Used to measure the impact of ConstantNodes not recording their usages. This and all code * predicated on this value being true will be removed at some point. */ - public static final boolean ConstantNodeRecordsUsages = !ConstantNodesAreExternal && Boolean.getBoolean("graal.constantNodeRecordsUsages"); + public static final boolean ConstantNodeRecordsUsages = Boolean.getBoolean("graal.constantNodeRecordsUsages"); @Override public boolean recordsUsages() { return ConstantNodeRecordsUsages; } - @Override - public boolean isDeleted() { - if (!ConstantNodesAreExternal) { - return super.isDeleted(); - } - return false; - } - - @Override - public boolean isAlive() { - if (!ConstantNodesAreExternal) { - return super.isAlive(); - } - return true; - } - - @Override - public boolean isExternal() { - return ConstantNodesAreExternal; - } - /** * Computes the usages of this node by iterating over all the nodes in the graph, searching for * those that have this node as an input. @@ -121,23 +96,10 @@ /** * Gathers all the {@link ConstantNode}s that are inputs to the - * {@linkplain StructuredGraph#getNodes() live nodes} in a given graph. This is an expensive - * operation that should only be used in test/verification/AOT code. + * {@linkplain StructuredGraph#getNodes() live nodes} in a given graph. */ public static NodeIterable getConstantNodes(StructuredGraph graph) { - if (!ConstantNodesAreExternal) { - return graph.getNodes().filter(ConstantNode.class); - } - - Map result = new HashMap<>(); - for (Node node : graph.getNodes()) { - for (Node input : node.inputs()) { - if (input instanceof ConstantNode) { - result.put((ConstantNode) input, (ConstantNode) input); - } - } - } - return new ConstantNodeList(result.keySet()); + return graph.getNodes().filter(ConstantNode.class); } /** @@ -150,9 +112,7 @@ for (Node usage : usages) { usage.replaceFirstInput(this, replacement); } - if (!isExternal()) { - graph.removeFloating(this); - } + graph.removeFloating(this); } else { assert graph == graph(); graph().replaceFloating(this, replacement); @@ -184,10 +144,6 @@ if (constant.getKind().getStackKind() == Kind.Int && constant.getKind() != Kind.Int) { return forInt(constant.asInt(), graph); } - if (ConstantNodesAreExternal && !CacheExternalNodesInGraph) { - Stamp stamp = constant.getKind() == Kind.Object ? StampFactory.forConstant(constant, metaAccess) : StampFactory.forConstant(constant); - return graph.asConstantNode(constant, stamp); - } if (constant.getKind() == Kind.Object) { return unique(graph, new ConstantNode(constant, StampFactory.forConstant(constant, metaAccess))); } else { @@ -210,9 +166,6 @@ * @return a node for a double constant */ public static ConstantNode forDouble(double d, StructuredGraph graph) { - if (ConstantNodesAreExternal && !CacheExternalNodesInGraph) { - return graph.asConstantNode(Constant.forDouble(d), null); - } return unique(graph, createPrimitive(Constant.forDouble(d))); } @@ -223,9 +176,6 @@ * @return a node for a float constant */ public static ConstantNode forFloat(float f, StructuredGraph graph) { - if (ConstantNodesAreExternal && !CacheExternalNodesInGraph) { - return graph.asConstantNode(Constant.forFloat(f), null); - } return unique(graph, createPrimitive(Constant.forFloat(f))); } @@ -236,9 +186,6 @@ * @return a node for an long constant */ public static ConstantNode forLong(long i, StructuredGraph graph) { - if (ConstantNodesAreExternal && !CacheExternalNodesInGraph) { - return graph.asConstantNode(Constant.forLong(i), null); - } return unique(graph, createPrimitive(Constant.forLong(i))); } @@ -249,9 +196,6 @@ * @return a node for an integer constant */ public static ConstantNode forInt(int i, StructuredGraph graph) { - if (ConstantNodesAreExternal && !CacheExternalNodesInGraph) { - return graph.asConstantNode(Constant.forInt(i), null); - } return unique(graph, createPrimitive(Constant.forInt(i))); } @@ -262,9 +206,6 @@ * @return a node representing the boolean */ public static ConstantNode forBoolean(boolean i, StructuredGraph graph) { - if (ConstantNodesAreExternal && !CacheExternalNodesInGraph) { - return graph.asConstantNode(i ? Constant.INT_1 : Constant.INT_0, null); - } return unique(graph, createPrimitive(Constant.forInt(i ? 1 : 0))); } @@ -275,9 +216,6 @@ * @return a node representing the byte */ public static ConstantNode forByte(byte i, StructuredGraph graph) { - if (ConstantNodesAreExternal && !CacheExternalNodesInGraph) { - return graph.asConstantNode(Constant.forInt(i), null); - } return unique(graph, createPrimitive(Constant.forInt(i))); } @@ -288,9 +226,6 @@ * @return a node representing the char */ public static ConstantNode forChar(char i, StructuredGraph graph) { - if (ConstantNodesAreExternal && !CacheExternalNodesInGraph) { - return graph.asConstantNode(Constant.forInt(i), null); - } return unique(graph, createPrimitive(Constant.forInt(i))); } @@ -301,9 +236,6 @@ * @return a node representing the short */ public static ConstantNode forShort(short i, StructuredGraph graph) { - if (ConstantNodesAreExternal && !CacheExternalNodesInGraph) { - return graph.asConstantNode(Constant.forInt(i), null); - } return unique(graph, createPrimitive(Constant.forInt(i))); } @@ -316,18 +248,11 @@ public static ConstantNode forObject(Object o, MetaAccessProvider metaAccess, StructuredGraph graph) { assert !(o instanceof Constant) : "wrapping a Constant into a Constant"; Constant constant = Constant.forObject(o); - if (ConstantNodesAreExternal && !CacheExternalNodesInGraph) { - return graph.asConstantNode(constant, StampFactory.forConstant(constant, metaAccess)); - } return unique(graph, new ConstantNode(constant, StampFactory.forConstant(constant, metaAccess))); } private static ConstantNode unique(StructuredGraph graph, ConstantNode node) { - if (!ConstantNodesAreExternal) { - return graph.unique(node); - } - assert CacheExternalNodesInGraph; - return graph.uniqueExternal(node); + return graph.unique(node); } public static ConstantNode forIntegerKind(Kind kind, long value, StructuredGraph graph) { @@ -390,15 +315,4 @@ return super.toString(verbosity); } } - - static class ConstantNodeList extends NodeList { - - public ConstantNodeList(Collection nodes) { - super(nodes.toArray(new ConstantNode[nodes.size()])); - } - - @Override - protected void update(ConstantNode oldNode, ConstantNode newNode) { - } - } } diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/StateSplit.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/StateSplit.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/StateSplit.java Tue Dec 17 21:26:42 2013 -0800 @@ -42,9 +42,9 @@ void setStateAfter(FrameState x); /** - * Determines if this node has a side-effect. Such nodes can not be safely re-executed because - * they modified state which is visible to other thread or modified state beyond what is - * captured in {@link FrameState} nodes. + * Determines if this node has a side-effect. Such nodes cannot be safely re-executed because + * they modify state which is visible to other threads or modify state beyond what is captured + * in {@link FrameState} nodes. */ boolean hasSideEffect(); } diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/StructuredGraph.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/StructuredGraph.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/StructuredGraph.java Tue Dec 17 21:26:42 2013 -0800 @@ -29,7 +29,6 @@ import com.oracle.graal.graph.*; import com.oracle.graal.nodes.calc.*; import com.oracle.graal.nodes.java.*; -import com.oracle.graal.nodes.type.*; import com.oracle.graal.nodes.util.*; /** @@ -82,38 +81,6 @@ private boolean isAfterFloatingReadPhase = false; /** - * Used to create canonical {@link ConstantNode}s for {@link Constant}s in this graph. - */ - private Map constants; - - /** - * Gets a node for a given constant that is unique/canonical within this graph. - * - * @param stamp the stamp for an {@link Kind#Object} constant (ignored otherwise) - */ - public ConstantNode asConstantNode(Constant constant, Stamp stamp) { - ConstantNode node; - if (constants == null) { - constants = new HashMap<>(); - node = null; - } else { - node = constants.get(constant); - } - if (node == null) { - node = new ConstantNode(constant, stamp == null ? StampFactory.forConstant(constant) : stamp); - constants.put(constant, node); - } - return node; - } - - @SuppressWarnings("unchecked") - @Override - public T uniqueExternal(T node) { - ConstantNode cn = (ConstantNode) node; - return (T) asConstantNode(cn.asConstant(), cn.stamp()); - } - - /** * Creates a new Graph containing a single {@link AbstractBeginNode} as the {@link #start() * start} node. */ diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/AbstractNewArrayNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/AbstractNewArrayNode.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/AbstractNewArrayNode.java Tue Dec 17 21:26:42 2013 -0800 @@ -22,7 +22,6 @@ */ package com.oracle.graal.nodes.java; -import com.oracle.graal.graph.*; import com.oracle.graal.graph.spi.*; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.spi.*; @@ -31,10 +30,9 @@ /** * The {@code AbstractNewArrayNode} is used for all 1-dimensional array allocations. */ -public class AbstractNewArrayNode extends DeoptimizingFixedWithNextNode implements Canonicalizable, Lowerable, ArrayLengthProvider { +public class AbstractNewArrayNode extends AbstractNewObjectNode implements ArrayLengthProvider { @Input private ValueNode length; - private final boolean fillContents; @Override public ValueNode length() { @@ -49,16 +47,8 @@ * @param fillContents determines whether the array elements should be initialized to zero/null. */ protected AbstractNewArrayNode(Stamp stamp, ValueNode length, boolean fillContents) { - super(stamp); + super(stamp, fillContents); this.length = length; - this.fillContents = fillContents; - } - - /** - * @return true if the elements of the array will be initialized. - */ - public boolean fillContents() { - return fillContents; } /** @@ -77,23 +67,11 @@ } @Override - public Node canonical(CanonicalizerTool tool) { - if (usages().isEmpty()) { - Stamp stamp = length.stamp(); - if (stamp instanceof IntegerStamp && ((IntegerStamp) stamp).isPositive()) { - return null; - } + public void simplify(SimplifierTool tool) { + Stamp stamp = length.stamp(); + if (stamp instanceof IntegerStamp && ((IntegerStamp) stamp).isPositive()) { + // otherwise, removing the allocation might swallow a NegativeArraySizeException + super.simplify(tool); } - return this; - } - - @Override - public void lower(LoweringTool tool) { - tool.getLowerer().lower(this, tool); - } - - @Override - public boolean canDeoptimize() { - return true; } } diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/AbstractNewObjectNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/AbstractNewObjectNode.java Tue Dec 17 21:26:42 2013 -0800 @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.nodes.java; + +import java.util.*; + +import com.oracle.graal.graph.*; +import com.oracle.graal.graph.spi.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.extended.*; +import com.oracle.graal.nodes.spi.*; +import com.oracle.graal.nodes.type.*; + +/** + * The {@code AbstractNewObjectNode} is the base class for the new instance and new array nodes. + */ +public class AbstractNewObjectNode extends DeoptimizingFixedWithNextNode implements Simplifiable, Lowerable { + + private final boolean fillContents; + + /** + * Constructs a new AbstractNewObjectNode. + * + * @param stamp the stamp of the newly created object + * @param fillContents determines if the object's contents should be initialized to zero/null. + */ + protected AbstractNewObjectNode(Stamp stamp, boolean fillContents) { + super(stamp); + this.fillContents = fillContents; + } + + /** + * @return true if the object's contents should be initialized to zero/null. + */ + public boolean fillContents() { + return fillContents; + } + + @Override + public void simplify(SimplifierTool tool) { + // poor man's escape analysis: check if the object can be trivially removed + for (Node usage : usages()) { + if (usage instanceof FixedValueAnchorNode) { + if (((FixedValueAnchorNode) usage).usages().isNotEmpty()) { + return; + } + } else if (usage instanceof WriteNode) { + if (((WriteNode) usage).object() != this || usage.usages().isNotEmpty()) { + // we would need to fix up the memory graph if the write has usages + return; + } + } else { + return; + } + } + for (Node usage : usages().snapshot()) { + List snapshot = usage.inputs().snapshot(); + graph().removeFixed((FixedWithNextNode) usage); + for (Node input : snapshot) { + tool.removeIfUnused(input); + } + } + List snapshot = inputs().snapshot(); + graph().removeFixed(this); + for (Node input : snapshot) { + tool.removeIfUnused(input); + } + } + + @Override + public void lower(LoweringTool tool) { + tool.getLowerer().lower(this, tool); + } + + @Override + public boolean canDeoptimize() { + return true; + } +} diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/CompareAndSwapNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/CompareAndSwapNode.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/CompareAndSwapNode.java Tue Dec 17 21:26:42 2013 -0800 @@ -25,6 +25,8 @@ import static com.oracle.graal.graph.UnsafeAccess.*; import com.oracle.graal.api.meta.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.graph.spi.*; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.extended.*; import com.oracle.graal.nodes.spi.*; @@ -34,13 +36,14 @@ * Represents an atomic compare-and-swap operation The result is a boolean that contains whether the * value matched the expected value. */ -public class CompareAndSwapNode extends AbstractMemoryCheckpoint implements Lowerable, MemoryCheckpoint.Single { +public class CompareAndSwapNode extends AbstractMemoryCheckpoint implements Lowerable, MemoryCheckpoint.Single, Canonicalizable { @Input private ValueNode object; @Input private ValueNode offset; @Input private ValueNode expected; @Input private ValueNode newValue; private final int displacement; + private final LocationIdentity locationIdentity; public ValueNode object() { return object; @@ -63,6 +66,10 @@ } public CompareAndSwapNode(ValueNode object, int displacement, ValueNode offset, ValueNode expected, ValueNode newValue) { + this(object, displacement, offset, expected, newValue, LocationIdentity.ANY_LOCATION); + } + + public CompareAndSwapNode(ValueNode object, int displacement, ValueNode offset, ValueNode expected, ValueNode newValue, LocationIdentity locationIdentity) { super(StampFactory.forKind(Kind.Boolean.getStackKind())); assert expected.kind() == newValue.kind(); this.object = object; @@ -70,11 +77,12 @@ this.expected = expected; this.newValue = newValue; this.displacement = displacement; + this.locationIdentity = locationIdentity; } @Override public LocationIdentity getLocationIdentity() { - return LocationIdentity.ANY_LOCATION; + return locationIdentity; } @Override @@ -82,6 +90,24 @@ tool.getLowerer().lower(this, tool); } + @Override + public Node canonical(CanonicalizerTool tool) { + if (getLocationIdentity() == LocationIdentity.ANY_LOCATION) { + Constant offsetConstant = offset().asConstant(); + if (offsetConstant != null) { + ResolvedJavaType receiverType = ObjectStamp.typeOrNull(object()); + if (receiverType != null) { + long constantOffset = offsetConstant.asLong(); + ResolvedJavaField field = receiverType.findInstanceFieldWithOffset(constantOffset); + if (field != null && expected().kind() == field.getKind() && newValue().kind() == field.getKind()) { + return graph().add(new CompareAndSwapNode(object, displacement, offset, expected, newValue, field)); + } + } + } + } + return this; + } + // specialized on value type until boxing/unboxing is sorted out in intrinsification @NodeIntrinsic public static boolean compareAndSwap(Object object, @ConstantNodeParameter int displacement, long offset, Object expected, Object newValue) { diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/DynamicNewArrayNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/DynamicNewArrayNode.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/DynamicNewArrayNode.java Tue Dec 17 21:26:42 2013 -0800 @@ -23,6 +23,7 @@ package com.oracle.graal.nodes.java; import java.lang.reflect.*; +import java.util.*; import com.oracle.graal.api.meta.*; import com.oracle.graal.graph.*; @@ -34,7 +35,7 @@ * The {@code DynamicNewArrayNode} is used for allocation of arrays when the type is not a * compile-time constant. */ -public class DynamicNewArrayNode extends AbstractNewArrayNode implements Canonicalizable { +public class DynamicNewArrayNode extends AbstractNewArrayNode { @Input private ValueNode elementType; @@ -52,15 +53,20 @@ } @Override - public Node canonical(CanonicalizerTool tool) { - if (elementType.isConstant()) { + public void simplify(SimplifierTool tool) { + if (isAlive() && elementType.isConstant()) { Class elementClass = (Class) elementType.asConstant().asObject(); if (elementClass != null && !(elementClass.equals(void.class))) { ResolvedJavaType javaType = tool.getMetaAccess().lookupJavaType(elementClass); - return graph().add(new NewArrayNode(javaType, length(), fillContents())); + NewArrayNode newArray = graph().add(new NewArrayNode(javaType, length(), fillContents())); + List snapshot = inputs().snapshot(); + graph().replaceFixedWithFixed(this, newArray); + for (Node input : snapshot) { + tool.removeIfUnused(input); + } + tool.addToWorkList(newArray); } } - return this; } @NodeIntrinsic diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/NewInstanceNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/NewInstanceNode.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/NewInstanceNode.java Tue Dec 17 21:26:42 2013 -0800 @@ -26,7 +26,6 @@ import com.oracle.graal.api.meta.*; import com.oracle.graal.graph.*; -import com.oracle.graal.graph.spi.*; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.spi.*; import com.oracle.graal.nodes.type.*; @@ -36,10 +35,9 @@ * The {@code NewInstanceNode} represents the allocation of an instance class object. */ @NodeInfo(nameTemplate = "New {p#instanceClass/s}") -public class NewInstanceNode extends DeoptimizingFixedWithNextNode implements Canonicalizable, Lowerable, VirtualizableAllocation { +public class NewInstanceNode extends AbstractNewObjectNode implements VirtualizableAllocation { private final ResolvedJavaType instanceClass; - private final boolean fillContents; /** * Constructs a NewInstanceNode. @@ -49,10 +47,9 @@ * zero/null. */ public NewInstanceNode(ResolvedJavaType type, boolean fillContents) { - super(StampFactory.exactNonNull(type)); + super(StampFactory.exactNonNull(type), fillContents); assert !type.isArray(); this.instanceClass = type; - this.fillContents = fillContents; } /** @@ -64,27 +61,6 @@ return instanceClass; } - /** - * @return true if the fields of the new object will be initialized. - */ - public boolean fillContents() { - return fillContents; - } - - @Override - public Node canonical(CanonicalizerTool tool) { - if (usages().isEmpty()) { - return null; - } else { - return this; - } - } - - @Override - public void lower(LoweringTool tool) { - tool.getLowerer().lower(this, tool); - } - @Override public void virtualize(VirtualizerTool tool) { /* @@ -107,9 +83,4 @@ protected ConstantNode defaultFieldValue(ResolvedJavaField field) { return ConstantNode.defaultForKind(field.getType().getKind(), graph()); } - - @Override - public boolean canDeoptimize() { - return true; - } } diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/LIRGeneratorTool.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/LIRGeneratorTool.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/LIRGeneratorTool.java Tue Dec 17 21:26:42 2013 -0800 @@ -102,6 +102,7 @@ /** * Called just before register allocation is performed on the LIR owned by this generator. + * Overriding implementations of this method must call the overridden method. */ void beforeRegisterAllocation(); diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/NodeWithState.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/NodeWithState.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/NodeWithState.java Tue Dec 17 21:26:42 2013 -0800 @@ -29,13 +29,11 @@ /** * Interface for nodes which have {@link FrameState} nodes as input. *

- * Some node can declare more than one interface which requires a {@link FrameState} input (e.g. + * Some node can implement more than one interface which requires a {@link FrameState} input (e.g. * {@link DeoptimizingNode} and {@link StateSplit}). Since this interface can only report one - * {@link FrameState}, such nodes must ensure they only maintain a link to at most one - * {@link FrameState} at all times. Usually this is not a problem because {@link FrameState} are - * associated only with {@link StateSplit} nodes before the {@link #AFTER_FSA} stage and only with - * {@link DeoptimizingNode} after. - * + * FrameState, such nodes must ensure they only maintain a link to at most one FrameState at all + * times. Usually this is not a problem because FrameStates are associated only with StateSplit + * nodes before the {@link #AFTER_FSA} stage and only with DeoptimizingNodes after. * */ public interface NodeWithState { diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/util/GraphUtil.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/util/GraphUtil.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/util/GraphUtil.java Tue Dec 17 21:26:42 2013 -0800 @@ -123,7 +123,7 @@ } public static void killWithUnusedFloatingInputs(Node node) { - if (node.recordsUsages() && !node.isExternal()) { + if (node.recordsUsages()) { List floatingInputs = node.inputs().filter(isFloatingNode()).snapshot(); node.safeDelete(); @@ -354,14 +354,14 @@ * Process a node as part of this search. * * @param node the next node encountered in the search - * @param worklist if non-null and {@code node} is not external, {@code node} will be added - * to this list. Otherwise, {@code node} is treated as a candidate result. + * @param worklist if non-null, {@code node} will be added to this list. Otherwise, + * {@code node} is treated as a candidate result. * @return true if the search should continue, false if a definitive {@link #result} has * been found */ private boolean process(ValueNode node, NodeWorkList worklist) { if (node.isAlive()) { - if (node.isExternal() || worklist == null) { + if (worklist == null) { if (result == null) { // Initial candidate result: continue search result = node; diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/DeadCodeEliminationPhase.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/DeadCodeEliminationPhase.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/DeadCodeEliminationPhase.java Tue Dec 17 21:26:42 2013 -0800 @@ -112,17 +112,13 @@ for (Node node : graph.getNodes()) { if (flood.isMarked(node)) { for (Node input : node.inputs()) { - if (!input.isExternal()) { - flood.add(input); - } + flood.add(input); } } } for (Node current : flood) { for (Node input : current.inputs()) { - if (!input.isExternal()) { - flood.add(input); - } + flood.add(input); } } } diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/GuardLoweringPhase.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/GuardLoweringPhase.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/GuardLoweringPhase.java Tue Dec 17 21:26:42 2013 -0800 @@ -27,6 +27,7 @@ import java.util.*; import java.util.Map.Entry; +import com.oracle.graal.debug.*; import com.oracle.graal.graph.*; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.StructuredGraph.GuardsStage; @@ -34,7 +35,6 @@ import com.oracle.graal.nodes.cfg.*; import com.oracle.graal.nodes.extended.*; import com.oracle.graal.nodes.util.*; -import com.oracle.graal.options.*; import com.oracle.graal.phases.*; import com.oracle.graal.phases.graph.*; import com.oracle.graal.phases.schedule.*; @@ -54,13 +54,6 @@ * does the actual control-flow expansion of the remaining {@link GuardNode GuardNodes}. */ public class GuardLoweringPhase extends BasePhase { - static class Options { - //@formatter:off - @Option(help = "") - public static final OptionValue UseGuardIdAsSpeculationId = new OptionValue<>(false); - //@formatter:on - } - private static class UseImplicitNullChecks extends ScheduledNodeIterator { private final IdentityHashMap nullGuarded = new IdentityHashMap<>(); @@ -134,9 +127,9 @@ private final Block block; private boolean useGuardIdAsSpeculationId; - public LowerGuards(Block block) { + public LowerGuards(Block block, boolean useGuardIdAsSpeculationId) { this.block = block; - this.useGuardIdAsSpeculationId = Options.UseGuardIdAsSpeculationId.getValue(); + this.useGuardIdAsSpeculationId = useGuardIdAsSpeculationId; } @Override @@ -203,6 +196,6 @@ if (OptImplicitNullChecks.getValue() && implicitNullCheckLimit > 0) { new UseImplicitNullChecks(implicitNullCheckLimit).processNodes(block, schedule); } - new LowerGuards(block).processNodes(block, schedule); + new LowerGuards(block, Debug.isDumpEnabledForMethod() || Debug.isLogEnabledForMethod()).processNodes(block, schedule); } } diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/IncrementalCanonicalizerPhase.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/IncrementalCanonicalizerPhase.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/IncrementalCanonicalizerPhase.java Tue Dec 17 21:26:42 2013 -0800 @@ -22,13 +22,10 @@ */ package com.oracle.graal.phases.common; -import java.util.*; - import com.oracle.graal.graph.Graph.Mark; -import com.oracle.graal.graph.Graph.NodeChangedListener; -import com.oracle.graal.graph.*; import com.oracle.graal.nodes.*; import com.oracle.graal.phases.*; +import com.oracle.graal.phases.common.util.*; import com.oracle.graal.phases.tiers.*; /** @@ -46,15 +43,8 @@ @Override protected void run(StructuredGraph graph, C context) { Mark newNodesMark = graph.getMark(); - final Set changedNodes = new HashSet<>(); - NodeChangedListener listener = new NodeChangedListener() { - - @Override - public void nodeChanged(Node node) { - changedNodes.add(node); - } - }; + HashSetNodeChangeListener listener = new HashSetNodeChangeListener(); graph.trackInputChange(listener); graph.trackUsagesDroppedZero(listener); @@ -63,8 +53,8 @@ graph.stopTrackingInputChange(); graph.stopTrackingUsagesDroppedZero(); - if (graph.getMark() != newNodesMark || !changedNodes.isEmpty()) { - canonicalizer.applyIncremental(graph, context, changedNodes, newNodesMark, false); + if (graph.getMark() != newNodesMark || !listener.getChangedNodes().isEmpty()) { + canonicalizer.applyIncremental(graph, context, listener.getChangedNodes(), newNodesMark, false); } } } diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningUtil.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningUtil.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningUtil.java Tue Dec 17 21:26:42 2013 -0800 @@ -40,7 +40,6 @@ import com.oracle.graal.debug.Debug.Scope; import com.oracle.graal.graph.*; import com.oracle.graal.graph.Graph.DuplicationReplacement; -import com.oracle.graal.graph.Node.ValueNumberable; import com.oracle.graal.graph.Node.Verbosity; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.calc.*; @@ -1433,12 +1432,7 @@ if (returnNode.result() instanceof LocalNode) { returnValue = localReplacement.replacement(returnNode.result()); } else if (returnNode.result() != null) { - returnValue = returnNode.result(); - if (!returnValue.isExternal()) { - returnValue = duplicates.get(returnValue); - } else if (returnValue instanceof ValueNumberable) { - returnValue = graph.uniqueExternal(returnValue); - } + returnValue = duplicates.get(returnNode.result()); } invoke.asNode().replaceAtUsages(returnValue); Node returnDuplicate = duplicates.get(returnNode); diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/LoweringPhase.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/LoweringPhase.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/LoweringPhase.java Tue Dec 17 21:26:42 2013 -0800 @@ -324,7 +324,7 @@ assert (unscheduledUsages = getUnscheduledUsages(node)) != null; Mark preLoweringMark = node.graph().getMark(); ((Lowerable) node).lower(loweringTool); - if (node == startAnchor && node.isDeleted()) { + if (loweringTool.guardAnchor.asNode().isDeleted()) { loweringTool.guardAnchor = BeginNode.prevBegin(nextNode); } assert checkPostNodeLowering(node, loweringTool, preLoweringMark, unscheduledUsages); diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/TailDuplicationPhase.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/TailDuplicationPhase.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/TailDuplicationPhase.java Tue Dec 17 21:26:42 2013 -0800 @@ -87,17 +87,22 @@ if (fixedNodeCount < TailDuplicationTrivialSize.getValue()) { return true; } - HashSet improvements = new HashSet<>(); + ArrayList improvements = null; for (PhiNode phi : merge.phis()) { Stamp phiStamp = phi.stamp(); for (ValueNode input : phi.values()) { if (!input.stamp().equals(phiStamp)) { - improvements.add(phi); + if (improvements == null) { + improvements = new ArrayList<>(); + } + if (!improvements.contains(phi)) { + improvements.add(phi); + } break; } } } - if (improvements.isEmpty()) { + if (improvements == null) { return false; } FixedNode current = merge; @@ -365,7 +370,7 @@ // stop iterating: fixed nodes within the given set are traversal roots // anyway, and all other // fixed nodes are known to be outside. - } else if (!node.isExternal() && !aboveBound.isMarked(node)) { + } else if (!aboveBound.isMarked(node)) { worklist.add(node); aboveBound.mark(node); } @@ -378,9 +383,7 @@ while (!worklist.isEmpty()) { Node current = worklist.remove(); for (Node input : current.inputs()) { - if (!input.isExternal()) { - aboveClosure.apply(current, input); - } + aboveClosure.apply(current, input); } } diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/GraalOptions.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/GraalOptions.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/GraalOptions.java Tue Dec 17 21:26:42 2013 -0800 @@ -87,14 +87,6 @@ @Option(help = "") public static final OptionValue DeoptsToDisableOptimisticOptimization = new OptionValue<>(40); - // compilation queue - @Option(help = "Compile all methods in all classes on given class path") - public static final OptionValue CompileTheWorld = new OptionValue<>(null); - @Option(help = "First class to consider when using CompileTheWorld") - public static final OptionValue CompileTheWorldStartAt = new OptionValue<>(1); - @Option(help = "Last class to consider when using CompileTheWorld") - public static final OptionValue CompileTheWorldStopAt = new OptionValue<>(Integer.MAX_VALUE); - // graph caching @Option(help = "") public static final OptionValue CacheGraphs = new OptionValue<>(true); @@ -226,9 +218,7 @@ public static final OptionValue CanOmitFrame = new OptionValue<>(true); @Option(help = "") - public static final OptionValue MemoryAwareScheduling = new OptionValue<>(false); - @Option(help = "") - public static final OptionValue NewMemoryAwareScheduling = new OptionValue<>(true); + public static final OptionValue MemoryAwareScheduling = new OptionValue<>(true); // Translating tableswitch instructions @Option(help = "") diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/graph/ComputeProbabilityClosure.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/graph/ComputeProbabilityClosure.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/graph/ComputeProbabilityClosure.java Tue Dec 17 21:26:42 2013 -0800 @@ -28,6 +28,7 @@ import com.oracle.graal.graph.*; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.util.*; +import com.oracle.graal.phases.util.*; /** * Computes probabilities for nodes in a graph. @@ -58,7 +59,7 @@ public ComputeProbabilityClosure(StructuredGraph graph) { this.graph = graph; this.nodeProbabilities = new NodesToDoubles(graph.getNodeCount()); - this.loopInfos = new HashSet<>(); + this.loopInfos = new ArraySet<>(); this.mergeLoops = new IdentityHashMap<>(); } @@ -75,7 +76,7 @@ * Assume that paths with a DeoptimizeNode at their end are taken infrequently. */ private void adjustControlSplitProbabilities() { - HashSet result = new HashSet<>(); + Set result = new ArraySet<>(); NodeBitMap visitedNodes = new NodeBitMap(graph); for (AbstractDeoptimizeNode n : graph.getNodes(AbstractDeoptimizeNode.class)) { if (!(n instanceof DeoptimizeNode) || ((DeoptimizeNode) n).action().doesInvalidateCompilation()) { @@ -90,7 +91,7 @@ } } - private static void findParentControlSplitNodes(HashSet result, AbstractDeoptimizeNode n, NodeBitMap visitedNodes) { + private static void findParentControlSplitNodes(Set result, AbstractDeoptimizeNode n, NodeBitMap visitedNodes) { ArrayDeque nodes = new ArrayDeque<>(); nodes.push(n); @@ -221,13 +222,13 @@ private class Probability extends MergeableState { public double probability; - public HashSet loops; + public Set loops; public LoopInfo loopInfo; - public Probability(double probability, HashSet loops) { + public Probability(double probability, Set loops) { assert probability >= 0.0; this.probability = probability; - this.loops = new HashSet<>(4); + this.loops = new ArraySet<>(4); if (loops != null) { this.loops.addAll(loops); } @@ -241,7 +242,7 @@ @Override public boolean merge(MergeNode merge, List withStates) { if (merge.forwardEndCount() > 1) { - HashSet intersection = new HashSet<>(loops); + Set intersection = new ArraySet<>(loops); for (Probability other : withStates) { intersection.retainAll(other.loops); } @@ -271,7 +272,7 @@ assert probability >= 0; } loops = intersection; - mergeLoops.put(merge, new HashSet<>(intersection)); + mergeLoops.put(merge, new ArraySet<>(intersection)); probability = Math.max(0.0, probability); } return true; diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/schedule/SchedulePhase.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/schedule/SchedulePhase.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/schedule/SchedulePhase.java Tue Dec 17 21:26:42 2013 -0800 @@ -33,7 +33,6 @@ import com.oracle.graal.graph.*; import com.oracle.graal.graph.Node.Verbosity; import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.calc.*; import com.oracle.graal.nodes.cfg.*; import com.oracle.graal.nodes.extended.*; import com.oracle.graal.nodes.spi.*; @@ -42,6 +41,7 @@ import com.oracle.graal.phases.graph.*; import com.oracle.graal.phases.graph.ReentrantBlockIterator.BlockIteratorClosure; import com.oracle.graal.phases.graph.ReentrantBlockIterator.LoopInfo; +import com.oracle.graal.phases.util.*; public final class SchedulePhase extends Phase { @@ -74,111 +74,14 @@ } public static enum MemoryScheduling { - NONE, CONSERVATIVE, OPTIMAL - } - - /** - * This closure iterates over all nodes of a scheduled graph (it expects a - * {@link SchedulingStrategy#EARLIEST} schedule) and keeps a list of "active" reads. Whenever it - * encounters a read, it adds it to the active reads. Whenever it encounters a memory - * checkpoint, it adds all reads that need to be committed before this checkpoint to the - * "phantom" usages and inputs, so that the read is scheduled before the checkpoint afterwards. - * - * At merges, the intersection of all sets of active reads is calculated. A read that was - * committed within one predecessor branch cannot be scheduled after the merge anyway. - * - * Similarly for loops, all reads that are killed somewhere within the loop are removed from the - * exits' active reads, since they cannot be scheduled after the exit anyway. - */ - private class MemoryScheduleClosure extends BlockIteratorClosure> { - - @Override - protected HashSet getInitialState() { - return new HashSet<>(); - } - - @Override - protected HashSet processBlock(Block block, HashSet currentState) { - for (Node node : blockToNodesMap.get(block)) { - if (node instanceof FloatingReadNode) { - currentState.add((FloatingReadNode) node); - } else if (node instanceof MemoryCheckpoint.Single) { - LocationIdentity identity = ((MemoryCheckpoint.Single) node).getLocationIdentity(); - processIdentity(currentState, (FixedNode) node, identity); - } else if (node instanceof MemoryCheckpoint.Multi) { - for (LocationIdentity identity : ((MemoryCheckpoint.Multi) node).getLocationIdentities()) { - processIdentity(currentState, (FixedNode) node, identity); - } - } - assert MemoryCheckpoint.TypeAssertion.correctType(node); - } - return currentState; - } - - private void processIdentity(HashSet currentState, FixedNode fixed, LocationIdentity identity) { - for (Iterator iter = currentState.iterator(); iter.hasNext();) { - FloatingReadNode read = iter.next(); - if (identity == ANY_LOCATION || read.location().getLocationIdentity() == identity) { - addPhantomReference(read, fixed); - iter.remove(); - } - } - } - - public void addPhantomReference(FloatingReadNode read, FixedNode fixed) { - List usageList = phantomUsages.get(read); - if (usageList == null) { - phantomUsages.put(read, usageList = new ArrayList<>()); - } - usageList.add(fixed); - List inputList = phantomInputs.get(fixed); - if (inputList == null) { - phantomInputs.put(fixed, inputList = new ArrayList<>()); - } - inputList.add(read); - } - - @Override - protected HashSet merge(Block merge, List> states) { - HashSet state = new HashSet<>(states.get(0)); - for (int i = 1; i < states.size(); i++) { - state.retainAll(states.get(i)); - } - return state; - } - - @Override - protected HashSet cloneState(HashSet oldState) { - return new HashSet<>(oldState); - } - - @Override - protected List> processLoop(Loop loop, HashSet state) { - LoopInfo> info = ReentrantBlockIterator.processLoop(this, loop, new HashSet<>(state)); - - List> loopEndStates = info.endStates; - - // collect all reads that were killed in some branch within the loop - Set killedReads = new HashSet<>(state); - Set survivingReads = new HashSet<>(loopEndStates.get(0)); - for (int i = 1; i < loopEndStates.size(); i++) { - survivingReads.retainAll(loopEndStates.get(i)); - } - killedReads.removeAll(survivingReads); - - // reads that were killed within the loop cannot be scheduled after the loop anyway - for (HashSet exitState : info.exitStates) { - exitState.removeAll(killedReads); - } - return info.exitStates; - } + NONE, OPTIMAL } private class KillSet implements Iterable { private final Set set; public KillSet() { - this.set = new HashSet<>(); + this.set = new ArraySet<>(); } public KillSet(KillSet other) { @@ -340,8 +243,6 @@ */ private BlockMap> blockToNodesMap; private BlockMap blockToKillSet; - private final Map> phantomUsages = new IdentityHashMap<>(); - private final Map> phantomInputs = new IdentityHashMap<>(); private final SchedulingStrategy selectedStrategy; private final MemoryScheduling memsched; @@ -350,16 +251,7 @@ } public SchedulePhase(SchedulingStrategy strategy) { - if (MemoryAwareScheduling.getValue() && NewMemoryAwareScheduling.getValue()) { - throw new SchedulingError("cannot enable both: MemoryAware- and NewMemoryAwareScheduling"); - } - if (MemoryAwareScheduling.getValue()) { - this.memsched = MemoryScheduling.CONSERVATIVE; - } else if (NewMemoryAwareScheduling.getValue()) { - this.memsched = MemoryScheduling.OPTIMAL; - } else { - this.memsched = MemoryScheduling.NONE; - } + this.memsched = MemoryAwareScheduling.getValue() ? MemoryScheduling.OPTIMAL : MemoryScheduling.NONE; this.selectedStrategy = strategy; } @@ -374,19 +266,7 @@ earliestCache = graph.createNodeMap(); blockToNodesMap = new BlockMap<>(cfg); - if (memsched == MemoryScheduling.CONSERVATIVE && selectedStrategy != SchedulingStrategy.EARLIEST && graph.getNodes(FloatingReadNode.class).isNotEmpty()) { - assignBlockToNodes(graph, SchedulingStrategy.EARLIEST); - sortNodesWithinBlocks(graph, SchedulingStrategy.EARLIEST); - - ReentrantBlockIterator.apply(new MemoryScheduleClosure(), getCFG().getStartBlock()); - - cfg.clearNodeToBlock(); - blockToNodesMap = new BlockMap<>(cfg); - - assignBlockToNodes(graph, selectedStrategy); - sortNodesWithinBlocks(graph, selectedStrategy); - printSchedule("after sorting nodes within blocks"); - } else if (memsched == MemoryScheduling.OPTIMAL && selectedStrategy != SchedulingStrategy.EARLIEST && graph.getNodes(FloatingReadNode.class).isNotEmpty()) { + if (memsched == MemoryScheduling.OPTIMAL && selectedStrategy != SchedulingStrategy.EARLIEST && graph.getNodes(FloatingReadNode.class).isNotEmpty()) { blockToKillSet = new BlockMap<>(cfg); assignBlockToNodes(graph, selectedStrategy); @@ -654,8 +534,7 @@ } } } - assert false : "should have found a block for " + n; - return null; + throw new SchedulingError("should have found a block for " + n); } /** @@ -719,15 +598,6 @@ blocksForUsage(node, usage, cdbc, strategy); } } - List usages = phantomUsages.get(node); - if (usages != null) { - for (FixedNode usage : usages) { - if (cfg.getNodeToBlock().get(usage) == null) { - throw new SchedulingError(); - } - cdbc.apply(cfg.getNodeToBlock().get(usage)); - } - } if (assertionEnabled()) { if (cdbc.block != null && !earliestBlock(node).dominates(cdbc.block)) { @@ -759,10 +629,6 @@ * Determines the earliest block in which the given node can be scheduled. */ private Block earliestBlock(Node node) { - if (node.isExternal()) { - return cfg.getStartBlock(); - } - Block earliest = cfg.getNodeToBlock().get(node); if (earliest != null) { return earliest; @@ -1013,10 +879,6 @@ List reads = new ArrayList<>(); if (memsched == MemoryScheduling.OPTIMAL) { - /* - * TODO: add assert for invariant - * "floatingreads occur always after memory checkpoints in unsorted list" - */ for (ScheduledNode i : instructions) { if (i instanceof FloatingReadNode) { FloatingReadNode frn = (FloatingReadNode) i; @@ -1070,8 +932,7 @@ if (frn.getLastLocationAccess() == node) { assert identity == ANY_LOCATION || readLocation == identity : "location doesn't match: " + readLocation + ", " + identity; beforeLastLocation.clear(frn); - } else if (!beforeLastLocation.isMarked(frn) && (readLocation == identity || (!(node instanceof StartNode) && ANY_LOCATION == identity))) { - // TODO: replace instanceof check with object identity check + } else if (!beforeLastLocation.isMarked(frn) && (readLocation == identity || (node != getCFG().graph.start() && ANY_LOCATION == identity))) { reads.remove(frn); addToLatestSorting(b, frn, sortedInstructions, visited, reads, beforeLastLocation); } @@ -1088,7 +949,7 @@ for (Node input : state.inputs()) { if (input instanceof VirtualState) { addUnscheduledToLatestSorting(b, (VirtualState) input, sortedInstructions, visited, reads, beforeLastLocation); - } else if (!input.isExternal()) { + } else { addToLatestSorting(b, (ScheduledNode) input, sortedInstructions, visited, reads, beforeLastLocation); } } @@ -1105,17 +966,11 @@ if (input instanceof FrameState) { assert state == null; state = (FrameState) input; - } else if (!input.isExternal()) { + } else { addToLatestSorting(b, (ScheduledNode) input, sortedInstructions, visited, reads, beforeLastLocation); } } - List inputs = phantomInputs.get(i); - if (inputs != null) { - for (FloatingNode input : inputs) { - addToLatestSorting(b, input, sortedInstructions, visited, reads, beforeLastLocation); - } - } if (memsched == MemoryScheduling.OPTIMAL && reads.size() != 0) { if (i instanceof MemoryCheckpoint.Single) { diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/util/ArraySet.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/util/ArraySet.java Tue Dec 17 21:26:42 2013 -0800 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.phases.util; + +import java.util.*; + +/** + * Mimic a set implementation with an ArrayList. Beneficial for small sets (compared to + * {@link HashSet}). + */ +public class ArraySet extends ArrayList implements Set { + private static final long serialVersionUID = 4476957522387436654L; + + public ArraySet() { + super(); + } + + public ArraySet(int i) { + super(i); + } + + public ArraySet(Collection c) { + super(c); + } + + @Override + public boolean add(E e) { + // avoid duplicated entries + if (contains(e)) { + return true; + } + return super.add(e); + } +} diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.printer/src/com/oracle/graal/printer/BinaryGraphPrinter.java --- a/graal/com.oracle.graal.printer/src/com/oracle/graal/printer/BinaryGraphPrinter.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.printer/src/com/oracle/graal/printer/BinaryGraphPrinter.java Tue Dec 17 21:26:42 2013 -0800 @@ -105,7 +105,6 @@ } private final ConstantPool constantPool; - private final Map externalNodeIds; private final ByteBuffer buffer; private final WritableByteChannel channel; @@ -113,7 +112,6 @@ constantPool = new ConstantPool(); buffer = ByteBuffer.allocateDirect(256 * 1024); this.channel = channel; - this.externalNodeIds = new HashMap<>(); } public void print(Graph graph, String title, SchedulePhase predefinedSchedule) throws IOException { @@ -389,25 +387,9 @@ } } - /** - * Should be higher than any internal {@link Node#getId() node id}. - */ - @SuppressWarnings("javadoc") private static final int FIRST_EXTERNAL_NODE_ID = Integer.getInteger("graal.binaryGraphPrinter.firstExternalNodeId", 10000000); - @SuppressWarnings("deprecation") - private int getNodeId(Node node) { - if (!node.isExternal()) { - assert node.getId() < FIRST_EXTERNAL_NODE_ID : "internal node ids exceeded lowest external node id (" + FIRST_EXTERNAL_NODE_ID + - ") - use graal.binaryGraphPrinter.firstExternalNodeId system property to increase the latter"; - return node.getId(); - } else { - Integer id = externalNodeIds.get(node); - if (id == null) { - id = FIRST_EXTERNAL_NODE_ID + externalNodeIds.size(); - externalNodeIds.put(node, id); - } - return id; - } + private static int getNodeId(Node node) { + return node.getId(); } private void writeNodes(Graph graph) throws IOException { @@ -419,16 +401,8 @@ } } Map props = new HashMap<>(); - Map externalNodes = new HashMap<>(); - for (Node node : graph.getNodes()) { - for (Node input : node.inputs()) { - if (input.isExternal()) { - externalNodes.put(input, getNodeId(input)); - } - } - } - writeInt(graph.getNodeCount() + externalNodes.size()); + writeInt(graph.getNodeCount()); for (Node node : graph.getNodes()) { NodeClass nodeClass = node.getNodeClass(); @@ -453,22 +427,6 @@ props.clear(); } - for (Map.Entry e : externalNodes.entrySet()) { - Node node = e.getKey(); - NodeClass nodeClass = node.getNodeClass(); - node.getDebugProperties(props); - writeInt(e.getValue()); - writePoolObject(nodeClass); - writeByte(0); - // properties - writeShort((char) props.size()); - for (Entry entry : props.entrySet()) { - String key = entry.getKey().toString(); - writePoolObject(key); - writePropertyObject(entry.getValue()); - } - props.clear(); - } } private void writeEdges(Node node, Collection positions) throws IOException { diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.replacements.hsail/src/com/oracle/graal/replacements/hsail/HSAILMathIntrinsicsNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.replacements.hsail/src/com/oracle/graal/replacements/hsail/HSAILMathIntrinsicsNode.java Tue Dec 17 21:26:42 2013 -0800 @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.replacements.hsail; + +import com.oracle.graal.api.meta.Constant; +import com.oracle.graal.api.meta.Value; +import com.oracle.graal.compiler.hsail.HSAILLIRGenerator; +import com.oracle.graal.graph.GraalInternalError; +import com.oracle.graal.graph.Node; +import com.oracle.graal.graph.spi.Canonicalizable; +import com.oracle.graal.graph.spi.CanonicalizerTool; +import com.oracle.graal.lir.hsail.HSAILArithmetic; +import com.oracle.graal.nodes.calc.FloatingNode; +import com.oracle.graal.nodes.ConstantNode; +import com.oracle.graal.nodes.ValueNode; +import com.oracle.graal.nodes.spi.ArithmeticLIRGenerator; +import com.oracle.graal.nodes.spi.ArithmeticLIRLowerable; +import com.oracle.graal.nodes.type.StampFactory; + +/** + * This node implements HSAIL intrinsics for specific {@link Math} routines. + */ +public class HSAILMathIntrinsicsNode extends FloatingNode implements Canonicalizable, ArithmeticLIRLowerable { + + /** + * The parameter passed to the math operation that this node represents. + */ + @Input private ValueNode param; + + /** + * The math operation that this Node represents. + */ + private final HSAILArithmetic operation; + + /** + * Gets the parameter passed to the math operation that this node represents. + * + * @return the parameter + */ + public ValueNode getParameter() { + return param; + } + + /** + * Returns the math operation represented by this node. + * + * @return the operation + */ + public HSAILArithmetic operation() { + return operation; + } + + /** + * Creates a new HSAILMathIntrinsicNode. + * + * @param x the argument to the math operation + * @param op the math operation + */ + public HSAILMathIntrinsicsNode(ValueNode x, HSAILArithmetic op) { + super(StampFactory.forKind(x.kind())); + this.param = x; + this.operation = op; + } + + /** + * Generates the LIR instructions for the math operation represented by this node. + */ + @Override + public void generate(ArithmeticLIRGenerator gen) { + Value input = gen.operand(getParameter()); + Value result; + switch (operation()) { + case ABS: + result = gen.emitMathAbs(input); + break; + case CEIL: + result = ((HSAILLIRGenerator) (gen)).emitMathCeil(input); + break; + case FLOOR: + result = ((HSAILLIRGenerator) (gen)).emitMathFloor(input); + break; + case RINT: + result = ((HSAILLIRGenerator) (gen)).emitMathRint(input); + break; + case SQRT: + result = gen.emitMathSqrt(input); + break; + + default: + throw GraalInternalError.shouldNotReachHere(); + } + gen.setResult(this, result); + } + + /** + * Converts a constant to a boxed double. + */ + public Constant evalConst(Constant... inputs) { + assert inputs.length == 1; + return Constant.forDouble(compute(inputs[0].asDouble(), operation())); + } + + /** + * Converts the result of the math operation to a boxed Double constant node. + */ + @Override + public Node canonical(CanonicalizerTool tool) { + if (getParameter().isConstant()) { + return ConstantNode.forPrimitive(evalConst(getParameter().asConstant()), graph()); + } + return this; + } + + // The routines below are node intrinsics. A call to any of these routines is replaced by a + // HSAILMathIntrinsicNode to handle the Math library operation represented by the + // HSAILArithmetic argument. + + /** + * Node intrinsic for {@link Math} routines taking a single int parameter. + * + * @param value + * @param op the math operation + * @return the result of the operation + */ + @NodeIntrinsic + public static native int compute(int value, @ConstantNodeParameter HSAILArithmetic op); + + /** + * Node intrinsic for {@link Math} routines taking a single double parameter. + * + * @param value the input parameter + * @param op the math operation + * @return the result of the operation + */ + @NodeIntrinsic + public static native long compute(long value, @ConstantNodeParameter HSAILArithmetic op); + + /** + * Node intrinsic for {@link Math} routines taking a single float parameter. + * + * @param value the input parameter + * @param op the math operation + * @return the result of the operation + */ + @NodeIntrinsic + public static native float compute(float value, @ConstantNodeParameter HSAILArithmetic op); + + /** + * Node intrinsic for {@link Math} routines taking a single double parameter. + * + * @param value the input parameter + * @param op the math operation + * + * @return the result of the operation + */ + @NodeIntrinsic + public static native double compute(double value, @ConstantNodeParameter HSAILArithmetic op); +} diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.replacements.hsail/src/com/oracle/graal/replacements/hsail/HSAILMathSubstitutions.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.replacements.hsail/src/com/oracle/graal/replacements/hsail/HSAILMathSubstitutions.java Tue Dec 17 21:26:42 2013 -0800 @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.replacements.hsail; + +import com.oracle.graal.api.replacements.ClassSubstitution; +import com.oracle.graal.api.replacements.MethodSubstitution; +import com.oracle.graal.lir.hsail.HSAILArithmetic; + +/** + * Substitutions for {@link Math} methods. For any calls to the routines listed below and annotated + * with {@link MethodSubstitution}, Graal replaces the call with a {@link HSAILMathIntrinsicsNode}. + */ +@ClassSubstitution(java.lang.Math.class) +public class HSAILMathSubstitutions { + + /** + * Substitution for {@link Math#abs(int)}. + * + * @param x the input + * @return the result of the computation + */ + @MethodSubstitution + public static int abs(int x) { + return HSAILMathIntrinsicsNode.compute(x, HSAILArithmetic.ABS); + } + + /** + * Substitution for {@link Math#abs(long)}. + * + * @param x the input + * @return the result of the computation + */ + @MethodSubstitution + public static long abs(long x) { + return HSAILMathIntrinsicsNode.compute(x, HSAILArithmetic.ABS); + } + + /** + * Substitution for {@link Math#abs(float)}. + * + * @param x the input + * @return the result of the computation + */ + @MethodSubstitution + public static float abs(float x) { + return HSAILMathIntrinsicsNode.compute(x, HSAILArithmetic.ABS); + } + + /** + * Substitution for {@link Math#abs(double)}. + * + * @param x the input + * @return the result of the computation + */ + @MethodSubstitution + public static double abs(double x) { + return HSAILMathIntrinsicsNode.compute(x, HSAILArithmetic.ABS); + } + + /** + * Substitution for Math.ceil(double). + * + * @param x the input + * @return the result of the computation + */ + @MethodSubstitution + public static double ceil(double x) { + return HSAILMathIntrinsicsNode.compute(x, HSAILArithmetic.CEIL); + } + + /** + * Substitution for {@link Math#floor(double)}. + * + * @param x the input + * @return the result of the computation + */ + @MethodSubstitution + public static double floor(double x) { + return HSAILMathIntrinsicsNode.compute(x, HSAILArithmetic.FLOOR); + } + + /** + * Substitution for {@link Math#rint(double)}. + * + * @param x the input + * @return the result of the computation + */ + @MethodSubstitution + public static double rint(double x) { + return HSAILMathIntrinsicsNode.compute(x, HSAILArithmetic.RINT); + } + + /** + * Substitution for {@link Math#sqrt(double)}. + * + * @param x the input + * @return the result of the computation + */ + @MethodSubstitution + public static double sqrt(double x) { + return HSAILMathIntrinsicsNode.compute(x, HSAILArithmetic.SQRT); + } +} diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerOptions.java --- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerOptions.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerOptions.java Tue Dec 17 21:26:42 2013 -0800 @@ -60,7 +60,7 @@ @Option(help = "") public static final OptionValue TruffleInliningMaxRecursiveDepth = new OptionValue<>(2); @Option(help = "") - public static final OptionValue TruffleInliningMaxCallerSize = new OptionValue<>(2500); + public static final OptionValue TruffleInliningMaxCallerSize = new OptionValue<>(2250); @Option(help = "") public static final OptionValue TruffleInliningMaxCalleeSize = new OptionValue<>(250); @Option(help = "") diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/EffectsClosure.java --- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/EffectsClosure.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/EffectsClosure.java Tue Dec 17 21:26:42 2013 -0800 @@ -231,7 +231,7 @@ public ValueNode getScalarAlias(ValueNode node) { assert !(node instanceof VirtualObjectNode); - if (node == null || !node.isAlive() || node.isExternal() || aliases.isNew(node)) { + if (node == null || !node.isAlive() || aliases.isNew(node)) { return node; } ValueNode result = aliases.get(node); diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/GraphEffectList.java --- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/GraphEffectList.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/GraphEffectList.java Tue Dec 17 21:26:42 2013 -0800 @@ -344,7 +344,7 @@ commit.getLocks().addAll(locks); assert commit.usages().filter(AllocatedObjectNode.class).count() == commit.usages().count(); - HashSet materializedValues = new HashSet<>(commit.usages().filter(AllocatedObjectNode.class).snapshot()); + List materializedValues = commit.usages().filter(AllocatedObjectNode.class).snapshot(); for (int i = 0; i < commit.getValues().size(); i++) { if (materializedValues.contains(commit.getValues().get(i))) { commit.getValues().set(i, ((AllocatedObjectNode) commit.getValues().get(i)).getVirtualObject()); diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeClosure.java --- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeClosure.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeClosure.java Tue Dec 17 21:26:42 2013 -0800 @@ -37,6 +37,7 @@ import com.oracle.graal.nodes.spi.Virtualizable.EscapeState; import com.oracle.graal.nodes.virtual.*; import com.oracle.graal.phases.schedule.*; +import com.oracle.graal.phases.util.*; import com.oracle.graal.virtual.nodes.*; public abstract class PartialEscapeClosure> extends EffectsClosure { @@ -136,7 +137,7 @@ nodeWithState.asNode().replaceFirstInput(frameState, frameState.copyWithInputs()); frameState = nodeWithState.getState(); } - final HashSet virtual = new HashSet<>(); + final Set virtual = new ArraySet<>(); frameState.applyToNonVirtual(new NodeClosure() { @Override @@ -611,7 +612,7 @@ } public ObjectState getObjectState(PartialEscapeBlockState state, ValueNode value) { - if (value == null || value.isExternal()) { + if (value == null) { return null; } if (value.isAlive() && !aliases.isNew(value)) { diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/VirtualUtil.java --- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/VirtualUtil.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/VirtualUtil.java Tue Dec 17 21:26:42 2013 -0800 @@ -69,22 +69,18 @@ for (Node node : graph.getNodes()) { if (flood.isMarked(node)) { for (Node input : node.inputs()) { - if (!input.isExternal()) { - flood.add(input); - if (!path.containsKey(input)) { - path.put(input, node); - } + flood.add(input); + if (!path.containsKey(input)) { + path.put(input, node); } } } } for (Node current : flood) { for (Node input : current.inputs()) { - if (!input.isExternal()) { - flood.add(input); - if (!path.containsKey(input)) { - path.put(input, current); - } + flood.add(input); + if (!path.containsKey(input)) { + path.put(input, current); } } } diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/utilities/AlwaysValidAssumptionTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/utilities/AlwaysValidAssumptionTest.java Tue Dec 17 21:26:42 2013 -0800 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.test.utilities; + +import static org.junit.Assert.*; +import org.junit.*; + +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.utilities.*; + +public class AlwaysValidAssumptionTest { + + @Test + public void testCheck() throws InvalidAssumptionException { + final AlwaysValidAssumption assumption = AlwaysValidAssumption.INSTANCE; + assumption.check(); + } + + @Test + public void testIsValid() { + final AlwaysValidAssumption assumption = AlwaysValidAssumption.INSTANCE; + assertTrue(assumption.isValid()); + } + + @Test(expected = UnsupportedOperationException.class) + public void testCannotInvalidate() { + final AlwaysValidAssumption assumption = AlwaysValidAssumption.INSTANCE; + assumption.invalidate(); + } + +} diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/utilities/AssumedValueTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/utilities/AssumedValueTest.java Tue Dec 17 21:26:42 2013 -0800 @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.test.utilities; + +import static org.junit.Assert.*; +import org.junit.*; + +import com.oracle.truffle.api.utilities.*; + +public class AssumedValueTest { + + @Test + public void testGet() { + final AssumedValue assumedValue = new AssumedValue<>("assumed-value", "1"); + assertEquals("1", assumedValue.get()); + } + + @Test + public void testSet() { + final AssumedValue assumedValue = new AssumedValue<>("assumed-value", "1"); + assertEquals("1", assumedValue.get()); + assumedValue.set("2"); + assertEquals("2", assumedValue.get()); + } + +} diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/utilities/BranchProfileTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/utilities/BranchProfileTest.java Tue Dec 17 21:26:42 2013 -0800 @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.test.utilities; + +import org.junit.*; + +import com.oracle.truffle.api.utilities.*; + +public class BranchProfileTest { + + @Test + public void testEnter() { + BranchProfile profile = new BranchProfile(); + profile.enter(); + profile.enter(); + } + +} diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/utilities/CyclicAssumptionTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/utilities/CyclicAssumptionTest.java Tue Dec 17 21:26:42 2013 -0800 @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.test.utilities; + +import static org.junit.Assert.*; + +import org.junit.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.utilities.*; + +public class CyclicAssumptionTest { + + @Test + public void testIsValid() { + final CyclicAssumption assumption = new CyclicAssumption("cyclic-assumption"); + assertTrue(assumption.getAssumption().isValid()); + } + + @Test + public void testInvalidate() { + final CyclicAssumption cyclicAssumption = new CyclicAssumption("cyclic-assumption"); + + final Assumption firstAssumption = cyclicAssumption.getAssumption(); + assertEquals("cyclic-assumption", firstAssumption.getName()); + assertTrue(firstAssumption.isValid()); + + cyclicAssumption.invalidate(); + + assertFalse(firstAssumption.isValid()); + + final Assumption secondAssumption = cyclicAssumption.getAssumption(); + assertEquals("cyclic-assumption", secondAssumption.getName()); + assertTrue(secondAssumption.isValid()); + + cyclicAssumption.invalidate(); + + assertFalse(firstAssumption.isValid()); + assertFalse(secondAssumption.isValid()); + } + +} diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/utilities/UnionAssumptionTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/utilities/UnionAssumptionTest.java Tue Dec 17 21:26:42 2013 -0800 @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.test.utilities; + +import static org.junit.Assert.*; + +import org.junit.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.utilities.*; + +public class UnionAssumptionTest { + + @Test + public void testIsValid() { + final Assumption first = Truffle.getRuntime().createAssumption("first"); + final Assumption second = Truffle.getRuntime().createAssumption("second"); + final UnionAssumption union = new UnionAssumption(first, second); + assertTrue(union.isValid()); + } + + @Test + public void testCheck() throws InvalidAssumptionException { + final Assumption first = Truffle.getRuntime().createAssumption("first"); + final Assumption second = Truffle.getRuntime().createAssumption("second"); + final UnionAssumption union = new UnionAssumption(first, second); + union.check(); + } + + @Test + public void testFirstInvalidateIsValid() { + final Assumption first = Truffle.getRuntime().createAssumption("first"); + final Assumption second = Truffle.getRuntime().createAssumption("second"); + final UnionAssumption union = new UnionAssumption(first, second); + + first.invalidate(); + + assertFalse(union.isValid()); + } + + @Test(expected = InvalidAssumptionException.class) + public void testFirstInvalidateCheck() throws InvalidAssumptionException { + final Assumption first = Truffle.getRuntime().createAssumption("first"); + final Assumption second = Truffle.getRuntime().createAssumption("second"); + final UnionAssumption union = new UnionAssumption(first, second); + + first.invalidate(); + + union.check(); + } + + @Test + public void testSecondInvalidateIsValid() { + final Assumption first = Truffle.getRuntime().createAssumption("first"); + final Assumption second = Truffle.getRuntime().createAssumption("second"); + final UnionAssumption union = new UnionAssumption(first, second); + + second.invalidate(); + + assertFalse(union.isValid()); + } + + @Test(expected = InvalidAssumptionException.class) + public void testSecondInvalidateCheck() throws InvalidAssumptionException { + final Assumption first = Truffle.getRuntime().createAssumption("first"); + final Assumption second = Truffle.getRuntime().createAssumption("second"); + final UnionAssumption union = new UnionAssumption(first, second); + + second.invalidate(); + + union.check(); + } + + @Test + public void testBothInvalidateIsValid() { + final Assumption first = Truffle.getRuntime().createAssumption("first"); + final Assumption second = Truffle.getRuntime().createAssumption("second"); + final UnionAssumption union = new UnionAssumption(first, second); + + first.invalidate(); + second.invalidate(); + + assertFalse(union.isValid()); + } + + @Test(expected = InvalidAssumptionException.class) + public void testBothInvalidateCheck() throws InvalidAssumptionException { + final Assumption first = Truffle.getRuntime().createAssumption("first"); + final Assumption second = Truffle.getRuntime().createAssumption("second"); + final UnionAssumption union = new UnionAssumption(first, second); + + first.invalidate(); + second.invalidate(); + + union.check(); + } + +} diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/Truffle.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/Truffle.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/Truffle.java Tue Dec 17 21:26:42 2013 -0800 @@ -31,19 +31,21 @@ */ public class Truffle { - private static TruffleRuntime runtime; + private static final TruffleRuntime RUNTIME; private static native TruffleRuntime initializeRuntime(); public static TruffleRuntime getRuntime() { - return runtime; + return RUNTIME; } static { + TruffleRuntime runtime; try { runtime = initializeRuntime(); } catch (UnsatisfiedLinkError e) { runtime = new DefaultTruffleRuntime(); } + RUNTIME = runtime; } } diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/DefaultCallTarget.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/DefaultCallTarget.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/DefaultCallTarget.java Tue Dec 17 21:26:42 2013 -0800 @@ -50,6 +50,10 @@ return rootNode.execute(frame); } + public FrameDescriptor getFrameDescriptor() { + return frameDescriptor; + } + public RootNode getRootNode() { return rootNode; } diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/utilities/AlwaysValidAssumption.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/utilities/AlwaysValidAssumption.java Tue Dec 17 21:26:42 2013 -0800 @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.utilities; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.nodes.*; + +/** + * An assumption that is always valid. Used as a placeholder where an assumption is needed but never + * invalidated. + */ +public final class AlwaysValidAssumption implements Assumption { + + public static final AlwaysValidAssumption INSTANCE = new AlwaysValidAssumption(); + + private AlwaysValidAssumption() { + } + + @Override + public void check() throws InvalidAssumptionException { + } + + @Override + public void invalidate() { + throw new UnsupportedOperationException("Cannot invalidate this assumption - it is always valid"); + } + + @Override + public String getName() { + return getClass().getName(); + } + + @Override + public boolean isValid() { + return true; + } + +} diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/utilities/AssumedValue.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/utilities/AssumedValue.java Tue Dec 17 21:26:42 2013 -0800 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.utilities; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.nodes.*; + +/** + * A {@link CompilationFinal} value combined with an {@link Assumption} to notify when it changes. + * Note that you should be careful that modifications to this value do not cause deoptimization + * loops. This could be by using a value that is monotonic. + */ +public class AssumedValue { + + @CompilationFinal private T value; + private final CyclicAssumption assumption; + + public AssumedValue(String name, T initialValue) { + assumption = new CyclicAssumption(name); + value = initialValue; + } + + /** + * Get the current value, updating it if it has been {@link #set}. The compiler may be able to + * make this method return a constant value, but still accommodate mutation. + */ + public T get() { + try { + assumption.getAssumption().check(); + } catch (InvalidAssumptionException e) { + // No need to rewrite anything - just pick up the new value + } + + return value; + } + + /** + * Set a new value, which will be picked up the next time {@link #get} is called. + */ + public void set(T newValue) { + value = newValue; + assumption.invalidate(); + } + +} diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/utilities/BranchProfile.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/utilities/BranchProfile.java Tue Dec 17 21:26:42 2013 -0800 @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.utilities; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.CompilerDirectives.*; + +/** + * Utility class to speculate on branches to be never visited. If the {@link #enter()} method is + * invoked first the optimized code is invalidated and the branch where {@link #enter()} is invoked + * is enabled for compilation. Otherwise if the {@link #enter()} method was never invoked the branch + * will not get compiled. + */ +public final class BranchProfile { + + @CompilationFinal private boolean visited; + + public void enter() { + if (!visited) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + visited = true; + } + } + +} diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/utilities/CyclicAssumption.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/utilities/CyclicAssumption.java Tue Dec 17 21:26:42 2013 -0800 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.utilities; + +import com.oracle.truffle.api.*; + +/** + * Holds an {@link Assumption}, and knows how to recreate it with the same properties on + * invalidation. Used so that mutability is isolated in this class, and all other classes that need + * an assumption that may be recreated can have a final reference to an object of this class. Note + * that you should be careful that repeated invalidations do not cause a deoptimization loop in that + * same way that you would with any other assumption. + */ +public class CyclicAssumption { + + private final String name; + private Assumption assumption; + + public CyclicAssumption(String name) { + this.name = name; + invalidate(); + } + + public void invalidate() { + if (assumption != null) { + assumption.invalidate(); + } + + assumption = Truffle.getRuntime().createAssumption(name); + } + + public Assumption getAssumption() { + return assumption; + } + +} diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/utilities/UnionAssumption.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/utilities/UnionAssumption.java Tue Dec 17 21:26:42 2013 -0800 @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.utilities; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.nodes.*; + +/** + * An assumption that combines two other assumptions. A check on this assumption checks both of the + * child assumptions. + */ +public class UnionAssumption implements Assumption { + + private final String name; + private final Assumption first; + private final Assumption second; + + public UnionAssumption(String name, Assumption first, Assumption second) { + this.name = name; + this.first = first; + this.second = second; + } + + public UnionAssumption(Assumption first, Assumption second) { + this(null, first, second); + } + + @Override + public void check() throws InvalidAssumptionException { + first.check(); + second.check(); + } + + @Override + public void invalidate() { + first.invalidate(); + second.invalidate(); + } + + @Override + public String getName() { + return name; + } + + @Override + public boolean isValid() { + return first.isValid() && second.isValid(); + } + +} diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/CallNode.java --- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/CallNode.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/CallNode.java Tue Dec 17 21:26:42 2013 -0800 @@ -34,127 +34,126 @@ private static final int INLINE_CACHE_SIZE = 2; @Child protected TypedNode functionNode; - - private CallNode(TypedNode functionNode) { - this.functionNode = adoptChild(functionNode); - } + @Child protected ArgumentsNode argumentsNode; - private CallTarget executeCallTargetNode(VirtualFrame frame) { - try { - return functionNode.executeCallTarget(frame); - } catch (UnexpectedResultException e) { - throw new UnsupportedOperationException("Call to " + e.getMessage() + " not supported."); - } + public CallNode(TypedNode functionNode, ArgumentsNode argumentsNode) { + this.functionNode = adoptChild(functionNode); + this.argumentsNode = adoptChild(argumentsNode); } @Override public final Object executeGeneric(VirtualFrame frame) { - return executeGeneric(frame, executeCallTargetNode(frame)); + CallTarget function; + try { + function = functionNode.executeCallTarget(frame); + } catch (UnexpectedResultException e) { + throw new UnsupportedOperationException("Call to " + e.getMessage() + " not supported."); + } + Object[] arguments = argumentsNode.executeArray(frame); + return executeCall(frame, function, arguments); } - public abstract Object executeGeneric(VirtualFrame frame, CallTarget function); + public abstract Object executeCall(VirtualFrame frame, CallTarget function, Object[] arguments); public static CallNode create(TypedNode function, TypedNode[] arguments) { return new UninitializedCallNode(function, new ArgumentsNode(arguments), 0); } - private static final class CachedCallNode extends CallNode { - - @Child protected CallNode nextNode; - @Child protected TypedNode currentNode; - private final CallTarget cachedFunction; - - public CachedCallNode(TypedNode function, TypedNode current, CallNode next, CallTarget cachedFunction) { - super(function); - this.currentNode = adoptChild(current); - this.nextNode = adoptChild(next); - this.cachedFunction = cachedFunction; - } - - @Override - public Object executeGeneric(VirtualFrame frame, CallTarget function) { - if (this.cachedFunction == function) { - return currentNode.executeGeneric(frame); - } - return nextNode.executeGeneric(frame, function); - } - } - private static final class UninitializedCallNode extends CallNode { - @Child protected ArgumentsNode uninitializedArgs; protected final int depth; UninitializedCallNode(TypedNode function, ArgumentsNode args, int depth) { - super(function); - this.uninitializedArgs = adoptChild(args); + super(function, args); this.depth = depth; } UninitializedCallNode(UninitializedCallNode copy) { - super(null); - this.uninitializedArgs = adoptChild(copy.uninitializedArgs); + super(null, null); this.depth = copy.depth + 1; } @Override - public Object executeGeneric(VirtualFrame frame, CallTarget function) { + public Object executeCall(VirtualFrame frame, CallTarget function, Object[] arguments) { CompilerDirectives.transferToInterpreter(); - return specialize(function).executeGeneric(frame, function); + return specialize(function).executeCall(frame, function, arguments); } private CallNode specialize(CallTarget function) { CompilerAsserts.neverPartOfCompilation(); if (depth < INLINE_CACHE_SIZE) { - TypedNode current = createCacheNode(function); + DefaultCallTarget callTarget = (DefaultCallTarget) function; + FunctionRootNode root = (FunctionRootNode) callTarget.getRootNode(); CallNode next = new UninitializedCallNode(this); - return replace(new CachedCallNode(this.functionNode, current, next, function)); + InlinableDirectCallNode directCall = new InlinableDirectCallNode(functionNode, argumentsNode, next, callTarget); + replace(directCall); + if (root.isInlineImmediatly()) { + return directCall.inlineImpl(); + } else { + return directCall; + } } else { - CallNode topMost = (CallNode) getTopNode(); - return topMost.replace(new GenericCallNode(topMost.functionNode, uninitializedArgs)); + CallNode topMost = (CallNode) NodeUtil.getNthParent(this, depth); + return topMost.replace(new GenericCallNode(topMost.functionNode, topMost.argumentsNode)); } } - protected Node getTopNode() { - Node parentNode = this; - for (int i = 0; i < depth; i++) { - parentNode = parentNode.getParent(); - } - return parentNode; + } + + private abstract static class DirectCallNode extends CallNode { + + protected final DefaultCallTarget cachedFunction; + + @Child protected CallNode nextNode; + + public DirectCallNode(TypedNode function, ArgumentsNode arguments, DefaultCallTarget cachedFunction, CallNode next) { + super(function, arguments); + this.cachedFunction = cachedFunction; + this.nextNode = adoptChild(next); } - protected TypedNode createCacheNode(CallTarget function) { - ArgumentsNode clonedArgs = NodeUtil.cloneNode(uninitializedArgs); + @Override + public Object executeCall(VirtualFrame frame, CallTarget function, Object[] arguments) { + if (this.cachedFunction == function) { + return executeCurrent(frame, arguments); + } + return nextNode.executeCall(frame, function, arguments); + } - if (function instanceof DefaultCallTarget) { - DefaultCallTarget defaultFunction = (DefaultCallTarget) function; - RootNode rootNode = defaultFunction.getRootNode(); - if (rootNode instanceof FunctionRootNode) { - FunctionRootNode root = (FunctionRootNode) rootNode; - if (root.isAlwaysInline()) { - TypedNode inlinedCall = root.inline(clonedArgs); - if (inlinedCall != null) { - return inlinedCall; - } - } - return new InlinableCallNode((DefaultCallTarget) function, clonedArgs); - } - // got a call target that is not inlinable (should not occur for SL) - return new DispatchedCallNode(defaultFunction, clonedArgs); - } else { - throw new AssertionError(); - } + protected abstract Object executeCurrent(VirtualFrame frame, Object[] arguments); - } } - private static final class InlinableCallNode extends DispatchedCallNode implements InlinableCallSite { + private static final class InlinableDirectCallNode extends DirectCallNode implements InlinableCallSite { @CompilationFinal private int callCount; - InlinableCallNode(DefaultCallTarget function, ArgumentsNode arguments) { - super(function, arguments); + InlinableDirectCallNode(TypedNode function, ArgumentsNode arguments, CallNode next, DefaultCallTarget cachedFunction) { + super(function, arguments, cachedFunction, next); + } + + @Override + public Object executeCurrent(VirtualFrame frame, Object[] arguments) { + if (CompilerDirectives.inInterpreter()) { + callCount++; + } + return cachedFunction.call(frame.pack(), new SLArguments(arguments)); + } + + InlinedDirectCallNode inlineImpl() { + CompilerAsserts.neverPartOfCompilation(); + RootNode root = cachedFunction.getRootNode(); + TypedNode inlinedNode = ((FunctionRootNode) root).inline(); + assert inlinedNode != null; + return replace(new InlinedDirectCallNode(this, inlinedNode), "Inlined " + root); + } + + @Override + public boolean inline(FrameFactory factory) { + inlineImpl(); + /* SL is always able to inline if required. */ + return true; } @Override @@ -169,7 +168,7 @@ @Override public Node getInlineTree() { - RootNode root = function.getRootNode(); + RootNode root = cachedFunction.getRootNode(); if (root instanceof FunctionRootNode) { return ((FunctionRootNode) root).getUninitializedBody(); } @@ -177,67 +176,46 @@ } @Override - public boolean inline(FrameFactory factory) { - CompilerAsserts.neverPartOfCompilation(); - TypedNode functionCall = null; - - RootNode root = function.getRootNode(); - if (root instanceof FunctionRootNode) { - functionCall = ((FunctionRootNode) root).inline(NodeUtil.cloneNode(args)); - } - if (functionCall != null) { - this.replace(functionCall); - return true; - } else { - return false; - } - } - - @Override - public Object executeGeneric(VirtualFrame frame) { - if (CompilerDirectives.inInterpreter()) { - callCount++; - } - return super.executeGeneric(frame); - } - - @Override public CallTarget getCallTarget() { - return function; + return cachedFunction; } } - private static class DispatchedCallNode extends TypedNode { + private static class InlinedDirectCallNode extends DirectCallNode implements InlinedCallSite { + + private final FrameDescriptor descriptor; + @Child private TypedNode inlinedBody; - @Child protected ArgumentsNode args; - protected final DefaultCallTarget function; - - DispatchedCallNode(DefaultCallTarget function, ArgumentsNode arguments) { - this.args = adoptChild(arguments); - this.function = function; + InlinedDirectCallNode(InlinableDirectCallNode prev, TypedNode inlinedBody) { + super(prev.functionNode, prev.argumentsNode, prev.cachedFunction, prev.nextNode); + this.descriptor = cachedFunction.getFrameDescriptor(); + this.inlinedBody = adoptChild(inlinedBody); } @Override - public Object executeGeneric(VirtualFrame frame) { - SLArguments argsObject = new SLArguments(args.executeArray(frame)); - return function.call(frame.pack(), argsObject); + public Object executeCurrent(VirtualFrame frame, Object[] arguments) { + SLArguments slArguments = new SLArguments(arguments); + VirtualFrame newFrame = Truffle.getRuntime().createVirtualFrame(frame.pack(), slArguments, descriptor); + return inlinedBody.executeGeneric(newFrame); } + + @Override + public CallTarget getCallTarget() { + return cachedFunction; + } + } private static final class GenericCallNode extends CallNode { - @Child protected ArgumentsNode args; - GenericCallNode(TypedNode functionNode, ArgumentsNode arguments) { - super(functionNode); - this.args = adoptChild(arguments); + super(functionNode, arguments); } @Override - public Object executeGeneric(VirtualFrame frame, CallTarget function) { - SLArguments argsObject = new SLArguments(args.executeArray(frame)); - return function.call(frame.pack(), argsObject); + public Object executeCall(VirtualFrame frame, CallTarget function, Object[] arguments) { + return function.call(frame.pack(), new SLArguments(arguments)); } } diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/FunctionRootNode.java --- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/FunctionRootNode.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/FunctionRootNode.java Tue Dec 17 21:26:42 2013 -0800 @@ -35,14 +35,14 @@ private final TypedNode uninitializedBody; private final String name; - private final boolean alwaysInline; + private final boolean inlineImmediatly; - private FunctionRootNode(TypedNode body, String name, boolean alwaysInline) { + private FunctionRootNode(TypedNode body, String name, boolean inlineImmediatly) { super(null); this.uninitializedBody = NodeUtil.cloneNode(body); this.body = adoptChild(body); this.name = name; - this.alwaysInline = alwaysInline; + this.inlineImmediatly = inlineImmediatly; } public static CallTarget createBuiltin(SLContext context, NodeFactory factory, String name) { @@ -67,36 +67,12 @@ return body.executeGeneric(frame); } - public boolean isAlwaysInline() { - return alwaysInline; - } - - public TypedNode inline(ArgumentsNode clonedArgs) { - TypedNode clonedBody = NodeUtil.cloneNode(uninitializedBody); - if (clonedBody instanceof BuiltinNode) { - return inlineBuiltin(clonedArgs, (BuiltinNode) clonedBody); - } else if (clonedBody instanceof FunctionBodyNode) { - return inlineFunction(clonedArgs, (FunctionBodyNode) clonedBody); - } else { - throw new UnsupportedOperationException(); - } + public boolean isInlineImmediatly() { + return inlineImmediatly; } - private InlinedFunctionNode inlineFunction(ArgumentsNode clonedArgs, FunctionBodyNode clonedBody) { - return new InlinedFunctionNode(getCallTarget(), clonedBody, clonedArgs); - } - - private static TypedNode inlineBuiltin(ArgumentsNode clonedArgs, BuiltinNode builtin) { - TypedNode[] callerArgs = clonedArgs.getArguments(); - TypedNode[] builtinArgs = builtin.getArguments(); - for (int i = 0; i < builtinArgs.length; i++) { - if (i < callerArgs.length) { - builtinArgs[i].replace(callerArgs[i]); - } else { - builtinArgs[i].replace(new NullLiteralNode()); - } - } - return builtin; + public TypedNode inline() { + return NodeUtil.cloneNode(uninitializedBody); } public Node getUninitializedBody() { @@ -108,34 +84,6 @@ return "function " + name; } - private static final class InlinedFunctionNode extends TypedNode implements InlinedCallSite { - - @Child private FunctionBodyNode body; - @Child private ArgumentsNode arguments; - - private final CallTarget callTarget; - private final FrameDescriptor frameDescriptor; - - public InlinedFunctionNode(CallTarget callTarget, FunctionBodyNode body, ArgumentsNode arguments) { - this.callTarget = callTarget; - this.body = adoptChild(body); - this.frameDescriptor = body.getFrameDescriptor(); - this.arguments = adoptChild(arguments); - } - - @Override - public Object executeGeneric(VirtualFrame frame) { - SLArguments args = new SLArguments(arguments.executeArray(frame)); - VirtualFrame childFrame = Truffle.getRuntime().createVirtualFrame(frame.pack(), args, frameDescriptor); - return body.executeGeneric(childFrame); - } - - public CallTarget getCallTarget() { - return callTarget; - } - - } - public String getName() { return name; } diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/IfNode.java --- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/IfNode.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/IfNode.java Tue Dec 17 21:26:42 2013 -0800 @@ -24,6 +24,7 @@ import com.oracle.truffle.api.dsl.*; import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.utilities.*; @NodeChild(value = "conditionNode", type = ConditionNode.class) public abstract class IfNode extends StatementNode { @@ -31,6 +32,9 @@ @Child private StatementNode thenPartNode; @Child private StatementNode elsePartNode; + private final BranchProfile ifBranch = new BranchProfile(); + private final BranchProfile elseBranch = new BranchProfile(); + public IfNode(StatementNode thenPart, StatementNode elsePart) { this.thenPartNode = adoptChild(thenPart); this.elsePartNode = adoptChild(elsePart); @@ -43,9 +47,11 @@ @Specialization public void doVoid(VirtualFrame frame, boolean condition) { if (condition) { + ifBranch.enter(); thenPartNode.executeVoid(frame); } else { if (elsePartNode != null) { + elseBranch.enter(); elsePartNode.executeVoid(frame); } } diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/ReadArgumentNode.java --- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/ReadArgumentNode.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/ReadArgumentNode.java Tue Dec 17 21:26:42 2013 -0800 @@ -23,12 +23,16 @@ package com.oracle.truffle.sl.nodes; import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.utilities.*; import com.oracle.truffle.sl.runtime.*; public class ReadArgumentNode extends TypedNode { private final int index; + private final BranchProfile outOfBounds = new BranchProfile(); + private final BranchProfile inBounds = new BranchProfile(); + public ReadArgumentNode(int index) { this.index = index; } @@ -37,8 +41,10 @@ public Object executeGeneric(VirtualFrame frame) { Object[] args = SLArguments.get(frame).arguments; if (index < args.length) { + inBounds.enter(); return args[index]; } else { + outOfBounds.enter(); return SLNull.INSTANCE; } } diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/ReadFunctionNode.java --- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/ReadFunctionNode.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/ReadFunctionNode.java Tue Dec 17 21:26:42 2013 -0800 @@ -23,16 +23,15 @@ package com.oracle.truffle.sl.nodes; import com.oracle.truffle.api.*; -import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.utilities.*; import com.oracle.truffle.sl.runtime.*; public final class ReadFunctionNode extends TypedNode { private final SLFunctionRegistry registry; private final String name; - - @CompilationFinal private boolean seenInvalidFunction; + private final BranchProfile invalidFunction = new BranchProfile(); public ReadFunctionNode(SLFunctionRegistry registry, String name) { this.registry = registry; @@ -50,14 +49,8 @@ if (target != null) { return target; } - if (!seenInvalidFunction) { - CompilerDirectives.transferToInterpreter(); - seenInvalidFunction = true; - } - if (seenInvalidFunction) { - throw new RuntimeException("Function with name '" + name + "' not found."); - } - return null; + invalidFunction.enter(); + throw new RuntimeException("Function with name '" + name + "' not found."); } } diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/WhileNode.java --- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/WhileNode.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/WhileNode.java Tue Dec 17 21:26:42 2013 -0800 @@ -23,6 +23,7 @@ package com.oracle.truffle.sl.nodes; import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.utilities.*; public class WhileNode extends StatementNode { @@ -33,6 +34,11 @@ private final BreakException breakTarget; private final ContinueException continueTarget; + private final BranchProfile continueMismatch = new BranchProfile(); + private final BranchProfile continueMatch = new BranchProfile(); + private final BranchProfile breakMismatch = new BranchProfile(); + private final BranchProfile breakMatch = new BranchProfile(); + public WhileNode(ConditionNode condition, StatementNode body) { this.condition = adoptChild(condition); this.body = adoptChild(body); @@ -49,15 +55,19 @@ body.executeVoid(frame); } catch (ContinueException ex) { if (ex != continueTarget) { + continueMismatch.enter(); throw ex; } + continueMatch.enter(); // Fall through to next loop iteration. } } } catch (BreakException ex) { if (ex != breakTarget) { + breakMismatch.enter(); throw ex; } + breakMatch.enter(); // Done executing this loop, exit method to execute statement following the loop. } } diff -r 69d2e4baa215 -r 9c88b1138240 graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/WriteLocalNode.java --- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/WriteLocalNode.java Tue Dec 17 20:22:45 2013 -0800 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/WriteLocalNode.java Tue Dec 17 21:26:42 2013 -0800 @@ -22,6 +22,7 @@ */ package com.oracle.truffle.sl.nodes; +import com.oracle.truffle.api.*; import com.oracle.truffle.api.dsl.*; import com.oracle.truffle.api.frame.*; @@ -36,22 +37,51 @@ this(node.slot); } - @Specialization + @Specialization(guards = "isIntKind") public int write(VirtualFrame frame, int right) { frame.setInt(slot, right); return right; } - @Specialization + @Specialization(guards = "isBooleanKind") public boolean write(VirtualFrame frame, boolean right) { frame.setBoolean(slot, right); return right; } - @Specialization + @Specialization(guards = "isObjectKind") public Object writeGeneric(VirtualFrame frame, Object right) { frame.setObject(slot, right); return right; } + protected final boolean isIntKind() { + return isKind(FrameSlotKind.Int); + } + + protected final boolean isBooleanKind() { + return isKind(FrameSlotKind.Boolean); + } + + protected final boolean isObjectKind() { + if (slot.getKind() != FrameSlotKind.Object) { + CompilerDirectives.transferToInterpreter(); + slot.setKind(FrameSlotKind.Object); + } + return true; + } + + private boolean isKind(FrameSlotKind kind) { + return slot.getKind() == kind || initialSetKind(kind); + } + + private boolean initialSetKind(FrameSlotKind kind) { + if (slot.getKind() == FrameSlotKind.Illegal) { + CompilerDirectives.transferToInterpreter(); + slot.setKind(kind); + return true; + } + return false; + } + } diff -r 69d2e4baa215 -r 9c88b1138240 mx/mx_graal.py --- a/mx/mx_graal.py Tue Dec 17 20:22:45 2013 -0800 +++ b/mx/mx_graal.py Tue Dec 17 21:26:42 2013 -0800 @@ -808,7 +808,7 @@ f_testfile.close() harness(projectscp, vmArgs) -def _unittest(args, annotations): +def _unittest(args, annotations, prefixcp=""): mxdir = dirname(__file__) name = 'JUnitWrapper' javaSource = join(mxdir, name + '.java') @@ -830,9 +830,9 @@ if len(testclasses) == 1: # Execute Junit directly when one test is being run. This simplifies # replaying the VM execution in a native debugger (e.g., gdb). - vm(prefixArgs + vmArgs + ['-cp', projectscp, 'org.junit.runner.JUnitCore'] + testclasses) + vm(prefixArgs + vmArgs + ['-cp', prefixcp + projectscp, 'org.junit.runner.JUnitCore'] + testclasses) else: - vm(prefixArgs + vmArgs + ['-cp', projectscp + os.pathsep + mxdir, name] + [testfile]) + vm(prefixArgs + vmArgs + ['-cp', prefixcp + projectscp + os.pathsep + mxdir, name] + [testfile]) try: _run_tests(args, harness, annotations, testfile) @@ -1348,9 +1348,17 @@ def trufflejar(args=None): """make truffle.jar""" + + # Test with the built classes + _unittest(["com.oracle.truffle.api.test", "com.oracle.truffle.api.dsl.test"], ['@Test', '@LongTest', '@Parameters']) + # We use the DSL processor as the starting point for the classpath - this # therefore includes the DSL processor, the DSL and the API. - packagejar(mx.classpath("com.oracle.truffle.dsl.processor").split(os.pathsep), "truffle.jar", None) + packagejar(mx.classpath("com.oracle.truffle.dsl.processor").split(os.pathsep), "truffle.jar", None, "com.oracle.truffle.dsl.processor.TruffleProcessor") + + # Test with the JAR + _unittest(["com.oracle.truffle.api.test", "com.oracle.truffle.api.dsl.test"], ['@Test', '@LongTest', '@Parameters'], "truffle.jar:") + def isGraalEnabled(vm): return vm != 'original' and not vm.endswith('nograal') @@ -1444,8 +1452,8 @@ mx.distribution('GRAAL').add_update_listener(_installGraalJarInJdks) -def packagejar(classpath, outputFile, mainClass): - prefix = '' if mx.get_os() != 'windows' else '\\??\\' # long file name hack +def packagejar(classpath, outputFile, mainClass=None, annotationProcessor=None, stripDebug=False): + prefix = '' if mx.get_os() != 'windows' else '\\??\\' # long file name hack print "creating", outputFile filecount, totalsize = 0, 0 with zipfile.ZipFile(outputFile, 'w', zipfile.ZIP_DEFLATED) as zf: @@ -1453,6 +1461,8 @@ if mainClass != None: manifest += "Main-Class: %s\n\n" % (mainClass) zf.writestr("META-INF/MANIFEST.MF", manifest) + if annotationProcessor != None: + zf.writestr("META-INF/services/javax.annotation.processing.Processor", annotationProcessor) for cp in classpath: print "+", cp if cp.endswith(".jar"): @@ -1465,7 +1475,7 @@ for root, _, files in os.walk(cp): for f in files: fullname = os.path.join(root, f) - arcname = fullname[len(cp)+1:].replace('\\', '/') + arcname = fullname[len(cp) + 1:].replace('\\', '/') if f.endswith(".class"): zf.write(prefix + fullname, arcname) @@ -1473,5 +1483,5 @@ filecount += 1 totalsize += zi.file_size print "%d files (total size: %.2f kB, jar size: %.2f kB)" % (filecount, totalsize / 1e3, os.path.getsize(outputFile) / 1e3) - mx.run([mx.exe_suffix(join(mx.java().jdk, 'bin', 'pack200')), '-r', '-G', outputFile]) + mx.run([mx.exe_suffix(join(mx.java().jdk, 'bin', 'pack200')), '-r'] + (['-G'] if stripDebug else []) + [outputFile]) print "repacked jar size: %.2f kB" % (os.path.getsize(outputFile) / 1e3) diff -r 69d2e4baa215 -r 9c88b1138240 mx/projects --- a/mx/projects Tue Dec 17 20:22:45 2013 -0800 +++ b/mx/projects Tue Dec 17 21:26:42 2013 -0800 @@ -1,5 +1,5 @@ # The format of this file is described in the documentation for my.py. - +mxversion=1.0 suite=graal library@JDK_TOOLS@path=${JAVA_HOME}/lib/tools.jar @@ -162,7 +162,7 @@ # graal.hotspot.hsail project@com.oracle.graal.hotspot.hsail@subDir=graal project@com.oracle.graal.hotspot.hsail@sourceDirs=src -project@com.oracle.graal.hotspot.hsail@dependencies=com.oracle.graal.compiler.hsail,com.oracle.graal.hotspot +project@com.oracle.graal.hotspot.hsail@dependencies=com.oracle.graal.replacements.hsail,com.oracle.graal.hotspot project@com.oracle.graal.hotspot.hsail@checkstyle=com.oracle.graal.graph project@com.oracle.graal.hotspot.hsail@annotationProcessors=com.oracle.graal.service.processor project@com.oracle.graal.hotspot.hsail@javaCompliance=1.7 @@ -311,6 +311,14 @@ project@com.oracle.graal.replacements.amd64@javaCompliance=1.7 project@com.oracle.graal.replacements.amd64@workingSets=Graal,Replacements,AMD64 +# graal.replacements.hsail +project@com.oracle.graal.replacements.hsail@subDir=graal +project@com.oracle.graal.replacements.hsail@sourceDirs=src +project@com.oracle.graal.replacements.hsail@dependencies=com.oracle.graal.compiler.hsail +project@com.oracle.graal.replacements.hsail@checkstyle=com.oracle.graal.graph +project@com.oracle.graal.replacements.hsail@javaCompliance=1.7 +project@com.oracle.graal.replacements.hsail@workingSets=Graal,Replacements,HSAIL + # graal.replacements.test project@com.oracle.graal.replacements.test@subDir=graal project@com.oracle.graal.replacements.test@sourceDirs=src diff -r 69d2e4baa215 -r 9c88b1138240 mxtool/mx.py --- a/mxtool/mx.py Tue Dec 17 20:22:45 2013 -0800 +++ b/mxtool/mx.py Tue Dec 17 21:26:42 2013 -0800 @@ -675,6 +675,7 @@ self.imports = [] self.commands = None self.primary = primary + self.requiredMxVersion = None self.name = _suitename(mxDir) # validated in _load_projects if load: # load suites bottom up to make sure command overriding works properly @@ -714,10 +715,17 @@ parts = key.split('@') if len(parts) == 1: - if parts[0] != 'suite': + if parts[0] == 'suite': + if self.name != value: + abort('suite name in project file does not match ' + _suitename(self.mxDir)) + elif parts[0] == 'mxversion': + try: + self.requiredMxVersion = JavaVersion(value) + except AssertionError as ae: + abort('Exception while parsing "mxversion" in project file: ' + str(ae)) + else: abort('Single part property must be "suite": ' + key) - if self.name != value: - abort('suite name in project file does not match ' + _suitename(self.mxDir)) + continue if len(parts) != 3: abort('Property name does not have 3 parts separated by "@": ' + key) @@ -889,6 +897,10 @@ def _post_init(self, opts): self._load_projects() + if self.requiredMxVersion is None: + warn("This suite does not express any required mx version. Consider adding 'mxversion=' to your projects file.") + elif self.requiredMxVersion > version: + abort("This suite requires mx version " + str(self.requiredMxVersion) + " while your current mx verion is " + str(version) + ". Please update mx.") # set the global data structures, checking for conflicts unless _check_global_structures is False for p in self.projects: existing = _projects.get(p.name) @@ -1552,7 +1564,7 @@ def __init__(self, versionString): validChar = r'[\x21-\x25\x27-\x29\x2c\x2f-\x5e\x60-\x7f]' separator = r'[.\-_]' - m = re.match(validChar + '+(' + separator + validChar + '+)*', versionString) + m = re.match("^" + validChar + '+(' + separator + validChar + '+)*$', versionString) assert m is not None, 'not a recognized version string: ' + versionString self.versionString = versionString self.parts = [int(f) if f.isdigit() else f for f in re.split(separator, versionString)] @@ -4516,6 +4528,8 @@ # no need to show the stack trace when the user presses CTRL-C abort(1) +version = JavaVersion("1.0") + if __name__ == '__main__': # rename this module as 'mx' so it is not imported twice by the commands.py modules sys.modules['mx'] = sys.modules.pop('__main__') diff -r 69d2e4baa215 -r 9c88b1138240 src/share/vm/compiler/compileBroker.cpp --- a/src/share/vm/compiler/compileBroker.cpp Tue Dec 17 20:22:45 2013 -0800 +++ b/src/share/vm/compiler/compileBroker.cpp Tue Dec 17 21:26:42 2013 -0800 @@ -2311,7 +2311,7 @@ } } -void CompileBroker::print_times() { +void CompileBroker::print_times(bool per_compiler, bool aggregate) { #ifdef GRAAL elapsedTimer standard_compilation; elapsedTimer total_compilation; @@ -2331,7 +2331,7 @@ for (unsigned int i = 0; i < sizeof(_compilers) / sizeof(AbstractCompiler*); i++) { AbstractCompiler* comp = _compilers[i]; if (comp != NULL) { - if (!printedHeader) { + if (per_compiler && aggregate && !printedHeader) { printedHeader = true; tty->cr(); tty->print_cr("Individual compiler times (for compiled methods only)"); @@ -2352,11 +2352,13 @@ nmethods_size += stats->_nmethods_size; nmethods_code_size += stats->_nmethods_code_size; + if (per_compiler) { tty->print_cr(" %s {speed: %d bytes/s; standard: %6.3f s, %d bytes, %d methods; osr: %6.3f s, %d bytes, %d methods; nmethods_size: %d bytes; nmethods_code_size: %d bytes}", comp->name(), stats->bytes_per_second(), stats->_standard._time.seconds(), stats->_standard._bytes, stats->_standard._count, stats->_osr._time.seconds(), stats->_osr._bytes, stats->_osr._count, stats->_nmethods_size, stats->_nmethods_code_size); + } } } total_compile_count = osr_compile_count + standard_compile_count; @@ -2378,6 +2380,9 @@ int nmethods_code_size = CompileBroker::_sum_nmethod_size; #endif + if (!aggregate) { + return; + } tty->cr(); tty->print_cr("Accumulated compiler times (for compiled methods only)"); tty->print_cr("------------------------------------------------"); diff -r 69d2e4baa215 -r 9c88b1138240 src/share/vm/compiler/compileBroker.hpp --- a/src/share/vm/compiler/compileBroker.hpp Tue Dec 17 20:22:45 2013 -0800 +++ b/src/share/vm/compiler/compileBroker.hpp Tue Dec 17 21:26:42 2013 -0800 @@ -434,7 +434,7 @@ static void mark_on_stack(); // Print a detailed accounting of compilation time - static void print_times(); + static void print_times(bool per_compiler = true, bool aggregate = true); // Debugging output for failure static void print_last_compile(); diff -r 69d2e4baa215 -r 9c88b1138240 src/share/vm/graal/graalCompiler.cpp --- a/src/share/vm/graal/graalCompiler.cpp Tue Dec 17 20:22:45 2013 -0800 +++ b/src/share/vm/graal/graalCompiler.cpp Tue Dec 17 21:26:42 2013 -0800 @@ -115,6 +115,12 @@ // Avoid -Xcomp and -Xbatch problems by turning on interpreter and background compilation for bootstrapping. FlagSetting a(UseInterpreter, true); FlagSetting b(BackgroundCompilation, true); +#ifndef PRODUCT + // Turn off CompileTheWorld during bootstrap so that a counter overflow event + // triggers further compilation (see NonTieredCompPolicy::event()) hence + // allowing a complete bootstrap + FlagSetting c(CompileTheWorld, false); +#endif VMToCompiler::bootstrap(); } @@ -274,18 +280,6 @@ return VMToCompiler::createJavaField(field_holder, name, field_type, offset, flags, false, CHECK_NH); } -Handle GraalCompiler::createHotSpotResolvedObjectType(methodHandle method, TRAPS) { - KlassHandle klass = method->method_holder(); - oop java_class = klass->java_mirror(); - oop graal_mirror = java_lang_Class::graal_mirror(java_class); - if (graal_mirror != NULL) { - assert(graal_mirror->is_a(HotSpotResolvedObjectType::klass()), "unexpected class..."); - return graal_mirror; - } - Handle name = java_lang_String::create_from_symbol(klass->name(), CHECK_NH); - return GraalCompiler::createHotSpotResolvedObjectType(klass, name, CHECK_NH); -} - Handle GraalCompiler::createHotSpotResolvedObjectType(KlassHandle klass, Handle name, TRAPS) { oop java_class = klass->java_mirror(); oop graal_mirror = java_lang_Class::graal_mirror(java_class); diff -r 69d2e4baa215 -r 9c88b1138240 src/share/vm/graal/graalCompiler.hpp --- a/src/share/vm/graal/graalCompiler.hpp Tue Dec 17 20:22:45 2013 -0800 +++ b/src/share/vm/graal/graalCompiler.hpp Tue Dec 17 21:26:42 2013 -0800 @@ -78,7 +78,6 @@ static Handle get_JavaField(int offset, int flags, Symbol* field_name, Handle field_holder, Handle field_type, TRAPS); static Handle createHotSpotResolvedObjectType(KlassHandle klass, Handle name, TRAPS); - static Handle createHotSpotResolvedObjectType(methodHandle method, TRAPS); void exit(); diff -r 69d2e4baa215 -r 9c88b1138240 src/share/vm/graal/graalCompilerToVM.cpp --- a/src/share/vm/graal/graalCompilerToVM.cpp Tue Dec 17 20:22:45 2013 -0800 +++ b/src/share/vm/graal/graalCompilerToVM.cpp Tue Dec 17 21:26:42 2013 -0800 @@ -171,33 +171,14 @@ return (jlong) (address) method(); } -C2V_VMENTRY(jlong, getUniqueConcreteMethod, (JNIEnv *, jobject, jlong metaspace_method, jobject resultHolder)) +C2V_VMENTRY(jlong, findUniqueConcreteMethod, (JNIEnv *, jobject, jlong metaspace_method)) methodHandle method = asMethod(metaspace_method); KlassHandle holder = method->method_holder(); - if (holder->is_interface()) { - // Cannot trust interfaces. Because of: - // interface I { void foo(); } - // class A { public void foo() {} } - // class B extends A implements I { } - // class C extends B { public void foo() { } } - // class D extends B { } - // Would lead to identify C.foo() as the unique concrete method for I.foo() without seeing A.foo(). - return 0L; - } - methodHandle ucm; - { - ResourceMark rm; - MutexLocker locker(Compile_lock); - ucm = Dependencies::find_unique_concrete_method(holder(), method()); - } - - if (ucm.is_null()) { - return 0L; - } - - Handle type = GraalCompiler::createHotSpotResolvedObjectType(ucm(), CHECK_0); - objArrayOop(JNIHandles::resolve(resultHolder))->obj_at_put(0, type()); - return (jlong) (address) ucm(); + assert(!holder->is_interface(), "should be handled in Java code"); + ResourceMark rm; + MutexLocker locker(Compile_lock); + Method* ucm = Dependencies::find_unique_concrete_method(holder(), method()); + return (jlong) (address) ucm; C2V_END C2V_VMENTRY(jobject, getUniqueImplementor, (JNIEnv *, jobject, jobject interface_type)) @@ -670,6 +651,10 @@ } C2V_END +C2V_VMENTRY(void, printCompilationStatistics, (JNIEnv *jniEnv, jobject, jboolean per_compiler, jboolean aggregate)) +CompileBroker::print_times(per_compiler, aggregate); +C2V_END + C2V_VMENTRY(void, resetCompilationStatistics, (JNIEnv *jniEnv, jobject)) CompilerStatistics* stats = GraalCompiler::instance()->stats(); stats->_standard._time.reset(); @@ -885,7 +870,7 @@ {CC"initializeBytecode", CC"("METASPACE_METHOD"[B)[B", FN_PTR(initializeBytecode)}, {CC"exceptionTableStart", CC"("METASPACE_METHOD")J", FN_PTR(exceptionTableStart)}, {CC"hasBalancedMonitors", CC"("METASPACE_METHOD")Z", FN_PTR(hasBalancedMonitors)}, - {CC"getUniqueConcreteMethod", CC"("METASPACE_METHOD"["HS_RESOLVED_TYPE")"METASPACE_METHOD, FN_PTR(getUniqueConcreteMethod)}, + {CC"findUniqueConcreteMethod", CC"("METASPACE_METHOD")"METASPACE_METHOD, FN_PTR(findUniqueConcreteMethod)}, {CC"getUniqueImplementor", CC"("HS_RESOLVED_TYPE")"RESOLVED_TYPE, FN_PTR(getUniqueImplementor)}, {CC"getStackTraceElement", CC"("METASPACE_METHOD"I)"STACK_TRACE_ELEMENT, FN_PTR(getStackTraceElement)}, {CC"initializeMethod", CC"("METASPACE_METHOD HS_RESOLVED_METHOD")V", FN_PTR(initializeMethod)}, @@ -908,6 +893,7 @@ {CC"initializeConfiguration", CC"("HS_CONFIG")V", FN_PTR(initializeConfiguration)}, {CC"installCode0", CC"("HS_COMPILED_CODE HS_INSTALLED_CODE"[Z)I", FN_PTR(installCode0)}, {CC"notifyCompilationStatistics", CC"(I"HS_RESOLVED_METHOD"ZIJJ"HS_INSTALLED_CODE")V", FN_PTR(notifyCompilationStatistics)}, + {CC"printCompilationStatistics", CC"(ZZ)V", FN_PTR(printCompilationStatistics)}, {CC"resetCompilationStatistics", CC"()V", FN_PTR(resetCompilationStatistics)}, {CC"disassembleCodeBlob", CC"(J)"STRING, FN_PTR(disassembleCodeBlob)}, {CC"executeCompiledMethodVarargs", CC"(["OBJECT HS_INSTALLED_CODE")"OBJECT, FN_PTR(executeCompiledMethodVarargs)}, diff -r 69d2e4baa215 -r 9c88b1138240 src/share/vm/runtime/vframe.cpp --- a/src/share/vm/runtime/vframe.cpp Tue Dec 17 20:22:45 2013 -0800 +++ b/src/share/vm/runtime/vframe.cpp Tue Dec 17 21:26:42 2013 -0800 @@ -266,7 +266,7 @@ // Get oopmap describing oops and int for current bci InterpreterOopMap oop_mask; - if (TraceDeoptimization && Verbose) { + if ((TraceDeoptimization && Verbose) GRAAL_ONLY( || PrintDeoptimizationDetails)) { methodHandle m_h(thread(), method()); OopMapCache::compute_one_oop_map(m_h, bci(), &oop_mask); } else { @@ -333,7 +333,7 @@ InterpreterOopMap oop_mask; // Get oopmap describing oops and int for current bci - if (TraceDeoptimization && Verbose) { + if ((TraceDeoptimization && Verbose) GRAAL_ONLY( || PrintDeoptimizationDetails)) { methodHandle m_h(method()); OopMapCache::compute_one_oop_map(m_h, bci(), &oop_mask); } else {