representation-change.cc revision 1b268ca467c924004286c97bac133db489cf43d0
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 13namespace v8 { 14namespace internal { 15namespace compiler { 16 17const 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 57Truncation::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 73bool 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 98namespace { 99 100bool IsWord(MachineRepresentation rep) { 101 return rep == MachineRepresentation::kWord8 || 102 rep == MachineRepresentation::kWord16 || 103 rep == MachineRepresentation::kWord32; 104} 105 106} // namespace 107 108 109// Changes representation from {output_rep} to {use_rep}. The {truncation} 110// parameter is only used for sanity checking - if the changer cannot figure 111// out signedness for the word32->float64 conversion, then we check that the 112// uses truncate to word32 (so they do not care about signedness). 113Node* RepresentationChanger::GetRepresentationFor( 114 Node* node, MachineRepresentation output_rep, Type* output_type, 115 MachineRepresentation use_rep, Truncation truncation) { 116 if (output_rep == MachineRepresentation::kNone) { 117 // The output representation should be set. 118 return TypeError(node, output_rep, output_type, use_rep); 119 } 120 if (use_rep == output_rep) { 121 // Representations are the same. That's a no-op. 122 return node; 123 } 124 if (IsWord(use_rep) && IsWord(output_rep)) { 125 // Both are words less than or equal to 32-bits. 126 // Since loads of integers from memory implicitly sign or zero extend the 127 // value to the full machine word size and stores implicitly truncate, 128 // no representation change is necessary. 129 return node; 130 } 131 switch (use_rep) { 132 case MachineRepresentation::kTagged: 133 return GetTaggedRepresentationFor(node, output_rep, output_type); 134 case MachineRepresentation::kFloat32: 135 return GetFloat32RepresentationFor(node, output_rep, output_type, 136 truncation); 137 case MachineRepresentation::kFloat64: 138 return GetFloat64RepresentationFor(node, output_rep, output_type, 139 truncation); 140 case MachineRepresentation::kBit: 141 return GetBitRepresentationFor(node, output_rep, output_type); 142 case MachineRepresentation::kWord8: 143 case MachineRepresentation::kWord16: 144 case MachineRepresentation::kWord32: 145 return GetWord32RepresentationFor(node, output_rep, output_type, 146 truncation); 147 case MachineRepresentation::kWord64: 148 return GetWord64RepresentationFor(node, output_rep, output_type); 149 case MachineRepresentation::kSimd128: // Fall through. 150 // TODO(bbudge) Handle conversions between tagged and untagged. 151 break; 152 case MachineRepresentation::kNone: 153 return node; 154 } 155 UNREACHABLE(); 156 return nullptr; 157} 158 159 160Node* RepresentationChanger::GetTaggedRepresentationFor( 161 Node* node, MachineRepresentation output_rep, Type* output_type) { 162 // Eagerly fold representation changes for constants. 163 switch (node->opcode()) { 164 case IrOpcode::kNumberConstant: 165 case IrOpcode::kHeapConstant: 166 return node; // No change necessary. 167 case IrOpcode::kInt32Constant: 168 if (output_type->Is(Type::Signed32())) { 169 int32_t value = OpParameter<int32_t>(node); 170 return jsgraph()->Constant(value); 171 } else if (output_type->Is(Type::Unsigned32())) { 172 uint32_t value = static_cast<uint32_t>(OpParameter<int32_t>(node)); 173 return jsgraph()->Constant(static_cast<double>(value)); 174 } else if (output_rep == MachineRepresentation::kBit) { 175 return OpParameter<int32_t>(node) == 0 ? jsgraph()->FalseConstant() 176 : jsgraph()->TrueConstant(); 177 } else { 178 return TypeError(node, output_rep, output_type, 179 MachineRepresentation::kTagged); 180 } 181 case IrOpcode::kFloat64Constant: 182 return jsgraph()->Constant(OpParameter<double>(node)); 183 case IrOpcode::kFloat32Constant: 184 return jsgraph()->Constant(OpParameter<float>(node)); 185 default: 186 break; 187 } 188 // Select the correct X -> Tagged operator. 189 const Operator* op; 190 if (output_rep == MachineRepresentation::kBit) { 191 op = simplified()->ChangeBitToBool(); 192 } else if (IsWord(output_rep)) { 193 if (output_type->Is(Type::Unsigned32())) { 194 op = simplified()->ChangeUint32ToTagged(); 195 } else if (output_type->Is(Type::Signed32())) { 196 op = simplified()->ChangeInt32ToTagged(); 197 } else { 198 return TypeError(node, output_rep, output_type, 199 MachineRepresentation::kTagged); 200 } 201 } else if (output_rep == 202 MachineRepresentation::kFloat32) { // float32 -> float64 -> tagged 203 node = InsertChangeFloat32ToFloat64(node); 204 op = simplified()->ChangeFloat64ToTagged(); 205 } else if (output_rep == MachineRepresentation::kFloat64) { 206 op = simplified()->ChangeFloat64ToTagged(); 207 } else { 208 return TypeError(node, output_rep, output_type, 209 MachineRepresentation::kTagged); 210 } 211 return jsgraph()->graph()->NewNode(op, node); 212} 213 214 215Node* RepresentationChanger::GetFloat32RepresentationFor( 216 Node* node, MachineRepresentation output_rep, Type* output_type, 217 Truncation truncation) { 218 // Eagerly fold representation changes for constants. 219 switch (node->opcode()) { 220 case IrOpcode::kFloat64Constant: 221 case IrOpcode::kNumberConstant: 222 return jsgraph()->Float32Constant( 223 DoubleToFloat32(OpParameter<double>(node))); 224 case IrOpcode::kInt32Constant: 225 if (output_type->Is(Type::Unsigned32())) { 226 uint32_t value = static_cast<uint32_t>(OpParameter<int32_t>(node)); 227 return jsgraph()->Float32Constant(static_cast<float>(value)); 228 } else { 229 int32_t value = OpParameter<int32_t>(node); 230 return jsgraph()->Float32Constant(static_cast<float>(value)); 231 } 232 case IrOpcode::kFloat32Constant: 233 return node; // No change necessary. 234 default: 235 break; 236 } 237 // Select the correct X -> Float32 operator. 238 const Operator* op = nullptr; 239 if (IsWord(output_rep)) { 240 if (output_type->Is(Type::Signed32())) { 241 // int32 -> float64 -> float32 242 op = machine()->ChangeInt32ToFloat64(); 243 node = jsgraph()->graph()->NewNode(op, node); 244 op = machine()->TruncateFloat64ToFloat32(); 245 } else if (output_type->Is(Type::Unsigned32()) || 246 truncation.TruncatesToWord32()) { 247 // Either the output is uint32 or the uses only care about the 248 // low 32 bits (so we can pick uint32 safely). 249 250 // uint32 -> float64 -> float32 251 op = machine()->ChangeUint32ToFloat64(); 252 node = jsgraph()->graph()->NewNode(op, node); 253 op = machine()->TruncateFloat64ToFloat32(); 254 } 255 } else if (output_rep == MachineRepresentation::kTagged) { 256 if (output_type->Is(Type::Number())) { 257 op = simplified() 258 ->ChangeTaggedToFloat64(); // tagged -> float64 -> float32 259 node = jsgraph()->graph()->NewNode(op, node); 260 op = machine()->TruncateFloat64ToFloat32(); 261 } 262 } else if (output_rep == MachineRepresentation::kFloat64) { 263 op = machine()->TruncateFloat64ToFloat32(); 264 } 265 if (op == nullptr) { 266 return TypeError(node, output_rep, output_type, 267 MachineRepresentation::kFloat32); 268 } 269 return jsgraph()->graph()->NewNode(op, node); 270} 271 272 273Node* RepresentationChanger::GetFloat64RepresentationFor( 274 Node* node, MachineRepresentation output_rep, Type* output_type, 275 Truncation truncation) { 276 // Eagerly fold representation changes for constants. 277 switch (node->opcode()) { 278 case IrOpcode::kNumberConstant: 279 return jsgraph()->Float64Constant(OpParameter<double>(node)); 280 case IrOpcode::kInt32Constant: 281 if (output_type->Is(Type::Signed32())) { 282 int32_t value = OpParameter<int32_t>(node); 283 return jsgraph()->Float64Constant(value); 284 } else { 285 DCHECK(output_type->Is(Type::Unsigned32())); 286 uint32_t value = static_cast<uint32_t>(OpParameter<int32_t>(node)); 287 return jsgraph()->Float64Constant(static_cast<double>(value)); 288 } 289 case IrOpcode::kFloat64Constant: 290 return node; // No change necessary. 291 case IrOpcode::kFloat32Constant: 292 return jsgraph()->Float64Constant(OpParameter<float>(node)); 293 default: 294 break; 295 } 296 // Select the correct X -> Float64 operator. 297 const Operator* op = nullptr; 298 if (IsWord(output_rep)) { 299 if (output_type->Is(Type::Signed32())) { 300 op = machine()->ChangeInt32ToFloat64(); 301 } else if (output_type->Is(Type::Unsigned32()) || 302 truncation.TruncatesToWord32()) { 303 // Either the output is uint32 or the uses only care about the 304 // low 32 bits (so we can pick uint32 safely). 305 op = machine()->ChangeUint32ToFloat64(); 306 } 307 } else if (output_rep == MachineRepresentation::kTagged) { 308 if (output_type->Is(Type::Number())) { 309 op = simplified()->ChangeTaggedToFloat64(); 310 } 311 } else if (output_rep == MachineRepresentation::kFloat32) { 312 op = machine()->ChangeFloat32ToFloat64(); 313 } 314 if (op == nullptr) { 315 return TypeError(node, output_rep, output_type, 316 MachineRepresentation::kFloat64); 317 } 318 return jsgraph()->graph()->NewNode(op, node); 319} 320 321 322Node* RepresentationChanger::MakeTruncatedInt32Constant(double value) { 323 return jsgraph()->Int32Constant(DoubleToInt32(value)); 324} 325 326Node* RepresentationChanger::GetWord32RepresentationFor( 327 Node* node, MachineRepresentation output_rep, Type* output_type, 328 Truncation truncation) { 329 // Eagerly fold representation changes for constants. 330 switch (node->opcode()) { 331 case IrOpcode::kInt32Constant: 332 return node; // No change necessary. 333 case IrOpcode::kFloat32Constant: 334 return MakeTruncatedInt32Constant(OpParameter<float>(node)); 335 case IrOpcode::kNumberConstant: 336 case IrOpcode::kFloat64Constant: 337 return MakeTruncatedInt32Constant(OpParameter<double>(node)); 338 default: 339 break; 340 } 341 // Select the correct X -> Word32 operator. 342 const Operator* op = nullptr; 343 if (output_rep == MachineRepresentation::kBit) { 344 return node; // Sloppy comparison -> word32 345 } else if (output_rep == MachineRepresentation::kFloat64) { 346 if (output_type->Is(Type::Unsigned32())) { 347 op = machine()->ChangeFloat64ToUint32(); 348 } else if (output_type->Is(Type::Signed32())) { 349 op = machine()->ChangeFloat64ToInt32(); 350 } else if (truncation.TruncatesToWord32()) { 351 op = machine()->TruncateFloat64ToInt32(TruncationMode::kJavaScript); 352 } 353 } else if (output_rep == MachineRepresentation::kFloat32) { 354 node = InsertChangeFloat32ToFloat64(node); // float32 -> float64 -> int32 355 if (output_type->Is(Type::Unsigned32())) { 356 op = machine()->ChangeFloat64ToUint32(); 357 } else if (output_type->Is(Type::Signed32())) { 358 op = machine()->ChangeFloat64ToInt32(); 359 } else if (truncation.TruncatesToWord32()) { 360 op = machine()->TruncateFloat64ToInt32(TruncationMode::kJavaScript); 361 } 362 } else if (output_rep == MachineRepresentation::kTagged) { 363 if (output_type->Is(Type::Unsigned32())) { 364 op = simplified()->ChangeTaggedToUint32(); 365 } else if (output_type->Is(Type::Signed32())) { 366 op = simplified()->ChangeTaggedToInt32(); 367 } else if (truncation.TruncatesToWord32()) { 368 node = InsertChangeTaggedToFloat64(node); 369 op = machine()->TruncateFloat64ToInt32(TruncationMode::kJavaScript); 370 } 371 } 372 if (op == nullptr) { 373 return TypeError(node, output_rep, output_type, 374 MachineRepresentation::kWord32); 375 } 376 return jsgraph()->graph()->NewNode(op, node); 377} 378 379 380Node* 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 406Node* 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 417const 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 449const 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 case IrOpcode::kNumberClz32: 469 return machine()->Word32Clz(); 470 case IrOpcode::kNumberImul: 471 return machine()->Int32Mul(); 472 default: 473 UNREACHABLE(); 474 return nullptr; 475 } 476} 477 478 479const Operator* RepresentationChanger::Float64OperatorFor( 480 IrOpcode::Value opcode) { 481 switch (opcode) { 482 case IrOpcode::kNumberAdd: 483 return machine()->Float64Add(); 484 case IrOpcode::kNumberSubtract: 485 return machine()->Float64Sub(); 486 case IrOpcode::kNumberMultiply: 487 return machine()->Float64Mul(); 488 case IrOpcode::kNumberDivide: 489 return machine()->Float64Div(); 490 case IrOpcode::kNumberModulus: 491 return machine()->Float64Mod(); 492 case IrOpcode::kNumberEqual: 493 return machine()->Float64Equal(); 494 case IrOpcode::kNumberLessThan: 495 return machine()->Float64LessThan(); 496 case IrOpcode::kNumberLessThanOrEqual: 497 return machine()->Float64LessThanOrEqual(); 498 default: 499 UNREACHABLE(); 500 return nullptr; 501 } 502} 503 504 505Node* RepresentationChanger::TypeError(Node* node, 506 MachineRepresentation output_rep, 507 Type* output_type, 508 MachineRepresentation use) { 509 type_error_ = true; 510 if (!testing_type_errors_) { 511 std::ostringstream out_str; 512 out_str << output_rep << " ("; 513 output_type->PrintTo(out_str, Type::SEMANTIC_DIM); 514 out_str << ")"; 515 516 std::ostringstream use_str; 517 use_str << use; 518 519 V8_Fatal(__FILE__, __LINE__, 520 "RepresentationChangerError: node #%d:%s of " 521 "%s cannot be changed to %s", 522 node->id(), node->op()->mnemonic(), out_str.str().c_str(), 523 use_str.str().c_str()); 524 } 525 return node; 526} 527 528 529Node* RepresentationChanger::InsertChangeFloat32ToFloat64(Node* node) { 530 return jsgraph()->graph()->NewNode(machine()->ChangeFloat32ToFloat64(), node); 531} 532 533 534Node* RepresentationChanger::InsertChangeTaggedToFloat64(Node* node) { 535 return jsgraph()->graph()->NewNode(simplified()->ChangeTaggedToFloat64(), 536 node); 537} 538 539} // namespace compiler 540} // namespace internal 541} // namespace v8 542