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