[vm, compiler] Avoid undefined behavior when tracking induction variables wraps around.

TEST=ubsan
Bug: https://github.com/dart-lang/sdk/issues/45511
Change-Id: Iaa5733dc048a811c87f479fa54fdb89bf64a0373
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/193443
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
diff --git a/runtime/platform/utils.h b/runtime/platform/utils.h
index 788dd2a..a381505 100644
--- a/runtime/platform/utils.h
+++ b/runtime/platform/utils.h
@@ -292,6 +292,13 @@
     return static_cast<T>(static_cast<Unsigned>(a) * static_cast<Unsigned>(b));
   }
 
+  template <typename T = int64_t>
+  static inline T NegWithWrapAround(T a) {
+    // Avoid undefined behavior by doing arithmetic in the unsigned type.
+    using Unsigned = typename std::make_unsigned<T>::type;
+    return static_cast<T>(-static_cast<Unsigned>(a));
+  }
+
   // Shifts int64_t value left. Supports any non-negative number of bits and
   // silently discards shifted out bits.
   static inline int64_t ShiftLeftWithTruncation(int64_t a, int64_t b) {
diff --git a/runtime/vm/compiler/backend/loops.cc b/runtime/vm/compiler/backend/loops.cc
index 4ff2dc8..f28ae39 100644
--- a/runtime/vm/compiler/backend/loops.cc
+++ b/runtime/vm/compiler/backend/loops.cc
@@ -723,13 +723,16 @@
       // Invariant + Invariant : only for same or just one instruction.
       if (x->def_ == y->def_) {
         return new (zone_)
-            InductionVar(x->offset_ + y->offset_, x->mult_ + y->mult_, x->def_);
+            InductionVar(Utils::AddWithWrapAround(x->offset_, y->offset_),
+                         Utils::AddWithWrapAround(x->mult_, y->mult_), x->def_);
       } else if (y->mult_ == 0) {
         return new (zone_)
-            InductionVar(x->offset_ + y->offset_, x->mult_, x->def_);
+            InductionVar(Utils::AddWithWrapAround(x->offset_, y->offset_),
+                         x->mult_, x->def_);
       } else if (x->mult_ == 0) {
         return new (zone_)
-            InductionVar(x->offset_ + y->offset_, y->mult_, y->def_);
+            InductionVar(Utils::AddWithWrapAround(x->offset_, y->offset_),
+                         y->mult_, y->def_);
       }
     } else if (y != nullptr) {
       // Invariant + Induction.
@@ -768,13 +771,16 @@
       // Invariant + Invariant : only for same or just one instruction.
       if (x->def_ == y->def_) {
         return new (zone_)
-            InductionVar(x->offset_ - y->offset_, x->mult_ - y->mult_, x->def_);
+            InductionVar(Utils::SubWithWrapAround(x->offset_, y->offset_),
+                         Utils::SubWithWrapAround(x->mult_, y->mult_), x->def_);
       } else if (y->mult_ == 0) {
         return new (zone_)
-            InductionVar(x->offset_ - y->offset_, x->mult_, x->def_);
+            InductionVar(Utils::SubWithWrapAround(x->offset_, y->offset_),
+                         x->mult_, x->def_);
       } else if (x->mult_ == 0) {
         return new (zone_)
-            InductionVar(x->offset_ - y->offset_, -y->mult_, y->def_);
+            InductionVar(Utils::SubWithWrapAround(x->offset_, y->offset_),
+                         Utils::NegWithWrapAround(y->mult_), y->def_);
       }
     } else if (y != nullptr) {
       // Invariant - Induction.
@@ -823,7 +829,8 @@
   if (InductionVar::IsConstant(x) && y != nullptr) {
     if (y->kind_ == InductionVar::kInvariant) {
       return new (zone_)
-          InductionVar(x->offset_ * y->offset_, x->offset_ * y->mult_, y->def_);
+          InductionVar(Utils::MulWithWrapAround(x->offset_, y->offset_),
+                       Utils::MulWithWrapAround(x->offset_, y->mult_), y->def_);
     }
     return new (zone_)
         InductionVar(y->kind_, Mul(x, y->initial_), Mul(x, y->next_));