# HG changeset patch # User Stefan Anzinger # Date 1431456964 -7200 # Node ID 25bd9e2320de222e749e44a54818323d4c8eecad # Parent d24e4f349cbc16bdf0aa4f6c1c7227a3cc694458# Parent a818a6a57ef454ee1ef711e1cdb9943a2af54efc Merge diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/BytecodePosition.java --- a/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/BytecodePosition.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/BytecodePosition.java Tue May 12 20:56:04 2015 +0200 @@ -109,4 +109,15 @@ public BytecodePosition getCaller() { return caller; } + + /* + * Adds a caller to the current position returning the new position. + */ + public BytecodePosition addCaller(BytecodePosition link) { + if (getCaller() == null) { + return new BytecodePosition(link, getMethod(), getBCI()); + } else { + return new BytecodePosition(getCaller().addCaller(link), getMethod(), getBCI()); + } + } } diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/CodeUtil.java --- a/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/CodeUtil.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/CodeUtil.java Tue May 12 20:56:04 2015 +0200 @@ -294,6 +294,9 @@ */ public static StringBuilder append(StringBuilder sb, BytecodeFrame frame) { MetaUtil.appendLocation(sb.append("at "), frame.getMethod(), frame.getBCI()); + assert sb.charAt(sb.length() - 1) == ']'; + sb.deleteCharAt(sb.length() - 1); + sb.append(", duringCall: ").append(frame.duringCall).append(", rethrow: ").append(frame.rethrowException).append(']'); if (frame.values != null && frame.values.length > 0) { sb.append(NEW_LINE); String table = tabulateValues(frame); diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.api.meta.test/src/com/oracle/graal/api/meta/test/TestResolvedJavaMethod.java --- a/graal/com.oracle.graal.api.meta.test/src/com/oracle/graal/api/meta/test/TestResolvedJavaMethod.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.api.meta.test/src/com/oracle/graal/api/meta/test/TestResolvedJavaMethod.java Tue May 12 20:56:04 2015 +0200 @@ -312,6 +312,25 @@ } } + @Test + public void isJavaLangObjectInitTest() throws NoSuchMethodException { + ResolvedJavaMethod method = metaAccess.lookupJavaMethod(Object.class.getConstructor()); + assertTrue(method.isJavaLangObjectInit()); + for (Map.Entry e : methods.entrySet()) { + ResolvedJavaMethod m = e.getValue(); + assertFalse(m.isJavaLangObjectInit()); + } + for (Map.Entry, ResolvedJavaMethod> e : constructors.entrySet()) { + ResolvedJavaMethod m = e.getValue(); + Constructor key = e.getKey(); + if (key.getDeclaringClass() == Object.class && key.getParameters().length == 0) { + assertTrue(m.isJavaLangObjectInit()); + } else { + assertFalse(m.isJavaLangObjectInit()); + } + } + } + private Method findTestMethod(Method apiMethod) { String testName = apiMethod.getName() + "Test"; for (Method m : getClass().getDeclaredMethods()) { diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/LIRKind.java --- a/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/LIRKind.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/LIRKind.java Tue May 12 20:56:04 2015 +0200 @@ -133,15 +133,49 @@ */ public static LIRKind merge(Value... inputs) { assert inputs.length > 0; + LIRKind mergeKind = null; + for (Value input : inputs) { LIRKind kind = input.getLIRKind(); + + assert mergeKind == null || verifyMoveKinds(mergeKind, kind) : String.format("Input kinds do not match %s vs. %s", mergeKind, kind); + if (kind.isDerivedReference()) { + /** + * Kind is a derived reference therefore the result can only be also a derived + * reference. + */ return kind; } + if (mergeKind == null) { + mergeKind = kind; + continue; + } + + if (kind.isValue()) { + /* Kind is a value. */ + if (mergeKind.referenceMask != 0) { + /* + * Inputs consists of values and references. Make the result a derived + * reference. + */ + return mergeKind.makeDerivedReference(); + } + /* Check that other inputs are also values. */ + } else { + /* Kind is a reference. */ + if (mergeKind.referenceMask != kind.referenceMask) { + /* + * Reference maps do not match so the result can only be a derived reference. + */ + return mergeKind.makeDerivedReference(); + } + } + } // all inputs are values or references, just return one of them - return inputs[0].getLIRKind(); + return mergeKind; } /** @@ -277,4 +311,26 @@ LIRKind other = (LIRKind) obj; return platformKind == other.platformKind && referenceMask == other.referenceMask; } + + public static boolean verifyMoveKinds(LIRKind dst, LIRKind src) { + if (src.equals(dst)) { + return true; + } + /* + * TODO(je,rs) What we actually want is toStackKind(src.getPlatformKind()).equals( + * dst.getPlatformKind()) but due to the handling of sub-integer at the current point + * (phi-)moves from e.g. integer to short can happen. Therefore we compare stack kinds. + */ + if (toStackKind(src.getPlatformKind()).equals(toStackKind(dst.getPlatformKind()))) { + return !src.isDerivedReference() || dst.isDerivedReference(); + } + return false; + } + + private static PlatformKind toStackKind(PlatformKind platformKind) { + if (platformKind instanceof Kind) { + return ((Kind) platformKind).getStackKind(); + } + return platformKind; + } } diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/ResolvedJavaMethod.java --- a/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/ResolvedJavaMethod.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/ResolvedJavaMethod.java Tue May 12 20:56:04 2015 +0200 @@ -288,4 +288,11 @@ default boolean hasReceiver() { return !isStatic(); } + + /** + * Determines if this method is {@link java.lang.Object#Object()}. + */ + default boolean isJavaLangObjectInit() { + return getDeclaringClass().isJavaLangObject() && getName().equals(""); + } } diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64LIRGenerator.java --- a/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64LIRGenerator.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64LIRGenerator.java Tue May 12 20:56:04 2015 +0200 @@ -81,6 +81,7 @@ private static final RegisterValue RCX_I = AMD64.rcx.asValue(LIRKind.value(Kind.Int)); private AMD64SpillMoveFactory moveFactory; + private Map categorized; private static class RegisterBackupPair { public final Register register; @@ -93,7 +94,6 @@ } private class AMD64SpillMoveFactory implements LIRGeneratorTool.SpillMoveFactory { - private Map categorized; @Override public LIRInstruction createMove(AllocatableValue result, Value input) { @@ -102,34 +102,9 @@ @Override public LIRInstruction createStackMove(AllocatableValue result, Value input) { - RegisterBackupPair backup = getScratchRegister(input.getPlatformKind()); - return new AMD64StackMove(result, input, backup.register, backup.backupSlot); + return AMD64LIRGenerator.this.createStackMove(result, input); } - private RegisterBackupPair getScratchRegister(PlatformKind kind) { - PlatformKind.Key key = kind.getKey(); - if (categorized == null) { - categorized = new HashMap<>(); - } else if (categorized.containsKey(key)) { - return categorized.get(key); - } - - FrameMapBuilder frameMapBuilder = getResult().getFrameMapBuilder(); - RegisterConfig registerConfig = frameMapBuilder.getRegisterConfig(); - - Register[] availableRegister = registerConfig.filterAllocatableRegisters(kind, registerConfig.getAllocatableRegisters()); - assert availableRegister != null && availableRegister.length > 1; - Register scratchRegister = availableRegister[0]; - - Architecture arch = frameMapBuilder.getCodeCache().getTarget().arch; - LIRKind largestKind = LIRKind.value(arch.getLargestStorableKind(scratchRegister.getRegisterCategory())); - VirtualStackSlot backupSlot = frameMapBuilder.allocateSpillSlot(largestKind); - - RegisterBackupPair value = new RegisterBackupPair(scratchRegister, backupSlot); - categorized.put(key, value); - - return value; - } } public AMD64LIRGenerator(LIRKindTool lirKindTool, Providers providers, CallingConvention cc, LIRGenerationResult lirGenRes) { @@ -165,6 +140,42 @@ } } + protected LIRInstruction createStackMove(AllocatableValue result, Value input) { + RegisterBackupPair backup = getScratchRegister(input.getPlatformKind()); + Register scratchRegister = backup.register; + StackSlotValue backupSlot = backup.backupSlot; + return createStackMove(result, input, scratchRegister, backupSlot); + } + + protected LIRInstruction createStackMove(AllocatableValue result, Value input, Register scratchRegister, StackSlotValue backupSlot) { + return new AMD64StackMove(result, input, scratchRegister, backupSlot); + } + + protected RegisterBackupPair getScratchRegister(PlatformKind kind) { + PlatformKind.Key key = kind.getKey(); + if (categorized == null) { + categorized = new HashMap<>(); + } else if (categorized.containsKey(key)) { + return categorized.get(key); + } + + FrameMapBuilder frameMapBuilder = getResult().getFrameMapBuilder(); + RegisterConfig registerConfig = frameMapBuilder.getRegisterConfig(); + + Register[] availableRegister = registerConfig.filterAllocatableRegisters(kind, registerConfig.getAllocatableRegisters()); + assert availableRegister != null && availableRegister.length > 1; + Register scratchRegister = availableRegister[0]; + + Architecture arch = frameMapBuilder.getCodeCache().getTarget().arch; + LIRKind largestKind = LIRKind.value(arch.getLargestStorableKind(scratchRegister.getRegisterCategory())); + VirtualStackSlot backupSlot = frameMapBuilder.allocateSpillSlot(largestKind); + + RegisterBackupPair value = new RegisterBackupPair(scratchRegister, backupSlot); + categorized.put(key, value); + + return value; + } + @Override public void emitMove(AllocatableValue dst, Value src) { append(createMove(dst, src)); diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java --- a/graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java Tue May 12 20:56:04 2015 +0200 @@ -74,7 +74,7 @@ @Override public LIRInstruction createStackMove(AllocatableValue result, Value input) { - return new SPARCStackMove(result, input); + return SPARCLIRGenerator.this.createStackMove(result, input); } } @@ -117,6 +117,10 @@ } } + protected LIRInstruction createStackMove(AllocatableValue result, Value input) { + return new SPARCStackMove(result, input); + } + @Override public void emitMove(AllocatableValue dst, Value src) { append(createMove(dst, src)); diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/CopyOfVirtualizationTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/CopyOfVirtualizationTest.java Tue May 12 20:56:04 2015 +0200 @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.graal.compiler.test; + +import java.util.*; + +import org.junit.*; + +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.java.*; + +public class CopyOfVirtualizationTest extends GraalCompilerTest { + + @Override + protected boolean checkMidTierGraph(StructuredGraph graph) { + assertTrue(graph.getNodes().filter(node -> node instanceof NewArrayNode).count() == 0, "shouldn't require allocation in %s", graph); + return super.checkMidTierGraph(graph); + } + + public byte byteCopyOfVirtualization(int index) { + byte[] array = new byte[]{1, 2, 3, 4}; + return Arrays.copyOf(array, array.length)[index]; + } + + public short shortCopyOfVirtualization(int index) { + short[] array = new short[]{1, 2, 3, 4}; + return Arrays.copyOf(array, array.length)[index]; + } + + public char charCopyOfVirtualization(int index) { + char[] array = new char[]{1, 2, 3, 4}; + return Arrays.copyOf(array, array.length)[index]; + } + + public int intCopyOfVirtualization(int index) { + int[] array = new int[]{1, 2, 3, 4}; + return Arrays.copyOf(array, array.length)[index]; + } + + public long longCopyOfVirtualization(int index) { + long[] array = new long[]{1, 2, 3, 4}; + return Arrays.copyOf(array, array.length)[index]; + } + + public float floatCopyOfVirtualization(int index) { + float[] array = new float[]{1, 2, 3, 4}; + return Arrays.copyOf(array, array.length)[index]; + } + + public double doubleCopyOfVirtualization(int index) { + double[] array = new double[]{1, 2, 3, 4}; + return Arrays.copyOf(array, array.length)[index]; + } + + public Object objectCopyOfVirtualization(int index) { + Object[] array = new Object[]{1, 2, 3, 4}; + return Arrays.copyOf(array, array.length)[index]; + } + + // @Test + public void testCopyOfVirtualization() { + test("byteCopyOfVirtualization", 3); + test("shortCopyOfVirtualization", 3); + test("charCopyOfVirtualization", 3); + test("intCopyOfVirtualization", 3); + test("longCopyOfVirtualization", 3); + test("floatCopyOfVirtualization", 3); + test("doubleCopyOfVirtualization", 3); + test("objectCopyOfVirtualization", 3); + } + + static final byte[] byteArray = new byte[]{1, 2, 3, 4}; + + public byte byteCopyOfVirtualizableAllocation() { + return Arrays.copyOf(byteArray, byteArray.length)[3]; + } + + static final short[] shortArray = new short[]{1, 2, 3, 4}; + + public short shortCopyOfVirtualizableAllocation() { + return Arrays.copyOf(shortArray, shortArray.length)[3]; + } + + static final char[] charArray = new char[]{1, 2, 3, 4}; + + public char charCopyOfVirtualizableAllocation() { + return Arrays.copyOf(charArray, charArray.length)[3]; + } + + static final int[] intArray = new int[]{1, 2, 3, 4}; + + public int intCopyOfVirtualizableAllocation() { + return Arrays.copyOf(intArray, intArray.length)[3]; + } + + static final long[] longArray = new long[]{1, 2, 3, 4}; + + public long longCopyOfVirtualizableAllocation() { + return Arrays.copyOf(longArray, longArray.length)[3]; + } + + static final float[] floatArray = new float[]{1, 2, 3, 4}; + + public float floatCopyOfVirtualizableAllocation() { + return Arrays.copyOf(floatArray, floatArray.length)[3]; + } + + static final double[] doubleArray = new double[]{1, 2, 3, 4}; + + public double doubleCopyOfVirtualizableAllocation() { + return Arrays.copyOf(doubleArray, doubleArray.length)[3]; + } + + static final Object[] objectArray = new Object[]{1, 2, 3, 4}; + + public Object objectCopyOfVirtualizableAllocation() { + return Arrays.copyOf(objectArray, objectArray.length)[3]; + } + + @Test + public void testCopyOfVirtualizableAllocation() { + test("byteCopyOfVirtualizableAllocation"); + test("shortCopyOfVirtualizableAllocation"); + test("charCopyOfVirtualizableAllocation"); + test("intCopyOfVirtualizableAllocation"); + test("longCopyOfVirtualizableAllocation"); + test("floatCopyOfVirtualizableAllocation"); + test("doubleCopyOfVirtualizableAllocation"); + test("objectCopyOfVirtualizableAllocation"); + } +} diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java Tue May 12 20:56:04 2015 +0200 @@ -321,7 +321,7 @@ result.append("\n"); for (Node node : schedule.getBlockToNodesMap().get(block)) { if (node instanceof ValueNode && node.isAlive()) { - if (!excludeVirtual || !(node instanceof VirtualObjectNode || node instanceof ProxyNode)) { + if (!excludeVirtual || !(node instanceof VirtualObjectNode || node instanceof ProxyNode || node instanceof InfopointNode)) { if (node instanceof ConstantNode) { String name = checkConstants ? node.toString(Verbosity.Name) : node.getClass().getSimpleName(); String str = name + (excludeVirtual ? "\n" : " (" + node.getUsageCount() + ")\n"); diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/TestCopyOfVirtualization.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/TestCopyOfVirtualization.java Tue May 12 20:55:48 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,152 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package com.oracle.graal.compiler.test; - -import java.util.*; - -import org.junit.*; - -import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.java.*; - -public class TestCopyOfVirtualization extends GraalCompilerTest { - - @Override - protected boolean checkMidTierGraph(StructuredGraph graph) { - assertTrue(graph.getNodes().filter(node -> node instanceof NewArrayNode).count() == 0, "shouldn't require allocation in %s", graph); - return super.checkMidTierGraph(graph); - } - - public byte byteCopyOfVirtualization(int index) { - byte[] array = new byte[]{1, 2, 3, 4}; - return Arrays.copyOf(array, array.length)[index]; - } - - public short shortCopyOfVirtualization(int index) { - short[] array = new short[]{1, 2, 3, 4}; - return Arrays.copyOf(array, array.length)[index]; - } - - public char charCopyOfVirtualization(int index) { - char[] array = new char[]{1, 2, 3, 4}; - return Arrays.copyOf(array, array.length)[index]; - } - - public int intCopyOfVirtualization(int index) { - int[] array = new int[]{1, 2, 3, 4}; - return Arrays.copyOf(array, array.length)[index]; - } - - public long longCopyOfVirtualization(int index) { - long[] array = new long[]{1, 2, 3, 4}; - return Arrays.copyOf(array, array.length)[index]; - } - - public float floatCopyOfVirtualization(int index) { - float[] array = new float[]{1, 2, 3, 4}; - return Arrays.copyOf(array, array.length)[index]; - } - - public double doubleCopyOfVirtualization(int index) { - double[] array = new double[]{1, 2, 3, 4}; - return Arrays.copyOf(array, array.length)[index]; - } - - public Object objectCopyOfVirtualization(int index) { - Object[] array = new Object[]{1, 2, 3, 4}; - return Arrays.copyOf(array, array.length)[index]; - } - - // @Test - public void testCopyOfVirtualization() { - test("byteCopyOfVirtualization", 3); - test("shortCopyOfVirtualization", 3); - test("charCopyOfVirtualization", 3); - test("intCopyOfVirtualization", 3); - test("longCopyOfVirtualization", 3); - test("floatCopyOfVirtualization", 3); - test("doubleCopyOfVirtualization", 3); - test("objectCopyOfVirtualization", 3); - } - - static final byte[] byteArray = new byte[]{1, 2, 3, 4}; - - public byte byteCopyOfVirtualizableAllocation() { - return Arrays.copyOf(byteArray, byteArray.length)[3]; - } - - static final short[] shortArray = new short[]{1, 2, 3, 4}; - - public short shortCopyOfVirtualizableAllocation() { - return Arrays.copyOf(shortArray, shortArray.length)[3]; - } - - static final char[] charArray = new char[]{1, 2, 3, 4}; - - public char charCopyOfVirtualizableAllocation() { - return Arrays.copyOf(charArray, charArray.length)[3]; - } - - static final int[] intArray = new int[]{1, 2, 3, 4}; - - public int intCopyOfVirtualizableAllocation() { - return Arrays.copyOf(intArray, intArray.length)[3]; - } - - static final long[] longArray = new long[]{1, 2, 3, 4}; - - public long longCopyOfVirtualizableAllocation() { - return Arrays.copyOf(longArray, longArray.length)[3]; - } - - static final float[] floatArray = new float[]{1, 2, 3, 4}; - - public float floatCopyOfVirtualizableAllocation() { - return Arrays.copyOf(floatArray, floatArray.length)[3]; - } - - static final double[] doubleArray = new double[]{1, 2, 3, 4}; - - public double doubleCopyOfVirtualizableAllocation() { - return Arrays.copyOf(doubleArray, doubleArray.length)[3]; - } - - static final Object[] objectArray = new Object[]{1, 2, 3, 4}; - - public Object objectCopyOfVirtualizableAllocation() { - return Arrays.copyOf(objectArray, objectArray.length)[3]; - } - - @Test - public void testCopyOfVirtualizableAllocation() { - test("byteCopyOfVirtualizableAllocation"); - test("shortCopyOfVirtualizableAllocation"); - test("charCopyOfVirtualizableAllocation"); - test("intCopyOfVirtualizableAllocation"); - test("longCopyOfVirtualizableAllocation"); - test("floatCopyOfVirtualizableAllocation"); - test("doubleCopyOfVirtualizableAllocation"); - test("objectCopyOfVirtualizableAllocation"); - } -} diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PartialEscapeAnalysisTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PartialEscapeAnalysisTest.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PartialEscapeAnalysisTest.java Tue May 12 20:56:04 2015 +0200 @@ -23,12 +23,14 @@ package com.oracle.graal.compiler.test.ea; import java.lang.ref.*; + import org.junit.*; import com.oracle.graal.compiler.test.*; import com.oracle.graal.graph.*; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.cfg.*; +import com.oracle.graal.nodes.extended.*; import com.oracle.graal.nodes.java.*; import com.oracle.graal.nodes.virtual.*; import com.oracle.graal.phases.common.*; @@ -191,6 +193,23 @@ assertTrue(graph.getNodes().filter(ReturnNode.class).first().result() == graph.getParameter(0)); } + public static int testBoxLoopSnippet(int n) { + Integer sum = 0; + for (Integer i = 0; i < n; i++) { + if (sum == null) { + sum = null; + } else { + sum += i; + } + } + return sum; + } + + @Test + public void testBoxLoop() { + testPartialEscapeAnalysis("testBoxLoopSnippet", 0, 0, BoxNode.class, UnboxNode.class); + } + @SafeVarargs protected final void testPartialEscapeAnalysis(String snippet, double expectedProbability, int expectedCount, Class... invalidNodeClasses) { prepareGraph(snippet, false); diff -r d24e4f349cbc -r 25bd9e2320de 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 May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java Tue May 12 20:56:04 2015 +0200 @@ -223,7 +223,7 @@ public static void emitBackEnd(StructuredGraph graph, Object stub, CallingConvention cc, ResolvedJavaMethod installedCodeOwner, Backend backend, TargetDescription target, T compilationResult, CompilationResultBuilderFactory factory, SchedulePhase schedule, RegisterConfig registerConfig, LIRSuites lirSuites) { - try (Scope s = Debug.scope("BackEnd"); DebugCloseable a = BackEnd.start()) { + try (Scope s = Debug.scope("BackEnd", schedule); DebugCloseable a = BackEnd.start()) { // Repeatedly run the LIR code generation pass to improve statistical profiling results. for (int i = 0; i < EmitLIRRepeatCount.getValue(); i++) { SchedulePhase dummySchedule = new SchedulePhase(); diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/NodeLIRBuilder.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/NodeLIRBuilder.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/NodeLIRBuilder.java Tue May 12 20:56:04 2015 +0200 @@ -212,7 +212,7 @@ private static boolean verifyPHIKind(LIRKind derivedKind, LIRKind phiKind) { assert derivedKind.getPlatformKind() != Kind.Object || !derivedKind.isDerivedReference(); PlatformKind phiPlatformKind = phiKind.getPlatformKind(); - assert derivedKind.getPlatformKind().equals(phiPlatformKind instanceof Kind ? ((Kind) phiPlatformKind).getStackKind() : phiPlatformKind); + assert derivedKind.equals(phiKind) || derivedKind.getPlatformKind().equals(phiPlatformKind instanceof Kind ? ((Kind) phiPlatformKind).getStackKind() : phiPlatformKind); return true; } diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/GraphBuilderConfiguration.java --- a/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/GraphBuilderConfiguration.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/GraphBuilderConfiguration.java Tue May 12 20:56:04 2015 +0200 @@ -229,6 +229,10 @@ return debugInfoMode.ordinal() >= DebugInfoMode.Full.ordinal(); } + public boolean insertSimpleDebugInfo() { + return debugInfoMode == DebugInfoMode.Simple; + } + public boolean doLivenessAnalysis() { return doLivenessAnalysis; } diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/GraphBuilderContext.java --- a/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/GraphBuilderContext.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/GraphBuilderContext.java Tue May 12 20:56:04 2015 +0200 @@ -41,38 +41,6 @@ public interface GraphBuilderContext { /** - * Information about a snippet or method substitution currently being processed by the graph - * builder. When in the scope of a replacement, the graph builder does not check the value kinds - * flowing through the JVM state since replacements can employ non-Java kinds to represent - * values such as raw machine words and pointers. - */ - public interface Replacement { - - /** - * Gets the method being replaced. - */ - ResolvedJavaMethod getOriginalMethod(); - - /** - * Gets the replacement method. - */ - ResolvedJavaMethod getReplacementMethod(); - - /** - * Determines if this replacement is being inlined as a compiler intrinsic. A compiler - * intrinsic is atomic with respect to deoptimization. Deoptimization within a compiler - * intrinsic will restart the interpreter at the intrinsified call. - */ - boolean isIntrinsic(); - - /** - * Determines if a call within the compilation scope of this replacement represents a call - * to the {@linkplain #getOriginalMethod() original} method. - */ - boolean isCallToOriginal(ResolvedJavaMethod method); - } - - /** * Raw operation for adding a node to the graph when neither {@link #add}, * {@link #addPush(ValueNode)} nor {@link #addPush(Kind, ValueNode)} can be used. * @@ -115,8 +83,8 @@ T equivalentValue = append(value); if (equivalentValue instanceof StateSplit) { StateSplit stateSplit = (StateSplit) equivalentValue; - if (stateSplit.stateAfter() == null) { - stateSplit.setStateAfter(createStateAfter()); + if (stateSplit.stateAfter() == null && stateSplit.hasSideEffect()) { + setStateAfter(stateSplit); } } return equivalentValue; @@ -149,8 +117,8 @@ push(kind.getStackKind(), equivalentValue); if (equivalentValue instanceof StateSplit) { StateSplit stateSplit = (StateSplit) equivalentValue; - if (stateSplit.stateAfter() == null) { - stateSplit.setStateAfter(createStateAfter()); + if (stateSplit.stateAfter() == null && stateSplit.hasSideEffect()) { + setStateAfter(stateSplit); } } return equivalentValue; @@ -194,9 +162,12 @@ /** * Creates a snap shot of the current frame state with the BCI of the instruction after the one - * currently being parsed. + * currently being parsed and assigns it to a given {@linkplain StateSplit#hasSideEffect() side + * effect} node. + * + * @param sideEffect a side effect node just appended to the graph */ - FrameState createStateAfter(); + void setStateAfter(StateSplit sideEffect); /** * Gets the parsing context for the method that inlines the method being parsed by this context. @@ -204,6 +175,18 @@ GraphBuilderContext getParent(); /** + * Gets the first ancestor parsing context that is not parsing a + * {@linkplain #parsingIntrinsic() intrinsic}. + */ + default GraphBuilderContext getNonReplacementAncestor() { + GraphBuilderContext ancestor = getParent(); + while (ancestor != null && ancestor.parsingIntrinsic()) { + ancestor = ancestor.getParent(); + } + return ancestor; + } + + /** * Gets the method currently being parsed. */ ResolvedJavaMethod getMethod(); @@ -235,15 +218,15 @@ /** * Determines if the current parsing context is a snippet or method substitution. */ - default boolean parsingReplacement() { - return getReplacement() != null; + default boolean parsingIntrinsic() { + return getIntrinsic() != null; } /** - * Gets the replacement of the current parsing context or {@code null} if not - * {@link #parsingReplacement() parsing a replacement}. + * Gets the intrinsic of the current parsing context or {@code null} if not + * {@link #parsingIntrinsic() parsing an intrinsic}. */ - Replacement getReplacement(); + IntrinsicContext getIntrinsic(); BailoutException bailout(String string); @@ -268,5 +251,4 @@ } return value; } - } diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InlineInvokePlugin.java --- a/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InlineInvokePlugin.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InlineInvokePlugin.java Tue May 12 20:56:04 2015 +0200 @@ -26,8 +26,9 @@ import com.oracle.graal.nodes.*; /** - * Plugin for specifying what is inlined during graph parsing or for post-processing non-inlined - * invocations that result in {@link Invoke} nodes. + * Plugin for specifying what is inlined during graph parsing. This plugin is also + * {@linkplain #notifyOfNoninlinedInvoke notified} of non-inlined invocations (i.e., those for which + * an {@link Invoke} node is created). */ public interface InlineInvokePlugin extends GraphBuilderPlugin { @@ -39,24 +40,14 @@ public final ResolvedJavaMethod methodToInline; /** - * Specifies if {@link #methodToInline} is to be considered a - * {@linkplain GraphBuilderContext.Replacement replacement} for the {@code method} passed to - * {@link InlineInvokePlugin#getInlineInfo}. - */ - public final boolean isReplacement; - - /** - * Specifies if {@link #methodToInline} is an intrinsic for the original method. If so, any - * {@link StateSplit} node created in the (recursive) inlining scope will be given a frame - * state that restarts the interpreter just before the intrinsified invocation. + * Specifies if {@link #methodToInline} is an intrinsic for the original method (i.e., the + * {@code method} passed to {@link InlineInvokePlugin#getInlineInfo}). */ public final boolean isIntrinsic; - public InlineInfo(ResolvedJavaMethod methodToInline, boolean isReplacement, boolean isIntrinsic) { + public InlineInfo(ResolvedJavaMethod methodToInline, boolean isIntrinsic) { this.methodToInline = methodToInline; this.isIntrinsic = isIntrinsic; - this.isReplacement = isReplacement; - assert !isIntrinsic || isReplacement : "cannot be an intrinsic without also being a replacement"; } } diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/IntrinsicContext.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/IntrinsicContext.java Tue May 12 20:56:04 2015 +0200 @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.graphbuilderconf; + +import static com.oracle.graal.api.code.BytecodeFrame.*; +import static com.oracle.graal.graphbuilderconf.IntrinsicContext.CompilationContext.*; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.nodes.*; + +/** + * An intrinsic is a substitute implementation of a Java method (or a bytecode in the case of + * snippets) that is itself implemented in Java. This interface provides information about the + * intrinsic currently being processed by the graph builder. + * + * When in the scope of an intrinsic, the graph builder does not check the value kinds flowing + * through the JVM state since intrinsics can employ non-Java kinds to represent values such as raw + * machine words and pointers. + */ +public class IntrinsicContext { + + /** + * Gets the method being intrinsified. + */ + final ResolvedJavaMethod method; + + /** + * Gets the method providing the intrinsic implementation. + */ + final ResolvedJavaMethod intrinsic; + + public ResolvedJavaMethod getOriginalMethod() { + return method; + } + + public ResolvedJavaMethod getIntrinsicMethod() { + return intrinsic; + } + + /** + * Determines if a call within the compilation scope of this intrinsic represents a call to the + * {@linkplain #getOriginalMethod() original} method. This denotes the path where a partial + * intrinsification falls back to the original method. + */ + public boolean isCallToOriginal(ResolvedJavaMethod targetMethod) { + return method.equals(targetMethod) || intrinsic.equals(targetMethod); + } + + final CompilationContext compilationContext; + + public IntrinsicContext(ResolvedJavaMethod method, ResolvedJavaMethod intrinsic, CompilationContext compilationContext) { + this.method = method; + this.intrinsic = intrinsic; + this.compilationContext = compilationContext; + assert !isCompilationRoot() || method.hasBytecodes() : "Cannot root compile intrinsic for native or abstract method " + method.format("%H.%n(%p)"); + } + + public boolean isPostParseInlined() { + return compilationContext.equals(INLINE_AFTER_PARSING); + } + + public boolean isCompilationRoot() { + return compilationContext.equals(ROOT_COMPILATION); + } + + /** + * Denotes the compilation context in which an intrinsic is being parsed. + */ + public enum CompilationContext { + /** + * An intrinsic is being processed when parsing an invoke bytecode that calls the + * intrinsified method. + */ + INLINE_DURING_PARSING, + + /** + * An intrinsic is being processed when inlining an {@link Invoke} in an existing graph. + */ + INLINE_AFTER_PARSING, + + /** + * An intrinsic is the root of compilation. + */ + ROOT_COMPILATION + } + + /** + * Models the state of a graph in terms of {@link StateSplit#hasSideEffect() side effects} that + * are control flow predecessors of the current point in a graph. + */ + public interface SideEffectsState { + + /** + * Determines if the current program point is preceded by one or more side effects. + */ + boolean isAfterSideEffect(); + + /** + * Gets the side effects preceding the current program point. + */ + Iterable sideEffects(); + + /** + * Records a side effect for the current program point. + */ + void addSideEffect(StateSplit sideEffect); + } + + public FrameState createFrameState(StructuredGraph graph, SideEffectsState sideEffects, StateSplit forStateSplit) { + assert forStateSplit != graph.start(); + if (forStateSplit.hasSideEffect()) { + if (sideEffects.isAfterSideEffect()) { + // Only the last side effect on any execution path in a replacement + // can inherit the stateAfter of the replaced node + FrameState invalid = graph.add(new FrameState(INVALID_FRAMESTATE_BCI)); + for (StateSplit lastSideEffect : sideEffects.sideEffects()) { + lastSideEffect.setStateAfter(invalid); + } + } + sideEffects.addSideEffect(forStateSplit); + return graph.add(new FrameState(AFTER_BCI)); + } else { + if (forStateSplit instanceof AbstractMergeNode) { + // Merge nodes always need a frame state + if (sideEffects.isAfterSideEffect()) { + // A merge after one or more side effects + return graph.add(new FrameState(AFTER_BCI)); + } else { + // A merge before any side effects + return graph.add(new FrameState(BEFORE_BCI)); + } + } else { + // Other non-side-effects do not need a state + return null; + } + } + } + + @Override + public String toString() { + return "Intrinsic{original: " + method.format("%H.%n(%p)") + ", intrinsic: " + intrinsic.format("%H.%n(%p)") + ", context: " + compilationContext + "}"; + } +} diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/ArrayCopyIntrinsificationTest.java --- a/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/ArrayCopyIntrinsificationTest.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/ArrayCopyIntrinsificationTest.java Tue May 12 20:56:04 2015 +0200 @@ -31,6 +31,7 @@ import com.oracle.graal.api.meta.*; import com.oracle.graal.compiler.test.*; import com.oracle.graal.graph.*; +import com.oracle.graal.hotspot.replacements.arraycopy.*; import com.oracle.graal.nodes.*; /** @@ -52,9 +53,14 @@ Assert.assertTrue(invoke.callTarget() instanceof DirectCallTargetNode); LoweredCallTargetNode directCall = (LoweredCallTargetNode) invoke.callTarget(); JavaMethod callee = directCall.targetMethod(); - Assert.assertTrue(callee.getName().equals("")); - Assert.assertTrue(getMetaAccess().lookupJavaType(ArrayIndexOutOfBoundsException.class).equals(callee.getDeclaringClass()) || - getMetaAccess().lookupJavaType(NullPointerException.class).equals(callee.getDeclaringClass())); + if (callee.getDeclaringClass().equals(getMetaAccess().lookupJavaType(System.class)) && callee.getName().equals("arraycopy")) { + // A partial snippet (e.g., ArrayCopySnippets.checkcastArraycopy) may + // call the original arraycopy method + } else { + Assert.assertTrue(callee.toString(), callee.getName().equals("")); + Assert.assertTrue(getMetaAccess().lookupJavaType(ArrayIndexOutOfBoundsException.class).equals(callee.getDeclaringClass()) || + getMetaAccess().lookupJavaType(NullPointerException.class).equals(callee.getDeclaringClass())); + } } } } else { @@ -145,25 +151,28 @@ @Test public void testObject() { - mustIntrinsify = false; // a generic call to arraycopy will not be intrinsified - Object[] src = {"one", "two", "three", new ArrayList<>(), new HashMap<>()}; testHelper("objectArraycopy", src); + } - mustIntrinsify = true; + /** + * Tests {@link ArrayCopySnippets#checkcastArraycopySnippet}. + */ + @Test + public void testArrayStoreException() { + Object[] src = {"one", "two", "three", new ArrayList<>(), new HashMap<>()}; + Object[] dst = new CharSequence[src.length]; + // Will throw ArrayStoreException for 4th element + test("objectArraycopy", src, 0, dst, 0, src.length); } @Test public void testDisjointObject() { - mustIntrinsify = false; // a generic call to arraycopy will not be intrinsified - Integer[] src1 = {1, 2, 3, 4}; test("objectArraycopy", src1, 0, src1, 1, src1.length - 1); Integer[] src2 = {1, 2, 3, 4}; test("objectArraycopy", src2, 1, src2, 0, src2.length - 1); - - mustIntrinsify = true; } @Test diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/HotSpotCryptoSubstitutionTest.java --- a/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/HotSpotCryptoSubstitutionTest.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/HotSpotCryptoSubstitutionTest.java Tue May 12 20:56:04 2015 +0200 @@ -22,7 +22,7 @@ */ package com.oracle.graal.hotspot.test; -import static com.oracle.graal.java.IntrinsicContext.*; +import static com.oracle.graal.graphbuilderconf.IntrinsicContext.CompilationContext.*; import java.io.*; import java.lang.reflect.*; @@ -41,7 +41,7 @@ import com.oracle.graal.hotspot.meta.*; import com.oracle.graal.java.*; import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.StructuredGraph.*; +import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions; import com.oracle.graal.phases.*; /** @@ -132,7 +132,7 @@ StructuredGraph graph = new StructuredGraph(substMethod, AllowAssumptions.YES); Plugins plugins = new Plugins(((HotSpotProviders) getProviders()).getGraphBuilderPlugins()); GraphBuilderConfiguration config = GraphBuilderConfiguration.getSnippetDefault(plugins); - IntrinsicContext initialReplacementContext = new IntrinsicContext(installedCodeOwner, substMethod, null, ROOT_COMPILATION_BCI); + IntrinsicContext initialReplacementContext = new IntrinsicContext(installedCodeOwner, substMethod, ROOT_COMPILATION); new GraphBuilderPhase.Instance(getMetaAccess(), getProviders().getStampProvider(), getConstantReflection(), config, OptimisticOptimizations.NONE, initialReplacementContext).apply(graph); Assert.assertNotNull(getCode(installedCodeOwner, graph, true)); atLeastOneCompiled = true; diff -r d24e4f349cbc -r 25bd9e2320de 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 May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompileTheWorld.java Tue May 12 20:56:04 2015 +0200 @@ -305,7 +305,7 @@ } if (excludeMethodFilters != null && excludeMethodFilters.length > 0) { String exclude = Arrays.asList(excludeMethodFilters).stream().map(MethodFilter::toString).collect(Collectors.joining(", ")); - println("CompileTheWorld : Excluding all methods matching one of the follwing filters: " + exclude); + println("CompileTheWorld : Excluding all methods matching one of the following filters: " + exclude); } println(); diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotConstantReflectionProvider.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotConstantReflectionProvider.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotConstantReflectionProvider.java Tue May 12 20:56:04 2015 +0200 @@ -35,7 +35,6 @@ import com.oracle.graal.hotspot.*; import com.oracle.graal.options.*; import com.oracle.graal.replacements.*; -import com.oracle.graal.replacements.ReplacementsImpl.FrameStateProcessing; import com.oracle.graal.replacements.SnippetTemplate.Arguments; /** @@ -314,7 +313,7 @@ ResolvedJavaMethod initMethod = null; try { Class rjm = ResolvedJavaMethod.class; - makeGraphMethod = metaAccess.lookupJavaMethod(ReplacementsImpl.class.getDeclaredMethod("makeGraph", rjm, Object[].class, rjm, FrameStateProcessing.class)); + makeGraphMethod = metaAccess.lookupJavaMethod(ReplacementsImpl.class.getDeclaredMethod("makeGraph", rjm, Object[].class, rjm)); initMethod = metaAccess.lookupJavaMethod(SnippetTemplate.AbstractTemplates.class.getDeclaredMethod("template", Arguments.class)); } catch (NoSuchMethodException | SecurityException e) { throw new GraalInternalError(e); diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotLoadFieldPlugin.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotLoadFieldPlugin.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotLoadFieldPlugin.java Tue May 12 20:56:04 2015 +0200 @@ -41,7 +41,7 @@ static final ThreadLocal FieldReadEnabledInImmutableCode = new ThreadLocal<>(); public boolean apply(GraphBuilderContext b, ValueNode receiver, ResolvedJavaField field) { - if (!ImmutableCode.getValue() || b.parsingReplacement()) { + if (!ImmutableCode.getValue() || b.parsingIntrinsic()) { if (receiver.isConstant()) { JavaConstant asJavaConstant = receiver.asJavaConstant(); return tryReadField(b, field, asJavaConstant); @@ -64,10 +64,10 @@ } public boolean apply(GraphBuilderContext b, ResolvedJavaField staticField) { - if (!ImmutableCode.getValue() || b.parsingReplacement()) { + if (!ImmutableCode.getValue() || b.parsingIntrinsic()) { // Javac does not allow use of "$assertionsDisabled" for a field name but // Eclipse does in which case a suffix is added to the generated field. - if (b.parsingReplacement() && staticField.isSynthetic() && staticField.getName().startsWith("$assertionsDisabled")) { + if (b.parsingIntrinsic() && staticField.isSynthetic() && staticField.getName().startsWith("$assertionsDisabled")) { // For methods called indirectly from intrinsics, we (silently) disable // assertions so that the parser won't see calls to the AssertionError // constructor (all Invokes must be removed from intrinsics - see diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotLoadIndexedPlugin.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotLoadIndexedPlugin.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotLoadIndexedPlugin.java Tue May 12 20:56:04 2015 +0200 @@ -40,7 +40,7 @@ } public boolean apply(GraphBuilderContext b, ValueNode array, ValueNode index, Kind elementKind) { - if (b.parsingReplacement()) { + if (b.parsingIntrinsic()) { ResolvedJavaType arrayType = StampTool.typeOrNull(array); /* * There are cases where the array does not have a known type yet, i.e., the type is diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotParameterPlugin.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotParameterPlugin.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotParameterPlugin.java Tue May 12 20:56:04 2015 +0200 @@ -38,7 +38,7 @@ } public FloatingNode interceptParameter(GraphBuilderContext b, int index, Stamp stamp) { - if (b.parsingReplacement()) { + if (b.parsingIntrinsic()) { ResolvedJavaType type = StampTool.typeOrNull(stamp); if (wordTypes.isWord(type)) { return new ParameterNode(index, wordTypes.getWordStamp(type)); diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotSuitesProvider.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotSuitesProvider.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotSuitesProvider.java Tue May 12 20:56:04 2015 +0200 @@ -119,7 +119,7 @@ * for equality. */ private boolean appendGraphEncoderTest(PhaseSuite suite) { - suite.appendPhase(new BasePhase() { + suite.appendPhase(new BasePhase("VerifyEncodingDecoding") { @Override protected void run(StructuredGraph graph, HighTierContext context) { EncodedGraph encodedGraph = GraphEncoder.encodeSingleGraph(graph, runtime.getTarget().arch); diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/HotSpotClassSubstitutions.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/HotSpotClassSubstitutions.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/HotSpotClassSubstitutions.java Tue May 12 20:56:04 2015 +0200 @@ -48,6 +48,7 @@ public static boolean isInterface(final Class thisObj) { KlassPointer klass = ClassGetHubNode.readClass(thisObj); if (klass.isNull()) { + // Class for primitive type return false; } else { int accessFlags = klass.readInt(klassAccessFlagsOffset(), KLASS_ACCESS_FLAGS_LOCATION); @@ -58,6 +59,7 @@ public static boolean isArray(final Class thisObj) { KlassPointer klass = ClassGetHubNode.readClass(thisObj); if (klass.isNull()) { + // Class for primitive type return false; } else { return klassIsArray(klass); @@ -85,6 +87,8 @@ } } } + } else { + // Class for primitive type } return null; } @@ -99,6 +103,8 @@ if (klassIsArray(klass)) { return PiNode.asNonNullClass(klass.readObject(arrayKlassComponentMirrorOffset(), ARRAY_KLASS_COMPONENT_MIRROR)); } + } else { + // Class for primitive type } return null; } diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/HotSpotReplacementsUtil.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/HotSpotReplacementsUtil.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/HotSpotReplacementsUtil.java Tue May 12 20:56:04 2015 +0200 @@ -825,6 +825,11 @@ public static final LocationIdentity OBJ_ARRAY_KLASS_ELEMENT_KLASS_LOCATION = NamedLocationIdentity.immutable("ObjArrayKlass::_element_klass"); + @Fold + public static int arrayClassElementOffset() { + return config().arrayClassElementOffset; + } + public static final LocationIdentity PRIMARY_SUPERS_LOCATION = NamedLocationIdentity.immutable("PrimarySupers"); public static final LocationIdentity METASPACE_ARRAY_LENGTH_LOCATION = NamedLocationIdentity.immutable("MetaspaceArrayLength"); diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/arraycopy/ArrayCopySnippets.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/arraycopy/ArrayCopySnippets.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/arraycopy/ArrayCopySnippets.java Tue May 12 20:56:04 2015 +0200 @@ -194,7 +194,8 @@ // no storecheck required. ArrayCopyCallNode.arraycopy(nonNullSrc, srcPos, nonNullDest, destPos, length, Kind.Object, false, false); } else { - KlassPointer destElemKlass = loadHub(nonNullDest); + KlassPointer destKlass = loadHub(nonNullDest); + KlassPointer destElemKlass = destKlass.readKlassPointer(arrayClassElementOffset(), OBJ_ARRAY_KLASS_ELEMENT_KLASS_LOCATION); checkcastArraycopyHelper(srcPos, destPos, length, nonNullSrc, nonNullDest, destElemKlass); } } diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/SnippetStub.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/SnippetStub.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/SnippetStub.java Tue May 12 20:56:04 2015 +0200 @@ -22,6 +22,8 @@ */ package com.oracle.graal.hotspot.stubs; +import static com.oracle.graal.graphbuilderconf.IntrinsicContext.CompilationContext.*; + import java.lang.reflect.*; import com.oracle.graal.api.meta.*; @@ -94,8 +96,8 @@ assert SnippetGraphUnderConstruction.get() == null; SnippetGraphUnderConstruction.set(graph); - ReplacementContext initialReplacementContext = new ReplacementContext(method, method); - new GraphBuilderPhase.Instance(metaAccess, providers.getStampProvider(), providers.getConstantReflection(), config, OptimisticOptimizations.NONE, initialReplacementContext).apply(graph); + IntrinsicContext initialIntrinsicContext = new IntrinsicContext(method, method, INLINE_AFTER_PARSING); + new GraphBuilderPhase.Instance(metaAccess, providers.getStampProvider(), providers.getConstantReflection(), config, OptimisticOptimizations.NONE, initialIntrinsicContext).apply(graph); SnippetGraphUnderConstruction.set(null); graph.setGuardsStage(GuardsStage.FLOATING_GUARDS); diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/word/KlassPointer.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/word/KlassPointer.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/word/KlassPointer.java Tue May 12 20:56:04 2015 +0200 @@ -44,8 +44,8 @@ public static native KlassPointer fromWord(Pointer pointer); @HotSpotOperation(opcode = READ_KLASS_POINTER) - public native KlassPointer readKlassPointer(int secondarySuperCacheOffset, LocationIdentity secondarySuperCacheLocation); + public native KlassPointer readKlassPointer(int offset, LocationIdentity locationIdentity); @Operation(opcode = Opcode.WRITE_POINTER) - public native void writeKlassPointer(int secondarySuperCacheOffset, KlassPointer t, LocationIdentity secondarySuperCacheLocation); + public native void writeKlassPointer(int offset, KlassPointer t, LocationIdentity locationIdentity); } diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.java/src/com/oracle/graal/java/AbstractBytecodeParser.java --- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/AbstractBytecodeParser.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/AbstractBytecodeParser.java Tue May 12 20:56:04 2015 +0200 @@ -73,6 +73,9 @@ @Option(help = "Do not bail out but throw an exception on failed loop explosion.", type = OptionType.Debug) public static final OptionValue FailedLoopExplosionIsFatal = new OptionValue<>(false); + @Option(help = "When creating info points hide the methods of the substitutions.", type = OptionType.Debug) + public static final OptionValue HideSubstitutionStates = new OptionValue<>(false); + // @formatter:on } @@ -99,7 +102,7 @@ protected final ConstantPool constantPool; protected final MetaAccessProvider metaAccess; - protected final ReplacementContext replacementContext; + protected final IntrinsicContext intrinsicContext; /** * Meters the number of actual bytecodes parsed. @@ -107,7 +110,7 @@ public static final DebugMetric BytecodesParsed = Debug.metric("BytecodesParsed"); public AbstractBytecodeParser(MetaAccessProvider metaAccess, ResolvedJavaMethod method, GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts, - ReplacementContext replacementContext) { + IntrinsicContext intrinsicContext) { this.graphBuilderConfig = graphBuilderConfig; this.optimisticOpts = optimisticOpts; this.metaAccess = metaAccess; @@ -115,7 +118,7 @@ this.profilingInfo = (graphBuilderConfig.getUseProfiling() ? method.getProfilingInfo() : null); this.constantPool = method.getConstantPool(); this.method = method; - this.replacementContext = replacementContext; + this.intrinsicContext = intrinsicContext; assert metaAccess != null; } @@ -140,7 +143,7 @@ if (kind == Kind.Object) { value = frameState.xpop(); // astore and astore_ may be used to store a returnAddress (jsr) - assert parsingReplacement() || (value.getKind() == Kind.Object || value.getKind() == Kind.Int) : value + ":" + value.getKind(); + assert parsingIntrinsic() || (value.getKind() == Kind.Object || value.getKind() == Kind.Int) : value + ":" + value.getKind(); } else { value = frameState.pop(kind); } @@ -584,13 +587,13 @@ } private void maybeEagerlyResolve(int cpi, int bytecode) { - if (graphBuilderConfig.eagerResolving() || replacementContext instanceof IntrinsicContext) { + if (graphBuilderConfig.eagerResolving() || intrinsicContext != null) { constantPool.loadReferencedType(cpi, bytecode); } } private JavaTypeProfile getProfileForTypeCheck(ResolvedJavaType type) { - if (parsingReplacement() || profilingInfo == null || !optimisticOpts.useTypeCheckHints() || !canHaveSubtype(type)) { + if (parsingIntrinsic() || profilingInfo == null || !optimisticOpts.useTypeCheckHints() || !canHaveSubtype(type)) { return null; } else { return profilingInfo.getTypeProfile(bci()); @@ -1251,7 +1254,7 @@ Debug.log("%s", sb); } - public boolean parsingReplacement() { - return replacementContext != null; + public boolean parsingIntrinsic() { + return intrinsicContext != null; } } diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.java/src/com/oracle/graal/java/AbstractFrameStateBuilder.java --- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/AbstractFrameStateBuilder.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/AbstractFrameStateBuilder.java Tue May 12 20:56:04 2015 +0200 @@ -176,8 +176,8 @@ public T loadLocal(int i) { T x = locals[i]; assert x != null : i; - assert parser.parsingReplacement() || (x.getKind().getSlotCount() == 1 || locals[i + 1] == null); - assert parser.parsingReplacement() || (i == 0 || locals[i - 1] == null || locals[i - 1].getKind().getSlotCount() == 1); + assert parser.parsingIntrinsic() || (x.getKind().getSlotCount() == 1 || locals[i + 1] == null); + assert parser.parsingIntrinsic() || (i == 0 || locals[i - 1] == null || locals[i - 1].getKind().getSlotCount() == 1); return x; } @@ -193,14 +193,14 @@ * @param x the instruction which produces the value for the local */ public void storeLocal(int i, T x, Kind kind) { - assert x == null || parser.parsingReplacement() || (x.getKind() != Kind.Void && x.getKind() != Kind.Illegal) : "unexpected value: " + x; + assert x == null || parser.parsingIntrinsic() || (x.getKind() != Kind.Void && x.getKind() != Kind.Illegal) : "unexpected value: " + x; locals[i] = x; if (x != null) { - if (kind.needsTwoSlots() && !parser.parsingReplacement()) { + if (kind.needsTwoSlots() && !parser.parsingIntrinsic()) { // if this is a double word, then kill i+1 locals[i + 1] = null; } - if (i > 0 && !parser.parsingReplacement()) { + if (i > 0 && !parser.parsingIntrinsic()) { T p = locals[i - 1]; if (p != null && p.getKind().needsTwoSlots()) { // if there was a double word at i - 1, then kill it @@ -235,7 +235,7 @@ * @param x the instruction to push onto the stack */ public void xpush(T x) { - assert parser.parsingReplacement() || (x == null || (x.getKind() != Kind.Void && x.getKind() != Kind.Illegal)); + assert parser.parsingIntrinsic() || (x == null || (x.getKind() != Kind.Void && x.getKind() != Kind.Illegal)); stack[stackSize++] = x; } @@ -379,7 +379,7 @@ newStackSize--; assert stack[newStackSize].getKind().needsTwoSlots(); } else { - assert parser.parsingReplacement() || (stack[newStackSize].getKind().getSlotCount() == 1); + assert parser.parsingIntrinsic() || (stack[newStackSize].getKind().getSlotCount() == 1); } result[i] = stack[newStackSize]; } @@ -415,7 +415,7 @@ } private T assertKind(Kind kind, T x) { - assert x != null && (parser.parsingReplacement() || x.getKind() == kind) : "kind=" + kind + ", value=" + x + ((x == null) ? "" : ", value.kind=" + x.getKind()); + assert x != null && (parser.parsingIntrinsic() || x.getKind() == kind) : "kind=" + kind + ", value=" + x + ((x == null) ? "" : ", value.kind=" + x.getKind()); return x; } @@ -435,7 +435,7 @@ } private T assertObject(T x) { - assert x != null && (parser.parsingReplacement() || (x.getKind() == Kind.Object)); + assert x != null && (parser.parsingIntrinsic() || (x.getKind() == Kind.Object)); return x; } diff -r d24e4f349cbc -r 25bd9e2320de 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 May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java Tue May 12 20:56:04 2015 +0200 @@ -28,6 +28,7 @@ import static com.oracle.graal.compiler.common.GraalInternalError.*; import static com.oracle.graal.compiler.common.GraalOptions.*; import static com.oracle.graal.compiler.common.type.StampFactory.*; +import static com.oracle.graal.graphbuilderconf.IntrinsicContext.CompilationContext.*; import static com.oracle.graal.java.AbstractBytecodeParser.Options.*; import static com.oracle.graal.nodes.StructuredGraph.*; import static com.oracle.graal.nodes.type.StampTool.*; @@ -52,6 +53,7 @@ import com.oracle.graal.graphbuilderconf.InvocationPlugins.InvocationPluginReceiver; import com.oracle.graal.java.BciBlockMapping.BciBlock; import com.oracle.graal.java.BciBlockMapping.ExceptionDispatchBlock; +import com.oracle.graal.java.GraphBuilderPhase.Instance.BytecodeParser; import com.oracle.graal.nodeinfo.*; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.CallTargetNode.InvokeKind; @@ -84,6 +86,87 @@ return graphBuilderConfig; } + static class IntrinsicScope implements AutoCloseable { + FrameState stateBefore; + final Mark mark; + final BytecodeParser parser; + + static IntrinsicScope create(boolean enteringIntrinsic, BytecodeParser parser, HIRFrameStateBuilder currentFrameState, ValueNode[] args) { + if (enteringIntrinsic) { + return new IntrinsicScope(parser, currentFrameState, args); + } + return null; + } + + public IntrinsicScope(BytecodeParser parser, HIRFrameStateBuilder currentFrameState, ValueNode[] args) { + this.parser = parser; + if (args == null) { + assert parser.parent == null; + assert parser.bci() == 0; + mark = null; + } else { + mark = parser.getGraph().getMark(); + for (ValueNode arg : args) { + currentFrameState.push(arg.getKind(), arg); + } + stateBefore = currentFrameState.create(parser.bci(), null); + for (int i = args.length - 1; i >= 0; i--) { + ValueNode arg = args[i]; + currentFrameState.pop(arg.getKind()); + } + } + } + + public void close() { + IntrinsicContext intrinsic = parser.intrinsicContext; + if (intrinsic != null && intrinsic.isPostParseInlined()) { + return; + } + + FrameState stateAfterReturn = null; + StructuredGraph graph = parser.getGraph(); + for (Node node : graph.getNewNodes(mark)) { + if (node instanceof FrameState) { + FrameState fs = (FrameState) node; + if (BytecodeFrame.isPlaceholderBci(fs.bci)) { + if (fs.bci == BytecodeFrame.AFTER_BCI) { + if (fs.stackSize() != 0) { + assert fs.usages().count() == 1; + ValueNode returnVal = fs.stackAt(0); + assert returnVal == fs.usages().first(); + + ValueNode tos = parser.frameState.pop(returnVal.getKind()); + parser.frameState.push(returnVal.getKind(), returnVal); + FrameState newFs = parser.frameState.create(parser.stream.nextBCI(), null); + fs.replaceAndDelete(newFs); + parser.frameState.pop(returnVal.getKind()); + parser.frameState.push(tos.getKind(), tos); + } else { + if (stateAfterReturn == null) { + if (intrinsic != null && intrinsic.isCompilationRoot()) { + stateAfterReturn = graph.add(new FrameState(BytecodeFrame.INVALID_FRAMESTATE_BCI)); + } else { + stateAfterReturn = parser.frameState.create(parser.stream.nextBCI(), null); + } + } + fs.replaceAndDelete(stateAfterReturn); + } + } else if (fs.bci == BytecodeFrame.BEFORE_BCI) { + if (stateBefore == null) { + stateBefore = graph.start().stateAfter(); + } + if (stateBefore != fs) { + fs.replaceAndDelete(stateBefore); + } + } else { + assert fs.bci == BytecodeFrame.INVALID_FRAMESTATE_BCI; + } + } + } + } + } + } + // Fully qualified name is a workaround for JDK-8056066 public static class Instance extends com.oracle.graal.phases.Phase { @@ -91,7 +174,7 @@ private final MetaAccessProvider metaAccess; - private final ReplacementContext initialReplacementContext; + private final IntrinsicContext initialIntrinsicContext; private final GraphBuilderConfiguration graphBuilderConfig; private final OptimisticOptimizations optimisticOpts; @@ -99,13 +182,13 @@ private final ConstantReflectionProvider constantReflection; public Instance(MetaAccessProvider metaAccess, StampProvider stampProvider, ConstantReflectionProvider constantReflection, GraphBuilderConfiguration graphBuilderConfig, - OptimisticOptimizations optimisticOpts, ReplacementContext initialReplacementContext) { + OptimisticOptimizations optimisticOpts, IntrinsicContext initialIntrinsicContext) { this.graphBuilderConfig = graphBuilderConfig; this.optimisticOpts = optimisticOpts; this.metaAccess = metaAccess; this.stampProvider = stampProvider; this.constantReflection = constantReflection; - this.initialReplacementContext = initialReplacementContext; + this.initialIntrinsicContext = initialIntrinsicContext; assert metaAccess != null; } @@ -118,12 +201,15 @@ this.graph = graph; TTY.Filter filter = new TTY.Filter(PrintFilter.getValue(), method); try { - ReplacementContext replacementContext = initialReplacementContext; - BytecodeParser parser = new BytecodeParser(null, metaAccess, method, graphBuilderConfig, optimisticOpts, entryBCI, replacementContext); + IntrinsicContext intrinsicContext = initialIntrinsicContext; + BytecodeParser parser = new BytecodeParser(null, metaAccess, method, graphBuilderConfig, optimisticOpts, entryBCI, intrinsicContext); HIRFrameStateBuilder frameState = new HIRFrameStateBuilder(parser, method, graph); - frameState.initializeForMethodStart(graphBuilderConfig.eagerResolving() || replacementContext != null, graphBuilderConfig.getPlugins().getParameterPlugin()); - parser.build(graph.start(), frameState); + + frameState.initializeForMethodStart(graphBuilderConfig.eagerResolving() || intrinsicContext != null, graphBuilderConfig.getPlugins().getParameterPlugin()); + try (IntrinsicScope s = IntrinsicScope.create(intrinsicContext != null, parser, frameState, null)) { + parser.build(graph.start(), frameState); + } GraphUtil.normalizeLoops(graph); // Remove dead parameters. @@ -252,12 +338,12 @@ private HIRFrameStateBuilder[][] entryStateMatrix; public BytecodeParser(BytecodeParser parent, MetaAccessProvider metaAccess, ResolvedJavaMethod method, GraphBuilderConfiguration graphBuilderConfig, - OptimisticOptimizations optimisticOpts, int entryBCI, ReplacementContext replacementContext) { - super(metaAccess, method, graphBuilderConfig, optimisticOpts, replacementContext); + OptimisticOptimizations optimisticOpts, int entryBCI, IntrinsicContext intrinsicContext) { + super(metaAccess, method, graphBuilderConfig, optimisticOpts, intrinsicContext); this.entryBCI = entryBCI; this.parent = parent; - if (graphBuilderConfig.insertNonSafepointDebugInfo()) { + if (graphBuilderConfig.insertNonSafepointDebugInfo() && !parsingIntrinsic()) { lnt = method.getLineNumberTable(); previousLineNumber = -1; } @@ -326,11 +412,27 @@ if (startInstruction == graph.start()) { StartNode startNode = graph.start(); if (method.isSynchronized()) { - startNode.setStateAfter(createFrameState(BytecodeFrame.BEFORE_BCI)); + assert !parsingIntrinsic(); + startNode.setStateAfter(createFrameState(BytecodeFrame.BEFORE_BCI, startNode)); } else { - frameState.clearNonLiveLocals(startBlock, liveness, true); - assert bci() == 0; - startNode.setStateAfter(createFrameState(bci())); + if (!parsingIntrinsic()) { + if (graph.method() != null && graph.method().isJavaLangObjectInit()) { + /* + * Don't clear the receiver when Object. is the + * compilation root. The receiver is needed as input to + * RegisterFinalizerNode. + */ + } else { + frameState.clearNonLiveLocals(startBlock, liveness, true); + } + assert bci() == 0; + startNode.setStateAfter(createFrameState(bci(), startNode)); + } else { + if (startNode.stateAfter() == null) { + FrameState stateAfterStart = createStateAfterStartOfReplacementGraph(); + startNode.setStateAfter(stateAfterStart); + } + } } } @@ -342,7 +444,7 @@ genMonitorEnter(methodSynchronizedObject, bci()); } - if (graphBuilderConfig.insertNonSafepointDebugInfo()) { + if (graphBuilderConfig.insertNonSafepointDebugInfo() && !parsingIntrinsic()) { append(createInfoPointNode(InfopointReason.METHOD_START)); } @@ -372,6 +474,42 @@ } } + /** + * Creates the frame state after the start node of a graph for an + * {@link IntrinsicContext intrinsic} that is the parse root (either for root compiling + * or for post-parse inlining). + */ + private FrameState createStateAfterStartOfReplacementGraph() { + assert parent == null; + assert frameState.method.equals(intrinsicContext.getIntrinsicMethod()); + assert bci() == 0; + assert frameState.stackSize == 0; + FrameState stateAfterStart; + if (intrinsicContext.isPostParseInlined()) { + stateAfterStart = graph.add(new FrameState(BytecodeFrame.BEFORE_BCI)); + } else { + ResolvedJavaMethod original = intrinsicContext.getOriginalMethod(); + ValueNode[] locals; + if (original.getMaxLocals() == frameState.localsSize() || original.isNative()) { + locals = frameState.locals; + } else { + locals = new ValueNode[original.getMaxLocals()]; + int parameterCount = original.getSignature().getParameterCount(!original.isStatic()); + for (int i = 0; i < parameterCount; i++) { + ValueNode param = frameState.locals[i]; + locals[i] = param; + assert param == null || param instanceof ParameterNode || param.isConstant(); + } + } + ValueNode[] stack = {}; + int stackSize = 0; + ValueNode[] locks = {}; + List monitorIds = Collections.emptyList(); + stateAfterStart = graph.add(new FrameState(null, original, 0, locals, stack, stackSize, locks, monitorIds, false, false)); + } + return stateAfterStart; + } + private void detectLoops(FixedNode startInstruction) { NodeBitMap visited = graph.createNodeBitMap(); NodeBitMap active = graph.createNodeBitMap(); @@ -404,7 +542,7 @@ } LoopBeginNode loopBegin = (LoopBeginNode) ((EndNode) merge.next()).merge(); LoopEndNode loopEnd = graph.add(new LoopEndNode(loopBegin)); - if (parsingReplacement()) { + if (parsingIntrinsic()) { loopEnd.disableSafepoint(); } endNode.replaceAndDelete(loopEnd); @@ -728,11 +866,11 @@ dispatchBegin = graph.add(new ExceptionObjectNode(metaAccess)); dispatchState.apush(dispatchBegin); dispatchState.setRethrowException(true); - dispatchBegin.setStateAfter(dispatchState.create(bci)); + dispatchBegin.setStateAfter(dispatchState.create(bci, dispatchBegin)); } else { dispatchBegin = graph.add(new DispatchBeginNode()); dispatchState.apush(exceptionObject); - dispatchBegin.setStateAfter(dispatchState.create(bci)); + dispatchBegin.setStateAfter(dispatchState.create(bci, dispatchBegin)); dispatchState.setRethrowException(true); } this.controlFlowSplit = true; @@ -946,7 +1084,7 @@ append(new IfNode(graph.unique(new IsNullNode(receiver)), exception, falseSucc, 0.01)); lastInstr = falseSucc; - exception.setStateAfter(createFrameState(bci())); + exception.setStateAfter(createFrameState(bci(), exception)); exception.setNext(handleException(exception, bci())); return nonNullReceiver; } @@ -958,7 +1096,7 @@ append(new IfNode(graph.unique(IntegerBelowNode.create(index, length, constantReflection)), trueSucc, exception, 0.99)); lastInstr = trueSucc; - exception.setStateAfter(createFrameState(bci())); + exception.setStateAfter(createFrameState(bci(), exception)); exception.setNext(handleException(exception, bci())); } @@ -971,7 +1109,7 @@ protected void genStoreField(ValueNode receiver, ResolvedJavaField field, ValueNode value) { StoreFieldNode storeFieldNode = new StoreFieldNode(receiver, field, value); append(storeFieldNode); - storeFieldNode.setStateAfter(this.createFrameState(stream.nextBCI())); + storeFieldNode.setStateAfter(this.createFrameState(stream.nextBCI(), storeFieldNode)); } /** @@ -1104,7 +1242,7 @@ } JavaType returnType = targetMethod.getSignature().getReturnType(method.getDeclaringClass()); - if (graphBuilderConfig.eagerResolving() || parsingReplacement()) { + if (graphBuilderConfig.eagerResolving() || parsingIntrinsic()) { returnType = returnType.resolve(targetMethod.getDeclaringClass()); } if (invokeKind.hasReceiver()) { @@ -1210,8 +1348,8 @@ for (Node n : newNodes) { if (n instanceof StateSplit) { StateSplit stateSplit = (StateSplit) n; - assert stateSplit.stateAfter() != null : error("%s node added by plugin for %s need to have a non-null frame state: %s", StateSplit.class.getSimpleName(), - targetMethod.format("%H.%n(%p)"), stateSplit); + assert stateSplit.stateAfter() != null || !stateSplit.hasSideEffect() : error("%s node added by plugin for %s need to have a non-null frame state: %s", + StateSplit.class.getSimpleName(), targetMethod.format("%H.%n(%p)"), stateSplit); } } try { @@ -1231,9 +1369,8 @@ InvocationPlugin plugin = graphBuilderConfig.getPlugins().getInvocationPlugins().lookupInvocation(targetMethod); if (plugin != null) { - ReplacementContext context = this.replacementContext; - if (context != null && context.isCallToOriginal(targetMethod)) { - // Self recursive replacement means the original + if (intrinsicContext != null && intrinsicContext.isCallToOriginal(targetMethod)) { + // Self recursive intrinsic means the original // method should be called. assert !targetMethod.hasBytecodes() : "TODO: when does this happen?"; return false; @@ -1256,69 +1393,56 @@ private boolean tryInline(ValueNode[] args, ResolvedJavaMethod targetMethod, JavaType returnType) { InlineInvokePlugin plugin = graphBuilderConfig.getPlugins().getInlineInvokePlugin(); - boolean canBeInlined = parsingReplacement() || targetMethod.canBeInlined(); + boolean canBeInlined = parsingIntrinsic() || targetMethod.canBeInlined(); if (plugin == null || !canBeInlined) { return false; } InlineInfo inlineInfo = plugin.getInlineInfo(this, targetMethod, args, returnType); if (inlineInfo != null) { - return inline(plugin, targetMethod, inlineInfo.methodToInline, inlineInfo.isReplacement, inlineInfo.isIntrinsic, args); + return inline(plugin, targetMethod, inlineInfo.methodToInline, inlineInfo.isIntrinsic, args); } return false; } public void intrinsify(ResolvedJavaMethod targetMethod, ResolvedJavaMethod substitute, ValueNode[] args) { - boolean res = inline(null, targetMethod, substitute, true, true, args); + boolean res = inline(null, targetMethod, substitute, true, args); assert res : "failed to inline " + substitute; } - private boolean inline(InlineInvokePlugin plugin, ResolvedJavaMethod targetMethod, ResolvedJavaMethod inlinedMethod, boolean isReplacement, boolean isIntrinsic, ValueNode[] args) { - int bci = bci(); + private boolean inline(InlineInvokePlugin plugin, ResolvedJavaMethod targetMethod, ResolvedJavaMethod inlinedMethod, boolean isIntrinsic, ValueNode[] args) { if (TraceInlineDuringParsing.getValue() || TraceParserPlugins.getValue()) { if (targetMethod.equals(inlinedMethod)) { traceWithContext("inlining call to %s", inlinedMethod.format("%h.%n(%p)")); } else { - traceWithContext("inlining call to %s as replacement for %s", inlinedMethod.format("%h.%n(%p)"), targetMethod.format("%h.%n(%p)")); + traceWithContext("inlining call to %s as intrinsic for %s", inlinedMethod.format("%h.%n(%p)"), targetMethod.format("%h.%n(%p)")); } } - ReplacementContext context = this.replacementContext; - if (context != null && context.isCallToOriginal(targetMethod)) { - IntrinsicContext intrinsic = context.asIntrinsic(); - if (intrinsic != null) { - if (intrinsic.isCompilationRoot()) { - // A root compiled intrinsic needs to deoptimize - // if the slow path is taken - DeoptimizeNode deopt = append(new DeoptimizeNode(InvalidateRecompile, RuntimeConstraint)); - deopt.setStateBefore(intrinsic.getInvokeStateBefore(graph, null)); - return true; - } else { - // Otherwise inline the original method. Any frame state created - // during the inlining will exclude frame(s) in the - // intrinsic method (see HIRFrameStateBuilder.create(int bci)). - parseAndInlineCallee(context.method, args, null); - return true; - } + IntrinsicContext intrinsic = this.intrinsicContext; + if (intrinsic != null && intrinsic.isCallToOriginal(targetMethod)) { + if (intrinsic.isCompilationRoot()) { + // A root compiled intrinsic needs to deoptimize + // if the slow path is taken. During frame state + // assignment, the deopt node will get its stateBefore + // from the start node of the intrinsic + append(new DeoptimizeNode(InvalidateRecompile, RuntimeConstraint)); + return true; } else { - // Self recursive replacement means the original - // method should be called. - if (context.method.hasBytecodes()) { - parseAndInlineCallee(context.method, args, null); - return true; - } else { + // Otherwise inline the original method. Any frame state created + // during the inlining will exclude frame(s) in the + // intrinsic method (see HIRFrameStateBuilder.create(int bci)). + if (intrinsic.getOriginalMethod().isNative()) { return false; } + parseAndInlineCallee(intrinsic.getOriginalMethod(), args, null); + return true; } } else { - if (context == null && isReplacement) { + if (intrinsic == null && isIntrinsic) { assert !inlinedMethod.equals(targetMethod); - if (isIntrinsic) { - context = new IntrinsicContext(targetMethod, inlinedMethod, args, bci); - } else { - context = new ReplacementContext(targetMethod, inlinedMethod); - } + intrinsic = new IntrinsicContext(targetMethod, inlinedMethod, INLINE_DURING_PARSING); } if (inlinedMethod.hasBytecodes()) { - parseAndInlineCallee(inlinedMethod, args, context); + parseAndInlineCallee(inlinedMethod, args, intrinsic); if (plugin != null) { plugin.postInline(inlinedMethod); } @@ -1362,34 +1486,38 @@ return res; } - private void parseAndInlineCallee(ResolvedJavaMethod targetMethod, ValueNode[] args, ReplacementContext calleeReplacementContext) { - BytecodeParser parser = new BytecodeParser(this, metaAccess, targetMethod, graphBuilderConfig, optimisticOpts, INVOCATION_ENTRY_BCI, calleeReplacementContext); - HIRFrameStateBuilder startFrameState = new HIRFrameStateBuilder(parser, targetMethod, graph); - if (!targetMethod.isStatic()) { - args[0] = nullCheckedValue(args[0]); - } - startFrameState.initializeFromArgumentsArray(args); - parser.build(this.lastInstr, startFrameState); + private void parseAndInlineCallee(ResolvedJavaMethod targetMethod, ValueNode[] args, IntrinsicContext calleeIntrinsicContext) { + try (IntrinsicScope s = IntrinsicScope.create(calleeIntrinsicContext != null && !parsingIntrinsic(), this, frameState, args)) { + + BytecodeParser parser = new BytecodeParser(this, metaAccess, targetMethod, graphBuilderConfig, optimisticOpts, INVOCATION_ENTRY_BCI, calleeIntrinsicContext); + HIRFrameStateBuilder startFrameState = new HIRFrameStateBuilder(parser, targetMethod, graph); + if (!targetMethod.isStatic()) { + args[0] = nullCheckedValue(args[0]); + } + startFrameState.initializeFromArgumentsArray(args); + parser.build(this.lastInstr, startFrameState); - FixedWithNextNode calleeBeforeReturnNode = parser.getBeforeReturnNode(); - this.lastInstr = calleeBeforeReturnNode; - if (calleeBeforeReturnNode != null) { - ValueNode calleeReturnValue = parser.getReturnValue(); - if (calleeReturnValue != null) { - frameState.push(targetMethod.getSignature().getReturnKind().getStackKind(), calleeReturnValue); + FixedWithNextNode calleeBeforeReturnNode = parser.getBeforeReturnNode(); + this.lastInstr = calleeBeforeReturnNode; + Kind calleeReturnKind = targetMethod.getSignature().getReturnKind(); + if (calleeBeforeReturnNode != null) { + ValueNode calleeReturnValue = parser.getReturnValue(); + if (calleeReturnValue != null) { + frameState.push(calleeReturnKind.getStackKind(), calleeReturnValue); + } } - } - FixedWithNextNode calleeBeforeUnwindNode = parser.getBeforeUnwindNode(); - if (calleeBeforeUnwindNode != null) { - ValueNode calleeUnwindValue = parser.getUnwindValue(); - assert calleeUnwindValue != null; - calleeBeforeUnwindNode.setNext(handleException(calleeUnwindValue, bci())); - } + FixedWithNextNode calleeBeforeUnwindNode = parser.getBeforeUnwindNode(); + if (calleeBeforeUnwindNode != null) { + ValueNode calleeUnwindValue = parser.getUnwindValue(); + assert calleeUnwindValue != null; + calleeBeforeUnwindNode.setNext(handleException(calleeUnwindValue, bci())); + } - // Record inlined method dependency in the graph - if (graph.isInlinedMethodRecordingEnabled()) { - graph.getInlinedMethods().add(targetMethod); + // Record inlined method dependency in the graph + if (graph.isInlinedMethodRecordingEnabled()) { + graph.getInlinedMethods().add(targetMethod); + } } } @@ -1400,7 +1528,7 @@ protected InvokeNode createInvoke(CallTargetNode callTarget, Kind resultType) { InvokeNode invoke = append(new InvokeNode(callTarget, bci())); frameState.pushReturn(resultType, invoke); - invoke.setStateAfter(createFrameState(stream.nextBCI())); + invoke.setStateAfter(createFrameState(stream.nextBCI(), invoke)); return invoke; } @@ -1408,13 +1536,35 @@ DispatchBeginNode exceptionEdge = handleException(null, bci()); InvokeWithExceptionNode invoke = append(new InvokeWithExceptionNode(callTarget, exceptionEdge, bci())); frameState.pushReturn(resultType, invoke); - invoke.setStateAfter(createFrameState(stream.nextBCI())); + invoke.setStateAfter(createFrameState(stream.nextBCI(), invoke)); return invoke; } @Override protected void genReturn(ValueNode returnVal, Kind returnKind) { - + if (parsingIntrinsic() && returnVal != null) { + if (returnVal instanceof StateSplit) { + StateSplit stateSplit = (StateSplit) returnVal; + FrameState stateAfter = stateSplit.stateAfter(); + if (stateSplit.hasSideEffect()) { + assert stateSplit != null; + if (stateAfter.bci == BytecodeFrame.AFTER_BCI) { + assert stateAfter.usages().count() == 1; + assert stateAfter.usages().first() == stateSplit; + stateAfter.replaceAtUsages(graph.add(new FrameState(BytecodeFrame.AFTER_BCI, returnVal))); + GraphUtil.killWithUnusedFloatingInputs(stateAfter); + } else { + /* + * This must be the return value from within a partial + * intrinsification. + */ + assert !BytecodeFrame.isPlaceholderBci(stateAfter.bci); + } + } else { + assert stateAfter == null; + } + } + } if (parent == null) { frameState.setRethrowException(false); frameState.clearStack(); @@ -1440,7 +1590,10 @@ } private void beforeReturn(ValueNode x, Kind kind) { - if (graphBuilderConfig.insertNonSafepointDebugInfo()) { + if (graph.method() != null && graph.method().isJavaLangObjectInit()) { + append(new RegisterFinalizerNode(frameState.localAt(0))); + } + if (graphBuilderConfig.insertNonSafepointDebugInfo() && !parsingIntrinsic()) { append(createInfoPointNode(InfopointReason.METHOD_END)); } @@ -1455,7 +1608,7 @@ MonitorIdNode monitorId = graph.add(new MonitorIdNode(frameState.lockDepth())); MonitorEnterNode monitorEnter = append(new MonitorEnterNode(x, monitorId)); frameState.pushLock(x, monitorId); - monitorEnter.setStateAfter(createFrameState(bci)); + monitorEnter.setStateAfter(createFrameState(bci, monitorEnter)); } @Override @@ -1466,7 +1619,7 @@ throw bailout(String.format("unbalanced monitors: mismatch at monitorexit, %s != %s", GraphUtil.originalValue(x), GraphUtil.originalValue(lockedObject))); } MonitorExitNode monitorExit = append(new MonitorExitNode(x, monitorId, escapedReturnValue)); - monitorExit.setStateAfter(createFrameState(bci)); + monitorExit.setStateAfter(createFrameState(bci, monitorExit)); } @Override @@ -1617,7 +1770,7 @@ lastLoopExit = loopExit; Debug.log("Target %s Exits %s, scanning framestates...", targetBlock, loop); newState.insertLoopProxies(loopExit, getEntryState(loop, this.getCurrentDimension())); - loopExit.setStateAfter(newState.create(bci)); + loopExit.setStateAfter(newState.create(bci, loopExit)); } lastLoopExit.setNext(target); @@ -1776,7 +1929,7 @@ */ LoopBeginNode loopBegin = (LoopBeginNode) getFirstInstruction(block, operatingDimension); LoopEndNode loopEnd = graph.add(new LoopEndNode(loopBegin)); - if (parsingReplacement()) { + if (parsingIntrinsic()) { loopEnd.disableSafepoint(); } Target target = checkLoopExit(loopEnd, block, state); @@ -1962,7 +2115,7 @@ if (block instanceof ExceptionDispatchBlock) { bci = ((ExceptionDispatchBlock) block).deoptBci; } - abstractMergeNode.setStateAfter(createFrameState(bci)); + abstractMergeNode.setStateAfter(createFrameState(bci, abstractMergeNode)); } } @@ -2045,7 +2198,7 @@ // Create phi functions for all local variables and operand stack slots. frameState.insertLoopPhis(liveness, block.loopId, loopBegin); - loopBegin.setStateAfter(createFrameState(block.startBci)); + loopBegin.setStateAfter(createFrameState(block.startBci, loopBegin)); /* * We have seen all forward branches. All subsequent backward branches will @@ -2074,9 +2227,14 @@ int bci = block.startBci; BytecodesParsed.add(block.endBci - bci); + /* Reset line number for new block */ + if (graphBuilderConfig.insertSimpleDebugInfo()) { + previousLineNumber = -1; + } + while (bci < endBCI) { - if (graphBuilderConfig.insertNonSafepointDebugInfo() && lnt != null) { - currentLineNumber = lnt.getLineNumber(bci); + if (graphBuilderConfig.insertNonSafepointDebugInfo() && !parsingIntrinsic()) { + currentLineNumber = lnt != null ? lnt.getLineNumber(bci) : (graphBuilderConfig.insertFullDebugInfo() ? -1 : bci); if (currentLineNumber != previousLineNumber) { append(createInfoPointNode(InfopointReason.LINE_NUMBER)); previousLineNumber = currentLineNumber; @@ -2093,7 +2251,7 @@ } EntryMarkerNode x = append(new EntryMarkerNode()); frameState.insertProxies(x); - x.setStateAfter(createFrameState(bci)); + x.setStateAfter(createFrameState(bci, x)); } try { @@ -2110,7 +2268,7 @@ bci = stream.currentBCI(); assert block == currentBlock; - assert !(lastInstr instanceof StateSplit) || lastInstr instanceof BeginNode || ((StateSplit) lastInstr).stateAfter() != null : lastInstr; + assert checkLastInstruction(); lastInstr = finishInstruction(lastInstr, frameState); if (bci < endBCI) { if (bci > block.endBci) { @@ -2124,6 +2282,18 @@ } } + protected boolean checkLastInstruction() { + if (lastInstr instanceof BeginNode) { + // ignore + } else if (lastInstr instanceof StateSplit) { + StateSplit stateSplit = (StateSplit) lastInstr; + if (stateSplit.hasSideEffect()) { + assert stateSplit.stateAfter() != null : "side effect " + lastInstr + " requires a non-null stateAfter"; + } + } + return true; + } + private LoopBeginNode appendLoopBegin(FixedWithNextNode fixedWithNext) { EndNode preLoopEnd = graph.add(new EndNode()); LoopBeginNode loopBegin = graph.add(new LoopBeginNode()); @@ -2146,9 +2316,18 @@ private InfopointNode createInfoPointNode(InfopointReason reason) { if (graphBuilderConfig.insertFullDebugInfo()) { - return new FullInfopointNode(reason, createFrameState(bci())); + return new FullInfopointNode(reason, createFrameState(bci(), null)); } else { - return new SimpleInfopointNode(reason, new BytecodePosition(null, method, bci())); + BytecodePosition position = createBytecodePosition(); + // Update the previous infopoint position if no new fixed nodes were inserted + if (lastInstr instanceof SimpleInfopointNode) { + SimpleInfopointNode lastInfopoint = (SimpleInfopointNode) lastInstr; + if (lastInfopoint.getReason() == reason) { + lastInfopoint.setPosition(position); + return lastInfopoint; + } + } + return new SimpleInfopointNode(reason, position); } } @@ -2247,7 +2426,7 @@ FixedNode falseSuccessor = createTarget(falseBlock, frameState, false, true); ValueNode ifNode = genIfNode(condition, trueSuccessor, falseSuccessor, probability); append(ifNode); - if (parsingReplacement()) { + if (parsingIntrinsic()) { if (x instanceof BranchProbabilityNode) { ((BranchProbabilityNode) x).simplify(null); } else if (y instanceof BranchProbabilityNode) { @@ -2377,8 +2556,8 @@ return parent; } - public Replacement getReplacement() { - return replacementContext; + public IntrinsicContext getIntrinsic() { + return intrinsicContext; } @Override @@ -2390,7 +2569,7 @@ if (bp != this) { fmt.format("%n%s", indent); } - fmt.format("%s [bci: %d, replacement: %s]", bp.method.asStackTraceElement(bp.bci()), bp.bci(), bp.parsingReplacement()); + fmt.format("%s [bci: %d, intrinsic: %s]", bp.method.asStackTraceElement(bp.bci()), bp.bci(), bp.parsingIntrinsic()); fmt.format("%n%s", new BytecodeDisassembler().disassemble(bp.method, bp.bci(), bp.bci() + 10)); bp = bp.parent; indent += " "; @@ -2399,21 +2578,27 @@ } public BailoutException bailout(String string) { - FrameState currentFrameState = createFrameState(bci()); + FrameState currentFrameState = createFrameState(bci(), null); StackTraceElement[] elements = GraphUtil.approxSourceStackTraceElement(currentFrameState); BailoutException bailout = new BailoutException(string); throw GraphUtil.createBailoutException(string, bailout, elements); } - private FrameState createFrameState(int bci) { + private FrameState createFrameState(int bci, StateSplit forStateSplit) { if (currentBlock != null && bci > currentBlock.endBci) { frameState.clearNonLiveLocals(currentBlock, liveness, false); } - return frameState.create(bci); + return frameState.create(bci, forStateSplit); } - public FrameState createStateAfter() { - return createFrameState(stream.nextBCI()); + public void setStateAfter(StateSplit sideEffect) { + assert sideEffect.hasSideEffect(); + FrameState stateAfter = createFrameState(stream.nextBCI(), sideEffect); + sideEffect.setStateAfter(stateAfter); + } + + private BytecodePosition createBytecodePosition() { + return frameState.createBytecodePosition(bci()); } } } diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.java/src/com/oracle/graal/java/HIRFrameStateBuilder.java --- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/HIRFrameStateBuilder.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/HIRFrameStateBuilder.java Tue May 12 20:56:04 2015 +0200 @@ -32,6 +32,7 @@ import com.oracle.graal.compiler.common.type.*; import com.oracle.graal.debug.*; import com.oracle.graal.graphbuilderconf.*; +import com.oracle.graal.graphbuilderconf.IntrinsicContext.*; import com.oracle.graal.java.BciBlockMapping.BciBlock; import com.oracle.graal.java.GraphBuilderPhase.Instance.BytecodeParser; import com.oracle.graal.nodeinfo.*; @@ -40,7 +41,7 @@ import com.oracle.graal.nodes.java.*; import com.oracle.graal.nodes.util.*; -public final class HIRFrameStateBuilder { +public final class HIRFrameStateBuilder implements SideEffectsState { static final ValueNode[] EMPTY_ARRAY = new ValueNode[0]; static final MonitorIdNode[] EMPTY_MONITOR_ARRAY = new MonitorIdNode[0]; @@ -62,6 +63,12 @@ private FrameState outerFrameState; /** + * The closest {@link StateSplit#hasSideEffect() side-effect} predecessors. There will be more + * than one when the current block contains no side-effects but merging predecessor blocks do. + */ + protected List sideEffects; + + /** * Creates a new frame state builder for the given method and the given target graph. * * @param method the method whose frame is simulated @@ -144,14 +151,6 @@ javaIndex += kind.getSlotCount(); index++; } - - if (parser.replacementContext instanceof IntrinsicContext) { - IntrinsicContext intrinsic = (IntrinsicContext) parser.replacementContext; - if (intrinsic.isCompilationRoot()) { - // Records the parameters to an root compiled intrinsic - intrinsic.args = locals.clone(); - } - } } private HIRFrameStateBuilder(HIRFrameStateBuilder other) { @@ -202,25 +201,19 @@ return sb.toString(); } - public FrameState create(int bci) { - BytecodeParser parent = parser.getParent(); - if (parser.parsingReplacement()) { - IntrinsicContext intrinsic = parser.replacementContext.asIntrinsic(); - if (intrinsic != null) { - return intrinsic.getInvokeStateBefore(parser.getGraph(), parent); - } + public FrameState create(int bci, StateSplit forStateSplit) { + if (parser.parsingIntrinsic()) { + return parser.intrinsicContext.createFrameState(parser.getGraph(), this, forStateSplit); } - // If this is the recursive call in a partial intrinsification - // the frame(s) of the intrinsic method are omitted - while (parent != null && parent.parsingReplacement() && parent.replacementContext.asIntrinsic() != null) { - parent = parent.getParent(); - } + + // Skip intrinsic frames + BytecodeParser parent = (BytecodeParser) parser.getNonReplacementAncestor(); return create(bci, parent, false); } public FrameState create(int bci, BytecodeParser parent, boolean duringCall) { if (outerFrameState == null && parent != null) { - outerFrameState = parent.getFrameState().create(parent.bci()); + outerFrameState = parent.getFrameState().create(parent.bci(), null); } if (bci == BytecodeFrame.AFTER_EXCEPTION_BCI && parent != null) { FrameState newFrameState = outerFrameState.duplicateModified(outerFrameState.bci, true, Kind.Void, this.peek(0)); @@ -228,11 +221,37 @@ } if (bci == BytecodeFrame.INVALID_FRAMESTATE_BCI) { throw GraalInternalError.shouldNotReachHere(); - // return graph.add(new FrameState(bci)); } return graph.add(new FrameState(outerFrameState, method, bci, locals, stack, stackSize, lockedObjects, Arrays.asList(monitorIds), rethrowException, duringCall)); } + public BytecodePosition createBytecodePosition(int bci) { + BytecodeParser parent = parser.getParent(); + if (AbstractBytecodeParser.Options.HideSubstitutionStates.getValue()) { + if (parser.parsingIntrinsic()) { + // Attribute to the method being replaced + return new BytecodePosition(parent.getFrameState().createBytecodePosition(parent.bci()), parser.intrinsicContext.getOriginalMethod(), -1); + } + // Skip intrinsic frames + parent = (BytecodeParser) parser.getNonReplacementAncestor(); + } + return create(null, bci, parent); + } + + private BytecodePosition create(BytecodePosition o, int bci, BytecodeParser parent) { + BytecodePosition outer = o; + if (outer == null && parent != null) { + outer = parent.getFrameState().createBytecodePosition(parent.bci()); + } + if (bci == BytecodeFrame.AFTER_EXCEPTION_BCI && parent != null) { + return FrameState.toBytecodePosition(outerFrameState); + } + if (bci == BytecodeFrame.INVALID_FRAMESTATE_BCI) { + throw GraalInternalError.shouldNotReachHere(); + } + return new BytecodePosition(outer, method, bci); + } + public HIRFrameStateBuilder copy() { return new HIRFrameStateBuilder(this); } @@ -283,6 +302,14 @@ lockedObjects[i] = merge(lockedObjects[i], other.lockedObjects[i], block); assert monitorIds[i] == other.monitorIds[i]; } + + if (sideEffects == null) { + sideEffects = other.sideEffects; + } else { + if (other.sideEffects != null) { + sideEffects.addAll(other.sideEffects); + } + } } private ValueNode merge(ValueNode currentValue, ValueNode otherValue, AbstractMergeNode block) { @@ -573,8 +600,8 @@ private boolean assertLoadLocal(int i, ValueNode x) { assert x != null : i; - assert parser.parsingReplacement() || (x.getKind().getSlotCount() == 1 || locals[i + 1] == null); - assert parser.parsingReplacement() || (i == 0 || locals[i - 1] == null || locals[i - 1].getKind().getSlotCount() == 1); + assert parser.parsingIntrinsic() || (x.getKind().getSlotCount() == 1 || locals[i + 1] == null); + assert parser.parsingIntrinsic() || (i == 0 || locals[i - 1] == null || locals[i - 1].getKind().getSlotCount() == 1); return true; } @@ -593,11 +620,11 @@ assert assertStoreLocal(x); locals[i] = x; if (x != null) { - if (kind.needsTwoSlots() && !parser.parsingReplacement()) { + if (kind.needsTwoSlots() && !parser.parsingIntrinsic()) { // if this is a double word, then kill i+1 locals[i + 1] = null; } - if (i > 0 && !parser.parsingReplacement()) { + if (i > 0 && !parser.parsingIntrinsic()) { ValueNode p = locals[i - 1]; if (p != null && p.getKind().needsTwoSlots()) { // if there was a double word at i - 1, then kill it @@ -608,7 +635,7 @@ } private boolean assertStoreLocal(ValueNode x) { - assert x == null || parser.parsingReplacement() || (x.getKind() != Kind.Void && x.getKind() != Kind.Illegal) : "unexpected value: " + x; + assert x == null || parser.parsingIntrinsic() || (x.getKind() != Kind.Void && x.getKind() != Kind.Illegal) : "unexpected value: " + x; return true; } @@ -637,8 +664,8 @@ } private boolean assertPush(Kind kind, ValueNode x) { - assert parser.parsingReplacement() || (x.getKind() != Kind.Void && x.getKind() != Kind.Illegal); - assert x != null && (parser.parsingReplacement() || x.getKind() == kind); + assert parser.parsingIntrinsic() || (x.getKind() != Kind.Void && x.getKind() != Kind.Illegal); + assert x != null && (parser.parsingIntrinsic() || x.getKind() == kind); return true; } @@ -653,7 +680,7 @@ } private boolean assertXpush(ValueNode x) { - assert parser.parsingReplacement() || (x == null || (x.getKind() != Kind.Void && x.getKind() != Kind.Illegal)); + assert parser.parsingIntrinsic() || (x == null || (x.getKind() != Kind.Void && x.getKind() != Kind.Illegal)); return true; } @@ -732,7 +759,7 @@ private boolean assertPop(Kind kind) { assert kind != Kind.Void; ValueNode x = xpeek(); - assert x != null && (parser.parsingReplacement() || x.getKind() == kind); + assert x != null && (parser.parsingIntrinsic() || x.getKind() == kind); return true; } @@ -819,7 +846,7 @@ newStackSize--; assert stack[newStackSize].getKind().needsTwoSlots(); } else { - assert parser.parsingReplacement() || (stack[newStackSize].getKind().getSlotCount() == 1); + assert parser.parsingIntrinsic() || (stack[newStackSize].getKind().getSlotCount() == 1); } result[i] = stack[newStackSize]; } @@ -886,7 +913,7 @@ } private boolean assertObject(ValueNode x) { - assert x != null && (parser.parsingReplacement() || (x.getKind() == Kind.Object)); + assert x != null && (parser.parsingIntrinsic() || (x.getKind() == Kind.Object)); return true; } @@ -970,4 +997,24 @@ } } } + + @Override + public boolean isAfterSideEffect() { + return sideEffects != null; + } + + @Override + public Iterable sideEffects() { + return sideEffects; + } + + @Override + public void addSideEffect(StateSplit sideEffect) { + assert sideEffect != null; + assert sideEffect.hasSideEffect(); + if (sideEffects == null) { + sideEffects = new ArrayList<>(4); + } + sideEffects.add(sideEffect); + } } diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.java/src/com/oracle/graal/java/IntrinsicContext.java --- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/IntrinsicContext.java Tue May 12 20:55:48 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,127 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.graal.java; - -import static com.oracle.graal.java.HIRFrameStateBuilder.*; - -import java.util.*; - -import com.oracle.graal.api.code.*; -import com.oracle.graal.api.meta.*; -import com.oracle.graal.java.GraphBuilderPhase.Instance.*; -import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.java.*; - -/** - * Context for a replacement being inlined as a compiler intrinsic. Deoptimization within a compiler - * intrinsic must replay the intrinsified call. This context object retains the information required - * to build a frame state denoting the JVM state just before the intrinsified call. - */ -public class IntrinsicContext extends ReplacementContext { - - /** - * BCI denoting an intrinsic is being parsed for inlining after the caller has been parsed. - */ - public static final int POST_PARSE_INLINE_BCI = -1; - - /** - * BCI denoting an intrinsic is the compilation root. - */ - public static final int ROOT_COMPILATION_BCI = -2; - - /** - * The arguments to the intrinsic. - */ - ValueNode[] args; - - /** - * The BCI of the intrinsified invocation, {@link #POST_PARSE_INLINE_BCI} or - * {@link #ROOT_COMPILATION_BCI}. - */ - final int bci; - - private FrameState stateBeforeCache; - - public IntrinsicContext(ResolvedJavaMethod method, ResolvedJavaMethod substitute, ValueNode[] args, int bci) { - super(method, substitute); - assert bci != POST_PARSE_INLINE_BCI || args == null; - this.args = args; - this.bci = bci; - assert !isCompilationRoot() || method.hasBytecodes() : "Cannot intrinsic for native or abstract method " + method.format("%H.%n(%p)"); - } - - @Override - public boolean isIntrinsic() { - return true; - } - - public boolean isPostParseInlined() { - return bci == POST_PARSE_INLINE_BCI; - } - - public boolean isCompilationRoot() { - return bci == ROOT_COMPILATION_BCI; - } - - public FrameState getInvokeStateBefore(StructuredGraph graph, BytecodeParser parent) { - if (isCompilationRoot()) { - int maxLocals = method.getMaxLocals(); - // The 'args' were initialized based on the intrinsic method but a - // frame state's 'locals' needs to have the same length as the frame - // state method's 'max_locals'. - ValueNode[] locals = maxLocals == args.length ? args : Arrays.copyOf(args, maxLocals); - ValueNode[] stack = EMPTY_ARRAY; - int stackSize = 0; - ValueNode[] locks = EMPTY_ARRAY; - List monitorIds = Collections.emptyList(); - return graph.add(new FrameState(null, method, 0, locals, stack, stackSize, locks, monitorIds, false, false)); - } else if (isPostParseInlined()) { - return graph.add(new FrameState(BytecodeFrame.BEFORE_BCI)); - } else { - assert !parent.parsingReplacement() || parent.replacementContext instanceof IntrinsicContext; - if (stateBeforeCache == null) { - - // Find the non-intrinsic ancestor calling the intrinsified method - BytecodeParser ancestor = parent; - while (ancestor.parsingReplacement()) { - assert ancestor.replacementContext instanceof IntrinsicContext; - ancestor = ancestor.getParent(); - } - FrameState stateDuring = ancestor.getFrameState().create(ancestor.bci(), ancestor.getParent(), true); - stateBeforeCache = stateDuring.duplicateModifiedBeforeCall(bci, Kind.Void, args); - } - return stateBeforeCache; - } - } - - @Override - IntrinsicContext asIntrinsic() { - return this; - } - - @Override - public String toString() { - return "Intrinsic{original: " + method.format("%H.%n(%p)") + ", replacement: " + replacement.format("%H.%n(%p)") + ", bci: " + bci + (args == null ? "" : ", args: " + Arrays.toString(args)) + - (stateBeforeCache == null ? "" : ", stateBefore: " + stateBeforeCache) + "}"; - } -} diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.java/src/com/oracle/graal/java/ReplacementContext.java --- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/ReplacementContext.java Tue May 12 20:55:48 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.graal.java; - -import com.oracle.graal.api.meta.*; -import com.oracle.graal.api.replacements.*; -import com.oracle.graal.graphbuilderconf.GraphBuilderContext.Replacement; - -/** - * Information about a substitute method being parsed in lieu of an original method. This can happen - * when a call to a {@link MethodSubstitution} is encountered or the root of compilation is a - * {@link MethodSubstitution} or a snippet. - */ -public class ReplacementContext implements Replacement { - /** - * The method being replaced. - */ - final ResolvedJavaMethod method; - - /** - * The replacement method. - */ - final ResolvedJavaMethod replacement; - - public ReplacementContext(ResolvedJavaMethod method, ResolvedJavaMethod substitute) { - this.method = method; - this.replacement = substitute; - } - - public ResolvedJavaMethod getOriginalMethod() { - return method; - } - - public ResolvedJavaMethod getReplacementMethod() { - return replacement; - } - - public boolean isIntrinsic() { - return false; - } - - /** - * Determines if a call within the compilation scope of a replacement represents a call to the - * original method. - */ - public boolean isCallToOriginal(ResolvedJavaMethod targetMethod) { - return method.equals(targetMethod) || replacement.equals(targetMethod); - } - - IntrinsicContext asIntrinsic() { - return null; - } - - @Override - public String toString() { - return "Replacement{original: " + method.format("%H.%n(%p)") + ", replacement: " + replacement.format("%H.%n(%p)") + "}"; - } -} diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/lang/Math_sin.java --- a/graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/lang/Math_sin.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/lang/Math_sin.java Tue May 12 20:56:04 2015 +0200 @@ -44,6 +44,21 @@ } @Test + public void runFirst() throws Throwable { + /* + * Execute Double.isNaN enough times to create a profile indicating that the path returning + * false is never taken. Then compile and execute the test with a NaN value to test that + * deoptimization works in the case of an uncommon trap inlined into an intrinsic. Of + * course, this relies on Double.isNaN never having yet been called with NaN. if it has, + * this test is equivalent to run0. + */ + for (int i = 0; i < 10000; i++) { + Double.isNaN(1D); + } + executeActual(getResolvedJavaMethod("test"), null, java.lang.Double.NaN); + } + + @Test public void run0() throws Throwable { runTest("test", java.lang.Double.NaN); } diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/threads/Thread_isInterrupted04.java --- a/graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/threads/Thread_isInterrupted04.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/threads/Thread_isInterrupted04.java Tue May 12 20:56:04 2015 +0200 @@ -67,7 +67,7 @@ } - @Test(timeout = 20000) + @Test public void run0() throws Throwable { runTest("test"); } diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.lir.jtt/src/com/oracle/graal/lir/jtt/LIRTest.java --- a/graal/com.oracle.graal.lir.jtt/src/com/oracle/graal/lir/jtt/LIRTest.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.lir.jtt/src/com/oracle/graal/lir/jtt/LIRTest.java Tue May 12 20:56:04 2015 +0200 @@ -31,7 +31,6 @@ import com.oracle.graal.compiler.common.*; import com.oracle.graal.compiler.common.type.*; import com.oracle.graal.graph.*; -import com.oracle.graal.graph.spi.*; import com.oracle.graal.graphbuilderconf.*; import com.oracle.graal.graphbuilderconf.MethodIdMap.Receiver; import com.oracle.graal.jtt.*; @@ -49,26 +48,26 @@ */ public abstract class LIRTest extends JTTTest { - protected abstract static class LIRTestSpecification { + public abstract static class LIRTestSpecification { private Value result; - void generate(LIRGeneratorTool gen, Value arg0) { + public void generate(LIRGeneratorTool gen, Value arg0) { defaultHandler(gen, arg0); } - void generate(LIRGeneratorTool gen, Value arg0, Value arg1) { + public void generate(LIRGeneratorTool gen, Value arg0, Value arg1) { defaultHandler(gen, arg0, arg1); } - void generate(LIRGeneratorTool gen, Value arg0, Value arg1, Value arg2) { + public void generate(LIRGeneratorTool gen, Value arg0, Value arg1, Value arg2) { defaultHandler(gen, arg0, arg1, arg2); } - void generate(LIRGeneratorTool gen, Value arg0, Value arg1, Value arg2, Value arg3) { + public void generate(LIRGeneratorTool gen, Value arg0, Value arg1, Value arg2, Value arg3) { defaultHandler(gen, arg0, arg1, arg2, arg3); } - void generate(LIRGeneratorTool gen, Value arg0, Value arg1, Value arg2, Value arg3, Value arg4) { + public void generate(LIRGeneratorTool gen, Value arg0, Value arg1, Value arg2, Value arg3, Value arg4) { defaultHandler(gen, arg0, arg1, arg2, arg3, arg4); } @@ -142,7 +141,7 @@ } @NodeInfo - private static final class FloatingLIRTestNode extends FloatingNode implements LIRLowerable, Simplifiable { + private static final class FloatingLIRTestNode extends FloatingNode implements LIRLowerable { public static final NodeClass TYPE = NodeClass.create(FloatingLIRTestNode.class); @Input protected ValueNode opsNode; @@ -165,13 +164,6 @@ } @Override - public void simplify(SimplifierTool tool) { - if (tool.allUsagesAvailable() && getLIROpsNode().isConstant()) { - getLIROpsNode().asConstant(); - } - } - - @Override public void generate(NodeLIRBuilderTool gen) { LIRTestSpecification ops = getLIROperations(); Stream v = values().stream().map(node -> gen.operand(node)); diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScan.java --- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScan.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScan.java Tue May 12 20:56:04 2015 +0200 @@ -25,9 +25,7 @@ import static com.oracle.graal.api.code.CodeUtil.*; import static com.oracle.graal.api.code.ValueUtil.*; import static com.oracle.graal.compiler.common.GraalOptions.*; -import static com.oracle.graal.compiler.common.cfg.AbstractControlFlowGraph.*; import static com.oracle.graal.lir.LIRValueUtil.*; -import static com.oracle.graal.lir.debug.LIRGenerationDebugContext.*; import java.util.*; @@ -36,20 +34,15 @@ import com.oracle.graal.compiler.common.*; import com.oracle.graal.compiler.common.alloc.*; import com.oracle.graal.compiler.common.cfg.*; -import com.oracle.graal.compiler.common.util.*; import com.oracle.graal.debug.*; -import com.oracle.graal.debug.Debug.Scope; import com.oracle.graal.lir.*; import com.oracle.graal.lir.LIRInstruction.OperandFlag; import com.oracle.graal.lir.LIRInstruction.OperandMode; -import com.oracle.graal.lir.StandardOp.LabelOp; -import com.oracle.graal.lir.StandardOp.MoveOp; import com.oracle.graal.lir.alloc.lsra.Interval.RegisterBinding; -import com.oracle.graal.lir.alloc.lsra.Interval.RegisterPriority; -import com.oracle.graal.lir.alloc.lsra.Interval.SpillState; import com.oracle.graal.lir.framemap.*; import com.oracle.graal.lir.gen.*; import com.oracle.graal.lir.gen.LIRGeneratorTool.SpillMoveFactory; +import com.oracle.graal.lir.phases.AllocationPhase.AllocationContext; import com.oracle.graal.options.*; /** @@ -60,19 +53,18 @@ */ class LinearScan { - final TargetDescription target; final LIRGenerationResult res; final LIR ir; final FrameMapBuilder frameMapBuilder; - final SpillMoveFactory spillMoveFactory; final RegisterAttributes[] registerAttributes; final Register[] registers; final RegisterAllocationConfig regAllocConfig; + private final SpillMoveFactory moveFactory; final boolean callKillsRegisters; public static final int DOMINATOR_SPILL_MOVE_ID = -2; - private static final int SPLIT_INTERVALS_CAPACITY_RIGHT_SHIFT = 1; + static final int SPLIT_INTERVALS_CAPACITY_RIGHT_SHIFT = 1; public static class Options { // @formatter:off @@ -113,52 +105,45 @@ public BitSet liveKill; } - final BlockMap blockData; + private final BlockMap blockData; /** * List of blocks in linear-scan order. This is only correct as long as the CFG does not change. */ final List> sortedBlocks; - /** - * Map from {@linkplain #operandNumber(Value) operand numbers} to intervals. - */ - Interval[] intervals; + /** @see #intervals() */ + private Interval[] intervals; /** * The number of valid entries in {@link #intervals}. */ - int intervalsSize; + private int intervalsSize; /** * The index of the first entry in {@link #intervals} for a * {@linkplain #createDerivedInterval(Interval) derived interval}. */ - int firstDerivedIntervalIndex = -1; + private int firstDerivedIntervalIndex = -1; /** * Intervals sorted by {@link Interval#from()}. */ - Interval[] sortedIntervals; + private Interval[] sortedIntervals; /** * Map from an instruction {@linkplain LIRInstruction#id id} to the instruction. Entries should * be retrieved with {@link #instructionForId(int)} as the id is not simply an index into this * array. */ - LIRInstruction[] opIdToInstructionMap; + private LIRInstruction[] opIdToInstructionMap; /** * Map from an instruction {@linkplain LIRInstruction#id id} to the * {@linkplain AbstractBlockBase block} containing the instruction. Entries should be retrieved * with {@link #blockForId(int)} as the id is not simply an index into this array. */ - AbstractBlockBase[] opIdToBlockMap; - - /** - * Bit set for each variable that is contained in each loop. - */ - BitMap2D intervalInLoop; + private AbstractBlockBase[] opIdToBlockMap; /** * The {@linkplain #operandNumber(Value) number} of the first variable operand allocated. @@ -166,10 +151,9 @@ private final int firstVariableNumber; LinearScan(TargetDescription target, LIRGenerationResult res, SpillMoveFactory spillMoveFactory, RegisterAllocationConfig regAllocConfig) { - this.target = target; this.res = res; this.ir = res.getLIR(); - this.spillMoveFactory = spillMoveFactory; + this.moveFactory = spillMoveFactory; this.frameMapBuilder = res.getFrameMapBuilder(); this.sortedBlocks = ir.linearScanOrder(); this.registerAttributes = regAllocConfig.getRegisterConfig().getAttributesMap(); @@ -179,8 +163,10 @@ this.firstVariableNumber = registers.length; this.blockData = new BlockMap<>(ir.getControlFlowGraph()); - // If all allocatable registers are caller saved, then no registers are live across a call - // site. The register allocator can save time not trying to find a register at a call site. + /* + * If all allocatable registers are caller saved, then no registers are live across a call + * site. The register allocator can save time not trying to find a register at a call site. + */ this.callKillsRegisters = regAllocConfig.getRegisterConfig().areAllAllocatableRegistersCallerSaved(); } @@ -198,7 +184,7 @@ } SpillMoveFactory getSpillMoveFactory() { - return spillMoveFactory; + return moveFactory; } protected MoveResolver createMoveResolver() { @@ -216,7 +202,7 @@ * the {@linkplain Variable variables} and {@linkplain RegisterValue registers} being processed * by this allocator. */ - private int operandNumber(Value operand) { + int operandNumber(Value operand) { if (isRegister(operand)) { int number = asRegister(operand).number; assert number < firstVariableNumber; @@ -229,7 +215,7 @@ /** * Gets the number of operands. This value will increase by 1 for new variable. */ - private int operandSize() { + int operandSize() { return firstVariableNumber + ir.numVariables(); } @@ -240,6 +226,14 @@ return firstVariableNumber - 1; } + BlockData getBlockData(AbstractBlockBase block) { + return blockData.get(block); + } + + void initBlockData(AbstractBlockBase block) { + blockData.put(block, new BlockData()); + } + static final IntervalPredicate IS_PRECOLORED_INTERVAL = new IntervalPredicate() { @Override @@ -273,8 +267,10 @@ } 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 + /* + * Assign the canonical spill slot of the parent (if a part of the interval is already + * spilled) or allocate a new spill slot. + */ if (interval.canMaterialize()) { interval.assignLocation(Value.ILLEGAL); } else if (interval.spillSlot() != null) { @@ -287,6 +283,18 @@ } /** + * Map from {@linkplain #operandNumber(Value) operand numbers} to intervals. + */ + Interval[] intervals() { + return intervals; + } + + void initIntervals() { + intervalsSize = operandSize(); + intervals = new Interval[intervalsSize + (intervalsSize >> SPLIT_INTERVALS_CAPACITY_RIGHT_SHIFT)]; + } + + /** * Creates a new interval. * * @param operand the operand for the interval @@ -345,10 +353,6 @@ return ir.getControlFlowGraph().getLoops().size(); } - boolean isIntervalInLoop(int interval, int loop) { - return intervalInLoop.at(interval, loop); - } - Interval intervalFor(int operandNumber) { return intervals[operandNumber]; } @@ -368,6 +372,16 @@ } } + void initOpIdMaps(int numInstructions) { + opIdToInstructionMap = new LIRInstruction[numInstructions]; + opIdToBlockMap = new AbstractBlockBase[numInstructions]; + } + + void putOpIdMaps(int index, LIRInstruction op, AbstractBlockBase block) { + opIdToInstructionMap[index] = op; + opIdToBlockMap[index] = block; + } + /** * Gets the highest instruction id allocated by this object. */ @@ -378,10 +392,10 @@ /** * Converts an {@linkplain LIRInstruction#id instruction id} to an instruction index. All LIR - * instructions in a method have an index one greater than their linear-scan order predecesor + * instructions in a method have an index one greater than their linear-scan order predecessor * with the first instruction having an index of 0. */ - static int opIdToIndex(int opId) { + private static int opIdToIndex(int opId) { return opId >> 1; } @@ -429,909 +443,15 @@ return instructionForId(opId).destroysCallerSavedRegisters(); } - /** - * Eliminates moves from register to stack if the stack slot is known to be correct. - */ - void changeSpillDefinitionPos(Interval interval, int defPos) { - assert interval.isSplitParent() : "can only be called for split parents"; - - switch (interval.spillState()) { - case NoDefinitionFound: - assert interval.spillDefinitionPos() == -1 : "must no be set before"; - interval.setSpillDefinitionPos(defPos); - interval.setSpillState(SpillState.NoSpillStore); - break; - - case NoSpillStore: - assert defPos <= interval.spillDefinitionPos() : "positions are processed in reverse order when intervals are created"; - if (defPos < interval.spillDefinitionPos() - 2) { - // second definition found, so no spill optimization possible for this interval - interval.setSpillState(SpillState.NoOptimization); - } else { - // two consecutive definitions (because of two-operand LIR form) - assert blockForId(defPos) == blockForId(interval.spillDefinitionPos()) : "block must be equal"; - } - break; - - case NoOptimization: - // nothing to do - break; - - default: - throw new BailoutException("other states not allowed at this time"); - } - } - - // called during register allocation - void changeSpillState(Interval interval, int spillPos) { - switch (interval.spillState()) { - case NoSpillStore: { - int defLoopDepth = blockForId(interval.spillDefinitionPos()).getLoopDepth(); - int spillLoopDepth = blockForId(spillPos).getLoopDepth(); - - if (defLoopDepth < spillLoopDepth) { - // the loop depth of the spilling position is higher then the loop depth - // at the definition of the interval . move write to memory out of loop. - if (Options.LSRAOptimizeSpillPosition.getValue()) { - // find best spill position in dominator the tree - interval.setSpillState(SpillState.SpillInDominator); - } else { - // store at definition of the interval - interval.setSpillState(SpillState.StoreAtDefinition); - } - } else { - // the interval is currently spilled only once, so for now there is no - // reason to store the interval at the definition - interval.setSpillState(SpillState.OneSpillStore); - } - break; - } - - case OneSpillStore: { - if (Options.LSRAOptimizeSpillPosition.getValue()) { - // the interval is spilled more then once - interval.setSpillState(SpillState.SpillInDominator); - } else { - // it is better to store it to - // memory at the definition - interval.setSpillState(SpillState.StoreAtDefinition); - } - break; - } - - case SpillInDominator: - case StoreAtDefinition: - case StartInMemory: - case NoOptimization: - case NoDefinitionFound: - // nothing to do - break; - - default: - throw new BailoutException("other states not allowed at this time"); - } - } - abstract static class IntervalPredicate { abstract boolean apply(Interval i); } - private static final IntervalPredicate mustStoreAtDefinition = new IntervalPredicate() { - - @Override - public boolean apply(Interval i) { - return i.isSplitParent() && i.spillState() == SpillState.StoreAtDefinition; - } - }; - - /** - * @return the index of the first instruction that is of interest for - * {@link #eliminateSpillMoves()} - */ - protected int firstInstructionOfInterest() { - // skip the first because it is always a label - return 1; - } - - // called once before assignment of register numbers - void eliminateSpillMoves() { - try (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 - Interval interval; - interval = createUnhandledLists(mustStoreAtDefinition, null).first; - if (DetailedAsserts.getValue()) { - checkIntervals(interval); - } - - LIRInsertionBuffer insertionBuffer = new LIRInsertionBuffer(); - for (AbstractBlockBase block : sortedBlocks) { - try (Indent indent1 = Debug.logAndIndent("Handle %s", block)) { - List instructions = ir.getLIRforBlock(block); - int numInst = instructions.size(); - - // iterate all instructions of the block. - for (int j = firstInstructionOfInterest(); j < numInst; j++) { - LIRInstruction op = instructions.get(j); - int opId = op.id(); - - if (opId == -1) { - MoveOp move = (MoveOp) op; - /* - * Remove move from register to stack if the stack slot is guaranteed to - * be correct. Only moves that have been inserted by LinearScan can be - * removed. - */ - if (canEliminateSpillMove(block, move)) { - /* - * Move target is a stack slot that is always correct, so eliminate - * instruction. - */ - if (Debug.isLogEnabled()) { - Debug.log("eliminating move from interval %d (%s) to %d (%s) in block %s", operandNumber(move.getInput()), move.getInput(), operandNumber(move.getResult()), - move.getResult(), block); - } - - // null-instructions are deleted by assignRegNum - instructions.set(j, null); - } - - } else { - /* - * Insert move from register to stack just after the beginning of the - * interval. - */ - assert interval == Interval.EndMarker || interval.spillDefinitionPos() >= opId : "invalid order"; - assert interval == Interval.EndMarker || (interval.isSplitParent() && interval.spillState() == SpillState.StoreAtDefinition) : "invalid interval"; - - while (interval != Interval.EndMarker && interval.spillDefinitionPos() == opId) { - if (!interval.canMaterialize()) { - if (!insertionBuffer.initialized()) { - /* - * prepare insertion buffer (appended when all instructions - * in the block are processed) - */ - insertionBuffer.init(instructions); - } - - AllocatableValue fromLocation = interval.location(); - AllocatableValue toLocation = canonicalSpillOpr(interval); - if (!fromLocation.equals(toLocation)) { - - assert isRegister(fromLocation) : "from operand must be a register but is: " + fromLocation + " toLocation=" + toLocation + " spillState=" + - interval.spillState(); - assert isStackSlotValue(toLocation) : "to operand must be a stack slot"; - - LIRInstruction move = getSpillMoveFactory().createMove(toLocation, fromLocation); - insertionBuffer.append(j + 1, move); - - if (Debug.isLogEnabled()) { - Debug.log("inserting move after definition of interval %d to stack slot %s at opId %d", interval.operandNumber, interval.spillSlot(), opId); - } - } - } - interval = interval.next; - } - } - } // end of instruction iteration - - if (insertionBuffer.initialized()) { - insertionBuffer.finish(); - } - } - } // end of block iteration - - assert interval == Interval.EndMarker : "missed an interval"; - } - } - - /** - * @param block The block {@code move} is located in. - * @param move Spill move. - */ - protected boolean canEliminateSpillMove(AbstractBlockBase block, MoveOp move) { - assert isVariable(move.getResult()) : "LinearScan inserts only moves to variables: " + move; - - Interval curInterval = intervalFor(move.getResult()); - - if (!isRegister(curInterval.location()) && curInterval.alwaysInMemory()) { - assert isStackSlotValue(curInterval.location()) : "Not a stack slot: " + curInterval.location(); - return true; - } - return false; - } - - private static void checkIntervals(Interval interval) { - Interval prev = null; - Interval temp = interval; - while (temp != Interval.EndMarker) { - assert temp.spillDefinitionPos() > 0 : "invalid spill definition pos"; - if (prev != null) { - assert temp.from() >= prev.from() : "intervals not sorted"; - assert temp.spillDefinitionPos() >= prev.spillDefinitionPos() : "when intervals are sorted by from : then they must also be sorted by spillDefinitionPos"; - } - - assert temp.spillSlot() != null || temp.canMaterialize() : "interval has no spill slot assigned"; - assert temp.spillDefinitionPos() >= temp.from() : "invalid order"; - assert temp.spillDefinitionPos() <= temp.from() + 2 : "only intervals defined once at their start-pos can be optimized"; - - if (Debug.isLogEnabled()) { - Debug.log("interval %d (from %d to %d) must be stored at %d", temp.operandNumber, temp.from(), temp.to(), temp.spillDefinitionPos()); - } - - prev = temp; - temp = temp.next; - } - } - - /** - * Numbers all instructions in all blocks. The numbering follows the - * {@linkplain ComputeBlockOrder linear scan order}. - */ - void numberInstructions() { - - intervalsSize = operandSize(); - intervals = new Interval[intervalsSize + (intervalsSize >> SPLIT_INTERVALS_CAPACITY_RIGHT_SHIFT)]; - - ValueConsumer setVariableConsumer = (value, mode, flags) -> { - if (isVariable(value)) { - getOrCreateInterval(asVariable(value)); - } - }; - - // Assign IDs to LIR nodes and build a mapping, lirOps, from ID to LIRInstruction node. - int numInstructions = 0; - for (AbstractBlockBase block : sortedBlocks) { - numInstructions += ir.getLIRforBlock(block).size(); - } - - // initialize with correct length - opIdToInstructionMap = new LIRInstruction[numInstructions]; - opIdToBlockMap = new AbstractBlockBase[numInstructions]; - - int opId = 0; - int index = 0; - for (AbstractBlockBase block : sortedBlocks) { - blockData.put(block, new BlockData()); - - List instructions = ir.getLIRforBlock(block); - - int numInst = instructions.size(); - for (int j = 0; j < numInst; j++) { - LIRInstruction op = instructions.get(j); - op.setId(opId); - - opIdToInstructionMap[index] = op; - opIdToBlockMap[index] = block; - assert instructionForId(opId) == op : "must match"; - - op.visitEachTemp(setVariableConsumer); - op.visitEachOutput(setVariableConsumer); - - index++; - opId += 2; // numbering of lirOps by two - } - } - assert index == numInstructions : "must match"; - assert (index << 1) == opId : "must match: " + (index << 1); - } - - /** - * Computes local live sets (i.e. {@link BlockData#liveGen} and {@link BlockData#liveKill}) - * separately for each block. - */ - void computeLocalLiveSets() { - int liveSize = liveSetSize(); - - intervalInLoop = new BitMap2D(operandSize(), numLoops()); - - // iterate all blocks - for (final AbstractBlockBase block : sortedBlocks) { - try (Indent indent = Debug.logAndIndent("compute local live sets for block %s", block)) { - - final BitSet liveGen = new BitSet(liveSize); - final BitSet liveKill = new BitSet(liveSize); - - List instructions = ir.getLIRforBlock(block); - int numInst = instructions.size(); - - ValueConsumer useConsumer = (operand, mode, flags) -> { - if (isVariable(operand)) { - int operandNum = operandNumber(operand); - if (!liveKill.get(operandNum)) { - liveGen.set(operandNum); - if (Debug.isLogEnabled()) { - Debug.log("liveGen for operand %d(%s)", operandNum, operand); - } - } - if (block.getLoop() != null) { - intervalInLoop.setBit(operandNum, block.getLoop().getIndex()); - } - } - - if (DetailedAsserts.getValue()) { - verifyInput(block, liveKill, operand); - } - }; - ValueConsumer stateConsumer = (operand, mode, flags) -> { - if (isVariableOrRegister(operand)) { - int operandNum = operandNumber(operand); - if (!liveKill.get(operandNum)) { - liveGen.set(operandNum); - if (Debug.isLogEnabled()) { - Debug.log("liveGen in state for operand %d(%s)", operandNum, operand); - } - } - } - }; - ValueConsumer defConsumer = (operand, mode, flags) -> { - if (isVariable(operand)) { - int varNum = operandNumber(operand); - liveKill.set(varNum); - if (Debug.isLogEnabled()) { - Debug.log("liveKill for operand %d(%s)", varNum, operand); - } - if (block.getLoop() != null) { - intervalInLoop.setBit(varNum, block.getLoop().getIndex()); - } - } - - if (DetailedAsserts.getValue()) { - // fixed intervals are never live at block boundaries, so - // they need not be processed in live sets - // process them only in debug mode so that this can be checked - verifyTemp(liveKill, operand); - } - }; - - // iterate all instructions of the block - for (int j = 0; j < numInst; j++) { - final LIRInstruction op = instructions.get(j); - - try (Indent indent2 = Debug.logAndIndent("handle op %d", op.id())) { - op.visitEachInput(useConsumer); - op.visitEachAlive(useConsumer); - // Add uses of live locals from interpreter's point of view for proper debug - // information generation - op.visitEachState(stateConsumer); - op.visitEachTemp(defConsumer); - op.visitEachOutput(defConsumer); - } - } // end of instruction iteration - - BlockData blockSets = blockData.get(block); - blockSets.liveGen = liveGen; - blockSets.liveKill = liveKill; - blockSets.liveIn = new BitSet(liveSize); - blockSets.liveOut = new BitSet(liveSize); - - if (Debug.isLogEnabled()) { - Debug.log("liveGen B%d %s", block.getId(), blockSets.liveGen); - Debug.log("liveKill B%d %s", block.getId(), blockSets.liveKill); - } - - } - } // end of block iteration - } - - private void verifyTemp(BitSet liveKill, Value operand) { - // fixed intervals are never live at block boundaries, so - // they need not be processed in live sets - // process them only in debug mode so that this can be checked - if (isRegister(operand)) { - if (isProcessed(operand)) { - liveKill.set(operandNumber(operand)); - } - } - } - - private void verifyInput(AbstractBlockBase block, BitSet liveKill, Value operand) { - // fixed intervals are never live at block boundaries, so - // they need not be processed in live sets. - // this is checked by these assertions to be sure about it. - // the entry block may have incoming - // values in registers, which is ok. - if (isRegister(operand) && block != ir.getControlFlowGraph().getStartBlock()) { - if (isProcessed(operand)) { - assert liveKill.get(operandNumber(operand)) : "using fixed register that is not defined in this block"; - } - } - } - - /** - * Performs a backward dataflow analysis to compute global live sets (i.e. - * {@link BlockData#liveIn} and {@link BlockData#liveOut}) for each block. - */ - void computeGlobalLiveSets() { - try (Indent indent = Debug.logAndIndent("compute global live sets")) { - int numBlocks = blockCount(); - boolean changeOccurred; - boolean changeOccurredInBlock; - int iterationCount = 0; - BitSet liveOut = new BitSet(liveSetSize()); // scratch set for calculations - - // Perform a backward dataflow analysis to compute liveOut and liveIn for each block. - // The loop is executed until a fixpoint is reached (no changes in an iteration) - do { - changeOccurred = false; - - try (Indent indent2 = Debug.logAndIndent("new iteration %d", iterationCount)) { - - // iterate all blocks in reverse order - for (int i = numBlocks - 1; i >= 0; i--) { - AbstractBlockBase block = blockAt(i); - BlockData blockSets = blockData.get(block); - - changeOccurredInBlock = false; - - // liveOut(block) is the union of liveIn(sux), for successors sux of block - int n = block.getSuccessorCount(); - if (n > 0) { - liveOut.clear(); - // block has successors - if (n > 0) { - for (AbstractBlockBase successor : block.getSuccessors()) { - liveOut.or(blockData.get(successor).liveIn); - } - } - - if (!blockSets.liveOut.equals(liveOut)) { - // A change occurred. Swap the old and new live out - // sets to avoid copying. - BitSet temp = blockSets.liveOut; - blockSets.liveOut = liveOut; - liveOut = temp; - - changeOccurred = true; - changeOccurredInBlock = true; - } - } - - if (iterationCount == 0 || changeOccurredInBlock) { - // liveIn(block) is the union of liveGen(block) with (liveOut(block) & - // !liveKill(block)) - // note: liveIn has to be computed only in first iteration - // or if liveOut has changed! - BitSet liveIn = blockSets.liveIn; - liveIn.clear(); - liveIn.or(blockSets.liveOut); - liveIn.andNot(blockSets.liveKill); - liveIn.or(blockSets.liveGen); - - if (Debug.isLogEnabled()) { - Debug.log("block %d: livein = %s, liveout = %s", block.getId(), liveIn, blockSets.liveOut); - } - } - } - iterationCount++; - - if (changeOccurred && iterationCount > 50) { - throw new BailoutException("too many iterations in computeGlobalLiveSets"); - } - } - } while (changeOccurred); - - if (DetailedAsserts.getValue()) { - verifyLiveness(); - } - - // check that the liveIn set of the first block is empty - AbstractBlockBase startBlock = ir.getControlFlowGraph().getStartBlock(); - if (blockData.get(startBlock).liveIn.cardinality() != 0) { - if (DetailedAsserts.getValue()) { - reportFailure(numBlocks); - } - // bailout if this occurs in product mode. - throw new GraalInternalError("liveIn set of first block must be empty: " + blockData.get(startBlock).liveIn); - } - } - } - - private void reportFailure(int numBlocks) { - try (Scope s = Debug.forceLog()) { - try (Indent indent = Debug.logAndIndent("report failure")) { - - BitSet startBlockLiveIn = blockData.get(ir.getControlFlowGraph().getStartBlock()).liveIn; - try (Indent indent2 = Debug.logAndIndent("Error: liveIn set of first block must be empty (when this fails, variables are used before they are defined):")) { - for (int operandNum = startBlockLiveIn.nextSetBit(0); operandNum >= 0; operandNum = startBlockLiveIn.nextSetBit(operandNum + 1)) { - Interval interval = intervalFor(operandNum); - if (interval != null) { - Value operand = interval.operand; - Debug.log("var %d; operand=%s; node=%s", operandNum, operand, getSourceForOperandFromDebugContext(operand)); - } else { - Debug.log("var %d; missing operand", operandNum); - } - } - } - - // print some additional information to simplify debugging - for (int operandNum = startBlockLiveIn.nextSetBit(0); operandNum >= 0; operandNum = startBlockLiveIn.nextSetBit(operandNum + 1)) { - Interval interval = intervalFor(operandNum); - Value operand = null; - Object valueForOperandFromDebugContext = null; - if (interval != null) { - operand = interval.operand; - valueForOperandFromDebugContext = getSourceForOperandFromDebugContext(operand); - } - try (Indent indent2 = Debug.logAndIndent("---- Detailed information for var %d; operand=%s; node=%s ----", operandNum, operand, valueForOperandFromDebugContext)) { - - Deque> definedIn = new ArrayDeque<>(); - HashSet> usedIn = new HashSet<>(); - for (AbstractBlockBase block : sortedBlocks) { - if (blockData.get(block).liveGen.get(operandNum)) { - usedIn.add(block); - try (Indent indent3 = Debug.logAndIndent("used in block B%d", block.getId())) { - for (LIRInstruction ins : ir.getLIRforBlock(block)) { - try (Indent indent4 = Debug.logAndIndent("%d: %s", ins.id(), ins)) { - ins.forEachState((liveStateOperand, mode, flags) -> { - Debug.log("operand=%s", liveStateOperand); - return liveStateOperand; - }); - } - } - } - } - if (blockData.get(block).liveKill.get(operandNum)) { - definedIn.add(block); - try (Indent indent3 = Debug.logAndIndent("defined in block B%d", block.getId())) { - for (LIRInstruction ins : ir.getLIRforBlock(block)) { - Debug.log("%d: %s", ins.id(), ins); - } - } - } - } - - int[] hitCount = new int[numBlocks]; - - while (!definedIn.isEmpty()) { - AbstractBlockBase block = definedIn.removeFirst(); - usedIn.remove(block); - for (AbstractBlockBase successor : block.getSuccessors()) { - if (successor.isLoopHeader()) { - if (!block.isLoopEnd()) { - definedIn.add(successor); - } - } else { - if (++hitCount[successor.getId()] == successor.getPredecessorCount()) { - definedIn.add(successor); - } - } - } - } - try (Indent indent3 = Debug.logAndIndent("**** offending usages are in: ")) { - for (AbstractBlockBase block : usedIn) { - Debug.log("B%d", block.getId()); - } - } - } - } - } - } catch (Throwable e) { - throw Debug.handle(e); - } - } - - private void verifyLiveness() { - // check that fixed intervals are not live at block boundaries - // (live set must be empty at fixed intervals) - for (AbstractBlockBase block : sortedBlocks) { - for (int j = 0; j <= maxRegisterNumber(); j++) { - assert !blockData.get(block).liveIn.get(j) : "liveIn set of fixed register must be empty"; - assert !blockData.get(block).liveOut.get(j) : "liveOut set of fixed register must be empty"; - assert !blockData.get(block).liveGen.get(j) : "liveGen set of fixed register must be empty"; - } - } - } - - void addUse(AllocatableValue operand, int from, int to, RegisterPriority registerPriority, LIRKind kind) { - if (!isProcessed(operand)) { - return; - } - - Interval interval = getOrCreateInterval(operand); - if (!kind.equals(LIRKind.Illegal)) { - interval.setKind(kind); - } - - interval.addRange(from, to); - - // Register use position at even instruction id. - interval.addUsePos(to & ~1, registerPriority); - - if (Debug.isLogEnabled()) { - Debug.log("add use: %s, from %d to %d (%s)", interval, from, to, registerPriority.name()); - } - } - - void addTemp(AllocatableValue operand, int tempPos, RegisterPriority registerPriority, LIRKind kind) { - if (!isProcessed(operand)) { - return; - } - - Interval interval = getOrCreateInterval(operand); - if (!kind.equals(LIRKind.Illegal)) { - interval.setKind(kind); - } - - interval.addRange(tempPos, tempPos + 1); - interval.addUsePos(tempPos, registerPriority); - interval.addMaterializationValue(null); - - if (Debug.isLogEnabled()) { - 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, LIRInstruction op, RegisterPriority registerPriority, LIRKind kind) { - if (!isProcessed(operand)) { - return; - } - int defPos = op.id(); - - Interval interval = getOrCreateInterval(operand); - if (!kind.equals(LIRKind.Illegal)) { - interval.setKind(kind); - } - - Range r = interval.first(); - if (r.from <= defPos) { - // Update the starting point (when a range is first created for a use, its - // start is the beginning of the current block until a def is encountered.) - r.from = defPos; - interval.addUsePos(defPos, registerPriority); - - } else { - // Dead value - make vacuous interval - // also add register priority for dead intervals - interval.addRange(defPos, defPos + 1); - interval.addUsePos(defPos, registerPriority); - if (Debug.isLogEnabled()) { - Debug.log("Warning: def of operand %s at %d occurs without use", operand, defPos); - } - } - - changeSpillDefinitionPos(interval, defPos); - if (registerPriority == RegisterPriority.None && interval.spillState().ordinal() <= SpillState.StartInMemory.ordinal() && isStackSlot(operand)) { - // detection of method-parameters and roundfp-results - interval.setSpillState(SpillState.StartInMemory); - } - interval.addMaterializationValue(LinearScan.getMaterializedValue(op, operand, interval)); - - if (Debug.isLogEnabled()) { - Debug.log("add def: %s defPos %d (%s)", interval, defPos, registerPriority.name()); - } - } - - /** - * Determines the register priority for an instruction's output/result operand. - */ - static RegisterPriority registerPriorityOfOutputOperand(LIRInstruction op) { - if (op instanceof MoveOp) { - MoveOp move = (MoveOp) op; - if (optimizeMethodArgument(move.getInput())) { - return RegisterPriority.None; - } - } else if (op instanceof LabelOp) { - LabelOp label = (LabelOp) op; - if (label.isPhiIn()) { - return RegisterPriority.None; - } - } - - // all other operands require a register - return RegisterPriority.MustHaveRegister; - } - - /** - * Determines the priority which with an instruction's input operand will be allocated a - * register. - */ - static RegisterPriority registerPriorityOfInputOperand(EnumSet flags) { - if (flags.contains(OperandFlag.STACK)) { - return RegisterPriority.ShouldHaveRegister; - } - // all other operands require a register - return RegisterPriority.MustHaveRegister; - } - - private static boolean optimizeMethodArgument(Value value) { - /* - * Object method arguments that are passed on the stack are currently not optimized because - * this requires that the runtime visits method arguments during stack walking. - */ - return isStackSlot(value) && asStackSlot(value).isInCallerFrame() && value.getKind() != Kind.Object; - } - - /** - * Optimizes moves related to incoming stack based arguments. The interval for the destination - * of such moves is assigned the stack slot (which is in the caller's frame) as its spill slot. - */ - void handleMethodArguments(LIRInstruction op) { - if (op instanceof MoveOp) { - MoveOp move = (MoveOp) op; - if (optimizeMethodArgument(move.getInput())) { - StackSlot slot = asStackSlot(move.getInput()); - if (DetailedAsserts.getValue()) { - assert op.id() > 0 : "invalid id"; - assert blockForId(op.id()).getPredecessorCount() == 0 : "move from stack must be in first block"; - assert isVariable(move.getResult()) : "result of move must be a variable"; - - if (Debug.isLogEnabled()) { - Debug.log("found move from stack slot %s to %s", slot, move.getResult()); - } - } - - Interval interval = intervalFor(move.getResult()); - interval.setSpillSlot(slot); - interval.assignLocation(slot); - } - } - } - - void addRegisterHint(final LIRInstruction op, final Value targetValue, OperandMode mode, EnumSet flags, final boolean hintAtDef) { - if (flags.contains(OperandFlag.HINT) && isVariableOrRegister(targetValue)) { - - op.forEachRegisterHint(targetValue, mode, (registerHint, valueMode, valueFlags) -> { - if (isVariableOrRegister(registerHint)) { - Interval from = getOrCreateInterval((AllocatableValue) registerHint); - Interval to = getOrCreateInterval((AllocatableValue) targetValue); - - /* hints always point from def to use */ - if (hintAtDef) { - to.setLocationHint(from); - } else { - from.setLocationHint(to); - } - if (Debug.isLogEnabled()) { - Debug.log("operation at opId %d: added hint from interval %d to %d", op.id(), from.operandNumber, to.operandNumber); - } - - return registerHint; - } - return null; - }); - } - } - - void buildIntervals() { - - try (Indent indent = Debug.logAndIndent("build intervals")) { - InstructionValueConsumer outputConsumer = (op, operand, mode, flags) -> { - if (isVariableOrRegister(operand)) { - addDef((AllocatableValue) operand, op, registerPriorityOfOutputOperand(op), operand.getLIRKind()); - addRegisterHint(op, operand, mode, flags, true); - } - }; - - InstructionValueConsumer tempConsumer = (op, operand, mode, flags) -> { - if (isVariableOrRegister(operand)) { - addTemp((AllocatableValue) operand, op.id(), RegisterPriority.MustHaveRegister, operand.getLIRKind()); - addRegisterHint(op, operand, mode, flags, false); - } - }; - - InstructionValueConsumer aliveConsumer = (op, operand, mode, flags) -> { - if (isVariableOrRegister(operand)) { - RegisterPriority p = registerPriorityOfInputOperand(flags); - int opId = op.id(); - int blockFrom = getFirstLirInstructionId((blockForId(opId))); - addUse((AllocatableValue) operand, blockFrom, opId + 1, p, operand.getLIRKind()); - addRegisterHint(op, operand, mode, flags, false); - } - }; - - InstructionValueConsumer inputConsumer = (op, operand, mode, flags) -> { - if (isVariableOrRegister(operand)) { - int opId = op.id(); - int blockFrom = getFirstLirInstructionId((blockForId(opId))); - RegisterPriority p = registerPriorityOfInputOperand(flags); - addUse((AllocatableValue) operand, blockFrom, opId, p, operand.getLIRKind()); - addRegisterHint(op, operand, mode, flags, false); - } - }; - - InstructionValueConsumer stateProc = (op, operand, mode, flags) -> { - if (isVariableOrRegister(operand)) { - int opId = op.id(); - int blockFrom = getFirstLirInstructionId((blockForId(opId))); - addUse((AllocatableValue) operand, blockFrom, opId + 1, RegisterPriority.None, operand.getLIRKind()); - } - }; - - // create a list with all caller-save registers (cpu, fpu, xmm) - Register[] callerSaveRegs = regAllocConfig.getRegisterConfig().getCallerSaveRegisters(); - - // iterate all blocks in reverse order - for (int i = blockCount() - 1; i >= 0; i--) { - - AbstractBlockBase block = blockAt(i); - try (Indent indent2 = Debug.logAndIndent("handle block %d", block.getId())) { - - List instructions = ir.getLIRforBlock(block); - final int blockFrom = getFirstLirInstructionId(block); - int blockTo = getLastLirInstructionId(block); - - assert blockFrom == instructions.get(0).id(); - assert blockTo == instructions.get(instructions.size() - 1).id(); - - // Update intervals for operands live at the end of this block; - BitSet live = blockData.get(block).liveOut; - for (int operandNum = live.nextSetBit(0); operandNum >= 0; operandNum = live.nextSetBit(operandNum + 1)) { - assert live.get(operandNum) : "should not stop here otherwise"; - AllocatableValue operand = intervalFor(operandNum).operand; - if (Debug.isLogEnabled()) { - Debug.log("live in %d: %s", operandNum, operand); - } - - addUse(operand, blockFrom, blockTo + 2, RegisterPriority.None, LIRKind.Illegal); - - // add special use positions for loop-end blocks when the - // interval is used anywhere inside this loop. It's possible - // that the block was part of a non-natural loop, so it might - // have an invalid loop index. - if (block.isLoopEnd() && block.getLoop() != null && isIntervalInLoop(operandNum, block.getLoop().getIndex())) { - intervalFor(operandNum).addUsePos(blockTo + 1, RegisterPriority.LiveAtLoopEnd); - } - } - - // iterate all instructions of the block in reverse order. - // definitions of intervals are processed before uses - for (int j = instructions.size() - 1; j >= 0; j--) { - final LIRInstruction op = instructions.get(j); - final int opId = op.id(); - - try (Indent indent3 = Debug.logAndIndent("handle inst %d: %s", opId, op)) { - - // add a temp range for each register if operation destroys - // caller-save registers - if (op.destroysCallerSavedRegisters()) { - for (Register r : callerSaveRegs) { - if (attributes(r).isAllocatable()) { - addTemp(r.asValue(), opId, RegisterPriority.None, LIRKind.Illegal); - } - } - if (Debug.isLogEnabled()) { - Debug.log("operation destroys all caller-save registers"); - } - } - - op.visitEachOutput(outputConsumer); - op.visitEachTemp(tempConsumer); - op.visitEachAlive(aliveConsumer); - op.visitEachInput(inputConsumer); - - // Add uses of live locals from interpreter's point of view for proper - // debug information generation - // Treat these operands as temp values (if the live range is extended - // to a call site, the value would be in a register at - // the call otherwise) - op.visitEachState(stateProc); - - // special steps for some instructions (especially moves) - handleMethodArguments(op); - - } - - } // end of instruction iteration - } - } // end of block iteration - - // add the range [0, 1] to all fixed intervals. - // the register allocator need not handle unhandled fixed intervals - for (Interval interval : intervals) { - if (interval != null && isRegister(interval.operand)) { - interval.addRange(0, 1); - } - } - } - } - // * Phase 5: actual register allocation private static boolean isSorted(Interval[] intervals) { @@ -1461,30 +581,6 @@ sortedIntervals = combinedList; } - void allocateRegisters() { - try (Indent indent = Debug.logAndIndent("allocate registers")) { - Interval precoloredIntervals; - Interval notPrecoloredIntervals; - - Interval.Pair result = createUnhandledLists(IS_PRECOLORED_INTERVAL, IS_VARIABLE_INTERVAL); - precoloredIntervals = result.first; - notPrecoloredIntervals = result.second; - - // allocate cpu registers - LinearScanWalker lsw; - if (OptimizingLinearScanWalker.Options.LSRAOptimization.getValue()) { - lsw = new OptimizingLinearScanWalker(this, precoloredIntervals, notPrecoloredIntervals); - } else { - lsw = new LinearScanWalker(this, precoloredIntervals, notPrecoloredIntervals); - } - lsw.walk(); - lsw.finishAllocation(); - } - } - - // * Phase 6: resolve data flow - // (insert moves at edges between blocks if intervals have been split) - // wrapper for Interval.splitChildAtOpId that performs a bailout in product mode // instead of returning null Interval splitChildAtOpId(Interval interval, int opId, LIRInstruction.OperandMode mode) { @@ -1500,579 +596,86 @@ throw new BailoutException("LinearScan: interval is null"); } - void resolveCollectMappings(AbstractBlockBase fromBlock, AbstractBlockBase toBlock, AbstractBlockBase midBlock, MoveResolver moveResolver) { - assert moveResolver.checkEmpty(); - assert midBlock == null || - (midBlock.getPredecessorCount() == 1 && midBlock.getSuccessorCount() == 1 && midBlock.getPredecessors().get(0).equals(fromBlock) && midBlock.getSuccessors().get(0).equals( - toBlock)); - - int toBlockFirstInstructionId = getFirstLirInstructionId(toBlock); - int fromBlockLastInstructionId = getLastLirInstructionId(fromBlock) + 1; - int numOperands = operandSize(); - BitSet liveAtEdge = blockData.get(toBlock).liveIn; - - // visit all variables for which the liveAtEdge bit is set - for (int operandNum = liveAtEdge.nextSetBit(0); operandNum >= 0; operandNum = liveAtEdge.nextSetBit(operandNum + 1)) { - assert operandNum < numOperands : "live information set for not exisiting interval"; - assert blockData.get(fromBlock).liveOut.get(operandNum) && blockData.get(toBlock).liveIn.get(operandNum) : "interval not live at this edge"; - - Interval fromInterval = splitChildAtOpId(intervalFor(operandNum), fromBlockLastInstructionId, LIRInstruction.OperandMode.DEF); - Interval toInterval = splitChildAtOpId(intervalFor(operandNum), toBlockFirstInstructionId, LIRInstruction.OperandMode.DEF); - - if (fromInterval != toInterval && !fromInterval.location().equals(toInterval.location())) { - // need to insert move instruction - moveResolver.addMapping(fromInterval, toInterval); - } - } - } - - void resolveFindInsertPos(AbstractBlockBase fromBlock, AbstractBlockBase toBlock, MoveResolver moveResolver) { - if (fromBlock.getSuccessorCount() <= 1) { - if (Debug.isLogEnabled()) { - Debug.log("inserting moves at end of fromBlock B%d", fromBlock.getId()); - } - - List instructions = ir.getLIRforBlock(fromBlock); - LIRInstruction instr = instructions.get(instructions.size() - 1); - if (instr instanceof StandardOp.JumpOp) { - // insert moves before branch - moveResolver.setInsertPosition(instructions, instructions.size() - 1); - } else { - moveResolver.setInsertPosition(instructions, instructions.size()); - } - - } else { - if (Debug.isLogEnabled()) { - Debug.log("inserting moves at beginning of toBlock B%d", toBlock.getId()); - } - - if (DetailedAsserts.getValue()) { - assert ir.getLIRforBlock(fromBlock).get(0) instanceof StandardOp.LabelOp : "block does not start with a label"; - - // because the number of predecessor edges matches the number of - // successor edges, blocks which are reached by switch statements - // may have be more than one predecessor but it will be guaranteed - // that all predecessors will be the same. - for (AbstractBlockBase predecessor : toBlock.getPredecessors()) { - assert fromBlock == predecessor : "all critical edges must be broken"; - } - } - - moveResolver.setInsertPosition(ir.getLIRforBlock(toBlock), 1); - } - } - - /** - * Inserts necessary moves (spilling or reloading) at edges between blocks for intervals that - * have been split. - */ - void resolveDataFlow() { - try (Indent indent = Debug.logAndIndent("resolve data flow")) { - - int numBlocks = blockCount(); - MoveResolver moveResolver = createMoveResolver(); - BitSet blockCompleted = new BitSet(numBlocks); - BitSet alreadyResolved = new BitSet(numBlocks); - - for (AbstractBlockBase block : sortedBlocks) { - - // check if block has only one predecessor and only one successor - if (block.getPredecessorCount() == 1 && block.getSuccessorCount() == 1) { - List instructions = ir.getLIRforBlock(block); - assert instructions.get(0) instanceof StandardOp.LabelOp : "block must start with label"; - assert instructions.get(instructions.size() - 1) instanceof StandardOp.JumpOp : "block with successor must end with unconditional jump"; - - // check if block is empty (only label and branch) - if (instructions.size() == 2) { - AbstractBlockBase pred = block.getPredecessors().iterator().next(); - AbstractBlockBase sux = block.getSuccessors().iterator().next(); - - // prevent optimization of two consecutive blocks - if (!blockCompleted.get(pred.getLinearScanNumber()) && !blockCompleted.get(sux.getLinearScanNumber())) { - if (Debug.isLogEnabled()) { - Debug.log(" optimizing empty block B%d (pred: B%d, sux: B%d)", block.getId(), pred.getId(), sux.getId()); - } - - blockCompleted.set(block.getLinearScanNumber()); - - // directly resolve between pred and sux (without looking - // at the empty block - // between) - resolveCollectMappings(pred, sux, block, moveResolver); - if (moveResolver.hasMappings()) { - moveResolver.setInsertPosition(instructions, 1); - moveResolver.resolveAndAppendMoves(); - } - } - } - } - } - - for (AbstractBlockBase fromBlock : sortedBlocks) { - if (!blockCompleted.get(fromBlock.getLinearScanNumber())) { - alreadyResolved.clear(); - alreadyResolved.or(blockCompleted); - - for (AbstractBlockBase toBlock : fromBlock.getSuccessors()) { - - // check for duplicate edges between the same blocks (can happen with switch - // blocks) - if (!alreadyResolved.get(toBlock.getLinearScanNumber())) { - if (Debug.isLogEnabled()) { - Debug.log("processing edge between B%d and B%d", fromBlock.getId(), toBlock.getId()); - } - - alreadyResolved.set(toBlock.getLinearScanNumber()); - - // collect all intervals that have been split between - // fromBlock and toBlock - resolveCollectMappings(fromBlock, toBlock, null, moveResolver); - if (moveResolver.hasMappings()) { - resolveFindInsertPos(fromBlock, toBlock, moveResolver); - moveResolver.resolveAndAppendMoves(); - } - } - } - } - } - - } - } - - // * Phase 7: assign register numbers back to LIR - // (includes computation of debug information and oop maps) - static StackSlotValue canonicalSpillOpr(Interval interval) { assert interval.spillSlot() != null : "canonical spill slot not set"; return interval.spillSlot(); } - /** - * Assigns the allocated location for an LIR instruction operand back into the instruction. - * - * @param operand an LIR instruction operand - * @param opId the id of the LIR instruction using {@code operand} - * @param mode the usage mode for {@code operand} by the instruction - * @return the location assigned for the operand - */ - private Value colorLirOperand(Variable operand, int opId, OperandMode mode) { + boolean isMaterialized(AllocatableValue operand, int opId, OperandMode mode) { Interval interval = intervalFor(operand); assert interval != null : "interval must exist"; if (opId != -1) { - if (DetailedAsserts.getValue()) { - AbstractBlockBase block = blockForId(opId); - if (block.getSuccessorCount() <= 1 && opId == getLastLirInstructionId(block)) { - /* - * Check if spill moves could have been appended at the end of this block, but - * before the branch instruction. So the split child information for this branch - * would be incorrect. - */ - LIRInstruction instr = ir.getLIRforBlock(block).get(ir.getLIRforBlock(block).size() - 1); - if (instr instanceof StandardOp.JumpOp) { - if (blockData.get(block).liveOut.get(operandNumber(operand))) { - assert false : String.format( - "can't get split child for the last branch of a block because the information would be incorrect (moves are inserted before the branch in resolveDataFlow) block=%s, instruction=%s, operand=%s", - block, instr, operand); - } - } - } - } - - // operands are not changed when an interval is split during allocation, - // so search the right interval here - interval = splitChildAtOpId(interval, opId, mode); - } - - if (isIllegal(interval.location()) && interval.canMaterialize()) { - assert mode != OperandMode.DEF; - return interval.getMaterializedValue(); - } - return interval.location(); - } - - private boolean isMaterialized(AllocatableValue operand, int opId, OperandMode mode) { - Interval interval = intervalFor(operand); - assert interval != null : "interval must exist"; - - if (opId != -1) { - // operands are not changed when an interval is split during allocation, - // so search the right interval here + /* + * Operands are not changed when an interval is split during allocation, so search the + * right interval here. + */ interval = splitChildAtOpId(interval, opId, mode); } return isIllegal(interval.location()) && interval.canMaterialize(); } - protected IntervalWalker initIntervalWalker(IntervalPredicate predicate) { - // setup lists of potential oops for walking - Interval oopIntervals; - Interval nonOopIntervals; - - 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 - // with a high operation id - nonOopIntervals = new Interval(Value.ILLEGAL, -1); - nonOopIntervals.addRange(Integer.MAX_VALUE - 2, Integer.MAX_VALUE - 1); - - return new IntervalWalker(this, oopIntervals, nonOopIntervals); - } - - private boolean isCallerSave(Value operand) { + boolean isCallerSave(Value operand) { return attributes(asRegister(operand)).isCallerSave(); } - /** - * @param op - * @param operand - * @param valueMode - * @param flags - * @see InstructionValueProcedure#doValue(LIRInstruction, Value, OperandMode, EnumSet) - */ - private Value debugInfoProcedure(LIRInstruction op, Value operand, OperandMode valueMode, EnumSet flags) { - if (isVirtualStackSlot(operand)) { - return operand; - } - int tempOpId = op.id(); - OperandMode mode = OperandMode.USE; - AbstractBlockBase block = blockForId(tempOpId); - if (block.getSuccessorCount() == 1 && tempOpId == getLastLirInstructionId(block)) { - // generating debug information for the last instruction of a block. - // if this instruction is a branch, spill moves are inserted before this branch - // and so the wrong operand would be returned (spill moves at block boundaries - // are not - // considered in the live ranges of intervals) - // Solution: use the first opId of the branch target block instead. - final LIRInstruction instr = ir.getLIRforBlock(block).get(ir.getLIRforBlock(block).size() - 1); - if (instr instanceof StandardOp.JumpOp) { - if (blockData.get(block).liveOut.get(operandNumber(operand))) { - tempOpId = getFirstLirInstructionId(block.getSuccessors().iterator().next()); - mode = OperandMode.DEF; - } - } - } - - // Get current location of operand - // The operand must be live because debug information is considered when building - // the intervals - // if the interval is not live, colorLirOperand will cause an assert on failure - Value result = colorLirOperand((Variable) operand, tempOpId, mode); - assert !hasCall(tempOpId) || isStackSlotValue(result) || isConstant(result) || !isCallerSave(result) : "cannot have caller-save register operands at calls"; - return result; - } - - private void computeDebugInfo(final LIRInstruction op, LIRFrameState info) { - info.forEachState(op, this::debugInfoProcedure); - } - - private void assignLocations(List instructions) { - int numInst = instructions.size(); - boolean hasDead = false; - - InstructionValueProcedure assignProc = (op, operand, mode, flags) -> isVariable(operand) ? colorLirOperand((Variable) operand, op.id(), mode) : operand; - for (int j = 0; j < numInst; j++) { - final LIRInstruction op = instructions.get(j); - if (op == null) { // this can happen when spill-moves are removed in eliminateSpillMoves - hasDead = true; - continue; - } - - // remove useless moves - MoveOp move = null; - if (op instanceof MoveOp) { - move = (MoveOp) op; - AllocatableValue result = move.getResult(); - if (isVariable(result) && isMaterialized(result, op.id(), OperandMode.DEF)) { - /* - * This happens if a materializable interval is originally not spilled but then - * kicked out in LinearScanWalker.splitForSpilling(). When kicking out such an - * interval this move operation was already generated. - */ - instructions.set(j, null); - hasDead = true; - continue; - } - } - - op.forEachInput(assignProc); - op.forEachAlive(assignProc); - op.forEachTemp(assignProc); - op.forEachOutput(assignProc); - - // compute reference map and debug information - op.forEachState((inst, state) -> computeDebugInfo(inst, state)); - - // remove useless moves - if (move != null) { - if (move.getInput().equals(move.getResult())) { - instructions.set(j, null); - hasDead = true; - } - } - } - - if (hasDead) { - // Remove null values from the list. - instructions.removeAll(Collections.singleton(null)); - } - } - - private void assignLocations() { - try (Indent indent = Debug.logAndIndent("assign locations")) { - for (AbstractBlockBase block : sortedBlocks) { - try (Indent indent2 = Debug.logAndIndent("assign locations in block B%d", block.getId())) { - assignLocations(ir.getLIRforBlock(block)); - } - } - } - } - - private static final DebugTimer lifetimeTimer = Debug.timer("LinearScan_LifetimeAnalysis"); - private static final DebugTimer registerAllocationTimer = Debug.timer("LinearScan_RegisterAllocation"); - private static final DebugTimer optimizedSpillPositionTimer = Debug.timer("LinearScan_OptimizeSpillPosition"); - private static final DebugTimer resolveDataFlowTimer = Debug.timer("LinearScan_ResolveDataFlow"); - private static final DebugTimer eliminateSpillMoveTimer = Debug.timer("LinearScan_EliminateSpillMove"); - private static final DebugTimer assignLocationsTimer = Debug.timer("LinearScan_AssignLocations"); - - void allocate() { + > void allocate(TargetDescription target, LIRGenerationResult lirGenRes, List codeEmittingOrder, List linearScanOrder, SpillMoveFactory spillMoveFactory) { /* * This is the point to enable debug logging for the whole register allocation. */ try (Indent indent = Debug.logAndIndent("LinearScan allocate")) { + AllocationContext context = new AllocationContext(spillMoveFactory); - try (Scope s = Debug.scope("LifetimeAnalysis"); DebugCloseable a = lifetimeTimer.start()) { - numberInstructions(); - printLir("Before register allocation", true); - computeLocalLiveSets(); - computeGlobalLiveSets(); - buildIntervals(); - sortIntervalsBeforeAllocation(); - } catch (Throwable e) { - throw Debug.handle(e); - } + createLifetimeAnalysisPhase().apply(target, lirGenRes, codeEmittingOrder, linearScanOrder, context, false); - try (Scope s = Debug.scope("RegisterAllocation"); DebugCloseable a = registerAllocationTimer.start()) { - printIntervals("Before register allocation"); - allocateRegisters(); - } catch (Throwable e) { - throw Debug.handle(e); - } + sortIntervalsBeforeAllocation(); - if (Options.LSRAOptimizeSpillPosition.getValue()) { - try (Scope s = Debug.scope("OptimizeSpillPosition"); DebugCloseable a = optimizedSpillPositionTimer.start()) { - optimizeSpillPosition(); - } catch (Throwable e) { - throw Debug.handle(e); - } - } + createRegisterAllocationPhase().apply(target, lirGenRes, codeEmittingOrder, linearScanOrder, context, false); - try (Scope s = Debug.scope("ResolveDataFlow"); DebugCloseable a = resolveDataFlowTimer.start()) { - resolveDataFlow(); - } catch (Throwable e) { - throw Debug.handle(e); + if (LinearScan.Options.LSRAOptimizeSpillPosition.getValue()) { + createOptimizeSpillPositionPhase().apply(target, lirGenRes, codeEmittingOrder, linearScanOrder, context, false); } - - try (Scope s = Debug.scope("DebugInfo")) { - printIntervals("After register allocation"); - printLir("After register allocation", true); - - sortIntervalsAfterAllocation(); - - if (DetailedAsserts.getValue()) { - verify(); - } - - beforeSpillMoveElimination(); + createResolveDataFlowPhase().apply(target, lirGenRes, codeEmittingOrder, linearScanOrder, context); - try (Scope s1 = Debug.scope("EliminateSpillMove"); DebugCloseable a = eliminateSpillMoveTimer.start()) { - eliminateSpillMoves(); - } catch (Throwable e) { - throw Debug.handle(e); - } - printLir("After spill move elimination", true); + sortIntervalsAfterAllocation(); - try (Scope s1 = Debug.scope("AssignLocations"); DebugCloseable a = assignLocationsTimer.start()) { - assignLocations(); - } catch (Throwable e) { - throw Debug.handle(e); - } - - if (DetailedAsserts.getValue()) { - verifyIntervals(); - } - } catch (Throwable e) { - throw Debug.handle(e); + if (DetailedAsserts.getValue()) { + verify(); } - printLir("After register number assignment", true); - } - } - - protected void beforeSpillMoveElimination() { - } - - private static final DebugMetric betterSpillPos = Debug.metric("BetterSpillPosition"); - private static final DebugMetric betterSpillPosWithLowerProbability = Debug.metric("BetterSpillPositionWithLowerProbability"); - - private void optimizeSpillPosition() { - LIRInsertionBuffer[] insertionBuffers = new LIRInsertionBuffer[ir.linearScanOrder().size()]; - for (Interval interval : intervals) { - if (interval != null && interval.isSplitParent() && interval.spillState() == SpillState.SpillInDominator) { - AbstractBlockBase defBlock = blockForId(interval.spillDefinitionPos()); - AbstractBlockBase spillBlock = null; - Interval firstSpillChild = null; - try (Indent indent = Debug.logAndIndent("interval %s (%s)", interval, defBlock)) { - for (Interval splitChild : interval.getSplitChildren()) { - if (isStackSlotValue(splitChild.location())) { - if (firstSpillChild == null || splitChild.from() < firstSpillChild.from()) { - firstSpillChild = splitChild; - } else { - assert firstSpillChild.from() < splitChild.from(); - } - // iterate all blocks where the interval has use positions - for (AbstractBlockBase splitBlock : blocksForInterval(splitChild)) { - if (dominates(defBlock, splitBlock)) { - if (Debug.isLogEnabled()) { - Debug.log("Split interval %s, block %s", splitChild, splitBlock); - } - if (spillBlock == null) { - spillBlock = splitBlock; - } else { - spillBlock = commonDominator(spillBlock, splitBlock); - assert spillBlock != null; - } - } - } - } - } - if (spillBlock == null) { - // no spill interval - interval.setSpillState(SpillState.StoreAtDefinition); - } else { - // move out of loops - if (defBlock.getLoopDepth() < spillBlock.getLoopDepth()) { - spillBlock = moveSpillOutOfLoop(defBlock, spillBlock); - } + createSpillMoveEliminationPhase().apply(target, lirGenRes, codeEmittingOrder, linearScanOrder, context); + createAssignLocationsPhase().apply(target, lirGenRes, codeEmittingOrder, linearScanOrder, context); - /* - * If the spill block is the begin of the first split child (aka the value - * is on the stack) spill in the dominator. - */ - assert firstSpillChild != null; - if (!defBlock.equals(spillBlock) && spillBlock.equals(blockForId(firstSpillChild.from()))) { - AbstractBlockBase dom = spillBlock.getDominator(); - if (Debug.isLogEnabled()) { - Debug.log("Spill block (%s) is the beginning of a spill child -> use dominator (%s)", spillBlock, dom); - } - spillBlock = dom; - } - - if (!defBlock.equals(spillBlock)) { - assert dominates(defBlock, spillBlock); - betterSpillPos.increment(); - if (Debug.isLogEnabled()) { - Debug.log("Better spill position found (Block %s)", spillBlock); - } - - if (defBlock.probability() <= spillBlock.probability()) { - // better spill block has the same probability -> do nothing - interval.setSpillState(SpillState.StoreAtDefinition); - } else { - LIRInsertionBuffer insertionBuffer = insertionBuffers[spillBlock.getId()]; - if (insertionBuffer == null) { - insertionBuffer = new LIRInsertionBuffer(); - insertionBuffers[spillBlock.getId()] = insertionBuffer; - insertionBuffer.init(ir.getLIRforBlock(spillBlock)); - } - int spillOpId = getFirstLirInstructionId(spillBlock); - // insert spill move - AllocatableValue fromLocation = interval.getSplitChildAtOpId(spillOpId, OperandMode.DEF, this).location(); - AllocatableValue toLocation = canonicalSpillOpr(interval); - LIRInstruction move = getSpillMoveFactory().createMove(toLocation, fromLocation); - move.setId(DOMINATOR_SPILL_MOVE_ID); - /* - * We can use the insertion buffer directly because we always insert - * at position 1. - */ - insertionBuffer.append(1, move); - - betterSpillPosWithLowerProbability.increment(); - interval.setSpillDefinitionPos(spillOpId); - } - } else { - // definition is the best choice - interval.setSpillState(SpillState.StoreAtDefinition); - } - } - } - } - } - for (LIRInsertionBuffer insertionBuffer : insertionBuffers) { - if (insertionBuffer != null) { - assert insertionBuffer.initialized() : "Insertion buffer is nonnull but not initialized!"; - insertionBuffer.finish(); + if (DetailedAsserts.getValue()) { + verifyIntervals(); } } } - /** - * Iterate over all {@link AbstractBlockBase blocks} of an interval. - */ - private class IntervalBlockIterator implements Iterator> { - - Range range; - AbstractBlockBase block; - - public IntervalBlockIterator(Interval interval) { - range = interval.first(); - block = blockForId(range.from); - } + protected LinearScanLifetimeAnalysisPhase createLifetimeAnalysisPhase() { + return new LinearScanLifetimeAnalysisPhase(this); + } - public AbstractBlockBase next() { - AbstractBlockBase currentBlock = block; - int nextBlockIndex = block.getLinearScanNumber() + 1; - if (nextBlockIndex < sortedBlocks.size()) { - block = sortedBlocks.get(nextBlockIndex); - if (range.to <= getFirstLirInstructionId(block)) { - range = range.next; - if (range == Range.EndMarker) { - block = null; - } else { - block = blockForId(range.from); - } - } - } else { - block = null; - } - return currentBlock; - } - - public boolean hasNext() { - return block != null; - } + protected LinearScanRegisterAllocationPhase createRegisterAllocationPhase() { + return new LinearScanRegisterAllocationPhase(this); } - private Iterable> blocksForInterval(Interval interval) { - return new Iterable>() { - public Iterator> iterator() { - return new IntervalBlockIterator(interval); - } - }; + protected LinearScanOptimizeSpillPositionPhase createOptimizeSpillPositionPhase() { + return new LinearScanOptimizeSpillPositionPhase(this); } - private static AbstractBlockBase moveSpillOutOfLoop(AbstractBlockBase defBlock, AbstractBlockBase spillBlock) { - int defLoopDepth = defBlock.getLoopDepth(); - for (AbstractBlockBase block = spillBlock.getDominator(); !defBlock.equals(block); block = block.getDominator()) { - assert block != null : "spill block not dominated by definition block?"; - if (block.getLoopDepth() <= defLoopDepth) { - assert block.getLoopDepth() == defLoopDepth : "Cannot spill an interval outside of the loop where it is defined!"; - return block; - } - } - return defBlock; + protected LinearScanResolveDataFlowPhase createResolveDataFlowPhase() { + return new LinearScanResolveDataFlowPhase(this); + } + + protected LinearScanEliminateSpillMovePhase createSpillMoveEliminationPhase() { + return new LinearScanEliminateSpillMovePhase(this); + } + + protected LinearScanAssignLocationsPhase createAssignLocationsPhase() { + return new LinearScanAssignLocationsPhase(this); } void printIntervals(String label) { @@ -2229,14 +832,18 @@ iw.walkBefore(op.id()); boolean checkLive = true; - // Make sure none of the fixed registers is live across an - // oopmap since we can't handle that correctly. + /* + * Make sure none of the fixed registers is live across an oopmap since we + * can't handle that correctly. + */ if (checkLive) { for (Interval interval = iw.activeLists.get(RegisterBinding.Fixed); interval != Interval.EndMarker; interval = interval.next) { if (interval.currentTo() > op.id() + 1) { - // This interval is live out of this op so make sure - // that this interval represents some value that's - // referenced by this op either as an input or output. + /* + * This interval is live out of this op so make sure that this + * interval represents some value that's referenced by this op + * either as an input or output. + */ checkConsumer.curInterval = interval; checkConsumer.ok = false; @@ -2255,37 +862,4 @@ } } - /** - * Returns a value for a interval definition, which can be used for re-materialization. - * - * @param op An instruction which defines a value - * @param operand The destination operand of the instruction - * @param interval The interval for this defined value. - * @return Returns the value which is moved to the instruction and which can be reused at all - * reload-locations in case the interval of this instruction is spilled. Currently this - * can only be a {@link JavaConstant}. - */ - public static JavaConstant getMaterializedValue(LIRInstruction op, Value operand, Interval interval) { - if (op instanceof MoveOp) { - MoveOp move = (MoveOp) op; - if (move.getInput() instanceof JavaConstant) { - /* - * Check if the interval has any uses which would accept an stack location (priority - * == ShouldHaveRegister). Rematerialization of such intervals can result in a - * degradation, because rematerialization always inserts a constant load, even if - * the value is not needed in a register. - */ - Interval.UsePosList usePosList = interval.usePosList(); - int numUsePos = usePosList.size(); - for (int useIdx = 0; useIdx < numUsePos; useIdx++) { - Interval.RegisterPriority priority = usePosList.registerPriority(useIdx); - if (priority == Interval.RegisterPriority.ShouldHaveRegister) { - return null; - } - } - return (JavaConstant) move.getInput(); - } - } - return null; - } } diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanAssignLocationsPhase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanAssignLocationsPhase.java Tue May 12 20:56:04 2015 +0200 @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.lir.alloc.lsra; + +import static com.oracle.graal.api.code.ValueUtil.*; +import static com.oracle.graal.compiler.common.GraalOptions.*; +import static com.oracle.graal.lir.LIRValueUtil.*; + +import java.util.*; + +import com.oracle.graal.api.code.*; +import com.oracle.graal.api.meta.*; +import com.oracle.graal.compiler.common.cfg.*; +import com.oracle.graal.debug.*; +import com.oracle.graal.lir.*; +import com.oracle.graal.lir.LIRInstruction.*; +import com.oracle.graal.lir.StandardOp.*; +import com.oracle.graal.lir.gen.*; +import com.oracle.graal.lir.gen.LIRGeneratorTool.*; +import com.oracle.graal.lir.phases.*; + +/** + * Phase 7: Assign register numbers back to LIR. + */ +final class LinearScanAssignLocationsPhase extends AllocationPhase { + + private final LinearScan allocator; + + LinearScanAssignLocationsPhase(LinearScan allocator) { + this.allocator = allocator; + } + + @Override + protected > void run(TargetDescription target, LIRGenerationResult lirGenRes, List codeEmittingOrder, List linearScanOrder, SpillMoveFactory spillMoveFactory) { + assignLocations(); + } + + /** + * Assigns the allocated location for an LIR instruction operand back into the instruction. + * + * @param operand an LIR instruction operand + * @param opId the id of the LIR instruction using {@code operand} + * @param mode the usage mode for {@code operand} by the instruction + * @return the location assigned for the operand + */ + private Value colorLirOperand(Variable operand, int opId, OperandMode mode) { + Interval interval = allocator.intervalFor(operand); + assert interval != null : "interval must exist"; + + if (opId != -1) { + if (DetailedAsserts.getValue()) { + AbstractBlockBase block = allocator.blockForId(opId); + if (block.getSuccessorCount() <= 1 && opId == allocator.getLastLirInstructionId(block)) { + /* + * Check if spill moves could have been appended at the end of this block, but + * before the branch instruction. So the split child information for this branch + * would be incorrect. + */ + LIRInstruction instr = allocator.ir.getLIRforBlock(block).get(allocator.ir.getLIRforBlock(block).size() - 1); + if (instr instanceof StandardOp.JumpOp) { + if (allocator.getBlockData(block).liveOut.get(allocator.operandNumber(operand))) { + assert false : String.format( + "can't get split child for the last branch of a block because the information would be incorrect (moves are inserted before the branch in resolveDataFlow) block=%s, instruction=%s, operand=%s", + block, instr, operand); + } + } + } + } + + /* + * Operands are not changed when an interval is split during allocation, so search the + * right interval here. + */ + interval = allocator.splitChildAtOpId(interval, opId, mode); + } + + if (isIllegal(interval.location()) && interval.canMaterialize()) { + assert mode != OperandMode.DEF; + return interval.getMaterializedValue(); + } + return interval.location(); + } + + /** + * @param op + * @param operand + * @param valueMode + * @param flags + * @see InstructionValueProcedure#doValue(LIRInstruction, Value, OperandMode, EnumSet) + */ + private Value debugInfoProcedure(LIRInstruction op, Value operand, OperandMode valueMode, EnumSet flags) { + if (isVirtualStackSlot(operand)) { + return operand; + } + int tempOpId = op.id(); + OperandMode mode = OperandMode.USE; + AbstractBlockBase block = allocator.blockForId(tempOpId); + if (block.getSuccessorCount() == 1 && tempOpId == allocator.getLastLirInstructionId(block)) { + /* + * Generating debug information for the last instruction of a block. If this instruction + * is a branch, spill moves are inserted before this branch and so the wrong operand + * would be returned (spill moves at block boundaries are not considered in the live + * ranges of intervals). + * + * Solution: use the first opId of the branch target block instead. + */ + final LIRInstruction instr = allocator.ir.getLIRforBlock(block).get(allocator.ir.getLIRforBlock(block).size() - 1); + if (instr instanceof StandardOp.JumpOp) { + if (allocator.getBlockData(block).liveOut.get(allocator.operandNumber(operand))) { + tempOpId = allocator.getFirstLirInstructionId(block.getSuccessors().iterator().next()); + mode = OperandMode.DEF; + } + } + } + + /* + * Get current location of operand. The operand must be live because debug information is + * considered when building the intervals if the interval is not live, colorLirOperand will + * cause an assert on failure. + */ + Value result = colorLirOperand((Variable) operand, tempOpId, mode); + assert !allocator.hasCall(tempOpId) || isStackSlotValue(result) || isConstant(result) || !allocator.isCallerSave(result) : "cannot have caller-save register operands at calls"; + return result; + } + + private void computeDebugInfo(final LIRInstruction op, LIRFrameState info) { + info.forEachState(op, this::debugInfoProcedure); + } + + private void assignLocations(List instructions) { + int numInst = instructions.size(); + boolean hasDead = false; + + InstructionValueProcedure assignProc = (op, operand, mode, flags) -> isVariable(operand) ? colorLirOperand((Variable) operand, op.id(), mode) : operand; + for (int j = 0; j < numInst; j++) { + final LIRInstruction op = instructions.get(j); + if (op == null) { + /* + * this can happen when spill-moves are removed in eliminateSpillMoves + */ + hasDead = true; + continue; + } + + // remove useless moves + MoveOp move = null; + if (op instanceof MoveOp) { + move = (MoveOp) op; + AllocatableValue result = move.getResult(); + if (isVariable(result) && allocator.isMaterialized(result, op.id(), OperandMode.DEF)) { + /* + * This happens if a materializable interval is originally not spilled but then + * kicked out in LinearScanWalker.splitForSpilling(). When kicking out such an + * interval this move operation was already generated. + */ + instructions.set(j, null); + hasDead = true; + continue; + } + } + + op.forEachInput(assignProc); + op.forEachAlive(assignProc); + op.forEachTemp(assignProc); + op.forEachOutput(assignProc); + + // compute reference map and debug information + op.forEachState((inst, state) -> computeDebugInfo(inst, state)); + + // remove useless moves + if (move != null) { + if (move.getInput().equals(move.getResult())) { + instructions.set(j, null); + hasDead = true; + } + } + } + + if (hasDead) { + // Remove null values from the list. + instructions.removeAll(Collections.singleton(null)); + } + } + + private void assignLocations() { + try (Indent indent = Debug.logAndIndent("assign locations")) { + for (AbstractBlockBase block : allocator.sortedBlocks) { + try (Indent indent2 = Debug.logAndIndent("assign locations in block B%d", block.getId())) { + assignLocations(allocator.ir.getLIRforBlock(block)); + } + } + } + } +} diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanEliminateSpillMovePhase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanEliminateSpillMovePhase.java Tue May 12 20:56:04 2015 +0200 @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.lir.alloc.lsra; + +import static com.oracle.graal.api.code.ValueUtil.*; +import static com.oracle.graal.compiler.common.GraalOptions.*; +import static com.oracle.graal.lir.LIRValueUtil.*; + +import java.util.*; + +import com.oracle.graal.api.code.*; +import com.oracle.graal.api.meta.*; +import com.oracle.graal.compiler.common.cfg.*; +import com.oracle.graal.debug.*; +import com.oracle.graal.lir.*; +import com.oracle.graal.lir.StandardOp.*; +import com.oracle.graal.lir.alloc.lsra.Interval.*; +import com.oracle.graal.lir.alloc.lsra.LinearScan.*; +import com.oracle.graal.lir.gen.*; +import com.oracle.graal.lir.gen.LIRGeneratorTool.*; +import com.oracle.graal.lir.phases.*; + +class LinearScanEliminateSpillMovePhase extends AllocationPhase { + + private static final IntervalPredicate mustStoreAtDefinition = new LinearScan.IntervalPredicate() { + + @Override + public boolean apply(Interval i) { + return i.isSplitParent() && i.spillState() == SpillState.StoreAtDefinition; + } + }; + + protected final LinearScan allocator; + + LinearScanEliminateSpillMovePhase(LinearScan allocator) { + this.allocator = allocator; + } + + @Override + protected > void run(TargetDescription target, LIRGenerationResult lirGenRes, List codeEmittingOrder, List linearScanOrder, SpillMoveFactory spillMoveFactory) { + beforeSpillMoveElimination(); + eliminateSpillMoves(); + } + + protected void beforeSpillMoveElimination() { + } + + /** + * @return the index of the first instruction that is of interest for + * {@link #eliminateSpillMoves()} + */ + protected int firstInstructionOfInterest() { + // skip the first because it is always a label + return 1; + } + + // called once before assignment of register numbers + void eliminateSpillMoves() { + try (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. + */ + Interval interval; + interval = allocator.createUnhandledLists(mustStoreAtDefinition, null).first; + if (DetailedAsserts.getValue()) { + checkIntervals(interval); + } + + LIRInsertionBuffer insertionBuffer = new LIRInsertionBuffer(); + for (AbstractBlockBase block : allocator.sortedBlocks) { + try (Indent indent1 = Debug.logAndIndent("Handle %s", block)) { + List instructions = allocator.ir.getLIRforBlock(block); + int numInst = instructions.size(); + + // iterate all instructions of the block. + for (int j = firstInstructionOfInterest(); j < numInst; j++) { + LIRInstruction op = instructions.get(j); + int opId = op.id(); + + if (opId == -1) { + MoveOp move = (MoveOp) op; + /* + * Remove move from register to stack if the stack slot is guaranteed to + * be correct. Only moves that have been inserted by LinearScan can be + * removed. + */ + if (canEliminateSpillMove(block, move)) { + /* + * Move target is a stack slot that is always correct, so eliminate + * instruction. + */ + if (Debug.isLogEnabled()) { + Debug.log("eliminating move from interval %d (%s) to %d (%s) in block %s", allocator.operandNumber(move.getInput()), move.getInput(), + allocator.operandNumber(move.getResult()), move.getResult(), block); + } + + // null-instructions are deleted by assignRegNum + instructions.set(j, null); + } + + } else { + /* + * Insert move from register to stack just after the beginning of the + * interval. + */ + assert interval == Interval.EndMarker || interval.spillDefinitionPos() >= opId : "invalid order"; + assert interval == Interval.EndMarker || (interval.isSplitParent() && interval.spillState() == SpillState.StoreAtDefinition) : "invalid interval"; + + while (interval != Interval.EndMarker && interval.spillDefinitionPos() == opId) { + if (!interval.canMaterialize()) { + if (!insertionBuffer.initialized()) { + /* + * prepare insertion buffer (appended when all instructions + * in the block are processed) + */ + insertionBuffer.init(instructions); + } + + AllocatableValue fromLocation = interval.location(); + AllocatableValue toLocation = LinearScan.canonicalSpillOpr(interval); + if (!fromLocation.equals(toLocation)) { + + assert isRegister(fromLocation) : "from operand must be a register but is: " + fromLocation + " toLocation=" + toLocation + " spillState=" + + interval.spillState(); + assert isStackSlotValue(toLocation) : "to operand must be a stack slot"; + + LIRInstruction move = allocator.getSpillMoveFactory().createMove(toLocation, fromLocation); + insertionBuffer.append(j + 1, move); + + if (Debug.isLogEnabled()) { + Debug.log("inserting move after definition of interval %d to stack slot %s at opId %d", interval.operandNumber, interval.spillSlot(), opId); + } + } + } + interval = interval.next; + } + } + } // end of instruction iteration + + if (insertionBuffer.initialized()) { + insertionBuffer.finish(); + } + } + } // end of block iteration + + assert interval == Interval.EndMarker : "missed an interval"; + } + } + + /** + * @param block The block {@code move} is located in. + * @param move Spill move. + */ + protected boolean canEliminateSpillMove(AbstractBlockBase block, MoveOp move) { + assert isVariable(move.getResult()) : "LinearScan inserts only moves to variables: " + move; + + Interval curInterval = allocator.intervalFor(move.getResult()); + + if (!isRegister(curInterval.location()) && curInterval.alwaysInMemory()) { + assert isStackSlotValue(curInterval.location()) : "Not a stack slot: " + curInterval.location(); + return true; + } + return false; + } + + private static void checkIntervals(Interval interval) { + Interval prev = null; + Interval temp = interval; + while (temp != Interval.EndMarker) { + assert temp.spillDefinitionPos() > 0 : "invalid spill definition pos"; + if (prev != null) { + assert temp.from() >= prev.from() : "intervals not sorted"; + assert temp.spillDefinitionPos() >= prev.spillDefinitionPos() : "when intervals are sorted by from : then they must also be sorted by spillDefinitionPos"; + } + + assert temp.spillSlot() != null || temp.canMaterialize() : "interval has no spill slot assigned"; + assert temp.spillDefinitionPos() >= temp.from() : "invalid order"; + assert temp.spillDefinitionPos() <= temp.from() + 2 : "only intervals defined once at their start-pos can be optimized"; + + if (Debug.isLogEnabled()) { + Debug.log("interval %d (from %d to %d) must be stored at %d", temp.operandNumber, temp.from(), temp.to(), temp.spillDefinitionPos()); + } + + prev = temp; + temp = temp.next; + } + } + +} diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanLifetimeAnalysisPhase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanLifetimeAnalysisPhase.java Tue May 12 20:56:04 2015 +0200 @@ -0,0 +1,829 @@ +/* + * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.lir.alloc.lsra; + +import static com.oracle.graal.api.code.ValueUtil.*; +import static com.oracle.graal.compiler.common.GraalOptions.*; +import static com.oracle.graal.lir.LIRValueUtil.*; +import static com.oracle.graal.lir.debug.LIRGenerationDebugContext.*; + +import java.util.*; + +import com.oracle.graal.api.code.*; +import com.oracle.graal.api.meta.*; +import com.oracle.graal.compiler.common.*; +import com.oracle.graal.compiler.common.alloc.*; +import com.oracle.graal.compiler.common.cfg.*; +import com.oracle.graal.compiler.common.util.*; +import com.oracle.graal.debug.*; +import com.oracle.graal.debug.Debug.Scope; +import com.oracle.graal.lir.*; +import com.oracle.graal.lir.LIRInstruction.OperandFlag; +import com.oracle.graal.lir.LIRInstruction.OperandMode; +import com.oracle.graal.lir.StandardOp.LabelOp; +import com.oracle.graal.lir.StandardOp.MoveOp; +import com.oracle.graal.lir.alloc.lsra.Interval.RegisterPriority; +import com.oracle.graal.lir.alloc.lsra.Interval.SpillState; +import com.oracle.graal.lir.alloc.lsra.LinearScan.BlockData; +import com.oracle.graal.lir.gen.*; +import com.oracle.graal.lir.gen.LIRGeneratorTool.SpillMoveFactory; +import com.oracle.graal.lir.phases.*; + +class LinearScanLifetimeAnalysisPhase extends AllocationPhase { + + protected final LinearScan allocator; + + /** + * @param linearScan + */ + LinearScanLifetimeAnalysisPhase(LinearScan linearScan) { + allocator = linearScan; + } + + @Override + protected > void run(TargetDescription target, LIRGenerationResult lirGenRes, List codeEmittingOrder, List linearScanOrder, SpillMoveFactory spillMoveFactory) { + numberInstructions(); + allocator.printLir("Before register allocation", true); + computeLocalLiveSets(); + computeGlobalLiveSets(); + buildIntervals(); + } + + /** + * Bit set for each variable that is contained in each loop. + */ + BitMap2D intervalInLoop; + + boolean isIntervalInLoop(int interval, int loop) { + return intervalInLoop.at(interval, loop); + } + + /** + * Numbers all instructions in all blocks. The numbering follows the + * {@linkplain ComputeBlockOrder linear scan order}. + */ + void numberInstructions() { + + allocator.initIntervals(); + + ValueConsumer setVariableConsumer = (value, mode, flags) -> { + if (isVariable(value)) { + allocator.getOrCreateInterval(asVariable(value)); + } + }; + + // Assign IDs to LIR nodes and build a mapping, lirOps, from ID to LIRInstruction node. + int numInstructions = 0; + for (AbstractBlockBase block : allocator.sortedBlocks) { + numInstructions += allocator.ir.getLIRforBlock(block).size(); + } + + // initialize with correct length + allocator.initOpIdMaps(numInstructions); + + int opId = 0; + int index = 0; + for (AbstractBlockBase block : allocator.sortedBlocks) { + allocator.initBlockData(block); + + List instructions = allocator.ir.getLIRforBlock(block); + + int numInst = instructions.size(); + for (int j = 0; j < numInst; j++) { + LIRInstruction op = instructions.get(j); + op.setId(opId); + + allocator.putOpIdMaps(index, op, block); + assert allocator.instructionForId(opId) == op : "must match"; + + op.visitEachTemp(setVariableConsumer); + op.visitEachOutput(setVariableConsumer); + + index++; + opId += 2; // numbering of lirOps by two + } + } + assert index == numInstructions : "must match"; + assert (index << 1) == opId : "must match: " + (index << 1); + } + + /** + * Computes local live sets (i.e. {@link BlockData#liveGen} and {@link BlockData#liveKill}) + * separately for each block. + */ + void computeLocalLiveSets() { + int liveSize = allocator.liveSetSize(); + + intervalInLoop = new BitMap2D(allocator.operandSize(), allocator.numLoops()); + + // iterate all blocks + for (final AbstractBlockBase block : allocator.sortedBlocks) { + try (Indent indent = Debug.logAndIndent("compute local live sets for block %s", block)) { + + final BitSet liveGen = new BitSet(liveSize); + final BitSet liveKill = new BitSet(liveSize); + + List instructions = allocator.ir.getLIRforBlock(block); + int numInst = instructions.size(); + + ValueConsumer useConsumer = (operand, mode, flags) -> { + if (isVariable(operand)) { + int operandNum = allocator.operandNumber(operand); + if (!liveKill.get(operandNum)) { + liveGen.set(operandNum); + if (Debug.isLogEnabled()) { + Debug.log("liveGen for operand %d(%s)", operandNum, operand); + } + } + if (block.getLoop() != null) { + intervalInLoop.setBit(operandNum, block.getLoop().getIndex()); + } + } + + if (DetailedAsserts.getValue()) { + verifyInput(block, liveKill, operand); + } + }; + ValueConsumer stateConsumer = (operand, mode, flags) -> { + if (LinearScan.isVariableOrRegister(operand)) { + int operandNum = allocator.operandNumber(operand); + if (!liveKill.get(operandNum)) { + liveGen.set(operandNum); + if (Debug.isLogEnabled()) { + Debug.log("liveGen in state for operand %d(%s)", operandNum, operand); + } + } + } + }; + ValueConsumer defConsumer = (operand, mode, flags) -> { + if (isVariable(operand)) { + int varNum = allocator.operandNumber(operand); + liveKill.set(varNum); + if (Debug.isLogEnabled()) { + Debug.log("liveKill for operand %d(%s)", varNum, operand); + } + if (block.getLoop() != null) { + intervalInLoop.setBit(varNum, block.getLoop().getIndex()); + } + } + + if (DetailedAsserts.getValue()) { + /* + * Fixed intervals are never live at block boundaries, so they need not be + * processed in live sets. Process them only in debug mode so that this can + * be checked + */ + verifyTemp(liveKill, operand); + } + }; + + // iterate all instructions of the block + for (int j = 0; j < numInst; j++) { + final LIRInstruction op = instructions.get(j); + + try (Indent indent2 = Debug.logAndIndent("handle op %d", op.id())) { + op.visitEachInput(useConsumer); + op.visitEachAlive(useConsumer); + /* + * Add uses of live locals from interpreter's point of view for proper debug + * information generation. + */ + op.visitEachState(stateConsumer); + op.visitEachTemp(defConsumer); + op.visitEachOutput(defConsumer); + } + } // end of instruction iteration + + BlockData blockSets = allocator.getBlockData(block); + blockSets.liveGen = liveGen; + blockSets.liveKill = liveKill; + blockSets.liveIn = new BitSet(liveSize); + blockSets.liveOut = new BitSet(liveSize); + + if (Debug.isLogEnabled()) { + Debug.log("liveGen B%d %s", block.getId(), blockSets.liveGen); + Debug.log("liveKill B%d %s", block.getId(), blockSets.liveKill); + } + + } + } // end of block iteration + } + + private void verifyTemp(BitSet liveKill, Value operand) { + /* + * Fixed intervals are never live at block boundaries, so they need not be processed in live + * sets. Process them only in debug mode so that this can be checked + */ + if (isRegister(operand)) { + if (allocator.isProcessed(operand)) { + liveKill.set(allocator.operandNumber(operand)); + } + } + } + + private void verifyInput(AbstractBlockBase block, BitSet liveKill, Value operand) { + /* + * Fixed intervals are never live at block boundaries, so they need not be processed in live + * sets. This is checked by these assertions to be sure about it. The entry block may have + * incoming values in registers, which is ok. + */ + if (isRegister(operand) && block != allocator.ir.getControlFlowGraph().getStartBlock()) { + if (allocator.isProcessed(operand)) { + assert liveKill.get(allocator.operandNumber(operand)) : "using fixed register that is not defined in this block"; + } + } + } + + /** + * Performs a backward dataflow analysis to compute global live sets (i.e. + * {@link BlockData#liveIn} and {@link BlockData#liveOut}) for each block. + */ + void computeGlobalLiveSets() { + try (Indent indent = Debug.logAndIndent("compute global live sets")) { + int numBlocks = allocator.blockCount(); + boolean changeOccurred; + boolean changeOccurredInBlock; + int iterationCount = 0; + BitSet liveOut = new BitSet(allocator.liveSetSize()); // scratch set for calculations + + /* + * Perform a backward dataflow analysis to compute liveOut and liveIn for each block. + * The loop is executed until a fixpoint is reached (no changes in an iteration). + */ + do { + changeOccurred = false; + + try (Indent indent2 = Debug.logAndIndent("new iteration %d", iterationCount)) { + + // iterate all blocks in reverse order + for (int i = numBlocks - 1; i >= 0; i--) { + AbstractBlockBase block = allocator.blockAt(i); + BlockData blockSets = allocator.getBlockData(block); + + changeOccurredInBlock = false; + + /* liveOut(block) is the union of liveIn(sux), for successors sux of block. */ + int n = block.getSuccessorCount(); + if (n > 0) { + liveOut.clear(); + // block has successors + if (n > 0) { + for (AbstractBlockBase successor : block.getSuccessors()) { + liveOut.or(allocator.getBlockData(successor).liveIn); + } + } + + if (!blockSets.liveOut.equals(liveOut)) { + /* + * A change occurred. Swap the old and new live out sets to avoid + * copying. + */ + BitSet temp = blockSets.liveOut; + blockSets.liveOut = liveOut; + liveOut = temp; + + changeOccurred = true; + changeOccurredInBlock = true; + } + } + + if (iterationCount == 0 || changeOccurredInBlock) { + /* + * liveIn(block) is the union of liveGen(block) with (liveOut(block) & + * !liveKill(block)). + * + * Note: liveIn has to be computed only in first iteration or if liveOut + * has changed! + */ + BitSet liveIn = blockSets.liveIn; + liveIn.clear(); + liveIn.or(blockSets.liveOut); + liveIn.andNot(blockSets.liveKill); + liveIn.or(blockSets.liveGen); + + if (Debug.isLogEnabled()) { + Debug.log("block %d: livein = %s, liveout = %s", block.getId(), liveIn, blockSets.liveOut); + } + } + } + iterationCount++; + + if (changeOccurred && iterationCount > 50) { + throw new BailoutException("too many iterations in computeGlobalLiveSets"); + } + } + } while (changeOccurred); + + if (DetailedAsserts.getValue()) { + verifyLiveness(); + } + + // check that the liveIn set of the first block is empty + AbstractBlockBase startBlock = allocator.ir.getControlFlowGraph().getStartBlock(); + if (allocator.getBlockData(startBlock).liveIn.cardinality() != 0) { + if (DetailedAsserts.getValue()) { + reportFailure(numBlocks); + } + // bailout if this occurs in product mode. + throw new GraalInternalError("liveIn set of first block must be empty: " + allocator.getBlockData(startBlock).liveIn); + } + } + } + + private void reportFailure(int numBlocks) { + try (Scope s = Debug.forceLog()) { + try (Indent indent = Debug.logAndIndent("report failure")) { + + BitSet startBlockLiveIn = allocator.getBlockData(allocator.ir.getControlFlowGraph().getStartBlock()).liveIn; + try (Indent indent2 = Debug.logAndIndent("Error: liveIn set of first block must be empty (when this fails, variables are used before they are defined):")) { + for (int operandNum = startBlockLiveIn.nextSetBit(0); operandNum >= 0; operandNum = startBlockLiveIn.nextSetBit(operandNum + 1)) { + Interval interval = allocator.intervalFor(operandNum); + if (interval != null) { + Value operand = interval.operand; + Debug.log("var %d; operand=%s; node=%s", operandNum, operand, getSourceForOperandFromDebugContext(operand)); + } else { + Debug.log("var %d; missing operand", operandNum); + } + } + } + + // print some additional information to simplify debugging + for (int operandNum = startBlockLiveIn.nextSetBit(0); operandNum >= 0; operandNum = startBlockLiveIn.nextSetBit(operandNum + 1)) { + Interval interval = allocator.intervalFor(operandNum); + Value operand = null; + Object valueForOperandFromDebugContext = null; + if (interval != null) { + operand = interval.operand; + valueForOperandFromDebugContext = getSourceForOperandFromDebugContext(operand); + } + try (Indent indent2 = Debug.logAndIndent("---- Detailed information for var %d; operand=%s; node=%s ----", operandNum, operand, valueForOperandFromDebugContext)) { + + Deque> definedIn = new ArrayDeque<>(); + HashSet> usedIn = new HashSet<>(); + for (AbstractBlockBase block : allocator.sortedBlocks) { + if (allocator.getBlockData(block).liveGen.get(operandNum)) { + usedIn.add(block); + try (Indent indent3 = Debug.logAndIndent("used in block B%d", block.getId())) { + for (LIRInstruction ins : allocator.ir.getLIRforBlock(block)) { + try (Indent indent4 = Debug.logAndIndent("%d: %s", ins.id(), ins)) { + ins.forEachState((liveStateOperand, mode, flags) -> { + Debug.log("operand=%s", liveStateOperand); + return liveStateOperand; + }); + } + } + } + } + if (allocator.getBlockData(block).liveKill.get(operandNum)) { + definedIn.add(block); + try (Indent indent3 = Debug.logAndIndent("defined in block B%d", block.getId())) { + for (LIRInstruction ins : allocator.ir.getLIRforBlock(block)) { + Debug.log("%d: %s", ins.id(), ins); + } + } + } + } + + int[] hitCount = new int[numBlocks]; + + while (!definedIn.isEmpty()) { + AbstractBlockBase block = definedIn.removeFirst(); + usedIn.remove(block); + for (AbstractBlockBase successor : block.getSuccessors()) { + if (successor.isLoopHeader()) { + if (!block.isLoopEnd()) { + definedIn.add(successor); + } + } else { + if (++hitCount[successor.getId()] == successor.getPredecessorCount()) { + definedIn.add(successor); + } + } + } + } + try (Indent indent3 = Debug.logAndIndent("**** offending usages are in: ")) { + for (AbstractBlockBase block : usedIn) { + Debug.log("B%d", block.getId()); + } + } + } + } + } + } catch (Throwable e) { + throw Debug.handle(e); + } + } + + private void verifyLiveness() { + /* + * Check that fixed intervals are not live at block boundaries (live set must be empty at + * fixed intervals). + */ + for (AbstractBlockBase block : allocator.sortedBlocks) { + for (int j = 0; j <= allocator.maxRegisterNumber(); j++) { + assert !allocator.getBlockData(block).liveIn.get(j) : "liveIn set of fixed register must be empty"; + assert !allocator.getBlockData(block).liveOut.get(j) : "liveOut set of fixed register must be empty"; + assert !allocator.getBlockData(block).liveGen.get(j) : "liveGen set of fixed register must be empty"; + } + } + } + + void addUse(AllocatableValue operand, int from, int to, RegisterPriority registerPriority, LIRKind kind) { + if (!allocator.isProcessed(operand)) { + return; + } + + Interval interval = allocator.getOrCreateInterval(operand); + if (!kind.equals(LIRKind.Illegal)) { + interval.setKind(kind); + } + + interval.addRange(from, to); + + // Register use position at even instruction id. + interval.addUsePos(to & ~1, registerPriority); + + if (Debug.isLogEnabled()) { + Debug.log("add use: %s, from %d to %d (%s)", interval, from, to, registerPriority.name()); + } + } + + void addTemp(AllocatableValue operand, int tempPos, RegisterPriority registerPriority, LIRKind kind) { + if (!allocator.isProcessed(operand)) { + return; + } + + Interval interval = allocator.getOrCreateInterval(operand); + if (!kind.equals(LIRKind.Illegal)) { + interval.setKind(kind); + } + + interval.addRange(tempPos, tempPos + 1); + interval.addUsePos(tempPos, registerPriority); + interval.addMaterializationValue(null); + + if (Debug.isLogEnabled()) { + Debug.log("add temp: %s tempPos %d (%s)", interval, tempPos, RegisterPriority.MustHaveRegister.name()); + } + } + + void addDef(AllocatableValue operand, LIRInstruction op, RegisterPriority registerPriority, LIRKind kind) { + if (!allocator.isProcessed(operand)) { + return; + } + int defPos = op.id(); + + Interval interval = allocator.getOrCreateInterval(operand); + if (!kind.equals(LIRKind.Illegal)) { + interval.setKind(kind); + } + + Range r = interval.first(); + if (r.from <= defPos) { + /* + * Update the starting point (when a range is first created for a use, its start is the + * beginning of the current block until a def is encountered). + */ + r.from = defPos; + interval.addUsePos(defPos, registerPriority); + + } else { + /* + * Dead value - make vacuous interval also add register priority for dead intervals + */ + interval.addRange(defPos, defPos + 1); + interval.addUsePos(defPos, registerPriority); + if (Debug.isLogEnabled()) { + Debug.log("Warning: def of operand %s at %d occurs without use", operand, defPos); + } + } + + changeSpillDefinitionPos(interval, defPos); + if (registerPriority == RegisterPriority.None && interval.spillState().ordinal() <= SpillState.StartInMemory.ordinal() && isStackSlot(operand)) { + // detection of method-parameters and roundfp-results + interval.setSpillState(SpillState.StartInMemory); + } + interval.addMaterializationValue(getMaterializedValue(op, operand, interval)); + + if (Debug.isLogEnabled()) { + Debug.log("add def: %s defPos %d (%s)", interval, defPos, registerPriority.name()); + } + } + + /** + * Optimizes moves related to incoming stack based arguments. The interval for the destination + * of such moves is assigned the stack slot (which is in the caller's frame) as its spill slot. + */ + void handleMethodArguments(LIRInstruction op) { + if (op instanceof MoveOp) { + MoveOp move = (MoveOp) op; + if (optimizeMethodArgument(move.getInput())) { + StackSlot slot = asStackSlot(move.getInput()); + if (DetailedAsserts.getValue()) { + assert op.id() > 0 : "invalid id"; + assert allocator.blockForId(op.id()).getPredecessorCount() == 0 : "move from stack must be in first block"; + assert isVariable(move.getResult()) : "result of move must be a variable"; + + if (Debug.isLogEnabled()) { + Debug.log("found move from stack slot %s to %s", slot, move.getResult()); + } + } + + Interval interval = allocator.intervalFor(move.getResult()); + interval.setSpillSlot(slot); + interval.assignLocation(slot); + } + } + } + + void addRegisterHint(final LIRInstruction op, final Value targetValue, OperandMode mode, EnumSet flags, final boolean hintAtDef) { + if (flags.contains(OperandFlag.HINT) && LinearScan.isVariableOrRegister(targetValue)) { + + op.forEachRegisterHint(targetValue, mode, (registerHint, valueMode, valueFlags) -> { + if (LinearScan.isVariableOrRegister(registerHint)) { + Interval from = allocator.getOrCreateInterval((AllocatableValue) registerHint); + Interval to = allocator.getOrCreateInterval((AllocatableValue) targetValue); + + /* hints always point from def to use */ + if (hintAtDef) { + to.setLocationHint(from); + } else { + from.setLocationHint(to); + } + if (Debug.isLogEnabled()) { + Debug.log("operation at opId %d: added hint from interval %d to %d", op.id(), from.operandNumber, to.operandNumber); + } + + return registerHint; + } + return null; + }); + } + } + + /** + * Eliminates moves from register to stack if the stack slot is known to be correct. + */ + void changeSpillDefinitionPos(Interval interval, int defPos) { + assert interval.isSplitParent() : "can only be called for split parents"; + + switch (interval.spillState()) { + case NoDefinitionFound: + assert interval.spillDefinitionPos() == -1 : "must no be set before"; + interval.setSpillDefinitionPos(defPos); + interval.setSpillState(SpillState.NoSpillStore); + break; + + case NoSpillStore: + assert defPos <= interval.spillDefinitionPos() : "positions are processed in reverse order when intervals are created"; + if (defPos < interval.spillDefinitionPos() - 2) { + // second definition found, so no spill optimization possible for this interval + interval.setSpillState(SpillState.NoOptimization); + } else { + // two consecutive definitions (because of two-operand LIR form) + assert allocator.blockForId(defPos) == allocator.blockForId(interval.spillDefinitionPos()) : "block must be equal"; + } + break; + + case NoOptimization: + // nothing to do + break; + + default: + throw new BailoutException("other states not allowed at this time"); + } + } + + private static boolean optimizeMethodArgument(Value value) { + /* + * Object method arguments that are passed on the stack are currently not optimized because + * this requires that the runtime visits method arguments during stack walking. + */ + return isStackSlot(value) && asStackSlot(value).isInCallerFrame() && value.getKind() != Kind.Object; + } + + /** + * Determines the register priority for an instruction's output/result operand. + */ + private static RegisterPriority registerPriorityOfOutputOperand(LIRInstruction op) { + if (op instanceof MoveOp) { + MoveOp move = (MoveOp) op; + if (optimizeMethodArgument(move.getInput())) { + return RegisterPriority.None; + } + } else if (op instanceof LabelOp) { + LabelOp label = (LabelOp) op; + if (label.isPhiIn()) { + return RegisterPriority.None; + } + } + + // all other operands require a register + return RegisterPriority.MustHaveRegister; + } + + /** + * Determines the priority which with an instruction's input operand will be allocated a + * register. + */ + private static RegisterPriority registerPriorityOfInputOperand(EnumSet flags) { + if (flags.contains(OperandFlag.STACK)) { + return RegisterPriority.ShouldHaveRegister; + } + // all other operands require a register + return RegisterPriority.MustHaveRegister; + } + + void buildIntervals() { + + try (Indent indent = Debug.logAndIndent("build intervals")) { + InstructionValueConsumer outputConsumer = (op, operand, mode, flags) -> { + if (LinearScan.isVariableOrRegister(operand)) { + addDef((AllocatableValue) operand, op, registerPriorityOfOutputOperand(op), operand.getLIRKind()); + addRegisterHint(op, operand, mode, flags, true); + } + }; + + InstructionValueConsumer tempConsumer = (op, operand, mode, flags) -> { + if (LinearScan.isVariableOrRegister(operand)) { + addTemp((AllocatableValue) operand, op.id(), RegisterPriority.MustHaveRegister, operand.getLIRKind()); + addRegisterHint(op, operand, mode, flags, false); + } + }; + + InstructionValueConsumer aliveConsumer = (op, operand, mode, flags) -> { + if (LinearScan.isVariableOrRegister(operand)) { + RegisterPriority p = registerPriorityOfInputOperand(flags); + int opId = op.id(); + int blockFrom = allocator.getFirstLirInstructionId((allocator.blockForId(opId))); + addUse((AllocatableValue) operand, blockFrom, opId + 1, p, operand.getLIRKind()); + addRegisterHint(op, operand, mode, flags, false); + } + }; + + InstructionValueConsumer inputConsumer = (op, operand, mode, flags) -> { + if (LinearScan.isVariableOrRegister(operand)) { + int opId = op.id(); + int blockFrom = allocator.getFirstLirInstructionId((allocator.blockForId(opId))); + RegisterPriority p = registerPriorityOfInputOperand(flags); + addUse((AllocatableValue) operand, blockFrom, opId, p, operand.getLIRKind()); + addRegisterHint(op, operand, mode, flags, false); + } + }; + + InstructionValueConsumer stateProc = (op, operand, mode, flags) -> { + if (LinearScan.isVariableOrRegister(operand)) { + int opId = op.id(); + int blockFrom = allocator.getFirstLirInstructionId((allocator.blockForId(opId))); + addUse((AllocatableValue) operand, blockFrom, opId + 1, RegisterPriority.None, operand.getLIRKind()); + } + }; + + // create a list with all caller-save registers (cpu, fpu, xmm) + Register[] callerSaveRegs = allocator.regAllocConfig.getRegisterConfig().getCallerSaveRegisters(); + + // iterate all blocks in reverse order + for (int i = allocator.blockCount() - 1; i >= 0; i--) { + + AbstractBlockBase block = allocator.blockAt(i); + try (Indent indent2 = Debug.logAndIndent("handle block %d", block.getId())) { + + List instructions = allocator.ir.getLIRforBlock(block); + final int blockFrom = allocator.getFirstLirInstructionId(block); + int blockTo = allocator.getLastLirInstructionId(block); + + assert blockFrom == instructions.get(0).id(); + assert blockTo == instructions.get(instructions.size() - 1).id(); + + // Update intervals for operands live at the end of this block; + BitSet live = allocator.getBlockData(block).liveOut; + for (int operandNum = live.nextSetBit(0); operandNum >= 0; operandNum = live.nextSetBit(operandNum + 1)) { + assert live.get(operandNum) : "should not stop here otherwise"; + AllocatableValue operand = allocator.intervalFor(operandNum).operand; + if (Debug.isLogEnabled()) { + Debug.log("live in %d: %s", operandNum, operand); + } + + addUse(operand, blockFrom, blockTo + 2, RegisterPriority.None, LIRKind.Illegal); + + /* + * Add special use positions for loop-end blocks when the interval is used + * anywhere inside this loop. It's possible that the block was part of a + * non-natural loop, so it might have an invalid loop index. + */ + if (block.isLoopEnd() && block.getLoop() != null && isIntervalInLoop(operandNum, block.getLoop().getIndex())) { + allocator.intervalFor(operandNum).addUsePos(blockTo + 1, RegisterPriority.LiveAtLoopEnd); + } + } + + /* + * Iterate all instructions of the block in reverse order. definitions of + * intervals are processed before uses. + */ + for (int j = instructions.size() - 1; j >= 0; j--) { + final LIRInstruction op = instructions.get(j); + final int opId = op.id(); + + try (Indent indent3 = Debug.logAndIndent("handle inst %d: %s", opId, op)) { + + // add a temp range for each register if operation destroys + // caller-save registers + if (op.destroysCallerSavedRegisters()) { + for (Register r : callerSaveRegs) { + if (allocator.attributes(r).isAllocatable()) { + addTemp(r.asValue(), opId, RegisterPriority.None, LIRKind.Illegal); + } + } + if (Debug.isLogEnabled()) { + Debug.log("operation destroys all caller-save registers"); + } + } + + op.visitEachOutput(outputConsumer); + op.visitEachTemp(tempConsumer); + op.visitEachAlive(aliveConsumer); + op.visitEachInput(inputConsumer); + + /* + * Add uses of live locals from interpreter's point of view for proper + * debug information generation. Treat these operands as temp values (if + * the live range is extended to a call site, the value would be in a + * register at the call otherwise). + */ + op.visitEachState(stateProc); + + // special steps for some instructions (especially moves) + handleMethodArguments(op); + + } + + } // end of instruction iteration + } + } // end of block iteration + + /* + * Add the range [0, 1] to all fixed intervals. the register allocator need not handle + * unhandled fixed intervals. + */ + for (Interval interval : allocator.intervals()) { + if (interval != null && isRegister(interval.operand)) { + interval.addRange(0, 1); + } + } + } + } + + /** + * Returns a value for a interval definition, which can be used for re-materialization. + * + * @param op An instruction which defines a value + * @param operand The destination operand of the instruction + * @param interval The interval for this defined value. + * @return Returns the value which is moved to the instruction and which can be reused at all + * reload-locations in case the interval of this instruction is spilled. Currently this + * can only be a {@link JavaConstant}. + */ + static JavaConstant getMaterializedValue(LIRInstruction op, Value operand, Interval interval) { + if (op instanceof MoveOp) { + MoveOp move = (MoveOp) op; + if (move.getInput() instanceof JavaConstant) { + /* + * Check if the interval has any uses which would accept an stack location (priority + * == ShouldHaveRegister). Rematerialization of such intervals can result in a + * degradation, because rematerialization always inserts a constant load, even if + * the value is not needed in a register. + */ + Interval.UsePosList usePosList = interval.usePosList(); + int numUsePos = usePosList.size(); + for (int useIdx = 0; useIdx < numUsePos; useIdx++) { + Interval.RegisterPriority priority = usePosList.registerPriority(useIdx); + if (priority == Interval.RegisterPriority.ShouldHaveRegister) { + return null; + } + } + return (JavaConstant) move.getInput(); + } + } + return null; + } +} diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanOptimizeSpillPositionPhase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanOptimizeSpillPositionPhase.java Tue May 12 20:56:04 2015 +0200 @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.lir.alloc.lsra; + +import static com.oracle.graal.api.code.ValueUtil.*; +import static com.oracle.graal.compiler.common.cfg.AbstractControlFlowGraph.*; + +import java.util.*; + +import com.oracle.graal.api.code.*; +import com.oracle.graal.api.meta.*; +import com.oracle.graal.compiler.common.cfg.*; +import com.oracle.graal.debug.*; +import com.oracle.graal.lir.*; +import com.oracle.graal.lir.LIRInstruction.OperandMode; +import com.oracle.graal.lir.alloc.lsra.Interval.SpillState; +import com.oracle.graal.lir.gen.*; +import com.oracle.graal.lir.gen.LIRGeneratorTool.SpillMoveFactory; +import com.oracle.graal.lir.phases.*; + +final class LinearScanOptimizeSpillPositionPhase extends AllocationPhase { + + private static final DebugMetric betterSpillPos = Debug.metric("BetterSpillPosition"); + private static final DebugMetric betterSpillPosWithLowerProbability = Debug.metric("BetterSpillPositionWithLowerProbability"); + + private final LinearScan allocator; + + LinearScanOptimizeSpillPositionPhase(LinearScan allocator) { + this.allocator = allocator; + } + + @Override + protected > void run(TargetDescription target, LIRGenerationResult lirGenRes, List codeEmittingOrder, List linearScanOrder, SpillMoveFactory spillMoveFactory) { + optimizeSpillPosition(); + allocator.printIntervals("After optimize spill position"); + } + + private void optimizeSpillPosition() { + LIRInsertionBuffer[] insertionBuffers = new LIRInsertionBuffer[allocator.ir.linearScanOrder().size()]; + for (Interval interval : allocator.intervals()) { + if (interval != null && interval.isSplitParent() && interval.spillState() == SpillState.SpillInDominator) { + AbstractBlockBase defBlock = allocator.blockForId(interval.spillDefinitionPos()); + AbstractBlockBase spillBlock = null; + Interval firstSpillChild = null; + try (Indent indent = Debug.logAndIndent("interval %s (%s)", interval, defBlock)) { + for (Interval splitChild : interval.getSplitChildren()) { + if (isStackSlotValue(splitChild.location())) { + if (firstSpillChild == null || splitChild.from() < firstSpillChild.from()) { + firstSpillChild = splitChild; + } else { + assert firstSpillChild.from() < splitChild.from(); + } + // iterate all blocks where the interval has use positions + for (AbstractBlockBase splitBlock : blocksForInterval(splitChild)) { + if (dominates(defBlock, splitBlock)) { + if (Debug.isLogEnabled()) { + Debug.log("Split interval %s, block %s", splitChild, splitBlock); + } + if (spillBlock == null) { + spillBlock = splitBlock; + } else { + spillBlock = commonDominator(spillBlock, splitBlock); + assert spillBlock != null; + } + } + } + } + } + if (spillBlock == null) { + // no spill interval + interval.setSpillState(SpillState.StoreAtDefinition); + } else { + // move out of loops + if (defBlock.getLoopDepth() < spillBlock.getLoopDepth()) { + spillBlock = moveSpillOutOfLoop(defBlock, spillBlock); + } + + /* + * If the spill block is the begin of the first split child (aka the value + * is on the stack) spill in the dominator. + */ + assert firstSpillChild != null; + if (!defBlock.equals(spillBlock) && spillBlock.equals(allocator.blockForId(firstSpillChild.from()))) { + AbstractBlockBase dom = spillBlock.getDominator(); + if (Debug.isLogEnabled()) { + Debug.log("Spill block (%s) is the beginning of a spill child -> use dominator (%s)", spillBlock, dom); + } + spillBlock = dom; + } + + if (!defBlock.equals(spillBlock)) { + assert dominates(defBlock, spillBlock); + betterSpillPos.increment(); + if (Debug.isLogEnabled()) { + Debug.log("Better spill position found (Block %s)", spillBlock); + } + + if (defBlock.probability() <= spillBlock.probability()) { + // better spill block has the same probability -> do nothing + interval.setSpillState(SpillState.StoreAtDefinition); + } else { + LIRInsertionBuffer insertionBuffer = insertionBuffers[spillBlock.getId()]; + if (insertionBuffer == null) { + insertionBuffer = new LIRInsertionBuffer(); + insertionBuffers[spillBlock.getId()] = insertionBuffer; + insertionBuffer.init(allocator.ir.getLIRforBlock(spillBlock)); + } + int spillOpId = allocator.getFirstLirInstructionId(spillBlock); + // insert spill move + AllocatableValue fromLocation = interval.getSplitChildAtOpId(spillOpId, OperandMode.DEF, allocator).location(); + AllocatableValue toLocation = LinearScan.canonicalSpillOpr(interval); + LIRInstruction move = allocator.getSpillMoveFactory().createMove(toLocation, fromLocation); + move.setId(LinearScan.DOMINATOR_SPILL_MOVE_ID); + /* + * We can use the insertion buffer directly because we always insert + * at position 1. + */ + insertionBuffer.append(1, move); + + betterSpillPosWithLowerProbability.increment(); + interval.setSpillDefinitionPos(spillOpId); + } + } else { + // definition is the best choice + interval.setSpillState(SpillState.StoreAtDefinition); + } + } + } + } + } + for (LIRInsertionBuffer insertionBuffer : insertionBuffers) { + if (insertionBuffer != null) { + assert insertionBuffer.initialized() : "Insertion buffer is nonnull but not initialized!"; + insertionBuffer.finish(); + } + } + } + + /** + * Iterate over all {@link AbstractBlockBase blocks} of an interval. + */ + private class IntervalBlockIterator implements Iterator> { + + Range range; + AbstractBlockBase block; + + public IntervalBlockIterator(Interval interval) { + range = interval.first(); + block = allocator.blockForId(range.from); + } + + public AbstractBlockBase next() { + AbstractBlockBase currentBlock = block; + int nextBlockIndex = block.getLinearScanNumber() + 1; + if (nextBlockIndex < allocator.sortedBlocks.size()) { + block = allocator.sortedBlocks.get(nextBlockIndex); + if (range.to <= allocator.getFirstLirInstructionId(block)) { + range = range.next; + if (range == Range.EndMarker) { + block = null; + } else { + block = allocator.blockForId(range.from); + } + } + } else { + block = null; + } + return currentBlock; + } + + public boolean hasNext() { + return block != null; + } + } + + private Iterable> blocksForInterval(Interval interval) { + return new Iterable>() { + public Iterator> iterator() { + return new IntervalBlockIterator(interval); + } + }; + } + + private static AbstractBlockBase moveSpillOutOfLoop(AbstractBlockBase defBlock, AbstractBlockBase spillBlock) { + int defLoopDepth = defBlock.getLoopDepth(); + for (AbstractBlockBase block = spillBlock.getDominator(); !defBlock.equals(block); block = block.getDominator()) { + assert block != null : "spill block not dominated by definition block?"; + if (block.getLoopDepth() <= defLoopDepth) { + assert block.getLoopDepth() == defLoopDepth : "Cannot spill an interval outside of the loop where it is defined!"; + return block; + } + } + return defBlock; + } +} diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanPhase.java --- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanPhase.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanPhase.java Tue May 12 20:56:04 2015 +0200 @@ -49,11 +49,13 @@ @Override protected > void run(TargetDescription target, LIRGenerationResult lirGenRes, List codeEmittingOrder, List linearScanOrder, SpillMoveFactory spillMoveFactory) { + final LinearScan allocator; if (LinearScanPhase.SSA_LSRA.getValue()) { - new SSALinearScan(target, lirGenRes, spillMoveFactory, new RegisterAllocationConfig(lirGenRes.getFrameMapBuilder().getRegisterConfig())).allocate(); + allocator = new SSALinearScan(target, lirGenRes, spillMoveFactory, new RegisterAllocationConfig(lirGenRes.getFrameMapBuilder().getRegisterConfig())); } else { - new LinearScan(target, lirGenRes, spillMoveFactory, new RegisterAllocationConfig(lirGenRes.getFrameMapBuilder().getRegisterConfig())).allocate(); + allocator = new LinearScan(target, lirGenRes, spillMoveFactory, new RegisterAllocationConfig(lirGenRes.getFrameMapBuilder().getRegisterConfig())); } + allocator.allocate(target, lirGenRes, codeEmittingOrder, linearScanOrder, spillMoveFactory); } } diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanRegisterAllocationPhase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanRegisterAllocationPhase.java Tue May 12 20:56:04 2015 +0200 @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.lir.alloc.lsra; + +import java.util.*; + +import com.oracle.graal.api.code.*; +import com.oracle.graal.compiler.common.cfg.*; +import com.oracle.graal.debug.*; +import com.oracle.graal.lir.gen.*; +import com.oracle.graal.lir.gen.LIRGeneratorTool.*; +import com.oracle.graal.lir.phases.*; + +final class LinearScanRegisterAllocationPhase extends AllocationPhase { + + private final LinearScan allocator; + + LinearScanRegisterAllocationPhase(LinearScan allocator) { + this.allocator = allocator; + } + + @Override + protected > void run(TargetDescription target, LIRGenerationResult lirGenRes, List codeEmittingOrder, List linearScanOrder, SpillMoveFactory spillMoveFactory) { + allocator.printIntervals("Before register allocation"); + allocateRegisters(); + allocator.printIntervals("After register allocation"); + } + + void allocateRegisters() { + try (Indent indent = Debug.logAndIndent("allocate registers")) { + Interval precoloredIntervals; + Interval notPrecoloredIntervals; + + Interval.Pair result = allocator.createUnhandledLists(LinearScan.IS_PRECOLORED_INTERVAL, LinearScan.IS_VARIABLE_INTERVAL); + precoloredIntervals = result.first; + notPrecoloredIntervals = result.second; + + // allocate cpu registers + LinearScanWalker lsw; + if (OptimizingLinearScanWalker.Options.LSRAOptimization.getValue()) { + lsw = new OptimizingLinearScanWalker(allocator, precoloredIntervals, notPrecoloredIntervals); + } else { + lsw = new LinearScanWalker(allocator, precoloredIntervals, notPrecoloredIntervals); + } + lsw.walk(); + lsw.finishAllocation(); + } + } + +} diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanResolveDataFlowPhase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanResolveDataFlowPhase.java Tue May 12 20:56:04 2015 +0200 @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.lir.alloc.lsra; + +import static com.oracle.graal.compiler.common.GraalOptions.*; + +import java.util.*; + +import com.oracle.graal.api.code.*; +import com.oracle.graal.compiler.common.cfg.*; +import com.oracle.graal.debug.*; +import com.oracle.graal.lir.*; +import com.oracle.graal.lir.gen.*; +import com.oracle.graal.lir.gen.LIRGeneratorTool.SpillMoveFactory; +import com.oracle.graal.lir.phases.*; + +/** + * Phase 6: resolve data flow + * + * Insert moves at edges between blocks if intervals have been split. + */ +class LinearScanResolveDataFlowPhase extends AllocationPhase { + + protected final LinearScan allocator; + + LinearScanResolveDataFlowPhase(LinearScan allocator) { + this.allocator = allocator; + } + + @Override + protected > void run(TargetDescription target, LIRGenerationResult lirGenRes, List codeEmittingOrder, List linearScanOrder, SpillMoveFactory spillMoveFactory) { + resolveDataFlow(); + } + + void resolveCollectMappings(AbstractBlockBase fromBlock, AbstractBlockBase toBlock, AbstractBlockBase midBlock, MoveResolver moveResolver) { + assert moveResolver.checkEmpty(); + assert midBlock == null || + (midBlock.getPredecessorCount() == 1 && midBlock.getSuccessorCount() == 1 && midBlock.getPredecessors().get(0).equals(fromBlock) && midBlock.getSuccessors().get(0).equals( + toBlock)); + + int toBlockFirstInstructionId = allocator.getFirstLirInstructionId(toBlock); + int fromBlockLastInstructionId = allocator.getLastLirInstructionId(fromBlock) + 1; + int numOperands = allocator.operandSize(); + BitSet liveAtEdge = allocator.getBlockData(toBlock).liveIn; + + // visit all variables for which the liveAtEdge bit is set + for (int operandNum = liveAtEdge.nextSetBit(0); operandNum >= 0; operandNum = liveAtEdge.nextSetBit(operandNum + 1)) { + assert operandNum < numOperands : "live information set for not exisiting interval"; + assert allocator.getBlockData(fromBlock).liveOut.get(operandNum) && allocator.getBlockData(toBlock).liveIn.get(operandNum) : "interval not live at this edge"; + + Interval fromInterval = allocator.splitChildAtOpId(allocator.intervalFor(operandNum), fromBlockLastInstructionId, LIRInstruction.OperandMode.DEF); + Interval toInterval = allocator.splitChildAtOpId(allocator.intervalFor(operandNum), toBlockFirstInstructionId, LIRInstruction.OperandMode.DEF); + + if (fromInterval != toInterval && !fromInterval.location().equals(toInterval.location())) { + // need to insert move instruction + moveResolver.addMapping(fromInterval, toInterval); + } + } + } + + void resolveFindInsertPos(AbstractBlockBase fromBlock, AbstractBlockBase toBlock, MoveResolver moveResolver) { + if (fromBlock.getSuccessorCount() <= 1) { + if (Debug.isLogEnabled()) { + Debug.log("inserting moves at end of fromBlock B%d", fromBlock.getId()); + } + + List instructions = allocator.ir.getLIRforBlock(fromBlock); + LIRInstruction instr = instructions.get(instructions.size() - 1); + if (instr instanceof StandardOp.JumpOp) { + // insert moves before branch + moveResolver.setInsertPosition(instructions, instructions.size() - 1); + } else { + moveResolver.setInsertPosition(instructions, instructions.size()); + } + + } else { + if (Debug.isLogEnabled()) { + Debug.log("inserting moves at beginning of toBlock B%d", toBlock.getId()); + } + + if (DetailedAsserts.getValue()) { + assert allocator.ir.getLIRforBlock(fromBlock).get(0) instanceof StandardOp.LabelOp : "block does not start with a label"; + + /* + * Because the number of predecessor edges matches the number of successor edges, + * blocks which are reached by switch statements may have be more than one + * predecessor but it will be guaranteed that all predecessors will be the same. + */ + for (AbstractBlockBase predecessor : toBlock.getPredecessors()) { + assert fromBlock == predecessor : "all critical edges must be broken"; + } + } + + moveResolver.setInsertPosition(allocator.ir.getLIRforBlock(toBlock), 1); + } + } + + /** + * Inserts necessary moves (spilling or reloading) at edges between blocks for intervals that + * have been split. + */ + void resolveDataFlow() { + try (Indent indent = Debug.logAndIndent("resolve data flow")) { + + int numBlocks = allocator.blockCount(); + MoveResolver moveResolver = allocator.createMoveResolver(); + BitSet blockCompleted = new BitSet(numBlocks); + BitSet alreadyResolved = new BitSet(numBlocks); + + for (AbstractBlockBase block : allocator.sortedBlocks) { + + // check if block has only one predecessor and only one successor + if (block.getPredecessorCount() == 1 && block.getSuccessorCount() == 1) { + List instructions = allocator.ir.getLIRforBlock(block); + assert instructions.get(0) instanceof StandardOp.LabelOp : "block must start with label"; + assert instructions.get(instructions.size() - 1) instanceof StandardOp.JumpOp : "block with successor must end with unconditional jump"; + + // check if block is empty (only label and branch) + if (instructions.size() == 2) { + AbstractBlockBase pred = block.getPredecessors().iterator().next(); + AbstractBlockBase sux = block.getSuccessors().iterator().next(); + + // prevent optimization of two consecutive blocks + if (!blockCompleted.get(pred.getLinearScanNumber()) && !blockCompleted.get(sux.getLinearScanNumber())) { + if (Debug.isLogEnabled()) { + Debug.log(" optimizing empty block B%d (pred: B%d, sux: B%d)", block.getId(), pred.getId(), sux.getId()); + } + + blockCompleted.set(block.getLinearScanNumber()); + + /* + * Directly resolve between pred and sux (without looking at the empty + * block between). + */ + resolveCollectMappings(pred, sux, block, moveResolver); + if (moveResolver.hasMappings()) { + moveResolver.setInsertPosition(instructions, 1); + moveResolver.resolveAndAppendMoves(); + } + } + } + } + } + + for (AbstractBlockBase fromBlock : allocator.sortedBlocks) { + if (!blockCompleted.get(fromBlock.getLinearScanNumber())) { + alreadyResolved.clear(); + alreadyResolved.or(blockCompleted); + + for (AbstractBlockBase toBlock : fromBlock.getSuccessors()) { + + /* + * Check for duplicate edges between the same blocks (can happen with switch + * blocks). + */ + if (!alreadyResolved.get(toBlock.getLinearScanNumber())) { + if (Debug.isLogEnabled()) { + Debug.log("processing edge between B%d and B%d", fromBlock.getId(), toBlock.getId()); + } + + alreadyResolved.set(toBlock.getLinearScanNumber()); + + // collect all intervals that have been split between + // fromBlock and toBlock + resolveCollectMappings(fromBlock, toBlock, null, moveResolver); + if (moveResolver.hasMappings()) { + resolveFindInsertPos(fromBlock, toBlock, moveResolver); + moveResolver.resolveAndAppendMoves(); + } + } + } + } + } + + } + } +} diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanWalker.java --- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanWalker.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanWalker.java Tue May 12 20:56:04 2015 +0200 @@ -483,7 +483,7 @@ allocator.assignSpillSlot(interval); handleSpillSlot(interval); - allocator.changeSpillState(interval, minSplitPos); + 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 @@ -529,7 +529,7 @@ Interval spilledPart = interval.split(optimalSplitPos, allocator); allocator.assignSpillSlot(spilledPart); handleSpillSlot(spilledPart); - allocator.changeSpillState(spilledPart, optimalSplitPos); + changeSpillState(spilledPart, optimalSplitPos); if (!allocator.isBlockBegin(optimalSplitPos)) { if (Debug.isLogEnabled()) { @@ -551,6 +551,59 @@ } } + // called during register allocation + private void changeSpillState(Interval interval, int spillPos) { + switch (interval.spillState()) { + case NoSpillStore: { + int defLoopDepth = allocator.blockForId(interval.spillDefinitionPos()).getLoopDepth(); + int spillLoopDepth = allocator.blockForId(spillPos).getLoopDepth(); + + if (defLoopDepth < spillLoopDepth) { + /* + * The loop depth of the spilling position is higher then the loop depth at the + * definition of the interval. Move write to memory out of loop. + */ + if (LinearScan.Options.LSRAOptimizeSpillPosition.getValue()) { + // find best spill position in dominator the tree + interval.setSpillState(SpillState.SpillInDominator); + } else { + // store at definition of the interval + interval.setSpillState(SpillState.StoreAtDefinition); + } + } else { + /* + * The interval is currently spilled only once, so for now there is no reason to + * store the interval at the definition. + */ + interval.setSpillState(SpillState.OneSpillStore); + } + break; + } + + case OneSpillStore: { + if (LinearScan.Options.LSRAOptimizeSpillPosition.getValue()) { + // the interval is spilled more then once + interval.setSpillState(SpillState.SpillInDominator); + } else { + // It is better to store it to memory at the definition. + interval.setSpillState(SpillState.StoreAtDefinition); + } + break; + } + + case SpillInDominator: + case StoreAtDefinition: + case StartInMemory: + case NoOptimization: + case NoDefinitionFound: + // nothing to do + break; + + default: + throw new BailoutException("other states not allowed at this time"); + } + } + /** * This is called for every interval that is assigned to a stack slot. */ diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/MoveResolver.java --- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/MoveResolver.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/MoveResolver.java Tue May 12 20:56:04 2015 +0200 @@ -226,7 +226,7 @@ private void insertMove(Interval fromInterval, Interval toInterval) { assert !fromInterval.operand.equals(toInterval.operand) : "from and to interval equal: " + fromInterval; - assert fromInterval.kind().equals(toInterval.kind()) : "move between different types"; + assert LIRKind.verifyMoveKinds(toInterval.kind(), fromInterval.kind()) : "move between different types"; assert insertIdx != -1 : "must setup insert position first"; insertionBuffer.append(insertIdx, createMove(fromInterval.operand, toInterval.operand, fromInterval.location(), toInterval.location())); @@ -247,7 +247,7 @@ } private void insertMove(Value fromOpr, Interval toInterval) { - assert fromOpr.getLIRKind().equals(toInterval.kind()) : format("move between different types %s %s", fromOpr.getLIRKind(), toInterval.kind()); + assert LIRKind.verifyMoveKinds(toInterval.kind(), fromOpr.getLIRKind()) : format("move between different types %s %s", fromOpr.getLIRKind(), toInterval.kind()); assert insertIdx != -1 : "must setup insert position first"; AllocatableValue toOpr = toInterval.operand; @@ -409,8 +409,8 @@ } assert !fromInterval.operand.equals(toInterval.operand) : "from and to interval equal: " + fromInterval; - assert fromInterval.kind().equals(toInterval.kind()) || (fromInterval.kind().getPlatformKind().equals(toInterval.kind().getPlatformKind()) && toInterval.kind().isDerivedReference()) : String.format( - "Kind mismatch: %s vs. %s, from=%s, to=%s", fromInterval.kind(), toInterval.kind(), fromInterval, toInterval); + assert LIRKind.verifyMoveKinds(toInterval.kind(), fromInterval.kind()) : String.format("Kind mismatch: %s vs. %s, from=%s, to=%s", fromInterval.kind(), toInterval.kind(), fromInterval, + toInterval); mappingFrom.add(fromInterval); mappingFromOpr.add(Value.ILLEGAL); mappingTo.add(toInterval); diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/SSALinarScanResolveDataFlowPhase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/SSALinarScanResolveDataFlowPhase.java Tue May 12 20:56:04 2015 +0200 @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.lir.alloc.lsra; + +import static com.oracle.graal.api.code.ValueUtil.*; + +import java.util.*; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.compiler.common.cfg.*; +import com.oracle.graal.debug.*; +import com.oracle.graal.lir.*; +import com.oracle.graal.lir.ssa.*; +import com.oracle.graal.lir.ssa.SSAUtils.PhiValueVisitor; + +class SSALinarScanResolveDataFlowPhase extends LinearScanResolveDataFlowPhase { + + private static final DebugMetric numPhiResolutionMoves = Debug.metric("SSA LSRA[numPhiResolutionMoves]"); + private static final DebugMetric numStackToStackMoves = Debug.metric("SSA LSRA[numStackToStackMoves]"); + + SSALinarScanResolveDataFlowPhase(LinearScan allocator) { + super(allocator); + } + + @Override + void resolveCollectMappings(AbstractBlockBase fromBlock, AbstractBlockBase toBlock, AbstractBlockBase midBlock, MoveResolver moveResolver) { + super.resolveCollectMappings(fromBlock, toBlock, midBlock, moveResolver); + + if (toBlock.getPredecessorCount() > 1) { + int toBlockFirstInstructionId = allocator.getFirstLirInstructionId(toBlock); + int fromBlockLastInstructionId = allocator.getLastLirInstructionId(fromBlock) + 1; + + AbstractBlockBase phiOutBlock = midBlock != null ? midBlock : fromBlock; + List instructions = allocator.ir.getLIRforBlock(phiOutBlock); + int phiOutIdx = SSAUtils.phiOutIndex(allocator.ir, phiOutBlock); + int phiOutId = midBlock != null ? fromBlockLastInstructionId : instructions.get(phiOutIdx).id(); + assert phiOutId >= 0; + + PhiValueVisitor visitor = new PhiValueVisitor() { + + public void visit(Value phiIn, Value phiOut) { + Interval toInterval = allocator.splitChildAtOpId(allocator.intervalFor(phiIn), toBlockFirstInstructionId, LIRInstruction.OperandMode.DEF); + if (isConstant(phiOut)) { + numPhiResolutionMoves.increment(); + moveResolver.addMapping(phiOut, toInterval); + } else { + Interval fromInterval = allocator.splitChildAtOpId(allocator.intervalFor(phiOut), phiOutId, LIRInstruction.OperandMode.DEF); + if (fromInterval != toInterval && !fromInterval.location().equals(toInterval.location())) { + numPhiResolutionMoves.increment(); + if (!(isStackSlotValue(toInterval.location()) && isStackSlotValue(fromInterval.location()))) { + moveResolver.addMapping(fromInterval, toInterval); + } else { + numStackToStackMoves.increment(); + moveResolver.addMapping(fromInterval, toInterval); + } + } + } + } + }; + + SSAUtils.forEachPhiValuePair(allocator.ir, toBlock, phiOutBlock, visitor); + SSAUtils.removePhiOut(allocator.ir, phiOutBlock); + } + } + +} diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/SSALinearScan.java --- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/SSALinearScan.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/SSALinearScan.java Tue May 12 20:56:04 2015 +0200 @@ -22,32 +22,13 @@ */ package com.oracle.graal.lir.alloc.lsra; -import static com.oracle.graal.api.code.ValueUtil.*; -import static com.oracle.graal.lir.LIRValueUtil.*; - -import java.util.*; - import com.oracle.graal.api.code.*; -import com.oracle.graal.api.meta.*; import com.oracle.graal.compiler.common.alloc.*; -import com.oracle.graal.compiler.common.cfg.*; -import com.oracle.graal.debug.*; -import com.oracle.graal.debug.Debug.Scope; -import com.oracle.graal.lir.*; -import com.oracle.graal.lir.LIRInstruction.OperandFlag; -import com.oracle.graal.lir.LIRInstruction.OperandMode; -import com.oracle.graal.lir.StandardOp.LabelOp; -import com.oracle.graal.lir.StandardOp.MoveOp; import com.oracle.graal.lir.gen.*; import com.oracle.graal.lir.gen.LIRGeneratorTool.SpillMoveFactory; -import com.oracle.graal.lir.ssa.*; -import com.oracle.graal.lir.ssa.SSAUtils.PhiValueVisitor; final class SSALinearScan extends LinearScan { - private static final DebugMetric numPhiResolutionMoves = Debug.metric("SSA LSRA[numPhiResolutionMoves]"); - private static final DebugMetric numStackToStackMoves = Debug.metric("SSA LSRA[numStackToStackMoves]"); - SSALinearScan(TargetDescription target, LIRGenerationResult res, SpillMoveFactory spillMoveFactory, RegisterAllocationConfig regAllocConfig) { super(target, res, spillMoveFactory, regAllocConfig); } @@ -60,141 +41,18 @@ } @Override - protected int firstInstructionOfInterest() { - // also look at Labels as they define PHI values - return 0; - } - - @Override - void resolveCollectMappings(AbstractBlockBase fromBlock, AbstractBlockBase toBlock, AbstractBlockBase midBlock, MoveResolver moveResolver) { - super.resolveCollectMappings(fromBlock, toBlock, midBlock, moveResolver); - - if (toBlock.getPredecessorCount() > 1) { - int toBlockFirstInstructionId = getFirstLirInstructionId(toBlock); - int fromBlockLastInstructionId = getLastLirInstructionId(fromBlock) + 1; - - AbstractBlockBase phiOutBlock = midBlock != null ? midBlock : fromBlock; - List instructions = ir.getLIRforBlock(phiOutBlock); - int phiOutIdx = SSAUtils.phiOutIndex(ir, phiOutBlock); - int phiOutId = midBlock != null ? fromBlockLastInstructionId : instructions.get(phiOutIdx).id(); - assert phiOutId >= 0; - - PhiValueVisitor visitor = new PhiValueVisitor() { - - public void visit(Value phiIn, Value phiOut) { - Interval toInterval = splitChildAtOpId(intervalFor(phiIn), toBlockFirstInstructionId, LIRInstruction.OperandMode.DEF); - if (isConstant(phiOut)) { - numPhiResolutionMoves.increment(); - moveResolver.addMapping(phiOut, toInterval); - } else { - Interval fromInterval = splitChildAtOpId(intervalFor(phiOut), phiOutId, LIRInstruction.OperandMode.DEF); - if (fromInterval != toInterval && !fromInterval.location().equals(toInterval.location())) { - numPhiResolutionMoves.increment(); - if (!(isStackSlotValue(toInterval.location()) && isStackSlotValue(fromInterval.location()))) { - moveResolver.addMapping(fromInterval, toInterval); - } else { - numStackToStackMoves.increment(); - moveResolver.addMapping(fromInterval, toInterval); - } - } - } - } - }; - - SSAUtils.forEachPhiValuePair(ir, toBlock, phiOutBlock, visitor); - SSAUtils.removePhiOut(ir, phiOutBlock); - } - } - - @Override - protected void beforeSpillMoveElimination() { - /* - * PHI Ins are needed for the RegisterVerifier, otherwise PHIs where the Out and In value - * matches (ie. there is no resolution move) are falsely detected as errors. - */ - try (Scope s1 = Debug.scope("Remove Phi In")) { - for (AbstractBlockBase toBlock : sortedBlocks) { - if (toBlock.getPredecessorCount() > 1) { - SSAUtils.removePhiIn(ir, toBlock); - } - } - } + protected LinearScanLifetimeAnalysisPhase createLifetimeAnalysisPhase() { + return new SSALinearScanLifetimeAnalysisPhase(this); } @Override - protected boolean canEliminateSpillMove(AbstractBlockBase block, MoveOp move) { - // SSA Linear Scan might introduce moves to stack slots - assert isVariable(move.getResult()) || LinearScanPhase.SSA_LSRA.getValue() : "Move should not be produced in a non-SSA compilation: " + move; - - Interval curInterval = intervalFor(move.getResult()); - - if (!isRegister(curInterval.location()) && curInterval.alwaysInMemory() && !isPhiResolutionMove(block, move, curInterval)) { - assert isStackSlotValue(curInterval.location()) : "Not a stack slot: " + curInterval.location(); - return true; - } - return false; + protected LinearScanResolveDataFlowPhase createResolveDataFlowPhase() { + return new SSALinarScanResolveDataFlowPhase(this); } @Override - void addRegisterHint(final LIRInstruction op, final Value targetValue, OperandMode mode, EnumSet flags, final boolean hintAtDef) { - super.addRegisterHint(op, targetValue, mode, flags, hintAtDef); - - if (hintAtDef && op instanceof LabelOp) { - LabelOp label = (LabelOp) op; - - Interval to = getOrCreateInterval((AllocatableValue) targetValue); - - SSAUtils.forEachPhiRegisterHint(ir, blockForId(label.id()), label, targetValue, mode, (ValueConsumer) (registerHint, valueMode, valueFlags) -> { - if (isVariableOrRegister(registerHint)) { - Interval from = getOrCreateInterval((AllocatableValue) registerHint); - - setHint(op, to, from); - setHint(op, from, to); - } - }); - } + protected LinearScanEliminateSpillMovePhase createSpillMoveEliminationPhase() { + return new SSALinearScanEliminateSpillMovePhase(this); } - private static void setHint(final LIRInstruction op, Interval target, Interval source) { - Interval currentHint = target.locationHint(false); - if (currentHint == null || currentHint.from() > target.from()) { - /* - * Update hint if there was none or if the hint interval starts after the hinted - * interval. - */ - target.setLocationHint(source); - if (Debug.isLogEnabled()) { - Debug.log("operation at opId %d: added hint from interval %d to %d", op.id(), source.operandNumber, target.operandNumber); - } - } - } - - private boolean isPhiResolutionMove(AbstractBlockBase block, MoveOp move, Interval toInterval) { - if (!LinearScanPhase.SSA_LSRA.getValue()) { - return false; - } - if (!toInterval.isSplitParent()) { - return false; - } - if ((toInterval.from() & 1) == 1) { - // phi intervals start at even positions. - return false; - } - if (block.getSuccessorCount() != 1) { - return false; - } - LIRInstruction op = instructionForId(toInterval.from()); - if (!(op instanceof LabelOp)) { - return false; - } - AbstractBlockBase intStartBlock = blockForId(toInterval.from()); - assert ir.getLIRforBlock(intStartBlock).get(0).equals(op); - if (!block.getSuccessors().get(0).equals(intStartBlock)) { - return false; - } - try (Indent indet = Debug.indent()) { - Debug.log("Is a move (%s) to phi interval %s", move, toInterval); - } - return true; - } } diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/SSALinearScanEliminateSpillMovePhase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/SSALinearScanEliminateSpillMovePhase.java Tue May 12 20:56:04 2015 +0200 @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.lir.alloc.lsra; + +import static com.oracle.graal.api.code.ValueUtil.*; +import static com.oracle.graal.lir.LIRValueUtil.*; + +import com.oracle.graal.compiler.common.cfg.*; +import com.oracle.graal.debug.*; +import com.oracle.graal.debug.Debug.*; +import com.oracle.graal.lir.*; +import com.oracle.graal.lir.StandardOp.*; +import com.oracle.graal.lir.ssa.*; + +public class SSALinearScanEliminateSpillMovePhase extends LinearScanEliminateSpillMovePhase { + + SSALinearScanEliminateSpillMovePhase(LinearScan allocator) { + super(allocator); + } + + @Override + protected int firstInstructionOfInterest() { + // also look at Labels as they define PHI values + return 0; + } + + @Override + protected void beforeSpillMoveElimination() { + /* + * PHI Ins are needed for the RegisterVerifier, otherwise PHIs where the Out and In value + * matches (ie. there is no resolution move) are falsely detected as errors. + */ + try (Scope s1 = Debug.scope("Remove Phi In")) { + for (AbstractBlockBase toBlock : allocator.sortedBlocks) { + if (toBlock.getPredecessorCount() > 1) { + SSAUtils.removePhiIn(allocator.ir, toBlock); + } + } + } + } + + @Override + protected boolean canEliminateSpillMove(AbstractBlockBase block, MoveOp move) { + // SSA Linear Scan might introduce moves to stack slots + assert isVariable(move.getResult()) || LinearScanPhase.SSA_LSRA.getValue() : "Move should not be produced in a non-SSA compilation: " + move; + + Interval curInterval = allocator.intervalFor(move.getResult()); + + if (!isRegister(curInterval.location()) && curInterval.alwaysInMemory() && !isPhiResolutionMove(block, move, curInterval)) { + assert isStackSlotValue(curInterval.location()) : "Not a stack slot: " + curInterval.location(); + return true; + } + return false; + } + + private boolean isPhiResolutionMove(AbstractBlockBase block, MoveOp move, Interval toInterval) { + if (!LinearScanPhase.SSA_LSRA.getValue()) { + return false; + } + if (!toInterval.isSplitParent()) { + return false; + } + if ((toInterval.from() & 1) == 1) { + // phi intervals start at even positions. + return false; + } + if (block.getSuccessorCount() != 1) { + return false; + } + LIRInstruction op = allocator.instructionForId(toInterval.from()); + if (!(op instanceof LabelOp)) { + return false; + } + AbstractBlockBase intStartBlock = allocator.blockForId(toInterval.from()); + assert allocator.ir.getLIRforBlock(intStartBlock).get(0).equals(op); + if (!block.getSuccessors().get(0).equals(intStartBlock)) { + return false; + } + try (Indent indet = Debug.indent()) { + Debug.log("Is a move (%s) to phi interval %s", move, toInterval); + } + return true; + } +} diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/SSALinearScanLifetimeAnalysisPhase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/SSALinearScanLifetimeAnalysisPhase.java Tue May 12 20:56:04 2015 +0200 @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.lir.alloc.lsra; + +import java.util.*; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.debug.*; +import com.oracle.graal.lir.*; +import com.oracle.graal.lir.LIRInstruction.*; +import com.oracle.graal.lir.StandardOp.*; +import com.oracle.graal.lir.ssa.*; + +public class SSALinearScanLifetimeAnalysisPhase extends LinearScanLifetimeAnalysisPhase { + + SSALinearScanLifetimeAnalysisPhase(LinearScan linearScan) { + super(linearScan); + } + + @Override + void addRegisterHint(final LIRInstruction op, final Value targetValue, OperandMode mode, EnumSet flags, final boolean hintAtDef) { + super.addRegisterHint(op, targetValue, mode, flags, hintAtDef); + + if (hintAtDef && op instanceof LabelOp) { + LabelOp label = (LabelOp) op; + + Interval to = allocator.getOrCreateInterval((AllocatableValue) targetValue); + + SSAUtils.forEachPhiRegisterHint(allocator.ir, allocator.blockForId(label.id()), label, targetValue, mode, (ValueConsumer) (registerHint, valueMode, valueFlags) -> { + if (LinearScan.isVariableOrRegister(registerHint)) { + Interval from = allocator.getOrCreateInterval((AllocatableValue) registerHint); + + setHint(op, to, from); + setHint(op, from, to); + } + }); + } + } + + private static void setHint(final LIRInstruction op, Interval target, Interval source) { + Interval currentHint = target.locationHint(false); + if (currentHint == null || currentHint.from() > target.from()) { + /* + * Update hint if there was none or if the hint interval starts after the hinted + * interval. + */ + target.setLocationHint(source); + if (Debug.isLogEnabled()) { + Debug.log("operation at opId %d: added hint from interval %d to %d", op.id(), source.operandNumber, target.operandNumber); + } + } + } +} diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/SSAMoveResolver.java diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/LIRGeneratorTool.java diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.lir/src/com/oracle/graal/lir/stackslotalloc/StackInterval.java --- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/stackslotalloc/StackInterval.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/stackslotalloc/StackInterval.java Tue May 12 20:56:04 2015 +0200 @@ -99,7 +99,7 @@ @Override public String toString() { - return String.format("SI[%d-%d] k=%s o=%s l=%s h=%s", from, to, kind, operand, location, hint.getOperand()); + return String.format("SI[%d-%d] k=%s o=%s l=%s h=%s", from, to, kind, operand, location, hint != null ? hint.getOperand() : "null"); } public void setLocationHint(StackInterval locationHint) { diff -r d24e4f349cbc -r 25bd9e2320de 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 May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/LoopEx.java Tue May 12 20:56:04 2015 +0200 @@ -162,7 +162,7 @@ public boolean detectCounted() { LoopBeginNode loopBegin = loopBegin(); FixedNode next = loopBegin.next(); - while (next instanceof FixedGuardNode || next instanceof ValueAnchorNode) { + while (next instanceof FixedGuardNode || next instanceof ValueAnchorNode || next instanceof InfopointNode) { next = ((FixedWithNextNode) next).next(); } if (next instanceof IfNode) { diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/FrameState.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/FrameState.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/FrameState.java Tue May 12 20:56:04 2015 +0200 @@ -117,6 +117,19 @@ bci == BytecodeFrame.INVALID_FRAMESTATE_BCI; } + /** + * Creates a placeholder frame state with a single element on the stack representing a return + * value. This allows the parsing of an intrinsic to communicate the returned value in a + * {@link StateSplit#stateAfter() stateAfter} to the inlining call site. + * + * @param bci this must be {@link BytecodeFrame#AFTER_BCI} + */ + public FrameState(int bci, ValueNode returnValue) { + this(null, null, bci, 0, returnValue.getKind().getSlotCount(), 0, false, false, null, Collections. emptyList()); + assert bci == BytecodeFrame.AFTER_BCI; + this.values.initialize(0, returnValue); + } + public FrameState(FrameState outerFrameState, ResolvedJavaMethod method, int bci, ValueNode[] locals, ValueNode[] stack, int stackSize, ValueNode[] locks, List monitorIds, boolean rethrowException, boolean duringCall) { this(outerFrameState, method, bci, locals.length, stackSize, locks.length, rethrowException, duringCall, monitorIds, Collections. emptyList()); @@ -153,6 +166,17 @@ this.outerFrameState = x; } + public BytecodePosition toBytecodePosition() { + return toBytecodePosition(this); + } + + public static BytecodePosition toBytecodePosition(FrameState fs) { + if (fs == null) { + return null; + } + return new BytecodePosition(toBytecodePosition(fs.outerFrameState()), fs.method(), fs.bci); + } + /** * @see BytecodeFrame#rethrowException */ @@ -410,7 +434,11 @@ String nl = CodeUtil.NEW_LINE; FrameState fs = frameState; while (fs != null) { - MetaUtil.appendLocation(sb, fs.method, fs.bci).append(nl); + MetaUtil.appendLocation(sb, fs.method, fs.bci); + if (BytecodeFrame.isPlaceholderBci(fs.bci)) { + sb.append("//").append(getPlaceholderBciName(fs.bci)); + } + sb.append(nl); sb.append("locals: ["); for (int i = 0; i < fs.localsSize(); i++) { sb.append(i == 0 ? "" : ", ").append(fs.localAt(i) == null ? "_" : fs.localAt(i).toString(Verbosity.Id)); @@ -434,7 +462,11 @@ if (verbosity == Verbosity.Debugger) { return toString(this); } else if (verbosity == Verbosity.Name) { - return super.toString(Verbosity.Name) + "@" + bci; + String res = super.toString(Verbosity.Name) + "@" + bci; + if (BytecodeFrame.isPlaceholderBci(bci)) { + res += "[" + getPlaceholderBciName(bci) + "]"; + } + return res; } else { return super.toString(verbosity); } diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GraphDecoder.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GraphDecoder.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GraphDecoder.java Tue May 12 20:56:04 2015 +0200 @@ -409,7 +409,7 @@ methodScope.unwindNode = (UnwindNode) node; } else { - simplifyFixedNode(methodScope, loopScope, nodeOrderId, node); + handleFixedNode(methodScope, loopScope, nodeOrderId, node); } return resultScope; @@ -527,7 +527,7 @@ * @param nodeOrderId The orderId of the node. * @param node The node to be simplified. */ - protected void simplifyFixedNode(MethodScope methodScope, LoopScope loopScope, int nodeOrderId, FixedNode node) { + protected void handleFixedNode(MethodScope methodScope, LoopScope loopScope, int nodeOrderId, FixedNode node) { } protected void handleProxyNodes(MethodScope methodScope, LoopScope loopScope, LoopExitNode loopExit) { diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/SimpleInfopointNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/SimpleInfopointNode.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/SimpleInfopointNode.java Tue May 12 20:56:04 2015 +0200 @@ -48,15 +48,7 @@ } public void addCaller(BytecodePosition caller) { - this.position = relink(this.position, caller); - } - - private static BytecodePosition relink(BytecodePosition position, BytecodePosition link) { - if (position.getCaller() == null) { - return new BytecodePosition(link, position.getMethod(), position.getBCI()); - } else { - return new BytecodePosition(relink(position.getCaller(), link), position.getMethod(), position.getBCI()); - } + this.position = position.addCaller(caller); } @Override @@ -65,4 +57,21 @@ graph().removeFixed(this); } } + + public void setPosition(BytecodePosition position) { + this.position = position; + } + + @Override + public boolean verify() { + BytecodePosition pos = position; + if (pos != null) { + // Verify that the outermost position belongs to this graph. + while (pos.getCaller() != null) { + pos = pos.getCaller(); + } + assert pos.getMethod().equals(graph().method()); + } + return super.verify(); + } } diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/SimplifyingGraphDecoder.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/SimplifyingGraphDecoder.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/SimplifyingGraphDecoder.java Tue May 12 20:56:04 2015 +0200 @@ -91,7 +91,7 @@ } @Override - protected void simplifyFixedNode(MethodScope methodScope, LoopScope loopScope, int nodeOrderId, FixedNode node) { + protected void handleFixedNode(MethodScope methodScope, LoopScope loopScope, int nodeOrderId, FixedNode node) { if (node instanceof IfNode) { IfNode ifNode = (IfNode) node; if (ifNode.condition() instanceof LogicNegationNode) { diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/OrNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/OrNode.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/OrNode.java Tue May 12 20:56:04 2015 +0200 @@ -37,7 +37,7 @@ import com.oracle.graal.nodes.util.*; @NodeInfo(shortName = "|") -public final class OrNode extends BinaryArithmeticNode implements BinaryCommutative { +public final class OrNode extends BinaryArithmeticNode implements BinaryCommutative, NarrowableArithmeticNode { public static final NodeClass TYPE = NodeClass.create(OrNode.class); diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/SqrtNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/SqrtNode.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/SqrtNode.java Tue May 12 20:56:04 2015 +0200 @@ -34,7 +34,7 @@ * Square root. */ @NodeInfo -public final class SqrtNode extends UnaryArithmeticNode implements ArithmeticLIRLowerable, NarrowableArithmeticNode { +public final class SqrtNode extends UnaryArithmeticNode implements ArithmeticLIRLowerable { public static final NodeClass TYPE = NodeClass.create(SqrtNode.class); diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/XorNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/XorNode.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/XorNode.java Tue May 12 20:56:04 2015 +0200 @@ -37,7 +37,7 @@ import com.oracle.graal.nodes.util.*; @NodeInfo(shortName = "^") -public final class XorNode extends BinaryArithmeticNode implements BinaryCommutative { +public final class XorNode extends BinaryArithmeticNode implements BinaryCommutative, NarrowableArithmeticNode { public static final NodeClass TYPE = NodeClass.create(XorNode.class); diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/ForeignCallNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/ForeignCallNode.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/ForeignCallNode.java Tue May 12 20:56:04 2015 +0200 @@ -107,6 +107,12 @@ } @Override + public void setStateAfter(FrameState x) { + assert hasSideEffect() || x == null; + super.setStateAfter(x); + } + + @Override public FrameState stateDuring() { return stateDuring; } diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningUtil.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningUtil.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningUtil.java Tue May 12 20:56:04 2015 +0200 @@ -427,11 +427,18 @@ } public static BytecodePosition processSimpleInfopoint(Invoke invoke, SimpleInfopointNode infopointNode, BytecodePosition incomingPos) { - BytecodePosition pos = incomingPos != null ? incomingPos : new BytecodePosition(toBytecodePosition(invoke.stateAfter().outerFrameState()), invoke.asNode().graph().method(), invoke.bci()); + BytecodePosition pos = processBytecodePosition(invoke, incomingPos); infopointNode.addCaller(pos); + assert infopointNode.verify(); return pos; } + public static BytecodePosition processBytecodePosition(Invoke invoke, BytecodePosition incomingPos) { + assert invoke.stateAfter() != null; + assert incomingPos == null || incomingPos.equals(InliningUtil.processBytecodePosition(invoke, null)) : incomingPos + " " + InliningUtil.processBytecodePosition(invoke, null); + return incomingPos != null ? incomingPos : new BytecodePosition(FrameState.toBytecodePosition(invoke.stateAfter().outerFrameState()), invoke.stateAfter().method(), invoke.bci()); + } + public static void processMonitorId(FrameState stateAfter, MonitorIdNode monitorIdNode) { if (stateAfter != null) { int callerLockDepth = stateAfter.nestedLockDepth(); @@ -439,13 +446,6 @@ } } - private static BytecodePosition toBytecodePosition(FrameState fs) { - if (fs == null) { - return null; - } - return new BytecodePosition(toBytecodePosition(fs.outerFrameState()), fs.method(), fs.bci); - } - protected static void processFrameStates(Invoke invoke, StructuredGraph inlineGraph, Map duplicates, FrameState stateAtExceptionEdge, boolean alwaysDuplicateStateAfter) { FrameState stateAtReturn = invoke.stateAfter(); FrameState outerFrameState = null; @@ -468,14 +468,28 @@ Kind invokeReturnKind = invoke.asNode().getKind(); if (frameState.bci == BytecodeFrame.AFTER_BCI) { + FrameState stateAfterReturn = stateAtReturn; + if (frameState.method() == null) { + // This is a frame state for a side effect within an intrinsic + // that was parsed for post-parse intrinsification + for (Node usage : frameState.usages()) { + if (usage instanceof ForeignCallNode) { + // A foreign call inside an intrinsic needs to have + // the BCI of the invoke being intrinsified + ForeignCallNode foreign = (ForeignCallNode) usage; + foreign.setBci(invoke.bci()); + } + } + } + /* * pop return kind from invoke's stateAfter and replace with this frameState's return * value (top of stack) */ - FrameState stateAfterReturn = stateAtReturn; if (invokeReturnKind != Kind.Void && (alwaysDuplicateStateAfter || (frameState.stackSize() > 0 && stateAfterReturn.stackAt(0) != frameState.stackAt(0)))) { stateAfterReturn = stateAtReturn.duplicateModified(invokeReturnKind, frameState.stackAt(0)); } + frameState.replaceAndDelete(stateAfterReturn); return stateAfterReturn; } else if (stateAtExceptionEdge != null && isStateAfterException(frameState)) { @@ -581,25 +595,26 @@ ValueNode singleReturnValue = null; PhiNode returnValuePhi = null; for (ReturnNode returnNode : returnNodes) { - if (returnNode.result() != null) { - if (returnValuePhi == null && (singleReturnValue == null || singleReturnValue == returnNode.result())) { + ValueNode result = returnNode.result(); + if (result != null) { + if (returnValuePhi == null && (singleReturnValue == null || singleReturnValue == result)) { /* Only one return value, so no need yet for a phi node. */ - singleReturnValue = returnNode.result(); + singleReturnValue = result; } else if (returnValuePhi == null) { /* Found a second return value, so create phi node. */ - returnValuePhi = merge.graph().addWithoutUnique(new ValuePhiNode(returnNode.result().stamp().unrestricted(), merge)); + returnValuePhi = merge.graph().addWithoutUnique(new ValuePhiNode(result.stamp().unrestricted(), merge)); if (canonicalizedNodes != null) { canonicalizedNodes.add(returnValuePhi); } for (int i = 0; i < merge.forwardEndCount(); i++) { returnValuePhi.addInput(singleReturnValue); } - returnValuePhi.addInput(returnNode.result()); + returnValuePhi.addInput(result); } else { /* Multiple return values, just add to existing phi node. */ - returnValuePhi.addInput(returnNode.result()); + returnValuePhi.addInput(result); } } diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/DynamicNewArrayTest.java --- a/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/DynamicNewArrayTest.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/DynamicNewArrayTest.java Tue May 12 20:56:04 2015 +0200 @@ -55,6 +55,7 @@ @Test public void test4() { test("dynamic", Boolean.class, -7); + test("dynamicSynchronized", Boolean.class, -7); } @Test @@ -95,4 +96,8 @@ public static Object dynamic(Class elementType, int length) { return Array.newInstance(elementType, length); } + + public static synchronized Object dynamicSynchronized(Class elementType, int length) { + return Array.newInstance(elementType, length); + } } diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/ObjectAccessTest.java --- a/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/ObjectAccessTest.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/ObjectAccessTest.java Tue May 12 20:56:04 2015 +0200 @@ -32,7 +32,6 @@ import com.oracle.graal.nodes.calc.*; import com.oracle.graal.nodes.extended.*; import com.oracle.graal.replacements.*; -import com.oracle.graal.replacements.ReplacementsImpl.FrameStateProcessing; import com.oracle.graal.word.*; /** @@ -50,7 +49,7 @@ @Override protected StructuredGraph parseEager(ResolvedJavaMethod m, AllowAssumptions allowAssumptions) { - return installer.makeGraph(m, null, null, FrameStateProcessing.CollapseFrameForSingleSideEffect); + return installer.makeGraph(m, null, null); } @Test diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/PointerTest.java --- a/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/PointerTest.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/PointerTest.java Tue May 12 20:56:04 2015 +0200 @@ -35,7 +35,6 @@ import com.oracle.graal.phases.common.*; import com.oracle.graal.phases.tiers.*; import com.oracle.graal.replacements.*; -import com.oracle.graal.replacements.ReplacementsImpl.FrameStateProcessing; import com.oracle.graal.word.*; import com.oracle.graal.word.nodes.*; @@ -56,7 +55,7 @@ @Override protected StructuredGraph parseEager(ResolvedJavaMethod m, AllowAssumptions allowAssumptions) { - return installer.makeGraph(m, null, null, FrameStateProcessing.CollapseFrameForSingleSideEffect); + return installer.makeGraph(m, null, null); } @Test diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/WordTest.java --- a/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/WordTest.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/WordTest.java Tue May 12 20:56:04 2015 +0200 @@ -29,7 +29,6 @@ import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions; import com.oracle.graal.replacements.*; -import com.oracle.graal.replacements.ReplacementsImpl.FrameStateProcessing; import com.oracle.graal.word.*; /** @@ -45,7 +44,7 @@ @Override protected StructuredGraph parseEager(ResolvedJavaMethod m, AllowAssumptions allowAssumptions) { - return installer.makeGraph(m, null, null, FrameStateProcessing.CollapseFrameForSingleSideEffect); + return installer.makeGraph(m, null, null); } @Test diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/CollapseFrameForSingleSideEffectPhase.java --- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/CollapseFrameForSingleSideEffectPhase.java Tue May 12 20:55:48 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,240 +0,0 @@ -/* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.graal.replacements; - -import static com.oracle.graal.api.code.BytecodeFrame.*; - -import java.util.*; - -import com.oracle.graal.api.code.*; -import com.oracle.graal.graph.*; -import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.util.*; -import com.oracle.graal.phases.*; -import com.oracle.graal.phases.graph.*; -import com.oracle.graal.phases.graph.ReentrantNodeIterator.LoopInfo; -import com.oracle.graal.phases.graph.ReentrantNodeIterator.NodeIteratorClosure; - -/** - * This phase ensures that there's a single {@linkplain BytecodeFrame#AFTER_BCI collapsed frame - * state} per path. - * - * Removes other frame states from {@linkplain StateSplit#hasSideEffect() non-side-effecting} nodes - * in the graph, and replaces them with {@linkplain BytecodeFrame#INVALID_FRAMESTATE_BCI invalid - * frame states}. - * - * The invalid frame states ensure that no deoptimization to a snippet frame state will happen. - */ -public class CollapseFrameForSingleSideEffectPhase extends Phase { - - private static class IterationState { - public final IterationState previous; - public final Node node; - public final Collection merge; - public final boolean invalid; - - private IterationState(IterationState previous, Node node, Collection merge, boolean invalid) { - this.previous = previous; - this.node = node; - this.merge = merge; - this.invalid = invalid; - } - - public IterationState() { - this(null, null, null, false); - } - - public IterationState addSideEffect(StateSplit sideEffect) { - return new IterationState(this, sideEffect.asNode(), null, true); - } - - public IterationState addBranch(AbstractBeginNode begin) { - return new IterationState(this, begin, null, this.invalid); - } - - public static IterationState merge(AbstractMergeNode merge, Collection before, boolean invalid) { - return new IterationState(null, merge, before, invalid); - } - - public void markAll(NodeBitMap set) { - IterationState state = this; - while (state != null && state.node != null && !set.contains(state.node)) { - set.mark(state.node); - if (state.merge != null) { - for (IterationState branch : state.merge) { - branch.markAll(set); - } - } - state = state.previous; - } - } - - public void markMasked(NodeBitMap unmasked, NodeBitMap masked) { - IterationState state = this; - while (state != null && state.node != null && !masked.contains(state.node)) { - if (state.node instanceof StateSplit) { - unmasked.mark(state.node); - StateSplit split = (StateSplit) state.node; - if (split.hasSideEffect() && state.previous != null) { - state.previous.markAll(masked); - return; - } - } - - if (state.merge != null) { - for (IterationState branch : state.merge) { - branch.markMasked(unmasked, masked); - } - } - state = state.previous; - } - } - } - - @Override - protected void run(StructuredGraph graph) { - CollapseFrameForSingleSideEffectClosure closure = new CollapseFrameForSingleSideEffectClosure(); - ReentrantNodeIterator.apply(closure, graph.start(), new IterationState()); - closure.finishProcessing(graph); - } - - private static class CollapseFrameForSingleSideEffectClosure extends NodeIteratorClosure { - - private List returnStates = new ArrayList<>(); - private List unwindStates = new ArrayList<>(); - - @Override - protected IterationState processNode(FixedNode node, IterationState currentState) { - IterationState state = currentState; - if (node instanceof StateSplit) { - StateSplit stateSplit = (StateSplit) node; - FrameState frameState = stateSplit.stateAfter(); - if (frameState != null) { - if (stateSplit.hasSideEffect()) { - setStateAfter(node.graph(), stateSplit, INVALID_FRAMESTATE_BCI, false); - state = state.addSideEffect(stateSplit); - } else if (currentState.invalid) { - setStateAfter(node.graph(), stateSplit, INVALID_FRAMESTATE_BCI, false); - } else if (stateSplit instanceof StartNode) { - setStateAfter(node.graph(), stateSplit, BEFORE_BCI, false); - } else { - stateSplit.setStateAfter(null); - if (frameState.hasNoUsages()) { - GraphUtil.killWithUnusedFloatingInputs(frameState); - } - } - } - } - if (node instanceof ReturnNode) { - returnStates.add(currentState); - } else if (node instanceof UnwindNode) { - unwindStates.add(currentState); - } - return state; - } - - @Override - protected IterationState merge(AbstractMergeNode merge, List states) { - boolean invalid = false; - for (IterationState state : states) { - if (state.invalid) { - invalid = true; - break; - } - } - return IterationState.merge(merge, states, invalid); - } - - public void finishProcessing(StructuredGraph graph) { - NodeBitMap maskedSideEffects = new NodeBitMap(graph); - NodeBitMap returnSideEffects = new NodeBitMap(graph); - NodeBitMap unwindSideEffects = new NodeBitMap(graph); - - for (IterationState returnState : returnStates) { - returnState.markMasked(returnSideEffects, maskedSideEffects); - } - for (IterationState unwindState : unwindStates) { - unwindState.markMasked(unwindSideEffects, maskedSideEffects); - } - - for (Node returnSideEffect : returnSideEffects) { - if (!unwindSideEffects.contains(returnSideEffect) && !maskedSideEffects.contains(returnSideEffect)) { - StateSplit split = (StateSplit) returnSideEffect; - setStateAfter(graph, split, AFTER_BCI, true); - } - } - - for (Node unwindSideEffect : unwindSideEffects) { - if (!returnSideEffects.contains(unwindSideEffect) && !maskedSideEffects.contains(unwindSideEffect)) { - StateSplit split = (StateSplit) unwindSideEffect; - setStateAfter(graph, split, AFTER_EXCEPTION_BCI, true); - } - } - } - - @Override - protected IterationState afterSplit(AbstractBeginNode node, IterationState oldState) { - return oldState.addBranch(node); - } - - @Override - protected Map processLoop(LoopBeginNode loop, IterationState initialState) { - LoopInfo info = ReentrantNodeIterator.processLoop(this, loop, initialState); - - boolean isNowInvalid = initialState.invalid; - for (IterationState endState : info.endStates.values()) { - isNowInvalid |= endState.invalid; - } - - if (isNowInvalid) { - setStateAfter(loop.graph(), loop, INVALID_FRAMESTATE_BCI, false); - } - - IterationState endState = IterationState.merge(loop, info.endStates.values(), isNowInvalid); - return ReentrantNodeIterator.processLoop(this, loop, endState).exitStates; - } - - /** - * Creates and sets a special frame state for a node. If the existing frame state is - * non-null and has no other usages, it is deleted via - * {@link GraphUtil#killWithUnusedFloatingInputs(Node)}. - * - * @param graph the graph context - * @param node the node whose frame state is updated - * @param bci {@link BytecodeFrame#BEFORE_BCI}, {@link BytecodeFrame#AFTER_EXCEPTION_BCI} or - * {@link BytecodeFrame#INVALID_FRAMESTATE_BCI} - * @param replaceOnly only perform the update if the node currently has a non-null frame - * state - */ - private static void setStateAfter(StructuredGraph graph, StateSplit node, int bci, boolean replaceOnly) { - assert (bci == BEFORE_BCI && node instanceof StartNode) || bci == AFTER_BCI || bci == AFTER_EXCEPTION_BCI || bci == INVALID_FRAMESTATE_BCI; - FrameState currentStateAfter = node.stateAfter(); - if (currentStateAfter != null || !replaceOnly) { - node.setStateAfter(graph.add(new FrameState(bci))); - if (currentStateAfter != null && currentStateAfter.hasNoUsages()) { - GraphUtil.killWithUnusedFloatingInputs(currentStateAfter); - } - } - } - } -} diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/DefaultGenericInvocationPlugin.java --- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/DefaultGenericInvocationPlugin.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/DefaultGenericInvocationPlugin.java Tue May 12 20:56:04 2015 +0200 @@ -73,9 +73,9 @@ } public boolean apply(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args) { - if (b.parsingReplacement() && wordOperationPlugin.apply(b, method, args)) { + if (b.parsingIntrinsic() && wordOperationPlugin.apply(b, method, args)) { return true; - } else if (b.parsingReplacement()) { + } else if (b.parsingIntrinsic()) { NodeIntrinsic intrinsic = nodeIntrinsification.getIntrinsic(method); if (intrinsic != null) { Signature sig = method.getSignature(); @@ -145,12 +145,18 @@ b.add(new UnsafeStoreNode(copy.destinationObject(), copy.destinationOffset(), value, copy.accessKind(), copy.getLocationIdentity())); return true; } else if (res instanceof ForeignCallNode) { - ForeignCallNode foreign = (ForeignCallNode) res; - foreign.setBci(b.bci()); + /* + * Need to update the BCI of a ForeignCallNode so that it gets the stateDuring in the + * case that the foreign call can deoptimize. As with all deoptimization, we need a + * state in a normal method as opposed to an intrinsic. + */ + GraphBuilderContext ancestor = b.getNonReplacementAncestor(); + if (ancestor != null) { + ForeignCallNode foreign = (ForeignCallNode) res; + foreign.setBci(ancestor.bci()); + } } - res = b.add(res); - boolean nonValueType = false; if (returnKind == Kind.Object && stamp instanceof ObjectStamp) { ResolvedJavaType type = ((ObjectStamp) stamp).type(); @@ -162,16 +168,10 @@ if (returnKind != Kind.Void) { assert nonValueType || res.getKind().getStackKind() != Kind.Void; - b.push(returnKind.getStackKind(), res); + res = b.addPush(returnKind.getStackKind(), res); } else { assert res.getKind().getStackKind() == Kind.Void; - } - - if (res instanceof StateSplit) { - StateSplit stateSplit = (StateSplit) res; - if (stateSplit.stateAfter() == null) { - stateSplit.setStateAfter(b.createStateAfter()); - } + res = b.add(res); } return true; diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/DefaultInlineInvokePlugin.java --- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/DefaultInlineInvokePlugin.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/DefaultInlineInvokePlugin.java Tue May 12 20:56:04 2015 +0200 @@ -40,7 +40,7 @@ InlineInfo inlineInfo = replacements.getInlineInfo(b, method, args, returnType); if (inlineInfo == null) { if (InlineDuringParsing.getValue() && method.hasBytecodes() && method.getCode().length <= TrivialInliningSize.getValue() && b.getDepth() < InlineDuringParsingMaxDepth.getValue()) { - return new InlineInfo(method, false, false); + return new InlineInfo(method, false); } } return inlineInfo; diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/GraphKit.java --- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/GraphKit.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/GraphKit.java Tue May 12 20:56:04 2015 +0200 @@ -22,6 +22,8 @@ */ package com.oracle.graal.replacements; +import static com.oracle.graal.graphbuilderconf.IntrinsicContext.CompilationContext.*; + import java.lang.reflect.*; import java.util.*; @@ -152,7 +154,7 @@ if (invoke.getKind() != Kind.Void) { frameStateBuilder.push(returnType.getKind(), invoke); } - invoke.setStateAfter(frameStateBuilder.create(bci)); + invoke.setStateAfter(frameStateBuilder.create(bci, invoke)); if (invoke.getKind() != Kind.Void) { frameStateBuilder.pop(returnType.getKind()); } @@ -219,7 +221,7 @@ GraphBuilderConfiguration config = GraphBuilderConfiguration.getSnippetDefault(plugins); StructuredGraph calleeGraph = new StructuredGraph(method, AllowAssumptions.NO); - IntrinsicContext initialReplacementContext = new IntrinsicContext(method, method, null, IntrinsicContext.POST_PARSE_INLINE_BCI); + IntrinsicContext initialReplacementContext = new IntrinsicContext(method, method, INLINE_AFTER_PARSING); new GraphBuilderPhase.Instance(metaAccess, providers.getStampProvider(), providers.getConstantReflection(), config, OptimisticOptimizations.NONE, initialReplacementContext).apply(calleeGraph); // Remove all frame states from inlinee diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/IntrinsicGraphBuilder.java --- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/IntrinsicGraphBuilder.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/IntrinsicGraphBuilder.java Tue May 12 20:56:04 2015 +0200 @@ -152,8 +152,10 @@ return graph; } - public FrameState createStateAfter() { - return getGraph().add(new FrameState(BytecodeFrame.BEFORE_BCI)); + public void setStateAfter(StateSplit sideEffect) { + assert sideEffect.hasSideEffect(); + FrameState stateAfter = getGraph().add(new FrameState(BytecodeFrame.BEFORE_BCI)); + sideEffect.setStateAfter(stateAfter); } public GraphBuilderContext getParent() { @@ -180,11 +182,11 @@ return 0; } - public boolean parsingReplacement() { + public boolean parsingIntrinsic() { return true; } - public Replacement getReplacement() { + public IntrinsicContext getIntrinsic() { throw GraalInternalError.shouldNotReachHere(); } diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/PEGraphDecoder.java --- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/PEGraphDecoder.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/PEGraphDecoder.java Tue May 12 20:56:04 2015 +0200 @@ -97,6 +97,15 @@ public boolean isInlinedMethod() { return caller != null; } + + public BytecodePosition getBytecodePosition() { + if (bytecodePosition == null) { + ensureOuterStateDecoded(this); + ensureExceptionStateDecoded(this); + bytecodePosition = InliningUtil.processBytecodePosition(invokeData.invoke, null); + } + return bytecodePosition; + } } protected class PENonAppendGraphBuilderContext implements GraphBuilderContext { @@ -139,7 +148,7 @@ } @Override - public Replacement getReplacement() { + public IntrinsicContext getIntrinsic() { return null; } @@ -169,7 +178,7 @@ } @Override - public FrameState createStateAfter() { + public void setStateAfter(StateSplit stateSplit) { throw unimplemented(); } @@ -217,10 +226,11 @@ } @Override - public FrameState createStateAfter() { + public void setStateAfter(StateSplit stateSplit) { Node stateAfter = decodeFloatingNode(methodScope.caller, methodScope.callerLoopScope, methodScope.invokeData.stateAfterOrderId); getGraph().add(stateAfter); - return (FrameState) handleFloatingNodeAfterAdd(methodScope.caller, methodScope.callerLoopScope, stateAfter); + FrameState fs = (FrameState) handleFloatingNodeAfterAdd(methodScope.caller, methodScope.callerLoopScope, stateAfter); + stateSplit.setStateAfter(fs); } @Override @@ -411,7 +421,7 @@ if (inlineInfo == null) { return false; } - assert !inlineInfo.isIntrinsic && !inlineInfo.isReplacement : "not supported"; + assert !inlineInfo.isIntrinsic : "not supported"; ResolvedJavaMethod inlineMethod = inlineInfo.methodToInline; EncodedGraph graphToInline = lookupEncodedGraph(inlineMethod); @@ -519,6 +529,15 @@ protected abstract EncodedGraph lookupEncodedGraph(ResolvedJavaMethod method); @Override + protected void handleFixedNode(MethodScope s, LoopScope loopScope, int nodeOrderId, FixedNode node) { + PEMethodScope methodScope = (PEMethodScope) s; + if (node instanceof SimpleInfopointNode && methodScope.isInlinedMethod()) { + InliningUtil.processSimpleInfopoint(methodScope.invokeData.invoke, (SimpleInfopointNode) node, methodScope.getBytecodePosition()); + } + super.handleFixedNode(s, loopScope, nodeOrderId, node); + } + + @Override protected Node handleFloatingNodeBeforeAdd(MethodScope s, LoopScope loopScope, Node node) { PEMethodScope methodScope = (PEMethodScope) s; @@ -591,11 +610,7 @@ PEMethodScope methodScope = (PEMethodScope) s; if (methodScope.isInlinedMethod()) { - if (node instanceof SimpleInfopointNode) { - methodScope.bytecodePosition = InliningUtil.processSimpleInfopoint(methodScope.invokeData.invoke, (SimpleInfopointNode) node, methodScope.bytecodePosition); - return node; - - } else if (node instanceof FrameState) { + if (node instanceof FrameState) { FrameState frameState = (FrameState) node; ensureOuterStateDecoded(methodScope); diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java --- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java Tue May 12 20:56:04 2015 +0200 @@ -24,6 +24,7 @@ import static com.oracle.graal.api.meta.MetaUtil.*; import static com.oracle.graal.compiler.common.GraalOptions.*; +import static com.oracle.graal.graphbuilderconf.IntrinsicContext.CompilationContext.*; import static com.oracle.graal.java.AbstractBytecodeParser.Options.*; import static com.oracle.graal.phases.common.DeadCodeEliminationPhase.Optionality.*; import static java.lang.String.*; @@ -45,7 +46,6 @@ import com.oracle.graal.graph.Node.NodeIntrinsic; import com.oracle.graal.graphbuilderconf.*; import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins; -import com.oracle.graal.graphbuilderconf.GraphBuilderContext.Replacement; import com.oracle.graal.java.*; import com.oracle.graal.java.GraphBuilderPhase.Instance; import com.oracle.graal.nodes.*; @@ -93,13 +93,13 @@ public InlineInfo getInlineInfo(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args, JavaType returnType) { ResolvedJavaMethod subst = getSubstitutionMethod(method); if (subst != null) { - if (b.parsingReplacement() || InlineDuringParsing.getValue() || InlineIntrinsicsDuringParsing.getValue()) { + if (b.parsingIntrinsic() || InlineDuringParsing.getValue() || InlineIntrinsicsDuringParsing.getValue()) { // Forced inlining of intrinsics - return new InlineInfo(subst, true, true); + return new InlineInfo(subst, true); } return null; } - if (b.parsingReplacement()) { + if (b.parsingIntrinsic()) { assert !hasGenericInvocationPluginAnnotation(method) : format("%s should have been handled by %s", method.format("%H.%n(%p)"), DefaultGenericInvocationPlugin.class.getName()); assert b.getDepth() < MAX_GRAPH_INLINING_DEPTH : "inlining limit exceeded"; @@ -110,7 +110,7 @@ } // Force inlining when parsing replacements - return new InlineInfo(method, true, true); + return new InlineInfo(method, true); } else { assert method.getAnnotation(NodeIntrinsic.class) == null : String.format("@%s method %s must only be called from within a replacement%n%s", NodeIntrinsic.class.getSimpleName(), method.format("%h.%n"), b); @@ -119,10 +119,10 @@ } public void notifyOfNoninlinedInvoke(GraphBuilderContext b, ResolvedJavaMethod method, Invoke invoke) { - if (b.parsingReplacement()) { - Replacement replacement = b.getReplacement(); - assert replacement.isCallToOriginal(method) : format("All non-recursive calls in the replacement %s must be inlined or intrinsified: found call to %s", - replacement.getReplacementMethod().format("%H.%n(%p)"), method.format("%h.%n(%p)")); + if (b.parsingIntrinsic()) { + IntrinsicContext intrinsic = b.getIntrinsic(); + assert intrinsic.isCallToOriginal(method) : format("All non-recursive calls in the intrinsic %s must be inlined or intrinsified: found call to %s", + intrinsic.getIntrinsicMethod().format("%H.%n(%p)"), method.format("%h.%n(%p)")); } } @@ -299,9 +299,7 @@ StructuredGraph graph = UseSnippetGraphCache ? graphs.get(method) : null; if (graph == null) { try (DebugCloseable a = SnippetPreparationTime.start()) { - FrameStateProcessing frameStateProcessing = method.getAnnotation(Snippet.class).removeAllFrameStates() ? FrameStateProcessing.Removal - : FrameStateProcessing.CollapseFrameForSingleSideEffect; - StructuredGraph newGraph = makeGraph(method, args, recursiveEntry, frameStateProcessing); + StructuredGraph newGraph = makeGraph(method, args, recursiveEntry); Debug.metric("SnippetNodeCount[%#s]", method).add(newGraph.getNodeCount()); if (!UseSnippetGraphCache || args != null) { return newGraph; @@ -346,7 +344,7 @@ } StructuredGraph graph = graphs.get(substitute); if (graph == null) { - graph = makeGraph(substitute, null, original, FrameStateProcessing.None); + graph = makeGraph(substitute, null, original); graph.freeze(); graphs.putIfAbsent(substitute, graph); graph = graphs.get(substitute); @@ -445,19 +443,18 @@ * @param args * @param original the original method if {@code method} is a {@linkplain MethodSubstitution * substitution} otherwise null - * @param frameStateProcessing controls how {@link FrameState FrameStates} should be handled. */ - public StructuredGraph makeGraph(ResolvedJavaMethod method, Object[] args, ResolvedJavaMethod original, FrameStateProcessing frameStateProcessing) { + public StructuredGraph makeGraph(ResolvedJavaMethod method, Object[] args, ResolvedJavaMethod original) { try (OverrideScope s = OptionValue.override(DeoptALot, false)) { - return createGraphMaker(method, original, frameStateProcessing).makeGraph(args); + return createGraphMaker(method, original).makeGraph(args); } } /** * Can be overridden to return an object that specializes various parts of graph preprocessing. */ - protected GraphMaker createGraphMaker(ResolvedJavaMethod substitute, ResolvedJavaMethod original, FrameStateProcessing frameStateProcessing) { - return new GraphMaker(this, substitute, original, frameStateProcessing); + protected GraphMaker createGraphMaker(ResolvedJavaMethod substitute, ResolvedJavaMethod original) { + return new GraphMaker(this, substitute, original); } /** @@ -465,18 +462,6 @@ */ final ConcurrentMap graphCache = new ConcurrentHashMap<>(); - public enum FrameStateProcessing { - None, - /** - * @see CollapseFrameForSingleSideEffectPhase - */ - CollapseFrameForSingleSideEffect, - /** - * Removes frame states from all nodes in the graph. - */ - Removal - } - /** * Creates and preprocesses a graph for a replacement. */ @@ -496,16 +481,10 @@ */ protected final ResolvedJavaMethod substitutedMethod; - /** - * Controls how FrameStates are processed. - */ - private FrameStateProcessing frameStateProcessing; - - protected GraphMaker(ReplacementsImpl replacements, ResolvedJavaMethod substitute, ResolvedJavaMethod substitutedMethod, FrameStateProcessing frameStateProcessing) { + protected GraphMaker(ReplacementsImpl replacements, ResolvedJavaMethod substitute, ResolvedJavaMethod substitutedMethod) { this.replacements = replacements; this.method = substitute; this.substitutedMethod = substitutedMethod; - this.frameStateProcessing = frameStateProcessing; } public StructuredGraph makeGraph(Object[] args) { @@ -536,18 +515,6 @@ new ConvertDeoptimizeToGuardPhase().apply(graph, null); assert sideEffectCount == graph.getNodes().filter(e -> hasSideEffect(e)).count() : "deleted side effecting node"; - switch (frameStateProcessing) { - case Removal: - for (Node node : graph.getNodes()) { - if (node instanceof StateSplit) { - ((StateSplit) node).setStateAfter(null); - } - } - break; - case CollapseFrameForSingleSideEffect: - new CollapseFrameForSingleSideEffectPhase().apply(graph); - break; - } new DeadCodeEliminationPhase(Required).apply(graph); } @@ -627,16 +594,16 @@ protected Instance createGraphBuilder(MetaAccessProvider metaAccess, StampProvider stampProvider, ConstantReflectionProvider constantReflection, GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts) { - ReplacementContext initialReplacementContext = null; + IntrinsicContext initialIntrinsicContext = null; if (method.getAnnotation(Snippet.class) == null) { - // Late inlined intrinsic - initialReplacementContext = new IntrinsicContext(substitutedMethod, method, null, -1); + // Post-parse inlined intrinsic + initialIntrinsicContext = new IntrinsicContext(substitutedMethod, method, INLINE_AFTER_PARSING); } else { // Snippet ResolvedJavaMethod original = substitutedMethod != null ? substitutedMethod : method; - initialReplacementContext = new ReplacementContext(original, method); + initialIntrinsicContext = new IntrinsicContext(original, method, INLINE_AFTER_PARSING); } - return new GraphBuilderPhase.Instance(metaAccess, stampProvider, constantReflection, graphBuilderConfig, optimisticOpts, initialReplacementContext); + return new GraphBuilderPhase.Instance(metaAccess, stampProvider, constantReflection, graphBuilderConfig, optimisticOpts, initialIntrinsicContext); } } diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/StandardGraphBuilderPlugins.java --- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/StandardGraphBuilderPlugins.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/StandardGraphBuilderPlugins.java Tue May 12 20:56:04 2015 +0200 @@ -84,6 +84,7 @@ if (Options.UseBlackholeSubstitution.getValue()) { registerJMHBlackholePlugins(plugins); } + registerJFRThrowablePlugins(plugins); } private static final Field STRING_VALUE_FIELD; @@ -459,7 +460,7 @@ } public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) { - if (b.parsingReplacement()) { + if (b.parsingIntrinsic()) { ResolvedJavaMethod rootMethod = b.getGraph().method(); if (b.getMetaAccess().lookupJavaType(BoxingSnippets.class).isAssignableFrom(rootMethod.getDeclaringClass())) { // Disable invocation plugins for boxing snippets so that the @@ -486,7 +487,7 @@ } public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) { - if (b.parsingReplacement()) { + if (b.parsingIntrinsic()) { ResolvedJavaMethod rootMethod = b.getGraph().method(); if (b.getMetaAccess().lookupJavaType(BoxingSnippets.class).isAssignableFrom(rootMethod.getDeclaringClass())) { // Disable invocation plugins for unboxing snippets so that the @@ -651,4 +652,18 @@ } } } + + private static void registerJFRThrowablePlugins(InvocationPlugins plugins) { + String name = "oracle.jrockit.jfr.jdkevents.ThrowableTracer"; + Class tracerClass = MethodSubstitutionPlugin.resolveClass(name, true); + if (tracerClass != null) { + Registration r = new Registration(plugins, tracerClass); + r.register2("traceThrowable", Throwable.class, String.class, new InvocationPlugin() { + public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode throwable, ValueNode message) { + b.add(new VirtualizableInvokeMacroNode(b.getInvokeKind(), targetMethod, b.bci(), b.getInvokeReturnType(), throwable, message)); + return true; + } + }); + } + } } diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/MacroNode.java --- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/MacroNode.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/MacroNode.java Tue May 12 20:56:04 2015 +0200 @@ -24,7 +24,6 @@ import static com.oracle.graal.api.code.BytecodeFrame.*; -import com.oracle.graal.api.code.*; import com.oracle.graal.api.meta.*; import com.oracle.graal.api.replacements.*; import com.oracle.graal.compiler.common.*; @@ -114,13 +113,6 @@ StructuredGraph methodSubstitution = tool.getReplacements().getSubstitution(getTargetMethod(), true, bci); if (methodSubstitution != null) { methodSubstitution = methodSubstitution.copy(); - if (stateAfter() == null || stateAfter().bci == BytecodeFrame.AFTER_BCI) { - /* - * handles the case of a MacroNode inside a snippet used for another MacroNode - * lowering - */ - new CollapseFrameForSingleSideEffectPhase().apply(methodSubstitution); - } return lowerReplacement(methodSubstitution, tool); } return null; diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/VirtualizableInvokeMacroNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/VirtualizableInvokeMacroNode.java Tue May 12 20:56:04 2015 +0200 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.replacements.nodes; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.nodeinfo.*; +import com.oracle.graal.nodes.CallTargetNode.InvokeKind; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.spi.*; + +/** + * A helper class to allow elimination of byte code instrumentation that could interfere with escape + * analysis. + */ +@NodeInfo +public class VirtualizableInvokeMacroNode extends MacroStateSplitNode implements Virtualizable { + + public static final NodeClass TYPE = NodeClass.create(VirtualizableInvokeMacroNode.class); + + public VirtualizableInvokeMacroNode(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, int bci, JavaType returnType, ValueNode... arguments) { + super(TYPE, invokeKind, targetMethod, bci, returnType, arguments); + } + + @Override + public void virtualize(VirtualizerTool tool) { + for (ValueNode arg : arguments) { + State state = tool.getObjectState(arg); + if (state != null && state.getState() == EscapeState.Virtual) { + tool.delete(); + } + } + } +} diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.truffle.hotspot/src/com/oracle/graal/truffle/hotspot/HotSpotTruffleRuntime.java --- a/graal/com.oracle.graal.truffle.hotspot/src/com/oracle/graal/truffle/hotspot/HotSpotTruffleRuntime.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.truffle.hotspot/src/com/oracle/graal/truffle/hotspot/HotSpotTruffleRuntime.java Tue May 12 20:56:04 2015 +0200 @@ -306,7 +306,7 @@ } @Override - public boolean enableInfopoints() { + public boolean platformEnableInfopoints() { return HotSpotGraalRuntime.runtime().getCompilerToVM().shouldDebugNonSafepoints(); } diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/BytecodeInterpreterPartialEvaluationTest.java --- a/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/BytecodeInterpreterPartialEvaluationTest.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/BytecodeInterpreterPartialEvaluationTest.java Tue May 12 20:56:04 2015 +0200 @@ -269,7 +269,7 @@ assertPartialEvalEqualsAndRunsCorrect(new Program("nestedLoopsProgram", bytecodes, 0, 6)); } - @Test(timeout = 1000) + @Test(timeout = 2000) public void manyIfsProgram() { byte[] bytecodes = new byte[]{ /* 0: */Bytecode.CONST, diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/ConditionAnchoringTest.java --- a/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/ConditionAnchoringTest.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/ConditionAnchoringTest.java Tue May 12 20:56:04 2015 +0200 @@ -147,7 +147,7 @@ private static final class InlineEverythingPlugin implements InlineInvokePlugin { public InlineInfo getInlineInfo(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args, JavaType returnType) { assert method.hasBytecodes(); - return new InlineInfo(method, false, false); + return new InlineInfo(method, false); } } diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/GraalTruffleRuntime.java --- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/GraalTruffleRuntime.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/GraalTruffleRuntime.java Tue May 12 20:56:04 2015 +0200 @@ -281,7 +281,12 @@ public abstract void reinstallStubs(); - public abstract boolean enableInfopoints(); + public final boolean enableInfopoints() { + /* Currently infopoints can change code generation so don't enable them automatically */ + return platformEnableInfopoints() && TruffleEnableInfopoints.getValue(); + } + + protected abstract boolean platformEnableInfopoints(); private final class DispatchTruffleCompilationListener implements GraalTruffleCompilationListener { diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java --- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java Tue May 12 20:56:04 2015 +0200 @@ -190,7 +190,7 @@ if (replacements.hasSubstitution(original, builder.bci())) { return null; } - assert !builder.parsingReplacement(); + assert !builder.parsingIntrinsic(); if (TruffleCompilerOptions.TruffleFunctionInlining.getValue()) { if (original.equals(callSiteProxyMethod)) { @@ -210,12 +210,12 @@ if (decision != null && decision.isInline()) { inlining.push(decision); builder.getAssumptions().record(new AssumptionValidAssumption((OptimizedAssumption) decision.getTarget().getNodeRewritingAssumption())); - return new InlineInfo(callInlinedMethod, false, false); + return new InlineInfo(callInlinedMethod, false); } } } - return new InlineInfo(original, false, false); + return new InlineInfo(original, false); } @Override @@ -277,10 +277,10 @@ * We want to inline invokes that have a constant MethodHandle parameter to remove * invokedynamic related calls as early as possible. */ - return new InlineInfo(original, false, false); + return new InlineInfo(original, false); } if (inlineDuringParsing && original.hasBytecodes() && original.getCode().length < TrivialInliningSize.getValue() && builder.getDepth() < InlineDuringParsingMaxDepth.getValue()) { - return new InlineInfo(original, false, false); + return new InlineInfo(original, false); } return null; } diff -r d24e4f349cbc -r 25bd9e2320de 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 May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerOptions.java Tue May 12 20:56:04 2015 +0200 @@ -170,5 +170,8 @@ @Option(help = "Print additional more verbose Truffle compilation statistics at the end of a run.", type = OptionType.Debug) public static final OptionValue TruffleCompilationStatisticDetails = new OptionValue<>(false); + + @Option(help = "Enable support for simple infopoints in truffle partial evaluations.", type = OptionType.Expert) + public static final OptionValue TruffleEnableInfopoints = new OptionValue<>(false); // @formatter:on } diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/nodes/asserts/NeverPartOfCompilationNode.java --- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/nodes/asserts/NeverPartOfCompilationNode.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/nodes/asserts/NeverPartOfCompilationNode.java Tue May 12 20:56:04 2015 +0200 @@ -34,8 +34,8 @@ public static final NodeClass TYPE = NodeClass.create(NeverPartOfCompilationNode.class); protected final String message; - public NeverPartOfCompilationNode(String message, FrameState stateAfter) { - super(TYPE, StampFactory.forVoid(), stateAfter); + public NeverPartOfCompilationNode(String message) { + super(TYPE, StampFactory.forVoid()); this.message = message; } diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/substitutions/TruffleGraphBuilderPlugins.java --- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/substitutions/TruffleGraphBuilderPlugins.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/substitutions/TruffleGraphBuilderPlugins.java Tue May 12 20:56:04 2015 +0200 @@ -239,7 +239,7 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode message) { if (message.isConstant()) { String messageString = message.asConstant().toValueString(); - b.add(new NeverPartOfCompilationNode(messageString, b.createStateAfter())); + b.add(new NeverPartOfCompilationNode(messageString)); return true; } else if (canDelayIntrinsification) { return false; diff -r d24e4f349cbc -r 25bd9e2320de 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 May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/EffectsClosure.java Tue May 12 20:56:04 2015 +0200 @@ -182,7 +182,7 @@ /** * Collects the effects of virtualizing the given node. - * + * * @return {@code true} if the effects include removing the node, {@code false} otherwise. */ protected abstract boolean processNode(Node node, BlockT state, GraphEffectList effects, FixedWithNextNode lastFixedNode); @@ -210,6 +210,7 @@ BlockT loopEntryState = initialState; BlockT lastMergedState = cloneState(initialState); + processInitialLoopState(loop, lastMergedState); MergeProcessor mergeProcessor = createMergeProcessor(loop.getHeader()); for (int iteration = 0; iteration < 10; iteration++) { LoopInfo info = ReentrantBlockIterator.processLoop(this, loop, cloneState(lastMergedState)); @@ -245,6 +246,11 @@ throw new GraalInternalError("too many iterations at %s", loop); } + @SuppressWarnings("unused") + protected void processInitialLoopState(Loop loop, BlockT initialState) { + // nothing to do + } + private void doMergeWithoutDead(MergeProcessor mergeProcessor, List states) { int alive = 0; for (BlockT state : states) { diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PEReadEliminationBlockState.java --- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PEReadEliminationBlockState.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PEReadEliminationBlockState.java Tue May 12 20:56:04 2015 +0200 @@ -115,7 +115,7 @@ ValueNode cacheObject; ObjectState obj = closure.getObjectState(this, object); if (obj != null) { - assert !obj.isVirtual(); + assert !obj.isVirtual() : object; cacheObject = obj.getMaterializedValue(); } else { cacheObject = object; diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PEReadEliminationClosure.java --- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PEReadEliminationClosure.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PEReadEliminationClosure.java Tue May 12 20:56:04 2015 +0200 @@ -27,6 +27,7 @@ import java.util.*; import com.oracle.graal.api.meta.*; +import com.oracle.graal.compiler.common.cfg.*; import com.oracle.graal.graph.*; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.cfg.*; @@ -212,6 +213,23 @@ } @Override + protected void processInitialLoopState(Loop loop, PEReadEliminationBlockState initialState) { + super.processInitialLoopState(loop, initialState); + + for (PhiNode phi : ((LoopBeginNode) loop.getHeader().getBeginNode()).phis()) { + ValueNode firstValue = phi.valueAt(0); + if (firstValue != null) { + firstValue = GraphUtil.unproxify(firstValue); + for (Map.Entry entry : new ArrayList<>(initialState.getReadCache().entrySet())) { + if (entry.getKey().object == firstValue) { + initialState.addReadCache(phi, entry.getKey().identity, entry.getKey().index, entry.getValue(), this); + } + } + } + } + } + + @Override protected void processLoopExit(LoopExitNode exitNode, PEReadEliminationBlockState initialState, PEReadEliminationBlockState exitState, GraphEffectList effects) { super.processLoopExit(exitNode, initialState, exitState, effects); diff -r d24e4f349cbc -r 25bd9e2320de 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 May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeClosure.java Tue May 12 20:56:04 2015 +0200 @@ -26,6 +26,7 @@ import com.oracle.graal.api.meta.*; import com.oracle.graal.compiler.common.*; +import com.oracle.graal.compiler.common.cfg.*; import com.oracle.graal.compiler.common.type.*; import com.oracle.graal.compiler.common.util.*; import com.oracle.graal.debug.*; @@ -359,6 +360,20 @@ } @Override + protected void processInitialLoopState(Loop loop, BlockT initialState) { + super.processInitialLoopState(loop, initialState); + + for (PhiNode phi : ((LoopBeginNode) loop.getHeader().getBeginNode()).phis()) { + if (phi.valueAt(0) != null) { + ObjectState state = getObjectState(initialState, phi.valueAt(0)); + if (state != null && state.isVirtual()) { + addAndMarkAlias(state.virtual, phi); + } + } + } + } + + @Override protected void processLoopExit(LoopExitNode exitNode, BlockT initialState, BlockT exitState, GraphEffectList effects) { if (exitNode.graph().hasValueProxies()) { Map proxies = Node.newMap(); diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java Tue May 12 20:56:04 2015 +0200 @@ -420,7 +420,7 @@ @NodeInfo(cost = NodeCost.NONE) private final class ToolEvalNodeInstrumentNode extends AbstractInstrumentNode { - @Child ToolEvalNode toolEvalNode; + @Child private ToolEvalNode toolEvalNode; private ToolEvalNodeInstrumentNode(AbstractInstrumentNode nextNode) { super(nextNode); diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java Tue May 12 20:56:04 2015 +0200 @@ -35,32 +35,46 @@ * Representation of a guest language source code unit and its contents. Sources originate in * several ways: *
    - *
  • Literal: A named text string. These are not indexed and should be considered - * value objects; equality is defined based on contents.
    + *
  • Literal: An anonymous text string: not named and not indexed. These should + * be considered value objects; equality is defined based on contents.
    * See {@link Source#fromText(CharSequence, String)}
  • *

    + *

  • Named Literal: A text string that can be retrieved by name as if it were a + * file, but without any assumption that the name is related to a file path. Creating a new literal + * with an already existing name will replace its predecessor in the index.
    + * See {@link Source#fromNamedText(CharSequence, String)}
    + * See {@link Source#find(String)}
  • + *

    *

  • File: Each file is represented as a canonical object, indexed by the * absolute, canonical path name of the file. File contents are read lazily and contents * optionally cached.
    * See {@link Source#fromFileName(String)}
    - * See {@link Source#fromFileName(String, boolean)}
  • + * See {@link Source#fromFileName(String, boolean)}
    + * See {@link Source#find(String)} *

    *

  • URL: Each URL source is represented as a canonical object, indexed by the * URL. Contents are read eagerly and cached.
    - * See {@link Source#fromURL(URL, String)}
  • + * See {@link Source#fromURL(URL, String)}
    + * See {@link Source#find(String)} *

    - *

  • Reader: Contents are read eagerly and treated as a Literal - * .
    + *
  • Reader: Contents are read eagerly and treated as an anonymous + * (non-indexed) Literal .
    * See {@link Source#fromReader(Reader, String)}
  • *

    - *

  • Pseudo File: A literal text string that can be retrieved by name as if it - * were a file, unlike literal sources; useful for testing.
    - * See {@link Source#asPseudoFile(CharSequence, String)}
  • + *
  • Sub-Source: A representation of the contents of a sub-range of another + * {@link Source}.
    + * See @link {@link Source#subSource(Source, int, int)}
    + * See @link {@link Source#subSource(Source, int)}
  • + *

    + *

  • AppendableSource: Literal contents are provided by the client, + * incrementally, after the instance is created.
    + * See {@link Source#fromAppendableText(String)}
    + * See {@link Source#fromNamedAppendableText(String)}
  • *
*

* File cache: *

    - *
  1. File content caching is optional, off by default.
  2. + *
  3. File content caching is optional, on by default.
  4. *
  5. The first access to source file contents will result in the contents being read, and (if * enabled) cached.
  6. *
  7. If file contents have been cached, access to contents via {@link Source#getInputStream()} or @@ -128,14 +142,24 @@ */ private static final List> allSources = Collections.synchronizedList(new ArrayList>()); - // Files and pseudo files are indexed. - private static final Map> filePathToSource = new HashMap<>(); + /** + * Index of all named sources. + */ + private static final Map> nameToSource = new HashMap<>(); private static boolean fileCacheEnabled = true; private static final List sourceListeners = new ArrayList<>(); /** + * Locates an existing instance by the name under which it was indexed. + */ + public static Source find(String name) { + final WeakReference nameRef = nameToSource.get(name); + return nameRef == null ? null : nameRef.get(); + } + + /** * Gets the canonical representation of a source file, whose contents will be read lazily and * then cached. * @@ -146,7 +170,7 @@ */ public static Source fromFileName(String fileName, boolean reset) throws IOException { - final WeakReference nameRef = filePathToSource.get(fileName); + final WeakReference nameRef = nameToSource.get(fileName); Source source = nameRef == null ? null : nameRef.get(); if (source == null) { final File file = new File(fileName); @@ -154,11 +178,11 @@ throw new IOException("Can't read file " + fileName); } final String path = file.getCanonicalPath(); - final WeakReference pathRef = filePathToSource.get(path); + final WeakReference pathRef = nameToSource.get(path); source = pathRef == null ? null : pathRef.get(); if (source == null) { source = new FileSource(file, fileName, path); - filePathToSource.put(path, new WeakReference<>(source)); + nameToSource.put(path, new WeakReference<>(source)); } } if (reset) { @@ -193,17 +217,17 @@ */ public static Source fromFileName(CharSequence chars, String fileName) throws IOException { - final WeakReference nameRef = filePathToSource.get(fileName); + final WeakReference nameRef = nameToSource.get(fileName); Source source = nameRef == null ? null : nameRef.get(); if (source == null) { final File file = new File(fileName); // We are going to trust that the fileName is readable. final String path = file.getCanonicalPath(); - final WeakReference pathRef = filePathToSource.get(path); + final WeakReference pathRef = nameToSource.get(path); source = pathRef == null ? null : pathRef.get(); if (source == null) { source = new FileSource(file, fileName, path, chars); - filePathToSource.put(path, new WeakReference<>(source)); + nameToSource.put(path, new WeakReference<>(source)); } } notifyNewSource(source).tagAs(Tags.FROM_FILE); @@ -211,8 +235,7 @@ } /** - * Creates a non-canonical source from literal text. If an already created literal source must - * be retrievable by name, use {@link #asPseudoFile(CharSequence, String)}. + * Creates an anonymous source from literal text: not named and not indexed. * * @param chars textual source code * @param description a note about the origin, for error messages and debugging @@ -226,6 +249,79 @@ } /** + * Creates an anonymous source from literal text that is provided incrementally after creation: + * not named and not indexed. + * + * @param description a note about the origin, for error messages and debugging + * @return a newly created, non-indexed, initially empty, appendable source representation + */ + public static Source fromAppendableText(String description) { + final Source source = new AppendableLiteralSource(description); + notifyNewSource(source).tagAs(Tags.FROM_LITERAL); + return source; + } + + /** + * Creates a source from literal text that can be retrieved by name, with no assumptions about + * the structure or meaning of the name. If the name is already in the index, the new instance + * will replace the previously existing instance in the index. + * + * @param chars textual source code + * @param name string to use for indexing/lookup + * @return a newly created, source representation + */ + public static Source fromNamedText(CharSequence chars, String name) { + final Source source = new LiteralSource(name, chars.toString()); + nameToSource.put(name, new WeakReference<>(source)); + notifyNewSource(source).tagAs(Tags.FROM_LITERAL); + return source; + } + + /** + * Creates a source from literal text that is provided incrementally after creation and which + * can be retrieved by name, with no assumptions about the structure or meaning of the name. If + * the name is already in the index, the new instance will replace the previously existing + * instance in the index. + * + * @param name string to use for indexing/lookup + * @return a newly created, indexed, initially empty, appendable source representation + */ + public static Source fromNamedAppendableText(String name) { + final Source source = new AppendableLiteralSource(name); + nameToSource.put(name, new WeakReference<>(source)); + notifyNewSource(source).tagAs(Tags.FROM_LITERAL); + return source; + } + + /** + * Creates a {@linkplain Source Source instance} that represents the contents of a sub-range of + * an existing {@link Source}. + * + * @param base an existing Source instance + * @param baseCharIndex 0-based index of the first character of the sub-range + * @param length the number of characters in the sub-range + * @return a new instance representing a sub-range of another Source + * @throws IllegalArgumentException if the specified sub-range is not contained in the base + */ + public static Source subSource(Source base, int baseCharIndex, int length) { + final SubSource subSource = SubSource.create(base, baseCharIndex, length); + return subSource; + } + + /** + * Creates a {@linkplain Source Source instance} that represents the contents of a sub-range at + * the end of an existing {@link Source}. + * + * @param base an existing Source instance + * @param baseCharIndex 0-based index of the first character of the sub-range + * @return a new instance representing a sub-range at the end of another Source + * @throws IllegalArgumentException if the index is out of range + */ + public static Source subSource(Source base, int baseCharIndex) { + return subSource(base, baseCharIndex, base.getLength() - baseCharIndex); + } + + /** * Creates a source whose contents will be read immediately from a URL and cached. * * @param url @@ -286,21 +382,6 @@ return source; } - /** - * Creates a source from literal text, but which acts as a file and can be retrieved by name - * (unlike other literal sources); intended for testing. - * - * @param chars textual source code - * @param pseudoFileName string to use for indexing/lookup - * @return a newly created, source representation, canonical with respect to its name - */ - public static Source asPseudoFile(CharSequence chars, String pseudoFileName) { - final Source source = new LiteralSource(pseudoFileName, chars.toString()); - filePathToSource.put(pseudoFileName, new WeakReference<>(source)); - notifyNewSource(source).tagAs(Tags.FROM_LITERAL); - return source; - } - // TODO (mlvdv) enable per-file choice whether to cache? /** * Enables/disables caching of file contents, disabled by default. Caching of sources @@ -372,7 +453,7 @@ private final ArrayList tags = new ArrayList<>(); - Source() { + private Source() { } private TextMap textMap = null; @@ -448,7 +529,7 @@ * Gets the number of characters in the source. */ public final int getLength() { - return checkTextMap().length(); + return getTextMap().length(); } /** @@ -467,9 +548,8 @@ * Gets the text (not including a possible terminating newline) in a (1-based) numbered line. */ public final String getCode(int lineNumber) { - checkTextMap(); - final int offset = textMap.lineStartOffset(lineNumber); - final int length = textMap.lineLength(lineNumber); + final int offset = getTextMap().lineStartOffset(lineNumber); + final int length = getTextMap().lineLength(lineNumber); return getCode().substring(offset, offset + length); } @@ -478,7 +558,7 @@ * source without a terminating newline count as a line. */ public final int getLineCount() { - return checkTextMap().lineCount(); + return getTextMap().lineCount(); } /** @@ -488,7 +568,7 @@ * @throws IllegalArgumentException if the offset is outside the text contents */ public final int getLineNumber(int offset) throws IllegalArgumentException { - return checkTextMap().offsetToLine(offset); + return getTextMap().offsetToLine(offset); } /** @@ -497,7 +577,7 @@ * @throws IllegalArgumentException if the offset is outside the text contents */ public final int getColumnNumber(int offset) throws IllegalArgumentException { - return checkTextMap().offsetToCol(offset); + return getTextMap().offsetToCol(offset); } /** @@ -506,7 +586,7 @@ * @throws IllegalArgumentException if there is no such line in the text */ public final int getLineStartOffset(int lineNumber) throws IllegalArgumentException { - return checkTextMap().lineStartOffset(lineNumber); + return getTextMap().lineStartOffset(lineNumber); } /** @@ -516,7 +596,17 @@ * @throws IllegalArgumentException if there is no such line in the text */ public final int getLineLength(int lineNumber) throws IllegalArgumentException { - return checkTextMap().lineLength(lineNumber); + return getTextMap().lineLength(lineNumber); + } + + /** + * Append text to a Source explicitly created as Appendable. + * + * @param chars the text to append + * @throws UnsupportedOperationException by concrete subclasses that do not support appending + */ + public void appendCode(CharSequence chars) { + throw new UnsupportedOperationException(); } /** @@ -556,9 +646,8 @@ * @throws IllegalStateException if the source is one of the "null" instances */ public final SourceSection createSection(String identifier, int startLine, int startColumn, int length) { - checkTextMap(); - final int lineStartOffset = textMap.lineStartOffset(startLine); - if (startColumn > textMap.lineLength(startLine)) { + final int lineStartOffset = getTextMap().lineStartOffset(startLine); + if (startColumn > getTextMap().lineLength(startLine)) { throw new IllegalArgumentException("column out of range"); } final int startOffset = lineStartOffset + startColumn - 1; @@ -586,10 +675,8 @@ */ public final SourceSection createSection(String identifier, int charIndex, int length) throws IllegalArgumentException { checkRange(charIndex, length); - checkTextMap(); final int startLine = getLineNumber(charIndex); final int startColumn = charIndex - getLineStartOffset(startLine) + 1; - return new DefaultSourceSection(this, identifier, startLine, startColumn, charIndex, length); } @@ -610,9 +697,8 @@ * @throws IllegalStateException if the source is one of the "null" instances */ public final SourceSection createSection(String identifier, int lineNumber) { - checkTextMap(); - final int charIndex = textMap.lineStartOffset(lineNumber); - final int length = textMap.lineLength(lineNumber); + final int charIndex = getTextMap().lineStartOffset(lineNumber); + final int length = getTextMap().lineLength(lineNumber); return createSection(identifier, charIndex, length); } @@ -627,13 +713,25 @@ return new LineLocationImpl(this, lineNumber); } - private TextMap checkTextMap() { + /** + * An object suitable for using as a key into a hashtable that defines equivalence between + * different source types. + */ + protected Object getHashKey() { + return getName(); + } + + protected final TextMap getTextMap() { if (textMap == null) { textMap = createTextMap(); } return textMap; } + protected final void clearTextMap() { + textMap = null; + } + protected TextMap createTextMap() { final String code = getCode(); if (code == null) { @@ -644,22 +742,22 @@ private static final class LiteralSource extends Source { - private final String name; // Name used originally to describe the source + private final String description; private final String code; - public LiteralSource(String name, String code) { - this.name = name; + public LiteralSource(String description, String code) { + this.description = description; this.code = code; } @Override public String getName() { - return name; + return description; } @Override public String getShortName() { - return name; + return description; } @Override @@ -669,7 +767,7 @@ @Override public String getPath() { - return name; + return description; } @Override @@ -688,11 +786,7 @@ @Override public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + name.hashCode(); - result = prime * result + (code == null ? 0 : code.hashCode()); - return result; + return description.hashCode(); } @Override @@ -703,11 +797,69 @@ if (obj == null) { return false; } - if (!(obj instanceof LiteralSource)) { - return false; + if (obj instanceof LiteralSource) { + LiteralSource other = (LiteralSource) obj; + return description.equals(other.description); } - LiteralSource other = (LiteralSource) obj; - return name.equals(other.name) && code.equals(other.code); + return false; + } + } + + private static final class AppendableLiteralSource extends Source { + private String description; + final List codeList = new ArrayList<>(); + + public AppendableLiteralSource(String description) { + this.description = description; + } + + @Override + public String getName() { + return description; + } + + @Override + public String getShortName() { + return description; + } + + @Override + public String getCode() { + return getCodeFromIndex(0); + } + + @Override + public String getPath() { + return description; + } + + @Override + public URL getURL() { + return null; + } + + @Override + public Reader getReader() { + return new StringReader(getCode()); + } + + @Override + protected void reset() { + } + + private String getCodeFromIndex(int index) { + StringBuilder sb = new StringBuilder(); + for (int i = index; i < codeList.size(); i++) { + CharSequence s = codeList.get(i); + sb.append(s); + } + return sb.toString(); + } + + @Override + public void appendCode(CharSequence chars) { + codeList.add(chars); + clearTextMap(); } } @@ -745,6 +897,11 @@ } @Override + protected Object getHashKey() { + return path; + } + + @Override public String getCode() { if (fileCacheEnabled) { if (code == null || timeStamp != file.lastModified()) { @@ -807,7 +964,6 @@ protected void reset() { this.code = null; } - } private static final class URLSource extends Source { @@ -867,7 +1023,61 @@ @Override protected void reset() { } + } + private static final class SubSource extends Source { + private final Source base; + private final int baseIndex; + private final int subLength; + + private static SubSource create(Source base, int baseIndex, int length) { + if (baseIndex < 0 || length < 0 || baseIndex + length > base.getLength()) { + throw new IllegalArgumentException("text positions out of range"); + } + return new SubSource(base, baseIndex, length); + } + + private SubSource(Source base, int baseIndex, int length) { + this.base = base; + this.baseIndex = baseIndex; + this.subLength = length; + } + + @Override + protected void reset() { + assert false; + } + + @Override + public String getName() { + return base.getName(); + } + + @Override + public String getShortName() { + return base.getShortName(); + } + + @Override + public String getPath() { + return base.getPath(); + } + + @Override + public URL getURL() { + return null; + } + + @Override + public Reader getReader() { + assert false; + return null; + } + + @Override + public String getCode() { + return base.getCode(baseIndex, subLength); + } } private static final class BytesSource extends Source { @@ -1132,7 +1342,7 @@ final int prime = 31; int result = 1; result = prime * result + line; - result = prime * result + source.hashCode(); + result = prime * result + source.getHashKey().hashCode(); return result; } @@ -1151,7 +1361,7 @@ if (line != other.line) { return false; } - return source.equals(other.source); + return source.getHashKey().equals(other.source.getHashKey()); } } diff -r d24e4f349cbc -r 25bd9e2320de graal/com.oracle.truffle.api/src/com/oracle/truffle/api/utilities/ConditionProfile.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/utilities/ConditionProfile.java Tue May 12 20:55:48 2015 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/utilities/ConditionProfile.java Tue May 12 20:56:04 2015 +0200 @@ -52,6 +52,8 @@ * @see #createBinaryProfile() */ public abstract class ConditionProfile extends NodeCloneable { + ConditionProfile() { + } public abstract boolean profile(boolean value); diff -r d24e4f349cbc -r 25bd9e2320de mx/mx_graal.py --- a/mx/mx_graal.py Tue May 12 20:55:48 2015 +0200 +++ b/mx/mx_graal.py Tue May 12 20:56:04 2015 +0200 @@ -1606,7 +1606,6 @@ parser = ArgumentParser(prog='mx ctw') parser.add_argument('--ctwopts', action='store', help='space separated Graal options used for CTW compilations (default: --ctwopts="' + defaultCtwopts + '")', default=defaultCtwopts, metavar='') parser.add_argument('--jar', action='store', help='jar of classes to compiled instead of rt.jar', metavar='') - parser.add_argument('vmargs', nargs=REMAINDER, metavar='VM options...') args, vmargs = parser.parse_known_args(args) diff -r d24e4f349cbc -r 25bd9e2320de mxtool/mx.py --- a/mxtool/mx.py Tue May 12 20:55:48 2015 +0200 +++ b/mxtool/mx.py Tue May 12 20:56:04 2015 +0200 @@ -4157,8 +4157,8 @@ launchOut.open('launchConfiguration', {'type' : 'org.eclipse.ui.externaltools.ProgramBuilderLaunchConfigurationType'}) launchOut.element('booleanAttribute', {'key' : 'org.eclipse.debug.core.capture_output', 'value': consoleOn}) launchOut.open('mapAttribute', {'key' : 'org.eclipse.debug.core.environmentVariables'}) - launchOut.element('mapEntry', {'key' : 'JAVA_HOME', 'value' : _opts.java_home}) - launchOut.element('mapEntry', {'key' : 'EXTRA_JAVA_HOMES', 'value' : _opts.extra_java_homes}) + launchOut.element('mapEntry', {'key' : 'JAVA_HOME', 'value' : _default_java_home.jdk}) + launchOut.element('mapEntry', {'key' : 'EXTRA_JAVA_HOMES', 'value' : os.pathsep.join([extraJavaHome.jdk for extraJavaHome in _extra_java_homes])}) launchOut.close('mapAttribute') if refresh: