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);
+    }
+  }
+}