Recognize ABS cases.
Rationale:
enables more optimizations on ABS, such as SIMDization
Background:
this is a breakout CL for SAD
Bug: 64091002
Test: test-art-host test-art-target
Change-Id: I4551c66114a04d51e4140ff222722fdd52ea6e63
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index f2a829f..337177f 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -813,6 +813,57 @@
}
}
+// Constructs a new ABS(x) node in the HIR.
+static HInstruction* NewIntegralAbs(ArenaAllocator* arena, HInstruction* x, HInstruction* cursor) {
+ Primitive::Type type = x->GetType();
+ DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
+ // Construct a fake intrinsic with as much context as is needed to allocate one.
+ // The intrinsic will always be lowered into code later anyway.
+ // TODO: b/65164101 : moving towards a real HAbs node makes more sense.
+ HInvokeStaticOrDirect::DispatchInfo dispatch_info = {
+ HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress,
+ HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
+ 0u
+ };
+ HInvokeStaticOrDirect* invoke = new (arena) HInvokeStaticOrDirect(
+ arena,
+ 1,
+ type,
+ x->GetDexPc(),
+ /*method_idx*/ -1,
+ /*resolved_method*/ nullptr,
+ dispatch_info,
+ kStatic,
+ MethodReference(nullptr, dex::kDexNoIndex),
+ HInvokeStaticOrDirect::ClinitCheckRequirement::kNone);
+ invoke->SetArgumentAt(0, x);
+ invoke->SetIntrinsic(type == Primitive::kPrimInt ? Intrinsics::kMathAbsInt
+ : Intrinsics::kMathAbsLong,
+ kNoEnvironmentOrCache,
+ kNoSideEffects,
+ kNoThrow);
+ cursor->GetBlock()->InsertInstructionBefore(invoke, cursor);
+ return invoke;
+}
+
+// Returns true if operands a and b consists of widening type conversions
+// (either explicit or implicit) to the given to_type.
+static bool AreLowerPrecisionArgs(Primitive::Type to_type, HInstruction* a, HInstruction* b) {
+ if (a->IsTypeConversion() && a->GetType() == to_type) {
+ a = a->InputAt(0);
+ }
+ if (b->IsTypeConversion() && b->GetType() == to_type) {
+ b = b->InputAt(0);
+ }
+ Primitive::Type type1 = a->GetType();
+ Primitive::Type type2 = b->GetType();
+ return (type1 == Primitive::kPrimByte && type2 == Primitive::kPrimByte) ||
+ (type1 == Primitive::kPrimShort && type2 == Primitive::kPrimShort) ||
+ (type1 == Primitive::kPrimChar && type2 == Primitive::kPrimChar) ||
+ (type1 == Primitive::kPrimInt && type2 == Primitive::kPrimInt &&
+ to_type == Primitive::kPrimLong);
+}
+
void InstructionSimplifierVisitor::VisitSelect(HSelect* select) {
HInstruction* replace_with = nullptr;
HInstruction* condition = select->GetCondition();
@@ -849,6 +900,47 @@
// Replace (cond ? false : true) with (!cond).
replace_with = GetGraph()->InsertOppositeCondition(condition, select);
}
+ } else if (condition->IsCondition()) {
+ IfCondition cmp = condition->AsCondition()->GetCondition();
+ HInstruction* a = condition->InputAt(0);
+ HInstruction* b = condition->InputAt(1);
+ Primitive::Type t_type = true_value->GetType();
+ Primitive::Type f_type = false_value->GetType();
+ // Here we have a <cmp> b ? true_value : false_value.
+ // Test if both values are same-typed int or long.
+ if (t_type == f_type && (t_type == Primitive::kPrimInt || t_type == Primitive::kPrimLong)) {
+ // Try to replace typical integral ABS constructs.
+ if (true_value->IsNeg()) {
+ HInstruction* negated = true_value->InputAt(0);
+ if ((cmp == kCondLT || cmp == kCondLE) &&
+ (a == negated && a == false_value && IsInt64Value(b, 0))) {
+ // Found a < 0 ? -a : a which can be replaced by ABS(a).
+ replace_with = NewIntegralAbs(GetGraph()->GetArena(), false_value, select);
+ }
+ } else if (false_value->IsNeg()) {
+ HInstruction* negated = false_value->InputAt(0);
+ if ((cmp == kCondGT || cmp == kCondGE) &&
+ (a == true_value && a == negated && IsInt64Value(b, 0))) {
+ // Found a > 0 ? a : -a which can be replaced by ABS(a).
+ replace_with = NewIntegralAbs(GetGraph()->GetArena(), true_value, select);
+ }
+ } else if (true_value->IsSub() && false_value->IsSub()) {
+ HInstruction* true_sub1 = true_value->InputAt(0);
+ HInstruction* true_sub2 = true_value->InputAt(1);
+ HInstruction* false_sub1 = false_value->InputAt(0);
+ HInstruction* false_sub2 = false_value->InputAt(1);
+ if ((((cmp == kCondGT || cmp == kCondGE) &&
+ (a == true_sub1 && b == true_sub2 && a == false_sub2 && b == false_sub1)) ||
+ ((cmp == kCondLT || cmp == kCondLE) &&
+ (a == true_sub2 && b == true_sub1 && a == false_sub1 && b == false_sub2))) &&
+ AreLowerPrecisionArgs(t_type, a, b)) {
+ // Found a > b ? a - b : b - a or
+ // a < b ? b - a : a - b
+ // which can be replaced by ABS(a - b) for lower precision operands a, b.
+ replace_with = NewIntegralAbs(GetGraph()->GetArena(), true_value, select);
+ }
+ }
+ }
}
if (replace_with != nullptr) {
diff --git a/test/660-checker-sad-byte/expected.txt b/test/660-checker-sad-byte/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/660-checker-sad-byte/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/660-checker-sad-byte/info.txt b/test/660-checker-sad-byte/info.txt
new file mode 100644
index 0000000..0c1cbda
--- /dev/null
+++ b/test/660-checker-sad-byte/info.txt
@@ -0,0 +1 @@
+Functional tests on SAD scalar operations.
diff --git a/test/660-checker-sad-byte/src/Main.java b/test/660-checker-sad-byte/src/Main.java
new file mode 100644
index 0000000..6bcd046
--- /dev/null
+++ b/test/660-checker-sad-byte/src/Main.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+/**
+ * Tests for SAD (sum of absolute differences).
+ */
+public class Main {
+
+ /// CHECK-START: int Main.sad1(byte, byte) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Select:i\d+>> Select
+ /// CHECK-DAG: Return [<<Select>>]
+ //
+ /// CHECK-START: int Main.sad1(byte, byte) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Intrin:i\d+>> InvokeStaticOrDirect intrinsic:MathAbsInt
+ /// CHECK-DAG: Return [<<Intrin>>]
+ static int sad1(byte x, byte y) {
+ return x >= y ? x - y : y - x;
+ }
+
+ /// CHECK-START: int Main.sad2(byte, byte) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Select:i\d+>> Select
+ /// CHECK-DAG: Return [<<Select>>]
+ //
+ /// CHECK-START: int Main.sad2(byte, byte) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Intrin:i\d+>> InvokeStaticOrDirect intrinsic:MathAbsInt
+ /// CHECK-DAG: Return [<<Intrin>>]
+ static int sad2(byte x, byte y) {
+ int diff = x - y;
+ if (diff < 0) diff = -diff;
+ return diff;
+ }
+
+ /// CHECK-START: int Main.sad3(byte, byte) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Select:i\d+>> Select
+ /// CHECK-DAG: Return [<<Select>>]
+ //
+ /// CHECK-START: int Main.sad3(byte, byte) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Intrin:i\d+>> InvokeStaticOrDirect intrinsic:MathAbsInt
+ /// CHECK-DAG: Return [<<Intrin>>]
+ static int sad3(byte x, byte y) {
+ int diff = x - y;
+ return diff >= 0 ? diff : -diff;
+ }
+
+ /// CHECK-START: int Main.sad3Alt(byte, byte) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Select:i\d+>> Select
+ /// CHECK-DAG: Return [<<Select>>]
+ //
+ /// CHECK-START: int Main.sad3Alt(byte, byte) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Intrin:i\d+>> InvokeStaticOrDirect intrinsic:MathAbsInt
+ /// CHECK-DAG: Return [<<Intrin>>]
+ static int sad3Alt(byte x, byte y) {
+ int diff = x - y;
+ return 0 <= diff ? diff : -diff;
+ }
+
+ /// CHECK-START: long Main.sadL1(byte, byte) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Select:j\d+>> Select
+ /// CHECK-DAG: Return [<<Select>>]
+ //
+ /// CHECK-START: long Main.sadL1(byte, byte) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Intrin:j\d+>> InvokeStaticOrDirect intrinsic:MathAbsLong
+ /// CHECK-DAG: Return [<<Intrin>>]
+ static long sadL1(byte x, byte y) {
+ long xl = x;
+ long yl = y;
+ return xl >= yl ? xl - yl : yl - xl;
+ }
+
+ /// CHECK-START: long Main.sadL2(byte, byte) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Select:j\d+>> Select
+ /// CHECK-DAG: Return [<<Select>>]
+ //
+ /// CHECK-START: long Main.sadL2(byte, byte) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Intrin:j\d+>> InvokeStaticOrDirect intrinsic:MathAbsLong
+ /// CHECK-DAG: Return [<<Intrin>>]
+ static long sadL2(byte x, byte y) {
+ long diff = x - y;
+ if (diff < 0L) diff = -diff;
+ return diff;
+ }
+
+ /// CHECK-START: long Main.sadL3(byte, byte) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Select:j\d+>> Select
+ /// CHECK-DAG: Return [<<Select>>]
+ //
+ /// CHECK-START: long Main.sadL3(byte, byte) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Intrin:j\d+>> InvokeStaticOrDirect intrinsic:MathAbsLong
+ /// CHECK-DAG: Return [<<Intrin>>]
+ static long sadL3(byte x, byte y) {
+ long diff = x - y;
+ return diff >= 0L ? diff : -diff;
+ }
+
+ /// CHECK-START: long Main.sadL3Alt(byte, byte) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Select:j\d+>> Select
+ /// CHECK-DAG: Return [<<Select>>]
+ //
+ /// CHECK-START: long Main.sadL3Alt(byte, byte) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Intrin:j\d+>> InvokeStaticOrDirect intrinsic:MathAbsLong
+ /// CHECK-DAG: Return [<<Intrin>>]
+ static long sadL3Alt(byte x, byte y) {
+ long diff = x - y;
+ return 0L <= diff ? diff : -diff;
+ }
+
+ public static void main(String[] args) {
+ // Use cross-values to test all cases.
+ int n = 256;
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < n; j++) {
+ byte x = (byte) i;
+ byte y = (byte) j;
+ int e = Math.abs(x - y);
+ expectEquals(e, sad1(x, y));
+ expectEquals(e, sad2(x, y));
+ expectEquals(e, sad3(x, y));
+ expectEquals(e, sad3Alt(x, y));
+ expectEquals(e, sadL2(x, y));
+ expectEquals(e, sadL3(x, y));
+ expectEquals(e, sadL3Alt(x, y));
+ }
+ }
+ System.out.println("passed");
+ }
+
+ private static void expectEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ private static void expectEquals(long expected, long result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+}
diff --git a/test/660-checker-sad-char/expected.txt b/test/660-checker-sad-char/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/660-checker-sad-char/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/660-checker-sad-char/info.txt b/test/660-checker-sad-char/info.txt
new file mode 100644
index 0000000..0c1cbda
--- /dev/null
+++ b/test/660-checker-sad-char/info.txt
@@ -0,0 +1 @@
+Functional tests on SAD scalar operations.
diff --git a/test/660-checker-sad-char/src/Main.java b/test/660-checker-sad-char/src/Main.java
new file mode 100644
index 0000000..3033509
--- /dev/null
+++ b/test/660-checker-sad-char/src/Main.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+/**
+ * Tests for SAD (sum of absolute differences).
+ */
+public class Main {
+
+ /// CHECK-START: int Main.sad1(char, char) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Select:i\d+>> Select
+ /// CHECK-DAG: Return [<<Select>>]
+ //
+ /// CHECK-START: int Main.sad1(char, char) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Intrin:i\d+>> InvokeStaticOrDirect intrinsic:MathAbsInt
+ /// CHECK-DAG: Return [<<Intrin>>]
+ static int sad1(char x, char y) {
+ return x >= y ? x - y : y - x;
+ }
+
+ /// CHECK-START: int Main.sad2(char, char) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Select:i\d+>> Select
+ /// CHECK-DAG: Return [<<Select>>]
+ //
+ /// CHECK-START: int Main.sad2(char, char) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Intrin:i\d+>> InvokeStaticOrDirect intrinsic:MathAbsInt
+ /// CHECK-DAG: Return [<<Intrin>>]
+ static int sad2(char x, char y) {
+ int diff = x - y;
+ if (diff < 0) diff = -diff;
+ return diff;
+ }
+
+ /// CHECK-START: int Main.sad3(char, char) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Select:i\d+>> Select
+ /// CHECK-DAG: Return [<<Select>>]
+ //
+ /// CHECK-START: int Main.sad3(char, char) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Intrin:i\d+>> InvokeStaticOrDirect intrinsic:MathAbsInt
+ /// CHECK-DAG: Return [<<Intrin>>]
+ static int sad3(char x, char y) {
+ int diff = x - y;
+ return diff >= 0 ? diff : -diff;
+ }
+
+ /// CHECK-START: int Main.sad3Alt(char, char) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Select:i\d+>> Select
+ /// CHECK-DAG: Return [<<Select>>]
+ //
+ /// CHECK-START: int Main.sad3Alt(char, char) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Intrin:i\d+>> InvokeStaticOrDirect intrinsic:MathAbsInt
+ /// CHECK-DAG: Return [<<Intrin>>]
+ static int sad3Alt(char x, char y) {
+ int diff = x - y;
+ return 0 <= diff ? diff : -diff;
+ }
+
+ /// CHECK-START: long Main.sadL1(char, char) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Select:j\d+>> Select
+ /// CHECK-DAG: Return [<<Select>>]
+ //
+ /// CHECK-START: long Main.sadL1(char, char) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Intrin:j\d+>> InvokeStaticOrDirect intrinsic:MathAbsLong
+ /// CHECK-DAG: Return [<<Intrin>>]
+ static long sadL1(char x, char y) {
+ long xl = x;
+ long yl = y;
+ return xl >= yl ? xl - yl : yl - xl;
+ }
+
+ /// CHECK-START: long Main.sadL2(char, char) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Select:j\d+>> Select
+ /// CHECK-DAG: Return [<<Select>>]
+ //
+ /// CHECK-START: long Main.sadL2(char, char) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Intrin:j\d+>> InvokeStaticOrDirect intrinsic:MathAbsLong
+ /// CHECK-DAG: Return [<<Intrin>>]
+ static long sadL2(char x, char y) {
+ long diff = x - y;
+ if (diff < 0L) diff = -diff;
+ return diff;
+ }
+
+ /// CHECK-START: long Main.sadL3(char, char) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Select:j\d+>> Select
+ /// CHECK-DAG: Return [<<Select>>]
+ //
+ /// CHECK-START: long Main.sadL3(char, char) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Intrin:j\d+>> InvokeStaticOrDirect intrinsic:MathAbsLong
+ /// CHECK-DAG: Return [<<Intrin>>]
+ static long sadL3(char x, char y) {
+ long diff = x - y;
+ return diff >= 0L ? diff : -diff;
+ }
+
+ /// CHECK-START: long Main.sadL3Alt(char, char) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Select:j\d+>> Select
+ /// CHECK-DAG: Return [<<Select>>]
+ //
+ /// CHECK-START: long Main.sadL3Alt(char, char) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Intrin:j\d+>> InvokeStaticOrDirect intrinsic:MathAbsLong
+ /// CHECK-DAG: Return [<<Intrin>>]
+ static long sadL3Alt(char x, char y) {
+ long diff = x - y;
+ return 0L <= diff ? diff : -diff;
+ }
+
+ public static void main(String[] args) {
+ // Use cross-values to test all cases.
+ char[] interesting = {
+ (char) 0x0000, (char) 0x0001, (char) 0x007f,
+ (char) 0x0080, (char) 0x0081, (char) 0x00ff,
+ (char) 0x0100, (char) 0x0101, (char) 0x017f,
+ (char) 0x0180, (char) 0x0181, (char) 0x01ff,
+ (char) 0x7f00, (char) 0x7f01, (char) 0x7f7f,
+ (char) 0x7f80, (char) 0x7f81, (char) 0x7fff,
+ (char) 0x8000, (char) 0x8001, (char) 0x807f,
+ (char) 0x8080, (char) 0x8081, (char) 0x80ff,
+ (char) 0x8100, (char) 0x8101, (char) 0x817f,
+ (char) 0x8180, (char) 0x8181, (char) 0x81ff,
+ (char) 0xff00, (char) 0xff01, (char) 0xff7f,
+ (char) 0xff80, (char) 0xff81, (char) 0xffff
+ };
+ for (int i = 0; i < interesting.length; i++) {
+ for (int j = 0; j < interesting.length; j++) {
+ char x = interesting[i];
+ char y = interesting[j];
+ int e = Math.abs(x - y);
+ expectEquals(e, sad1(x, y));
+ expectEquals(e, sad2(x, y));
+ expectEquals(e, sad3(x, y));
+ expectEquals(e, sad3Alt(x, y));
+ expectEquals(e, sadL1(x, y));
+ expectEquals(e, sadL2(x, y));
+ expectEquals(e, sadL3(x, y));
+ expectEquals(e, sadL3Alt(x, y));
+ }
+ }
+ System.out.println("passed");
+ }
+
+ private static void expectEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ private static void expectEquals(long expected, long result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+}
diff --git a/test/660-checker-sad-int/expected.txt b/test/660-checker-sad-int/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/660-checker-sad-int/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/660-checker-sad-int/info.txt b/test/660-checker-sad-int/info.txt
new file mode 100644
index 0000000..0c1cbda
--- /dev/null
+++ b/test/660-checker-sad-int/info.txt
@@ -0,0 +1 @@
+Functional tests on SAD scalar operations.
diff --git a/test/660-checker-sad-int/src/Main.java b/test/660-checker-sad-int/src/Main.java
new file mode 100644
index 0000000..42ecea6
--- /dev/null
+++ b/test/660-checker-sad-int/src/Main.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+/**
+ * Tests for SAD (sum of absolute differences).
+ */
+public class Main {
+
+ /// CHECK-START: int Main.sad1(int, int) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Select:i\d+>> Select
+ /// CHECK-DAG: Return [<<Select>>]
+ //
+ /// CHECK-START: int Main.sad1(int, int) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Select:i\d+>> Select
+ /// CHECK-DAG: Return [<<Select>>]
+ //
+ /// CHECK-START: int Main.sad1(int, int) instruction_simplifier$after_inlining (after)
+ /// CHECK-NOT: InvokeStaticOrDirect intrinsic:MathAbsInt
+ //
+ // NOTE: for direct 32-bit operands, this is not an ABS.
+ static int sad1(int x, int y) {
+ return x >= y ? x - y : y - x;
+ }
+
+ /// CHECK-START: int Main.sad2(int, int) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Select:i\d+>> Select
+ /// CHECK-DAG: Return [<<Select>>]
+ //
+ /// CHECK-START: int Main.sad2(int, int) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Intrin:i\d+>> InvokeStaticOrDirect intrinsic:MathAbsInt
+ /// CHECK-DAG: Return [<<Intrin>>]
+ static int sad2(int x, int y) {
+ int diff = x - y;
+ if (diff < 0) diff = -diff;
+ return diff;
+ }
+
+ /// CHECK-START: int Main.sad3(int, int) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Select:i\d+>> Select
+ /// CHECK-DAG: Return [<<Select>>]
+ //
+ /// CHECK-START: int Main.sad3(int, int) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Intrin:i\d+>> InvokeStaticOrDirect intrinsic:MathAbsInt
+ /// CHECK-DAG: Return [<<Intrin>>]
+ static int sad3(int x, int y) {
+ int diff = x - y;
+ return diff >= 0 ? diff : -diff;
+ }
+
+ /// CHECK-START: int Main.sad3Alt(int, int) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Select:i\d+>> Select
+ /// CHECK-DAG: Return [<<Select>>]
+ //
+ /// CHECK-START: int Main.sad3Alt(int, int) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Intrin:i\d+>> InvokeStaticOrDirect intrinsic:MathAbsInt
+ /// CHECK-DAG: Return [<<Intrin>>]
+ static int sad3Alt(int x, int y) {
+ int diff = x - y;
+ return 0 <= diff ? diff : -diff;
+ }
+
+ /// CHECK-START: long Main.sadL1(int, int) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Select:j\d+>> Select
+ /// CHECK-DAG: Return [<<Select>>]
+ //
+ /// CHECK-START: long Main.sadL1(int, int) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Intrin:j\d+>> InvokeStaticOrDirect intrinsic:MathAbsLong
+ /// CHECK-DAG: Return [<<Intrin>>]
+ static long sadL1(int x, int y) {
+ long xl = x;
+ long yl = y;
+ return xl >= yl ? xl - yl : yl - xl;
+ }
+
+ /// CHECK-START: long Main.sadL2(int, int) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Select:j\d+>> Select
+ /// CHECK-DAG: Return [<<Select>>]
+ //
+ /// CHECK-START: long Main.sadL2(int, int) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Intrin:j\d+>> InvokeStaticOrDirect intrinsic:MathAbsLong
+ /// CHECK-DAG: Return [<<Intrin>>]
+ static long sadL2(int x, int y) {
+ long diff = x - y;
+ if (diff < 0L) diff = -diff;
+ return diff;
+ }
+
+ /// CHECK-START: long Main.sadL3(int, int) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Select:j\d+>> Select
+ /// CHECK-DAG: Return [<<Select>>]
+ //
+ /// CHECK-START: long Main.sadL3(int, int) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Intrin:j\d+>> InvokeStaticOrDirect intrinsic:MathAbsLong
+ /// CHECK-DAG: Return [<<Intrin>>]
+ static long sadL3(int x, int y) {
+ long diff = x - y;
+ return diff >= 0L ? diff : -diff;
+ }
+
+ /// CHECK-START: long Main.sadL3Alt(int, int) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Select:j\d+>> Select
+ /// CHECK-DAG: Return [<<Select>>]
+ //
+ /// CHECK-START: long Main.sadL3Alt(int, int) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Intrin:j\d+>> InvokeStaticOrDirect intrinsic:MathAbsLong
+ /// CHECK-DAG: Return [<<Intrin>>]
+ static long sadL3Alt(int x, int y) {
+ long diff = x - y;
+ return 0L <= diff ? diff : -diff;
+ }
+
+ public static void main(String[] args) {
+ // Use cross-values for the interesting values.
+ int[] interesting = {
+ 0x00000000, 0x00000001, 0x00007fff, 0x00008000, 0x00008001, 0x0000ffff,
+ 0x00010000, 0x00010001, 0x00017fff, 0x00018000, 0x00018001, 0x0001ffff,
+ 0x7fff0000, 0x7fff0001, 0x7fff7fff, 0x7fff8000, 0x7fff8001, 0x7fffffff,
+ 0x80000000, 0x80000001, 0x80007fff, 0x80008000, 0x80008001, 0x8000ffff,
+ 0x80010000, 0x80010001, 0x80017fff, 0x80018000, 0x80018001, 0x8001ffff,
+ 0xffff0000, 0xffff0001, 0xffff7fff, 0xffff8000, 0xffff8001, 0xffffffff
+ };
+ for (int i = 0; i < interesting.length; i++) {
+ for (int j = 0; j < interesting.length; j++) {
+ int x = interesting[i];
+ int y = interesting[j];
+ int e1 = x >= y ? x - y : y - x; // still select
+ expectEquals(e1, sad1(x, y));
+ int e2 = Math.abs(x - y); // pure abs
+ expectEquals(e2, sad2(x, y));
+ expectEquals(e2, sad3(x, y));
+ expectEquals(e2, sad3Alt(x, y));
+ long eL1 = Math.abs(((long)x) - ((long)y)); // now, different, but abs
+ expectEquals(eL1, sadL1(x, y));
+ long eL2 = Math.abs((long)(x - y)); // also, different, but abs
+ expectEquals(eL2, sadL2(x, y));
+ expectEquals(eL2, sadL3(x, y));
+ expectEquals(eL2, sadL3Alt(x, y));
+ }
+ }
+ System.out.println("passed");
+ }
+
+ private static void expectEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ private static void expectEquals(long expected, long result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+}
diff --git a/test/660-checker-sad-long/expected.txt b/test/660-checker-sad-long/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/660-checker-sad-long/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/660-checker-sad-long/info.txt b/test/660-checker-sad-long/info.txt
new file mode 100644
index 0000000..0c1cbda
--- /dev/null
+++ b/test/660-checker-sad-long/info.txt
@@ -0,0 +1 @@
+Functional tests on SAD scalar operations.
diff --git a/test/660-checker-sad-long/src/Main.java b/test/660-checker-sad-long/src/Main.java
new file mode 100644
index 0000000..d2e32ac
--- /dev/null
+++ b/test/660-checker-sad-long/src/Main.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+/**
+ * Tests for SAD (sum of absolute differences).
+ */
+public class Main {
+
+ /// CHECK-START: long Main.sad1(long, long) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Select:j\d+>> Select
+ /// CHECK-DAG: Return [<<Select>>]
+ //
+ /// CHECK-START: long Main.sad1(long, long) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Select:j\d+>> Select
+ /// CHECK-DAG: Return [<<Select>>]
+ //
+ /// CHECK-START: long Main.sad1(long, long) instruction_simplifier$after_inlining (after)
+ /// CHECK-NOT: InvokeStaticOrDirect intrinsic:MathAbsLong
+ //
+ // NOTE: for direct 64-bit operands, this is not an ABS.
+ static long sad1(long x, long y) {
+ return x >= y ? x - y : y - x;
+ }
+
+ /// CHECK-START: long Main.sad2(long, long) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Select:j\d+>> Select
+ /// CHECK-DAG: Return [<<Select>>]
+ //
+ /// CHECK-START: long Main.sad2(long, long) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Intrin:j\d+>> InvokeStaticOrDirect intrinsic:MathAbsLong
+ /// CHECK-DAG: Return [<<Intrin>>]
+ static long sad2(long x, long y) {
+ long diff = x - y;
+ if (diff < 0) diff = -diff;
+ return diff;
+ }
+
+ /// CHECK-START: long Main.sad3(long, long) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Select:j\d+>> Select
+ /// CHECK-DAG: Return [<<Select>>]
+ //
+ /// CHECK-START: long Main.sad3(long, long) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Intrin:j\d+>> InvokeStaticOrDirect intrinsic:MathAbsLong
+ /// CHECK-DAG: Return [<<Intrin>>]
+ static long sad3(long x, long y) {
+ long diff = x - y;
+ return diff >= 0 ? diff : -diff;
+ }
+
+ /// CHECK-START: long Main.sad3Alt(long, long) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Select:j\d+>> Select
+ /// CHECK-DAG: Return [<<Select>>]
+ //
+ /// CHECK-START: long Main.sad3Alt(long, long) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Intrin:j\d+>> InvokeStaticOrDirect intrinsic:MathAbsLong
+ /// CHECK-DAG: Return [<<Intrin>>]
+ static long sad3Alt(long x, long y) {
+ long diff = x - y;
+ return 0 <= diff ? diff : -diff;
+ }
+
+ public static void main(String[] args) {
+ // Use cross-values for the interesting values.
+ long[] interesting = {
+ 0x0000000000000000L, 0x0000000000000001L, 0x000000007fffffffL,
+ 0x0000000080000000L, 0x0000000080000001L, 0x00000000ffffffffL,
+ 0x0000000100000000L, 0x0000000100000001L, 0x000000017fffffffL,
+ 0x0000000180000000L, 0x0000000180000001L, 0x00000001ffffffffL,
+ 0x7fffffff00000000L, 0x7fffffff00000001L, 0x7fffffff7fffffffL,
+ 0x7fffffff80000000L, 0x7fffffff80000001L, 0x7fffffffffffffffL,
+ 0x8000000000000000L, 0x8000000000000001L, 0x800000007fffffffL,
+ 0x8000000080000000L, 0x8000000080000001L, 0x80000000ffffffffL,
+ 0x8000000100000000L, 0x8000000100000001L, 0x800000017fffffffL,
+ 0x8000000180000000L, 0x8000000180000001L, 0x80000001ffffffffL,
+ 0xffffffff00000000L, 0xffffffff00000001L, 0xffffffff7fffffffL,
+ 0xffffffff80000000L, 0xffffffff80000001L, 0xffffffffffffffffL
+ };
+ for (int i = 0; i < interesting.length; i++) {
+ for (int j = 0; j < interesting.length; j++) {
+ long x = interesting[i];
+ long y = interesting[j];
+ long e1 = x >= y ? x - y : y - x; // still select
+ expectEquals(e1, sad1(x, y));
+ long e2 = Math.abs(x - y); // pure abs
+ expectEquals(e2, sad2(x, y));
+ expectEquals(e2, sad3(x, y));
+ expectEquals(e2, sad3Alt(x, y));
+ }
+ }
+ System.out.println("passed");
+ }
+
+ private static void expectEquals(long expected, long result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+}
diff --git a/test/660-checker-sad-short/expected.txt b/test/660-checker-sad-short/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/660-checker-sad-short/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/660-checker-sad-short/info.txt b/test/660-checker-sad-short/info.txt
new file mode 100644
index 0000000..0c1cbda
--- /dev/null
+++ b/test/660-checker-sad-short/info.txt
@@ -0,0 +1 @@
+Functional tests on SAD scalar operations.
diff --git a/test/660-checker-sad-short/src/Main.java b/test/660-checker-sad-short/src/Main.java
new file mode 100644
index 0000000..182fe8a
--- /dev/null
+++ b/test/660-checker-sad-short/src/Main.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+/**
+ * Tests for SAD (sum of absolute differences).
+ */
+public class Main {
+
+ /// CHECK-START: int Main.sad1(short, short) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Select:i\d+>> Select
+ /// CHECK-DAG: Return [<<Select>>]
+ //
+ /// CHECK-START: int Main.sad1(short, short) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Intrin:i\d+>> InvokeStaticOrDirect intrinsic:MathAbsInt
+ /// CHECK-DAG: Return [<<Intrin>>]
+ static int sad1(short x, short y) {
+ return x >= y ? x - y : y - x;
+ }
+
+ /// CHECK-START: int Main.sad2(short, short) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Select:i\d+>> Select
+ /// CHECK-DAG: Return [<<Select>>]
+ //
+ /// CHECK-START: int Main.sad2(short, short) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Intrin:i\d+>> InvokeStaticOrDirect intrinsic:MathAbsInt
+ /// CHECK-DAG: Return [<<Intrin>>]
+ static int sad2(short x, short y) {
+ int diff = x - y;
+ if (diff < 0) diff = -diff;
+ return diff;
+ }
+
+ /// CHECK-START: int Main.sad3(short, short) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Select:i\d+>> Select
+ /// CHECK-DAG: Return [<<Select>>]
+ //
+ /// CHECK-START: int Main.sad3(short, short) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Intrin:i\d+>> InvokeStaticOrDirect intrinsic:MathAbsInt
+ /// CHECK-DAG: Return [<<Intrin>>]
+ static int sad3(short x, short y) {
+ int diff = x - y;
+ return diff >= 0 ? diff : -diff;
+ }
+
+ /// CHECK-START: int Main.sad3Alt(short, short) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Select:i\d+>> Select
+ /// CHECK-DAG: Return [<<Select>>]
+ //
+ /// CHECK-START: int Main.sad3Alt(short, short) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Intrin:i\d+>> InvokeStaticOrDirect intrinsic:MathAbsInt
+ /// CHECK-DAG: Return [<<Intrin>>]
+ static int sad3Alt(short x, short y) {
+ int diff = x - y;
+ return 0 <= diff ? diff : -diff;
+ }
+
+ /// CHECK-START: long Main.sadL1(short, short) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Select:j\d+>> Select
+ /// CHECK-DAG: Return [<<Select>>]
+ //
+ /// CHECK-START: long Main.sadL1(short, short) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Intrin:j\d+>> InvokeStaticOrDirect intrinsic:MathAbsLong
+ /// CHECK-DAG: Return [<<Intrin>>]
+ static long sadL1(short x, short y) {
+ long xl = x;
+ long yl = y;
+ return xl >= yl ? xl - yl : yl - xl;
+ }
+
+ /// CHECK-START: long Main.sadL2(short, short) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Select:j\d+>> Select
+ /// CHECK-DAG: Return [<<Select>>]
+ //
+ /// CHECK-START: long Main.sadL2(short, short) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Intrin:j\d+>> InvokeStaticOrDirect intrinsic:MathAbsLong
+ /// CHECK-DAG: Return [<<Intrin>>]
+ static long sadL2(short x, short y) {
+ long diff = x - y;
+ if (diff < 0L) diff = -diff;
+ return diff;
+ }
+
+ /// CHECK-START: long Main.sadL3(short, short) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Select:j\d+>> Select
+ /// CHECK-DAG: Return [<<Select>>]
+ //
+ /// CHECK-START: long Main.sadL3(short, short) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Intrin:j\d+>> InvokeStaticOrDirect intrinsic:MathAbsLong
+ /// CHECK-DAG: Return [<<Intrin>>]
+ static long sadL3(short x, short y) {
+ long diff = x - y;
+ return diff >= 0L ? diff : -diff;
+ }
+
+ /// CHECK-START: long Main.sadL3Alt(short, short) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Select:j\d+>> Select
+ /// CHECK-DAG: Return [<<Select>>]
+ //
+ /// CHECK-START: long Main.sadL3Alt(short, short) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Intrin:j\d+>> InvokeStaticOrDirect intrinsic:MathAbsLong
+ /// CHECK-DAG: Return [<<Intrin>>]
+ static long sadL3Alt(short x, short y) {
+ long diff = x - y;
+ return 0L <= diff ? diff : -diff;
+ }
+
+ public static void main(String[] args) {
+ // Use cross-values to test all cases.
+ short[] interesting = {
+ (short) 0x0000, (short) 0x0001, (short) 0x007f,
+ (short) 0x0080, (short) 0x0081, (short) 0x00ff,
+ (short) 0x0100, (short) 0x0101, (short) 0x017f,
+ (short) 0x0180, (short) 0x0181, (short) 0x01ff,
+ (short) 0x7f00, (short) 0x7f01, (short) 0x7f7f,
+ (short) 0x7f80, (short) 0x7f81, (short) 0x7fff,
+ (short) 0x8000, (short) 0x8001, (short) 0x807f,
+ (short) 0x8080, (short) 0x8081, (short) 0x80ff,
+ (short) 0x8100, (short) 0x8101, (short) 0x817f,
+ (short) 0x8180, (short) 0x8181, (short) 0x81ff,
+ (short) 0xff00, (short) 0xff01, (short) 0xff7f,
+ (short) 0xff80, (short) 0xff81, (short) 0xffff
+ };
+ for (int i = 0; i < interesting.length; i++) {
+ for (int j = 0; j < interesting.length; j++) {
+ short x = interesting[i];
+ short y = interesting[j];
+ int e = Math.abs(x - y);
+ expectEquals(e, sad1(x, y));
+ expectEquals(e, sad2(x, y));
+ expectEquals(e, sad3(x, y));
+ expectEquals(e, sad3Alt(x, y));
+ expectEquals(e, sadL1(x, y));
+ expectEquals(e, sadL2(x, y));
+ expectEquals(e, sadL3(x, y));
+ expectEquals(e, sadL3Alt(x, y));
+ }
+ }
+ System.out.println("passed");
+ }
+
+ private static void expectEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ private static void expectEquals(long expected, long result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+}