| // Copyright 2016 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/compiler/machine-graph-verifier.h" |
| |
| #include "src/compiler/common-operator.h" |
| #include "src/compiler/graph.h" |
| #include "src/compiler/linkage.h" |
| #include "src/compiler/machine-operator.h" |
| #include "src/compiler/node-properties.h" |
| #include "src/compiler/node.h" |
| #include "src/compiler/schedule.h" |
| #include "src/zone/zone.h" |
| |
| namespace v8 { |
| namespace internal { |
| namespace compiler { |
| |
| namespace { |
| |
| class MachineRepresentationInferrer { |
| public: |
| MachineRepresentationInferrer(Schedule const* schedule, Graph const* graph, |
| Linkage* linkage, Zone* zone) |
| : schedule_(schedule), |
| linkage_(linkage), |
| representation_vector_(graph->NodeCount(), MachineRepresentation::kNone, |
| zone) { |
| Run(); |
| } |
| |
| CallDescriptor* call_descriptor() const { |
| return linkage_->GetIncomingDescriptor(); |
| } |
| |
| MachineRepresentation GetRepresentation(Node const* node) const { |
| return representation_vector_.at(node->id()); |
| } |
| |
| private: |
| MachineRepresentation GetProjectionType(Node const* projection) { |
| size_t index = ProjectionIndexOf(projection->op()); |
| Node* input = projection->InputAt(0); |
| switch (input->opcode()) { |
| case IrOpcode::kInt32AddWithOverflow: |
| case IrOpcode::kInt32SubWithOverflow: |
| case IrOpcode::kInt32MulWithOverflow: |
| CHECK_LE(index, static_cast<size_t>(1)); |
| return index == 0 ? MachineRepresentation::kWord32 |
| : MachineRepresentation::kBit; |
| case IrOpcode::kInt64AddWithOverflow: |
| case IrOpcode::kInt64SubWithOverflow: |
| CHECK_LE(index, static_cast<size_t>(1)); |
| return index == 0 ? MachineRepresentation::kWord64 |
| : MachineRepresentation::kBit; |
| case IrOpcode::kTryTruncateFloat32ToInt64: |
| case IrOpcode::kTryTruncateFloat64ToInt64: |
| case IrOpcode::kTryTruncateFloat32ToUint64: |
| CHECK_LE(index, static_cast<size_t>(1)); |
| return index == 0 ? MachineRepresentation::kWord64 |
| : MachineRepresentation::kBit; |
| case IrOpcode::kCall: { |
| auto call_descriptor = CallDescriptorOf(input->op()); |
| return call_descriptor->GetReturnType(index).representation(); |
| } |
| case IrOpcode::kWord32AtomicPairLoad: |
| case IrOpcode::kWord32AtomicPairAdd: |
| case IrOpcode::kWord32AtomicPairSub: |
| case IrOpcode::kWord32AtomicPairAnd: |
| case IrOpcode::kWord32AtomicPairOr: |
| case IrOpcode::kWord32AtomicPairXor: |
| case IrOpcode::kWord32AtomicPairExchange: |
| case IrOpcode::kWord32AtomicPairCompareExchange: |
| CHECK_LE(index, static_cast<size_t>(1)); |
| return MachineRepresentation::kWord32; |
| default: |
| return MachineRepresentation::kNone; |
| } |
| } |
| |
| MachineRepresentation PromoteRepresentation(MachineRepresentation rep) { |
| switch (rep) { |
| case MachineRepresentation::kWord8: |
| case MachineRepresentation::kWord16: |
| case MachineRepresentation::kWord32: |
| return MachineRepresentation::kWord32; |
| default: |
| break; |
| } |
| return rep; |
| } |
| |
| void Run() { |
| auto blocks = schedule_->all_blocks(); |
| for (BasicBlock* block : *blocks) { |
| current_block_ = block; |
| for (size_t i = 0; i <= block->NodeCount(); ++i) { |
| Node const* node = |
| i < block->NodeCount() ? block->NodeAt(i) : block->control_input(); |
| if (node == nullptr) { |
| DCHECK_EQ(block->NodeCount(), i); |
| break; |
| } |
| switch (node->opcode()) { |
| case IrOpcode::kParameter: |
| representation_vector_[node->id()] = |
| linkage_->GetParameterType(ParameterIndexOf(node->op())) |
| .representation(); |
| break; |
| case IrOpcode::kReturn: { |
| representation_vector_[node->id()] = PromoteRepresentation( |
| linkage_->GetReturnType().representation()); |
| break; |
| } |
| case IrOpcode::kProjection: { |
| representation_vector_[node->id()] = GetProjectionType(node); |
| } break; |
| case IrOpcode::kTypedStateValues: |
| representation_vector_[node->id()] = MachineRepresentation::kNone; |
| break; |
| case IrOpcode::kWord32AtomicLoad: |
| case IrOpcode::kWord64AtomicLoad: |
| case IrOpcode::kLoad: |
| case IrOpcode::kProtectedLoad: |
| case IrOpcode::kPoisonedLoad: |
| representation_vector_[node->id()] = PromoteRepresentation( |
| LoadRepresentationOf(node->op()).representation()); |
| break; |
| case IrOpcode::kLoadFramePointer: |
| case IrOpcode::kLoadParentFramePointer: |
| representation_vector_[node->id()] = |
| MachineType::PointerRepresentation(); |
| break; |
| case IrOpcode::kUnalignedLoad: |
| representation_vector_[node->id()] = PromoteRepresentation( |
| LoadRepresentationOf(node->op()).representation()); |
| break; |
| case IrOpcode::kPhi: |
| representation_vector_[node->id()] = |
| PhiRepresentationOf(node->op()); |
| break; |
| case IrOpcode::kCall: { |
| auto call_descriptor = CallDescriptorOf(node->op()); |
| if (call_descriptor->ReturnCount() > 0) { |
| representation_vector_[node->id()] = |
| call_descriptor->GetReturnType(0).representation(); |
| } else { |
| representation_vector_[node->id()] = |
| MachineRepresentation::kTagged; |
| } |
| break; |
| } |
| case IrOpcode::kWord32AtomicStore: |
| case IrOpcode::kWord64AtomicStore: |
| representation_vector_[node->id()] = |
| PromoteRepresentation(AtomicStoreRepresentationOf(node->op())); |
| break; |
| case IrOpcode::kWord32AtomicPairLoad: |
| case IrOpcode::kWord32AtomicPairStore: |
| case IrOpcode::kWord32AtomicPairAdd: |
| case IrOpcode::kWord32AtomicPairSub: |
| case IrOpcode::kWord32AtomicPairAnd: |
| case IrOpcode::kWord32AtomicPairOr: |
| case IrOpcode::kWord32AtomicPairXor: |
| case IrOpcode::kWord32AtomicPairExchange: |
| case IrOpcode::kWord32AtomicPairCompareExchange: |
| representation_vector_[node->id()] = MachineRepresentation::kWord32; |
| break; |
| case IrOpcode::kWord32AtomicExchange: |
| case IrOpcode::kWord32AtomicCompareExchange: |
| case IrOpcode::kWord32AtomicAdd: |
| case IrOpcode::kWord32AtomicSub: |
| case IrOpcode::kWord32AtomicAnd: |
| case IrOpcode::kWord32AtomicOr: |
| case IrOpcode::kWord32AtomicXor: |
| case IrOpcode::kWord64AtomicExchange: |
| case IrOpcode::kWord64AtomicCompareExchange: |
| case IrOpcode::kWord64AtomicAdd: |
| case IrOpcode::kWord64AtomicSub: |
| case IrOpcode::kWord64AtomicAnd: |
| case IrOpcode::kWord64AtomicOr: |
| case IrOpcode::kWord64AtomicXor: |
| representation_vector_[node->id()] = PromoteRepresentation( |
| AtomicOpType(node->op()).representation()); |
| break; |
| case IrOpcode::kStore: |
| case IrOpcode::kProtectedStore: |
| representation_vector_[node->id()] = PromoteRepresentation( |
| StoreRepresentationOf(node->op()).representation()); |
| break; |
| case IrOpcode::kUnalignedStore: |
| representation_vector_[node->id()] = PromoteRepresentation( |
| UnalignedStoreRepresentationOf(node->op())); |
| break; |
| case IrOpcode::kHeapConstant: |
| representation_vector_[node->id()] = |
| MachineRepresentation::kTaggedPointer; |
| break; |
| case IrOpcode::kNumberConstant: |
| case IrOpcode::kDelayedStringConstant: |
| case IrOpcode::kChangeBitToTagged: |
| case IrOpcode::kIfException: |
| case IrOpcode::kOsrValue: |
| case IrOpcode::kChangeInt32ToTagged: |
| case IrOpcode::kChangeUint32ToTagged: |
| case IrOpcode::kBitcastWordToTagged: |
| case IrOpcode::kTaggedPoisonOnSpeculation: |
| representation_vector_[node->id()] = MachineRepresentation::kTagged; |
| break; |
| case IrOpcode::kWord32PoisonOnSpeculation: |
| representation_vector_[node->id()] = MachineRepresentation::kWord32; |
| break; |
| case IrOpcode::kWord64PoisonOnSpeculation: |
| representation_vector_[node->id()] = MachineRepresentation::kWord64; |
| break; |
| case IrOpcode::kCompressedHeapConstant: |
| representation_vector_[node->id()] = |
| MachineRepresentation::kCompressedPointer; |
| break; |
| case IrOpcode::kExternalConstant: |
| representation_vector_[node->id()] = |
| MachineType::PointerRepresentation(); |
| break; |
| case IrOpcode::kBitcastTaggedToWord: |
| case IrOpcode::kBitcastTaggedToWordForTagAndSmiBits: |
| representation_vector_[node->id()] = |
| MachineType::PointerRepresentation(); |
| break; |
| case IrOpcode::kBitcastWordToTaggedSigned: |
| representation_vector_[node->id()] = |
| MachineRepresentation::kTaggedSigned; |
| break; |
| case IrOpcode::kWord32Equal: |
| case IrOpcode::kInt32LessThan: |
| case IrOpcode::kInt32LessThanOrEqual: |
| case IrOpcode::kUint32LessThan: |
| case IrOpcode::kUint32LessThanOrEqual: |
| case IrOpcode::kWord64Equal: |
| case IrOpcode::kInt64LessThan: |
| case IrOpcode::kInt64LessThanOrEqual: |
| case IrOpcode::kUint64LessThan: |
| case IrOpcode::kUint64LessThanOrEqual: |
| case IrOpcode::kFloat32Equal: |
| case IrOpcode::kFloat32LessThan: |
| case IrOpcode::kFloat32LessThanOrEqual: |
| case IrOpcode::kFloat64Equal: |
| case IrOpcode::kFloat64LessThan: |
| case IrOpcode::kFloat64LessThanOrEqual: |
| case IrOpcode::kChangeTaggedToBit: |
| case IrOpcode::kStackPointerGreaterThan: |
| representation_vector_[node->id()] = MachineRepresentation::kBit; |
| break; |
| #define LABEL(opcode) case IrOpcode::k##opcode: |
| case IrOpcode::kTruncateInt64ToInt32: |
| case IrOpcode::kTruncateFloat32ToInt32: |
| case IrOpcode::kTruncateFloat32ToUint32: |
| case IrOpcode::kBitcastFloat32ToInt32: |
| case IrOpcode::kI32x4ExtractLane: |
| case IrOpcode::kI16x8ExtractLaneU: |
| case IrOpcode::kI16x8ExtractLaneS: |
| case IrOpcode::kI8x16ExtractLaneU: |
| case IrOpcode::kI8x16ExtractLaneS: |
| case IrOpcode::kInt32Constant: |
| case IrOpcode::kRelocatableInt32Constant: |
| case IrOpcode::kTruncateFloat64ToWord32: |
| case IrOpcode::kTruncateFloat64ToUint32: |
| case IrOpcode::kChangeFloat64ToInt32: |
| case IrOpcode::kChangeFloat64ToUint32: |
| case IrOpcode::kRoundFloat64ToInt32: |
| case IrOpcode::kFloat64ExtractLowWord32: |
| case IrOpcode::kFloat64ExtractHighWord32: |
| MACHINE_UNOP_32_LIST(LABEL) |
| MACHINE_BINOP_32_LIST(LABEL) { |
| representation_vector_[node->id()] = |
| MachineRepresentation::kWord32; |
| } |
| break; |
| case IrOpcode::kChangeInt32ToInt64: |
| case IrOpcode::kChangeUint32ToUint64: |
| case IrOpcode::kInt64Constant: |
| case IrOpcode::kRelocatableInt64Constant: |
| case IrOpcode::kBitcastFloat64ToInt64: |
| case IrOpcode::kChangeFloat64ToInt64: |
| case IrOpcode::kChangeFloat64ToUint64: |
| MACHINE_BINOP_64_LIST(LABEL) { |
| representation_vector_[node->id()] = |
| MachineRepresentation::kWord64; |
| } |
| break; |
| case IrOpcode::kRoundInt32ToFloat32: |
| case IrOpcode::kRoundUint32ToFloat32: |
| case IrOpcode::kRoundInt64ToFloat32: |
| case IrOpcode::kRoundUint64ToFloat32: |
| case IrOpcode::kBitcastInt32ToFloat32: |
| case IrOpcode::kFloat32Constant: |
| case IrOpcode::kTruncateFloat64ToFloat32: |
| MACHINE_FLOAT32_BINOP_LIST(LABEL) |
| MACHINE_FLOAT32_UNOP_LIST(LABEL) { |
| representation_vector_[node->id()] = |
| MachineRepresentation::kFloat32; |
| } |
| break; |
| case IrOpcode::kRoundInt64ToFloat64: |
| case IrOpcode::kRoundUint64ToFloat64: |
| case IrOpcode::kChangeFloat32ToFloat64: |
| case IrOpcode::kChangeInt32ToFloat64: |
| case IrOpcode::kChangeUint32ToFloat64: |
| case IrOpcode::kFloat64InsertLowWord32: |
| case IrOpcode::kFloat64InsertHighWord32: |
| case IrOpcode::kFloat64Constant: |
| case IrOpcode::kFloat64SilenceNaN: |
| MACHINE_FLOAT64_BINOP_LIST(LABEL) |
| MACHINE_FLOAT64_UNOP_LIST(LABEL) { |
| representation_vector_[node->id()] = |
| MachineRepresentation::kFloat64; |
| } |
| break; |
| case IrOpcode::kI32x4ReplaceLane: |
| case IrOpcode::kI32x4Splat: |
| representation_vector_[node->id()] = |
| MachineRepresentation::kSimd128; |
| break; |
| #undef LABEL |
| default: |
| break; |
| } |
| } |
| } |
| } |
| |
| Schedule const* const schedule_; |
| Linkage const* const linkage_; |
| ZoneVector<MachineRepresentation> representation_vector_; |
| BasicBlock* current_block_; |
| }; |
| |
| class MachineRepresentationChecker { |
| public: |
| MachineRepresentationChecker( |
| Schedule const* const schedule, |
| MachineRepresentationInferrer const* const inferrer, bool is_stub, |
| const char* name) |
| : schedule_(schedule), |
| inferrer_(inferrer), |
| is_stub_(is_stub), |
| name_(name), |
| current_block_(nullptr) {} |
| |
| void Run() { |
| BasicBlockVector const* blocks = schedule_->all_blocks(); |
| for (BasicBlock* block : *blocks) { |
| current_block_ = block; |
| for (size_t i = 0; i <= block->NodeCount(); ++i) { |
| Node const* node = |
| i < block->NodeCount() ? block->NodeAt(i) : block->control_input(); |
| if (node == nullptr) { |
| DCHECK_EQ(block->NodeCount(), i); |
| break; |
| } |
| switch (node->opcode()) { |
| case IrOpcode::kCall: |
| case IrOpcode::kTailCall: |
| CheckCallInputs(node); |
| break; |
| case IrOpcode::kChangeBitToTagged: |
| CHECK_EQ(MachineRepresentation::kBit, |
| inferrer_->GetRepresentation(node->InputAt(0))); |
| break; |
| case IrOpcode::kChangeTaggedToBit: |
| CHECK_EQ(MachineRepresentation::kTagged, |
| inferrer_->GetRepresentation(node->InputAt(0))); |
| break; |
| case IrOpcode::kRoundInt64ToFloat64: |
| case IrOpcode::kRoundUint64ToFloat64: |
| case IrOpcode::kRoundInt64ToFloat32: |
| case IrOpcode::kRoundUint64ToFloat32: |
| case IrOpcode::kTruncateInt64ToInt32: |
| CheckValueInputForInt64Op(node, 0); |
| break; |
| case IrOpcode::kBitcastWordToTagged: |
| case IrOpcode::kBitcastWordToTaggedSigned: |
| CheckValueInputRepresentationIs( |
| node, 0, MachineType::PointerRepresentation()); |
| break; |
| case IrOpcode::kWord32PoisonOnSpeculation: |
| CheckValueInputRepresentationIs(node, 0, |
| MachineRepresentation::kWord32); |
| break; |
| case IrOpcode::kWord64PoisonOnSpeculation: |
| CheckValueInputRepresentationIs(node, 0, |
| MachineRepresentation::kWord64); |
| break; |
| case IrOpcode::kBitcastTaggedToWord: |
| case IrOpcode::kBitcastTaggedToWordForTagAndSmiBits: |
| if (COMPRESS_POINTERS_BOOL) { |
| CheckValueInputIsCompressedOrTagged(node, 0); |
| } else { |
| CheckValueInputIsTagged(node, 0); |
| } |
| break; |
| case IrOpcode::kTaggedPoisonOnSpeculation: |
| CheckValueInputIsTagged(node, 0); |
| break; |
| case IrOpcode::kTruncateFloat64ToWord32: |
| case IrOpcode::kTruncateFloat64ToUint32: |
| case IrOpcode::kTruncateFloat64ToFloat32: |
| case IrOpcode::kChangeFloat64ToInt32: |
| case IrOpcode::kChangeFloat64ToUint32: |
| case IrOpcode::kRoundFloat64ToInt32: |
| case IrOpcode::kFloat64ExtractLowWord32: |
| case IrOpcode::kFloat64ExtractHighWord32: |
| case IrOpcode::kBitcastFloat64ToInt64: |
| case IrOpcode::kTryTruncateFloat64ToInt64: |
| CheckValueInputForFloat64Op(node, 0); |
| break; |
| case IrOpcode::kWord64Equal: |
| if (Is64() && !COMPRESS_POINTERS_BOOL) { |
| CheckValueInputIsTaggedOrPointer(node, 0); |
| CheckValueInputIsTaggedOrPointer(node, 1); |
| if (!is_stub_) { |
| CheckValueInputRepresentationIs( |
| node, 1, inferrer_->GetRepresentation(node->InputAt(0))); |
| } |
| } else { |
| CheckValueInputForInt64Op(node, 0); |
| CheckValueInputForInt64Op(node, 1); |
| } |
| break; |
| case IrOpcode::kInt64LessThan: |
| case IrOpcode::kInt64LessThanOrEqual: |
| case IrOpcode::kUint64LessThan: |
| case IrOpcode::kUint64LessThanOrEqual: |
| CheckValueInputForInt64Op(node, 0); |
| CheckValueInputForInt64Op(node, 1); |
| break; |
| case IrOpcode::kI32x4ExtractLane: |
| case IrOpcode::kI16x8ExtractLaneU: |
| case IrOpcode::kI16x8ExtractLaneS: |
| case IrOpcode::kI8x16ExtractLaneU: |
| case IrOpcode::kI8x16ExtractLaneS: |
| CheckValueInputRepresentationIs(node, 0, |
| MachineRepresentation::kSimd128); |
| break; |
| case IrOpcode::kI32x4ReplaceLane: |
| CheckValueInputRepresentationIs(node, 0, |
| MachineRepresentation::kSimd128); |
| CheckValueInputForInt32Op(node, 1); |
| break; |
| case IrOpcode::kI32x4Splat: |
| CheckValueInputForInt32Op(node, 0); |
| break; |
| #define LABEL(opcode) case IrOpcode::k##opcode: |
| case IrOpcode::kChangeInt32ToTagged: |
| case IrOpcode::kChangeUint32ToTagged: |
| case IrOpcode::kChangeInt32ToFloat64: |
| case IrOpcode::kChangeUint32ToFloat64: |
| case IrOpcode::kRoundInt32ToFloat32: |
| case IrOpcode::kRoundUint32ToFloat32: |
| case IrOpcode::kBitcastInt32ToFloat32: |
| case IrOpcode::kBitcastWord32ToWord64: |
| case IrOpcode::kChangeInt32ToInt64: |
| case IrOpcode::kChangeUint32ToUint64: |
| MACHINE_UNOP_32_LIST(LABEL) { CheckValueInputForInt32Op(node, 0); } |
| break; |
| case IrOpcode::kWord32Equal: |
| if (Is32()) { |
| CheckValueInputIsTaggedOrPointer(node, 0); |
| CheckValueInputIsTaggedOrPointer(node, 1); |
| if (!is_stub_) { |
| CheckValueInputRepresentationIs( |
| node, 1, inferrer_->GetRepresentation(node->InputAt(0))); |
| } |
| } else { |
| if (COMPRESS_POINTERS_BOOL) { |
| CheckValueInputIsCompressedOrTaggedOrInt32(node, 0); |
| CheckValueInputIsCompressedOrTaggedOrInt32(node, 1); |
| } else { |
| CheckValueIsTaggedOrInt32(node, 0); |
| CheckValueIsTaggedOrInt32(node, 1); |
| } |
| } |
| break; |
| |
| case IrOpcode::kInt32LessThan: |
| case IrOpcode::kInt32LessThanOrEqual: |
| case IrOpcode::kUint32LessThan: |
| case IrOpcode::kUint32LessThanOrEqual: |
| MACHINE_BINOP_32_LIST(LABEL) { |
| CheckValueInputForInt32Op(node, 0); |
| CheckValueInputForInt32Op(node, 1); |
| } |
| break; |
| MACHINE_BINOP_64_LIST(LABEL) { |
| CheckValueInputForInt64Op(node, 0); |
| CheckValueInputForInt64Op(node, 1); |
| } |
| break; |
| case IrOpcode::kFloat32Equal: |
| case IrOpcode::kFloat32LessThan: |
| case IrOpcode::kFloat32LessThanOrEqual: |
| MACHINE_FLOAT32_BINOP_LIST(LABEL) { |
| CheckValueInputForFloat32Op(node, 0); |
| CheckValueInputForFloat32Op(node, 1); |
| } |
| break; |
| case IrOpcode::kChangeFloat32ToFloat64: |
| case IrOpcode::kTruncateFloat32ToInt32: |
| case IrOpcode::kTruncateFloat32ToUint32: |
| case IrOpcode::kBitcastFloat32ToInt32: |
| MACHINE_FLOAT32_UNOP_LIST(LABEL) { |
| CheckValueInputForFloat32Op(node, 0); |
| } |
| break; |
| case IrOpcode::kFloat64Equal: |
| case IrOpcode::kFloat64LessThan: |
| case IrOpcode::kFloat64LessThanOrEqual: |
| MACHINE_FLOAT64_BINOP_LIST(LABEL) { |
| CheckValueInputForFloat64Op(node, 0); |
| CheckValueInputForFloat64Op(node, 1); |
| } |
| break; |
| case IrOpcode::kFloat64SilenceNaN: |
| case IrOpcode::kChangeFloat64ToInt64: |
| case IrOpcode::kChangeFloat64ToUint64: |
| MACHINE_FLOAT64_UNOP_LIST(LABEL) { |
| CheckValueInputForFloat64Op(node, 0); |
| } |
| break; |
| #undef LABEL |
| case IrOpcode::kFloat64InsertLowWord32: |
| case IrOpcode::kFloat64InsertHighWord32: |
| CheckValueInputForFloat64Op(node, 0); |
| CheckValueInputForInt32Op(node, 1); |
| break; |
| case IrOpcode::kParameter: |
| case IrOpcode::kProjection: |
| break; |
| case IrOpcode::kAbortCSAAssert: |
| CheckValueInputIsTagged(node, 0); |
| break; |
| case IrOpcode::kLoad: |
| case IrOpcode::kWord32AtomicLoad: |
| case IrOpcode::kWord32AtomicPairLoad: |
| case IrOpcode::kWord64AtomicLoad: |
| case IrOpcode::kPoisonedLoad: |
| CheckValueInputIsTaggedOrPointer(node, 0); |
| CheckValueInputRepresentationIs( |
| node, 1, MachineType::PointerRepresentation()); |
| break; |
| case IrOpcode::kWord32AtomicPairAdd: |
| case IrOpcode::kWord32AtomicPairSub: |
| case IrOpcode::kWord32AtomicPairAnd: |
| case IrOpcode::kWord32AtomicPairOr: |
| case IrOpcode::kWord32AtomicPairXor: |
| case IrOpcode::kWord32AtomicPairStore: |
| case IrOpcode::kWord32AtomicPairExchange: |
| CheckValueInputRepresentationIs(node, 3, |
| MachineRepresentation::kWord32); |
| V8_FALLTHROUGH; |
| case IrOpcode::kStore: |
| case IrOpcode::kWord32AtomicStore: |
| case IrOpcode::kWord32AtomicExchange: |
| case IrOpcode::kWord32AtomicAdd: |
| case IrOpcode::kWord32AtomicSub: |
| case IrOpcode::kWord32AtomicAnd: |
| case IrOpcode::kWord32AtomicOr: |
| case IrOpcode::kWord32AtomicXor: |
| case IrOpcode::kWord64AtomicStore: |
| case IrOpcode::kWord64AtomicExchange: |
| case IrOpcode::kWord64AtomicAdd: |
| case IrOpcode::kWord64AtomicSub: |
| case IrOpcode::kWord64AtomicAnd: |
| case IrOpcode::kWord64AtomicOr: |
| case IrOpcode::kWord64AtomicXor: |
| CheckValueInputIsTaggedOrPointer(node, 0); |
| CheckValueInputRepresentationIs( |
| node, 1, MachineType::PointerRepresentation()); |
| switch (inferrer_->GetRepresentation(node)) { |
| case MachineRepresentation::kTagged: |
| case MachineRepresentation::kTaggedPointer: |
| case MachineRepresentation::kTaggedSigned: |
| if (COMPRESS_POINTERS_BOOL && |
| node->opcode() == IrOpcode::kStore && |
| CanBeTaggedPointer( |
| StoreRepresentationOf(node->op()).representation())) { |
| CheckValueInputIsCompressedOrTagged(node, 2); |
| } else { |
| CheckValueInputIsTagged(node, 2); |
| } |
| break; |
| default: |
| CheckValueInputRepresentationIs( |
| node, 2, inferrer_->GetRepresentation(node)); |
| } |
| break; |
| case IrOpcode::kWord32AtomicPairCompareExchange: |
| CheckValueInputRepresentationIs(node, 4, |
| MachineRepresentation::kWord32); |
| CheckValueInputRepresentationIs(node, 5, |
| MachineRepresentation::kWord32); |
| V8_FALLTHROUGH; |
| case IrOpcode::kWord32AtomicCompareExchange: |
| case IrOpcode::kWord64AtomicCompareExchange: |
| CheckValueInputIsTaggedOrPointer(node, 0); |
| CheckValueInputRepresentationIs( |
| node, 1, MachineType::PointerRepresentation()); |
| switch (inferrer_->GetRepresentation(node)) { |
| case MachineRepresentation::kTagged: |
| case MachineRepresentation::kTaggedPointer: |
| case MachineRepresentation::kTaggedSigned: |
| CheckValueInputIsTagged(node, 2); |
| CheckValueInputIsTagged(node, 3); |
| break; |
| default: |
| CheckValueInputRepresentationIs( |
| node, 2, inferrer_->GetRepresentation(node)); |
| CheckValueInputRepresentationIs( |
| node, 3, inferrer_->GetRepresentation(node)); |
| } |
| break; |
| case IrOpcode::kPhi: |
| switch (inferrer_->GetRepresentation(node)) { |
| case MachineRepresentation::kTagged: |
| case MachineRepresentation::kTaggedPointer: |
| for (int i = 0; i < node->op()->ValueInputCount(); ++i) { |
| CheckValueInputIsTagged(node, i); |
| } |
| break; |
| case MachineRepresentation::kTaggedSigned: |
| for (int i = 0; i < node->op()->ValueInputCount(); ++i) { |
| if (COMPRESS_POINTERS_BOOL) { |
| CheckValueInputIsCompressedOrTagged(node, i); |
| } else { |
| CheckValueInputIsTagged(node, i); |
| } |
| } |
| break; |
| case MachineRepresentation::kCompressed: |
| case MachineRepresentation::kCompressedPointer: |
| for (int i = 0; i < node->op()->ValueInputCount(); ++i) { |
| CheckValueInputIsCompressedOrTagged(node, i); |
| } |
| break; |
| case MachineRepresentation::kWord32: |
| for (int i = 0; i < node->op()->ValueInputCount(); ++i) { |
| CheckValueInputForInt32Op(node, i); |
| } |
| break; |
| default: |
| for (int i = 0; i < node->op()->ValueInputCount(); ++i) { |
| CheckValueInputRepresentationIs( |
| node, i, inferrer_->GetRepresentation(node)); |
| } |
| break; |
| } |
| break; |
| case IrOpcode::kBranch: |
| case IrOpcode::kSwitch: |
| CheckValueInputForInt32Op(node, 0); |
| break; |
| case IrOpcode::kReturn: { |
| // TODO(ishell): enable once the pop count parameter type becomes |
| // MachineType::PointerRepresentation(). Currently it's int32 or |
| // word-size. |
| // CheckValueInputRepresentationIs( |
| // node, 0, MachineType::PointerRepresentation()); // Pop count |
| size_t return_count = inferrer_->call_descriptor()->ReturnCount(); |
| for (size_t i = 0; i < return_count; i++) { |
| MachineType type = inferrer_->call_descriptor()->GetReturnType(i); |
| int input_index = static_cast<int>(i + 1); |
| switch (type.representation()) { |
| case MachineRepresentation::kTagged: |
| case MachineRepresentation::kTaggedPointer: |
| case MachineRepresentation::kTaggedSigned: |
| CheckValueInputIsTagged(node, input_index); |
| break; |
| case MachineRepresentation::kWord32: |
| CheckValueInputForInt32Op(node, input_index); |
| break; |
| default: |
| CheckValueInputRepresentationIs(node, input_index, |
| type.representation()); |
| break; |
| } |
| } |
| break; |
| } |
| case IrOpcode::kStackPointerGreaterThan: |
| CheckValueInputRepresentationIs( |
| node, 0, MachineType::PointerRepresentation()); |
| break; |
| case IrOpcode::kThrow: |
| case IrOpcode::kTypedStateValues: |
| case IrOpcode::kFrameState: |
| case IrOpcode::kStaticAssert: |
| break; |
| default: |
| if (node->op()->ValueInputCount() != 0) { |
| std::stringstream str; |
| str << "Node #" << node->id() << ":" << *node->op() |
| << " in the machine graph is not being checked."; |
| PrintDebugHelp(str, node); |
| FATAL("%s", str.str().c_str()); |
| } |
| break; |
| } |
| } |
| } |
| } |
| |
| private: |
| static bool Is32() { |
| return MachineType::PointerRepresentation() == |
| MachineRepresentation::kWord32; |
| } |
| static bool Is64() { |
| return MachineType::PointerRepresentation() == |
| MachineRepresentation::kWord64; |
| } |
| |
| void CheckValueInputRepresentationIs(Node const* node, int index, |
| MachineRepresentation representation) { |
| Node const* input = node->InputAt(index); |
| MachineRepresentation input_representation = |
| inferrer_->GetRepresentation(input); |
| if (input_representation != representation) { |
| std::stringstream str; |
| str << "TypeError: node #" << node->id() << ":" << *node->op() |
| << " uses node #" << input->id() << ":" << *input->op() << ":" |
| << input_representation << " which doesn't have a " << representation |
| << " representation."; |
| PrintDebugHelp(str, node); |
| FATAL("%s", str.str().c_str()); |
| } |
| } |
| |
| void CheckValueInputIsTagged(Node const* node, int index) { |
| Node const* input = node->InputAt(index); |
| switch (inferrer_->GetRepresentation(input)) { |
| case MachineRepresentation::kTagged: |
| case MachineRepresentation::kTaggedPointer: |
| case MachineRepresentation::kTaggedSigned: |
| return; |
| default: |
| break; |
| } |
| std::ostringstream str; |
| str << "TypeError: node #" << node->id() << ":" << *node->op() |
| << " uses node #" << input->id() << ":" << *input->op() |
| << " which doesn't have a tagged representation."; |
| PrintDebugHelp(str, node); |
| FATAL("%s", str.str().c_str()); |
| } |
| |
| void CheckValueInputIsCompressedOrTagged(Node const* node, int index) { |
| Node const* input = node->InputAt(index); |
| switch (inferrer_->GetRepresentation(input)) { |
| case MachineRepresentation::kCompressed: |
| case MachineRepresentation::kCompressedPointer: |
| case MachineRepresentation::kTagged: |
| case MachineRepresentation::kTaggedPointer: |
| case MachineRepresentation::kTaggedSigned: |
| return; |
| default: |
| break; |
| } |
| std::ostringstream str; |
| str << "TypeError: node #" << node->id() << ":" << *node->op() |
| << " uses node #" << input->id() << ":" << *input->op() |
| << " which doesn't have a compressed or tagged representation."; |
| PrintDebugHelp(str, node); |
| FATAL("%s", str.str().c_str()); |
| } |
| |
| void CheckValueInputIsCompressedOrTaggedOrInt32(Node const* node, int index) { |
| Node const* input = node->InputAt(index); |
| switch (inferrer_->GetRepresentation(input)) { |
| case MachineRepresentation::kCompressed: |
| case MachineRepresentation::kCompressedPointer: |
| return; |
| case MachineRepresentation::kTagged: |
| case MachineRepresentation::kTaggedPointer: |
| case MachineRepresentation::kTaggedSigned: |
| return; |
| case MachineRepresentation::kBit: |
| case MachineRepresentation::kWord8: |
| case MachineRepresentation::kWord16: |
| case MachineRepresentation::kWord32: |
| return; |
| default: |
| break; |
| } |
| std::ostringstream str; |
| str << "TypeError: node #" << node->id() << ":" << *node->op() |
| << " uses node #" << input->id() << ":" << *input->op() |
| << " which doesn't have a compressed, tagged, or int32 representation."; |
| PrintDebugHelp(str, node); |
| FATAL("%s", str.str().c_str()); |
| } |
| |
| void CheckValueInputIsTaggedOrPointer(Node const* node, int index) { |
| Node const* input = node->InputAt(index); |
| switch (inferrer_->GetRepresentation(input)) { |
| case MachineRepresentation::kTagged: |
| case MachineRepresentation::kTaggedPointer: |
| case MachineRepresentation::kTaggedSigned: |
| return; |
| case MachineRepresentation::kBit: |
| case MachineRepresentation::kWord8: |
| case MachineRepresentation::kWord16: |
| case MachineRepresentation::kWord32: |
| if (Is32()) { |
| return; |
| } |
| break; |
| case MachineRepresentation::kWord64: |
| if (Is64()) { |
| return; |
| } |
| break; |
| default: |
| break; |
| } |
| if (inferrer_->GetRepresentation(input) != |
| MachineType::PointerRepresentation()) { |
| std::ostringstream str; |
| str << "TypeError: node #" << node->id() << ":" << *node->op() |
| << " uses node #" << input->id() << ":" << *input->op() |
| << " which doesn't have a tagged or pointer representation."; |
| PrintDebugHelp(str, node); |
| FATAL("%s", str.str().c_str()); |
| } |
| } |
| |
| void CheckValueInputForInt32Op(Node const* node, int index) { |
| Node const* input = node->InputAt(index); |
| switch (inferrer_->GetRepresentation(input)) { |
| case MachineRepresentation::kBit: |
| case MachineRepresentation::kWord8: |
| case MachineRepresentation::kWord16: |
| case MachineRepresentation::kWord32: |
| return; |
| case MachineRepresentation::kNone: { |
| std::ostringstream str; |
| str << "TypeError: node #" << input->id() << ":" << *input->op() |
| << " is untyped."; |
| PrintDebugHelp(str, node); |
| FATAL("%s", str.str().c_str()); |
| break; |
| } |
| default: |
| break; |
| } |
| std::ostringstream str; |
| str << "TypeError: node #" << node->id() << ":" << *node->op() |
| << " uses node #" << input->id() << ":" << *input->op() |
| << " which doesn't have an int32-compatible representation."; |
| PrintDebugHelp(str, node); |
| FATAL("%s", str.str().c_str()); |
| } |
| |
| void CheckValueIsTaggedOrInt32(Node const* node, int index) { |
| Node const* input = node->InputAt(index); |
| switch (inferrer_->GetRepresentation(input)) { |
| case MachineRepresentation::kBit: |
| case MachineRepresentation::kWord8: |
| case MachineRepresentation::kWord16: |
| case MachineRepresentation::kWord32: |
| return; |
| case MachineRepresentation::kTagged: |
| case MachineRepresentation::kTaggedPointer: |
| return; |
| default: |
| break; |
| } |
| std::ostringstream str; |
| str << "TypeError: node #" << node->id() << ":" << *node->op() |
| << " uses node #" << input->id() << ":" << *input->op() |
| << " which doesn't have a tagged or int32-compatible " |
| "representation."; |
| PrintDebugHelp(str, node); |
| FATAL("%s", str.str().c_str()); |
| } |
| |
| void CheckValueInputForInt64Op(Node const* node, int index) { |
| Node const* input = node->InputAt(index); |
| MachineRepresentation input_representation = |
| inferrer_->GetRepresentation(input); |
| switch (input_representation) { |
| case MachineRepresentation::kWord64: |
| return; |
| case MachineRepresentation::kNone: { |
| std::ostringstream str; |
| str << "TypeError: node #" << input->id() << ":" << *input->op() |
| << " is untyped."; |
| PrintDebugHelp(str, node); |
| FATAL("%s", str.str().c_str()); |
| break; |
| } |
| |
| default: |
| break; |
| } |
| std::ostringstream str; |
| str << "TypeError: node #" << node->id() << ":" << *node->op() |
| << " uses node #" << input->id() << ":" << *input->op() << ":" |
| << input_representation |
| << " which doesn't have a kWord64 representation."; |
| PrintDebugHelp(str, node); |
| FATAL("%s", str.str().c_str()); |
| } |
| |
| void CheckValueInputForFloat32Op(Node const* node, int index) { |
| Node const* input = node->InputAt(index); |
| if (MachineRepresentation::kFloat32 == |
| inferrer_->GetRepresentation(input)) { |
| return; |
| } |
| std::ostringstream str; |
| str << "TypeError: node #" << node->id() << ":" << *node->op() |
| << " uses node #" << input->id() << ":" << *input->op() |
| << " which doesn't have a kFloat32 representation."; |
| PrintDebugHelp(str, node); |
| FATAL("%s", str.str().c_str()); |
| } |
| |
| void CheckValueInputForFloat64Op(Node const* node, int index) { |
| Node const* input = node->InputAt(index); |
| if (MachineRepresentation::kFloat64 == |
| inferrer_->GetRepresentation(input)) { |
| return; |
| } |
| std::ostringstream str; |
| str << "TypeError: node #" << node->id() << ":" << *node->op() |
| << " uses node #" << input->id() << ":" << *input->op() |
| << " which doesn't have a kFloat64 representation."; |
| PrintDebugHelp(str, node); |
| FATAL("%s", str.str().c_str()); |
| } |
| |
| void CheckCallInputs(Node const* node) { |
| auto call_descriptor = CallDescriptorOf(node->op()); |
| std::ostringstream str; |
| bool should_log_error = false; |
| for (size_t i = 0; i < call_descriptor->InputCount(); ++i) { |
| Node const* input = node->InputAt(static_cast<int>(i)); |
| MachineRepresentation const input_type = |
| inferrer_->GetRepresentation(input); |
| MachineRepresentation const expected_input_type = |
| call_descriptor->GetInputType(i).representation(); |
| if (!IsCompatible(expected_input_type, input_type)) { |
| if (!should_log_error) { |
| should_log_error = true; |
| str << "TypeError: node #" << node->id() << ":" << *node->op() |
| << " has wrong type for:" << std::endl; |
| } else { |
| str << std::endl; |
| } |
| str << " * input " << i << " (" << input->id() << ":" << *input->op() |
| << ") has a " << input_type |
| << " representation (expected: " << expected_input_type << ")."; |
| } |
| } |
| if (should_log_error) { |
| PrintDebugHelp(str, node); |
| FATAL("%s", str.str().c_str()); |
| } |
| } |
| |
| bool IsCompatible(MachineRepresentation expected, |
| MachineRepresentation actual) { |
| switch (expected) { |
| case MachineRepresentation::kTagged: |
| return IsAnyTagged(actual); |
| case MachineRepresentation::kCompressed: |
| return IsAnyCompressed(actual); |
| case MachineRepresentation::kTaggedSigned: |
| case MachineRepresentation::kTaggedPointer: |
| // TODO(tebbi): At the moment, the machine graph doesn't contain |
| // reliable information if a node is kTaggedSigned, kTaggedPointer or |
| // kTagged, and often this is context-dependent. We should at least |
| // check for obvious violations: kTaggedSigned where we expect |
| // kTaggedPointer and the other way around, but at the moment, this |
| // happens in dead code. |
| return IsAnyTagged(actual); |
| case MachineRepresentation::kCompressedPointer: |
| case MachineRepresentation::kFloat32: |
| case MachineRepresentation::kFloat64: |
| case MachineRepresentation::kSimd128: |
| case MachineRepresentation::kBit: |
| case MachineRepresentation::kWord8: |
| case MachineRepresentation::kWord16: |
| case MachineRepresentation::kWord64: |
| return expected == actual; |
| break; |
| case MachineRepresentation::kWord32: |
| return (actual == MachineRepresentation::kBit || |
| actual == MachineRepresentation::kWord8 || |
| actual == MachineRepresentation::kWord16 || |
| actual == MachineRepresentation::kWord32); |
| case MachineRepresentation::kNone: |
| UNREACHABLE(); |
| } |
| return false; |
| } |
| |
| void PrintDebugHelp(std::ostream& out, Node const* node) { |
| if (DEBUG_BOOL) { |
| out << "\n# Current block: " << *current_block_; |
| out << "\n#\n# Specify option --csa-trap-on-node=" << name_ << "," |
| << node->id() << " for debugging."; |
| } |
| } |
| |
| Schedule const* const schedule_; |
| MachineRepresentationInferrer const* const inferrer_; |
| bool is_stub_; |
| const char* name_; |
| BasicBlock* current_block_; |
| }; |
| |
| } // namespace |
| |
| void MachineGraphVerifier::Run(Graph* graph, Schedule const* const schedule, |
| Linkage* linkage, bool is_stub, const char* name, |
| Zone* temp_zone) { |
| MachineRepresentationInferrer representation_inferrer(schedule, graph, |
| linkage, temp_zone); |
| MachineRepresentationChecker checker(schedule, &representation_inferrer, |
| is_stub, name); |
| checker.Run(); |
| } |
| |
| } // namespace compiler |
| } // namespace internal |
| } // namespace v8 |