Fix negation of immediates in ccmp, ccmn and neg (#99)

In two's-complement representation, the negation of the minimum value of a
signed type can't be represented. Detect and elide inversion for these cases
in ccmp, ccmn and neg.
diff --git a/src/aarch64/macro-assembler-aarch64.cc b/src/aarch64/macro-assembler-aarch64.cc
index 8b8bd52..8ee7144 100644
--- a/src/aarch64/macro-assembler-aarch64.cc
+++ b/src/aarch64/macro-assembler-aarch64.cc
@@ -1149,11 +1149,14 @@
                           StatusFlags nzcv,
                           Condition cond) {
   VIXL_ASSERT(allow_macro_instructions_);
-  if (operand.IsImmediate() && (operand.GetImmediate() < 0)) {
-    ConditionalCompareMacro(rn, -operand.GetImmediate(), nzcv, cond, CCMN);
-  } else {
-    ConditionalCompareMacro(rn, operand, nzcv, cond, CCMP);
+  if (operand.IsImmediate()) {
+    int64_t imm = operand.GetImmediate();
+    if ((imm < 0) && CanBeNegated(imm)) {
+      ConditionalCompareMacro(rn, -imm, nzcv, cond, CCMN);
+      return;
+    }
   }
+  ConditionalCompareMacro(rn, operand, nzcv, cond, CCMP);
 }
 
 
@@ -1162,11 +1165,14 @@
                           StatusFlags nzcv,
                           Condition cond) {
   VIXL_ASSERT(allow_macro_instructions_);
-  if (operand.IsImmediate() && (operand.GetImmediate() < 0)) {
-    ConditionalCompareMacro(rn, -operand.GetImmediate(), nzcv, cond, CCMP);
-  } else {
-    ConditionalCompareMacro(rn, operand, nzcv, cond, CCMN);
+  if (operand.IsImmediate()) {
+    int64_t imm = operand.GetImmediate();
+    if ((imm < 0) && CanBeNegated(imm)) {
+      ConditionalCompareMacro(rn, -imm, nzcv, cond, CCMP);
+      return;
+    }
   }
+  ConditionalCompareMacro(rn, operand, nzcv, cond, CCMN);
 }
 
 
@@ -1400,8 +1406,7 @@
   VIXL_ASSERT(allow_macro_instructions_);
   if (operand.IsImmediate()) {
     int64_t imm = operand.GetImmediate();
-    if ((imm < 0) && (imm != std::numeric_limits<int64_t>::min()) &&
-        IsImmAddSub(-imm)) {
+    if ((imm < 0) && CanBeNegated(imm) && IsImmAddSub(-imm)) {
       AddSubMacro(rd, rn, -imm, S, SUB);
       return;
     }
@@ -1488,8 +1493,7 @@
   VIXL_ASSERT(allow_macro_instructions_);
   if (operand.IsImmediate()) {
     int64_t imm = operand.GetImmediate();
-    if ((imm < 0) && (imm != std::numeric_limits<int64_t>::min()) &&
-        IsImmAddSub(-imm)) {
+    if ((imm < 0) && CanBeNegated(imm) && IsImmAddSub(-imm)) {
       AddSubMacro(rd, rn, -imm, S, ADD);
       return;
     }
@@ -1650,7 +1654,7 @@
 
 void MacroAssembler::Neg(const Register& rd, const Operand& operand) {
   VIXL_ASSERT(allow_macro_instructions_);
-  if (operand.IsImmediate()) {
+  if (operand.IsImmediate() && CanBeNegated(operand.GetImmediate())) {
     Mov(rd, -operand.GetImmediate());
   } else {
     Sub(rd, AppropriateZeroRegFor(rd), operand);
diff --git a/src/utils-vixl.h b/src/utils-vixl.h
index 7af9483..281c5cf 100644
--- a/src/utils-vixl.h
+++ b/src/utils-vixl.h
@@ -291,6 +291,12 @@
   return ~value + 1;
 }
 
+template <typename T>
+bool CanBeNegated(T value) {
+  VIXL_STATIC_ASSERT(std::is_signed<T>::value);
+  return (value == std::numeric_limits<T>::min()) ? false : true;
+}
+
 // An absolute operation for signed integers that is defined for results outside
 // the representable range. Specifically, Abs(MIN_INT) is MIN_INT.
 template <typename T>
diff --git a/tools/code_coverage.log b/tools/code_coverage.log
index 0a5969b..091124c 100644
--- a/tools/code_coverage.log
+++ b/tools/code_coverage.log
@@ -24,3 +24,4 @@
 1706691191 82.87% 97.59% 94.74%
 1707395574 82.89% 97.59% 94.77%
 1715261843 82.84% 97.60% 94.69%
+1718190785 82.85% 97.60% 94.70%