ART: Add CRC32.udate(int,int) intrinsic for ARM64
Use crc32 instructions for java.util.zip.CRC32.update(int,int).
Note that CRC32 is an optional feature on ARMv8, this intrinsic
is only enabled for devices with CRC32 intruction support.
Original author: tim.zhang@linaro.org
Performance improvements in CRC32Bench.UpdateInt:
Pixel 2: 22.8x
Nexus 6P:
little core: 28.3x
big core : 21.6x
Test: m test-art-target-gtest
Test: testrunner.py --target --optimizing --jit --interpreter
Test: 580-crc32
Change-Id: I1a9bc2befd2934b04103a27ce05806e919874070
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index 7684dc7..6d04b0e 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -2916,6 +2916,40 @@
void IntrinsicCodeGeneratorARM64::VisitReachabilityFence(HInvoke* invoke ATTRIBUTE_UNUSED) { }
+void IntrinsicLocationsBuilderARM64::VisitCRC32Update(HInvoke* invoke) {
+ if (!codegen_->GetInstructionSetFeatures().HasCRC()) {
+ return;
+ }
+
+ LocationSummary* locations = new (allocator_) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister());
+}
+
+// Lower the invoke of CRC32.update(int crc, int b).
+void IntrinsicCodeGeneratorARM64::VisitCRC32Update(HInvoke* invoke) {
+ DCHECK(codegen_->GetInstructionSetFeatures().HasCRC());
+
+ MacroAssembler* masm = GetVIXLAssembler();
+
+ Register crc = InputRegisterAt(invoke, 0);
+ Register val = InputRegisterAt(invoke, 1);
+ Register out = OutputRegister(invoke);
+
+ // The general algorithm of the CRC32 calculation is:
+ // crc = ~crc
+ // result = crc32_for_byte(crc, b)
+ // crc = ~result
+ // It is directly lowered to three instructions.
+ __ Mvn(out, crc);
+ __ Crc32b(out, out, val);
+ __ Mvn(out, out);
+}
+
UNIMPLEMENTED_INTRINSIC(ARM64, ReferenceGetReferent)
UNIMPLEMENTED_INTRINSIC(ARM64, StringStringIndexOf);
diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc
index 38e4c89..0c463a2 100644
--- a/compiler/optimizing/intrinsics_arm_vixl.cc
+++ b/compiler/optimizing/intrinsics_arm_vixl.cc
@@ -3059,6 +3059,7 @@
UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeCASLong) // High register pressure.
UNIMPLEMENTED_INTRINSIC(ARMVIXL, SystemArrayCopyChar)
UNIMPLEMENTED_INTRINSIC(ARMVIXL, ReferenceGetReferent)
+UNIMPLEMENTED_INTRINSIC(ARMVIXL, CRC32Update)
UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringStringIndexOf);
UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringStringIndexOfAfter);
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index 6f7f5e4..21fb7d7 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -2696,6 +2696,8 @@
UNIMPLEMENTED_INTRINSIC(MIPS, ReferenceGetReferent)
UNIMPLEMENTED_INTRINSIC(MIPS, SystemArrayCopy)
+UNIMPLEMENTED_INTRINSIC(MIPS, CRC32Update)
+
UNIMPLEMENTED_INTRINSIC(MIPS, StringStringIndexOf);
UNIMPLEMENTED_INTRINSIC(MIPS, StringStringIndexOfAfter);
UNIMPLEMENTED_INTRINSIC(MIPS, StringBufferAppend);
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index 2eb2529..4b86f5d 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -2346,6 +2346,7 @@
UNIMPLEMENTED_INTRINSIC(MIPS64, ReferenceGetReferent)
UNIMPLEMENTED_INTRINSIC(MIPS64, SystemArrayCopy)
+UNIMPLEMENTED_INTRINSIC(MIPS64, CRC32Update)
UNIMPLEMENTED_INTRINSIC(MIPS64, StringStringIndexOf);
UNIMPLEMENTED_INTRINSIC(MIPS64, StringStringIndexOfAfter);
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index 3504d7a..6dd4681 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -2967,6 +2967,7 @@
UNIMPLEMENTED_INTRINSIC(X86, LongHighestOneBit)
UNIMPLEMENTED_INTRINSIC(X86, IntegerLowestOneBit)
UNIMPLEMENTED_INTRINSIC(X86, LongLowestOneBit)
+UNIMPLEMENTED_INTRINSIC(X86, CRC32Update)
UNIMPLEMENTED_INTRINSIC(X86, StringStringIndexOf);
UNIMPLEMENTED_INTRINSIC(X86, StringStringIndexOfAfter);
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index 96f6eaa..7db26dc 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -2732,6 +2732,7 @@
UNIMPLEMENTED_INTRINSIC(X86_64, ReferenceGetReferent)
UNIMPLEMENTED_INTRINSIC(X86_64, FloatIsInfinite)
UNIMPLEMENTED_INTRINSIC(X86_64, DoubleIsInfinite)
+UNIMPLEMENTED_INTRINSIC(X86_64, CRC32Update)
UNIMPLEMENTED_INTRINSIC(X86_64, StringStringIndexOf);
UNIMPLEMENTED_INTRINSIC(X86_64, StringStringIndexOfAfter);
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index e9c17ee..d8da912 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -402,6 +402,7 @@
case Intrinsics::kUnsafeLoadFence:
case Intrinsics::kUnsafeStoreFence:
case Intrinsics::kUnsafeFullFence:
+ case Intrinsics::kCRC32Update:
// These intrinsics are on the light greylist and will fail a DCHECK in
// SetIntrinsic() if their flags change on the respective dex methods.
// Note that the DCHECK currently won't fail if the dex methods are
diff --git a/runtime/image.cc b/runtime/image.cc
index 376742a..59ac283 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -26,7 +26,7 @@
namespace art {
const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '6', '6', '\0' }; // Add metadata section.
+const uint8_t ImageHeader::kImageVersion[] = { '0', '6', '7', '\0' }; // Added CRC32 intrinsic
ImageHeader::ImageHeader(uint32_t image_begin,
uint32_t image_size,
diff --git a/runtime/interpreter/interpreter_intrinsics.cc b/runtime/interpreter/interpreter_intrinsics.cc
index 17b3cd4..24a026a 100644
--- a/runtime/interpreter/interpreter_intrinsics.cc
+++ b/runtime/interpreter/interpreter_intrinsics.cc
@@ -558,6 +558,7 @@
UNIMPLEMENTED_CASE(ReferenceGetReferent /* ()Ljava/lang/Object; */)
UNIMPLEMENTED_CASE(IntegerValueOf /* (I)Ljava/lang/Integer; */)
UNIMPLEMENTED_CASE(ThreadInterrupted /* ()Z */)
+ UNIMPLEMENTED_CASE(CRC32Update /* (II)I */)
INTRINSIC_CASE(VarHandleFullFence)
INTRINSIC_CASE(VarHandleAcquireFence)
INTRINSIC_CASE(VarHandleReleaseFence)
diff --git a/runtime/intrinsics_list.h b/runtime/intrinsics_list.h
index 2f91f5d..093dd7f 100644
--- a/runtime/intrinsics_list.h
+++ b/runtime/intrinsics_list.h
@@ -219,6 +219,7 @@
V(VarHandleLoadLoadFence, kStatic, kNeedsEnvironmentOrCache, kWriteSideEffects, kNoThrow, "Ljava/lang/invoke/VarHandle;", "loadLoadFence", "()V") \
V(VarHandleStoreStoreFence, kStatic, kNeedsEnvironmentOrCache, kReadSideEffects, kNoThrow, "Ljava/lang/invoke/VarHandle;", "storeStoreFence", "()V") \
V(ReachabilityFence, kStatic, kNeedsEnvironmentOrCache, kWriteSideEffects, kNoThrow, "Ljava/lang/ref/Reference;", "reachabilityFence", "(Ljava/lang/Object;)V") \
+ V(CRC32Update, kStatic, kNeedsEnvironmentOrCache, kReadSideEffects, kCanThrow, "Ljava/util/zip/CRC32;", "update", "(II)I") \
SIGNATURE_POLYMORPHIC_INTRINSICS_LIST(V)
#endif // ART_RUNTIME_INTRINSICS_LIST_H_
diff --git a/test/580-crc32/expected.txt b/test/580-crc32/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/580-crc32/expected.txt
diff --git a/test/580-crc32/info.txt b/test/580-crc32/info.txt
new file mode 100644
index 0000000..24f31e0
--- /dev/null
+++ b/test/580-crc32/info.txt
@@ -0,0 +1 @@
+This test case is used to test java.util.zip.CRC32.
diff --git a/test/580-crc32/src/Main.java b/test/580-crc32/src/Main.java
new file mode 100644
index 0000000..7fc1273
--- /dev/null
+++ b/test/580-crc32/src/Main.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.zip.CRC32;
+
+/**
+ * The ART compiler can use intrinsics for the java.util.zip.CRC32 method:
+ * private native static int update(int crc, int b)
+ *
+ * As the method is private it is not possible to check the use of intrinsics
+ * for it directly.
+ * The tests check that correct checksums are produced.
+ */
+public class Main {
+ private static CRC32 crc32 = new CRC32();
+
+ public Main() {
+ }
+
+ public static long TestInt(int value) {
+ crc32.reset();
+ crc32.update(value);
+ return crc32.getValue();
+ }
+
+ public static long TestInt(int... values) {
+ crc32.reset();
+ for (int value : values) {
+ crc32.update(value);
+ }
+ return crc32.getValue();
+ }
+
+ public static void assertEqual(long expected, long actual) {
+ if (expected != actual) {
+ throw new Error("Expected: " + expected + ", found: " + actual);
+ }
+ }
+
+ public static void main(String args[]) {
+ // public void update(int b)
+ //
+ // Tests for checksums of the byte 0x0
+ assertEqual(0xD202EF8DL, TestInt(0x0));
+ assertEqual(0xD202EF8DL, TestInt(0x0100));
+ assertEqual(0xD202EF8DL, TestInt(0x010000));
+ assertEqual(0xD202EF8DL, TestInt(0x01000000));
+ assertEqual(0xD202EF8DL, TestInt(0xff00));
+ assertEqual(0xD202EF8DL, TestInt(0xffff00));
+ assertEqual(0xD202EF8DL, TestInt(0xffffff00));
+ assertEqual(0xD202EF8DL, TestInt(0x1200));
+ assertEqual(0xD202EF8DL, TestInt(0x123400));
+ assertEqual(0xD202EF8DL, TestInt(0x12345600));
+ assertEqual(0xD202EF8DL, TestInt(Integer.MIN_VALUE));
+
+ // Tests for checksums of the byte 0x1
+ assertEqual(0xA505DF1BL, TestInt(0x1));
+ assertEqual(0xA505DF1BL, TestInt(0x0101));
+ assertEqual(0xA505DF1BL, TestInt(0x010001));
+ assertEqual(0xA505DF1BL, TestInt(0x01000001));
+ assertEqual(0xA505DF1BL, TestInt(0xff01));
+ assertEqual(0xA505DF1BL, TestInt(0xffff01));
+ assertEqual(0xA505DF1BL, TestInt(0xffffff01));
+ assertEqual(0xA505DF1BL, TestInt(0x1201));
+ assertEqual(0xA505DF1BL, TestInt(0x123401));
+ assertEqual(0xA505DF1BL, TestInt(0x12345601));
+
+ // Tests for checksums of the byte 0x0f
+ assertEqual(0x42BDF21CL, TestInt(0x0f));
+ assertEqual(0x42BDF21CL, TestInt(0x010f));
+ assertEqual(0x42BDF21CL, TestInt(0x01000f));
+ assertEqual(0x42BDF21CL, TestInt(0x0100000f));
+ assertEqual(0x42BDF21CL, TestInt(0xff0f));
+ assertEqual(0x42BDF21CL, TestInt(0xffff0f));
+ assertEqual(0x42BDF21CL, TestInt(0xffffff0f));
+ assertEqual(0x42BDF21CL, TestInt(0x120f));
+ assertEqual(0x42BDF21CL, TestInt(0x12340f));
+ assertEqual(0x42BDF21CL, TestInt(0x1234560f));
+
+ // Tests for checksums of the byte 0xff
+ assertEqual(0xFF000000L, TestInt(0x00ff));
+ assertEqual(0xFF000000L, TestInt(0x01ff));
+ assertEqual(0xFF000000L, TestInt(0x0100ff));
+ assertEqual(0xFF000000L, TestInt(0x010000ff));
+ assertEqual(0xFF000000L, TestInt(0x0000ffff));
+ assertEqual(0xFF000000L, TestInt(0x00ffffff));
+ assertEqual(0xFF000000L, TestInt(0xffffffff));
+ assertEqual(0xFF000000L, TestInt(0x12ff));
+ assertEqual(0xFF000000L, TestInt(0x1234ff));
+ assertEqual(0xFF000000L, TestInt(0x123456ff));
+ assertEqual(0xFF000000L, TestInt(Integer.MAX_VALUE));
+
+ // Tests for sequences
+ assertEqual(0xFF41D912L, TestInt(0, 0, 0));
+ assertEqual(0xFF41D912L, TestInt(0x0100, 0x010000, 0x01000000));
+ assertEqual(0xFF41D912L, TestInt(0xff00, 0xffff00, 0xffffff00));
+ assertEqual(0xFF41D912L, TestInt(0x1200, 0x123400, 0x12345600));
+
+ assertEqual(0x909FB2F2L, TestInt(1, 1, 1));
+ assertEqual(0x909FB2F2L, TestInt(0x0101, 0x010001, 0x01000001));
+ assertEqual(0x909FB2F2L, TestInt(0xff01, 0xffff01, 0xffffff01));
+ assertEqual(0x909FB2F2L, TestInt(0x1201, 0x123401, 0x12345601));
+
+ assertEqual(0xE33A9F71L, TestInt(0x0f, 0x0f, 0x0f));
+ assertEqual(0xE33A9F71L, TestInt(0x010f, 0x01000f, 0x0100000f));
+ assertEqual(0xE33A9F71L, TestInt(0xff0f, 0xffff0f, 0xffffff0f));
+ assertEqual(0xE33A9F71L, TestInt(0x120f, 0x12340f, 0x1234560f));
+
+ assertEqual(0xFFFFFF00L, TestInt(0x0ff, 0x0ff, 0x0ff));
+ assertEqual(0xFFFFFF00L, TestInt(0x01ff, 0x0100ff, 0x010000ff));
+ assertEqual(0xFFFFFF00L, TestInt(0x00ffff, 0x00ffffff, 0xffffffff));
+ assertEqual(0xFFFFFF00L, TestInt(0x12ff, 0x1234ff, 0x123456ff));
+
+ assertEqual(0xB6CC4292L, TestInt(0x01, 0x02));
+
+ assertEqual(0xB2DE047CL, TestInt(0x0, -1, Integer.MIN_VALUE, Integer.MAX_VALUE));
+ }
+}