changeset 13973:faa6fda7ee36

added Arrays.equals substitutions
author twisti
date Tue, 18 Feb 2014 21:55:26 -0800
parents 28f560605e77
children 4ab3f98d724a
files graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64LIRGenerator.java graal/com.oracle.graal.compiler.hsail/src/com/oracle/graal/compiler/hsail/HSAILLIRGenerator.java graal/com.oracle.graal.compiler.ptx/src/com/oracle/graal/compiler/ptx/PTXLIRGenerator.java graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/LIRGenerator.java graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64ArrayEqualsOp.java graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64CharArrayEqualsOp.java graal/com.oracle.graal.replacements.amd64/src/com/oracle/graal/replacements/amd64/AMD64Substitutions.java graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/ArraysSubstitutionsTest.java graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/StringSubstitutionsTest.java graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ArraysSubstitutions.java graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/StringSubstitutions.java graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/ArrayEqualsNode.java graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/CharArrayEqualsNode.java graal/com.oracle.graal.test/src/com/oracle/graal/test/GraalTest.java
diffstat 15 files changed, 1021 insertions(+), 299 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64LIRGenerator.java	Tue Feb 18 15:04:47 2014 -0800
+++ b/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64LIRGenerator.java	Tue Feb 18 21:55:26 2014 -0800
@@ -974,8 +974,8 @@
     }
 
     @Override
-    public void emitCharArrayEquals(Variable result, Value array1, Value array2, Value length) {
-        append(new AMD64CharArrayEqualsOp(this, result, array1, array2, asAllocatable(length)));
+    public void emitArrayEquals(Kind kind, Variable result, Value array1, Value array2, Value length) {
+        append(new AMD64ArrayEqualsOp(this, kind, result, array1, array2, asAllocatable(length)));
     }
 
     @Override
--- a/graal/com.oracle.graal.compiler.hsail/src/com/oracle/graal/compiler/hsail/HSAILLIRGenerator.java	Tue Feb 18 15:04:47 2014 -0800
+++ b/graal/com.oracle.graal.compiler.hsail/src/com/oracle/graal/compiler/hsail/HSAILLIRGenerator.java	Tue Feb 18 21:55:26 2014 -0800
@@ -734,7 +734,7 @@
     }
 
     @Override
-    public void emitCharArrayEquals(Variable result, Value array1, Value array2, Value length) {
+    public void emitArrayEquals(Kind kind, Variable result, Value array1, Value array2, Value length) {
         // TODO Auto-generated method stub
         throw GraalInternalError.unimplemented();
     }
--- a/graal/com.oracle.graal.compiler.ptx/src/com/oracle/graal/compiler/ptx/PTXLIRGenerator.java	Tue Feb 18 15:04:47 2014 -0800
+++ b/graal/com.oracle.graal.compiler.ptx/src/com/oracle/graal/compiler/ptx/PTXLIRGenerator.java	Tue Feb 18 21:55:26 2014 -0800
@@ -775,7 +775,7 @@
     }
 
     @Override
-    public void emitCharArrayEquals(Variable result, Value array1, Value array2, Value length) {
+    public void emitArrayEquals(Kind kind, Variable result, Value array1, Value array2, Value length) {
         // TODO Auto-generated method stub
         throw GraalInternalError.unimplemented();
     }
--- a/graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java	Tue Feb 18 15:04:47 2014 -0800
+++ b/graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java	Tue Feb 18 21:55:26 2014 -0800
@@ -443,7 +443,7 @@
     }
 
     @Override
