1 // Copyright 2013 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/crankshaft/hydrogen-range-analysis.h"
6
7 namespace v8 {
8 namespace internal {
9
10
11 class Pending {
12 public:
Pending(HBasicBlock * block,int last_changed_range)13 Pending(HBasicBlock* block, int last_changed_range)
14 : block_(block), last_changed_range_(last_changed_range) {}
15
block() const16 HBasicBlock* block() const { return block_; }
last_changed_range() const17 int last_changed_range() const { return last_changed_range_; }
18
19 private:
20 HBasicBlock* block_;
21 int last_changed_range_;
22 };
23
24
TraceRange(const char * msg,...)25 void HRangeAnalysisPhase::TraceRange(const char* msg, ...) {
26 if (FLAG_trace_range) {
27 va_list arguments;
28 va_start(arguments, msg);
29 base::OS::VPrint(msg, arguments);
30 va_end(arguments);
31 }
32 }
33
34
Run()35 void HRangeAnalysisPhase::Run() {
36 HBasicBlock* block(graph()->entry_block());
37 ZoneList<Pending> stack(graph()->blocks()->length(), zone());
38 while (block != NULL) {
39 TraceRange("Analyzing block B%d\n", block->block_id());
40
41 // Infer range based on control flow.
42 if (block->predecessors()->length() == 1) {
43 HBasicBlock* pred = block->predecessors()->first();
44 if (pred->end()->IsCompareNumericAndBranch()) {
45 InferControlFlowRange(HCompareNumericAndBranch::cast(pred->end()),
46 block);
47 }
48 }
49
50 // Process phi instructions.
51 for (int i = 0; i < block->phis()->length(); ++i) {
52 HPhi* phi = block->phis()->at(i);
53 InferRange(phi);
54 }
55
56 // Go through all instructions of the current block.
57 for (HInstructionIterator it(block); !it.Done(); it.Advance()) {
58 HValue* value = it.Current();
59 InferRange(value);
60
61 // Compute the bailout-on-minus-zero flag.
62 if (value->IsChange()) {
63 HChange* instr = HChange::cast(value);
64 // Propagate flags for negative zero checks upwards from conversions
65 // int32-to-tagged and int32-to-double.
66 Representation from = instr->value()->representation();
67 DCHECK(from.Equals(instr->from()));
68 if (from.IsSmiOrInteger32()) {
69 DCHECK(instr->to().IsTagged() ||
70 instr->to().IsDouble() ||
71 instr->to().IsSmiOrInteger32());
72 PropagateMinusZeroChecks(instr->value());
73 }
74 } else if (value->IsCompareMinusZeroAndBranch()) {
75 HCompareMinusZeroAndBranch* instr =
76 HCompareMinusZeroAndBranch::cast(value);
77 if (instr->value()->representation().IsSmiOrInteger32()) {
78 PropagateMinusZeroChecks(instr->value());
79 }
80 }
81 }
82
83 // Continue analysis in all dominated blocks.
84 const ZoneList<HBasicBlock*>* dominated_blocks(block->dominated_blocks());
85 if (!dominated_blocks->is_empty()) {
86 // Continue with first dominated block, and push the
87 // remaining blocks on the stack (in reverse order).
88 int last_changed_range = changed_ranges_.length();
89 for (int i = dominated_blocks->length() - 1; i > 0; --i) {
90 stack.Add(Pending(dominated_blocks->at(i), last_changed_range), zone());
91 }
92 block = dominated_blocks->at(0);
93 } else if (!stack.is_empty()) {
94 // Pop next pending block from stack.
95 Pending pending = stack.RemoveLast();
96 RollBackTo(pending.last_changed_range());
97 block = pending.block();
98 } else {
99 // All blocks done.
100 block = NULL;
101 }
102 }
103
104 // The ranges are not valid anymore due to SSI vs. SSA!
105 PoisonRanges();
106 }
107
108
PoisonRanges()109 void HRangeAnalysisPhase::PoisonRanges() {
110 #ifdef DEBUG
111 for (int i = 0; i < graph()->blocks()->length(); ++i) {
112 HBasicBlock* block = graph()->blocks()->at(i);
113 for (HInstructionIterator it(block); !it.Done(); it.Advance()) {
114 HInstruction* instr = it.Current();
115 if (instr->HasRange()) instr->PoisonRange();
116 }
117 }
118 #endif
119 }
120
121
InferControlFlowRange(HCompareNumericAndBranch * test,HBasicBlock * dest)122 void HRangeAnalysisPhase::InferControlFlowRange(HCompareNumericAndBranch* test,
123 HBasicBlock* dest) {
124 DCHECK((test->FirstSuccessor() == dest) == (test->SecondSuccessor() != dest));
125 if (test->representation().IsSmiOrInteger32()) {
126 Token::Value op = test->token();
127 if (test->SecondSuccessor() == dest) {
128 op = Token::NegateCompareOp(op);
129 }
130 Token::Value inverted_op = Token::ReverseCompareOp(op);
131 UpdateControlFlowRange(op, test->left(), test->right());
132 UpdateControlFlowRange(inverted_op, test->right(), test->left());
133 }
134 }
135
136
137 // We know that value [op] other. Use this information to update the range on
138 // value.
UpdateControlFlowRange(Token::Value op,HValue * value,HValue * other)139 void HRangeAnalysisPhase::UpdateControlFlowRange(Token::Value op,
140 HValue* value,
141 HValue* other) {
142 Range temp_range;
143 Range* range = other->range() != NULL ? other->range() : &temp_range;
144 Range* new_range = NULL;
145
146 TraceRange("Control flow range infer %d %s %d\n",
147 value->id(),
148 Token::Name(op),
149 other->id());
150
151 if (op == Token::EQ || op == Token::EQ_STRICT) {
152 // The same range has to apply for value.
153 new_range = range->Copy(graph()->zone());
154 } else if (op == Token::LT || op == Token::LTE) {
155 new_range = range->CopyClearLower(graph()->zone());
156 if (op == Token::LT) {
157 new_range->AddConstant(-1);
158 }
159 } else if (op == Token::GT || op == Token::GTE) {
160 new_range = range->CopyClearUpper(graph()->zone());
161 if (op == Token::GT) {
162 new_range->AddConstant(1);
163 }
164 }
165
166 if (new_range != NULL && !new_range->IsMostGeneric()) {
167 AddRange(value, new_range);
168 }
169 }
170
171
InferRange(HValue * value)172 void HRangeAnalysisPhase::InferRange(HValue* value) {
173 DCHECK(!value->HasRange());
174 if (!value->representation().IsNone()) {
175 value->ComputeInitialRange(graph()->zone());
176 Range* range = value->range();
177 TraceRange("Initial inferred range of %d (%s) set to [%d,%d]\n",
178 value->id(),
179 value->Mnemonic(),
180 range->lower(),
181 range->upper());
182 }
183 }
184
185
RollBackTo(int index)186 void HRangeAnalysisPhase::RollBackTo(int index) {
187 DCHECK(index <= changed_ranges_.length());
188 for (int i = index; i < changed_ranges_.length(); ++i) {
189 changed_ranges_[i]->RemoveLastAddedRange();
190 }
191 changed_ranges_.Rewind(index);
192 }
193
194
AddRange(HValue * value,Range * range)195 void HRangeAnalysisPhase::AddRange(HValue* value, Range* range) {
196 Range* original_range = value->range();
197 value->AddNewRange(range, graph()->zone());
198 changed_ranges_.Add(value, zone());
199 Range* new_range = value->range();
200 TraceRange("Updated range of %d set to [%d,%d]\n",
201 value->id(),
202 new_range->lower(),
203 new_range->upper());
204 if (original_range != NULL) {
205 TraceRange("Original range was [%d,%d]\n",
206 original_range->lower(),
207 original_range->upper());
208 }
209 TraceRange("New information was [%d,%d]\n",
210 range->lower(),
211 range->upper());
212 }
213
214
PropagateMinusZeroChecks(HValue * value)215 void HRangeAnalysisPhase::PropagateMinusZeroChecks(HValue* value) {
216 DCHECK(worklist_.is_empty());
217 DCHECK(in_worklist_.IsEmpty());
218
219 AddToWorklist(value);
220 while (!worklist_.is_empty()) {
221 value = worklist_.RemoveLast();
222
223 if (value->IsPhi()) {
224 // For phis, we must propagate the check to all of its inputs.
225 HPhi* phi = HPhi::cast(value);
226 for (int i = 0; i < phi->OperandCount(); ++i) {
227 AddToWorklist(phi->OperandAt(i));
228 }
229 } else if (value->IsUnaryMathOperation()) {
230 HUnaryMathOperation* instr = HUnaryMathOperation::cast(value);
231 if (instr->representation().IsSmiOrInteger32() &&
232 !instr->value()->representation().Equals(instr->representation())) {
233 if (instr->value()->range() == NULL ||
234 instr->value()->range()->CanBeMinusZero()) {
235 instr->SetFlag(HValue::kBailoutOnMinusZero);
236 }
237 }
238 if (instr->RequiredInputRepresentation(0).IsSmiOrInteger32() &&
239 instr->representation().Equals(
240 instr->RequiredInputRepresentation(0))) {
241 AddToWorklist(instr->value());
242 }
243 } else if (value->IsChange()) {
244 HChange* instr = HChange::cast(value);
245 if (!instr->from().IsSmiOrInteger32() &&
246 !instr->CanTruncateToInt32() &&
247 (instr->value()->range() == NULL ||
248 instr->value()->range()->CanBeMinusZero())) {
249 instr->SetFlag(HValue::kBailoutOnMinusZero);
250 }
251 } else if (value->IsForceRepresentation()) {
252 HForceRepresentation* instr = HForceRepresentation::cast(value);
253 AddToWorklist(instr->value());
254 } else if (value->IsMod()) {
255 HMod* instr = HMod::cast(value);
256 if (instr->range() == NULL || instr->range()->CanBeMinusZero()) {
257 instr->SetFlag(HValue::kBailoutOnMinusZero);
258 AddToWorklist(instr->left());
259 }
260 } else if (value->IsDiv() || value->IsMul()) {
261 HBinaryOperation* instr = HBinaryOperation::cast(value);
262 if (instr->range() == NULL || instr->range()->CanBeMinusZero()) {
263 instr->SetFlag(HValue::kBailoutOnMinusZero);
264 }
265 AddToWorklist(instr->right());
266 AddToWorklist(instr->left());
267 } else if (value->IsMathFloorOfDiv()) {
268 HMathFloorOfDiv* instr = HMathFloorOfDiv::cast(value);
269 instr->SetFlag(HValue::kBailoutOnMinusZero);
270 } else if (value->IsAdd() || value->IsSub()) {
271 HBinaryOperation* instr = HBinaryOperation::cast(value);
272 if (instr->range() == NULL || instr->range()->CanBeMinusZero()) {
273 // Propagate to the left argument. If the left argument cannot be -0,
274 // then the result of the add/sub operation cannot be either.
275 AddToWorklist(instr->left());
276 }
277 } else if (value->IsMathMinMax()) {
278 HMathMinMax* instr = HMathMinMax::cast(value);
279 AddToWorklist(instr->right());
280 AddToWorklist(instr->left());
281 }
282 }
283
284 in_worklist_.Clear();
285 DCHECK(in_worklist_.IsEmpty());
286 DCHECK(worklist_.is_empty());
287 }
288
289
290 } // namespace internal
291 } // namespace v8
292