Skip to content

Commit 0eb44ec

Browse files
committed
Use same range info when emitting code and computing if instruction can deopt.
For BinarySmiOpInstr and ShiftMintOpInstr we use range information to both decide if instruction can deopt and decide which checks should be emitted when emitting native code for this instruction. However because range information is attached to the definition and not uses it often gets out of sync as we mutate the graph. For example we might first make a decision that an instruction can't deoptimize based on more precise range information but then use less precise information in the backend for deciding which parts of the instruction to emit (because redifinition or a phi-instruction was removed from the graph). This mismatch can lead to a crash if less precise information tells backend that one of the inputs need to be checked - because there is no deoptimization label to jump to. This CL is addressing this problem by ensuring that range information is cached at the use and both ComputeCanDeoptimize() and backend use the same range information. Additionally this CL kills overly generic MergedMath instruction and replaces it with TruncDivModInstr. BUG=#29620 [email protected] Review-Url: https://codereview.chromium.org/2891113002 .
1 parent 81e428f commit 0eb44ec

15 files changed

+545
-610
lines changed
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// Regression test for dartbug.com/29620: check that decision to deoptimize
6+
// and decisions which parts of the instruction to emit use the same
7+
// range information for instruction inputs.
8+
9+
// VMOptions=--enable-inlining-annotations --optimization_counter_threshold=10 --no-use-osr --no-background-compilation
10+
11+
import "package:expect/expect.dart";
12+
13+
const alwaysInline = "AlwaysInline";
14+
const neverInline = "NeverInline";
15+
16+
class Flag {
17+
var value;
18+
Flag(this.value);
19+
20+
static final FLAG = new Flag(0);
21+
}
22+
23+
@alwaysInline
24+
void checkRange(bit) {
25+
if (bit < 0 || bit > 31) {
26+
throw "bit must be in [0, 31]";
27+
}
28+
}
29+
30+
@alwaysInline
31+
bool isSet(flags, bit) {
32+
checkRange(bit);
33+
// Note: > 0 here instead of == 0 to prevent merging into
34+
// TestSmi instruction.
35+
return (flags & (1 << bit)) > 0;
36+
}
37+
38+
@neverInline
39+
bool bug(flags) {
40+
var bit = Flag.FLAG.value;
41+
checkRange(bit);
42+
for (var i = 0; i < 1; i++) {
43+
bit = Flag.FLAG.value;
44+
checkRange(bit);
45+
}
46+
47+
// In early optimization stages `bit` would be a Phi(...). This Phi would be
48+
// dominated by checkRange and thus range analysis will infer [0, 31] range
49+
// for it - and thus a EliminateEnvironment will make decision that
50+
// (1 << bit) can't deoptimize and will detach environment from it. Later
51+
// passes will eliminate Phi for `bit` as it is redundant and as a result we
52+
// will loose precise range information for `bit` and backend will try
53+
// to emit a range check and a deoptimization.
54+
return isSet(flags, bit);
55+
}
56+
57+
main() {
58+
for (var i = 0; i < 100; i++) {
59+
bug(1);
60+
}
61+
}

runtime/vm/constant_propagator.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1119,7 +1119,7 @@ void ConstantPropagator::VisitInvokeMathCFunction(
11191119
}
11201120

11211121

