1 /*
   2  * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package org.graalvm.compiler.lir.sparc;
  24 
  25 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.BPCC;
  26 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Annul.ANNUL;
  27 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Annul.NOT_ANNUL;
  28 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.BranchPredict.PREDICT_NOT_TAKEN;
  29 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.BranchPredict.PREDICT_TAKEN;
  30 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.CC.Xcc;
  31 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.ConditionFlag.Equal;
  32 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.ConditionFlag.Less;
  33 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.ConditionFlag.NotEqual;
  34 import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.REG;
  35 import static jdk.vm.ci.code.ValueUtil.asRegister;
  36 import static jdk.vm.ci.sparc.SPARC.g0;
  37 import static jdk.vm.ci.sparc.SPARCKind.WORD;
  38 
  39 import java.lang.reflect.Array;
  40 import java.lang.reflect.Field;
  41 
  42 import org.graalvm.compiler.asm.Label;
  43 import org.graalvm.compiler.asm.sparc.SPARCAddress;
  44 import org.graalvm.compiler.asm.sparc.SPARCMacroAssembler;
  45 import org.graalvm.compiler.core.common.LIRKind;
  46 import org.graalvm.compiler.lir.LIRInstructionClass;
  47 import org.graalvm.compiler.lir.Opcode;
  48 import org.graalvm.compiler.lir.asm.CompilationResultBuilder;
  49 import org.graalvm.compiler.lir.gen.LIRGeneratorTool;
  50 
  51 import jdk.vm.ci.code.Register;
  52 import jdk.vm.ci.meta.JavaKind;
  53 import jdk.vm.ci.meta.Value;
  54 import jdk.vm.ci.sparc.SPARCKind;
  55 import sun.misc.Unsafe;
  56 
  57 /**
  58  * Emits code which compares two arrays of the same length.
  59  */
  60 @Opcode("ARRAY_EQUALS")
  61 public final class SPARCArrayEqualsOp extends SPARCLIRInstruction {
  62     public static final LIRInstructionClass<SPARCArrayEqualsOp> TYPE = LIRInstructionClass.create(SPARCArrayEqualsOp.class);
  63     public static final SizeEstimate SIZE = SizeEstimate.create(32);
  64 
  65     private final JavaKind kind;
  66     private final int arrayBaseOffset;
  67     private final int arrayIndexScale;
  68 
  69     @Def({REG}) protected Value resultValue;
  70     @Alive({REG}) protected Value array1Value;
  71     @Alive({REG}) protected Value array2Value;
  72     @Alive({REG}) protected Value lengthValue;
  73     @Temp({REG}) protected Value temp1;
  74     @Temp({REG}) protected Value temp2;
  75     @Temp({REG}) protected Value temp3;
  76     @Temp({REG}) protected Value temp4;
  77     @Temp({REG}) protected Value temp5;
  78 
  79     public SPARCArrayEqualsOp(LIRGeneratorTool tool, JavaKind kind, Value result, Value array1, Value array2, Value length) {
  80         super(TYPE, SIZE);
  81         this.kind = kind;
  82 
  83         Class<?> arrayClass = Array.newInstance(kind.toJavaClass(), 0).getClass();
  84         this.arrayBaseOffset = UNSAFE.arrayBaseOffset(arrayClass);
  85         this.arrayIndexScale = UNSAFE.arrayIndexScale(arrayClass);
  86 
  87         this.resultValue = result;
  88         this.array1Value = array1;
  89         this.array2Value = array2;
  90         this.lengthValue = length;
  91 
  92         // Allocate some temporaries.
  93         this.temp1 = tool.newVariable(LIRKind.unknownReference(tool.target().arch.getWordKind()));
  94         this.temp2 = tool.newVariable(LIRKind.unknownReference(tool.target().arch.getWordKind()));
  95         this.temp3 = tool.newVariable(LIRKind.value(tool.target().arch.getWordKind()));
  96         this.temp4 = tool.newVariable(LIRKind.value(tool.target().arch.getWordKind()));
  97         this.temp5 = tool.newVariable(LIRKind.value(tool.target().arch.getWordKind()));
  98     }
  99 
 100     @Override
 101     public void emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm) {
 102         Register result = asRegister(resultValue);
 103         Register array1 = asRegister(temp1);
 104         Register array2 = asRegister(temp2);
 105         Register length = asRegister(temp3);
 106 
 107         Label trueLabel = new Label();
 108         Label falseLabel = new Label();
 109         Label done = new Label();
 110 
 111         // Load array base addresses.
 112         masm.add(asRegister(array1Value), arrayBaseOffset, array1);
 113         masm.add(asRegister(array2Value), arrayBaseOffset, array2);
 114 
 115         // Get array length in bytes.
 116         masm.mulx(asRegister(lengthValue, WORD), arrayIndexScale, length);
 117         masm.mov(length, result); // copy
 118 
 119         emit8ByteCompare(masm, result, array1, array2, length, trueLabel, falseLabel);
 120         emitTailCompares(masm, result, array1, array2, trueLabel, falseLabel);
 121 
 122         // Return true
 123         masm.bind(trueLabel);
 124         masm.mov(1, result);
 125         masm.jmp(done);
 126 
 127         // Return false
 128         masm.bind(falseLabel);
 129         masm.mov(g0, result);
 130 
 131         // That's it
 132         masm.bind(done);
 133     }
 134 
 135     /**
 136      * Vector size used in {@link #emit8ByteCompare}.
 137      */
 138     private static final int VECTOR_SIZE = 8;
 139 
 140     /**
 141      * Emits code that uses 8-byte vector compares.
 142      */
 143     private void emit8ByteCompare(SPARCMacroAssembler masm, Register result, Register array1, Register array2, Register length, Label trueLabel, Label falseLabel) {
 144         assert lengthValue.getPlatformKind().equals(SPARCKind.WORD);
 145         Label loop = new Label();
 146         Label compareTail = new Label();
 147         Label compareTailCorrectVectorEnd = new Label();
 148 
 149         Register tempReg1 = asRegister(temp4);
 150         Register tempReg2 = asRegister(temp5);
 151 
 152         masm.sra(length, 0, length);
 153         masm.and(result, VECTOR_SIZE - 1, result); // tail count (in bytes)
 154         masm.andcc(length, ~(VECTOR_SIZE - 1), length);  // vector count (in bytes)
 155         BPCC.emit(masm, Xcc, Equal, NOT_ANNUL, PREDICT_NOT_TAKEN, compareTail);
 156 
 157         masm.sub(length, VECTOR_SIZE, length); // Delay slot
 158         masm.add(array1, length, array1);
 159         masm.add(array2, length, array2);
 160         masm.sub(g0, length, length);
 161 
 162         // Compare the last element first
 163         masm.ldx(new SPARCAddress(array1, 0), tempReg1);
 164         masm.ldx(new SPARCAddress(array2, 0), tempReg2);
 165         masm.compareBranch(tempReg1, tempReg2, NotEqual, Xcc, falseLabel, PREDICT_NOT_TAKEN, null);
 166         masm.compareBranch(length, 0, Equal, Xcc, compareTailCorrectVectorEnd, PREDICT_NOT_TAKEN, null);
 167 
 168         // Load the first value from array 1 (Later done in back branch delay-slot)
 169         masm.ldx(new SPARCAddress(array1, length), tempReg1);
 170         masm.bind(loop);
 171         masm.ldx(new SPARCAddress(array2, length), tempReg2);
 172         masm.cmp(tempReg1, tempReg2);
 173 
 174         BPCC.emit(masm, Xcc, NotEqual, NOT_ANNUL, PREDICT_NOT_TAKEN, falseLabel);
 175         // Delay slot, not annul, add for next iteration
 176         masm.addcc(length, VECTOR_SIZE, length);
 177         // Annul, to prevent access past the array
 178         BPCC.emit(masm, Xcc, NotEqual, ANNUL, PREDICT_TAKEN, loop);
 179         masm.ldx(new SPARCAddress(array1, length), tempReg1); // Load in delay slot
 180 
 181         // Tail count zero, therefore we can go to the end
 182         masm.compareBranch(result, 0, Equal, Xcc, trueLabel, PREDICT_TAKEN, null);
 183 
 184         masm.bind(compareTailCorrectVectorEnd);
 185         // Correct the array pointers
 186         masm.add(array1, VECTOR_SIZE, array1);
 187         masm.add(array2, VECTOR_SIZE, array2);
 188 
 189         masm.bind(compareTail);
 190     }
 191 
 192     /**
 193      * Emits code to compare the remaining 1 to 4 bytes.
 194      */
 195     private void emitTailCompares(SPARCMacroAssembler masm, Register result, Register array1, Register array2, Label trueLabel, Label falseLabel) {
 196         Label compare2Bytes = new Label();
 197         Label compare1Byte = new Label();
 198 
 199         Register tempReg1 = asRegister(temp3);
 200         Register tempReg2 = asRegister(temp4);
 201 
 202         if (kind.getByteCount() <= 4) {
 203             // Compare trailing 4 bytes, if any.
 204             masm.compareBranch(result, 4, Less, Xcc, compare2Bytes, PREDICT_NOT_TAKEN, null);
 205 
 206             masm.lduw(new SPARCAddress(array1, 0), tempReg1);
 207             masm.lduw(new SPARCAddress(array2, 0), tempReg2);
 208             masm.compareBranch(tempReg1, tempReg2, NotEqual, Xcc, falseLabel, PREDICT_NOT_TAKEN, null);
 209 
 210             if (kind.getByteCount() <= 2) {
 211                 // Move array pointers forward.
 212                 masm.add(array1, 4, array1);
 213                 masm.add(array2, 4, array2);
 214                 masm.sub(result, 4, result);
 215 
 216                 // Compare trailing 2 bytes, if any.
 217                 masm.bind(compare2Bytes);
 218 
 219                 masm.compareBranch(result, 2, Less, Xcc, compare1Byte, PREDICT_TAKEN, null);
 220 
 221                 masm.lduh(new SPARCAddress(array1, 0), tempReg1);
 222                 masm.lduh(new SPARCAddress(array2, 0), tempReg2);
 223 
 224                 masm.compareBranch(tempReg1, tempReg2, NotEqual, Xcc, falseLabel, PREDICT_TAKEN, null);
 225 
 226                 // The one-byte tail compare is only required for boolean and byte arrays.
 227                 if (kind.getByteCount() <= 1) {
 228                     // Move array pointers forward before we compare the last trailing byte.
 229                     masm.add(array1, 2, array1);
 230                     masm.add(array2, 2, array2);
 231                     masm.sub(result, 2, result);
 232 
 233                     // Compare trailing byte, if any.
 234                     masm.bind(compare1Byte);
 235                     masm.compareBranch(result, 1, NotEqual, Xcc, trueLabel, PREDICT_TAKEN, null);
 236 
 237                     masm.ldub(new SPARCAddress(array1, 0), tempReg1);
 238                     masm.ldub(new SPARCAddress(array2, 0), tempReg2);
 239                     masm.compareBranch(tempReg1, tempReg2, NotEqual, Xcc, falseLabel, PREDICT_TAKEN, null);
 240                 } else {
 241                     masm.bind(compare1Byte);
 242                 }
 243             } else {
 244                 masm.bind(compare2Bytes);
 245             }
 246         }
 247     }
 248 
 249     private static final Unsafe UNSAFE = initUnsafe();
 250 
 251     private static Unsafe initUnsafe() {
 252         try {
 253             return Unsafe.getUnsafe();
 254         } catch (SecurityException se) {
 255             try {
 256                 Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
 257                 theUnsafe.setAccessible(true);
 258                 return (Unsafe) theUnsafe.get(Unsafe.class);
 259             } catch (Exception e) {
 260                 throw new RuntimeException("exception while trying to get Unsafe", e);
 261             }
 262         }
 263     }
 264 }