changeset 17109:1a92d77a851b

[SPARC] Implementing ArrayEqualsOp for sparc
author Stefan Anzinger <stefan.anzinger@oracle.com>
date Mon, 15 Sep 2014 19:22:02 -0700
parents 33d2fea5d40e
children fe935dbf9863
files graal/com.oracle.graal.asm.sparc/src/com/oracle/graal/asm/sparc/SPARCAssembler.java graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java graal/com.oracle.graal.lir.sparc/src/com/oracle/graal/lir/sparc/SPARCArrayEqualsOp.java graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ArraysSubstitutions.java graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/StringSubstitutions.java
diffstat 5 files changed, 388 insertions(+), 22 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.asm.sparc/src/com/oracle/graal/asm/sparc/SPARCAssembler.java	Fri Sep 12 15:39:45 2014 -0700
+++ b/graal/com.oracle.graal.asm.sparc/src/com/oracle/graal/asm/sparc/SPARCAssembler.java	Mon Sep 15 19:22:02 2014 -0700
@@ -85,6 +85,8 @@
                     return Fmt00a.read(masm, pos);
                 case Bp:
                     return Fmt00c.read(masm, pos);
+                case Bpr:
+                    return Fmt00d.read(masm, pos);
                 default:
                     throw GraalInternalError.shouldNotReachHere("Unknown op2 " + op2);
             }
@@ -481,16 +483,112 @@
         }
     }
 
