001/*
002 * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package com.oracle.graal.lir.sparc;
024
025import static com.oracle.graal.asm.sparc.SPARCAssembler.Annul.*;
026import static com.oracle.graal.asm.sparc.SPARCAssembler.BranchPredict.*;
027import static com.oracle.graal.asm.sparc.SPARCAssembler.CC.*;
028import static com.oracle.graal.asm.sparc.SPARCAssembler.ConditionFlag.*;
029import static com.oracle.graal.asm.sparc.SPARCAssembler.RCondition.*;
030import static com.oracle.graal.lir.LIRInstruction.OperandFlag.*;
031import static jdk.internal.jvmci.code.ValueUtil.*;
032import static jdk.internal.jvmci.common.UnsafeAccess.*;
033import static jdk.internal.jvmci.sparc.SPARC.*;
034import static jdk.internal.jvmci.sparc.SPARC.CPUFeature.*;
035
036import java.lang.reflect.*;
037
038import jdk.internal.jvmci.code.*;
039import jdk.internal.jvmci.meta.*;
040import jdk.internal.jvmci.sparc.SPARC.CPUFeature;
041
042import com.oracle.graal.asm.*;
043import com.oracle.graal.asm.sparc.*;
044import com.oracle.graal.asm.sparc.SPARCAssembler.CC;
045import com.oracle.graal.asm.sparc.SPARCAssembler.ConditionFlag;
046import com.oracle.graal.lir.*;
047import com.oracle.graal.lir.asm.*;
048import com.oracle.graal.lir.gen.*;
049
050/**
051 * Emits code which compares two arrays of the same length.
052 */
053@Opcode("ARRAY_EQUALS")
054public final class SPARCArrayEqualsOp extends SPARCLIRInstruction {
055    public static final LIRInstructionClass<SPARCArrayEqualsOp> TYPE = LIRInstructionClass.create(SPARCArrayEqualsOp.class);
056    public static final SizeEstimate SIZE = SizeEstimate.create(32);
057
058    private final Kind kind;
059    private final int arrayBaseOffset;
060    private final int arrayIndexScale;
061
062    @Def({REG}) protected Value resultValue;
063    @Alive({REG}) protected Value array1Value;
064    @Alive({REG}) protected Value array2Value;
065    @Alive({REG}) protected Value lengthValue;
066    @Temp({REG}) protected Value temp1;
067    @Temp({REG}) protected Value temp2;
068    @Temp({REG}) protected Value temp3;
069    @Temp({REG}) protected Value temp4;
070    @Temp({REG}) protected Value temp5;
071
072    public SPARCArrayEqualsOp(LIRGeneratorTool tool, Kind kind, Value result, Value array1, Value array2, Value length) {
073        super(TYPE, SIZE);
074        this.kind = kind;
075
076        Class<?> arrayClass = Array.newInstance(kind.toJavaClass(), 0).getClass();
077        this.arrayBaseOffset = unsafe.arrayBaseOffset(arrayClass);
078        this.arrayIndexScale = unsafe.arrayIndexScale(arrayClass);
079
080        this.resultValue = result;
081        this.array1Value = array1;
082        this.array2Value = array2;
083        this.lengthValue = length;
084
085        // Allocate some temporaries.
086        this.temp1 = tool.newVariable(LIRKind.unknownReference(tool.target().wordKind));
087        this.temp2 = tool.newVariable(LIRKind.unknownReference(tool.target().wordKind));
088        this.temp3 = tool.newVariable(LIRKind.value(tool.target().wordKind));
089        this.temp4 = tool.newVariable(LIRKind.value(tool.target().wordKind));
090        this.temp5 = tool.newVariable(LIRKind.value(tool.target().wordKind));
091    }
092
093    @Override
094    public void emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm) {
095        Register result = asRegister(resultValue);
096        Register array1 = asRegister(temp1);
097        Register array2 = asRegister(temp2);
098        Register length = asRegister(temp3);
099
100        Label trueLabel = new Label();
101        Label falseLabel = new Label();
102        Label done = new Label();
103
104        // Load array base addresses.
105        masm.add(asObjectReg(array1Value), arrayBaseOffset, array1);
106        masm.add(asObjectReg(array2Value), arrayBaseOffset, array2);
107
108        // Get array length in bytes.
109        masm.mulx(asIntReg(lengthValue), arrayIndexScale, length);
110        masm.mov(length, result); // copy
111
112        emit8ByteCompare(masm, result, array1, array2, length, trueLabel, falseLabel);
113        emitTailCompares(masm, result, array1, array2, trueLabel, falseLabel);
114
115        // Return true
116        masm.bind(trueLabel);
117        masm.mov(1, result);
118        masm.bicc(Always, ANNUL, done);
119        masm.nop();
120
121        // Return false
122        masm.bind(falseLabel);
123        masm.mov(g0, result);
124
125        // That's it
126        masm.bind(done);
127    }
128
129    /**
130     * Vector size used in {@link #emit8ByteCompare}.
131     */
132    private static final int VECTOR_SIZE = 8;
133
134    /**
135     * Emits code that uses 8-byte vector compares.
136     */
137    private void emit8ByteCompare(SPARCMacroAssembler masm, Register result, Register array1, Register array2, Register length, Label trueLabel, Label falseLabel) {
138        assert lengthValue.getPlatformKind().equals(Kind.Int);
139        Label loop = new Label();
140        Label compareTail = new Label();
141        Label compareTailCorrectVectorEnd = new Label();
142
143        Register tempReg1 = asRegister(temp4);
144        Register tempReg2 = asRegister(temp5);
145
146        boolean hasCBcond = masm.hasFeature(CPUFeature.CBCOND);
147
148        masm.sra(length, 0, length);
149        masm.and(result, VECTOR_SIZE - 1, result); // tail count (in bytes)
150        masm.andcc(length, ~(VECTOR_SIZE - 1), length);  // vector count (in bytes)
151        masm.bpcc(ConditionFlag.Equal, NOT_ANNUL, compareTail, CC.Xcc, PREDICT_NOT_TAKEN);
152
153        masm.sub(length, VECTOR_SIZE, length); // Delay slot
154        masm.add(array1, length, array1);
155        masm.add(array2, length, array2);
156        masm.sub(g0, length, length);
157
158        // Compare the last element first
159        masm.ldx(new SPARCAddress(array1, 0), tempReg1);
160        masm.ldx(new SPARCAddress(array2, 0), tempReg2);
161        if (hasCBcond) {
162            masm.cbcondx(NotEqual, tempReg1, tempReg2, falseLabel);
163            masm.cbcondx(Equal, length, 0, compareTailCorrectVectorEnd);
164        } else {
165            masm.cmp(tempReg1, tempReg2);
166            masm.bpcc(NotEqual, NOT_ANNUL, falseLabel, Xcc, PREDICT_NOT_TAKEN);
167            masm.nop();
168            masm.bpr(Rc_z, NOT_ANNUL, compareTailCorrectVectorEnd, PREDICT_NOT_TAKEN, length);
169            masm.nop();
170        }
171
172        // Load the first value from array 1 (Later done in back branch delay-slot)
173        masm.ldx(new SPARCAddress(array1, length), tempReg1);
174        masm.bind(loop);
175        masm.ldx(new SPARCAddress(array2, length), tempReg2);
176        masm.cmp(tempReg1, tempReg2);
177        masm.bpcc(NotEqual, NOT_ANNUL, falseLabel, Xcc, PREDICT_NOT_TAKEN);
178        // Delay slot, not annul, add for next iteration
179        masm.addcc(length, VECTOR_SIZE, length);
180        // Annul, to prevent access past the array
181        masm.bpcc(NotEqual, ANNUL, loop, Xcc, PREDICT_TAKEN);
182        masm.ldx(new SPARCAddress(array1, length), tempReg1); // Load in delay slot
183
184        // Tail count zero, therefore we can go to the end
185        if (hasCBcond) {
186            masm.cbcondx(Equal, result, 0, trueLabel);
187        } else {
188            masm.bpr(Rc_z, NOT_ANNUL, trueLabel, PREDICT_TAKEN, result);
189            masm.nop();
190        }
191
192        masm.bind(compareTailCorrectVectorEnd);
193        // Correct the array pointers
194        masm.add(array1, VECTOR_SIZE, array1);
195        masm.add(array2, VECTOR_SIZE, array2);
196
197        masm.bind(compareTail);
198    }
199
200    /**
201     * Emits code to compare the remaining 1 to 4 bytes.
202     */
203    private void emitTailCompares(SPARCMacroAssembler masm, Register result, Register array1, Register array2, Label trueLabel, Label falseLabel) {
204        Label compare2Bytes = new Label();
205        Label compare1Byte = new Label();
206
207        Register tempReg1 = asRegister(temp3);
208        Register tempReg2 = asRegister(temp4);
209        boolean hasCBcond = masm.hasFeature(CBCOND);
210
211        if (kind.getByteCount() <= 4) {
212            // Compare trailing 4 bytes, if any.
213            if (hasCBcond) {
214                masm.cbcondx(Less, result, 4, compare2Bytes);
215            } else {
216                masm.cmp(result, 4);
217                masm.bpcc(Less, NOT_ANNUL, compare2Bytes, Xcc, PREDICT_NOT_TAKEN);
218                masm.nop();
219            }
220
221            masm.lduw(new SPARCAddress(array1, 0), tempReg1);
222            masm.lduw(new SPARCAddress(array2, 0), tempReg2);
223
224            if (hasCBcond) {
225                masm.cbcondx(NotEqual, tempReg1, tempReg2, falseLabel);
226            } else {
227                masm.cmp(tempReg1, tempReg2);
228                masm.bpcc(NotEqual, NOT_ANNUL, falseLabel, Xcc, PREDICT_NOT_TAKEN);
229                masm.nop();
230            }
231
232            if (kind.getByteCount() <= 2) {
233                // Move array pointers forward.
234                masm.add(array1, 4, array1);
235                masm.add(array2, 4, array2);
236                masm.sub(result, 4, result);
237
238                // Compare trailing 2 bytes, if any.
239                masm.bind(compare2Bytes);
240
241                if (hasCBcond) {
242                    masm.cbcondx(Less, result, 2, compare1Byte);
243                } else {
244                    masm.cmp(result, 2);
245                    masm.bpcc(Less, NOT_ANNUL, compare1Byte, Xcc, PREDICT_TAKEN);
246                    masm.nop();
247                }
248
249                masm.lduh(new SPARCAddress(array1, 0), tempReg1);
250                masm.lduh(new SPARCAddress(array2, 0), tempReg2);
251
252                if (hasCBcond) {
253                    masm.cbcondx(NotEqual, tempReg1, tempReg2, falseLabel);
254                } else {
255                    masm.cmp(tempReg1, tempReg2);
256                    masm.bpcc(NotEqual, NOT_ANNUL, falseLabel, Xcc, PREDICT_TAKEN);
257                    masm.nop();
258                }
259
260                // The one-byte tail compare is only required for boolean and byte arrays.
261                if (kind.getByteCount() <= 1) {
262                    // Move array pointers forward before we compare the last trailing byte.
263                    masm.add(array1, 2, array1);
264                    masm.add(array2, 2, array2);
265                    masm.sub(result, 2, result);
266
267                    // Compare trailing byte, if any.
268                    masm.bind(compare1Byte);
269                    if (hasCBcond) {
270                        masm.cbcondx(NotEqual, result, 1, trueLabel);
271                    } else {
272                        masm.cmp(result, 1);
273                        masm.bpcc(NotEqual, NOT_ANNUL, trueLabel, Xcc, PREDICT_TAKEN);
274                        masm.nop();
275                    }
276                    masm.ldub(new SPARCAddress(array1, 0), tempReg1);
277                    masm.ldub(new SPARCAddress(array2, 0), tempReg2);
278                    if (hasCBcond) {
279                        masm.cbcondx(NotEqual, tempReg1, tempReg2, falseLabel);
280                    } else {
281                        masm.cmp(tempReg1, tempReg2);
282                        masm.bpcc(NotEqual, NOT_ANNUL, falseLabel, Xcc, PREDICT_TAKEN);
283                        masm.nop();
284                    }
285                } else {
286                    masm.bind(compare1Byte);
287                }
288            } else {
289                masm.bind(compare2Bytes);
290            }
291        }
292    }
293}