[vm/compiler] Avoid unmatched representations after the last SelectRepresentations pass

Before this fix, Canonicalize pass could change representation
of inputs of EqualityCompare after the last SelectRepresentations
pass. This results in unmatched representations and invalid code
generated.

The fix is to disallow canonicalization of EqualityCompare
from null-aware to non-null-aware after the last SelectRepresentations.

TEST=vm/dart/regress_flutter98967_test
Fixes https://github.com/flutter/flutter/issues/98967

Change-Id: I05359737fe322fbb2a0fe6025e3716ba5d04ebbf
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/234324
Reviewed-by: Slava Egorov <vegorov@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
Alexander Markov
2022-02-25 18:36:02 +00:00
committed by Commit Bot
parent 507de0e2f5
commit 736cec66d9
6 changed files with 123 additions and 3 deletions
@@ -0,0 +1,53 @@
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// Regression test for https://github.com/flutter/flutter/issues/98967.
// Verifies that compiler doesn't generate wrong code for comparison of ints
// due to a late change in the representation of EqualityCompare inputs.
import 'package:expect/expect.dart';
class C {
int? val;
@pragma('vm:never-inline')
void testImpl(bool Function(int) compare) {
for (var i = 0; i < 2; i++) {
Expect.equals(false, compare(i));
val = i;
Expect.equals(true, compare(i));
}
final mint0 = int.parse("7fffffffffffffff", radix: 16);
final mint1 = int.parse("7fffffffffffffff", radix: 16);
if (mint0 != mint1) throw 'This is the same mint value';
Expect.equals(false, compare(mint0));
val = mint0;
Expect.equals(true, compare(mint0));
Expect.equals(true, compare(mint1),
'expected two different mints with the same value compare equal');
}
@pragma('vm:never-inline')
static void blackhole(void Function() f) {
f();
}
void test() {
return testImpl((v) {
// Note: need multiple context levels in the chain to delay
// optimizer forwarding load of [val] and subsequently
// clearing null_aware flag on the equality comparison.
// Hence the closure capturing [v] below.
final result = val != null ? val == v : false;
blackhole(() => v);
return result;
});
}
}
void main() {
C().test();
}
+12
View File
@@ -397,6 +397,17 @@ class FlowGraph : public ZoneAllocated {
// after this point.
void disallow_licm() { licm_allowed_ = false; }
// Returns true if mismatch in input/output representations is allowed.
bool unmatched_representations_allowed() const {
return unmatched_representations_allowed_;
}
// After the last SelectRepresentations pass all further transformations
// should maintain matching input/output representations.
void disallow_unmatched_representations() {
unmatched_representations_allowed_ = false;
}
PrologueInfo prologue_info() const { return prologue_info_; }
// Computes the loop hierarchy of the flow graph on demand.
@@ -623,6 +634,7 @@ class FlowGraph : public ZoneAllocated {
ConstantInstr* constant_dead_;
bool licm_allowed_;
bool unmatched_representations_allowed_ = true;
const PrologueInfo prologue_info_;
+5 -1
View File
@@ -3460,7 +3460,11 @@ Definition* EqualityCompareInstr::Canonicalize(FlowGraph* flow_graph) {
flow_graph->InsertBefore(this, replacement, env(), FlowGraph::kValue);
return replacement;
} else {
if (!left_type->is_nullable() && !right_type->is_nullable()) {
// Null-aware EqualityCompare takes boxed inputs, so make sure
// unmatched representations are still allowed when converting
// EqualityCompare to the unboxed instruction.
if (!left_type->is_nullable() && !right_type->is_nullable() &&
flow_graph->unmatched_representations_allowed()) {
set_null_aware(false);
}
}
+43
View File
@@ -390,6 +390,49 @@ static void WriteCidTo(intptr_t cid, BaseTextBuffer* buffer) {
}
}
static void TestNullAwareEqualityCompareCanonicalization(
Thread* thread,
bool allow_representation_change) {
using compiler::BlockBuilder;
CompilerState S(thread, /*is_aot=*/true, /*is_optimizing=*/true);
FlowGraphBuilderHelper H;
auto normal_entry = H.flow_graph()->graph_entry()->normal_entry();
EqualityCompareInstr* compare = nullptr;
{
BlockBuilder builder(H.flow_graph(), normal_entry);
Definition* v0 =
builder.AddParameter(0, 0, /*with_frame=*/true, kUnboxedInt64);
Definition* v1 =
builder.AddParameter(1, 1, /*with_frame=*/true, kUnboxedInt64);
Definition* box0 = builder.AddDefinition(new BoxInt64Instr(new Value(v0)));
Definition* box1 = builder.AddDefinition(new BoxInt64Instr(new Value(v1)));
compare = builder.AddDefinition(new EqualityCompareInstr(
InstructionSource(), Token::kEQ, new Value(box0), new Value(box1),
kMintCid, S.GetNextDeoptId(), /*null_aware=*/true));
builder.AddReturn(new Value(compare));
}
H.FinishGraph();
if (!allow_representation_change) {
H.flow_graph()->disallow_unmatched_representations();
}
H.flow_graph()->Canonicalize();
EXPECT(compare->is_null_aware() == !allow_representation_change);
}
ISOLATE_UNIT_TEST_CASE(IL_Canonicalize_EqualityCompare) {
TestNullAwareEqualityCompareCanonicalization(thread, true);
TestNullAwareEqualityCompareCanonicalization(thread, false);
}
static void WriteCidRangeVectorTo(const CidRangeVector& ranges,
BaseTextBuffer* buffer) {
if (ranges.is_empty()) {
+9 -2
View File
@@ -309,7 +309,7 @@ FlowGraph* CompilerPass::RunForceOptimizedPipeline(
INVOKE_PASS(ConstantPropagation);
INVOKE_PASS(TypePropagation);
INVOKE_PASS(WidenSmiToInt32);
INVOKE_PASS(SelectRepresentations);
INVOKE_PASS(SelectRepresentations_Final);
INVOKE_PASS(TypePropagation);
INVOKE_PASS(TryCatchOptimization);
INVOKE_PASS(EliminateEnvironments);
@@ -380,7 +380,7 @@ FlowGraph* CompilerPass::RunPipeline(PipelineMode mode,
INVOKE_PASS(EliminateDeadPhis);
INVOKE_PASS(DCE);
INVOKE_PASS(TypePropagation);
INVOKE_PASS(SelectRepresentations);
INVOKE_PASS(SelectRepresentations_Final);
INVOKE_PASS(Canonicalize);
INVOKE_PASS(UseTableDispatch);
INVOKE_PASS(EliminateStackOverflowChecks);
@@ -469,6 +469,13 @@ COMPILER_PASS(SelectRepresentations, {
flow_graph->SelectRepresentations();
});
COMPILER_PASS(SelectRepresentations_Final, {
// Final selection of representations. After this pass
// representations of inputs/outputs should match.
flow_graph->SelectRepresentations();
flow_graph->disallow_unmatched_representations();
});
COMPILER_PASS(UseTableDispatch, {
state->call_specializer->ReplaceInstanceCallsWithDispatchTableCalls();
});
+1
View File
@@ -46,6 +46,7 @@ namespace dart {
V(RangeAnalysis) \
V(ReorderBlocks) \
V(SelectRepresentations) \
V(SelectRepresentations_Final) \
V(SetOuterInliningId) \
V(TryCatchOptimization) \
V(TryOptimizePatterns) \