Version 2.13.0-90.0.dev

Merge commit '1f828c7cb2b005fe3a278a49433738946f3550fe' into 'dev'
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index fb55f7d..ecf74ae 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -3389,16 +3389,18 @@
 
   IntConverterInstr* box_defn = value()->definition()->AsIntConverter();
   if ((box_defn != NULL) && (box_defn->representation() == from())) {
-    // Do not erase truncating conversions from 64-bit value to 32-bit values
-    // because such conversions erase upper 32 bits.
-    if ((box_defn->from() == kUnboxedInt64) && box_defn->is_truncating()) {
+    // If the first convertion can erase bits (or deoptimize) we can't
+    // canonicalize it away.
+    auto src_defn = box_defn->value()->definition();
+    if ((box_defn->from() == kUnboxedInt64) &&
+        !Range::Fits(src_defn->range(), box_defn->to())) {
       return this;
     }
 
-    // It's safe to discard any other conversions from and then back to the same
-    // integer type.
+    // Otherise it is safe to discard any other conversions from and then back
+    // to the same integer type.
     if (box_defn->from() == to()) {
-      return box_defn->value()->definition();
+      return src_defn;
     }
 
     // Do not merge conversions where the first starts from Untagged or the
diff --git a/runtime/vm/compiler/backend/il_test.cc b/runtime/vm/compiler/backend/il_test.cc
index 4788686..e02bec5 100644
--- a/runtime/vm/compiler/backend/il_test.cc
+++ b/runtime/vm/compiler/backend/il_test.cc
@@ -7,7 +7,10 @@
 #include <vector>
 
 #include "platform/utils.h"
+#include "vm/compiler/backend/block_builder.h"
+#include "vm/compiler/backend/il_printer.h"
 #include "vm/compiler/backend/il_test_helper.h"
+#include "vm/compiler/backend/range_analysis.h"
 #include "vm/unit_test.h"
 
 namespace dart {
@@ -189,4 +192,73 @@
                             expected_stores_aot);
 }
 
+// Returns |true| if compiler canonicalizes away a chain of IntConverters going
+// from |initial| representation to |intermediate| representation and then
+// back to |initial| given that initial value has range [min_value, max_value].
+bool TestIntConverterCanonicalizationRule(Thread* thread,
+                                          int64_t min_value,
+                                          int64_t max_value,
+                                          Representation initial,
+                                          Representation intermediate) {
+  using compiler::BlockBuilder;
+
+  CompilerState S(thread, /*is_aot=*/false, /*is_optimizing=*/true);
+
+  FlowGraphBuilderHelper H;
+
+  // Add a variable into the scope which would provide static type for the
+  // parameter.
+  LocalVariable* v0_var =
+      new LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
+                        String::Handle(Symbols::New(thread, "v0")),
+                        AbstractType::ZoneHandle(Type::IntType()));
+  v0_var->set_type_check_mode(LocalVariable::kTypeCheckedByCaller);
+  H.flow_graph()->parsed_function().scope()->AddVariable(v0_var);
+
+  auto normal_entry = H.flow_graph()->graph_entry()->normal_entry();
+
+  Definition* v0;
+  ReturnInstr* ret;
+
+  {
+    BlockBuilder builder(H.flow_graph(), normal_entry);
+    v0 = builder.AddParameter(0, 0, /*with_frame=*/true, initial);
+    v0->set_range(Range(RangeBoundary::FromConstant(min_value),
+                        RangeBoundary::FromConstant(max_value)));
+    auto conv1 = builder.AddDefinition(new IntConverterInstr(
+        initial, intermediate, new Value(v0), S.GetNextDeoptId()));
+    auto conv2 = builder.AddDefinition(new IntConverterInstr(
+        intermediate, initial, new Value(conv1), S.GetNextDeoptId()));
+    ret = builder.AddReturn(new Value(conv2));
+  }
+
+  H.FinishGraph();
+
+  H.flow_graph()->Canonicalize();
+  H.flow_graph()->Canonicalize();
+
+  return ret->value()->definition() == v0;
+}
+
+ISOLATE_UNIT_TEST_CASE(IL_IntConverterCanonicalization) {
+  EXPECT(TestIntConverterCanonicalizationRule(thread, kMinInt16, kMaxInt16,
+                                              kUnboxedInt64, kUnboxedInt32));
+  EXPECT(TestIntConverterCanonicalizationRule(thread, kMinInt32, kMaxInt32,
+                                              kUnboxedInt64, kUnboxedInt32));
+  EXPECT(!TestIntConverterCanonicalizationRule(
+      thread, kMinInt32, static_cast<int64_t>(kMaxInt32) + 1, kUnboxedInt64,
+      kUnboxedInt32));
+  EXPECT(TestIntConverterCanonicalizationRule(thread, 0, kMaxInt16,
+                                              kUnboxedInt64, kUnboxedUint32));
+  EXPECT(TestIntConverterCanonicalizationRule(thread, 0, kMaxInt32,
+                                              kUnboxedInt64, kUnboxedUint32));
+  EXPECT(TestIntConverterCanonicalizationRule(thread, 0, kMaxUint32,
+                                              kUnboxedInt64, kUnboxedUint32));
+  EXPECT(!TestIntConverterCanonicalizationRule(
+      thread, 0, static_cast<int64_t>(kMaxUint32) + 1, kUnboxedInt64,
+      kUnboxedUint32));
+  EXPECT(!TestIntConverterCanonicalizationRule(thread, -1, kMaxInt16,
+                                               kUnboxedInt64, kUnboxedUint32));
+}
+
 }  // namespace dart
diff --git a/runtime/vm/compiler/backend/range_analysis.h b/runtime/vm/compiler/backend/range_analysis.h
index 9a8971e..7491def 100644
--- a/runtime/vm/compiler/backend/range_analysis.h
+++ b/runtime/vm/compiler/backend/range_analysis.h
@@ -439,6 +439,27 @@
            !max().UpperBound().Overflowed(size);
   }
 
+  // Returns true if this range fits without truncation into
+  // the given representation.
+  static bool Fits(Range* range, Representation rep) {
+    if (range == nullptr) return false;
+
+    switch (rep) {
+      case kUnboxedInt64:
+        return true;
+
+      case kUnboxedInt32:
+        return range->Fits(RangeBoundary::kRangeBoundaryInt32);
+
+      case kUnboxedUint32:
+        return range->IsWithin(0, kMaxUint32);
+
+      default:
+        break;
+    }
+    return false;
+  }
+
   // Clamp this to be within size.
   void Clamp(RangeBoundary::RangeSize size);
 
diff --git a/tools/VERSION b/tools/VERSION
index 0dbc0f2..d620660 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 13
 PATCH 0
-PRERELEASE 89
+PRERELEASE 90
 PRERELEASE_PATCH 0
\ No newline at end of file