# HG changeset patch # User Doug Simon # Date 1383760199 -3600 # Node ID d59a65c11febfe0ed6bd2802285c00fff8038c4e # Parent 8577cb721f5133a59305644cdb4c346b540064f6 HSAIL support for difference between compression of oops and class pointers Contributed-by: Tom Deneau diff -r 8577cb721f51 -r d59a65c11feb graal/com.oracle.graal.asm.hsail/src/com/oracle/graal/asm/hsail/HSAILAssembler.java --- a/graal/com.oracle.graal.asm.hsail/src/com/oracle/graal/asm/hsail/HSAILAssembler.java Wed Nov 06 18:43:45 2013 +0100 +++ b/graal/com.oracle.graal.asm.hsail/src/com/oracle/graal/asm/hsail/HSAILAssembler.java Wed Nov 06 18:49:59 2013 +0100 @@ -355,39 +355,75 @@ } /** - * Emit code to build a 64-bit pointer from a compressed-oop and the associated base and shift. - * We only emit this if base and shift are not both zero. + * Emits code to build a 64-bit pointer from a compressed value and the associated base and + * shift. The compressed value could represent either a normal oop or a klass ptr. If the + * compressed value is 0, the uncompressed must also be 0. We only emit this if base and shift + * are not both zero. + * + * @param result the register containing the compressed value on input and the uncompressed ptr + * on output + * @param base the amount to be added to the compressed value + * @param shift the number of bits to shift left the compressed value + * @param testForNull true if the compressed value might be null */ - public void emitCompressedOopDecode(Value result, long narrowOopBase, int narrowOopShift) { - if (narrowOopBase == 0) { - emit("shl", result, result, Constant.forInt(narrowOopShift)); - } else if (narrowOopShift == 0) { - // only use add if result is not starting as null (unsigned compare) - emitCompare(result, Constant.forLong(0), "eq", false, true); - emit("add", result, result, Constant.forLong(narrowOopBase)); - emitConditionalMove(result, Constant.forLong(0), result, 64); + public void emitCompressedOopDecode(Value result, long base, int shift, boolean testForNull) { + assert (base != 0 || shift != 0); + assert (!isConstant(result)); + if (base == 0) { + // we don't have to test for null if shl is the only operation + emitForceUnsigned("shl", result, result, Constant.forInt(shift)); + } else if (shift == 0) { + // only use add if result is not starting as null (test only if testForNull is true) + emitWithOptionalTestForNull(testForNull, "add", result, result, Constant.forLong(base)); } else { - // only use mad if result is not starting as null (unsigned compare) - emitCompare(result, Constant.forLong(0), "eq", false, true); - emitTextFormattedInstruction("mad_u64 ", result, result, Constant.forInt(1 << narrowOopShift), Constant.forLong(narrowOopBase)); - emitConditionalMove(result, Constant.forLong(0), result, 64); + // only use mad if result is not starting as null (test only if testForNull is true) + emitWithOptionalTestForNull(testForNull, "mad", result, result, Constant.forInt(1 << shift), Constant.forLong(base)); } } /** - * Emit code to build a 32-bit compressed pointer from a full 64-bit pointer using the - * associated base and shift. We only emit this if base and shift are not both zero. + * Emits code to build a compressed value from a full 64-bit pointer using the associated base + * and shift. The compressed value could represent either a normal oop or a klass ptr. If the + * ptr is 0, the compressed value must also be 0. We only emit this if base and shift are not + * both zero. + * + * @param result the register containing the 64-bit pointer on input and the compressed value on + * output + * @param base the amount to be subtracted from the 64-bit pointer + * @param shift the number of bits to shift right the 64-bit pointer + * @param testForNull true if the 64-bit pointer might be null */ - public void emitCompressedOopEncode(Value result, long narrowOopBase, int narrowOopShift) { - if (narrowOopBase != 0) { - // only use sub if result is not starting as null (unsigned compare) + public void emitCompressedOopEncode(Value result, long base, int shift, boolean testForNull) { + assert (base != 0 || shift != 0); + assert (!isConstant(result)); + if (base != 0) { + // only use sub if result is not starting as null (test only if testForNull is true) + emitWithOptionalTestForNull(testForNull, "sub", result, result, Constant.forLong(base)); + } + if (shift != 0) { + // note that the shr can still be done even if the result is null + emitForceUnsigned("shr", result, result, Constant.forInt(shift)); + } + } + + /** + * Emits code for the requested mnemonic on the result and sources. In addition, if testForNull + * is true, surrounds the instruction with code that will guarantee that if the result starts as + * 0, it will remain 0. + * + * @param testForNull true if we want to add the code to check for and preserve null + * @param mnemonic the instruction to be applied (without size prefix) + * @param result the register which is both an input and the final output + * @param sources the sources for the mnemonic instruction + */ + private void emitWithOptionalTestForNull(boolean testForNull, String mnemonic, Value result, Value... sources) { + if (testForNull) { emitCompare(result, Constant.forLong(0), "eq", false, true); - emit("sub", result, result, Constant.forLong(narrowOopBase)); + } + emitForceUnsigned(mnemonic, result, sources); + if (testForNull) { emitConditionalMove(result, Constant.forLong(0), result, 64); } - if (narrowOopShift != 0) { - emit("shr", result, result, Constant.forInt(narrowOopShift)); - } } /** diff -r 8577cb721f51 -r d59a65c11feb graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/InstanceOfTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/InstanceOfTest.java Wed Nov 06 18:49:59 2013 +0100 @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.compiler.hsail.test; + +import com.oracle.graal.compiler.hsail.test.infra.GraalKernelTester; +import org.junit.Test; + +/** + * Tests instanceof operator. Requires correct support for decompression of klass ptrs. + */ +public class InstanceOfTest extends GraalKernelTester { + + static final int NUM = 20; + + abstract static class Shape { + + public abstract float getArea(); + } + + static class Circle extends Shape { + + private float radius; + + Circle(float r) { + radius = r; + } + + @Override + public float getArea() { + return (float) (Math.PI * radius * radius); + } + } + + static class Square extends Shape { + + private float len; + + Square(float len) { + this.len = len; + } + + @Override + public float getArea() { + return len * len; + } + } + + @Result public float[] outArray = new float[NUM]; + public Shape[] inShapeArray = new Shape[NUM]; + + void setupArrays() { + for (int i = 0; i < NUM; i++) { + if (i % 2 == 0) { + inShapeArray[i] = new Circle(i + 1); + } else { + inShapeArray[i] = new Square(i + 1); + } + outArray[i] = -i; + } + } + + public void run(int gid) { + outArray[gid] = (inShapeArray[gid] instanceof Circle ? 1.0f : 2.0f); + } + + @Override + public void runTest() { + setupArrays(); + + dispatchMethodKernel(NUM); + } + + @Test + public void test() { + testGeneratedHsail(); + } +} diff -r 8577cb721f51 -r d59a65c11feb graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotLIRGenerator.java --- a/graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotLIRGenerator.java Wed Nov 06 18:43:45 2013 +0100 +++ b/graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotLIRGenerator.java Wed Nov 06 18:49:59 2013 +0100 @@ -46,6 +46,30 @@ this.config = config; } + private int getLogMinObjectAlignment() { + return config.logMinObjAlignment(); + } + + private int getNarrowOopShift() { + return config.narrowOopShift; + } + + private long getNarrowOopBase() { + return config.narrowOopBase; + } + + private int getLogKlassAlignment() { + return config.logKlassAlignment; + } + + private int getNarrowKlassShift() { + return config.narrowKlassShift; + } + + private long getNarrowKlassBase() { + return config.narrowKlassBase; + } + private static boolean isCompressCandidate(DeoptimizingNode access) { return access != null && ((HeapAccess) access).isCompressible(); } @@ -56,9 +80,12 @@ Variable result = newVariable(kind); LIRFrameState state = access != null ? state(access) : null; assert access == null || access instanceof HeapAccess; - if (config.useCompressedOops && isCompressCandidate(access)) { + if (isCompressCandidate(access) && config.useCompressedOops && kind == Kind.Object) { Variable scratch = newVariable(Kind.Long); - append(new LoadCompressedPointer(kind, result, scratch, loadAddress, state, config.narrowOopBase, config.narrowOopShift, config.logMinObjAlignment())); + append(new LoadCompressedPointer(kind, result, scratch, loadAddress, state, getNarrowOopBase(), getNarrowOopShift(), getLogMinObjectAlignment())); + } else if (isCompressCandidate(access) && config.useCompressedClassPointers && kind == Kind.Long) { + Variable scratch = newVariable(Kind.Long); + append(new LoadCompressedPointer(kind, result, scratch, loadAddress, state, getNarrowKlassBase(), getNarrowKlassShift(), getLogKlassAlignment())); } else { append(new LoadOp(kind, result, loadAddress, state)); } @@ -70,9 +97,12 @@ HSAILAddressValue storeAddress = asAddressValue(address); LIRFrameState state = access != null ? state(access) : null; Variable input = load(inputVal); - if (config.useCompressedOops && isCompressCandidate(access)) { + if (isCompressCandidate(access) && config.useCompressedOops && kind == Kind.Object) { Variable scratch = newVariable(Kind.Long); - append(new StoreCompressedPointer(kind, storeAddress, input, scratch, state, config.narrowOopBase, config.narrowOopShift, config.logMinObjAlignment())); + append(new StoreCompressedPointer(kind, storeAddress, input, scratch, state, getNarrowOopBase(), getNarrowOopShift(), getLogMinObjectAlignment())); + } else if (isCompressCandidate(access) && config.useCompressedClassPointers && kind == Kind.Long) { + Variable scratch = newVariable(Kind.Long); + append(new StoreCompressedPointer(kind, storeAddress, input, scratch, state, getNarrowKlassBase(), getNarrowKlassShift(), getLogKlassAlignment())); } else { append(new StoreOp(kind, storeAddress, input, state)); } diff -r 8577cb721f51 -r d59a65c11feb graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILMove.java --- a/graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILMove.java Wed Nov 06 18:43:45 2013 +0100 +++ b/graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILMove.java Wed Nov 06 18:49:59 2013 +0100 @@ -188,57 +188,57 @@ public static class LoadCompressedPointer extends LoadOp { - private long narrowOopBase; - private int narrowOopShift; - private int logMinObjAlignment; + private final long base; + private final int shift; + private final int alignment; @Temp({REG}) private AllocatableValue scratch; - public LoadCompressedPointer(Kind kind, AllocatableValue result, AllocatableValue scratch, HSAILAddressValue address, LIRFrameState state, long narrowOopBase, int narrowOopShift, - int logMinObjAlignment) { + public LoadCompressedPointer(Kind kind, AllocatableValue result, AllocatableValue scratch, HSAILAddressValue address, LIRFrameState state, long base, int shift, int alignment) { super(kind, result, address, state); - this.narrowOopBase = narrowOopBase; - this.narrowOopShift = narrowOopShift; - this.logMinObjAlignment = logMinObjAlignment; + this.base = base; + this.shift = shift; + this.alignment = alignment; this.scratch = scratch; - assert kind == Kind.Object; + assert kind == Kind.Object || kind == Kind.Long; } @Override public void emitMemAccess(HSAILAssembler masm) { // we will do a 32 bit load, zero extending into a 64 bit register masm.emitLoad(result, address.toAddress(), "u32"); - decodePointer(masm, result, narrowOopBase, narrowOopShift, logMinObjAlignment); + boolean testForNull = (kind == Kind.Object); + decodePointer(masm, result, base, shift, alignment, testForNull); } } public static class StoreCompressedPointer extends HSAILLIRInstruction { protected final Kind kind; - private long narrowOopBase; - private int narrowOopShift; - private int logMinObjAlignment; + private final long base; + private final int shift; + private final int alignment; @Temp({REG}) private AllocatableValue scratch; @Alive({REG}) protected AllocatableValue input; @Alive({COMPOSITE}) protected HSAILAddressValue address; @State protected LIRFrameState state; - public StoreCompressedPointer(Kind kind, HSAILAddressValue address, AllocatableValue input, AllocatableValue scratch, LIRFrameState state, long narrowOopBase, int narrowOopShift, - int logMinObjAlignment) { - this.narrowOopBase = narrowOopBase; - this.narrowOopShift = narrowOopShift; - this.logMinObjAlignment = logMinObjAlignment; + public StoreCompressedPointer(Kind kind, HSAILAddressValue address, AllocatableValue input, AllocatableValue scratch, LIRFrameState state, long base, int shift, int alignment) { + this.base = base; + this.shift = shift; + this.alignment = alignment; this.scratch = scratch; this.kind = kind; this.address = address; this.state = state; this.input = input; - assert kind == Kind.Object; + assert kind == Kind.Object || kind == Kind.Long; } @Override public void emitCode(TargetMethodAssembler tasm, HSAILAssembler masm) { masm.emitMov(scratch, input); - encodePointer(masm, scratch, narrowOopBase, narrowOopShift, logMinObjAlignment); + boolean testForNull = (kind == Kind.Object); + encodePointer(masm, scratch, base, shift, alignment, testForNull); if (state != null) { throw new InternalError("NYI"); // tasm.recordImplicitException(masm.codeBuffer.position(), state); @@ -247,24 +247,24 @@ } } - private static void encodePointer(HSAILAssembler masm, Value scratch, long narrowOopBase, int narrowOopShift, int logMinObjAlignment) { - if (narrowOopBase == 0 && narrowOopShift == 0) { + private static void encodePointer(HSAILAssembler masm, Value scratch, long base, int shift, int alignment, boolean testForNull) { + if (base == 0 && shift == 0) { return; } - if (narrowOopShift != 0) { - assert logMinObjAlignment == narrowOopShift : "Encode algorithm is wrong"; + if (shift != 0) { + assert alignment == shift : "Encode algorithm is wrong"; } - masm.emitCompressedOopEncode(scratch, narrowOopBase, narrowOopShift); + masm.emitCompressedOopEncode(scratch, base, shift, testForNull); } - private static void decodePointer(HSAILAssembler masm, Value result, long narrowOopBase, int narrowOopShift, int logMinObjAlignment) { - if (narrowOopBase == 0 && narrowOopShift == 0) { + private static void decodePointer(HSAILAssembler masm, Value result, long base, int shift, int alignment, boolean testForNull) { + if (base == 0 && shift == 0) { return; } - if (narrowOopShift != 0) { - assert logMinObjAlignment == narrowOopShift : "Decode algorithm is wrong"; + if (shift != 0) { + assert alignment == shift : "Decode algorithm is wrong"; } - masm.emitCompressedOopDecode(result, narrowOopBase, narrowOopShift); + masm.emitCompressedOopDecode(result, base, shift, testForNull); } public static class LeaOp extends HSAILLIRInstruction {