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: 13 Pending(HBasicBlock* block, int last_changed_range) 14 : block_(block), last_changed_range_(last_changed_range) {} 15 16 HBasicBlock* block() const { return block_; } 17 int last_changed_range() const { return last_changed_range_; } 18 19 private: 20 HBasicBlock* block_; 21 int last_changed_range_; 22 }; 23 24 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 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 } 75 } 76 77 // Continue analysis in all dominated blocks. 78 const ZoneList<HBasicBlock*>* dominated_blocks(block->dominated_blocks()); 79 if (!dominated_blocks->is_empty()) { 80 // Continue with first dominated block, and push the 81 // remaining blocks on the stack (in reverse order). 82 int last_changed_range = changed_ranges_.length(); 83 for (int i = dominated_blocks->length() - 1; i > 0; --i) { 84 stack.Add(Pending(dominated_blocks->at(i), last_changed_range), zone()); 85 } 86 block = dominated_blocks->at(0); 87 } else if (!stack.is_empty()) { 88 // Pop next pending block from stack. 89 Pending pending = stack.RemoveLast(); 90 RollBackTo(pending.last_changed_range()); 91 block = pending.block(); 92 } else { 93 // All blocks done. 94 block = NULL; 95 } 96 } 97 98 // The ranges are not valid anymore due to SSI vs. SSA! 99 PoisonRanges(); 100 } 101 102 103 void HRangeAnalysisPhase::PoisonRanges() { 104 #ifdef DEBUG 105 for (int i = 0; i < graph()->blocks()->length(); ++i) { 106 HBasicBlock* block = graph()->blocks()->at(i); 107 for (HInstructionIterator it(block); !it.Done(); it.Advance()) { 108 HInstruction* instr = it.Current(); 109 if (instr->HasRange()) instr->PoisonRange(); 110 } 111 } 112 #endif 113 } 114 115 116 void HRangeAnalysisPhase::InferControlFlowRange(HCompareNumericAndBranch* test, 117 HBasicBlock* dest) { 118 DCHECK((test->FirstSuccessor() == dest) == (test->SecondSuccessor() != dest)); 119 if (test->representation().IsSmiOrInteger32()) { 120 Token::Value op = test->token(); 121 if (test->SecondSuccessor() == dest) { 122 op = Token::NegateCompareOp(op); 123 } 124 Token::Value inverted_op = Token::ReverseCompareOp(op); 125 UpdateControlFlowRange(op, test->left(), test->right()); 126 UpdateControlFlowRange(inverted_op, test->right(), test->left()); 127 } 128 } 129 130 131 // We know that value [op] other. Use this information to update the range on 132 // value. 133 void HRangeAnalysisPhase::UpdateControlFlowRange(Token::Value op, 134 HValue* value, 135 HValue* other) { 136 Range temp_range; 137 Range* range = other->range() != NULL ? other->range() : &temp_range; 138 Range* new_range = NULL; 139 140 TraceRange("Control flow range infer %d %s %d\n", 141 value->id(), 142 Token::Name(op), 143 other->id()); 144 145 if (op == Token::EQ || op == Token::EQ_STRICT) { 146 // The same range has to apply for value. 147 new_range = range->Copy(graph()->zone()); 148 } else if (op == Token::LT || op == Token::LTE) { 149 new_range = range->CopyClearLower(graph()->zone()); 150 if (op == Token::LT) { 151 new_range->AddConstant(-1); 152 } 153 } else if (op == Token::GT || op == Token::GTE) { 154 new_range = range->CopyClearUpper(graph()->zone()); 155 if (op == Token::GT) { 156 new_range->AddConstant(1); 157 } 158 } 159 160 if (new_range != NULL && !new_range->IsMostGeneric()) { 161 AddRange(value, new_range); 162 } 163 } 164 165 166 void HRangeAnalysisPhase::InferRange(HValue* value) { 167 DCHECK(!value->HasRange()); 168 if (!value->representation().IsNone()) { 169 value->ComputeInitialRange(graph()->zone()); 170 Range* range = value->range(); 171 TraceRange("Initial inferred range of %d (%s) set to [%d,%d]\n", 172 value->id(), 173 value->Mnemonic(), 174 range->lower(), 175 range->upper()); 176 } 177 } 178 179 180 void HRangeAnalysisPhase::RollBackTo(int index) { 181 DCHECK(index <= changed_ranges_.length()); 182 for (int i = index; i < changed_ranges_.length(); ++i) { 183 changed_ranges_[i]->RemoveLastAddedRange(); 184 } 185 changed_ranges_.Rewind(index); 186 } 187 188 189 void HRangeAnalysisPhase::AddRange(HValue* value, Range* range) { 190 Range* original_range = value->range(); 191 value->AddNewRange(range, graph()->zone()); 192 changed_ranges_.Add(value, zone()); 193 Range* new_range = value->range(); 194 TraceRange("Updated range of %d set to [%d,%d]\n", 195 value->id(), 196 new_range->lower(), 197 new_range->upper()); 198 if (original_range != NULL) { 199 TraceRange("Original range was [%d,%d]\n", 200 original_range->lower(), 201 original_range->upper()); 202 } 203 TraceRange("New information was [%d,%d]\n", 204 range->lower(), 205 range->upper()); 206 } 207 208 209 void HRangeAnalysisPhase::PropagateMinusZeroChecks(HValue* value) { 210 DCHECK(worklist_.is_empty()); 211 DCHECK(in_worklist_.IsEmpty()); 212 213 AddToWorklist(value); 214 while (!worklist_.is_empty()) { 215 value = worklist_.RemoveLast(); 216 217 if (value->IsPhi()) { 218 // For phis, we must propagate the check to all of its inputs. 219 HPhi* phi = HPhi::cast(value); 220 for (int i = 0; i < phi->OperandCount(); ++i) { 221 AddToWorklist(phi->OperandAt(i)); 222 } 223 } else if (value->IsUnaryMathOperation()) { 224 HUnaryMathOperation* instr = HUnaryMathOperation::cast(value); 225 if (instr->representation().IsSmiOrInteger32() && 226 !instr->value()->representation().Equals(instr->representation())) { 227 if (instr->value()->range() == NULL || 228 instr->value()->range()->CanBeMinusZero()) { 229 instr->SetFlag(HValue::kBailoutOnMinusZero); 230 } 231 } 232 if (instr->RequiredInputRepresentation(0).IsSmiOrInteger32() && 233 instr->representation().Equals( 234 instr->RequiredInputRepresentation(0))) { 235 AddToWorklist(instr->value()); 236 } 237 } else if (value->IsChange()) { 238 HChange* instr = HChange::cast(value); 239 if (!instr->from().IsSmiOrInteger32() && 240 !instr->CanTruncateToInt32() && 241 (instr->value()->range() == NULL || 242 instr->value()->range()->CanBeMinusZero())) { 243 instr->SetFlag(HValue::kBailoutOnMinusZero); 244 } 245 } else if (value->IsForceRepresentation()) { 246 HForceRepresentation* instr = HForceRepresentation::cast(value); 247 AddToWorklist(instr->value()); 248 } else if (value->IsMod()) { 249 HMod* instr = HMod::cast(value); 250 if (instr->range() == NULL || instr->range()->CanBeMinusZero()) { 251 instr->SetFlag(HValue::kBailoutOnMinusZero); 252 AddToWorklist(instr->left()); 253 } 254 } else if (value->IsDiv() || value->IsMul()) { 255 HBinaryOperation* instr = HBinaryOperation::cast(value); 256 if (instr->range() == NULL || instr->range()->CanBeMinusZero()) { 257 instr->SetFlag(HValue::kBailoutOnMinusZero); 258 } 259 AddToWorklist(instr->right()); 260 AddToWorklist(instr->left()); 261 } else if (value->IsMathFloorOfDiv()) { 262 HMathFloorOfDiv* instr = HMathFloorOfDiv::cast(value); 263 instr->SetFlag(HValue::kBailoutOnMinusZero); 264 } else if (value->IsAdd() || value->IsSub()) { 265 HBinaryOperation* instr = HBinaryOperation::cast(value); 266 if (instr->range() == NULL || instr->range()->CanBeMinusZero()) { 267 // Propagate to the left argument. If the left argument cannot be -0, 268 // then the result of the add/sub operation cannot be either. 269 AddToWorklist(instr->left()); 270 } 271 } else if (value->IsMathMinMax()) { 272 HMathMinMax* instr = HMathMinMax::cast(value); 273 AddToWorklist(instr->right()); 274 AddToWorklist(instr->left()); 275 } 276 } 277 278 in_worklist_.Clear(); 279 DCHECK(in_worklist_.IsEmpty()); 280 DCHECK(worklist_.is_empty()); 281 } 282 283 284 } // namespace internal 285 } // namespace v8 286