1 /*
   2  * Copyright (c) 2013, 2015, 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.amd64;
  24 
  25 import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.ILLEGAL;
  26 import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.REG;
  27 import static jdk.vm.ci.code.ValueUtil.asRegister;
  28 
  29 import java.lang.reflect.Array;
  30 import java.lang.reflect.Field;
  31 
  32 import org.graalvm.compiler.asm.Label;
  33 import org.graalvm.compiler.asm.amd64.AMD64Address;
  34 import org.graalvm.compiler.asm.amd64.AMD64Address.Scale;
  35 import org.graalvm.compiler.asm.amd64.AMD64Assembler.ConditionFlag;
  36 import org.graalvm.compiler.asm.amd64.AMD64Assembler.OperandSize;
  37 import org.graalvm.compiler.asm.amd64.AMD64Assembler.SSEOp;
  38 import org.graalvm.compiler.asm.amd64.AMD64MacroAssembler;
  39 import org.graalvm.compiler.core.common.LIRKind;
  40 import org.graalvm.compiler.lir.LIRInstructionClass;
  41 import org.graalvm.compiler.lir.Opcode;
  42 import org.graalvm.compiler.lir.asm.CompilationResultBuilder;
  43 import org.graalvm.compiler.lir.gen.LIRGeneratorTool;
  44 
  45 import jdk.vm.ci.amd64.AMD64;
  46 import jdk.vm.ci.amd64.AMD64.CPUFeature;
  47 import jdk.vm.ci.amd64.AMD64Kind;
  48 import jdk.vm.ci.code.Register;
  49 import jdk.vm.ci.code.TargetDescription;
  50 import jdk.vm.ci.meta.JavaKind;
  51 import jdk.vm.ci.meta.Value;
  52 import sun.misc.Unsafe;
  53 
  54 /**
  55  * Emits code which compares two arrays of the same length. If the CPU supports any vector
  56  * instructions specialized code is emitted to leverage these instructions.
  57  */
  58 @Opcode("ARRAY_EQUALS")
  59 public final class AMD64ArrayEqualsOp extends AMD64LIRInstruction {
  60     public static final LIRInstructionClass<AMD64ArrayEqualsOp> TYPE = LIRInstructionClass.create(AMD64ArrayEqualsOp.class);
  61 
  62     private final JavaKind kind;
  63     private final int arrayBaseOffset;
  64     private final int arrayIndexScale;
  65 
  66     @Def({REG}) protected Value resultValue;
  67     @Alive({REG}) protected Value array1Value;
  68     @Alive({REG}) protected Value array2Value;
  69     @Alive({REG}) protected Value lengthValue;
  70     @Temp({REG}) protected Value temp1;
  71     @Temp({REG}) protected Value temp2;
  72     @Temp({REG}) protected Value temp3;
  73     @Temp({REG}) protected Value temp4;
  74 
  75     @Temp({REG, ILLEGAL}) protected Value temp5;
  76     @Temp({REG, ILLEGAL}) protected Value tempXMM;
  77 
  78     @Temp({REG, ILLEGAL}) protected Value vectorTemp1;
  79     @Temp({REG, ILLEGAL}) protected Value vectorTemp2;
  80 
  81     public AMD64ArrayEqualsOp(LIRGeneratorTool tool, JavaKind kind, Value result, Value array1, Value array2, Value length) {
  82         super(TYPE);
  83         this.kind = kind;
  84 
  85         Class<?> arrayClass = Array.newInstance(kind.toJavaClass(), 0).getClass();
  86         this.arrayBaseOffset = UNSAFE.arrayBaseOffset(arrayClass);
  87         this.arrayIndexScale = UNSAFE.arrayIndexScale(arrayClass);
  88 
  89         this.resultValue = result;
  90         this.array1Value = array1;
  91         this.array2Value = array2;
  92         this.lengthValue = length;
  93 
  94         // Allocate some temporaries.
  95         this.temp1 = tool.newVariable(LIRKind.unknownReference(tool.target().arch.getWordKind()));
  96         this.temp2 = tool.newVariable(LIRKind.unknownReference(tool.target().arch.getWordKind()));
  97         this.temp3 = tool.newVariable(LIRKind.value(tool.target().arch.getWordKind()));
  98         this.temp4 = tool.newVariable(LIRKind.value(tool.target().arch.getWordKind()));
  99 
 100         this.temp5 = kind.isNumericFloat() ? tool.newVariable(LIRKind.value(tool.target().arch.getWordKind())) : Value.ILLEGAL;
 101         if (kind == JavaKind.Float) {
 102             this.tempXMM = tool.newVariable(LIRKind.value(AMD64Kind.SINGLE));
 103         } else if (kind == JavaKind.Double) {
 104             this.tempXMM = tool.newVariable(LIRKind.value(AMD64Kind.DOUBLE));
 105         } else {
 106             this.tempXMM = Value.ILLEGAL;
 107         }
 108 
 109         // We only need the vector temporaries if we generate SSE code.
 110         if (supportsSSE41(tool.target())) {
 111             this.vectorTemp1 = tool.newVariable(LIRKind.value(AMD64Kind.DOUBLE));
 112             this.vectorTemp2 = tool.newVariable(LIRKind.value(AMD64Kind.DOUBLE));
 113         } else {
 114             this.vectorTemp1 = Value.ILLEGAL;
 115             this.vectorTemp2 = Value.ILLEGAL;
 116         }
 117     }
 118 
 119     @Override
 120     public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) {
 121         Register result = asRegister(resultValue);
 122         Register array1 = asRegister(temp1);
 123         Register array2 = asRegister(temp2);
 124         Register length = asRegister(temp3);
 125 
 126         Label trueLabel = new Label();
 127         Label falseLabel = new Label();
 128         Label done = new Label();
 129 
 130         // Load array base addresses.
 131         masm.leaq(array1, new AMD64Address(asRegister(array1Value), arrayBaseOffset));
 132         masm.leaq(array2, new AMD64Address(asRegister(array2Value), arrayBaseOffset));
 133 
 134         // Get array length in bytes.
 135         masm.imull(length, asRegister(lengthValue), arrayIndexScale);
 136         masm.movl(result, length); // copy
 137 
 138         if (supportsAVX2(crb.target)) {
 139             emitAVXCompare(crb, masm, result, array1, array2, length, trueLabel, falseLabel);
 140         } else if (supportsSSE41(crb.target)) {
 141             emitSSE41Compare(crb, masm, result, array1, array2, length, trueLabel, falseLabel);
 142         }
 143 
 144         emit8ByteCompare(crb, masm, result, array1, array2, length, trueLabel, falseLabel);
 145         emitTailCompares(masm, result, array1, array2, length, trueLabel, falseLabel);
 146 
 147         // Return true
 148         masm.bind(trueLabel);
 149         masm.movl(result, 1);
 150         masm.jmpb(done);
 151 
 152         // Return false
 153         masm.bind(falseLabel);
 154         masm.xorl(result, result);
 155 
 156         // That's it
 157         masm.bind(done);
 158     }
 159 
 160     /**
 161      * Returns if the underlying AMD64 architecture supports SSE 4.1 instructions.
 162      *
 163      * @param target target description of the underlying architecture
 164      * @return true if the underlying architecture supports SSE 4.1
 165      */
 166     private static boolean supportsSSE41(TargetDescription target) {
 167         AMD64 arch = (AMD64) target.arch;
 168         return arch.getFeatures().contains(CPUFeature.SSE4_1);
 169     }
 170 
 171     /**
 172      * Vector size used in {@link #emitSSE41Compare}.
 173      */
 174     private static final int SSE4_1_VECTOR_SIZE = 16;
 175 
 176     /**
 177      * Emits code that uses SSE4.1 128-bit (16-byte) vector compares.
 178      */
 179     private void emitSSE41Compare(CompilationResultBuilder crb, AMD64MacroAssembler masm, Register result, Register array1, Register array2, Register length, Label trueLabel, Label falseLabel) {
 180         assert supportsSSE41(crb.target);
 181 
 182         Register vector1 = asRegister(vectorTemp1, AMD64Kind.DOUBLE);
 183         Register vector2 = asRegister(vectorTemp2, AMD64Kind.DOUBLE);
 184 
 185         Label loop = new Label();
 186         Label compareTail = new Label();
 187 
 188         boolean requiresNaNCheck = kind.isNumericFloat();
 189         Label loopCheck = new Label();
 190         Label nanCheck = new Label();
 191 
 192         // Compare 16-byte vectors
 193         masm.andl(result, SSE4_1_VECTOR_SIZE - 1); // tail count (in bytes)
 194         masm.andl(length, ~(SSE4_1_VECTOR_SIZE - 1)); // vector count (in bytes)
 195         masm.jcc(ConditionFlag.Zero, compareTail);
 196 
 197         masm.leaq(array1, new AMD64Address(array1, length, Scale.Times1, 0));
 198         masm.leaq(array2, new AMD64Address(array2, length, Scale.Times1, 0));
 199         masm.negq(length);
 200 
 201         // Align the main loop
 202         masm.align(crb.target.wordSize * 2);
 203         masm.bind(loop);
 204         masm.movdqu(vector1, new AMD64Address(array1, length, Scale.Times1, 0));
 205         masm.movdqu(vector2, new AMD64Address(array2, length, Scale.Times1, 0));
 206         masm.pxor(vector1, vector2);
 207         masm.ptest(vector1, vector1);
 208         masm.jcc(ConditionFlag.NotZero, requiresNaNCheck ? nanCheck : falseLabel);
 209 
 210         masm.bind(loopCheck);
 211         masm.addq(length, SSE4_1_VECTOR_SIZE);
 212         masm.jcc(ConditionFlag.NotZero, loop);
 213 
 214         masm.testl(result, result);
 215         masm.jcc(ConditionFlag.Zero, trueLabel);
 216 
 217         if (requiresNaNCheck) {
 218             Label unalignedCheck = new Label();
 219             masm.jmpb(unalignedCheck);
 220             masm.bind(nanCheck);
 221             emitFloatCompareWithinRange(crb, masm, array1, array2, length, 0, falseLabel, SSE4_1_VECTOR_SIZE);
 222             masm.jmpb(loopCheck);
 223             masm.bind(unalignedCheck);
 224         }
 225 
 226         /*
 227          * Compare the remaining bytes with an unaligned memory load aligned to the end of the
 228          * array.
 229          */
 230         masm.movdqu(vector1, new AMD64Address(array1, result, Scale.Times1, -SSE4_1_VECTOR_SIZE));
 231         masm.movdqu(vector2, new AMD64Address(array2, result, Scale.Times1, -SSE4_1_VECTOR_SIZE));
 232         masm.pxor(vector1, vector2);
 233         masm.ptest(vector1, vector1);
 234         if (requiresNaNCheck) {
 235             masm.jcc(ConditionFlag.Zero, trueLabel);
 236             emitFloatCompareWithinRange(crb, masm, array1, array2, result, -SSE4_1_VECTOR_SIZE, falseLabel, SSE4_1_VECTOR_SIZE);
 237         } else {
 238             masm.jcc(ConditionFlag.NotZero, falseLabel);
 239         }
 240         masm.jmp(trueLabel);
 241 
 242         masm.bind(compareTail);
 243         masm.movl(length, result);
 244     }
 245 
 246     /**
 247      * Returns if the underlying AMD64 architecture supports AVX instructions.
 248      *
 249      * @param target target description of the underlying architecture
 250      * @return true if the underlying architecture supports AVX
 251      */
 252     private static boolean supportsAVX2(TargetDescription target) {
 253         AMD64 arch = (AMD64) target.arch;
 254         return arch.getFeatures().contains(CPUFeature.AVX2);
 255     }
 256 
 257     /**
 258      * Vector size used in {@link #emitAVXCompare}.
 259      */
 260     private static final int AVX_VECTOR_SIZE = 32;
 261 
 262     private void emitAVXCompare(CompilationResultBuilder crb, AMD64MacroAssembler masm, Register result, Register array1, Register array2, Register length, Label trueLabel, Label falseLabel) {
 263         assert supportsAVX2(crb.target);
 264 
 265         Register vector1 = asRegister(vectorTemp1, AMD64Kind.DOUBLE);
 266         Register vector2 = asRegister(vectorTemp2, AMD64Kind.DOUBLE);
 267 
 268         Label loop = new Label();
 269         Label compareTail = new Label();
 270 
 271         boolean requiresNaNCheck = kind.isNumericFloat();
 272         Label loopCheck = new Label();
 273         Label nanCheck = new Label();
 274 
 275         // Compare 16-byte vectors
 276         masm.andl(result, AVX_VECTOR_SIZE - 1); // tail count (in bytes)
 277         masm.andl(length, ~(AVX_VECTOR_SIZE - 1)); // vector count (in bytes)
 278         masm.jcc(ConditionFlag.Zero, compareTail);
 279 
 280         masm.leaq(array1, new AMD64Address(array1, length, Scale.Times1, 0));
 281         masm.leaq(array2, new AMD64Address(array2, length, Scale.Times1, 0));
 282         masm.negq(length);
 283 
 284         // Align the main loop
 285         masm.align(crb.target.wordSize * 2);
 286         masm.bind(loop);
 287         masm.vmovdqu(vector1, new AMD64Address(array1, length, Scale.Times1, 0));
 288         masm.vmovdqu(vector2, new AMD64Address(array2, length, Scale.Times1, 0));
 289         masm.vpxor(vector1, vector1, vector2);
 290         masm.vptest(vector1, vector1);
 291         masm.jcc(ConditionFlag.NotZero, requiresNaNCheck ? nanCheck : falseLabel);
 292 
 293         masm.bind(loopCheck);
 294         masm.addq(length, AVX_VECTOR_SIZE);
 295         masm.jcc(ConditionFlag.NotZero, loop);
 296 
 297         masm.testl(result, result);
 298         masm.jcc(ConditionFlag.Zero, trueLabel);
 299 
 300         if (requiresNaNCheck) {
 301             Label unalignedCheck = new Label();
 302             masm.jmpb(unalignedCheck);
 303             masm.bind(nanCheck);
 304             emitFloatCompareWithinRange(crb, masm, array1, array2, length, 0, falseLabel, AVX_VECTOR_SIZE);
 305             masm.jmpb(loopCheck);
 306             masm.bind(unalignedCheck);
 307         }
 308 
 309         /*
 310          * Compare the remaining bytes with an unaligned memory load aligned to the end of the
 311          * array.
 312          */
 313         masm.vmovdqu(vector1, new AMD64Address(array1, result, Scale.Times1, -AVX_VECTOR_SIZE));
 314         masm.vmovdqu(vector2, new AMD64Address(array2, result, Scale.Times1, -AVX_VECTOR_SIZE));
 315         masm.vpxor(vector1, vector1, vector2);
 316         masm.vptest(vector1, vector1);
 317         if (requiresNaNCheck) {
 318             masm.jcc(ConditionFlag.Zero, trueLabel);
 319             emitFloatCompareWithinRange(crb, masm, array1, array2, result, -AVX_VECTOR_SIZE, falseLabel, AVX_VECTOR_SIZE);
 320         } else {
 321             masm.jcc(ConditionFlag.NotZero, falseLabel);
 322         }
 323         masm.jmp(trueLabel);
 324 
 325         masm.bind(compareTail);
 326         masm.movl(length, result);
 327     }
 328 
 329     /**
 330      * Vector size used in {@link #emit8ByteCompare}.
 331      */
 332     private static final int VECTOR_SIZE = 8;
 333 
 334     /**
 335      * Emits code that uses 8-byte vector compares.
 336      */
 337     private void emit8ByteCompare(CompilationResultBuilder crb, AMD64MacroAssembler masm, Register result, Register array1, Register array2, Register length, Label trueLabel, Label falseLabel) {
 338         Label loop = new Label();
 339         Label compareTail = new Label();
 340 
 341         boolean requiresNaNCheck = kind.isNumericFloat();
 342         Label loopCheck = new Label();
 343         Label nanCheck = new Label();
 344 
 345         Register temp = asRegister(temp4);
 346 
 347         masm.andl(result, VECTOR_SIZE - 1); // tail count (in bytes)
 348         masm.andl(length, ~(VECTOR_SIZE - 1));  // vector count (in bytes)
 349         masm.jcc(ConditionFlag.Zero, compareTail);
 350 
 351         masm.leaq(array1, new AMD64Address(array1, length, Scale.Times1, 0));
 352         masm.leaq(array2, new AMD64Address(array2, length, Scale.Times1, 0));
 353         masm.negq(length);
 354 
 355         // Align the main loop
 356         masm.align(crb.target.wordSize * 2);
 357         masm.bind(loop);
 358         masm.movq(temp, new AMD64Address(array1, length, Scale.Times1, 0));
 359         masm.cmpq(temp, new AMD64Address(array2, length, Scale.Times1, 0));
 360         masm.jcc(ConditionFlag.NotEqual, requiresNaNCheck ? nanCheck : falseLabel);
 361 
 362         masm.bind(loopCheck);
 363         masm.addq(length, VECTOR_SIZE);
 364         masm.jccb(ConditionFlag.NotZero, loop);
 365 
 366         masm.testl(result, result);
 367         masm.jcc(ConditionFlag.Zero, trueLabel);
 368 
 369         if (requiresNaNCheck) {
 370             // NaN check is slow path and hence placed outside of the main loop.
 371             Label unalignedCheck = new Label();
 372             masm.jmpb(unalignedCheck);
 373             masm.bind(nanCheck);
 374             // At most two iterations, unroll in the emitted code.
 375             for (int offset = 0; offset < VECTOR_SIZE; offset += kind.getByteCount()) {
 376                 emitFloatCompare(masm, array1, array2, length, offset, falseLabel, kind.getByteCount() == VECTOR_SIZE);
 377             }
 378             masm.jmpb(loopCheck);
 379             masm.bind(unalignedCheck);
 380         }
 381 
 382         /*
 383          * Compare the remaining bytes with an unaligned memory load aligned to the end of the
 384          * array.
 385          */
 386         masm.movq(temp, new AMD64Address(array1, result, Scale.Times1, -VECTOR_SIZE));
 387         masm.cmpq(temp, new AMD64Address(array2, result, Scale.Times1, -VECTOR_SIZE));
 388         if (requiresNaNCheck) {
 389             masm.jcc(ConditionFlag.Equal, trueLabel);
 390             // At most two iterations, unroll in the emitted code.
 391             for (int offset = 0; offset < VECTOR_SIZE; offset += kind.getByteCount()) {
 392                 emitFloatCompare(masm, array1, array2, result, -VECTOR_SIZE + offset, falseLabel, kind.getByteCount() == VECTOR_SIZE);
 393             }
 394         } else {
 395             masm.jccb(ConditionFlag.NotEqual, falseLabel);
 396         }
 397         masm.jmpb(trueLabel);
 398 
 399         masm.bind(compareTail);
 400         masm.movl(length, result);
 401     }
 402 
 403     /**
 404      * Emits code to compare the remaining 1 to 4 bytes.
 405      */
 406     private void emitTailCompares(AMD64MacroAssembler masm, Register result, Register array1, Register array2, Register length, Label trueLabel, Label falseLabel) {
 407         Label compare2Bytes = new Label();
 408         Label compare1Byte = new Label();
 409 
 410         Register temp = asRegister(temp4);
 411 
 412         if (kind.getByteCount() <= 4) {
 413             // Compare trailing 4 bytes, if any.
 414             masm.testl(result, 4);
 415             masm.jccb(ConditionFlag.Zero, compare2Bytes);
 416             masm.movl(temp, new AMD64Address(array1, 0));
 417             masm.cmpl(temp, new AMD64Address(array2, 0));
 418             if (kind == JavaKind.Float) {
 419                 masm.jccb(ConditionFlag.Equal, trueLabel);
 420                 emitFloatCompare(masm, array1, array2, Register.None, 0, falseLabel, true);
 421                 masm.jmpb(trueLabel);
 422             } else {
 423                 masm.jccb(ConditionFlag.NotEqual, falseLabel);
 424             }
 425             if (kind.getByteCount() <= 2) {
 426                 // Move array pointers forward.
 427                 masm.leaq(array1, new AMD64Address(array1, 4));
 428                 masm.leaq(array2, new AMD64Address(array2, 4));
 429 
 430                 // Compare trailing 2 bytes, if any.
 431                 masm.bind(compare2Bytes);
 432                 masm.testl(result, 2);
 433                 masm.jccb(ConditionFlag.Zero, compare1Byte);
 434                 masm.movzwl(temp, new AMD64Address(array1, 0));
 435                 masm.movzwl(length, new AMD64Address(array2, 0));
 436                 masm.cmpl(temp, length);
 437                 masm.jccb(ConditionFlag.NotEqual, falseLabel);
 438 
 439                 // The one-byte tail compare is only required for boolean and byte arrays.
 440                 if (kind.getByteCount() <= 1) {
 441                     // Move array pointers forward before we compare the last trailing byte.
 442                     masm.leaq(array1, new AMD64Address(array1, 2));
 443                     masm.leaq(array2, new AMD64Address(array2, 2));
 444 
 445                     // Compare trailing byte, if any.
 446                     masm.bind(compare1Byte);
 447                     masm.testl(result, 1);
 448                     masm.jccb(ConditionFlag.Zero, trueLabel);
 449                     masm.movzbl(temp, new AMD64Address(array1, 0));
 450                     masm.movzbl(length, new AMD64Address(array2, 0));
 451                     masm.cmpl(temp, length);
 452                     masm.jccb(ConditionFlag.NotEqual, falseLabel);
 453                 } else {
 454                     masm.bind(compare1Byte);
 455                 }
 456             } else {
 457                 masm.bind(compare2Bytes);
 458             }
 459         }
 460     }
 461 
 462     /**
 463      * Emits code to fall through if {@code src} is NaN, otherwise jump to {@code branchOrdered}.
 464      */
 465     private void emitNaNCheck(AMD64MacroAssembler masm, AMD64Address src, Label branchIfNonNaN) {
 466         assert kind.isNumericFloat();
 467         Register tempXMMReg = asRegister(tempXMM);
 468         if (kind == JavaKind.Float) {
 469             masm.movflt(tempXMMReg, src);
 470         } else {
 471             masm.movdbl(tempXMMReg, src);
 472         }
 473         SSEOp.UCOMIS.emit(masm, kind == JavaKind.Float ? OperandSize.PS : OperandSize.PD, tempXMMReg, tempXMMReg);
 474         masm.jcc(ConditionFlag.NoParity, branchIfNonNaN);
 475     }
 476 
 477     /**
 478      * Emits code to compare if two floats are bitwise equal or both NaN.
 479      */
 480     private void emitFloatCompare(AMD64MacroAssembler masm, Register base1, Register base2, Register index, int offset, Label falseLabel, boolean skipBitwiseCompare) {
 481         AMD64Address address1 = new AMD64Address(base1, index, Scale.Times1, offset);
 482         AMD64Address address2 = new AMD64Address(base2, index, Scale.Times1, offset);
 483 
 484         Label bitwiseEqual = new Label();
 485 
 486         if (!skipBitwiseCompare) {
 487             // Bitwise compare
 488             Register temp = asRegister(temp4);
 489 
 490             if (kind == JavaKind.Float) {
 491                 masm.movl(temp, address1);
 492                 masm.cmpl(temp, address2);
 493             } else {
 494                 masm.movq(temp, address1);
 495                 masm.cmpq(temp, address2);
 496             }
 497             masm.jccb(ConditionFlag.Equal, bitwiseEqual);
 498         }
 499 
 500         emitNaNCheck(masm, address1, falseLabel);
 501         emitNaNCheck(masm, address2, falseLabel);
 502 
 503         masm.bind(bitwiseEqual);
 504     }
 505 
 506     /**
 507      * Emits code to compare float equality within a range.
 508      */
 509     private void emitFloatCompareWithinRange(CompilationResultBuilder crb, AMD64MacroAssembler masm, Register base1, Register base2, Register index, int offset, Label falseLabel, int range) {
 510         assert kind.isNumericFloat();
 511         Label loop = new Label();
 512         Register i = asRegister(temp5);
 513 
 514         masm.movq(i, range);
 515         masm.negq(i);
 516         // Align the main loop
 517         masm.align(crb.target.wordSize * 2);
 518         masm.bind(loop);
 519         emitFloatCompare(masm, base1, base2, index, offset, falseLabel, kind.getByteCount() == range);
 520         masm.addq(index, kind.getByteCount());
 521         masm.addq(i, kind.getByteCount());
 522         masm.jccb(ConditionFlag.NotZero, loop);
 523         // Floats within the range are equal, revert change to the register index
 524         masm.subq(index, range);
 525     }
 526 
 527     private static final Unsafe UNSAFE = initUnsafe();
 528 
 529     private static Unsafe initUnsafe() {
 530         try {
 531             return Unsafe.getUnsafe();
 532         } catch (SecurityException se) {
 533             try {
 534                 Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
 535                 theUnsafe.setAccessible(true);
 536                 return (Unsafe) theUnsafe.get(Unsafe.class);
 537             } catch (Exception e) {
 538                 throw new RuntimeException("exception while trying to get Unsafe", e);
 539             }
 540         }
 541     }
 542 }