--- /dev/null 2017-01-22 10:16:57.869617664 -0800 +++ new/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.sparc/src/org/graalvm/compiler/lir/sparc/SPARCArrayEqualsOp.java 2017-02-15 17:05:07.347697523 -0800 @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2013, 2016, 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 org.graalvm.compiler.lir.sparc; + +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.BPCC; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Annul.ANNUL; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Annul.NOT_ANNUL; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.BranchPredict.PREDICT_NOT_TAKEN; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.BranchPredict.PREDICT_TAKEN; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.CC.Xcc; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.ConditionFlag.Equal; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.ConditionFlag.Less; +import static org.graalvm.compiler.asm.sparc.SPARCAssembler.ConditionFlag.NotEqual; +import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.REG; +import static jdk.vm.ci.code.ValueUtil.asRegister; +import static jdk.vm.ci.sparc.SPARC.g0; +import static jdk.vm.ci.sparc.SPARCKind.WORD; + +import java.lang.reflect.Array; +import java.lang.reflect.Field; + +import org.graalvm.compiler.asm.Label; +import org.graalvm.compiler.asm.sparc.SPARCAddress; +import org.graalvm.compiler.asm.sparc.SPARCMacroAssembler; +import org.graalvm.compiler.core.common.LIRKind; +import org.graalvm.compiler.lir.LIRInstructionClass; +import org.graalvm.compiler.lir.Opcode; +import org.graalvm.compiler.lir.asm.CompilationResultBuilder; +import org.graalvm.compiler.lir.gen.LIRGeneratorTool; + +import jdk.vm.ci.code.Register; +import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.Value; +import jdk.vm.ci.sparc.SPARCKind; +import sun.misc.Unsafe; + +/** + * Emits code which compares two arrays of the same length. + */ +@Opcode("ARRAY_EQUALS") +public final class SPARCArrayEqualsOp extends SPARCLIRInstruction { + public static final LIRInstructionClass TYPE = LIRInstructionClass.create(SPARCArrayEqualsOp.class); + public static final SizeEstimate SIZE = SizeEstimate.create(32); + + private final JavaKind 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, JavaKind kind, Value result, Value array1, Value array2, Value length) { + super(TYPE, SIZE); + 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.unknownReference(tool.target().arch.getWordKind())); + this.temp2 = tool.newVariable(LIRKind.unknownReference(tool.target().arch.getWordKind())); + this.temp3 = tool.newVariable(LIRKind.value(tool.target().arch.getWordKind())); + this.temp4 = tool.newVariable(LIRKind.value(tool.target().arch.getWordKind())); + this.temp5 = tool.newVariable(LIRKind.value(tool.target().arch.getWordKind())); + } + + @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. + masm.add(asRegister(array1Value), arrayBaseOffset, array1); + masm.add(asRegister(array2Value), arrayBaseOffset, array2); + + // Get array length in bytes. + masm.mulx(asRegister(lengthValue, WORD), arrayIndexScale, length); + masm.mov(length, result); // copy + + emit8ByteCompare(masm, result, array1, array2, length, trueLabel, falseLabel); + emitTailCompares(masm, result, array1, array2, trueLabel, falseLabel); + + // Return true + masm.bind(trueLabel); + masm.mov(1, result); + masm.jmp(done); + + // Return false + masm.bind(falseLabel); + masm.mov(g0, result); + + // 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) { + assert lengthValue.getPlatformKind().equals(SPARCKind.WORD); + Label loop = new Label(); + Label compareTail = new Label(); + Label compareTailCorrectVectorEnd = new Label(); + + Register tempReg1 = asRegister(temp4); + Register tempReg2 = asRegister(temp5); + + masm.sra(length, 0, length); + masm.and(result, VECTOR_SIZE - 1, result); // tail count (in bytes) + masm.andcc(length, ~(VECTOR_SIZE - 1), length); // vector count (in bytes) + BPCC.emit(masm, Xcc, Equal, NOT_ANNUL, PREDICT_NOT_TAKEN, compareTail); + + masm.sub(length, VECTOR_SIZE, length); // Delay slot + masm.add(array1, length, array1); + masm.add(array2, length, array2); + masm.sub(g0, length, length); + + // Compare the last element first + masm.ldx(new SPARCAddress(array1, 0), tempReg1); + masm.ldx(new SPARCAddress(array2, 0), tempReg2); + masm.compareBranch(tempReg1, tempReg2, NotEqual, Xcc, falseLabel, PREDICT_NOT_TAKEN, null); + masm.compareBranch(length, 0, Equal, Xcc, compareTailCorrectVectorEnd, PREDICT_NOT_TAKEN, null); + + // Load the first value from array 1 (Later done in back branch delay-slot) + masm.ldx(new SPARCAddress(array1, length), tempReg1); + masm.bind(loop); + masm.ldx(new SPARCAddress(array2, length), tempReg2); + masm.cmp(tempReg1, tempReg2); + + BPCC.emit(masm, Xcc, NotEqual, NOT_ANNUL, PREDICT_NOT_TAKEN, falseLabel); + // Delay slot, not annul, add for next iteration + masm.addcc(length, VECTOR_SIZE, length); + // Annul, to prevent access past the array + BPCC.emit(masm, Xcc, NotEqual, ANNUL, PREDICT_TAKEN, loop); + masm.ldx(new SPARCAddress(array1, length), tempReg1); // Load in delay slot + + // Tail count zero, therefore we can go to the end + masm.compareBranch(result, 0, Equal, Xcc, trueLabel, PREDICT_TAKEN, null); + + masm.bind(compareTailCorrectVectorEnd); + // Correct the array pointers + masm.add(array1, VECTOR_SIZE, array1); + masm.add(array2, VECTOR_SIZE, array2); + + 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. + masm.compareBranch(result, 4, Less, Xcc, compare2Bytes, PREDICT_NOT_TAKEN, null); + + masm.lduw(new SPARCAddress(array1, 0), tempReg1); + masm.lduw(new SPARCAddress(array2, 0), tempReg2); + masm.compareBranch(tempReg1, tempReg2, NotEqual, Xcc, falseLabel, PREDICT_NOT_TAKEN, null); + + if (kind.getByteCount() <= 2) { + // Move array pointers forward. + masm.add(array1, 4, array1); + masm.add(array2, 4, array2); + masm.sub(result, 4, result); + + // Compare trailing 2 bytes, if any. + masm.bind(compare2Bytes); + + masm.compareBranch(result, 2, Less, Xcc, compare1Byte, PREDICT_TAKEN, null); + + masm.lduh(new SPARCAddress(array1, 0), tempReg1); + masm.lduh(new SPARCAddress(array2, 0), tempReg2); + + masm.compareBranch(tempReg1, tempReg2, NotEqual, Xcc, falseLabel, PREDICT_TAKEN, null); + + // 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. + masm.add(array1, 2, array1); + masm.add(array2, 2, array2); + masm.sub(result, 2, result); + + // Compare trailing byte, if any. + masm.bind(compare1Byte); + masm.compareBranch(result, 1, NotEqual, Xcc, trueLabel, PREDICT_TAKEN, null); + + masm.ldub(new SPARCAddress(array1, 0), tempReg1); + masm.ldub(new SPARCAddress(array2, 0), tempReg2); + masm.compareBranch(tempReg1, tempReg2, NotEqual, Xcc, falseLabel, PREDICT_TAKEN, null); + } else { + masm.bind(compare1Byte); + } + } else { + masm.bind(compare2Bytes); + } + } + } + + private static final Unsafe UNSAFE = initUnsafe(); + + private static Unsafe initUnsafe() { + try { + return Unsafe.getUnsafe(); + } catch (SecurityException se) { + try { + Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); + theUnsafe.setAccessible(true); + return (Unsafe) theUnsafe.get(Unsafe.class); + } catch (Exception e) { + throw new RuntimeException("exception while trying to get Unsafe", e); + } + } + } +}