-    public void emitCharArrayEquals(Variable result, Value array1, Value array2, Value length) {
+    public void emitArrayEquals(Kind kind, Variable result, Value array1, Value array2, Value length) {
         // TODO Auto-generated method stub
         throw GraalInternalError.unimplemented();
     }
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/LIRGenerator.java	Tue Feb 18 15:04:47 2014 -0800
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/LIRGenerator.java	Tue Feb 18 21:55:26 2014 -0800
@@ -1026,5 +1026,5 @@
 
     public abstract void emitByteSwap(Variable result, Value operand);
 
-    public abstract void emitCharArrayEquals(Variable result, Value array1, Value array2, Value length);
+    public abstract void emitArrayEquals(Kind kind, Variable result, Value array1, Value array2, Value length);
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64ArrayEqualsOp.java	Tue Feb 18 21:55:26 2014 -0800
@@ -0,0 +1,295 @@
+/*
+ * Copyright (c) 2013, 2014, 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 com.oracle.graal.lir.amd64;
+
+import static com.oracle.graal.api.code.ValueUtil.*;
+import static com.oracle.graal.graph.UnsafeAccess.*;
+import static com.oracle.graal.lir.LIRInstruction.OperandFlag.*;
+
+import java.lang.reflect.*;
+
+import com.oracle.graal.amd64.*;
+import com.oracle.graal.amd64.AMD64.*;
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.asm.*;
+import com.oracle.graal.asm.amd64.*;
+import com.oracle.graal.asm.amd64.AMD64Address.Scale;
+import com.oracle.graal.asm.amd64.AMD64Assembler.*;
+import com.oracle.graal.lir.*;
+import com.oracle.graal.lir.asm.*;
+import com.oracle.graal.nodes.spi.*;
+
+/**
+ * Emits code which compares two arrays of the same length. If the CPU supports any vector
+ * instructions specialized code is emitted to leverage these instructions.
+ */
+@Opcode("ARRAY_EQUALS")
+public class AMD64ArrayEqualsOp extends AMD64LIRInstruction {
+
+    private final Kind 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 vectorTemp1;
+    @Temp({REG}) protected Value vectorTemp2;
+
+    public AMD64ArrayEqualsOp(LIRGeneratorTool tool, Kind kind, Value result, Value array1, Value array2, Value length) {
+        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(tool.target().wordKind);
+        this.temp2 = tool.newVariable(tool.target().wordKind);
+        this.temp3 = tool.newVariable(tool.target().wordKind);
+        this.temp4 = tool.newVariable(tool.target().wordKind);
+
+        // We only need the vector temporaries if we generate SSE code.
+        if (supportsSSE41(tool.target())) {
+            this.vectorTemp1 = tool.newVariable(Kind.Double);
+            this.vectorTemp2 = tool.newVariable(Kind.Double);
+        }
+    }
+
+    @Override
+    public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler 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.leaq(array1, new AMD64Address(asRegister(array1Value), arrayBaseOffset));
+        masm.leaq(array2, new AMD64Address(asRegister(array2Value), arrayBaseOffset));
+
+        // Get array length in bytes.
+        masm.imull(length, asRegister(lengthValue), arrayIndexScale);
+        masm.movl(result, length); // copy
+
+        if (supportsSSE41(crb.target)) {
+            emitSSE41Compare(crb, masm, result, array1, array2, length, trueLabel, falseLabel);
+        }
+
+        emit8ByteCompare(crb, masm, result, array1, array2, length, trueLabel, falseLabel);
+        emitTailCompares(masm, result, array1, array2, length, trueLabel, falseLabel);
+
+        // Return true
+        masm.bind(trueLabel);
+        masm.movl(result, 1);
+        masm.jmpb(done);
+
+        // Return false
+        masm.bind(falseLabel);
+        masm.xorl(result, result);
+
+        // That's it
+        masm.bind(done);
+    }
+
+    /**
+     * Returns if the underlying AMD64 architecture supports SSE 4.1 instructions.
+     * 
+     * @param target target description of the underlying architecture
+     * @return true if the underlying architecture supports SSE 4.1
+     */
+    private static boolean supportsSSE41(TargetDescription target) {
+        AMD64 arch = (AMD64) target.arch;
+        return arch.getFeatures().contains(CPUFeature.SSE4_1);
+    }
+
+    /**
+     * Vector size used in {@link #emitSSE41Compare}.
+     */
+    private static final int SSE4_1_VECTOR_SIZE = 16;
+
+    /**
+     * Emits code that uses SSE4.1 128-bit (16-byte) vector compares.
+     */
+    private void emitSSE41Compare(CompilationResultBuilder crb, AMD64MacroAssembler masm, Register result, Register array1, Register array2, Register length, Label trueLabel, Label falseLabel) {
+        assert supportsSSE41(crb.target);
+
+        Register vector1 = asDoubleReg(vectorTemp1);
+        Register vector2 = asDoubleReg(vectorTemp2);
+
+        Label loop = new Label();
+        Label compareTail = new Label();
+
+        // Compare 16-byte vectors
+        masm.andl(result, SSE4_1_VECTOR_SIZE - 1); // tail count (in bytes)
+        masm.andl(length, ~(SSE4_1_VECTOR_SIZE - 1)); // vector count (in bytes)
+        masm.jccb(ConditionFlag.Zero, compareTail);
+
+        masm.leaq(array1, new AMD64Address(array1, length, Scale.Times1, 0));
+        masm.leaq(array2, new AMD64Address(array2, length, Scale.Times1, 0));
+        masm.negq(length);
+
+        // Align the main loop
+        masm.align(crb.target.wordSize * 2);
+        masm.bind(loop);
+        masm.movdqu(vector1, new AMD64Address(array1, length, Scale.Times1, 0));
+        masm.movdqu(vector2, new AMD64Address(array2, length, Scale.Times1, 0));
+        masm.pxor(vector1, vector2);
+        masm.ptest(vector1, vector1);
+        masm.jcc(ConditionFlag.NotZero, falseLabel);
+        masm.addq(length, SSE4_1_VECTOR_SIZE);
+        masm.jcc(ConditionFlag.NotZero, loop);
+
+        masm.testl(result, result);
+        masm.jcc(ConditionFlag.Zero, trueLabel);
+
+        /*
+         * Compare the remaining bytes with an unaligned memory load aligned to the end of the
+         * array.
+         */
+        masm.movdqu(vector1, new AMD64Address(array1, result, Scale.Times1, -SSE4_1_VECTOR_SIZE));
+        masm.movdqu(vector2, new AMD64Address(array2, result, Scale.Times1, -SSE4_1_VECTOR_SIZE));
+        masm.pxor(vector1, vector2);
+        masm.ptest(vector1, vector1);
+        masm.jcc(ConditionFlag.NotZero, falseLabel);
+        masm.jmp(trueLabel);
+
+        masm.bind(compareTail);
+        masm.movl(length, result);
+    }
+
+    /**
+     * Vector size used in {@link #emit8ByteCompare}.
+     */
+    private static final int VECTOR_SIZE = 8;
+
+    /**
+     * Emits code that uses 8-byte vector compares.
+     */
+    private void emit8ByteCompare(CompilationResultBuilder crb, AMD64MacroAssembler masm, Register result, Register array1, Register array2, Register length, Label trueLabel, Label falseLabel) {
+        Label loop = new Label();
+        Label compareTail = new Label();
+
+        Register temp = asRegister(temp4);
+
+        masm.andl(result, VECTOR_SIZE - 1); // tail count (in bytes)
+        masm.andl(length, ~(VECTOR_SIZE - 1));  // vector count (in bytes)
+        masm.jccb(ConditionFlag.Zero, compareTail);
+
+        masm.leaq(array1, new AMD64Address(array1, length, Scale.Times1, 0));
+        masm.leaq(array2, new AMD64Address(array2, length, Scale.Times1, 0));
+        masm.negq(length);
+
+        // Align the main loop
+        masm.align(crb.target.wordSize * 2);
+        masm.bind(loop);
+        masm.movq(temp, new AMD64Address(array1, length, Scale.Times1, 0));
+        masm.cmpq(temp, new AMD64Address(array2, length, Scale.Times1, 0));
+        masm.jccb(ConditionFlag.NotEqual, falseLabel);
+        masm.addq(length, VECTOR_SIZE);
+        masm.jccb(ConditionFlag.NotZero, loop);
+
+        masm.testl(result, result);
+        masm.jccb(ConditionFlag.Zero, trueLabel);
+
+        /*
+         * Compare the remaining bytes with an unaligned memory load aligned to the end of the
+         * array.
+         */
+        masm.movq(temp, new AMD64Address(array1, result, Scale.Times1, -VECTOR_SIZE));
+        masm.cmpq(temp, new AMD64Address(array2, result, Scale.Times1, -VECTOR_SIZE));
+        masm.jccb(ConditionFlag.NotEqual, falseLabel);
+        masm.jmpb(trueLabel);
+
+        masm.bind(compareTail);
+        masm.movl(length, result);
+    }
+
+    /**
+     * Emits code to compare the remaining 1 to 4 bytes.
+     */
+    private void emitTailCompares(AMD64MacroAssembler masm, Register result, Register array1, Register array2, Register length, Label trueLabel, Label falseLabel) {
+        Label compare2Bytes = new Label();
+        Label compare1Byte = new Label();
+
+        Register temp = asRegister(temp4);
+
+        if (kind.getByteCount() <= 4) {
+            // Compare trailing 4 bytes, if any.
+            masm.testl(result, 4);
+            masm.jccb(ConditionFlag.Zero, compare2Bytes);
+            masm.movl(temp, new AMD64Address(array1, 0));
+            masm.cmpl(temp, new AMD64Address(array2, 0));
+            masm.jccb(ConditionFlag.NotEqual, falseLabel);
+
+            if (kind.getByteCount() <= 2) {
+                // Move array pointers forward.
+                masm.leaq(array1, new AMD64Address(array1, 4));
+                masm.leaq(array2, new AMD64Address(array2, 4));
+
+                // Compare trailing 2 bytes, if any.
+                masm.bind(compare2Bytes);
+                masm.testl(result, 2);
+                masm.jccb(ConditionFlag.Zero, compare1Byte);
+                masm.movzwl(temp, new AMD64Address(array1, 0));
+                masm.movzwl(length, new AMD64Address(array2, 0));
+                masm.cmpl(temp, length);
+                masm.jccb(ConditionFlag.NotEqual, falseLabel);
+
+                // 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.leaq(array1, new AMD64Address(array1, 2));
+                    masm.leaq(array2, new AMD64Address(array2, 2));
+
+                    // Compare trailing byte, if any.
+                    masm.bind(compare1Byte);
+                    masm.testl(result, 1);
+                    masm.jccb(ConditionFlag.Zero, trueLabel);
+                    masm.movzbl(temp, new AMD64Address(array1, 0));
+                    masm.movzbl(length, new AMD64Address(array2, 0));
+                    masm.cmpl(temp, length);
+                    masm.jccb(ConditionFlag.NotEqual, falseLabel);
+                } else {
+                    masm.bind(compare1Byte);
+                }
+            } else {
+                masm.bind(compare2Bytes);
+            }
+        }
+    }
+}
--- a/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64CharArrayEqualsOp.java	Tue Feb 18 15:04:47 2014 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,216 +0,0 @@
-/*
- * Copyright (c) 2013, 2014, 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 com.oracle.graal.lir.amd64;
-
-import static com.oracle.graal.api.code.ValueUtil.*;
-import static com.oracle.graal.lir.LIRInstruction.OperandFlag.*;
-import sun.misc.*;
-
-import com.oracle.graal.amd64.*;
-import com.oracle.graal.amd64.AMD64.*;
-import com.oracle.graal.api.code.*;
-import com.oracle.graal.api.meta.*;
-import com.oracle.graal.asm.*;
-import com.oracle.graal.asm.amd64.*;
-import com.oracle.graal.asm.amd64.AMD64Address.Scale;
-import com.oracle.graal.asm.amd64.AMD64Assembler.*;
-import com.oracle.graal.lir.*;
-import com.oracle.graal.lir.asm.*;
-import com.oracle.graal.nodes.spi.*;
-
-/**
- * Emits code which compares two {@code char[]}. If the CPU supports any vector instructions
- * specialized code is emitted to leverage these instructions.
- */
-@Opcode("CHAR_ARRAY_EQUALS")
-public class AMD64CharArrayEqualsOp extends AMD64LIRInstruction {
-
-    @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 vectorTemp1;
-    @Temp({REG}) protected Value vectorTemp2;
-
-    public AMD64CharArrayEqualsOp(LIRGeneratorTool tool, Value result, Value array1, Value array2, Value length) {
-        this.resultValue = result;
-        this.array1Value = array1;
-        this.array2Value = array2;
-        this.lengthValue = length;
-
-        // Allocate some temporaries.
-        this.temp1 = tool.newVariable(tool.target().wordKind);
-        this.temp2 = tool.newVariable(tool.target().wordKind);
-        this.temp3 = tool.newVariable(tool.target().wordKind);
-        this.temp4 = tool.newVariable(tool.target().wordKind);
-
-        // We only need the vector temporaries if we generate SSE code.
-        if (supportsSSE41(tool.target())) {
-            this.vectorTemp1 = tool.newVariable(Kind.Double);
-            this.vectorTemp2 = tool.newVariable(Kind.Double);
-        }
-    }
-
-    /**
-     * Returns if the underlying AMD64 architecture supports SSE 4.1 instructions.
-     * 
-     * @param target target description of the underlying architecture
-     * @return true if the underlying architecture supports SSE 4.1
-     */
-    private static boolean supportsSSE41(TargetDescription target) {
-        AMD64 arch = (AMD64) target.arch;
-        return arch.getFeatures().contains(CPUFeature.SSE4_1);
-    }
-
-    @Override
-    public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler 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();
-
-        // Load array base addresses.
-        masm.leaq(array1, new AMD64Address(asRegister(array1Value), Unsafe.ARRAY_CHAR_BASE_OFFSET));
-        masm.leaq(array2, new AMD64Address(asRegister(array2Value), Unsafe.ARRAY_CHAR_BASE_OFFSET));
-
-        masm.movq(length, asRegister(lengthValue));
-        masm.shll(length, 1); // get length in bytes
-        masm.movl(result, length); // copy
-
-        if (supportsSSE41(crb.target)) {
-            emitSSE41Compare(crb, masm, result, array1, array2, length, falseLabel, trueLabel);
-            // Fall-through to tail compare.
-        }
-        emit4ByteCompare(crb, masm, result, array1, array2, length, falseLabel, trueLabel);
-    }
-
-    /**
-     * Vector size used in {@link #emit4ByteCompare}.
-     */
-    private static final int VECTOR_SIZE = 4;
-
-    /**
-     * Emits code that uses 4-byte vector compares.
-     */
-    private void emit4ByteCompare(CompilationResultBuilder crb, AMD64MacroAssembler masm, Register result, Register array1, Register array2, Register length, Label falseLabel, Label trueLabel) {
-        Label compareVectors = new Label();
-        Label compareChar = new Label();
-        Label done = new Label();
-        Register temp = asRegister(temp4);
-
-        masm.andl(length, 0xfffffffc); // vector count (in bytes)
-        masm.jccb(ConditionFlag.Zero, compareChar);
-
-        masm.leaq(array1, new AMD64Address(array1, length, Scale.Times1, 0));
-        masm.leaq(array2, new AMD64Address(array2, length, Scale.Times1, 0));
-        masm.negq(length);
-
-        // Align the main loop
-        masm.align(crb.target.wordSize * 2);
-        masm.bind(compareVectors);
-        masm.movl(temp, new AMD64Address(array1, length, Scale.Times1, 0));
-        masm.cmpl(temp, new AMD64Address(array2, length, Scale.Times1, 0));
-        masm.jccb(ConditionFlag.NotEqual, falseLabel);
-        masm.addq(length, VECTOR_SIZE);
-        masm.jcc(ConditionFlag.NotZero, compareVectors);
-
-        // Compare trailing char (final 2 bytes), if any
-        masm.bind(compareChar);
-        masm.testl(result, 0x2); // tail char
-        masm.jccb(ConditionFlag.Zero, trueLabel);
-        masm.movzwl(temp, new AMD64Address(array1, 0));
-        masm.movzwl(length, new AMD64Address(array2, 0));
-        masm.cmpl(temp, length);
-        masm.jccb(ConditionFlag.NotEqual, falseLabel);
-
-        masm.bind(trueLabel);
-        masm.movl(result, 1); // return true
-        masm.jmpb(done);
-
-        masm.bind(falseLabel);
-        masm.xorl(result, result); // return false
-
-        // That's it
-        masm.bind(done);
-    }
-
-    /**
-     * Vector size used in {@link #emitSSE41Compare}.
-     */
-    private static final int SSE4_1_VECTOR_SIZE = 16;
-
-    /**
-     * Emits code that uses SSE4.1 128-bit (16-byte) vector compares.
-     */
-    private void emitSSE41Compare(CompilationResultBuilder crb, AMD64MacroAssembler masm, Register result, Register array1, Register array2, Register length, Label falseLabel, Label trueLabel) {
-        assert supportsSSE41(crb.target);
-
-        Register vector1 = asDoubleReg(vectorTemp1);
-        Register vector2 = asDoubleReg(vectorTemp2);
-
-        Label compareWideVectors = new Label();
-        Label compareTail = new Label();
-
-        // Compare 16-byte vectors
-        masm.andl(result, 0x0000000e); // tail count (in bytes)
-        masm.andl(length, 0xfffffff0); // vector count (in bytes)
-        masm.jccb(ConditionFlag.Zero, compareTail);
-
-        masm.leaq(array1, new AMD64Address(array1, length, Scale.Times1, 0));
-        masm.leaq(array2, new AMD64Address(array2, length, Scale.Times1, 0));
-        masm.negq(length);
-
-        // Align the main loop
-        masm.align(crb.target.wordSize * 2);
-        masm.bind(compareWideVectors);
-        masm.movdqu(vector1, new AMD64Address(array1, length, Scale.Times1, 0));
-        masm.movdqu(vector2, new AMD64Address(array2, length, Scale.Times1, 0));
-        masm.pxor(vector1, vector2);
-
-        masm.ptest(vector1, vector1);
-        masm.jccb(ConditionFlag.NotZero, falseLabel);
-        masm.addq(length, SSE4_1_VECTOR_SIZE);
-        masm.jcc(ConditionFlag.NotZero, compareWideVectors);
-
-        masm.testl(result, result);
-        masm.jccb(ConditionFlag.Zero, trueLabel);
-
-        masm.movdqu(vector1, new AMD64Address(array1, result, Scale.Times1, -SSE4_1_VECTOR_SIZE));
-        masm.movdqu(vector2, new AMD64Address(array2, result, Scale.Times1, -SSE4_1_VECTOR_SIZE));
-        masm.pxor(vector1, vector2);
-
-        masm.ptest(vector1, vector1);
-        masm.jccb(ConditionFlag.NotZero, falseLabel);
-        masm.jmpb(trueLabel);
-
-        masm.bind(compareTail); // length is zero
-        masm.movl(length, result);
-    }
-}
--- a/graal/com.oracle.graal.replacements.amd64/src/com/oracle/graal/replacements/amd64/AMD64Substitutions.java	Tue Feb 18 15:04:47 2014 -0800
+++ b/graal/com.oracle.graal.replacements.amd64/src/com/oracle/graal/replacements/amd64/AMD64Substitutions.java	Tue Feb 18 21:55:26 2014 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2014, 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
@@ -39,6 +39,7 @@
 
     public void registerReplacements(MetaAccessProvider metaAccess, LoweringProvider lowerer, Replacements replacements, TargetDescription target) {
         if (Intrinsify.getValue()) {
+            replacements.registerSubstitutions(ArraysSubstitutions.class);
             replacements.registerSubstitutions(StringSubstitutions.class);
         }
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/ArraysSubstitutionsTest.java	Tue Feb 18 21:55:26 2014 -0800
@@ -0,0 +1,332 @@
+/*
+ * Copyright (c) 2014, 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 com.oracle.graal.replacements.test;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.junit.*;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.replacements.*;
+import com.oracle.graal.replacements.nodes.*;
+
+/**
+ * Tests {@link ArraysSubstitutions}.
+ */
+public class ArraysSubstitutionsTest extends MethodSubstitutionTest {
+
+    private static Object executeVarargsSafe(InstalledCode code, Object... args) {
+        try {
+            return code.executeVarargs(args);
+        } catch (InvalidInstalledCodeException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static Object invokeSafe(Method method, Object receiver, Object... args) {
+        method.setAccessible(true);
+        try {
+            Object result = method.invoke(receiver, args);
+            return result;
+        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public void testSubstitution(String testMethodName, Class<?> intrinsicClass, Class<?> holder, String methodName, Class<?>[] parameterTypes, boolean optional, Object[] args1, Object[] args2) {
+        Method realMethod = getMethod(holder, methodName, parameterTypes);
+        Method testMethod = getMethod(testMethodName);
+        StructuredGraph graph = test(testMethodName);
+
+        // Check to see if the resulting graph contains the expected node
+        StructuredGraph replacement = getReplacements().getMethodSubstitution(getMetaAccess().lookupJavaMethod(realMethod));
+        if (replacement == null && !optional) {
+            assertInGraph(graph, intrinsicClass);
+        }
+
+        // Force compilation
+        InstalledCode code = getCode(getMetaAccess().lookupJavaMethod(testMethod), parse(testMethod));
+        assert optional || code != null;
+
+        for (int i = 0; i < args1.length; i++) {
+            Object arg1 = args1[i];
+            Object arg2 = args2[i];
+            // Verify that the original method and the substitution produce the same value
+            assertEquals(invokeSafe(testMethod, null, arg1, arg2), invokeSafe(realMethod, null, arg1, arg2));
+            // Verify that the generated code and the original produce the same value
+            assertEquals(executeVarargsSafe(code, arg1, arg2), invokeSafe(realMethod, null, arg1, arg2));
+        }
+    }
+
+    private static final int N = 10;
+
+    @Test
+    public void testEqualsBoolean() {
+        Object[] args1 = new Object[N];
+        Object[] args2 = new Object[N];
+        int n = 0;
+
+        // equal arrays
+        for (int i = 0; i < N / 2; i++, n++) {
+            args1[n] = new boolean[i];
+            args2[n] = new boolean[i];
+        }
+
+        // non-equal arrays
+        for (int i = 0; i < N / 2; i++, n++) {
+            boolean[] a2 = new boolean[i];
+            if (i > 0) {
+                a2[i - 1] = true;
+            }
+            args1[n] = new boolean[i];
+            args2[n] = a2;
+        }
+        Class<?>[] parameterTypes = new Class<?>[]{boolean[].class, boolean[].class};
+        testSubstitution("arraysEqualsBoolean", ArrayEqualsNode.class, Arrays.class, "equals", parameterTypes, false, args1, args2);
+    }
+
+    @SuppressWarnings("all")
+    public static boolean arraysEqualsBoolean(boolean[] a, boolean[] b) {
+        return Arrays.equals(a, b);
+    }
+
+    @Test
+    public void testEqualsByte() {
+        Object[] args1 = new Object[N];
+        Object[] args2 = new Object[N];
+        int n = 0;
+
+        // equal arrays
+        for (int i = 0; i < N / 2; i++, n++) {
+            args1[n] = new byte[i];
+            args2[n] = new byte[i];
+        }
+
+        // non-equal arrays
+        for (int i = 0; i < N / 2; i++, n++) {
+            byte[] a2 = new byte[i];
+            if (i > 0) {
+                a2[i - 1] = 1;
+            }
+            args1[n] = new byte[i];
+            args2[n] = a2;
+        }
+
+        Class<?>[] parameterTypes = new Class<?>[]{byte[].class, byte[].class};
+        testSubstitution("arraysEqualsByte", ArrayEqualsNode.class, Arrays.class, "equals", parameterTypes, false, args1, args2);
+    }
+
+    @SuppressWarnings("all")
+    public static boolean arraysEqualsByte(byte[] a, byte[] b) {
+        return Arrays.equals(a, b);
+    }
+
+    @Test
+    public void testEqualsChar() {
+        Object[] args1 = new Object[N];
+        Object[] args2 = new Object[N];
+        int n = 0;
+
+        // equal arrays
+        for (int i = 0; i < N / 2; i++, n++) {
+            args1[n] = new char[i];
+            args2[n] = new char[i];
+        }
+
+        // non-equal arrays
+        for (int i = 0; i < N / 2; i++, n++) {
+            char[] a2 = new char[i];
+            if (i > 0) {
+                a2[i - 1] = 1;
+            }
+            args1[n] = new char[i];
+            args2[n] = a2;
+        }
+
+        Class<?>[] parameterTypes = new Class<?>[]{char[].class, char[].class};
+        testSubstitution("arraysEqualsChar", ArrayEqualsNode.class, Arrays.class, "equals", parameterTypes, false, args1, args2);
+    }
+
+    @SuppressWarnings("all")
+    public static boolean arraysEqualsChar(char[] a, char[] b) {
+        return Arrays.equals(a, b);
+    }
+
+    @Test
+    public void testEqualsShort() {
+        Object[] args1 = new Object[N];
+        Object[] args2 = new Object[N];
+        int n = 0;
+
+        // equal arrays
+        for (int i = 0; i < N / 2; i++, n++) {
+            args1[n] = new short[i];
+            args2[n] = new short[i];
+        }
+
+        // non-equal arrays
+        for (int i = 0; i < N / 2; i++, n++) {
+            short[] a2 = new short[i];
+            if (i > 0) {
+                a2[i - 1] = 1;
+            }
+            args1[n] = new short[i];
+            args2[n] = a2;
+        }
+
+        Class<?>[] parameterTypes = new Class<?>[]{short[].class, short[].class};
+        testSubstitution("arraysEqualsShort", ArrayEqualsNode.class, Arrays.class, "equals", parameterTypes, false, args1, args2);
+    }
+
+    @SuppressWarnings("all")
+    public static boolean arraysEqualsShort(short[] a, short[] b) {
+        return Arrays.equals(a, b);
+    }
+
+    @Test
+    public void testEqualsInt() {
+        Object[] args1 = new Object[N];
+        Object[] args2 = new Object[N];
+        int n = 0;
+
+        // equal arrays
+        for (int i = 0; i < N / 2; i++, n++) {
+            args1[n] = new int[i];
+            args2[n] = new int[i];
+        }
+
+        // non-equal arrays
+        for (int i = 0; i < N / 2; i++, n++) {
+            int[] a2 = new int[i];
+            if (i > 0) {
+                a2[i - 1] = 1;
+            }
+            args1[n] = new int[i];
+            args2[n] = a2;
+        }
+
+        Class<?>[] parameterTypes = new Class<?>[]{int[].class, int[].class};
+        testSubstitution("arraysEqualsInt", ArrayEqualsNode.class, Arrays.class, "equals", parameterTypes, false, args1, args2);
+    }
+
+    @SuppressWarnings("all")
+    public static boolean arraysEqualsInt(int[] a, int[] b) {
+        return Arrays.equals(a, b);
+    }
+
+    @Test
+    public void testEqualsLong() {
+        Object[] args1 = new Object[N];
+        Object[] args2 = new Object[N];
+        int n = 0;
+
+        // equal arrays
+        for (int i = 0; i < N / 2; i++, n++) {
+            args1[n] = new long[i];
+            args2[n] = new long[i];
+        }
+
+        // non-equal arrays
+        for (int i = 0; i < N / 2; i++, n++) {
+            long[] a2 = new long[i];
+            if (i > 0) {
+                a2[i - 1] = 1;
+            }
+            args1[n] = new long[i];
+            args2[n] = a2;
+        }
+
+        Class<?>[] parameterTypes = new Class<?>[]{long[].class, long[].class};
+        testSubstitution("arraysEqualsLong", ArrayEqualsNode.class, Arrays.class, "equals", parameterTypes, false, args1, args2);
+    }
+
+    @SuppressWarnings("all")
+    public static boolean arraysEqualsLong(long[] a, long[] b) {
+        return Arrays.equals(a, b);
+    }
+
+    @Test
+    public void testEqualsFloat() {
+        Object[] args1 = new Object[N];
+        Object[] args2 = new Object[N];
+        int n = 0;
+
+        // equal arrays
+        for (int i = 0; i < N / 2; i++, n++) {
+            args1[n] = new float[i];
+            args2[n] = new float[i];
+        }
+
+        // non-equal arrays
+        for (int i = 0; i < N / 2; i++, n++) {
+            float[] a2 = new float[i];
+            if (i > 0) {
+                a2[i - 1] = 1;
+            }
+            args1[n] = new float[i];
+            args2[n] = a2;
+        }
+
+        Class<?>[] parameterTypes = new Class<?>[]{float[].class, float[].class};
+        testSubstitution("arraysEqualsFloat", ArrayEqualsNode.class, Arrays.class, "equals", parameterTypes, false, args1, args2);
+    }
+
+    @SuppressWarnings("all")
+    public static boolean arraysEqualsFloat(float[] a, float[] b) {
+        return Arrays.equals(a, b);
+    }
+
+    @Test
+    public void testEqualsDouble() {
+        Object[] args1 = new Object[N];
+        Object[] args2 = new Object[N];
+        int n = 0;
+
+        // equal arrays
+        for (int i = 0; i < N / 2; i++, n++) {
+            args1[n] = new double[i];
+            args2[n] = new double[i];
+        }
+
+        // non-equal arrays
+        for (int i = 0; i < N / 2; i++, n++) {
+            double[] a2 = new double[i];
+            if (i > 0) {
+                a2[i - 1] = 1;
+            }
+            args1[n] = new double[i];
+            args2[n] = a2;
+        }
+
+        Class<?>[] parameterTypes = new Class<?>[]{double[].class, double[].class};
+        testSubstitution("arraysEqualsDouble", ArrayEqualsNode.class, Arrays.class, "equals", parameterTypes, false, args1, args2);
+    }
+
+    @SuppressWarnings("all")
+    public static boolean arraysEqualsDouble(double[] a, double[] b) {
+        return Arrays.equals(a, b);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/StringSubstitutionsTest.java	Tue Feb 18 21:55:26 2014 -0800
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2014, 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 com.oracle.graal.replacements.test;
+
+import java.lang.reflect.*;
+
+import org.junit.*;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.replacements.*;
+import com.oracle.graal.replacements.nodes.*;
+
+/**
+ * Tests {@link StringSubstitutions}.
+ */
+public class StringSubstitutionsTest extends MethodSubstitutionTest {
+
+    private static Object executeVarargsSafe(InstalledCode code, Object... args) {
+        try {
+            return code.executeVarargs(args);
+        } catch (InvalidInstalledCodeException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static Object invokeSafe(Method method, Object receiver, Object... args) {
+        method.setAccessible(true);
+        try {
+            Object result = method.invoke(receiver, args);
+            return result;
+        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public void testSubstitution(String testMethodName, Class<?> intrinsicClass, Class<?> holder, String methodName, boolean optional, Object[] args1, Object[] args2) {
+        Method realMethod = getMethod(holder, methodName);
+        Method testMethod = getMethod(testMethodName);
+        StructuredGraph graph = test(testMethodName);
+
+        // Check to see if the resulting graph contains the expected node
+        StructuredGraph replacement = getReplacements().getMethodSubstitution(getMetaAccess().lookupJavaMethod(realMethod));
+        if (replacement == null && !optional) {
+            assertInGraph(graph, intrinsicClass);
+        }
+
+        // Force compilation
+        InstalledCode code = getCode(getMetaAccess().lookupJavaMethod(testMethod), parse(testMethod));
+        assert optional || code != null;
+
+        for (int i = 0; i < args1.length; i++) {
+            Object arg1 = args1[i];
+            Object arg2 = args2[i];
+            // Verify that the original method and the substitution produce the same value
+            assertEquals(invokeSafe(testMethod, null, arg1, arg2), invokeSafe(realMethod, arg1, arg2));
+            // Verify that the generated code and the original produce the same value
+            assertEquals(executeVarargsSafe(code, arg1, arg2), invokeSafe(realMethod, arg1, arg2));
+        }
+    }
+
+    @Test
+    public void testEquals() {
+        final int n = 1000;
+        Object[] args1 = new Object[n];
+        Object[] args2 = new Object[n];
+
+        // equal strings
+        String s1 = "";
+        String s2 = "";
+        for (int i = 0; i < n / 2; i++) {
+            args1[i] = s1;
+            args2[i] = s2;
+            s1 = s1 + "0";
+            s2 = s2 + "0";
+        }
+
+        // non-equal strings
+        s1 = "";
+        s2 = "";
+        for (int i = n / 2; i < n; i++) {
+            args1[i] = s1;
+            args2[i] = s2;
+            s2 = s1 + "1";
+            s1 = s1 + "0";
+        }
+
+        testSubstitution("stringEquals", ArrayEqualsNode.class, String.class, "equals", false, args1, args2);
+    }
+
+    @SuppressWarnings("all")
+    public static boolean stringEquals(String a, String b) {
+        return a.equals(b);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ArraysSubstitutions.java	Tue Feb 18 21:55:26 2014 -0800
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2013, 2014, 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 com.oracle.graal.replacements;
+
+import com.oracle.graal.api.replacements.*;
+import com.oracle.graal.replacements.nodes.*;
+
+/**
+ * Substitutions for {@link java.util.Arrays} methods.
+ */
+@ClassSubstitution(java.util.Arrays.class)
+public class ArraysSubstitutions {
+
+    @MethodSubstitution
+    public static boolean equals(boolean[] a, boolean[] a2) {
+        if (a == a2) {
+            return true;
+        }
+        if (a == null || a2 == null || a.length != a2.length) {
+            return false;
+        }
+        return ArrayEqualsNode.equals(a, a2, a.length);
+    }
+
+    @MethodSubstitution
+    public static boolean equals(byte[] a, byte[] a2) {
+        if (a == a2) {
+            return true;
+        }
+        if (a == null || a2 == null || a.length != a2.length) {
+            return false;
+        }
+        return ArrayEqualsNode.equals(a, a2, a.length);
+    }
+
+    @MethodSubstitution
+    public static boolean equals(char[] a, char[] a2) {
+        if (a == a2) {
+            return true;
+        }
+        if (a == null || a2 == null || a.length != a2.length) {
+            return false;
+        }
+        return ArrayEqualsNode.equals(a, a2, a.length);
+    }
+
+    @MethodSubstitution
+    public static boolean equals(short[] a, short[] a2) {
+        if (a == a2) {
+            return true;
+        }
+        if (a == null || a2 == null || a.length != a2.length) {
+            return false;
+        }
+        return ArrayEqualsNode.equals(a, a2, a.length);
+    }
+
+    @MethodSubstitution
+    public static boolean equals(int[] a, int[] a2) {
+        if (a == a2) {
+            return true;
+        }
+        if (a == null || a2 == null || a.length != a2.length) {
+            return false;
+        }
+        return ArrayEqualsNode.equals(a, a2, a.length);
+    }
+
+    @MethodSubstitution
+    public static boolean equals(long[] a, long[] a2) {
+        if (a == a2) {
+            return true;
+        }
+        if (a == null || a2 == null || a.length != a2.length) {
+            return false;
+        }
+        return ArrayEqualsNode.equals(a, a2, a.length);
+    }
+
+    @MethodSubstitution
+    public static boolean equals(float[] a, float[] a2) {
+        if (a == a2) {
+            return true;
+        }
+        if (a == null || a2 == null || a.length != a2.length) {
+            return false;
+        }
+        return ArrayEqualsNode.equals(a, a2, a.length);
+    }
+
+    @MethodSubstitution
+    public static boolean equals(double[] a, double[] a2) {
+        if (a == a2) {
+            return true;
+        }
+        if (a == null || a2 == null || a.length != a2.length) {
+            return false;
+        }
+        return ArrayEqualsNode.equals(a, a2, a.length);
+    }
+}
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/StringSubstitutions.java	Tue Feb 18 15:04:47 2014 -0800
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/StringSubstitutions.java	Tue Feb 18 21:55:26 2014 -0800
@@ -66,9 +66,9 @@
             return true;
         }
 
-        final char[] thisArray = (char[]) unsafe.getObject(thisString, valueOffset);
-        final char[] thatArray = (char[]) unsafe.getObject(thatString, valueOffset);
+        final char[] array1 = (char[]) unsafe.getObject(thisString, valueOffset);
+        final char[] array2 = (char[]) unsafe.getObject(thatString, valueOffset);
 
-        return CharArrayEqualsNode.equals(thisArray, thatArray, thisArray.length);
+        return ArrayEqualsNode.equals(array1, array2, array1.length);
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/ArrayEqualsNode.java	Tue Feb 18 21:55:26 2014 -0800
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2013, 2014, 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 com.oracle.graal.replacements.nodes;
+
+import java.util.*;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.compiler.gen.*;
+import com.oracle.graal.compiler.target.*;
+import com.oracle.graal.graph.*;
+import com.oracle.graal.graph.spi.*;
+import com.oracle.graal.lir.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.calc.*;
+import com.oracle.graal.nodes.type.*;
+
+/**
+ * Compares two arrays with the same length.
+ */
+public class ArrayEqualsNode extends FloatingNode implements LIRGenLowerable, Canonicalizable {
+
+    /** {@link Kind} of the arrays to compare. */
+    private final Kind kind;
+
+    /** One array to be tested for equality. */
+    @Input private ValueNode array1;
+
+    /** The other array to be tested for equality. */
+    @Input private ValueNode array2;
+
+    /** Length of both arrays. */
+    @Input private ValueNode length;
+
+    public ArrayEqualsNode(ValueNode array1, ValueNode array2, ValueNode length) {
+        super(StampFactory.forKind(Kind.Boolean));
+
+        assert array1.stamp().equals(array2.stamp());
+        ObjectStamp stamp = (ObjectStamp) array1.stamp();
+        ResolvedJavaType componentType = stamp.type().getComponentType();
+        this.kind = componentType.getKind();
+
+        this.array1 = array1;
+        this.array2 = array2;
+        this.length = length;
+    }
+
+    @Override
+    public Node canonical(CanonicalizerTool tool) {
+        if (!array1.isConstant() || !array2.isConstant()) {
+            return this;
+        }
+
+        Object a1 = array1.asConstant().asObject();
+        Object a2 = array2.asConstant().asObject();
+        boolean x;
+        switch (kind) {
+            case Boolean:
+                x = Arrays.equals((boolean[]) a1, (boolean[]) a2);
+                break;
+            case Byte:
+                x = Arrays.equals((byte[]) a1, (byte[]) a2);
+                break;
+            case Char:
+                x = Arrays.equals((char[]) a1, (char[]) a2);
+                break;
+            case Short:
+                x = Arrays.equals((short[]) a1, (short[]) a2);
+                break;
+            case Int:
+                x = Arrays.equals((int[]) a1, (int[]) a2);
+                break;
+            case Long:
+                x = Arrays.equals((long[]) a1, (long[]) a2);
+                break;
+            case Float:
+                x = Arrays.equals((float[]) a1, (float[]) a2);
+                break;
+            case Double:
+                x = Arrays.equals((double[]) a1, (double[]) a2);
+                break;
+            default:
+                throw GraalInternalError.shouldNotReachHere("unknown kind " + kind);
+        }
+        return ConstantNode.forBoolean(x, graph());
+    }
+
+    @NodeIntrinsic
+    public static native boolean equals(boolean[] array1, boolean[] array2, int length);
+
+    @NodeIntrinsic
+    public static native boolean equals(byte[] array1, byte[] array2, int length);
+
+    @NodeIntrinsic
+    public static native boolean equals(char[] array1, char[] array2, int length);
+
+    @NodeIntrinsic
+    public static native boolean equals(short[] array1, short[] array2, int length);
+
+    @NodeIntrinsic
+    public static native boolean equals(int[] array1, int[] array2, int length);
+
+    @NodeIntrinsic
+    public static native boolean equals(long[] array1, long[] array2, int length);
+
+    @NodeIntrinsic
+    public static native boolean equals(float[] array1, float[] array2, int length);
+
+    @NodeIntrinsic
+    public static native boolean equals(double[] array1, double[] array2, int length);
+
+    @Override
+    public void generate(LIRGenerator gen) {
+        Variable result = gen.newVariable(Kind.Boolean);
+        gen.emitArrayEquals(kind, result, gen.operand(array1), gen.operand(array2), gen.operand(length));
+        gen.setResult(this, result);
+    }
+}
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/CharArrayEqualsNode.java	Tue Feb 18 15:04:47 2014 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,73 +0,0 @@
-/*
- * Copyright (c) 2013, 2014, 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 com.oracle.graal.replacements.nodes;
-
-import java.util.*;
-
-import com.oracle.graal.api.meta.*;
-import com.oracle.graal.compiler.gen.*;
-import com.oracle.graal.compiler.target.*;
-import com.oracle.graal.graph.*;
-import com.oracle.graal.graph.spi.*;
-import com.oracle.graal.lir.*;
-import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.calc.*;
-import com.oracle.graal.nodes.type.*;
-
-/**
- * Compares two {@code char} arrays with the same length.
- */
-public class CharArrayEqualsNode extends FloatingNode implements LIRGenLowerable, Canonicalizable {
-
-    @Input private ValueNode thisArray;
-    @Input private ValueNode thatArray;
-    @Input private ValueNode length;
-
-    public CharArrayEqualsNode(ValueNode thisArray, ValueNode thatArray, ValueNode length) {
-        super(StampFactory.forKind(Kind.Boolean));
-        this.thisArray = thisArray;
-        this.thatArray = thatArray;
-        this.length = length;
-    }
-
-    @Override
-    public Node canonical(CanonicalizerTool tool) {
-        if (thisArray.isConstant() && thatArray.isConstant()) {
-            char[] array1 = (char[]) thisArray.asConstant().asObject();
-            char[] array2 = (char[]) thatArray.asConstant().asObject();
-            final boolean result = Arrays.equals(array1, array2);
-            return ConstantNode.forBoolean(result, graph());
-        }
-        return this;
-    }
-
-    @NodeIntrinsic
-    public static native boolean equals(char[] thisArray, char[] thatArray, int length);
-
-    @Override
-    public void generate(LIRGenerator gen) {
-        Variable result = gen.newVariable(Kind.Boolean);
-        gen.emitCharArrayEquals(result, gen.operand(thisArray), gen.operand(thatArray), gen.operand(length));
-        gen.setResult(this, result);
-    }
-}
--- a/graal/com.oracle.graal.test/src/com/oracle/graal/test/GraalTest.java	Tue Feb 18 15:04:47 2014 -0800
+++ b/graal/com.oracle.graal.test/src/com/oracle/graal/test/GraalTest.java	Tue Feb 18 21:55:26 2014 -0800
@@ -23,6 +23,7 @@
 package com.oracle.graal.test;
 
 import java.lang.reflect.*;
+import java.util.*;
 
 import org.junit.*;
 import org.junit.runner.*;
@@ -53,4 +54,12 @@
             throw new RuntimeException("method not found: " + methodName);
         }
     }
+
+    protected Method getMethod(Class<?> clazz, String methodName, Class<?>[] parameterTypes) {
+        try {
+            return clazz.getMethod(methodName, parameterTypes);
+        } catch (NoSuchMethodException | SecurityException e) {
+            throw new RuntimeException("method not found: " + methodName + "" + Arrays.toString(parameterTypes));
+        }
+    }
 }