-    public static class Fmt00d {
-
-        public Fmt00d(SPARCAssembler masm, int op, int a, int rcond, int op2, int d16hi, int predict, int rs1, int d16lo) {
-            assert predict == 0 || predict == 1;
-            assert rcond >= 0 && rcond < 0x8;
-            assert op == 0;
-            assert op2 >= 0 && op2 < 0x8;
-            assert rs1 >= 0 && rs1 < 0x20;
-
-            masm.emitInt(op << 30 | a << 29 | rcond << 25 | op2 << 22 | d16hi & 3 | predict << 18 | rs1 << 14 | (d16lo & 0x003fff));
+    // @formatter:off
+    /**
+     * Instruction format for Branch on Integer Register with Prediction.
+     *
+     * |00   |a |- |rcond | 011 |d16hi|p | rs1 |          d16lo           |
+     * |31 30|29|28|27  25|24 22|21 20|19|18 14|                         0|
+     */
+    // @formatter:on
+    public static class Fmt00d extends Fmt00 {
+
+        private static final int A_SHIFT = 29;
+        private static final int RCOND_SHIFT = 25;
+        private static final int D16HI_SHIFT = 20;
+        private static final int P_SHIFT = 19;
+        private static final int RS1_SHIFT = 14;
+        private static final int D16LO_SHIFT = 0;
+
+        // @formatter:off
+        private static final int A_MASK        = 0b0010_0000_0000_0000_0000_0000_0000_0000;
+        private static final int RCOND_MASK    = 0b0000_1110_0000_0000_0000_0000_0000_0000;
+        private static final int D16HI_MASK    = 0b0000_0000_0011_0000_0000_0000_0000_0000;
+        private static final int P_MASK        = 0b0000_0000_0000_1000_0000_0000_0000_0000;
+        private static final int RS1_MASK      = 0b0000_0000_0000_0111_1100_0000_0000_0000;
+        private static final int D16LO_MASK    = 0b0000_0000_0000_0000_0011_1111_1111_1111;
+        // @formatter:on
+
+        private int annul;
+        private int rCondition;
+        private int disp16;
+        private int predictTaken;
+        private int rs1;
+        private Label label;
+
+        public Fmt00d(int op2, int rCondition, int predictTaken, int annul, int d16, int rs1, Label label) {
+            super(op2);
+            this.annul = annul;
+            this.rCondition = rCondition;
+            this.disp16 = d16;
+            this.predictTaken = predictTaken;
+            this.rs1 = rs1;
+            this.label = label;
+        }
+
+        @Override
+        public void setImm(int imm) {
+            setDisp16(imm);
+        }
+
+        public void setDisp16(int disp16) {
+            this.disp16 = disp16 >> 2;
+        }
+
+        public void emit(SPARCAssembler masm) {
+            if (label != null) {
+                final int pos = label.isBound() ? label.position() : patchUnbound(masm, label);
+                final int disp = pos - masm.position();
+                setDisp16(disp);
+            }
+            verify();
+            masm.emitInt(getInstructionBits());
+        }
+
+        private static int patchUnbound(SPARCAssembler masm, Label label) {
+            label.addPatchAt(masm.position());
+            return 0;
+        }
+
+        @Override
+        protected int getInstructionBits() {
+            int d16Split = 0;
+            d16Split |= (disp16 & 0b1100_0000_0000_0000) << D16HI_SHIFT - 14;
+            d16Split |= (disp16 & 0b0011_1111_1111_1111) << D16LO_SHIFT;
+            return super.getInstructionBits() | annul << A_SHIFT | rCondition << RCOND_SHIFT | d16Split | predictTaken << P_SHIFT | rs1 << RS1_SHIFT;
+        }
+
+        public static Fmt00d read(SPARCAssembler masm, int pos) {
+            final int inst = masm.getInt(pos);
+
+            // Make sure it's the right instruction:
+            final int op = (inst & OP_MASK) >> OP_SHIFT;
+            assert op == Ops.BranchOp.getValue();
+
+            // Get the instruction fields:
+            final int a = (inst & A_MASK) >> A_SHIFT;
+            final int cond = (inst & RCOND_MASK) >> RCOND_SHIFT;
+            final int op2 = (inst & OP2_MASK) >> OP2_SHIFT;
+            final int p = (inst & P_MASK) >> P_SHIFT;
+            final int rs1 = (inst & RS1_MASK) >> RS1_SHIFT;
+            final int d16hi = (inst & D16HI_MASK) >> D16HI_SHIFT;
+            assert (d16hi & ~0b11) == 0;
+            final int d16lo = (inst & D16LO_MASK) >> D16LO_SHIFT;
+            assert (d16lo & ~((1 << 14) - 1)) == 0;
+            final int d16 = (short) (((d16hi << 14) | d16lo) << 2); // sign extend
+            Fmt00d fmt = new Fmt00d(op2, cond, p, a, d16, rs1, null);
+            fmt.verify();
+            return fmt;
+        }
+
+        @Override
+        public void verify() {
+            super.verify();
+            assert (annul & ~1) == 0 : annul;
+            assert (rCondition & ~0b111) == 0 : rCondition;
+            assert isSimm(disp16, 16) : disp16;
+            assert (predictTaken & ~1) == 0 : predictTaken;
+            assert (rs1 & ~((1 << 5) - 1)) == 0 : rs1;
         }
     }
 
@@ -1710,12 +1808,12 @@
     public enum RCondition {
         // @formatter:off
 
-        Rc_z(1, "rc_z"),
-        Rc_lez(2, "rc_lez"),
-        Rc_lz(3, "rc_lz"),
-        Rc_nz(5, "rc_nz"),
-        Rc_gz(6, "rc_gz"),
-        Rc_gez(7, "rc_gez"),
+        Rc_z(0b001, "rc_z"),
+        Rc_lez(0b010, "rc_lez"),
+        Rc_lz(0b011, "rc_lz"),
+        Rc_nz(0b101, "rc_nz"),
+        Rc_gz(0b110, "rc_gz"),
+        Rc_gez(0b111, "rc_gez"),
         Rc_last(Rc_gez.getValue(), "rc_last");
 
         // @formatter:on
@@ -2050,6 +2148,12 @@
         }
     }
 
+    public static class Bpr extends Fmt00d {
+        public Bpr(RCondition rcond, boolean annul, boolean predictTaken, Register rs1, Label label) {
+            super(Op2s.Bpr.getValue(), rcond.getValue(), predictTaken ? 1 : 0, annul ? 1 : 0, 0, rs1.encoding(), label);
+        }
+    }
+
     public static class Bpa extends Fmt00c {
 
         public Bpa(int simm19) {
@@ -2089,6 +2193,14 @@
             super(0, ConditionFlag.Equal, Op2s.Bp, cc, 1, simm19);
         }
 
+        public Bpe(CC cc, Label label, boolean predictTaken) {
+            super(0, ConditionFlag.Equal, Op2s.Bp, cc, predictTaken ? 1 : 0, label);
+        }
+
+        public Bpe(boolean annul, CC cc, Label label, boolean predictTaken) {
+            super(annul ? 1 : 0, ConditionFlag.Equal, Op2s.Bp, cc, predictTaken ? 1 : 0, label);
+        }
+
         public Bpe(CC cc, Label label) {
             super(0, ConditionFlag.Equal, Op2s.Bp, cc, 1, label);
         }
@@ -2136,6 +2248,10 @@
         public Bpl(CC cc, Label label) {
             super(0, ConditionFlag.Less, Op2s.Bp, cc, 1, label);
         }
+
+        public Bpl(CC cc, boolean annul, boolean predictTaken, Label label) {
+            super(annul ? 1 : 0, ConditionFlag.Less, Op2s.Bp, cc, predictTaken ? 1 : 0, label);
+        }
     }
 
     public static class Bple extends Fmt00c {
@@ -2147,6 +2263,10 @@
         public Bple(CC cc, Label label) {
             super(0, ConditionFlag.LessEqual, Op2s.Bp, cc, 1, label);
         }
+
+        public Bple(CC cc, boolean annul, boolean predictTaken, Label label) {
+            super(annul ? 1 : 0, ConditionFlag.LessEqual, Op2s.Bp, cc, predictTaken ? 1 : 0, label);
+        }
     }
 
     public static class Bpleu extends Fmt00c {
@@ -2180,6 +2300,10 @@
         public Bpne(CC cc, Label label) {
             super(0, ConditionFlag.NotZero, Op2s.Bp, cc, 1, label);
         }
+
+        public Bpne(CC cc, boolean annul, boolean predictTaken, Label label) {
+            super(annul ? 1 : 0, ConditionFlag.NotZero, Op2s.Bp, cc, predictTaken ? 1 : 0, label);
+        }
     }
 
     public static class Bpneg extends Fmt00c {
@@ -3615,6 +3739,13 @@
         }
     }
 
