1 // Copyright 2015 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/compiler/representation-change.h" 6 7 #include <sstream> 8 9 #include "src/base/bits.h" 10 #include "src/code-factory.h" 11 #include "src/compiler/machine-operator.h" 12 13 namespace v8 { 14 namespace internal { 15 namespace compiler { 16 17 const char* Truncation::description() const { 18 switch (kind()) { 19 case TruncationKind::kNone: 20 return "no-value-use"; 21 case TruncationKind::kBool: 22 return "truncate-to-bool"; 23 case TruncationKind::kWord32: 24 return "truncate-to-word32"; 25 case TruncationKind::kWord64: 26 return "truncate-to-word64"; 27 case TruncationKind::kFloat32: 28 return "truncate-to-float32"; 29 case TruncationKind::kFloat64: 30 return "truncate-to-float64"; 31 case TruncationKind::kAny: 32 return "no-truncation"; 33 } 34 UNREACHABLE(); 35 return nullptr; 36 } 37 38 39 // Partial order for truncations: 40 // 41 // kWord64 kAny 42 // ^ ^ 43 // \ | 44 // \ kFloat64 <--+ 45 // \ ^ ^ | 46 // \ / | | 47 // kWord32 kFloat32 kBool 48 // ^ ^ ^ 49 // \ | / 50 // \ | / 51 // \ | / 52 // \ | / 53 // \ | / 54 // kNone 55 56 // static 57 Truncation::TruncationKind Truncation::Generalize(TruncationKind rep1, 58 TruncationKind rep2) { 59 if (LessGeneral(rep1, rep2)) return rep2; 60 if (LessGeneral(rep2, rep1)) return rep1; 61 // Handle the generalization of float64-representable values. 62 if (LessGeneral(rep1, TruncationKind::kFloat64) && 63 LessGeneral(rep2, TruncationKind::kFloat64)) { 64 return TruncationKind::kFloat64; 65 } 66 // All other combinations are illegal. 67 FATAL("Tried to combine incompatible truncations"); 68 return TruncationKind::kNone; 69 } 70 71 72 // static 73 bool Truncation::LessGeneral(TruncationKind rep1, TruncationKind rep2) { 74 switch (rep1) { 75 case TruncationKind::kNone: 76 return true; 77 case TruncationKind::kBool: 78 return rep2 == TruncationKind::kBool || rep2 == TruncationKind::kAny; 79 case TruncationKind::kWord32: 80 return rep2 == TruncationKind::kWord32 || 81 rep2 == TruncationKind::kWord64 || 82 rep2 == TruncationKind::kFloat64 || rep2 == TruncationKind::kAny; 83 case TruncationKind::kWord64: 84 return rep2 == TruncationKind::kWord64; 85 case TruncationKind::kFloat32: 86 return rep2 == TruncationKind::kFloat32 || 87 rep2 == TruncationKind::kFloat64 || rep2 == TruncationKind::kAny; 88 case TruncationKind::kFloat64: 89 return rep2 == TruncationKind::kFloat64 || rep2 == TruncationKind::kAny; 90 case TruncationKind::kAny: 91 return rep2 == TruncationKind::kAny; 92 } 93 UNREACHABLE(); 94 return false; 95 } 96 97 98 namespace { 99 100 // TODO(titzer): should Word64 also be implicitly convertable to others? 101 bool IsWord(MachineRepresentation rep) { 102 return rep == MachineRepresentation::kWord8 || 103 rep == MachineRepresentation::kWord16 || 104 rep == MachineRepresentation::kWord32; 105 } 106 107 } // namespace 108 109 110 // Changes representation from {output_rep} to {use_rep}. The {truncation} 111 // parameter is only used for sanity checking - if the changer cannot figure 112 // out signedness for the word32->float64 conversion, then we check that the 113 // uses truncate to word32 (so they do not care about signedness). 114 Node* RepresentationChanger::GetRepresentationFor( 115 Node* node, MachineRepresentation output_rep, Type* output_type, 116 MachineRepresentation use_rep, Truncation truncation) { 117 if (output_rep == MachineRepresentation::kNone) { 118 // The output representation should be set. 119 return TypeError(node, output_rep, output_type, use_rep); 120 } 121 if (use_rep == output_rep) { 122 // Representations are the same. That's a no-op. 123 return node; 124 } 125 if (IsWord(use_rep) && IsWord(output_rep)) { 126 // Both are words less than or equal to 32-bits. 127 // Since loads of integers from memory implicitly sign or zero extend the 128 // value to the full machine word size and stores implicitly truncate, 129 // no representation change is necessary. 130 return node; 131 } 132 switch (use_rep) { 133 case MachineRepresentation::kTagged: 134 return GetTaggedRepresentationFor(node, output_rep, output_type); 135 case MachineRepresentation::kFloat32: 136 return GetFloat32RepresentationFor(node, output_rep, output_type, 137 truncation); 138 case MachineRepresentation::kFloat64: 139 return GetFloat64RepresentationFor(node, output_rep, output_type, 140 truncation); 141 case MachineRepresentation::kBit: 142 return GetBitRepresentationFor(node, output_rep, output_type); 143 case MachineRepresentation::kWord8: 144 case MachineRepresentation::kWord16: 145 case MachineRepresentation::kWord32: 146 return GetWord32RepresentationFor(node, output_rep, output_type); 147 case MachineRepresentation::kWord64: 148 return GetWord64RepresentationFor(node, output_rep, output_type); 149 case MachineRepresentation::kNone: 150 return node; 151 } 152 UNREACHABLE(); 153 return nullptr; 154 } 155 156 157 Node* RepresentationChanger::GetTaggedRepresentationFor( 158 Node* node, MachineRepresentation output_rep, Type* output_type) { 159 // Eagerly fold representation changes for constants. 160 switch (node->opcode()) { 161 case IrOpcode::kNumberConstant: 162 case IrOpcode::kHeapConstant: 163 return node; // No change necessary. 164 case IrOpcode::kInt32Constant: 165 if (output_type->Is(Type::Signed32())) { 166 int32_t value = OpParameter<int32_t>(node); 167 return jsgraph()->Constant(value); 168 } else if (output_type->Is(Type::Unsigned32())) { 169 uint32_t value = static_cast<uint32_t>(OpParameter<int32_t>(node)); 170 return jsgraph()->Constant(static_cast<double>(value)); 171 } else if (output_rep == MachineRepresentation::kBit) { 172 return OpParameter<int32_t>(node) == 0 ? jsgraph()->FalseConstant() 173 : jsgraph()->TrueConstant(); 174 } else { 175 return TypeError(node, output_rep, output_type, 176 MachineRepresentation::kTagged); 177 } 178 case IrOpcode::kFloat64Constant: 179 return jsgraph()->Constant(OpParameter<double>(node)); 180 case IrOpcode::kFloat32Constant: 181 return jsgraph()->Constant(OpParameter<float>(node)); 182 default: 183 break; 184 } 185 // Select the correct X -> Tagged operator. 186 const Operator* op; 187 if (output_rep == MachineRepresentation::kBit) { 188 op = simplified()->ChangeBitToBool(); 189 } else if (IsWord(output_rep)) { 190 if (output_type->Is(Type::Unsigned32())) { 191 op = simplified()->ChangeUint32ToTagged(); 192 } else if (output_type->Is(Type::Signed32())) { 193 op = simplified()->ChangeInt32ToTagged(); 194 } else { 195 return TypeError(node, output_rep, output_type, 196 MachineRepresentation::kTagged); 197 } 198 } else if (output_rep == 199 MachineRepresentation::kFloat32) { // float32 -> float64 -> tagged 200 node = InsertChangeFloat32ToFloat64(node); 201 op = simplified()->ChangeFloat64ToTagged(); 202 } else if (output_rep == MachineRepresentation::kFloat64) { 203 op = simplified()->ChangeFloat64ToTagged(); 204 } else { 205 return TypeError(node, output_rep, output_type, 206 MachineRepresentation::kTagged); 207 } 208 return jsgraph()->graph()->NewNode(op, node); 209 } 210 211 212 Node* RepresentationChanger::GetFloat32RepresentationFor( 213 Node* node, MachineRepresentation output_rep, Type* output_type, 214 Truncation truncation) { 215 // Eagerly fold representation changes for constants. 216 switch (node->opcode()) { 217 case IrOpcode::kFloat64Constant: 218 case IrOpcode::kNumberConstant: 219 return jsgraph()->Float32Constant( 220 DoubleToFloat32(OpParameter<double>(node))); 221 case IrOpcode::kInt32Constant: 222 if (output_type->Is(Type::Unsigned32())) { 223 uint32_t value = static_cast<uint32_t>(OpParameter<int32_t>(node)); 224 return jsgraph()->Float32Constant(static_cast<float>(value)); 225 } else { 226 int32_t value = OpParameter<int32_t>(node); 227 return jsgraph()->Float32Constant(static_cast<float>(value)); 228 } 229 case IrOpcode::kFloat32Constant: 230 return node; // No change necessary. 231 default: 232 break; 233 } 234 // Select the correct X -> Float32 operator. 235 const Operator* op; 236 if (output_rep == MachineRepresentation::kBit) { 237 return TypeError(node, output_rep, output_type, 238 MachineRepresentation::kFloat32); 239 } else if (IsWord(output_rep)) { 240 if (output_type->Is(Type::Signed32())) { 241 op = machine()->ChangeInt32ToFloat64(); 242 } else { 243 // Either the output is int32 or the uses only care about the 244 // low 32 bits (so we can pick int32 safely). 245 DCHECK(output_type->Is(Type::Unsigned32()) || 246 truncation.TruncatesToWord32()); 247 op = machine()->ChangeUint32ToFloat64(); 248 } 249 // int32 -> float64 -> float32 250 node = jsgraph()->graph()->NewNode(op, node); 251 op = machine()->TruncateFloat64ToFloat32(); 252 } else if (output_rep == MachineRepresentation::kTagged) { 253 op = simplified()->ChangeTaggedToFloat64(); // tagged -> float64 -> float32 254 node = jsgraph()->graph()->NewNode(op, node); 255 op = machine()->TruncateFloat64ToFloat32(); 256 } else if (output_rep == MachineRepresentation::kFloat64) { 257 op = machine()->TruncateFloat64ToFloat32(); 258 } else { 259 return TypeError(node, output_rep, output_type, 260 MachineRepresentation::kFloat32); 261 } 262 return jsgraph()->graph()->NewNode(op, node); 263 } 264 265 266 Node* RepresentationChanger::GetFloat64RepresentationFor( 267 Node* node, MachineRepresentation output_rep, Type* output_type, 268 Truncation truncation) { 269 // Eagerly fold representation changes for constants. 270 switch (node->opcode()) { 271 case IrOpcode::kNumberConstant: 272 return jsgraph()->Float64Constant(OpParameter<double>(node)); 273 case IrOpcode::kInt32Constant: 274 if (output_type->Is(Type::Signed32())) { 275 int32_t value = OpParameter<int32_t>(node); 276 return jsgraph()->Float64Constant(value); 277 } else { 278 DCHECK(output_type->Is(Type::Unsigned32())); 279 uint32_t value = static_cast<uint32_t>(OpParameter<int32_t>(node)); 280 return jsgraph()->Float64Constant(static_cast<double>(value)); 281 } 282 case IrOpcode::kFloat64Constant: 283 return node; // No change necessary. 284 case IrOpcode::kFloat32Constant: 285 return jsgraph()->Float64Constant(OpParameter<float>(node)); 286 default: 287 break; 288 } 289 // Select the correct X -> Float64 operator. 290 const Operator* op; 291 if (output_rep == MachineRepresentation::kBit) { 292 return TypeError(node, output_rep, output_type, 293 MachineRepresentation::kFloat64); 294 } else if (IsWord(output_rep)) { 295 if (output_type->Is(Type::Signed32())) { 296 op = machine()->ChangeInt32ToFloat64(); 297 } else { 298 // Either the output is int32 or the uses only care about the 299 // low 32 bits (so we can pick int32 safely). 300 DCHECK(output_type->Is(Type::Unsigned32()) || 301 truncation.TruncatesToWord32()); 302 op = machine()->ChangeUint32ToFloat64(); 303 } 304 } else if (output_rep == MachineRepresentation::kTagged) { 305 op = simplified()->ChangeTaggedToFloat64(); 306 } else if (output_rep == MachineRepresentation::kFloat32) { 307 op = machine()->ChangeFloat32ToFloat64(); 308 } else { 309 return TypeError(node, output_rep, output_type, 310 MachineRepresentation::kFloat64); 311 } 312 return jsgraph()->graph()->NewNode(op, node); 313 } 314 315 316 Node* RepresentationChanger::MakeTruncatedInt32Constant(double value) { 317 return jsgraph()->Int32Constant(DoubleToInt32(value)); 318 } 319 320 321 Node* RepresentationChanger::GetWord32RepresentationFor( 322 Node* node, MachineRepresentation output_rep, Type* output_type) { 323 // Eagerly fold representation changes for constants. 324 switch (node->opcode()) { 325 case IrOpcode::kInt32Constant: 326 return node; // No change necessary. 327 case IrOpcode::kFloat32Constant: 328 return MakeTruncatedInt32Constant(OpParameter<float>(node)); 329 case IrOpcode::kNumberConstant: 330 case IrOpcode::kFloat64Constant: 331 return MakeTruncatedInt32Constant(OpParameter<double>(node)); 332 default: 333 break; 334 } 335 // Select the correct X -> Word32 operator. 336 const Operator* op; 337 Type* type = NodeProperties::GetType(node); 338 339 if (output_rep == MachineRepresentation::kBit) { 340 return node; // Sloppy comparison -> word32 341 } else if (output_rep == MachineRepresentation::kFloat64) { 342 // TODO(jarin) Use only output_type here, once we intersect it with the 343 // type inferred by the typer. 344 if (output_type->Is(Type::Unsigned32()) || type->Is(Type::Unsigned32())) { 345 op = machine()->ChangeFloat64ToUint32(); 346 } else if (output_type->Is(Type::Signed32()) || 347 type->Is(Type::Signed32())) { 348 op = machine()->ChangeFloat64ToInt32(); 349 } else { 350 op = machine()->TruncateFloat64ToInt32(TruncationMode::kJavaScript); 351 } 352 } else if (output_rep == MachineRepresentation::kFloat32) { 353 node = InsertChangeFloat32ToFloat64(node); // float32 -> float64 -> int32 354 if (output_type->Is(Type::Unsigned32()) || type->Is(Type::Unsigned32())) { 355 op = machine()->ChangeFloat64ToUint32(); 356 } else if (output_type->Is(Type::Signed32()) || 357 type->Is(Type::Signed32())) { 358 op = machine()->ChangeFloat64ToInt32(); 359 } else { 360 op = machine()->TruncateFloat64ToInt32(TruncationMode::kJavaScript); 361 } 362 } else if (output_rep == MachineRepresentation::kTagged) { 363 if (output_type->Is(Type::Unsigned32()) || type->Is(Type::Unsigned32())) { 364 op = simplified()->ChangeTaggedToUint32(); 365 } else if (output_type->Is(Type::Signed32()) || 366 type->Is(Type::Signed32())) { 367 op = simplified()->ChangeTaggedToInt32(); 368 } else { 369 node = InsertChangeTaggedToFloat64(node); 370 op = machine()->TruncateFloat64ToInt32(TruncationMode::kJavaScript); 371 } 372 } else { 373 return TypeError(node, output_rep, output_type, 374 MachineRepresentation::kWord32); 375 } 376 return jsgraph()->graph()->NewNode(op, node); 377 } 378 379 380 Node* RepresentationChanger::GetBitRepresentationFor( 381 Node* node, MachineRepresentation output_rep, Type* output_type) { 382 // Eagerly fold representation changes for constants. 383 switch (node->opcode()) { 384 case IrOpcode::kHeapConstant: { 385 Handle<HeapObject> value = OpParameter<Handle<HeapObject>>(node); 386 DCHECK(value.is_identical_to(factory()->true_value()) || 387 value.is_identical_to(factory()->false_value())); 388 return jsgraph()->Int32Constant( 389 value.is_identical_to(factory()->true_value()) ? 1 : 0); 390 } 391 default: 392 break; 393 } 394 // Select the correct X -> Bit operator. 395 const Operator* op; 396 if (output_rep == MachineRepresentation::kTagged) { 397 op = simplified()->ChangeBoolToBit(); 398 } else { 399 return TypeError(node, output_rep, output_type, 400 MachineRepresentation::kBit); 401 } 402 return jsgraph()->graph()->NewNode(op, node); 403 } 404 405 406 Node* RepresentationChanger::GetWord64RepresentationFor( 407 Node* node, MachineRepresentation output_rep, Type* output_type) { 408 if (output_rep == MachineRepresentation::kBit) { 409 return node; // Sloppy comparison -> word64 410 } 411 // Can't really convert Word64 to anything else. Purported to be internal. 412 return TypeError(node, output_rep, output_type, 413 MachineRepresentation::kWord64); 414 } 415 416 417 const Operator* RepresentationChanger::Int32OperatorFor( 418 IrOpcode::Value opcode) { 419 switch (opcode) { 420 case IrOpcode::kNumberAdd: 421 return machine()->Int32Add(); 422 case IrOpcode::kNumberSubtract: 423 return machine()->Int32Sub(); 424 case IrOpcode::kNumberMultiply: 425 return machine()->Int32Mul(); 426 case IrOpcode::kNumberDivide: 427 return machine()->Int32Div(); 428 case IrOpcode::kNumberModulus: 429 return machine()->Int32Mod(); 430 case IrOpcode::kNumberBitwiseOr: 431 return machine()->Word32Or(); 432 case IrOpcode::kNumberBitwiseXor: 433 return machine()->Word32Xor(); 434 case IrOpcode::kNumberBitwiseAnd: 435 return machine()->Word32And(); 436 case IrOpcode::kNumberEqual: 437 return machine()->Word32Equal(); 438 case IrOpcode::kNumberLessThan: 439 return machine()->Int32LessThan(); 440 case IrOpcode::kNumberLessThanOrEqual: 441 return machine()->Int32LessThanOrEqual(); 442 default: 443 UNREACHABLE(); 444 return nullptr; 445 } 446 } 447 448 449 const Operator* RepresentationChanger::Uint32OperatorFor( 450 IrOpcode::Value opcode) { 451 switch (opcode) { 452 case IrOpcode::kNumberAdd: 453 return machine()->Int32Add(); 454 case IrOpcode::kNumberSubtract: 455 return machine()->Int32Sub(); 456 case IrOpcode::kNumberMultiply: 457 return machine()->Int32Mul(); 458 case IrOpcode::kNumberDivide: 459 return machine()->Uint32Div(); 460 case IrOpcode::kNumberModulus: 461 return machine()->Uint32Mod(); 462 case IrOpcode::kNumberEqual: 463 return machine()->Word32Equal(); 464 case IrOpcode::kNumberLessThan: 465 return machine()->Uint32LessThan(); 466 case IrOpcode::kNumberLessThanOrEqual: 467 return machine()->Uint32LessThanOrEqual(); 468 default: 469 UNREACHABLE(); 470 return nullptr; 471 } 472 } 473 474 475 const Operator* RepresentationChanger::Float64OperatorFor( 476 IrOpcode::Value opcode) { 477 switch (opcode) { 478 case IrOpcode::kNumberAdd: 479 return machine()->Float64Add(); 480 case IrOpcode::kNumberSubtract: 481 return machine()->Float64Sub(); 482 case IrOpcode::kNumberMultiply: 483 return machine()->Float64Mul(); 484 case IrOpcode::kNumberDivide: 485 return machine()->Float64Div(); 486 case IrOpcode::kNumberModulus: 487 return machine()->Float64Mod(); 488 case IrOpcode::kNumberEqual: 489 return machine()->Float64Equal(); 490 case IrOpcode::kNumberLessThan: 491 return machine()->Float64LessThan(); 492 case IrOpcode::kNumberLessThanOrEqual: 493 return machine()->Float64LessThanOrEqual(); 494 default: 495 UNREACHABLE(); 496 return nullptr; 497 } 498 } 499 500 501 Node* RepresentationChanger::TypeError(Node* node, 502 MachineRepresentation output_rep, 503 Type* output_type, 504 MachineRepresentation use) { 505 type_error_ = true; 506 if (!testing_type_errors_) { 507 std::ostringstream out_str; 508 out_str << output_rep << " ("; 509 output_type->PrintTo(out_str, Type::SEMANTIC_DIM); 510 out_str << ")"; 511 512 std::ostringstream use_str; 513 use_str << use; 514 515 V8_Fatal(__FILE__, __LINE__, 516 "RepresentationChangerError: node #%d:%s of " 517 "%s cannot be changed to %s", 518 node->id(), node->op()->mnemonic(), out_str.str().c_str(), 519 use_str.str().c_str()); 520 } 521 return node; 522 } 523 524 525 Node* RepresentationChanger::InsertChangeFloat32ToFloat64(Node* node) { 526 return jsgraph()->graph()->NewNode(machine()->ChangeFloat32ToFloat64(), node); 527 } 528 529 530 Node* RepresentationChanger::InsertChangeTaggedToFloat64(Node* node) { 531 return jsgraph()->graph()->NewNode(simplified()->ChangeTaggedToFloat64(), 532 node); 533 } 534 535 } // namespace compiler 536 } // namespace internal 537 } // namespace v8 538