1 // Copyright 2014 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 #ifndef V8_COMPILER_REPRESENTATION_CHANGE_H_ 6 #define V8_COMPILER_REPRESENTATION_CHANGE_H_ 7 8 #include "src/compiler/feedback-source.h" 9 #include "src/compiler/js-graph.h" 10 #include "src/compiler/simplified-operator.h" 11 12 namespace v8 { 13 namespace internal { 14 namespace compiler { 15 16 // Foward declarations. 17 class SimplifiedLoweringVerifier; 18 class TypeCache; 19 20 enum IdentifyZeros : uint8_t { kIdentifyZeros, kDistinguishZeros }; 21 22 class Truncation final { 23 public: 24 // Constructors. None()25 static Truncation None() { 26 return Truncation(TruncationKind::kNone, kIdentifyZeros); 27 } Bool()28 static Truncation Bool() { 29 return Truncation(TruncationKind::kBool, kIdentifyZeros); 30 } Word32()31 static Truncation Word32() { 32 return Truncation(TruncationKind::kWord32, kIdentifyZeros); 33 } Word64()34 static Truncation Word64() { 35 return Truncation(TruncationKind::kWord64, kIdentifyZeros); 36 } 37 static Truncation OddballAndBigIntToNumber( 38 IdentifyZeros identify_zeros = kDistinguishZeros) { 39 return Truncation(TruncationKind::kOddballAndBigIntToNumber, 40 identify_zeros); 41 } 42 static Truncation Any(IdentifyZeros identify_zeros = kDistinguishZeros) { 43 return Truncation(TruncationKind::kAny, identify_zeros); 44 } 45 Generalize(Truncation t1,Truncation t2)46 static Truncation Generalize(Truncation t1, Truncation t2) { 47 return Truncation( 48 Generalize(t1.kind(), t2.kind()), 49 GeneralizeIdentifyZeros(t1.identify_zeros(), t2.identify_zeros())); 50 } 51 52 // Queries. IsUnused()53 bool IsUnused() const { return kind_ == TruncationKind::kNone; } IsUsedAsBool()54 bool IsUsedAsBool() const { 55 return LessGeneral(kind_, TruncationKind::kBool); 56 } IsUsedAsWord32()57 bool IsUsedAsWord32() const { 58 return LessGeneral(kind_, TruncationKind::kWord32); 59 } IsUsedAsWord64()60 bool IsUsedAsWord64() const { 61 return LessGeneral(kind_, TruncationKind::kWord64); 62 } TruncatesOddballAndBigIntToNumber()63 bool TruncatesOddballAndBigIntToNumber() const { 64 return LessGeneral(kind_, TruncationKind::kOddballAndBigIntToNumber); 65 } IdentifiesUndefinedAndZero()66 bool IdentifiesUndefinedAndZero() { 67 return LessGeneral(kind_, TruncationKind::kWord32) || 68 LessGeneral(kind_, TruncationKind::kBool); 69 } IdentifiesZeroAndMinusZero()70 bool IdentifiesZeroAndMinusZero() const { 71 return identify_zeros() == kIdentifyZeros; 72 } 73 74 // Operators. 75 bool operator==(Truncation other) const { 76 return kind() == other.kind() && identify_zeros() == other.identify_zeros(); 77 } 78 bool operator!=(Truncation other) const { return !(*this == other); } 79 80 // Debug utilities. 81 const char* description() const; IsLessGeneralThan(Truncation other)82 bool IsLessGeneralThan(Truncation other) const { 83 return LessGeneral(kind(), other.kind()) && 84 LessGeneralIdentifyZeros(identify_zeros(), other.identify_zeros()); 85 } 86 identify_zeros()87 IdentifyZeros identify_zeros() const { return identify_zeros_; } 88 89 private: 90 enum class TruncationKind : uint8_t { 91 kNone, 92 kBool, 93 kWord32, 94 kWord64, 95 kOddballAndBigIntToNumber, 96 kAny 97 }; 98 Truncation(TruncationKind kind,IdentifyZeros identify_zeros)99 explicit Truncation(TruncationKind kind, IdentifyZeros identify_zeros) 100 : kind_(kind), identify_zeros_(identify_zeros) {} 101 kind()102 TruncationKind kind() const { return kind_; } 103 104 friend class SimplifiedLoweringVerifier; 105 TruncationKind kind_; 106 IdentifyZeros identify_zeros_; 107 108 static TruncationKind Generalize(TruncationKind rep1, TruncationKind rep2); 109 static IdentifyZeros GeneralizeIdentifyZeros(IdentifyZeros i1, 110 IdentifyZeros i2); 111 static bool LessGeneral(TruncationKind rep1, TruncationKind rep2); 112 static bool LessGeneralIdentifyZeros(IdentifyZeros u1, IdentifyZeros u2); 113 }; 114 115 enum class TypeCheckKind : uint8_t { 116 kNone, 117 kSignedSmall, 118 kSigned32, 119 kSigned64, 120 kNumber, 121 kNumberOrBoolean, 122 kNumberOrOddball, 123 kHeapObject, 124 kBigInt, 125 kArrayIndex 126 }; 127 128 inline std::ostream& operator<<(std::ostream& os, TypeCheckKind type_check) { 129 switch (type_check) { 130 case TypeCheckKind::kNone: 131 return os << "None"; 132 case TypeCheckKind::kSignedSmall: 133 return os << "SignedSmall"; 134 case TypeCheckKind::kSigned32: 135 return os << "Signed32"; 136 case TypeCheckKind::kSigned64: 137 return os << "Signed64"; 138 case TypeCheckKind::kNumber: 139 return os << "Number"; 140 case TypeCheckKind::kNumberOrBoolean: 141 return os << "NumberOrBoolean"; 142 case TypeCheckKind::kNumberOrOddball: 143 return os << "NumberOrOddball"; 144 case TypeCheckKind::kHeapObject: 145 return os << "HeapObject"; 146 case TypeCheckKind::kBigInt: 147 return os << "BigInt"; 148 case TypeCheckKind::kArrayIndex: 149 return os << "ArrayIndex"; 150 } 151 UNREACHABLE(); 152 } 153 154 // The {UseInfo} class is used to describe a use of an input of a node. 155 // 156 // This information is used in two different ways, based on the phase: 157 // 158 // 1. During propagation, the use info is used to inform the input node 159 // about what part of the input is used (we call this truncation) and what 160 // is the preferred representation. For conversions that will require 161 // checks, we also keep track of whether a minus zero check is needed. 162 // 163 // 2. During lowering, the use info is used to properly convert the input 164 // to the preferred representation. The preferred representation might be 165 // insufficient to do the conversion (e.g. word32->float64 conv), so we also 166 // need the signedness information to produce the correct value. 167 // Additionally, use info may contain {CheckParameters} which contains 168 // information for the deoptimizer such as a CallIC on which speculation 169 // should be disallowed if the check fails. 170 class UseInfo { 171 public: 172 UseInfo(MachineRepresentation representation, Truncation truncation, 173 TypeCheckKind type_check = TypeCheckKind::kNone, 174 const FeedbackSource& feedback = FeedbackSource()) representation_(representation)175 : representation_(representation), 176 truncation_(truncation), 177 type_check_(type_check), 178 feedback_(feedback) {} TruncatingWord32()179 static UseInfo TruncatingWord32() { 180 return UseInfo(MachineRepresentation::kWord32, Truncation::Word32()); 181 } CheckedBigIntTruncatingWord64(const FeedbackSource & feedback)182 static UseInfo CheckedBigIntTruncatingWord64(const FeedbackSource& feedback) { 183 // Note that Trunction::Word64() can safely use kIdentifyZero, because 184 // TypeCheckKind::kBigInt will make sure we deopt for anything other than 185 // type BigInt anyway. 186 return UseInfo(MachineRepresentation::kWord64, Truncation::Word64(), 187 TypeCheckKind::kBigInt, feedback); 188 } Word64()189 static UseInfo Word64() { 190 return UseInfo(MachineRepresentation::kWord64, Truncation::Any()); 191 } Word()192 static UseInfo Word() { 193 return UseInfo(MachineType::PointerRepresentation(), Truncation::Any()); 194 } Bool()195 static UseInfo Bool() { 196 return UseInfo(MachineRepresentation::kBit, Truncation::Bool()); 197 } Float32()198 static UseInfo Float32() { 199 return UseInfo(MachineRepresentation::kFloat32, Truncation::Any()); 200 } Float64()201 static UseInfo Float64() { 202 return UseInfo(MachineRepresentation::kFloat64, Truncation::Any()); 203 } 204 static UseInfo TruncatingFloat64( 205 IdentifyZeros identify_zeros = kDistinguishZeros) { 206 return UseInfo(MachineRepresentation::kFloat64, 207 Truncation::OddballAndBigIntToNumber(identify_zeros)); 208 } AnyTagged()209 static UseInfo AnyTagged() { 210 return UseInfo(MachineRepresentation::kTagged, Truncation::Any()); 211 } TaggedSigned()212 static UseInfo TaggedSigned() { 213 return UseInfo(MachineRepresentation::kTaggedSigned, Truncation::Any()); 214 } TaggedPointer()215 static UseInfo TaggedPointer() { 216 return UseInfo(MachineRepresentation::kTaggedPointer, Truncation::Any()); 217 } 218 219 // Possibly deoptimizing conversions. CheckedTaggedAsArrayIndex(const FeedbackSource & feedback)220 static UseInfo CheckedTaggedAsArrayIndex(const FeedbackSource& feedback) { 221 return UseInfo(MachineType::PointerRepresentation(), 222 Truncation::Any(kIdentifyZeros), TypeCheckKind::kArrayIndex, 223 feedback); 224 } CheckedHeapObjectAsTaggedPointer(const FeedbackSource & feedback)225 static UseInfo CheckedHeapObjectAsTaggedPointer( 226 const FeedbackSource& feedback) { 227 return UseInfo(MachineRepresentation::kTaggedPointer, Truncation::Any(), 228 TypeCheckKind::kHeapObject, feedback); 229 } 230 CheckedBigIntAsTaggedPointer(const FeedbackSource & feedback)231 static UseInfo CheckedBigIntAsTaggedPointer(const FeedbackSource& feedback) { 232 return UseInfo(MachineRepresentation::kTaggedPointer, Truncation::Any(), 233 TypeCheckKind::kBigInt, feedback); 234 } 235 236 static UseInfo CheckedSignedSmallAsTaggedSigned( 237 const FeedbackSource& feedback, 238 IdentifyZeros identify_zeros = kDistinguishZeros) { 239 return UseInfo(MachineRepresentation::kTaggedSigned, 240 Truncation::Any(identify_zeros), TypeCheckKind::kSignedSmall, 241 feedback); 242 } CheckedSignedSmallAsWord32(IdentifyZeros identify_zeros,const FeedbackSource & feedback)243 static UseInfo CheckedSignedSmallAsWord32(IdentifyZeros identify_zeros, 244 const FeedbackSource& feedback) { 245 return UseInfo(MachineRepresentation::kWord32, 246 Truncation::Any(identify_zeros), TypeCheckKind::kSignedSmall, 247 feedback); 248 } CheckedSigned32AsWord32(IdentifyZeros identify_zeros,const FeedbackSource & feedback)249 static UseInfo CheckedSigned32AsWord32(IdentifyZeros identify_zeros, 250 const FeedbackSource& feedback) { 251 return UseInfo(MachineRepresentation::kWord32, 252 Truncation::Any(identify_zeros), TypeCheckKind::kSigned32, 253 feedback); 254 } CheckedSigned64AsWord64(IdentifyZeros identify_zeros,const FeedbackSource & feedback)255 static UseInfo CheckedSigned64AsWord64(IdentifyZeros identify_zeros, 256 const FeedbackSource& feedback) { 257 return UseInfo(MachineRepresentation::kWord64, 258 Truncation::Any(identify_zeros), TypeCheckKind::kSigned64, 259 feedback); 260 } CheckedNumberAsFloat64(IdentifyZeros identify_zeros,const FeedbackSource & feedback)261 static UseInfo CheckedNumberAsFloat64(IdentifyZeros identify_zeros, 262 const FeedbackSource& feedback) { 263 return UseInfo(MachineRepresentation::kFloat64, 264 Truncation::Any(identify_zeros), TypeCheckKind::kNumber, 265 feedback); 266 } CheckedNumberAsWord32(const FeedbackSource & feedback)267 static UseInfo CheckedNumberAsWord32(const FeedbackSource& feedback) { 268 return UseInfo(MachineRepresentation::kWord32, Truncation::Word32(), 269 TypeCheckKind::kNumber, feedback); 270 } CheckedNumberOrBooleanAsFloat64(IdentifyZeros identify_zeros,const FeedbackSource & feedback)271 static UseInfo CheckedNumberOrBooleanAsFloat64( 272 IdentifyZeros identify_zeros, const FeedbackSource& feedback) { 273 return UseInfo(MachineRepresentation::kFloat64, 274 Truncation::Any(identify_zeros), 275 TypeCheckKind::kNumberOrBoolean, feedback); 276 } CheckedNumberOrOddballAsFloat64(IdentifyZeros identify_zeros,const FeedbackSource & feedback)277 static UseInfo CheckedNumberOrOddballAsFloat64( 278 IdentifyZeros identify_zeros, const FeedbackSource& feedback) { 279 return UseInfo(MachineRepresentation::kFloat64, 280 Truncation::Any(identify_zeros), 281 TypeCheckKind::kNumberOrOddball, feedback); 282 } CheckedNumberOrOddballAsWord32(const FeedbackSource & feedback)283 static UseInfo CheckedNumberOrOddballAsWord32( 284 const FeedbackSource& feedback) { 285 return UseInfo(MachineRepresentation::kWord32, Truncation::Word32(), 286 TypeCheckKind::kNumberOrOddball, feedback); 287 } 288 289 // Undetermined representation. Any()290 static UseInfo Any() { 291 return UseInfo(MachineRepresentation::kNone, Truncation::Any()); 292 } AnyTruncatingToBool()293 static UseInfo AnyTruncatingToBool() { 294 return UseInfo(MachineRepresentation::kNone, Truncation::Bool()); 295 } 296 297 // Value not used. None()298 static UseInfo None() { 299 return UseInfo(MachineRepresentation::kNone, Truncation::None()); 300 } 301 representation()302 MachineRepresentation representation() const { return representation_; } truncation()303 Truncation truncation() const { return truncation_; } type_check()304 TypeCheckKind type_check() const { return type_check_; } minus_zero_check()305 CheckForMinusZeroMode minus_zero_check() const { 306 return truncation().IdentifiesZeroAndMinusZero() 307 ? CheckForMinusZeroMode::kDontCheckForMinusZero 308 : CheckForMinusZeroMode::kCheckForMinusZero; 309 } feedback()310 const FeedbackSource& feedback() const { return feedback_; } 311 312 private: 313 MachineRepresentation representation_; 314 Truncation truncation_; 315 TypeCheckKind type_check_; 316 FeedbackSource feedback_; 317 }; 318 319 // Contains logic related to changing the representation of values for constants 320 // and other nodes, as well as lowering Simplified->Machine operators. 321 // Eagerly folds any representation changes for constants. 322 class V8_EXPORT_PRIVATE RepresentationChanger final { 323 public: 324 RepresentationChanger(JSGraph* jsgraph, JSHeapBroker* broker, 325 SimplifiedLoweringVerifier* verifier); 326 327 // Changes representation from {output_type} to {use_rep}. The {truncation} 328 // parameter is only used for checking - if the changer cannot figure 329 // out signedness for the word32->float64 conversion, then we check that the 330 // uses truncate to word32 (so they do not care about signedness). 331 Node* GetRepresentationFor(Node* node, MachineRepresentation output_rep, 332 Type output_type, Node* use_node, 333 UseInfo use_info); 334 const Operator* Int32OperatorFor(IrOpcode::Value opcode); 335 const Operator* Int32OverflowOperatorFor(IrOpcode::Value opcode); 336 const Operator* Int64OperatorFor(IrOpcode::Value opcode); 337 const Operator* TaggedSignedOperatorFor(IrOpcode::Value opcode); 338 const Operator* Uint32OperatorFor(IrOpcode::Value opcode); 339 const Operator* Uint32OverflowOperatorFor(IrOpcode::Value opcode); 340 const Operator* Float64OperatorFor(IrOpcode::Value opcode); 341 TypeForBasePointer(const FieldAccess & access)342 MachineType TypeForBasePointer(const FieldAccess& access) { 343 return access.tag() != 0 ? MachineType::AnyTagged() 344 : MachineType::Pointer(); 345 } 346 TypeForBasePointer(const ElementAccess & access)347 MachineType TypeForBasePointer(const ElementAccess& access) { 348 return access.tag() != 0 ? MachineType::AnyTagged() 349 : MachineType::Pointer(); 350 } 351 verification_enabled()352 bool verification_enabled() const { return verifier_ != nullptr; } 353 354 private: 355 TypeCache const* cache_; 356 JSGraph* jsgraph_; 357 JSHeapBroker* broker_; 358 SimplifiedLoweringVerifier* verifier_; 359 360 friend class RepresentationChangerTester; // accesses the below fields. 361 362 bool testing_type_errors_; // If {true}, don't abort on a type error. 363 bool type_error_; // Set when a type error is detected. 364 365 Node* GetTaggedSignedRepresentationFor(Node* node, 366 MachineRepresentation output_rep, 367 Type output_type, Node* use_node, 368 UseInfo use_info); 369 Node* GetTaggedPointerRepresentationFor(Node* node, 370 MachineRepresentation output_rep, 371 Type output_type, Node* use_node, 372 UseInfo use_info); 373 Node* GetTaggedRepresentationFor(Node* node, MachineRepresentation output_rep, 374 Type output_type, Truncation truncation); 375 Node* GetFloat32RepresentationFor(Node* node, 376 MachineRepresentation output_rep, 377 Type output_type, Truncation truncation); 378 Node* GetFloat64RepresentationFor(Node* node, 379 MachineRepresentation output_rep, 380 Type output_type, Node* use_node, 381 UseInfo use_info); 382 Node* GetWord32RepresentationFor(Node* node, MachineRepresentation output_rep, 383 Type output_type, Node* use_node, 384 UseInfo use_info); 385 Node* GetBitRepresentationFor(Node* node, MachineRepresentation output_rep, 386 Type output_type); 387 Node* GetWord64RepresentationFor(Node* node, MachineRepresentation output_rep, 388 Type output_type, Node* use_node, 389 UseInfo use_info); 390 Node* TypeError(Node* node, MachineRepresentation output_rep, 391 Type output_type, MachineRepresentation use); 392 Node* MakeTruncatedInt32Constant(double value); 393 Node* InsertChangeBitToTagged(Node* node); 394 Node* InsertChangeFloat32ToFloat64(Node* node); 395 Node* InsertChangeFloat64ToInt32(Node* node); 396 Node* InsertChangeFloat64ToUint32(Node* node); 397 Node* InsertChangeInt32ToFloat64(Node* node); 398 Node* InsertChangeTaggedSignedToInt32(Node* node); 399 Node* InsertChangeTaggedToFloat64(Node* node); 400 Node* InsertChangeUint32ToFloat64(Node* node); 401 Node* InsertCheckedFloat64ToInt32(Node* node, CheckForMinusZeroMode check, 402 const FeedbackSource& feedback, 403 Node* use_node); 404 Node* InsertConversion(Node* node, const Operator* op, Node* use_node); 405 Node* InsertTruncateInt64ToInt32(Node* node); 406 Node* InsertUnconditionalDeopt(Node* node, DeoptimizeReason reason, 407 const FeedbackSource& feedback = {}); 408 Node* InsertTypeOverrideForVerifier(const Type& type, Node* node); 409 jsgraph()410 JSGraph* jsgraph() const { return jsgraph_; } 411 Isolate* isolate() const; factory()412 Factory* factory() const { return isolate()->factory(); } simplified()413 SimplifiedOperatorBuilder* simplified() { return jsgraph()->simplified(); } machine()414 MachineOperatorBuilder* machine() { return jsgraph()->machine(); } 415 }; 416 417 } // namespace compiler 418 } // namespace internal 419 } // namespace v8 420 421 #endif // V8_COMPILER_REPRESENTATION_CHANGE_H_ 422