+    public static class Ldub extends Fmt11 {
+
+        public Ldub(SPARCAddress src, Register dst) {
+            super(Op3s.Ldub, src, dst);
+        }
+    }
+
     public static class Ldsw extends Fmt11 {
 
         public Ldsw(SPARCAddress src, Register dst) {
--- a/graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java	Fri Sep 12 15:39:45 2014 -0700
+++ b/graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java	Mon Sep 15 19:22:02 2014 -0700
@@ -44,7 +44,6 @@
 import com.oracle.graal.lir.sparc.SPARCArithmetic.BinaryRegReg;
 import com.oracle.graal.lir.sparc.SPARCArithmetic.MulHighOp;
 import com.oracle.graal.lir.sparc.SPARCArithmetic.RemOp;
-//import com.oracle.graal.lir.sparc.SPARCArithmetic.ShiftOp;
 import com.oracle.graal.lir.sparc.SPARCArithmetic.Unary2Op;
 import com.oracle.graal.lir.sparc.SPARCCompare.CompareOp;
 import com.oracle.graal.lir.sparc.SPARCControlFlow.BranchOp;
@@ -458,8 +457,9 @@
 
     @Override
     public Value emitArrayEquals(Kind kind, Value array1, Value array2, Value length) {
-        // TODO Auto-generated method stub
-        throw GraalInternalError.unimplemented();
+        Variable result = newVariable(LIRKind.value(Kind.Int));
+        append(new SPARCArrayEqualsOp(this, kind, result, load(array1), load(array2), asAllocatable(length)));
+        return result;
     }
 
     @Override
@@ -871,8 +871,13 @@
         append(new MoveFpGp(dst, src, tempSlot));
     }
 
+    private StackSlot tmp;
+
     private StackSlot getTempSlot(LIRKind kind) {
-        return getResult().getFrameMap().allocateSpillSlot(kind);
+        if (tmp == null) {
+            tmp = getResult().getFrameMap().allocateSpillSlot(kind);
+        }
+        return tmp;
     }
 
     protected SPARC getArchitecture() {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.lir.sparc/src/com/oracle/graal/lir/sparc/SPARCArrayEqualsOp.java	Mon Sep 15 19:22:02 2014 -0700
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 2013, 2014, 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.sparc;
+
+import static com.oracle.graal.api.code.ValueUtil.*;
+import static com.oracle.graal.compiler.common.UnsafeAccess.*;
+import static com.oracle.graal.lir.LIRInstruction.OperandFlag.*;
+import static com.oracle.graal.sparc.SPARC.*;
+import static com.oracle.graal.asm.sparc.SPARCAssembler.*;
+import static com.oracle.graal.asm.sparc.SPARCAssembler.CC.*;
+
+import java.lang.reflect.*;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.asm.*;
+import com.oracle.graal.asm.sparc.*;
+import com.oracle.graal.asm.sparc.SPARCMacroAssembler.Cmp;
+import com.oracle.graal.asm.sparc.SPARCMacroAssembler.Mov;
+import com.oracle.graal.asm.sparc.SPARCMacroAssembler.Nop;
+import com.oracle.graal.lir.*;
+import com.oracle.graal.lir.asm.*;
+import com.oracle.graal.lir.gen.*;
+
+/**
+ * Emits code which compares two arrays of the same length. If the CPU supports any vector
+ * instructions specialized code is emitted to leverage these instructions.
+ */
+@Opcode("ARRAY_EQUALS")
+public class SPARCArrayEqualsOp extends SPARCLIRInstruction {
+
+    private final Kind kind;
+    private final int arrayBaseOffset;
+    private final int arrayIndexScale;
+
+    @Def({REG}) protected Value resultValue;
+    @Alive({REG}) protected Value array1Value;
+    @Alive({REG}) protected Value array2Value;
+    @Alive({REG}) protected Value lengthValue;
+    @Temp({REG}) protected Value temp1;
+    @Temp({REG}) protected Value temp2;
+    @Temp({REG}) protected Value temp3;
+    @Temp({REG}) protected Value temp4;
+    @Temp({REG}) protected Value temp5;
+
+    public SPARCArrayEqualsOp(LIRGeneratorTool tool, Kind kind, Value result, Value array1, Value array2, Value length) {
+        this.kind = kind;
+
+        Class<?> arrayClass = Array.newInstance(kind.toJavaClass(), 0).getClass();
+        this.arrayBaseOffset = unsafe.arrayBaseOffset(arrayClass);
+        this.arrayIndexScale = unsafe.arrayIndexScale(arrayClass);
+
+        this.resultValue = result;
+        this.array1Value = array1;
+        this.array2Value = array2;
+        this.lengthValue = length;
+
+        // Allocate some temporaries.
+        this.temp1 = tool.newVariable(LIRKind.derivedReference(tool.target().wordKind));
+        this.temp2 = tool.newVariable(LIRKind.derivedReference(tool.target().wordKind));
+        this.temp3 = tool.newVariable(LIRKind.value(tool.target().wordKind));
+        this.temp4 = tool.newVariable(LIRKind.value(tool.target().wordKind));
+        this.temp5 = tool.newVariable(LIRKind.value(tool.target().wordKind));
+    }
+
+    @Override
+    public void emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm) {
+        Register result = asRegister(resultValue);
+        Register array1 = asRegister(temp1);
+        Register array2 = asRegister(temp2);
+        Register length = asRegister(temp3);
+
+        Label trueLabel = new Label();
+        Label falseLabel = new Label();
+        Label done = new Label();
+
+        // Load array base addresses.
+        new Add(asObjectReg(array1Value), arrayBaseOffset, array1).emit(masm);
+        new Add(asObjectReg(array2Value), arrayBaseOffset, array2).emit(masm);
+
+        // Get array length in bytes.
+        new Mulx(asIntReg(lengthValue), arrayIndexScale, length).emit(masm);
+        new Mov(length, result).emit(masm); // copy
+
+        emit8ByteCompare(masm, result, array1, array2, length, trueLabel, falseLabel);
+        emitTailCompares(masm, result, array1, array2, trueLabel, falseLabel);
+
+        // Return true
+        masm.bind(trueLabel);
+        new Mov(1, result).emit(masm);
+        new Bpa(done).emit(masm);
+        new Nop().emit(masm);
+
+        // Return false
+        masm.bind(falseLabel);
+        new Mov(g0, result).emit(masm);
+
+        // That's it
+        masm.bind(done);
+    }
+
+    /**
+     * Vector size used in {@link #emit8ByteCompare}.
+     */
+    private static final int VECTOR_SIZE = 8;
+
+    /**
+     * Emits code that uses 8-byte vector compares.
+     */
+    private void emit8ByteCompare(SPARCMacroAssembler masm, Register result, Register array1, Register array2, Register length, Label trueLabel, Label falseLabel) {
+        Label loop = new Label();
+        Label compareTail = new Label();
+
+        Register tempReg1 = asRegister(temp4);
+        Register tempReg2 = asRegister(temp5);
+        new And(result, VECTOR_SIZE - 1, result).emit(masm); // tail count (in bytes)
+        new Andcc(length, ~(VECTOR_SIZE - 1), length).emit(masm);  // vector count (in bytes)
+        new Bpe(CC.Xcc, compareTail).emit(masm);
+        new Nop().emit(masm);
+
+        new Add(array1, length, array1).emit(masm);
+        new Add(array2, length, array2).emit(masm);
+        new Sub(g0, length, length).emit(masm);
+
+        // Align the main loop
+        new Ldx(new SPARCAddress(array1, length), tempReg1).emit(masm);
+        masm.bind(loop);
+        new Ldx(new SPARCAddress(array2, length), tempReg2).emit(masm);
+
+        new Cmp(tempReg1, tempReg2).emit(masm);
+        new Bpne(Xcc, false, false, falseLabel).emit(masm);
+        // Delay slot, not annul, add for next iteration
+        new Add(length, VECTOR_SIZE, length).emit(masm);
+
+        new Bpr(RCondition.Rc_nz, true, true, length, loop).emit(masm);
+        new Ldx(new SPARCAddress(array1, length), tempReg1).emit(masm); // Load in delay slot
+
+        // Tail count zero, therefore we can go to the end
+        new Bpr(RCondition.Rc_z, true, true, result, trueLabel).emit(masm);
+        new Nop().emit(masm);
+
+        masm.bind(compareTail);
+    }
+
+    /**
+     * Emits code to compare the remaining 1 to 4 bytes.
+     */
+    private void emitTailCompares(SPARCMacroAssembler masm, Register result, Register array1, Register array2, Label trueLabel, Label falseLabel) {
+        Label compare2Bytes = new Label();
+        Label compare1Byte = new Label();
+
+        Register tempReg1 = asRegister(temp3);
+        Register tempReg2 = asRegister(temp4);
+
+        if (kind.getByteCount() <= 4) {
+            // Compare trailing 4 bytes, if any.
+            new Cmp(result, 4).emit(masm);
+            new Bpl(Xcc, false, false, compare2Bytes).emit(masm);
+            new Nop().emit(masm);
+            new Lduw(new SPARCAddress(array1, 0), tempReg1).emit(masm);
+            new Lduw(new SPARCAddress(array2, 0), tempReg2).emit(masm);
+            new Cmp(tempReg1, tempReg2).emit(masm);
+
+            new Bpne(Xcc, false, false, falseLabel).emit(masm);
+            new Nop().emit(masm);
+
+            if (kind.getByteCount() <= 2) {
+                // Move array pointers forward.
+                new Add(array1, 4, array1).emit(masm);
+                new Add(array2, 4, array2).emit(masm);
+                new Sub(result, 4, result).emit(masm);
+
+                // Compare trailing 2 bytes, if any.
+                masm.bind(compare2Bytes);
+
+                new Cmp(result, 2).emit(masm);
+                new Bpl(Xcc, false, true, compare1Byte).emit(masm);
+                new Nop().emit(masm);
+                new Lduh(new SPARCAddress(array1, 0), tempReg1).emit(masm);
+                new Lduh(new SPARCAddress(array2, 0), tempReg2).emit(masm);
+                new Cmp(tempReg1, tempReg2).emit(masm);
+                new Bpne(Xcc, false, true, falseLabel).emit(masm);
+                new Nop().emit(masm);
+
+                // The one-byte tail compare is only required for boolean and byte arrays.
+                if (kind.getByteCount() <= 1) {
+                    // Move array pointers forward before we compare the last trailing byte.
+                    new Add(array1, 2, array1).emit(masm);
+                    new Add(array2, 2, array2).emit(masm);
+                    new Sub(result, 2, result).emit(masm);
+
+                    // Compare trailing byte, if any.
+                    masm.bind(compare1Byte);
+                    new Cmp(result, 1).emit(masm);
+                    new Bpne(Xcc, trueLabel).emit(masm);
+                    new Nop().emit(masm);
+                    new Ldub(new SPARCAddress(array1, 0), tempReg1).emit(masm);
+                    new Ldub(new SPARCAddress(array2, 0), tempReg2).emit(masm);
+                    new Cmp(tempReg1, tempReg2).emit(masm);
+                    new Bpne(Xcc, falseLabel).emit(masm);
+                    new Nop().emit(masm);
+                } else {
+                    masm.bind(compare1Byte);
+                }
+            } else {
+                masm.bind(compare2Bytes);
+            }
+        }
+    }
+}
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ArraysSubstitutions.java	Fri Sep 12 15:39:45 2014 -0700
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ArraysSubstitutions.java	Mon Sep 15 19:22:02 2014 -0700
@@ -28,7 +28,7 @@
 /**
  * Substitutions for {@link java.util.Arrays} methods.
  */
-@ClassSubstitution(value = java.util.Arrays.class, defaultGuard = UnsafeSubstitutions.GetAndSetGuard.class)
+@ClassSubstitution(value = java.util.Arrays.class)
 public class ArraysSubstitutions {
 
     @MethodSubstitution
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/StringSubstitutions.java	Fri Sep 12 15:39:45 2014 -0700
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/StringSubstitutions.java	Mon Sep 15 19:22:02 2014 -0700
@@ -35,7 +35,7 @@
 /**
  * Substitutions for {@link java.lang.String} methods.
  */
-@ClassSubstitution(value = java.lang.String.class, defaultGuard = UnsafeSubstitutions.GetAndSetGuard.class)
+@ClassSubstitution(value = java.lang.String.class)
 public class StringSubstitutions {
 
     /**