1 /*
   2  * Copyright (c) 2013, 2018, 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 
  24 
  25 package org.graalvm.compiler.lir.sparc;
  26 
  27 import static jdk.vm.ci.code.ValueUtil.asRegister;
  28 import static jdk.vm.ci.sparc.SPARC.g0;
  29 import static jdk.vm.ci.sparc.SPARCKind.WORD;
  30 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.BPCC;
  31 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Annul.ANNUL;
  32 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Annul.NOT_ANNUL;
  33 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.BranchPredict.PREDICT_NOT_TAKEN;
  34 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.BranchPredict.PREDICT_TAKEN;
  35 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.CC.Xcc;
  36 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.ConditionFlag.Equal;
  37 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.ConditionFlag.Less;
  38 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.ConditionFlag.NotEqual;
  39 import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.REG;
  40 
  41 import org.graalvm.compiler.asm.Label;
  42 import org.graalvm.compiler.asm.sparc.SPARCAddress;
  43 import org.graalvm.compiler.asm.sparc.SPARCMacroAssembler;
  44 import org.graalvm.compiler.core.common.LIRKind;
  45 import org.graalvm.compiler.lir.LIRInstructionClass;
  46 import org.graalvm.compiler.lir.Opcode;
  47 import org.graalvm.compiler.lir.asm.CompilationResultBuilder;
  48 import org.graalvm.compiler.lir.gen.LIRGeneratorTool;
  49 
  50 import jdk.vm.ci.code.Register;
  51 import jdk.vm.ci.meta.AllocatableValue;
  52 import jdk.vm.ci.meta.JavaKind;
  53 import jdk.vm.ci.sparc.SPARCKind;
  54 
  55 /**
  56  * Emits code which compares two arrays of the same length.
  57  */
  58 @Opcode("ARRAY_EQUALS")
  59 public final class SPARCArrayEqualsOp extends SPARCLIRInstruction {
  60     public static final LIRInstructionClass<SPARCArrayEqualsOp> TYPE = LIRInstructionClass.create(SPARCArrayEqualsOp.class);
  61     public static final SizeEstimate SIZE = SizeEstimate.create(32);
  62 
  63     private final JavaKind kind;
  64     private final int arrayBaseOffset;
  65     private final int arrayIndexScale;
  66 
  67     @Def({REG}) protected AllocatableValue resultValue;
  68     @Alive({REG}) protected AllocatableValue array1Value;
  69     @Alive({REG}) protected AllocatableValue array2Value;
  70     @Alive({REG}) protected AllocatableValue lengthValue;
  71     @Temp({REG}) protected AllocatableValue temp1;
  72     @Temp({REG}) protected AllocatableValue temp2;
  73     @Temp({REG}) protected AllocatableValue temp3;
  74     @Temp({REG}) protected AllocatableValue temp4;
  75     @Temp({REG}) protected AllocatableValue temp5;
  76 
  77     public SPARCArrayEqualsOp(LIRGeneratorTool tool, JavaKind kind, AllocatableValue result, AllocatableValue array1, AllocatableValue array2, AllocatableValue length, boolean directPointers) {
  78         super(TYPE, SIZE);
  79 
  80         assert !kind.isNumericFloat() : "Float arrays comparison (bitwise_equal || both_NaN) isn't supported";
  81         this.kind = kind;
  82 
  83         this.arrayBaseOffset = directPointers ? 0 : tool.getProviders().getMetaAccess().getArrayBaseOffset(kind);
  84         this.arrayIndexScale = tool.getProviders().getMetaAccess().getArrayIndexScale(kind);
  85 
  86         this.resultValue = result;
  87         this.array1Value = array1;
  88         this.array2Value = array2;
  89         this.lengthValue = length;
  90 
  91         // Allocate some temporaries.
  92         this.temp1 = tool.newVariable(LIRKind.unknownReference(tool.target().arch.getWordKind()));
  93         this.temp2 = tool.newVariable(LIRKind.unknownReference(tool.target().arch.getWordKind()));
  94         this.temp3 = tool.newVariable(LIRKind.value(tool.target().arch.getWordKind()));
  95         this.temp4 = tool.newVariable(LIRKind.value(tool.target().arch.getWordKind()));
  96         this.temp5 = tool.newVariable(LIRKind.value(tool.target().arch.getWordKind()));
  97     }
  98 
  99     @Override
 100     public void emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm) {
 101         Register result = asRegister(resultValue);
 102         Register array1 = asRegister(temp1);
 103         Register array2 = asRegister(temp2);
 104         Register length = asRegister(temp3);
 105 
 106         Label trueLabel = new Label();
 107         Label falseLabel = new Label();
 108         Label done = new Label();
 109 
 110         // Load array base addresses.
 111         masm.add(asRegister(array1Value), arrayBaseOffset, array1);
 112         masm.add(asRegister(array2Value), arrayBaseOffset, array2);
 113 
 114         // Get array length in bytes.
 115         masm.mulx(asRegister(lengthValue, WORD), arrayIndexScale, length);
 116         masm.mov(length, result); // copy
 117 
 118         emit8ByteCompare(masm, result, array1, array2, length, trueLabel, falseLabel);
 119         emitTailCompares(masm, result, array1, array2, trueLabel, falseLabel);
 120 
 121         // Return true
 122         masm.bind(trueLabel);
 123         masm.mov(1, result);
 124         masm.jmp(done);
 125 
 126         // Return false
 127         masm.bind(falseLabel);
 128         masm.mov(g0, result);
 129 
 130         // That's it
 131         masm.bind(done);
 132     }
 133 
 134     /**
 135      * Vector size used in {@link #emit8ByteCompare}.
 136      */
 137     private static final int VECTOR_SIZE = 8;
 138 
 139     /**
 140      * Emits code that uses 8-byte vector compares.
 141      */
 142     private void emit8ByteCompare(SPARCMacroAssembler masm, Register result, Register array1, Register array2, Register length, Label trueLabel, Label falseLabel) {
 143         assert lengthValue.getPlatformKind().equals(SPARCKind.WORD);
 144         Label loop = new Label();
 145         Label compareTail = new Label();
 146         Label compareTailCorrectVectorEnd = new Label();
 147 
 148         Register tempReg1 = asRegister(temp4);
 149         Register tempReg2 = asRegister(temp5);
 150 
 151         masm.sra(length, 0, length);
 152         masm.and(result, VECTOR_SIZE - 1, result); // tail count (in bytes)
 153         masm.andcc(length, ~(VECTOR_SIZE - 1), length);  // vector count (in bytes)
 154         BPCC.emit(masm, Xcc, Equal, NOT_ANNUL, PREDICT_NOT_TAKEN, compareTail);
 155 
 156         masm.sub(length, VECTOR_SIZE, length); // Delay slot
 157         masm.add(array1, length, array1);
 158         masm.add(array2, length, array2);
 159         masm.sub(g0, length, length);
 160 
 161         // Compare the last element first
 162         masm.ldx(new SPARCAddress(array1, 0), tempReg1);
 163         masm.ldx(new SPARCAddress(array2, 0), tempReg2);
 164         masm.compareBranch(tempReg1, tempReg2, NotEqual, Xcc, falseLabel, PREDICT_NOT_TAKEN, null);
 165         masm.compareBranch(length, 0, Equal, Xcc, compareTailCorrectVectorEnd, PREDICT_NOT_TAKEN, null);
 166 
 167         // Load the first value from array 1 (Later done in back branch delay-slot)
 168         masm.ldx(new SPARCAddress(array1, length), tempReg1);
 169         masm.bind(loop);
 170         masm.ldx(new SPARCAddress(array2, length), tempReg2);
 171         masm.cmp(tempReg1, tempReg2);
 172 
 173         BPCC.emit(masm, Xcc, NotEqual, NOT_ANNUL, PREDICT_NOT_TAKEN, falseLabel);
 174         // Delay slot, not annul, add for next iteration
 175         masm.addcc(length, VECTOR_SIZE, length);
 176         // Annul, to prevent access past the array
 177         BPCC.emit(masm, Xcc, NotEqual, ANNUL, PREDICT_TAKEN, loop);
 178         masm.ldx(new SPARCAddress(array1, length), tempReg1); // Load in delay slot
 179 
 180         // Tail count zero, therefore we can go to the end
 181         masm.compareBranch(result, 0, Equal, Xcc, trueLabel, PREDICT_TAKEN, null);
 182 
 183         masm.bind(compareTailCorrectVectorEnd);
 184         // Correct the array pointers
 185         masm.add(array1, VECTOR_SIZE, array1);
 186         masm.add(array2, VECTOR_SIZE, array2);
 187 
 188         masm.bind(compareTail);
 189     }
 190 
 191     /**
 192      * Emits code to compare the remaining 1 to 4 bytes.
 193      */
 194     private void emitTailCompares(SPARCMacroAssembler masm, Register result, Register array1, Register array2, Label trueLabel, Label falseLabel) {
 195         Label compare2Bytes = new Label();
 196         Label compare1Byte = new Label();
 197 
 198         Register tempReg1 = asRegister(temp3);
 199         Register tempReg2 = asRegister(temp4);
 200 
 201         if (kind.getByteCount() <= 4) {
 202             // Compare trailing 4 bytes, if any.
 203             masm.compareBranch(result, 4, Less, Xcc, compare2Bytes, PREDICT_NOT_TAKEN, null);
 204 
 205             masm.lduw(new SPARCAddress(array1, 0), tempReg1);
 206             masm.lduw(new SPARCAddress(array2, 0), tempReg2);
 207             masm.compareBranch(tempReg1, tempReg2, NotEqual, Xcc, falseLabel, PREDICT_NOT_TAKEN, null);
 208 
 209             if (kind.getByteCount() <= 2) {
 210                 // Move array pointers forward.
 211                 masm.add(array1, 4, array1);
 212                 masm.add(array2, 4, array2);
 213                 masm.sub(result, 4, result);
 214 
 215                 // Compare trailing 2 bytes, if any.
 216                 masm.bind(compare2Bytes);
 217 
 218                 masm.compareBranch(result, 2, Less, Xcc, compare1Byte, PREDICT_TAKEN, null);
 219 
 220                 masm.lduh(new SPARCAddress(array1, 0), tempReg1);
 221                 masm.lduh(new SPARCAddress(array2, 0), tempReg2);
 222 
 223                 masm.compareBranch(tempReg1, tempReg2, NotEqual, Xcc, falseLabel, PREDICT_TAKEN, null);
 224 
 225                 // The one-byte tail compare is only required for boolean and byte arrays.
 226                 if (kind.getByteCount() <= 1) {
 227                     // Move array pointers forward before we compare the last trailing byte.
 228                     masm.add(array1, 2, array1);
 229                     masm.add(array2, 2, array2);
 230                     masm.sub(result, 2, result);
 231 
 232                     // Compare trailing byte, if any.
 233                     masm.bind(compare1Byte);
 234                     masm.compareBranch(result, 1, NotEqual, Xcc, trueLabel, PREDICT_TAKEN, null);
 235 
 236                     masm.ldub(new SPARCAddress(array1, 0), tempReg1);
 237                     masm.ldub(new SPARCAddress(array2, 0), tempReg2);
 238                     masm.compareBranch(tempReg1, tempReg2, NotEqual, Xcc, falseLabel, PREDICT_TAKEN, null);
 239                 } else {
 240                     masm.bind(compare1Byte);
 241                 }
 242             } else {
 243                 masm.bind(compare2Bytes);
 244             }
 245         }
 246     }
 247 }