// 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(1)); return index == 0 ? MachineRepresentation::kWord32 : MachineRepresentation::kBit; case IrOpcode::kInt64AddWithOverflow: case IrOpcode::kInt64SubWithOverflow: CHECK_LE(index, static_cast(1)); return index == 0 ? MachineRepresentation::kWord64 : MachineRepresentation::kBit; case IrOpcode::kTryTruncateFloat32ToInt64: case IrOpcode::kTryTruncateFloat64ToInt64: case IrOpcode::kTryTruncateFloat32ToUint64: CHECK_LE(index, static_cast(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(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; case MachineRepresentation::kSandboxedPointer: // A sandboxed pointer is a Word64 that uses an encoded representation // when stored on the heap. return MachineRepresentation::kWord64; 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: representation_vector_[node->id()] = PromoteRepresentation(AtomicLoadParametersOf(node->op()) .representation() .representation()); break; case IrOpcode::kLoad: case IrOpcode::kLoadImmutable: case IrOpcode::kProtectedLoad: 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( AtomicStoreParametersOf(node->op()).representation()); 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: representation_vector_[node->id()] = MachineRepresentation::kTagged; 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: case IrOpcode::kWord32Popcnt: case IrOpcode::kI8x16BitMask: 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: case IrOpcode::kWord64Popcnt: case IrOpcode::kWord64Ctz: case IrOpcode::kWord64Clz: 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: case IrOpcode::kI8x16Splat: case IrOpcode::kI8x16Eq: representation_vector_[node->id()] = MachineRepresentation::kSimd128; break; #undef LABEL default: break; } } } } Schedule const* const schedule_; Linkage const* const linkage_; ZoneVector 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: case IrOpcode::kWord64Ctz: case IrOpcode::kWord64Clz: case IrOpcode::kWord64Popcnt: CheckValueInputForInt64Op(node, 0); break; case IrOpcode::kBitcastWordToTagged: case IrOpcode::kBitcastWordToTaggedSigned: CheckValueInputRepresentationIs( node, 0, MachineType::PointerRepresentation()); break; case IrOpcode::kBitcastTaggedToWord: case IrOpcode::kBitcastTaggedToWordForTagAndSmiBits: if (COMPRESS_POINTERS_BOOL) { CheckValueInputIsCompressedOrTagged(node, 0); } else { 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::kI8x16BitMask: 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: case IrOpcode::kI8x16Splat: CheckValueInputForInt32Op(node, 0); break; case IrOpcode::kI8x16Eq: CheckValueInputRepresentationIs(node, 0, MachineRepresentation::kSimd128); CheckValueInputRepresentationIs(node, 1, MachineRepresentation::kSimd128); 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: case IrOpcode::kWord32Popcnt: 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::kAbortCSADcheck: CheckValueInputIsTagged(node, 0); break; case IrOpcode::kLoad: case IrOpcode::kUnalignedLoad: case IrOpcode::kLoadImmutable: case IrOpcode::kWord32AtomicLoad: case IrOpcode::kWord32AtomicPairLoad: case IrOpcode::kWord64AtomicLoad: 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::kUnalignedStore: 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 && IsAnyTagged(StoreRepresentationOf(node->op()) .representation())) || (node->opcode() == IrOpcode::kWord32AtomicStore && IsAnyTagged(AtomicStoreParametersOf(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 j = 0; j < node->op()->ValueInputCount(); ++j) { CheckValueInputIsTagged(node, j); } break; case MachineRepresentation::kTaggedSigned: for (int j = 0; j < node->op()->ValueInputCount(); ++j) { if (COMPRESS_POINTERS_BOOL) { CheckValueInputIsCompressedOrTagged(node, j); } else { CheckValueInputIsTagged(node, j); } } break; case MachineRepresentation::kCompressed: case MachineRepresentation::kCompressedPointer: for (int j = 0; j < node->op()->ValueInputCount(); ++j) { CheckValueInputIsCompressedOrTagged(node, j); } break; case MachineRepresentation::kWord32: for (int j = 0; j < node->op()->ValueInputCount(); ++j) { CheckValueInputForInt32Op(node, j); } break; default: for (int j = 0; j < node->op()->ValueInputCount(); ++j) { CheckValueInputRepresentationIs( node, j, 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 j = 0; j < return_count; j++) { MachineType type = inferrer_->call_descriptor()->GetReturnType(j); int input_index = static_cast(j + 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()); } 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()); } 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(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::kMapWord: case MachineRepresentation::kTaggedSigned: case MachineRepresentation::kTaggedPointer: // TODO(turbofan): 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::kSandboxedPointer: case MachineRepresentation::kFloat32: case MachineRepresentation::kFloat64: case MachineRepresentation::kSimd128: case MachineRepresentation::kBit: case MachineRepresentation::kWord8: case MachineRepresentation::kWord16: case MachineRepresentation::kWord64: return expected == actual; 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