1122-
void ConstantPropagator::VisitMergedMath(MergedMathInstr* instr) {
1122+
void ConstantPropagator::VisitTruncDivMod(TruncDivModInstr* instr) {
11231123
// TODO(srdjan): Handle merged instruction.
11241124
SetValue(instr, non_constant_);
11251125
}

runtime/vm/flow_graph.cc

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2280,20 +2280,18 @@ void FlowGraph::TryMergeTruncDivMod(
22802280
(*merge_candidates)[k] = NULL; // Clear it.
22812281
ASSERT(curr_instr->HasUses());
22822282
AppendExtractNthOutputForMerged(
2283-
curr_instr, MergedMathInstr::OutputIndexOf(curr_instr->op_kind()),
2283+
curr_instr, TruncDivModInstr::OutputIndexOf(curr_instr->op_kind()),
22842284
kTagged, kSmiCid);
22852285
ASSERT(other_binop->HasUses());
22862286
AppendExtractNthOutputForMerged(
2287-
other_binop, MergedMathInstr::OutputIndexOf(other_binop->op_kind()),
2288-
kTagged, kSmiCid);
2289-
2290-
ZoneGrowableArray<Value*>* args = new (Z) ZoneGrowableArray<Value*>(2);
2291-
args->Add(new (Z) Value(curr_instr->left()->definition()));
2292-
args->Add(new (Z) Value(curr_instr->right()->definition()));
2287+
other_binop,
2288+
TruncDivModInstr::OutputIndexOf(other_binop->op_kind()), kTagged,
2289+
kSmiCid);
22932290

22942291
// Replace with TruncDivMod.
2295-
MergedMathInstr* div_mod = new (Z) MergedMathInstr(
2296-
args, curr_instr->deopt_id(), MergedMathInstr::kTruncDivMod);
2292+
TruncDivModInstr* div_mod = new (Z) TruncDivModInstr(
2293+
curr_instr->left()->CopyWithType(),
2294+
curr_instr->right()->CopyWithType(), curr_instr->deopt_id());
22972295
curr_instr->ReplaceWith(div_mod, NULL);
22982296
other_binop->ReplaceUsesWith(div_mod);
22992297
other_binop->RemoveFromGraph();

runtime/vm/flow_graph_range_analysis.cc

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2256,6 +2256,12 @@ void Range::Clamp(RangeBoundary::RangeSize size) {
22562256
}
22572257

22582258

2259+
void Range::ClampToConstant(RangeBoundary::RangeSize size) {
2260+
min_ = min_.LowerBound().Clamp(size);
2261+
max_ = max_.UpperBound().Clamp(size);
2262+
}
2263+
2264+
22592265
void Range::Shl(const Range* left,
22602266
const Range* right,
22612267
RangeBoundary* result_min,
@@ -2886,10 +2892,32 @@ void BinaryIntegerOpInstr::InferRangeHelper(const Range* left_range,
28862892
}
28872893

28882894

2895+
static void CacheRange(Range** slot,
2896+
const Range* range,
2897+
RangeBoundary::RangeSize size) {
2898+
if (range != NULL) {
2899+
if (*slot == NULL) {
2900+
*slot = new Range();
2901+
}
2902+
**slot = *range;
2903+
2904+
// Eliminate any symbolic dependencies from the range information.
2905+
(*slot)->ClampToConstant(size);
2906+
} else if (*slot != NULL) {
2907+
**slot = Range(); // Clear cached range information.
2908+
}
2909+
}
2910+
2911+
28892912
void BinarySmiOpInstr::InferRange(RangeAnalysis* analysis, Range* range) {
2913+
const Range* right_smi_range = analysis->GetSmiRange(right());
28902914
// TODO(vegorov) completely remove this once GetSmiRange is eliminated.
2891-
InferRangeHelper(analysis->GetSmiRange(left()),
2892-
analysis->GetSmiRange(right()), range);
2915+
if (op_kind() == Token::kSHR || op_kind() == Token::kSHL ||
2916+
op_kind() == Token::kMOD || op_kind() == Token::kTRUNCDIV) {
2917+
CacheRange(&right_range_, right_smi_range,
2918+
RangeBoundary::kRangeBoundarySmi);
2919+
}
2920+
InferRangeHelper(analysis->GetSmiRange(left()), right_smi_range, range);
28932921
}
28942922

28952923

@@ -2906,6 +2934,8 @@ void BinaryMintOpInstr::InferRange(RangeAnalysis* analysis, Range* range) {
29062934

29072935

29082936
void ShiftMintOpInstr::InferRange(RangeAnalysis* analysis, Range* range) {
2937+
CacheRange(&shift_range_, right()->definition()->range(),
2938+
RangeBoundary::kRangeBoundaryInt64);
29092939
InferRangeHelper(left()->definition()->range(),
29102940
right()->definition()->range(), range);
29112941
}

runtime/vm/flow_graph_range_analysis.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,9 @@ class Range : public ZoneAllocated {
396396
// Clamp this to be within size.
397397
void Clamp(RangeBoundary::RangeSize size);
398398

399+
// Clamp this to be within size and eliminate symbols.
400+
void ClampToConstant(RangeBoundary::RangeSize size);
401+
399402
static void Add(const Range* left_range,
400403
const Range* right_range,
401404
RangeBoundary* min,
@@ -469,6 +472,16 @@ class RangeUtils : public AllStatic {
469472
static bool IsPositive(Range* range) {
470473
return !Range::IsUnknown(range) && range->IsPositive();
471474
}
475+
476+
static bool Overlaps(Range* range, intptr_t min, intptr_t max) {
477+
return Range::IsUnknown(range) || range->Overlaps(min, max);
478+
}
479+
480+
static bool CanBeZero(Range* range) { return Overlaps(range, 0, 0); }
481+
482+
static bool OnlyLessThanOrEqualTo(Range* range, intptr_t value) {
483+
return !Range::IsUnknown(range) && range->OnlyLessThanOrEqualTo(value);
484+
}
472485
};
473486

474487

runtime/vm/flow_graph_type_propagator.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1520,7 +1520,7 @@ CompileType InvokeMathCFunctionInstr::ComputeType() const {
15201520
}
15211521

15221522

1523-
CompileType MergedMathInstr::ComputeType() const {
1523+
CompileType TruncDivModInstr::ComputeType() const {
15241524
return CompileType::Dynamic();
15251525
}
15261526

runtime/vm/il_printer.cc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -738,8 +738,7 @@ void MathUnaryInstr::PrintOperandsTo(BufferFormatter* f) const {
738738
}
739739

740740

741-
void MergedMathInstr::PrintOperandsTo(BufferFormatter* f) const {
742-
f->Print("'%s', ", MergedMathInstr::KindToCString(kind()));
741+
void TruncDivModInstr::PrintOperandsTo(BufferFormatter* f) const {
743742
Definition::PrintOperandsTo(f);
744743
}
745744

runtime/vm/intermediate_language.cc

Lines changed: 18 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1392,8 +1392,9 @@ bool BinaryInt32OpInstr::ComputeCanDeoptimize() const {
13921392
return false;
13931393

13941394
case Token::kSHL:
1395-
return can_overflow() ||
1396-
!RangeUtils::IsPositive(right()->definition()->range());
1395+
// Currently only shifts by in range constant are supported, see
1396+
// BinaryInt32OpInstr::IsSupported.
1397+
return can_overflow();
13971398

13981399
case Token::kMOD: {
13991400
UNREACHABLE();
@@ -1413,22 +1414,25 @@ bool BinarySmiOpInstr::ComputeCanDeoptimize() const {
14131414
return false;
14141415

14151416
case Token::kSHR:
1416-
return !RangeUtils::IsPositive(right()->definition()->range());
1417+
return !RangeUtils::IsPositive(right_range());
14171418

14181419
case Token::kSHL:
1419-
return can_overflow() ||
1420-
!RangeUtils::IsPositive(right()->definition()->range());
1420+
return can_overflow() || !RangeUtils::IsPositive(right_range());
1421+
1422+
case Token::kMOD:
1423+
return RangeUtils::CanBeZero(right_range());
14211424

1422-
case Token::kMOD: {
1423-
Range* right_range = this->right()->definition()->range();
1424-
return (right_range == NULL) || right_range->Overlaps(0, 0);
1425-
}
14261425
default:
14271426
return can_overflow();
14281427
}
14291428
}
14301429

14311430

1431+
bool ShiftMintOpInstr::has_shift_count_check() const {
1432+
return !RangeUtils::IsWithin(shift_range(), 0, kMintShiftCountLimit);
1433+
}
1434+
1435+
14321436
bool BinaryIntegerOpInstr::RightIsPowerOfTwoConstant() const {
14331437
if (!right()->definition()->IsConstant()) return false;
14341438
const Object& constant = right()->definition()->AsConstant()->value();
@@ -4246,33 +4250,14 @@ const RuntimeEntry& CaseInsensitiveCompareUC16Instr::TargetFunction() const {
42464250
}
42474251

42484252

4249-
MergedMathInstr::MergedMathInstr(ZoneGrowableArray<Value*>* inputs,
4250-
intptr_t deopt_id,
4251-
MergedMathInstr::Kind kind)
4252-
: PureDefinition(deopt_id), inputs_(inputs), kind_(kind) {
4253-
ASSERT(inputs_->length() == InputCountFor(kind_));
4254-
for (intptr_t i = 0; i < inputs_->length(); ++i) {
4255-
ASSERT((*inputs)[i] != NULL);
4256-
(*inputs)[i]->set_instruction(this);
4257-
(*inputs)[i]->set_use_index(i);
4258-
}
4259-
}
4260-
4261-
4262-
intptr_t MergedMathInstr::OutputIndexOf(MethodRecognizer::Kind kind) {
4263-
switch (kind) {
4264-
case MethodRecognizer::kMathSin:
4265-
return 1;
4266-
case MethodRecognizer::kMathCos:
4267-
return 0;
4268-
default:
4269-
UNIMPLEMENTED();
4270-
return -1;
4271-
}
4253+
TruncDivModInstr::TruncDivModInstr(Value* lhs, Value* rhs, intptr_t deopt_id)
4254+
: TemplateDefinition(deopt_id) {
4255+
SetInputAt(0, lhs);
4256+
SetInputAt(1, rhs);
42724257
}
42734258

42744259

4275-
intptr_t MergedMathInstr::OutputIndexOf(Token::Kind token) {
4260+
intptr_t TruncDivModInstr::OutputIndexOf(Token::Kind token) {
42764261
switch (token) {
42774262
case Token::kTRUNCDIV:
42784263
return 0;

0 commit comments

Comments
 (0)