| // Copyright 2014 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/verifier.h" | |
| #include <algorithm> | |
| #include <deque> | |
| #include <queue> | |
| #include <sstream> | |
| #include <string> | |
| #include "src/bit-vector.h" | |
| #include "src/compiler/all-nodes.h" | |
| #include "src/compiler/common-operator.h" | |
| #include "src/compiler/graph.h" | |
| #include "src/compiler/js-operator.h" | |
| #include "src/compiler/node-properties.h" | |
| #include "src/compiler/node.h" | |
| #include "src/compiler/opcodes.h" | |
| #include "src/compiler/operator-properties.h" | |
| #include "src/compiler/operator.h" | |
| #include "src/compiler/schedule.h" | |
| #include "src/compiler/simplified-operator.h" | |
| #include "src/compiler/type-cache.h" | |
| #include "src/ostreams.h" | |
| namespace v8 { | |
| namespace internal { | |
| namespace compiler { | |
| class Verifier::Visitor { | |
| public: | |
| Visitor(Zone* z, Typing typed, CheckInputs check_inputs, CodeType code_type) | |
| : zone(z), | |
| typing(typed), | |
| check_inputs(check_inputs), | |
| code_type(code_type) {} | |
| void Check(Node* node, const AllNodes& all); | |
| Zone* zone; | |
| Typing typing; | |
| CheckInputs check_inputs; | |
| CodeType code_type; | |
| private: | |
| void CheckNotTyped(Node* node) { | |
| if (NodeProperties::IsTyped(node)) { | |
| std::ostringstream str; | |
| str << "TypeError: node #" << node->id() << ":" << *node->op() | |
| << " should never have a type"; | |
| FATAL("%s", str.str().c_str()); | |
| } | |
| } | |
| void CheckTypeIs(Node* node, Type type) { | |
| if (typing == TYPED && !NodeProperties::GetType(node).Is(type)) { | |
| std::ostringstream str; | |
| str << "TypeError: node #" << node->id() << ":" << *node->op() << " type " | |
| << NodeProperties::GetType(node) << " is not " << type; | |
| FATAL("%s", str.str().c_str()); | |
| } | |
| } | |
| void CheckTypeMaybe(Node* node, Type type) { | |
| if (typing == TYPED && !NodeProperties::GetType(node).Maybe(type)) { | |
| std::ostringstream str; | |
| str << "TypeError: node #" << node->id() << ":" << *node->op() << " type " | |
| << NodeProperties::GetType(node) << " must intersect " << type; | |
| FATAL("%s", str.str().c_str()); | |
| } | |
| } | |
| void CheckValueInputIs(Node* node, int i, Type type) { | |
| Node* input = NodeProperties::GetValueInput(node, i); | |
| if (typing == TYPED && !NodeProperties::GetType(input).Is(type)) { | |
| std::ostringstream str; | |
| str << "TypeError: node #" << node->id() << ":" << *node->op() | |
| << "(input @" << i << " = " << input->opcode() << ":" | |
| << input->op()->mnemonic() << ") type " | |
| << NodeProperties::GetType(input) << " is not " << type; | |
| FATAL("%s", str.str().c_str()); | |
| } | |
| } | |
| void CheckOutput(Node* node, Node* use, int count, const char* kind) { | |
| if (count <= 0) { | |
| std::ostringstream str; | |
| str << "GraphError: node #" << node->id() << ":" << *node->op() | |
| << " does not produce " << kind << " output used by node #" | |
| << use->id() << ":" << *use->op(); | |
| FATAL("%s", str.str().c_str()); | |
| } | |
| } | |
| }; | |
| void Verifier::Visitor::Check(Node* node, const AllNodes& all) { | |
| int value_count = node->op()->ValueInputCount(); | |
| int context_count = OperatorProperties::GetContextInputCount(node->op()); | |
| int frame_state_count = | |
| OperatorProperties::GetFrameStateInputCount(node->op()); | |
| int effect_count = node->op()->EffectInputCount(); | |
| int control_count = node->op()->ControlInputCount(); | |
| // Verify number of inputs matches up. | |
| int input_count = value_count + context_count + frame_state_count; | |
| if (check_inputs == kAll) { | |
| input_count += effect_count + control_count; | |
| } | |
| CHECK_EQ(input_count, node->InputCount()); | |
| // If this node has any effect outputs, make sure that it is | |
| // consumed as an effect input somewhere else. | |
| // TODO(mvstanton): support this kind of verification for WASM | |
| // compiles, too. | |
| if (code_type != kWasm && node->op()->EffectOutputCount() > 0) { | |
| int effect_edges = 0; | |
| for (Edge edge : node->use_edges()) { | |
| if (all.IsLive(edge.from()) && NodeProperties::IsEffectEdge(edge)) { | |
| effect_edges++; | |
| } | |
| } | |
| DCHECK_GT(effect_edges, 0); | |
| } | |
| // Verify that frame state has been inserted for the nodes that need it. | |
| for (int i = 0; i < frame_state_count; i++) { | |
| Node* frame_state = NodeProperties::GetFrameStateInput(node); | |
| CHECK(frame_state->opcode() == IrOpcode::kFrameState || | |
| // kFrameState uses Start as a sentinel. | |
| (node->opcode() == IrOpcode::kFrameState && | |
| frame_state->opcode() == IrOpcode::kStart)); | |
| } | |
| // Verify all value inputs actually produce a value. | |
| for (int i = 0; i < value_count; ++i) { | |
| Node* value = NodeProperties::GetValueInput(node, i); | |
| CheckOutput(value, node, value->op()->ValueOutputCount(), "value"); | |
| // Verify that only parameters and projections can have input nodes with | |
| // multiple outputs. | |
| CHECK(node->opcode() == IrOpcode::kParameter || | |
| node->opcode() == IrOpcode::kProjection || | |
| value->op()->ValueOutputCount() <= 1); | |
| } | |
| // Verify all context inputs are value nodes. | |
| for (int i = 0; i < context_count; ++i) { | |
| Node* context = NodeProperties::GetContextInput(node); | |
| CheckOutput(context, node, context->op()->ValueOutputCount(), "context"); | |
| } | |
| if (check_inputs == kAll) { | |
| // Verify all effect inputs actually have an effect. | |
| for (int i = 0; i < effect_count; ++i) { | |
| Node* effect = NodeProperties::GetEffectInput(node); | |
| CheckOutput(effect, node, effect->op()->EffectOutputCount(), "effect"); | |
| } | |
| // Verify all control inputs are control nodes. | |
| for (int i = 0; i < control_count; ++i) { | |
| Node* control = NodeProperties::GetControlInput(node, i); | |
| CheckOutput(control, node, control->op()->ControlOutputCount(), | |
| "control"); | |
| } | |
| // Verify that nodes that can throw either have both IfSuccess/IfException | |
| // projections as the only control uses or no projections at all. | |
| if (!node->op()->HasProperty(Operator::kNoThrow)) { | |
| Node* discovered_if_exception = nullptr; | |
| Node* discovered_if_success = nullptr; | |
| int total_number_of_control_uses = 0; | |
| for (Edge edge : node->use_edges()) { | |
| if (!NodeProperties::IsControlEdge(edge)) { | |
| continue; | |
| } | |
| total_number_of_control_uses++; | |
| Node* control_use = edge.from(); | |
| if (control_use->opcode() == IrOpcode::kIfSuccess) { | |
| CHECK_NULL(discovered_if_success); // Only one allowed. | |
| discovered_if_success = control_use; | |
| } | |
| if (control_use->opcode() == IrOpcode::kIfException) { | |
| CHECK_NULL(discovered_if_exception); // Only one allowed. | |
| discovered_if_exception = control_use; | |
| } | |
| } | |
| if (discovered_if_success && !discovered_if_exception) { | |
| FATAL( | |
| "#%d:%s should be followed by IfSuccess/IfException, but is " | |
| "only followed by single #%d:%s", | |
| node->id(), node->op()->mnemonic(), discovered_if_success->id(), | |
| discovered_if_success->op()->mnemonic()); | |
| } | |
| if (discovered_if_exception && !discovered_if_success) { | |
| FATAL( | |
| "#%d:%s should be followed by IfSuccess/IfException, but is " | |
| "only followed by single #%d:%s", | |
| node->id(), node->op()->mnemonic(), discovered_if_exception->id(), | |
| discovered_if_exception->op()->mnemonic()); | |
| } | |
| if (discovered_if_success || discovered_if_exception) { | |
| CHECK_EQ(2, total_number_of_control_uses); | |
| } | |
| } | |
| } | |
| switch (node->opcode()) { | |
| case IrOpcode::kStart: | |
| // Start has no inputs. | |
| CHECK_EQ(0, input_count); | |
| // Type is a tuple. | |
| // TODO(rossberg): Multiple outputs are currently typed as Internal. | |
| CheckTypeIs(node, Type::Internal()); | |
| break; | |
| case IrOpcode::kEnd: | |
| // End has no outputs. | |
| CHECK_EQ(0, node->op()->ValueOutputCount()); | |
| CHECK_EQ(0, node->op()->EffectOutputCount()); | |
| CHECK_EQ(0, node->op()->ControlOutputCount()); | |
| // All inputs are graph terminators. | |
| for (const Node* input : node->inputs()) { | |
| CHECK(IrOpcode::IsGraphTerminator(input->opcode())); | |
| } | |
| // Type is empty. | |
| CheckNotTyped(node); | |
| break; | |
| case IrOpcode::kDead: | |
| // Dead is never connected to the graph. | |
| UNREACHABLE(); | |
| case IrOpcode::kDeadValue: | |
| CheckValueInputIs(node, 0, Type::None()); | |
| CheckTypeIs(node, Type::None()); | |
| break; | |
| case IrOpcode::kUnreachable: | |
| CheckTypeIs(node, Type::None()); | |
| for (Edge edge : node->use_edges()) { | |
| Node* use = edge.from(); | |
| if (NodeProperties::IsValueEdge(edge) && all.IsLive(use)) { | |
| // {Unreachable} nodes can only be used by {DeadValue}, because they | |
| // don't actually produce a value. | |
| CHECK_EQ(IrOpcode::kDeadValue, use->opcode()); | |
| } | |
| } | |
| break; | |
| case IrOpcode::kBranch: { | |
| // Branch uses are IfTrue and IfFalse. | |
| int count_true = 0, count_false = 0; | |
| for (const Node* use : node->uses()) { | |
| CHECK(all.IsLive(use) && (use->opcode() == IrOpcode::kIfTrue || | |
| use->opcode() == IrOpcode::kIfFalse)); | |
| if (use->opcode() == IrOpcode::kIfTrue) ++count_true; | |
| if (use->opcode() == IrOpcode::kIfFalse) ++count_false; | |
| } | |
| CHECK_EQ(1, count_true); | |
| CHECK_EQ(1, count_false); | |
| // The condition must be a Boolean. | |
| CheckValueInputIs(node, 0, Type::Boolean()); | |
| // Type is empty. | |
| CheckNotTyped(node); | |
| break; | |
| } | |
| case IrOpcode::kIfTrue: | |
| case IrOpcode::kIfFalse: { | |
| Node* control = NodeProperties::GetControlInput(node, 0); | |
| CHECK_EQ(IrOpcode::kBranch, control->opcode()); | |
| // Type is empty. | |
| CheckNotTyped(node); | |
| break; | |
| } | |
| case IrOpcode::kIfSuccess: { | |
| // IfSuccess and IfException continuation only on throwing nodes. | |
| Node* input = NodeProperties::GetControlInput(node, 0); | |
| CHECK(!input->op()->HasProperty(Operator::kNoThrow)); | |
| // Type is empty. | |
| CheckNotTyped(node); | |
| break; | |
| } | |
| case IrOpcode::kIfException: { | |
| // IfSuccess and IfException continuation only on throwing nodes. | |
| Node* input = NodeProperties::GetControlInput(node, 0); | |
| CHECK(!input->op()->HasProperty(Operator::kNoThrow)); | |
| // Type can be anything. | |
| CheckTypeIs(node, Type::Any()); | |
| break; | |
| } | |
| case IrOpcode::kSwitch: { | |
| // Switch uses are Case and Default. | |
| int count_case = 0, count_default = 0; | |
| for (const Node* use : node->uses()) { | |
| CHECK(all.IsLive(use)); | |
| switch (use->opcode()) { | |
| case IrOpcode::kIfValue: { | |
| for (const Node* user : node->uses()) { | |
| if (user != use && user->opcode() == IrOpcode::kIfValue) { | |
| CHECK_NE(IfValueParametersOf(use->op()).value(), | |
| IfValueParametersOf(user->op()).value()); | |
| } | |
| } | |
| ++count_case; | |
| break; | |
| } | |
| case IrOpcode::kIfDefault: { | |
| ++count_default; | |
| break; | |
| } | |
| default: { | |
| FATAL("Switch #%d illegally used by #%d:%s", node->id(), use->id(), | |
| use->op()->mnemonic()); | |
| break; | |
| } | |
| } | |
| } | |
| CHECK_EQ(1, count_default); | |
| CHECK_EQ(node->op()->ControlOutputCount(), count_case + count_default); | |
| // Type is empty. | |
| CheckNotTyped(node); | |
| break; | |
| } | |
| case IrOpcode::kIfValue: | |
| case IrOpcode::kIfDefault: | |
| CHECK_EQ(IrOpcode::kSwitch, | |
| NodeProperties::GetControlInput(node)->opcode()); | |
| // Type is empty. | |
| CheckNotTyped(node); | |
| break; | |
| case IrOpcode::kLoop: { | |
| CHECK_EQ(control_count, input_count); | |
| // Type is empty. | |
| CheckNotTyped(node); | |
| // All loops need to be connected to a {Terminate} node to ensure they | |
| // stay connected to the graph end. | |
| bool has_terminate = false; | |
| for (const Node* use : node->uses()) { | |
| if (all.IsLive(use) && use->opcode() == IrOpcode::kTerminate) { | |
| has_terminate = true; | |
| break; | |
| } | |
| } | |
| CHECK(has_terminate); | |
| break; | |
| } | |
| case IrOpcode::kMerge: | |
| CHECK_EQ(control_count, input_count); | |
| // Type is empty. | |
| CheckNotTyped(node); | |
| break; | |
| case IrOpcode::kDeoptimizeIf: | |
| case IrOpcode::kDeoptimizeUnless: | |
| // Type is empty. | |
| CheckNotTyped(node); | |
| break; | |
| case IrOpcode::kTrapIf: | |
| case IrOpcode::kTrapUnless: | |
| // Type is empty. | |
| CheckNotTyped(node); | |
| break; | |
| case IrOpcode::kDeoptimize: | |
| case IrOpcode::kReturn: | |
| case IrOpcode::kThrow: | |
| // Deoptimize, Return and Throw uses are End. | |
| for (const Node* use : node->uses()) { | |
| if (all.IsLive(use)) { | |
| CHECK_EQ(IrOpcode::kEnd, use->opcode()); | |
| } | |
| } | |
| // Type is empty. | |
| CheckNotTyped(node); | |
| break; | |
| case IrOpcode::kTerminate: | |
| // Terminates take one loop and effect. | |
| CHECK_EQ(1, control_count); | |
| CHECK_EQ(1, effect_count); | |
| CHECK_EQ(2, input_count); | |
| CHECK_EQ(IrOpcode::kLoop, | |
| NodeProperties::GetControlInput(node)->opcode()); | |
| // Terminate uses are End. | |
| for (const Node* use : node->uses()) { | |
| if (all.IsLive(use)) { | |
| CHECK_EQ(IrOpcode::kEnd, use->opcode()); | |
| } | |
| } | |
| // Type is empty. | |
| CheckNotTyped(node); | |
| break; | |
| case IrOpcode::kOsrNormalEntry: | |
| case IrOpcode::kOsrLoopEntry: | |
| // Osr entries take one control and effect. | |
| CHECK_EQ(1, control_count); | |
| CHECK_EQ(1, effect_count); | |
| CHECK_EQ(2, input_count); | |
| // Type is empty. | |
| CheckNotTyped(node); | |
| break; | |
| // Common operators | |
| // ---------------- | |
| case IrOpcode::kParameter: { | |
| // Parameters have the start node as inputs. | |
| CHECK_EQ(1, input_count); | |
| // Parameter has an input that produces enough values. | |
| int const index = ParameterIndexOf(node->op()); | |
| Node* const start = NodeProperties::GetValueInput(node, 0); | |
| CHECK_EQ(IrOpcode::kStart, start->opcode()); | |
| // Currently, parameter indices start at -1 instead of 0. | |
| CHECK_LE(-1, index); | |
| CHECK_LT(index + 1, start->op()->ValueOutputCount()); | |
| // Type can be anything. | |
| CheckTypeIs(node, Type::Any()); | |
| break; | |
| } | |
| case IrOpcode::kInt32Constant: // TODO(turbofan): rename Word32Constant? | |
| case IrOpcode::kInt64Constant: // TODO(turbofan): rename Word64Constant? | |
| case IrOpcode::kFloat32Constant: | |
| case IrOpcode::kFloat64Constant: | |
| case IrOpcode::kRelocatableInt32Constant: | |
| case IrOpcode::kRelocatableInt64Constant: | |
| // Constants have no inputs. | |
| CHECK_EQ(0, input_count); | |
| // Type is empty. | |
| CheckNotTyped(node); | |
| break; | |
| case IrOpcode::kNumberConstant: | |
| // Constants have no inputs. | |
| CHECK_EQ(0, input_count); | |
| // Type is a number. | |
| CheckTypeIs(node, Type::Number()); | |
| break; | |
| case IrOpcode::kHeapConstant: | |
| // Constants have no inputs. | |
| CHECK_EQ(0, input_count); | |
| // Type is anything. | |
| CheckTypeIs(node, Type::Any()); | |
| break; | |
| case IrOpcode::kExternalConstant: | |
| case IrOpcode::kPointerConstant: | |
| // Constants have no inputs. | |
| CHECK_EQ(0, input_count); | |
| // Type is an external pointer. | |
| CheckTypeIs(node, Type::ExternalPointer()); | |
| break; | |
| case IrOpcode::kOsrValue: | |
| // OSR values have a value and a control input. | |
| CHECK_EQ(1, control_count); | |
| CHECK_EQ(1, input_count); | |
| // Type is merged from other values in the graph and could be any. | |
| CheckTypeIs(node, Type::Any()); | |
| break; | |
| case IrOpcode::kProjection: { | |
| // Projection has an input that produces enough values. | |
| int index = static_cast<int>(ProjectionIndexOf(node->op())); | |
| Node* input = NodeProperties::GetValueInput(node, 0); | |
| CHECK_GT(input->op()->ValueOutputCount(), index); | |
| // Type can be anything. | |
| // TODO(rossberg): Introduce tuple types for this. | |
| // TODO(titzer): Convince rossberg not to. | |
| CheckTypeIs(node, Type::Any()); | |
| break; | |
| } | |
| case IrOpcode::kSelect: { | |
| CHECK_EQ(0, effect_count); | |
| CHECK_EQ(0, control_count); | |
| CHECK_EQ(3, value_count); | |
| // The condition must be a Boolean. | |
| CheckValueInputIs(node, 0, Type::Boolean()); | |
| // Type can be anything. | |
| CheckTypeIs(node, Type::Any()); | |
| break; | |
| } | |
| case IrOpcode::kPhi: { | |
| // Phi input count matches parent control node. | |
| CHECK_EQ(0, effect_count); | |
| CHECK_EQ(1, control_count); | |
| Node* control = NodeProperties::GetControlInput(node, 0); | |
| CHECK_EQ(value_count, control->op()->ControlInputCount()); | |
| CHECK_EQ(input_count, 1 + value_count); | |
| // Type must be subsumed by all input types. | |
| // TODO(rossberg): for now at least, narrowing does not really hold. | |
| /* | |
| for (int i = 0; i < value_count; ++i) { | |
| CHECK(type_of(ValueInput(node, i))->Is(type_of(node))); | |
| } | |
| */ | |
| break; | |
| } | |
| case IrOpcode::kInductionVariablePhi: { | |
| // This is only a temporary node for the typer. | |
| UNREACHABLE(); | |
| break; | |
| } | |
| case IrOpcode::kEffectPhi: { | |
| // EffectPhi input count matches parent control node. | |
| CHECK_EQ(0, value_count); | |
| CHECK_EQ(1, control_count); | |
| Node* control = NodeProperties::GetControlInput(node, 0); | |
| CHECK_EQ(effect_count, control->op()->ControlInputCount()); | |
| CHECK_EQ(input_count, 1 + effect_count); | |
| // If the control input is a Merge, then make sure that at least one | |
| // of it's usages is non-phi. | |
| if (control->opcode() == IrOpcode::kMerge) { | |
| bool non_phi_use_found = false; | |
| for (Node* use : control->uses()) { | |
| if (all.IsLive(use) && use->opcode() != IrOpcode::kEffectPhi && | |
| use->opcode() != IrOpcode::kPhi) { | |
| non_phi_use_found = true; | |
| } | |
| } | |
| CHECK(non_phi_use_found); | |
| } | |
| break; | |
| } | |
| case IrOpcode::kLoopExit: { | |
| CHECK_EQ(2, control_count); | |
| Node* loop = NodeProperties::GetControlInput(node, 1); | |
| CHECK_EQ(IrOpcode::kLoop, loop->opcode()); | |
| break; | |
| } | |
| case IrOpcode::kLoopExitValue: { | |
| CHECK_EQ(1, control_count); | |
| Node* loop_exit = NodeProperties::GetControlInput(node, 0); | |
| CHECK_EQ(IrOpcode::kLoopExit, loop_exit->opcode()); | |
| break; | |
| } | |
| case IrOpcode::kLoopExitEffect: { | |
| CHECK_EQ(1, control_count); | |
| Node* loop_exit = NodeProperties::GetControlInput(node, 0); | |
| CHECK_EQ(IrOpcode::kLoopExit, loop_exit->opcode()); | |
| break; | |
| } | |
| case IrOpcode::kCheckpoint: | |
| // Type is empty. | |
| CheckNotTyped(node); | |
| break; | |
| case IrOpcode::kBeginRegion: | |
| // TODO(rossberg): what are the constraints on these? | |
| break; | |
| case IrOpcode::kFinishRegion: { | |
| // TODO(rossberg): what are the constraints on these? | |
| // Type must be subsumed by input type. | |
| if (typing == TYPED) { | |
| Node* val = NodeProperties::GetValueInput(node, 0); | |
| CHECK(NodeProperties::GetType(val).Is(NodeProperties::GetType(node))); | |
| } | |
| break; | |
| } | |
| case IrOpcode::kFrameState: { | |
| // TODO(jarin): what are the constraints on these? | |
| CHECK_EQ(5, value_count); | |
| CHECK_EQ(0, control_count); | |
| CHECK_EQ(0, effect_count); | |
| CHECK_EQ(6, input_count); | |
| // Check that the parameters and registers are kStateValues or | |
| // kTypedStateValues. | |
| for (int i = 0; i < 2; ++i) { | |
| CHECK(NodeProperties::GetValueInput(node, i)->opcode() == | |
| IrOpcode::kStateValues || | |
| NodeProperties::GetValueInput(node, i)->opcode() == | |
| IrOpcode::kTypedStateValues); | |
| } | |
| // The accumulator (InputAt(2)) cannot be kStateValues, but it can be | |
| // kTypedStateValues (to signal the type). Once AST graph builder | |
| // is removed, we should check this here. Until then, AST graph | |
| // builder can generate expression stack as InputAt(2), which can | |
| // still be kStateValues. | |
| break; | |
| } | |
| case IrOpcode::kObjectId: | |
| CheckTypeIs(node, Type::Object()); | |
| break; | |
| case IrOpcode::kStateValues: | |
| case IrOpcode::kTypedStateValues: | |
| case IrOpcode::kArgumentsElementsState: | |
| case IrOpcode::kArgumentsLengthState: | |
| case IrOpcode::kObjectState: | |
| case IrOpcode::kTypedObjectState: | |
| // TODO(jarin): what are the constraints on these? | |
| break; | |
| case IrOpcode::kCall: | |
| case IrOpcode::kCallWithCallerSavedRegisters: | |
| // TODO(rossberg): what are the constraints on these? | |
| break; | |
| case IrOpcode::kTailCall: | |
| // TODO(bmeurer): what are the constraints on these? | |
| break; | |
| // JavaScript operators | |
| // -------------------- | |
| case IrOpcode::kJSEqual: | |
| case IrOpcode::kJSStrictEqual: | |
| case IrOpcode::kJSLessThan: | |
| case IrOpcode::kJSGreaterThan: | |
| case IrOpcode::kJSLessThanOrEqual: | |
| case IrOpcode::kJSGreaterThanOrEqual: | |
| // Type is Boolean. | |
| CheckTypeIs(node, Type::Boolean()); | |
| break; | |
| case IrOpcode::kJSAdd: | |
| CheckTypeIs(node, Type::NumericOrString()); | |
| break; | |
| case IrOpcode::kJSBitwiseOr: | |
| case IrOpcode::kJSBitwiseXor: | |
| case IrOpcode::kJSBitwiseAnd: | |
| case IrOpcode::kJSShiftLeft: | |
| case IrOpcode::kJSShiftRight: | |
| case IrOpcode::kJSShiftRightLogical: | |
| case IrOpcode::kJSSubtract: | |
| case IrOpcode::kJSMultiply: | |
| case IrOpcode::kJSDivide: | |
| case IrOpcode::kJSModulus: | |
| case IrOpcode::kJSExponentiate: | |
| case IrOpcode::kJSBitwiseNot: | |
| case IrOpcode::kJSDecrement: | |
| case IrOpcode::kJSIncrement: | |
| case IrOpcode::kJSNegate: | |
| CheckTypeIs(node, Type::Numeric()); | |
| break; | |
| case IrOpcode::kToBoolean: | |
| // Type is Boolean. | |
| CheckTypeIs(node, Type::Boolean()); | |
| break; | |
| case IrOpcode::kJSToInteger: | |
| // Type is OrderedNumber. | |
| CheckTypeIs(node, Type::OrderedNumber()); | |
| break; | |
| case IrOpcode::kJSToLength: | |
| CheckTypeIs(node, Type::Range(0, kMaxSafeInteger, zone)); | |
| break; | |
| case IrOpcode::kJSToName: | |
| // Type is Name. | |
| CheckTypeIs(node, Type::Name()); | |
| break; | |
| case IrOpcode::kJSToNumber: | |
| case IrOpcode::kJSToNumberConvertBigInt: | |
| // Type is Number. | |
| CheckTypeIs(node, Type::Number()); | |
| break; | |
| case IrOpcode::kJSToNumeric: | |
| // Type is Numeric. | |
| CheckTypeIs(node, Type::Numeric()); | |
| break; | |
| case IrOpcode::kJSToString: | |
| // Type is String. | |
| CheckTypeIs(node, Type::String()); | |
| break; | |
| case IrOpcode::kJSToObject: | |
| // Type is Receiver. | |
| CheckTypeIs(node, Type::Receiver()); | |
| break; | |
| case IrOpcode::kJSParseInt: | |
| CheckValueInputIs(node, 0, Type::Any()); | |
| CheckValueInputIs(node, 1, Type::Any()); | |
| CheckTypeIs(node, Type::Number()); | |
| break; | |
| case IrOpcode::kJSRegExpTest: | |
| CheckValueInputIs(node, 0, Type::Any()); | |
| CheckValueInputIs(node, 1, Type::String()); | |
| CheckTypeIs(node, Type::Boolean()); | |
| break; | |
| case IrOpcode::kJSCreate: | |
| // Type is Object. | |
| CheckTypeIs(node, Type::Object()); | |
| break; | |
| case IrOpcode::kJSCreateArguments: | |
| // Type is Array \/ OtherObject. | |
| CheckTypeIs(node, Type::ArrayOrOtherObject()); | |
| break; | |
| case IrOpcode::kJSCreateArray: | |
| // Type is Array. | |
| CheckTypeIs(node, Type::Array()); | |
| break; | |
| case IrOpcode::kJSCreateArrayIterator: | |
| // Type is OtherObject. | |
| CheckTypeIs(node, Type::OtherObject()); | |
| break; | |
| case IrOpcode::kJSCreateCollectionIterator: | |
| // Type is OtherObject. | |
| CheckTypeIs(node, Type::OtherObject()); | |
| break; | |
| case IrOpcode::kJSCreateBoundFunction: | |
| // Type is BoundFunction. | |
| CheckTypeIs(node, Type::BoundFunction()); | |
| break; | |
| case IrOpcode::kJSCreateClosure: | |
| // Type is Function. | |
| CheckTypeIs(node, Type::Function()); | |
| break; | |
| case IrOpcode::kJSCreateIterResultObject: | |
| // Type is OtherObject. | |
| CheckTypeIs(node, Type::OtherObject()); | |
| break; | |
| case IrOpcode::kJSCreateStringIterator: | |
| // Type is OtherObject. | |
| CheckTypeIs(node, Type::OtherObject()); | |
| break; | |
| case IrOpcode::kJSCreateKeyValueArray: | |
| // Type is OtherObject. | |
| CheckTypeIs(node, Type::OtherObject()); | |
| break; | |
| case IrOpcode::kJSCreateObject: | |
| // Type is Object. | |
| CheckTypeIs(node, Type::OtherObject()); | |
| break; | |
| case IrOpcode::kJSCreatePromise: | |
| // Type is OtherObject. | |
| CheckTypeIs(node, Type::OtherObject()); | |
| break; | |
| case IrOpcode::kJSCreateTypedArray: | |
| // Type is OtherObject. | |
| CheckTypeIs(node, Type::OtherObject()); | |
| break; | |
| case IrOpcode::kJSCreateLiteralArray: | |
| // Type is Array. | |
| CheckTypeIs(node, Type::Array()); | |
| break; | |
| case IrOpcode::kJSCreateEmptyLiteralArray: | |
| // Type is Array. | |
| CheckTypeIs(node, Type::Array()); | |
| break; | |
| case IrOpcode::kJSCreateLiteralObject: | |
| case IrOpcode::kJSCreateEmptyLiteralObject: | |
| case IrOpcode::kJSCloneObject: | |
| case IrOpcode::kJSCreateLiteralRegExp: | |
| // Type is OtherObject. | |
| CheckTypeIs(node, Type::OtherObject()); | |
| break; | |
| case IrOpcode::kJSLoadProperty: | |
| // Type can be anything. | |
| CheckTypeIs(node, Type::Any()); | |
| CHECK(PropertyAccessOf(node->op()).feedback().IsValid()); | |
| break; | |
| case IrOpcode::kJSLoadNamed: | |
| // Type can be anything. | |
| CheckTypeIs(node, Type::Any()); | |
| CHECK(NamedAccessOf(node->op()).feedback().IsValid()); | |
| break; | |
| case IrOpcode::kJSLoadGlobal: | |
| // Type can be anything. | |
| CheckTypeIs(node, Type::Any()); | |
| CHECK(LoadGlobalParametersOf(node->op()).feedback().IsValid()); | |
| break; | |
| case IrOpcode::kJSStoreProperty: | |
| // Type is empty. | |
| CheckNotTyped(node); | |
| CHECK(PropertyAccessOf(node->op()).feedback().IsValid()); | |
| break; | |
| case IrOpcode::kJSStoreNamed: | |
| // Type is empty. | |
| CheckNotTyped(node); | |
| CHECK(NamedAccessOf(node->op()).feedback().IsValid()); | |
| break; | |
| case IrOpcode::kJSStoreGlobal: | |
| // Type is empty. | |
| CheckNotTyped(node); | |
| CHECK(StoreGlobalParametersOf(node->op()).feedback().IsValid()); | |
| break; | |
| case IrOpcode::kJSStoreNamedOwn: | |
| // Type is empty. | |
| CheckNotTyped(node); | |
| CHECK(StoreNamedOwnParametersOf(node->op()).feedback().IsValid()); | |
| break; | |
| case IrOpcode::kJSStoreDataPropertyInLiteral: | |
| case IrOpcode::kJSStoreInArrayLiteral: | |
| // Type is empty. | |
| CheckNotTyped(node); | |
| CHECK(FeedbackParameterOf(node->op()).feedback().IsValid()); | |
| break; | |
| case IrOpcode::kJSDeleteProperty: | |
| case IrOpcode::kJSHasProperty: | |
| case IrOpcode::kJSHasInPrototypeChain: | |
| case IrOpcode::kJSInstanceOf: | |
| case IrOpcode::kJSOrdinaryHasInstance: | |
| // Type is Boolean. | |
| CheckTypeIs(node, Type::Boolean()); | |
| break; | |
| case IrOpcode::kTypeOf: | |
| // Type is InternalizedString. | |
| CheckTypeIs(node, Type::InternalizedString()); | |
| break; | |
| case IrOpcode::kJSGetSuperConstructor: | |
| // We don't check the input for Type::Function because | |
| // this_function can be context-allocated. | |
| // Any -> Callable. | |
| CheckValueInputIs(node, 0, Type::Any()); | |
| CheckTypeIs(node, Type::Callable()); | |
| break; | |
| case IrOpcode::kJSLoadContext: | |
| // Type can be anything. | |
| CheckTypeIs(node, Type::Any()); | |
| break; | |
| case IrOpcode::kJSStoreContext: | |
| // Type is empty. | |
| CheckNotTyped(node); | |
| break; | |
| case IrOpcode::kJSCreateFunctionContext: | |
| case IrOpcode::kJSCreateCatchContext: | |
| case IrOpcode::kJSCreateWithContext: | |
| case IrOpcode::kJSCreateBlockContext: { | |
| // Type is Context, and operand is Internal. | |
| Node* context = NodeProperties::GetContextInput(node); | |
| // TODO(bmeurer): This should say CheckTypeIs, but we don't have type | |
| // OtherInternal on certain contexts, i.e. those from OsrValue inputs. | |
| CheckTypeMaybe(context, Type::OtherInternal()); | |
| CheckTypeIs(node, Type::OtherInternal()); | |
| break; | |
| } | |
| case IrOpcode::kJSConstructForwardVarargs: | |
| case IrOpcode::kJSConstruct: | |
| case IrOpcode::kJSConstructWithArrayLike: | |
| case IrOpcode::kJSConstructWithSpread: | |
| // Type is Receiver. | |
| CheckTypeIs(node, Type::Receiver()); | |
| break; | |
| case IrOpcode::kJSCallForwardVarargs: | |
| case IrOpcode::kJSCall: | |
| case IrOpcode::kJSCallWithArrayLike: | |
| case IrOpcode::kJSCallWithSpread: | |
| case IrOpcode::kJSCallRuntime: | |
| // Type can be anything. | |
| CheckTypeIs(node, Type::Any()); | |
| break; | |
| case IrOpcode::kJSForInEnumerate: | |
| // Any -> OtherInternal. | |
| CheckValueInputIs(node, 0, Type::Any()); | |
| CheckTypeIs(node, Type::OtherInternal()); | |
| break; | |
| case IrOpcode::kJSForInPrepare: | |
| // TODO(bmeurer): What are the constraints on thse? | |
| CheckTypeIs(node, Type::Any()); | |
| break; | |
| case IrOpcode::kJSForInNext: | |
| CheckTypeIs(node, Type::Union(Type::Name(), Type::Undefined(), zone)); | |
| break; | |
| case IrOpcode::kJSLoadMessage: | |
| case IrOpcode::kJSStoreMessage: | |
| break; | |
| case IrOpcode::kJSLoadModule: | |
| CheckTypeIs(node, Type::Any()); | |
| break; | |
| case IrOpcode::kJSStoreModule: | |
| CheckNotTyped(node); | |
| break; | |
| case IrOpcode::kJSGeneratorStore: | |
| CheckNotTyped(node); | |
| break; | |
| case IrOpcode::kJSCreateGeneratorObject: | |
| CheckTypeIs(node, Type::OtherObject()); | |
| break; | |
| case IrOpcode::kJSGeneratorRestoreContinuation: | |
| CheckTypeIs(node, Type::SignedSmall()); | |
| break; | |
| case IrOpcode::kJSGeneratorRestoreContext: | |
| CheckTypeIs(node, Type::Any()); | |
| break; | |
| case IrOpcode::kJSGeneratorRestoreRegister: | |
| CheckTypeIs(node, Type::Any()); | |
| break; | |
| case IrOpcode::kJSGeneratorRestoreInputOrDebugPos: | |
| CheckTypeIs(node, Type::Any()); | |
| break; | |
| case IrOpcode::kJSStackCheck: | |
| case IrOpcode::kJSDebugger: | |
| // Type is empty. | |
| CheckNotTyped(node); | |
| break; | |
| case IrOpcode::kJSFulfillPromise: | |
| CheckValueInputIs(node, 0, Type::Any()); | |
| CheckValueInputIs(node, 1, Type::Any()); | |
| CheckTypeIs(node, Type::Undefined()); | |
| break; | |
| case IrOpcode::kJSPerformPromiseThen: | |
| CheckValueInputIs(node, 0, Type::Any()); | |
| CheckValueInputIs(node, 1, Type::Any()); | |
| CheckValueInputIs(node, 2, Type::Any()); | |
| CheckValueInputIs(node, 3, Type::Any()); | |
| CheckTypeIs(node, Type::Receiver()); | |
| break; | |
| case IrOpcode::kJSPromiseResolve: | |
| CheckValueInputIs(node, 0, Type::Any()); | |
| CheckValueInputIs(node, 1, Type::Any()); | |
| CheckTypeIs(node, Type::Receiver()); | |
| break; | |
| case IrOpcode::kJSRejectPromise: | |
| CheckValueInputIs(node, 0, Type::Any()); | |
| CheckValueInputIs(node, 1, Type::Any()); | |
| CheckValueInputIs(node, 2, Type::Any()); | |
| CheckTypeIs(node, Type::Undefined()); | |
| break; | |
| case IrOpcode::kJSResolvePromise: | |
| CheckValueInputIs(node, 0, Type::Any()); | |
| CheckValueInputIs(node, 1, Type::Any()); | |
| CheckTypeIs(node, Type::Undefined()); | |
| break; | |
| case IrOpcode::kJSObjectIsArray: | |
| CheckValueInputIs(node, 0, Type::Any()); | |
| CheckTypeIs(node, Type::Boolean()); | |
| break; | |
| case IrOpcode::kComment: | |
| case IrOpcode::kDebugAbort: | |
| case IrOpcode::kDebugBreak: | |
| case IrOpcode::kRetain: | |
| case IrOpcode::kUnsafePointerAdd: | |
| case IrOpcode::kRuntimeAbort: | |
| CheckNotTyped(node); | |
| break; | |
| // Simplified operators | |
| // ------------------------------- | |
| case IrOpcode::kBooleanNot: | |
| // Boolean -> Boolean | |
| CheckValueInputIs(node, 0, Type::Boolean()); | |
| CheckTypeIs(node, Type::Boolean()); | |
| break; | |
| case IrOpcode::kNumberEqual: | |
| // (Number, Number) -> Boolean | |
| CheckValueInputIs(node, 0, Type::Number()); | |
| CheckValueInputIs(node, 1, Type::Number()); | |
| CheckTypeIs(node, Type::Boolean()); | |
| break; | |
| case IrOpcode::kNumberLessThan: | |
| case IrOpcode::kNumberLessThanOrEqual: | |
| // (Number, Number) -> Boolean | |
| CheckValueInputIs(node, 0, Type::Number()); | |
| CheckValueInputIs(node, 1, Type::Number()); | |
| CheckTypeIs(node, Type::Boolean()); | |
| break; | |
| case IrOpcode::kSpeculativeSafeIntegerAdd: | |
| case IrOpcode::kSpeculativeSafeIntegerSubtract: | |
| case IrOpcode::kSpeculativeNumberAdd: | |
| case IrOpcode::kSpeculativeNumberSubtract: | |
| case IrOpcode::kSpeculativeNumberMultiply: | |
| case IrOpcode::kSpeculativeNumberDivide: | |
| case IrOpcode::kSpeculativeNumberModulus: | |
| CheckTypeIs(node, Type::Number()); | |
| break; | |
| case IrOpcode::kSpeculativeNumberEqual: | |
| case IrOpcode::kSpeculativeNumberLessThan: | |
| case IrOpcode::kSpeculativeNumberLessThanOrEqual: | |
| CheckTypeIs(node, Type::Boolean()); | |
| break; | |
| case IrOpcode::kNumberAdd: | |
| case IrOpcode::kNumberSubtract: | |
| case IrOpcode::kNumberMultiply: | |
| case IrOpcode::kNumberDivide: | |
| // (Number, Number) -> Number | |
| CheckValueInputIs(node, 0, Type::Number()); | |
| CheckValueInputIs(node, 1, Type::Number()); | |
| CheckTypeIs(node, Type::Number()); | |
| break; | |
| case IrOpcode::kNumberModulus: | |
| // (Number, Number) -> Number | |
| CheckValueInputIs(node, 0, Type::Number()); | |
| CheckValueInputIs(node, 1, Type::Number()); | |
| CheckTypeIs(node, Type::Number()); | |
| break; | |
| case IrOpcode::kNumberBitwiseOr: | |
| case IrOpcode::kNumberBitwiseXor: | |
| case IrOpcode::kNumberBitwiseAnd: | |
| // (Signed32, Signed32) -> Signed32 | |
| CheckValueInputIs(node, 0, Type::Signed32()); | |
| CheckValueInputIs(node, 1, Type::Signed32()); | |
| CheckTypeIs(node, Type::Signed32()); | |
| break; | |
| case IrOpcode::kSpeculativeNumberBitwiseOr: | |
| case IrOpcode::kSpeculativeNumberBitwiseXor: | |
| case IrOpcode::kSpeculativeNumberBitwiseAnd: | |
| CheckTypeIs(node, Type::Signed32()); | |
| break; | |
| case IrOpcode::kNumberShiftLeft: | |
| case IrOpcode::kNumberShiftRight: | |
| // (Signed32, Unsigned32) -> Signed32 | |
| CheckValueInputIs(node, 0, Type::Signed32()); | |
| CheckValueInputIs(node, 1, Type::Unsigned32()); | |
| CheckTypeIs(node, Type::Signed32()); | |
| break; | |
| case IrOpcode::kSpeculativeNumberShiftLeft: | |
| case IrOpcode::kSpeculativeNumberShiftRight: | |
| CheckTypeIs(node, Type::Signed32()); | |
| break; | |
| case IrOpcode::kNumberShiftRightLogical: | |
| // (Unsigned32, Unsigned32) -> Unsigned32 | |
| CheckValueInputIs(node, 0, Type::Unsigned32()); | |
| CheckValueInputIs(node, 1, Type::Unsigned32()); | |
| CheckTypeIs(node, Type::Unsigned32()); | |
| break; | |
| case IrOpcode::kSpeculativeNumberShiftRightLogical: | |
| CheckTypeIs(node, Type::Unsigned32()); | |
| break; | |
| case IrOpcode::kNumberImul: | |
| // (Unsigned32, Unsigned32) -> Signed32 | |
| CheckValueInputIs(node, 0, Type::Unsigned32()); | |
| CheckValueInputIs(node, 1, Type::Unsigned32()); | |
| CheckTypeIs(node, Type::Signed32()); | |
| break; | |
| case IrOpcode::kNumberClz32: | |
| // Unsigned32 -> Unsigned32 | |
| CheckValueInputIs(node, 0, Type::Unsigned32()); | |
| CheckTypeIs(node, Type::Unsigned32()); | |
| break; | |
| case IrOpcode::kNumberAtan2: | |
| case IrOpcode::kNumberMax: | |
| case IrOpcode::kNumberMin: | |
| case IrOpcode::kNumberPow: | |
| // (Number, Number) -> Number | |
| CheckValueInputIs(node, 0, Type::Number()); | |
| CheckValueInputIs(node, 1, Type::Number()); | |
| CheckTypeIs(node, Type::Number()); | |
| break; | |
| case IrOpcode::kNumberAbs: | |
| case IrOpcode::kNumberCeil: | |
| case IrOpcode::kNumberFloor: | |
| case IrOpcode::kNumberFround: | |
| case IrOpcode::kNumberAcos: | |
| case IrOpcode::kNumberAcosh: | |
| case IrOpcode::kNumberAsin: | |
| case IrOpcode::kNumberAsinh: | |
| case IrOpcode::kNumberAtan: | |
| case IrOpcode::kNumberAtanh: | |
| case IrOpcode::kNumberCos: | |
| case IrOpcode::kNumberCosh: | |
| case IrOpcode::kNumberExp: | |
| case IrOpcode::kNumberExpm1: | |
| case IrOpcode::kNumberLog: | |
| case IrOpcode::kNumberLog1p: | |
| case IrOpcode::kNumberLog2: | |
| case IrOpcode::kNumberLog10: | |
| case IrOpcode::kNumberCbrt: | |
| case IrOpcode::kNumberRound: | |
| case IrOpcode::kNumberSign: | |
| case IrOpcode::kNumberSin: | |
| case IrOpcode::kNumberSinh: | |
| case IrOpcode::kNumberSqrt: | |
| case IrOpcode::kNumberTan: | |
| case IrOpcode::kNumberTanh: | |
| case IrOpcode::kNumberTrunc: | |
| // Number -> Number | |
| CheckValueInputIs(node, 0, Type::Number()); | |
| CheckTypeIs(node, Type::Number()); | |
| break; | |
| case IrOpcode::kNumberToBoolean: | |
| // Number -> Boolean | |
| CheckValueInputIs(node, 0, Type::Number()); | |
| CheckTypeIs(node, Type::Boolean()); | |
| break; | |
| case IrOpcode::kNumberToInt32: | |
| // Number -> Signed32 | |
| CheckValueInputIs(node, 0, Type::Number()); | |
| CheckTypeIs(node, Type::Signed32()); | |
| break; | |
| case IrOpcode::kNumberToString: | |
| // Number -> String | |
| CheckValueInputIs(node, 0, Type::Number()); | |
| CheckTypeIs(node, Type::String()); | |
| break; | |
| case IrOpcode::kNumberToUint32: | |
| case IrOpcode::kNumberToUint8Clamped: | |
| // Number -> Unsigned32 | |
| CheckValueInputIs(node, 0, Type::Number()); | |
| CheckTypeIs(node, Type::Unsigned32()); | |
| break; | |
| case IrOpcode::kSpeculativeToNumber: | |
| // Any -> Number | |
| CheckValueInputIs(node, 0, Type::Any()); | |
| CheckTypeIs(node, Type::Number()); | |
| break; | |
| case IrOpcode::kPlainPrimitiveToNumber: | |
| // PlainPrimitive -> Number | |
| CheckValueInputIs(node, 0, Type::PlainPrimitive()); | |
| CheckTypeIs(node, Type::Number()); | |
| break; | |
| case IrOpcode::kPlainPrimitiveToWord32: | |
| // PlainPrimitive -> Integral32 | |
| CheckValueInputIs(node, 0, Type::PlainPrimitive()); | |
| CheckTypeIs(node, Type::Integral32()); | |
| break; | |
| case IrOpcode::kPlainPrimitiveToFloat64: | |
| // PlainPrimitive -> Number | |
| CheckValueInputIs(node, 0, Type::PlainPrimitive()); | |
| CheckTypeIs(node, Type::Number()); | |
| break; | |
| case IrOpcode::kStringEqual: | |
| case IrOpcode::kStringLessThan: | |
| case IrOpcode::kStringLessThanOrEqual: | |
| // (String, String) -> Boolean | |
| CheckValueInputIs(node, 0, Type::String()); | |
| CheckValueInputIs(node, 1, Type::String()); | |
| CheckTypeIs(node, Type::Boolean()); | |
| break; | |
| case IrOpcode::kStringToNumber: | |
| // String -> Number | |
| CheckValueInputIs(node, 0, Type::String()); | |
| CheckTypeIs(node, Type::Number()); | |
| break; | |
| case IrOpcode::kStringCharCodeAt: | |
| // (String, Unsigned32) -> UnsignedSmall | |
| CheckValueInputIs(node, 0, Type::String()); | |
| CheckValueInputIs(node, 1, Type::Unsigned32()); | |
| CheckTypeIs(node, Type::UnsignedSmall()); | |
| break; | |
| case IrOpcode::kStringCodePointAt: | |
| // (String, Unsigned32) -> UnsignedSmall | |
| CheckValueInputIs(node, 0, Type::String()); | |
| CheckValueInputIs(node, 1, Type::Unsigned32()); | |
| CheckTypeIs(node, Type::UnsignedSmall()); | |
| break; | |
| case IrOpcode::kStringFromSingleCharCode: | |
| // Number -> String | |
| CheckValueInputIs(node, 0, Type::Number()); | |
| CheckTypeIs(node, Type::String()); | |
| break; | |
| case IrOpcode::kStringFromSingleCodePoint: | |
| // (Unsigned32) -> String | |
| CheckValueInputIs(node, 0, Type::Number()); | |
| CheckTypeIs(node, Type::String()); | |
| break; | |
| case IrOpcode::kStringIndexOf: | |
| // (String, String, SignedSmall) -> SignedSmall | |
| CheckValueInputIs(node, 0, Type::String()); | |
| CheckValueInputIs(node, 1, Type::String()); | |
| CheckValueInputIs(node, 2, Type::SignedSmall()); | |
| CheckTypeIs(node, Type::SignedSmall()); | |
| break; | |
| case IrOpcode::kStringLength: | |
| CheckValueInputIs(node, 0, Type::String()); | |
| CheckTypeIs(node, TypeCache::Get().kStringLengthType); | |
| break; | |
| case IrOpcode::kStringToLowerCaseIntl: | |
| case IrOpcode::kStringToUpperCaseIntl: | |
| CheckValueInputIs(node, 0, Type::String()); | |
| CheckTypeIs(node, Type::String()); | |
| break; | |
| case IrOpcode::kStringSubstring: | |
| CheckValueInputIs(node, 0, Type::String()); | |
| CheckValueInputIs(node, 1, Type::SignedSmall()); | |
| CheckValueInputIs(node, 2, Type::SignedSmall()); | |
| CheckTypeIs(node, Type::String()); | |
| break; | |
| case IrOpcode::kReferenceEqual: | |
| // (Unique, Any) -> Boolean and | |
| // (Any, Unique) -> Boolean | |
| CheckTypeIs(node, Type::Boolean()); | |
| break; | |
| case IrOpcode::kSameValue: | |
| // (Any, Any) -> Boolean | |
| CheckValueInputIs(node, 0, Type::Any()); | |
| CheckValueInputIs(node, 1, Type::Any()); | |
| CheckTypeIs(node, Type::Boolean()); | |
| break; | |
| case IrOpcode::kObjectIsArrayBufferView: | |
| case IrOpcode::kObjectIsBigInt: | |
| case IrOpcode::kObjectIsCallable: | |
| case IrOpcode::kObjectIsConstructor: | |
| case IrOpcode::kObjectIsDetectableCallable: | |
| case IrOpcode::kObjectIsMinusZero: | |
| case IrOpcode::kObjectIsNaN: | |
| case IrOpcode::kObjectIsNonCallable: | |
| case IrOpcode::kObjectIsNumber: | |
| case IrOpcode::kObjectIsReceiver: | |
| case IrOpcode::kObjectIsSmi: | |
| case IrOpcode::kObjectIsString: | |
| case IrOpcode::kObjectIsSymbol: | |
| case IrOpcode::kObjectIsUndetectable: | |
| case IrOpcode::kArrayBufferWasNeutered: | |
| CheckValueInputIs(node, 0, Type::Any()); | |
| CheckTypeIs(node, Type::Boolean()); | |
| break; | |
| case IrOpcode::kNumberIsFloat64Hole: | |
| CheckValueInputIs(node, 0, Type::NumberOrHole()); | |
| CheckTypeIs(node, Type::Boolean()); | |
| break; | |
| case IrOpcode::kNumberIsFinite: | |
| CheckValueInputIs(node, 0, Type::Number()); | |
| CheckTypeIs(node, Type::Boolean()); | |
| break; | |
| case IrOpcode::kNumberIsNaN: | |
| CheckValueInputIs(node, 0, Type::Number()); | |
| CheckTypeIs(node, Type::Boolean()); | |
| break; | |
| case IrOpcode::kObjectIsFiniteNumber: | |
| CheckValueInputIs(node, 0, Type::Any()); | |
| CheckTypeIs(node, Type::Boolean()); | |
| break; | |
| case IrOpcode::kNumberIsInteger: | |
| CheckValueInputIs(node, 0, Type::Number()); | |
| CheckTypeIs(node, Type::Boolean()); | |
| break; | |
| case IrOpcode::kObjectIsSafeInteger: | |
| CheckValueInputIs(node, 0, Type::Any()); | |
| CheckTypeIs(node, Type::Boolean()); | |
| break; | |
| case IrOpcode::kNumberIsSafeInteger: | |
| CheckValueInputIs(node, 0, Type::Number()); | |
| CheckTypeIs(node, Type::Boolean()); | |
| break; | |
| case IrOpcode::kObjectIsInteger: | |
| CheckValueInputIs(node, 0, Type::Any()); | |
| CheckTypeIs(node, Type::Boolean()); | |
| break; | |
| case IrOpcode::kFindOrderedHashMapEntry: | |
| CheckValueInputIs(node, 0, Type::Any()); | |
| CheckTypeIs(node, Type::SignedSmall()); | |
| break; | |
| case IrOpcode::kFindOrderedHashMapEntryForInt32Key: | |
| CheckValueInputIs(node, 0, Type::Any()); | |
| CheckValueInputIs(node, 1, Type::Signed32()); | |
| CheckTypeIs(node, Type::SignedSmall()); | |
| break; | |
| case IrOpcode::kArgumentsLength: | |
| CheckValueInputIs(node, 0, Type::ExternalPointer()); | |
| CheckTypeIs(node, TypeCache::Get().kArgumentsLengthType); | |
| break; | |
| case IrOpcode::kArgumentsFrame: | |
| CheckTypeIs(node, Type::ExternalPointer()); | |
| break; | |
| case IrOpcode::kNewDoubleElements: | |
| case IrOpcode::kNewSmiOrObjectElements: | |
| CheckValueInputIs(node, 0, | |
| Type::Range(0.0, FixedArray::kMaxLength, zone)); | |
| CheckTypeIs(node, Type::OtherInternal()); | |
| break; | |
| case IrOpcode::kNewArgumentsElements: | |
| CheckValueInputIs(node, 0, Type::ExternalPointer()); | |
| CheckValueInputIs(node, 1, Type::Range(-Code::kMaxArguments, | |
| Code::kMaxArguments, zone)); | |
| CheckTypeIs(node, Type::OtherInternal()); | |
| break; | |
| case IrOpcode::kNewConsString: | |
| CheckValueInputIs(node, 0, TypeCache::Get().kStringLengthType); | |
| CheckValueInputIs(node, 1, Type::String()); | |
| CheckValueInputIs(node, 2, Type::String()); | |
| CheckTypeIs(node, Type::String()); | |
| break; | |
| case IrOpcode::kAllocate: | |
| CheckValueInputIs(node, 0, Type::PlainNumber()); | |
| break; | |
| case IrOpcode::kAllocateRaw: | |
| // CheckValueInputIs(node, 0, Type::PlainNumber()); | |
| break; | |
| case IrOpcode::kEnsureWritableFastElements: | |
| CheckValueInputIs(node, 0, Type::Any()); | |
| CheckValueInputIs(node, 1, Type::Internal()); | |
| CheckTypeIs(node, Type::Internal()); | |
| break; | |
| case IrOpcode::kMaybeGrowFastElements: | |
| CheckValueInputIs(node, 0, Type::Any()); | |
| CheckValueInputIs(node, 1, Type::Internal()); | |
| CheckValueInputIs(node, 2, Type::Unsigned31()); | |
| CheckValueInputIs(node, 3, Type::Unsigned31()); | |
| CheckTypeIs(node, Type::Internal()); | |
| break; | |
| case IrOpcode::kTransitionElementsKind: | |
| CheckValueInputIs(node, 0, Type::Any()); | |
| CheckNotTyped(node); | |
| break; | |
| case IrOpcode::kChangeTaggedSignedToInt32: { | |
| // Signed32 /\ Tagged -> Signed32 /\ UntaggedInt32 | |
| // TODO(neis): Activate once ChangeRepresentation works in typer. | |
| // Type from = Type::Intersect(Type::Signed32(), Type::Tagged()); | |
| // Type to = Type::Intersect(Type::Signed32(), Type::UntaggedInt32()); | |
| // CheckValueInputIs(node, 0, from)); | |
| // CheckTypeIs(node, to)); | |
| break; | |
| } | |
| case IrOpcode::kChangeTaggedToInt32: { | |
| // Signed32 /\ Tagged -> Signed32 /\ UntaggedInt32 | |
| // TODO(neis): Activate once ChangeRepresentation works in typer. | |
| // Type from = Type::Intersect(Type::Signed32(), Type::Tagged()); | |
| // Type to = Type::Intersect(Type::Signed32(), Type::UntaggedInt32()); | |
| // CheckValueInputIs(node, 0, from)); | |
| // CheckTypeIs(node, to)); | |
| break; | |
| } | |
| case IrOpcode::kChangeTaggedToUint32: { | |
| // Unsigned32 /\ Tagged -> Unsigned32 /\ UntaggedInt32 | |
| // TODO(neis): Activate once ChangeRepresentation works in typer. | |
| // Type from = Type::Intersect(Type::Unsigned32(), Type::Tagged()); | |
| // Type to =Type::Intersect(Type::Unsigned32(), Type::UntaggedInt32()); | |
| // CheckValueInputIs(node, 0, from)); | |
| // CheckTypeIs(node, to)); | |
| break; | |
| } | |
| case IrOpcode::kChangeTaggedToFloat64: { | |
| // NumberOrUndefined /\ Tagged -> Number /\ UntaggedFloat64 | |
| // TODO(neis): Activate once ChangeRepresentation works in typer. | |
| // Type from = Type::Intersect(Type::Number(), Type::Tagged()); | |
| // Type to = Type::Intersect(Type::Number(), Type::UntaggedFloat64()); | |
| // CheckValueInputIs(node, 0, from)); | |
| // CheckTypeIs(node, to)); | |
| break; | |
| } | |
| case IrOpcode::kChangeTaggedToTaggedSigned: | |
| break; | |
| case IrOpcode::kTruncateTaggedToFloat64: { | |
| // NumberOrUndefined /\ Tagged -> Number /\ UntaggedFloat64 | |
| // TODO(neis): Activate once ChangeRepresentation works in typer. | |
| // Type from = Type::Intersect(Type::NumberOrUndefined(), | |
| // Type::Tagged()); | |
| // Type to = Type::Intersect(Type::Number(), Type::UntaggedFloat64()); | |
| // CheckValueInputIs(node, 0, from)); | |
| // CheckTypeIs(node, to)); | |
| break; | |
| } | |
| case IrOpcode::kChangeInt31ToTaggedSigned: { | |
| // Signed31 /\ UntaggedInt32 -> Signed31 /\ Tagged | |
| // TODO(neis): Activate once ChangeRepresentation works in typer. | |
| // Type from =Type::Intersect(Type::Signed31(), Type::UntaggedInt32()); | |
| // Type to = Type::Intersect(Type::Signed31(), Type::Tagged()); | |
| // CheckValueInputIs(node, 0, from)); | |
| // CheckTypeIs(node, to)); | |
| break; | |
| } | |
| case IrOpcode::kChangeInt32ToTagged: { | |
| // Signed32 /\ UntaggedInt32 -> Signed32 /\ Tagged | |
| // TODO(neis): Activate once ChangeRepresentation works in typer. | |
| // Type from =Type::Intersect(Type::Signed32(), Type::UntaggedInt32()); | |
| // Type to = Type::Intersect(Type::Signed32(), Type::Tagged()); | |
| // CheckValueInputIs(node, 0, from)); | |
| // CheckTypeIs(node, to)); | |
| break; | |
| } | |
| case IrOpcode::kChangeUint32ToTagged: { | |
| // Unsigned32 /\ UntaggedInt32 -> Unsigned32 /\ Tagged | |
| // TODO(neis): Activate once ChangeRepresentation works in typer. | |
| // Type from=Type::Intersect(Type::Unsigned32(),Type::UntaggedInt32()); | |
| // Type to = Type::Intersect(Type::Unsigned32(), Type::Tagged()); | |
| // CheckValueInputIs(node, 0, from)); | |
| // CheckTypeIs(node, to)); | |
| break; | |
| } | |
| case IrOpcode::kChangeFloat64ToTagged: { | |
| // Number /\ UntaggedFloat64 -> Number /\ Tagged | |
| // TODO(neis): Activate once ChangeRepresentation works in typer. | |
| // Type from =Type::Intersect(Type::Number(), Type::UntaggedFloat64()); | |
| // Type to = Type::Intersect(Type::Number(), Type::Tagged()); | |
| // CheckValueInputIs(node, 0, from)); | |
| // CheckTypeIs(node, to)); | |
| break; | |
| } | |
| case IrOpcode::kChangeFloat64ToTaggedPointer: | |
| break; | |
| case IrOpcode::kChangeTaggedToBit: { | |
| // Boolean /\ TaggedPtr -> Boolean /\ UntaggedInt1 | |
| // TODO(neis): Activate once ChangeRepresentation works in typer. | |
| // Type from = Type::Intersect(Type::Boolean(), Type::TaggedPtr()); | |
| // Type to = Type::Intersect(Type::Boolean(), Type::UntaggedInt1()); | |
| // CheckValueInputIs(node, 0, from)); | |
| // CheckTypeIs(node, to)); | |
| break; | |
| } | |
| case IrOpcode::kChangeBitToTagged: { | |
| // Boolean /\ UntaggedInt1 -> Boolean /\ TaggedPtr | |
| // TODO(neis): Activate once ChangeRepresentation works in typer. | |
| // Type from = Type::Intersect(Type::Boolean(), Type::UntaggedInt1()); | |
| // Type to = Type::Intersect(Type::Boolean(), Type::TaggedPtr()); | |
| // CheckValueInputIs(node, 0, from)); | |
| // CheckTypeIs(node, to)); | |
| break; | |
| } | |
| case IrOpcode::kTruncateTaggedToWord32: { | |
| // Number /\ Tagged -> Signed32 /\ UntaggedInt32 | |
| // TODO(neis): Activate once ChangeRepresentation works in typer. | |
| // Type from = Type::Intersect(Type::Number(), Type::Tagged()); | |
| // Type to = Type::Intersect(Type::Number(), Type::UntaggedInt32()); | |
| // CheckValueInputIs(node, 0, from)); | |
| // CheckTypeIs(node, to)); | |
| break; | |
| } | |
| case IrOpcode::kTruncateTaggedToBit: | |
| case IrOpcode::kTruncateTaggedPointerToBit: | |
| break; | |
| case IrOpcode::kCheckBounds: | |
| CheckValueInputIs(node, 0, Type::Any()); | |
| CheckValueInputIs(node, 1, Type::Unsigned31()); | |
| CheckTypeIs(node, Type::Unsigned31()); | |
| break; | |
| case IrOpcode::kPoisonIndex: | |
| CheckValueInputIs(node, 0, Type::Unsigned32()); | |
| CheckTypeIs(node, Type::Unsigned32()); | |
| break; | |
| case IrOpcode::kCheckHeapObject: | |
| CheckValueInputIs(node, 0, Type::Any()); | |
| break; | |
| case IrOpcode::kCheckIf: | |
| CheckValueInputIs(node, 0, Type::Boolean()); | |
| CheckNotTyped(node); | |
| break; | |
| case IrOpcode::kCheckInternalizedString: | |
| CheckValueInputIs(node, 0, Type::Any()); | |
| CheckTypeIs(node, Type::InternalizedString()); | |
| break; | |
| case IrOpcode::kCheckMaps: | |
| CheckValueInputIs(node, 0, Type::Any()); | |
| CheckNotTyped(node); | |
| break; | |
| case IrOpcode::kCompareMaps: | |
| CheckValueInputIs(node, 0, Type::Any()); | |
| CheckTypeIs(node, Type::Boolean()); | |
| break; | |
| case IrOpcode::kCheckNumber: | |
| CheckValueInputIs(node, 0, Type::Any()); | |
| CheckTypeIs(node, Type::Number()); | |
| break; | |
| case IrOpcode::kCheckReceiver: | |
| CheckValueInputIs(node, 0, Type::Any()); | |
| CheckTypeIs(node, Type::Receiver()); | |
| break; | |
| case IrOpcode::kCheckSmi: | |
| CheckValueInputIs(node, 0, Type::Any()); | |
| break; | |
| case IrOpcode::kCheckString: | |
| CheckValueInputIs(node, 0, Type::Any()); | |
| CheckTypeIs(node, Type::String()); | |
| break; | |
| case IrOpcode::kCheckSymbol: | |
| CheckValueInputIs(node, 0, Type::Any()); | |
| CheckTypeIs(node, Type::Symbol()); | |
| break; | |
| case IrOpcode::kConvertReceiver: | |
| // (Any, Any) -> Receiver | |
| CheckValueInputIs(node, 0, Type::Any()); | |
| CheckValueInputIs(node, 1, Type::Any()); | |
| CheckTypeIs(node, Type::Receiver()); | |
| break; | |
| case IrOpcode::kCheckedInt32Add: | |
| case IrOpcode::kCheckedInt32Sub: | |
| case IrOpcode::kCheckedInt32Div: | |
| case IrOpcode::kCheckedInt32Mod: | |
| case IrOpcode::kCheckedUint32Div: | |
| case IrOpcode::kCheckedUint32Mod: | |
| case IrOpcode::kCheckedInt32Mul: | |
| case IrOpcode::kCheckedInt32ToTaggedSigned: | |
| case IrOpcode::kCheckedUint32ToInt32: | |
| case IrOpcode::kCheckedUint32ToTaggedSigned: | |
| case IrOpcode::kCheckedFloat64ToInt32: | |
| case IrOpcode::kCheckedTaggedSignedToInt32: | |
| case IrOpcode::kCheckedTaggedToInt32: | |
| case IrOpcode::kCheckedTaggedToFloat64: | |
| case IrOpcode::kCheckedTaggedToTaggedSigned: | |
| case IrOpcode::kCheckedTaggedToTaggedPointer: | |
| case IrOpcode::kCheckedTruncateTaggedToWord32: | |
| break; | |
| case IrOpcode::kCheckFloat64Hole: | |
| CheckValueInputIs(node, 0, Type::NumberOrHole()); | |
| CheckTypeIs(node, Type::NumberOrUndefined()); | |
| break; | |
| case IrOpcode::kCheckNotTaggedHole: | |
| CheckValueInputIs(node, 0, Type::Any()); | |
| CheckTypeIs(node, Type::NonInternal()); | |
| break; | |
| case IrOpcode::kConvertTaggedHoleToUndefined: | |
| CheckValueInputIs(node, 0, Type::Any()); | |
| CheckTypeIs(node, Type::NonInternal()); | |
| break; | |
| case IrOpcode::kCheckEqualsInternalizedString: | |
| CheckValueInputIs(node, 0, Type::InternalizedString()); | |
| CheckValueInputIs(node, 1, Type::Any()); | |
| CheckNotTyped(node); | |
| break; | |
| case IrOpcode::kCheckEqualsSymbol: | |
| CheckValueInputIs(node, 0, Type::Symbol()); | |
| CheckValueInputIs(node, 1, Type::Any()); | |
| CheckNotTyped(node); | |
| break; | |
| case IrOpcode::kLoadFieldByIndex: | |
| CheckValueInputIs(node, 0, Type::Any()); | |
| CheckValueInputIs(node, 1, Type::SignedSmall()); | |
| CheckTypeIs(node, Type::NonInternal()); | |
| break; | |
| case IrOpcode::kLoadField: | |
| // Object -> fieldtype | |
| // TODO(rossberg): activate once machine ops are typed. | |
| // CheckValueInputIs(node, 0, Type::Object()); | |
| // CheckTypeIs(node, FieldAccessOf(node->op()).type)); | |
| break; | |
| case IrOpcode::kLoadElement: | |
| // Object -> elementtype | |
| // TODO(rossberg): activate once machine ops are typed. | |
| // CheckValueInputIs(node, 0, Type::Object()); | |
| // CheckTypeIs(node, ElementAccessOf(node->op()).type)); | |
| break; | |
| case IrOpcode::kLoadTypedElement: | |
| break; | |
| case IrOpcode::kLoadDataViewElement: | |
| break; | |
| case IrOpcode::kStoreField: | |
| // (Object, fieldtype) -> _|_ | |
| // TODO(rossberg): activate once machine ops are typed. | |
| // CheckValueInputIs(node, 0, Type::Object()); | |
| // CheckValueInputIs(node, 1, FieldAccessOf(node->op()).type)); | |
| CheckNotTyped(node); | |
| break; | |
| case IrOpcode::kStoreElement: | |
| // (Object, elementtype) -> _|_ | |
| // TODO(rossberg): activate once machine ops are typed. | |
| // CheckValueInputIs(node, 0, Type::Object()); | |
| // CheckValueInputIs(node, 1, ElementAccessOf(node->op()).type)); | |
| CheckNotTyped(node); | |
| break; | |
| case IrOpcode::kTransitionAndStoreElement: | |
| CheckNotTyped(node); | |
| break; | |
| case IrOpcode::kTransitionAndStoreNumberElement: | |
| CheckNotTyped(node); | |
| break; | |
| case IrOpcode::kTransitionAndStoreNonNumberElement: | |
| CheckNotTyped(node); | |
| break; | |
| case IrOpcode::kStoreSignedSmallElement: | |
| CheckNotTyped(node); | |
| break; | |
| case IrOpcode::kStoreTypedElement: | |
| CheckNotTyped(node); | |
| break; | |
| case IrOpcode::kStoreDataViewElement: | |
| CheckNotTyped(node); | |
| break; | |
| case IrOpcode::kNumberSilenceNaN: | |
| CheckValueInputIs(node, 0, Type::Number()); | |
| CheckTypeIs(node, Type::Number()); | |
| break; | |
| case IrOpcode::kMapGuard: | |
| CheckNotTyped(node); | |
| break; | |
| case IrOpcode::kTypeGuard: | |
| CheckTypeIs(node, TypeGuardTypeOf(node->op())); | |
| break; | |
| case IrOpcode::kDateNow: | |
| CHECK_EQ(0, value_count); | |
| CheckTypeIs(node, Type::Number()); | |
| break; | |
| // Machine operators | |
| // ----------------------- | |
| case IrOpcode::kLoad: | |
| case IrOpcode::kPoisonedLoad: | |
| case IrOpcode::kProtectedLoad: | |
| case IrOpcode::kProtectedStore: | |
| case IrOpcode::kStore: | |
| case IrOpcode::kStackSlot: | |
| case IrOpcode::kWord32And: | |
| case IrOpcode::kWord32Or: | |
| case IrOpcode::kWord32Xor: | |
| case IrOpcode::kWord32Shl: | |
| case IrOpcode::kWord32Shr: | |
| case IrOpcode::kWord32Sar: | |
| case IrOpcode::kWord32Ror: | |
| case IrOpcode::kWord32Equal: | |
| case IrOpcode::kWord32Clz: | |
| case IrOpcode::kWord32Ctz: | |
| case IrOpcode::kWord32ReverseBits: | |
| case IrOpcode::kWord32ReverseBytes: | |
| case IrOpcode::kInt32AbsWithOverflow: | |
| case IrOpcode::kWord32Popcnt: | |
| case IrOpcode::kWord64And: | |
| case IrOpcode::kWord64Or: | |
| case IrOpcode::kWord64Xor: | |
| case IrOpcode::kWord64Shl: | |
| case IrOpcode::kWord64Shr: | |
| case IrOpcode::kWord64Sar: | |
| case IrOpcode::kWord64Ror: | |
| case IrOpcode::kWord64Clz: | |
| case IrOpcode::kWord64Popcnt: | |
| case IrOpcode::kWord64Ctz: | |
| case IrOpcode::kWord64ReverseBits: | |
| case IrOpcode::kWord64ReverseBytes: | |
| case IrOpcode::kInt64AbsWithOverflow: | |
| case IrOpcode::kWord64Equal: | |
| case IrOpcode::kInt32Add: | |
| case IrOpcode::kInt32AddWithOverflow: | |
| case IrOpcode::kInt32Sub: | |
| case IrOpcode::kInt32SubWithOverflow: | |
| case IrOpcode::kInt32Mul: | |
| case IrOpcode::kInt32MulWithOverflow: | |
| case IrOpcode::kInt32MulHigh: | |
| case IrOpcode::kInt32Div: | |
| case IrOpcode::kInt32Mod: | |
| case IrOpcode::kInt32LessThan: | |
| case IrOpcode::kInt32LessThanOrEqual: | |
| case IrOpcode::kUint32Div: | |
| case IrOpcode::kUint32Mod: | |
| case IrOpcode::kUint32MulHigh: | |
| case IrOpcode::kUint32LessThan: | |
| case IrOpcode::kUint32LessThanOrEqual: | |
| case IrOpcode::kInt64Add: | |
| case IrOpcode::kInt64AddWithOverflow: | |
| case IrOpcode::kInt64Sub: | |
| case IrOpcode::kInt64SubWithOverflow: | |
| case IrOpcode::kInt64Mul: | |
| case IrOpcode::kInt64Div: | |
| case IrOpcode::kInt64Mod: | |
| case IrOpcode::kInt64LessThan: | |
| case IrOpcode::kInt64LessThanOrEqual: | |
| case IrOpcode::kUint64Div: | |
| case IrOpcode::kUint64Mod: | |
| case IrOpcode::kUint64LessThan: | |
| case IrOpcode::kUint64LessThanOrEqual: | |
| case IrOpcode::kFloat32Add: | |
| case IrOpcode::kFloat32Sub: | |
| case IrOpcode::kFloat32Neg: | |
| case IrOpcode::kFloat32Mul: | |
| case IrOpcode::kFloat32Div: | |
| case IrOpcode::kFloat32Abs: | |
| case IrOpcode::kFloat32Sqrt: | |
| case IrOpcode::kFloat32Equal: | |
| case IrOpcode::kFloat32LessThan: | |
| case IrOpcode::kFloat32LessThanOrEqual: | |
| case IrOpcode::kFloat32Max: | |
| case IrOpcode::kFloat32Min: | |
| case IrOpcode::kFloat64Add: | |
| case IrOpcode::kFloat64Sub: | |
| case IrOpcode::kFloat64Neg: | |
| case IrOpcode::kFloat64Mul: | |
| case IrOpcode::kFloat64Div: | |
| case IrOpcode::kFloat64Mod: | |
| case IrOpcode::kFloat64Max: | |
| case IrOpcode::kFloat64Min: | |
| case IrOpcode::kFloat64Abs: | |
| case IrOpcode::kFloat64Acos: | |
| case IrOpcode::kFloat64Acosh: | |
| case IrOpcode::kFloat64Asin: | |
| case IrOpcode::kFloat64Asinh: | |
| case IrOpcode::kFloat64Atan: | |
| case IrOpcode::kFloat64Atan2: | |
| case IrOpcode::kFloat64Atanh: | |
| case IrOpcode::kFloat64Cbrt: | |
| case IrOpcode::kFloat64Cos: | |
| case IrOpcode::kFloat64Cosh: | |
| case IrOpcode::kFloat64Exp: | |
| case IrOpcode::kFloat64Expm1: | |
| case IrOpcode::kFloat64Log: | |
| case IrOpcode::kFloat64Log1p: | |
| case IrOpcode::kFloat64Log10: | |
| case IrOpcode::kFloat64Log2: | |
| case IrOpcode::kFloat64Pow: | |
| case IrOpcode::kFloat64Sin: | |
| case IrOpcode::kFloat64Sinh: | |
| case IrOpcode::kFloat64Sqrt: | |
| case IrOpcode::kFloat64Tan: | |
| case IrOpcode::kFloat64Tanh: | |
| case IrOpcode::kFloat32RoundDown: | |
| case IrOpcode::kFloat64RoundDown: | |
| case IrOpcode::kFloat32RoundUp: | |
| case IrOpcode::kFloat64RoundUp: | |
| case IrOpcode::kFloat32RoundTruncate: | |
| case IrOpcode::kFloat64RoundTruncate: | |
| case IrOpcode::kFloat64RoundTiesAway: | |
| case IrOpcode::kFloat32RoundTiesEven: | |
| case IrOpcode::kFloat64RoundTiesEven: | |
| case IrOpcode::kFloat64Equal: | |
| case IrOpcode::kFloat64LessThan: | |
| case IrOpcode::kFloat64LessThanOrEqual: | |
| case IrOpcode::kTruncateInt64ToInt32: | |
| case IrOpcode::kRoundFloat64ToInt32: | |
| case IrOpcode::kRoundInt32ToFloat32: | |
| case IrOpcode::kRoundInt64ToFloat32: | |
| case IrOpcode::kRoundInt64ToFloat64: | |
| case IrOpcode::kRoundUint32ToFloat32: | |
| case IrOpcode::kRoundUint64ToFloat64: | |
| case IrOpcode::kRoundUint64ToFloat32: | |
| case IrOpcode::kTruncateFloat64ToFloat32: | |
| case IrOpcode::kTruncateFloat64ToWord32: | |
| case IrOpcode::kBitcastFloat32ToInt32: | |
| case IrOpcode::kBitcastFloat64ToInt64: | |
| case IrOpcode::kBitcastInt32ToFloat32: | |
| case IrOpcode::kBitcastInt64ToFloat64: | |
| case IrOpcode::kBitcastTaggedToWord: | |
| case IrOpcode::kBitcastWordToTagged: | |
| case IrOpcode::kBitcastWordToTaggedSigned: | |
| case IrOpcode::kChangeInt32ToInt64: | |
| case IrOpcode::kChangeUint32ToUint64: | |
| case IrOpcode::kChangeInt32ToFloat64: | |
| case IrOpcode::kChangeUint32ToFloat64: | |
| case IrOpcode::kChangeFloat32ToFloat64: | |
| case IrOpcode::kChangeFloat64ToInt32: | |
| case IrOpcode::kChangeFloat64ToUint32: | |
| case IrOpcode::kChangeFloat64ToUint64: | |
| case IrOpcode::kFloat64SilenceNaN: | |
| case IrOpcode::kTruncateFloat64ToUint32: | |
| case IrOpcode::kTruncateFloat32ToInt32: | |
| case IrOpcode::kTruncateFloat32ToUint32: | |
| case IrOpcode::kTryTruncateFloat32ToInt64: | |
| case IrOpcode::kTryTruncateFloat64ToInt64: | |
| case IrOpcode::kTryTruncateFloat32ToUint64: | |
| case IrOpcode::kTryTruncateFloat64ToUint64: | |
| case IrOpcode::kFloat64ExtractLowWord32: | |
| case IrOpcode::kFloat64ExtractHighWord32: | |
| case IrOpcode::kFloat64InsertLowWord32: | |
| case IrOpcode::kFloat64InsertHighWord32: | |
| case IrOpcode::kInt32PairAdd: | |
| case IrOpcode::kInt32PairSub: | |
| case IrOpcode::kInt32PairMul: | |
| case IrOpcode::kWord32PairShl: | |
| case IrOpcode::kWord32PairShr: | |
| case IrOpcode::kWord32PairSar: | |
| case IrOpcode::kTaggedPoisonOnSpeculation: | |
| case IrOpcode::kWord32PoisonOnSpeculation: | |
| case IrOpcode::kWord64PoisonOnSpeculation: | |
| case IrOpcode::kLoadStackPointer: | |
| case IrOpcode::kLoadFramePointer: | |
| case IrOpcode::kLoadParentFramePointer: | |
| case IrOpcode::kUnalignedLoad: | |
| case IrOpcode::kUnalignedStore: | |
| case IrOpcode::kWord32AtomicLoad: | |
| case IrOpcode::kWord32AtomicStore: | |
| case IrOpcode::kWord32AtomicExchange: | |
| case IrOpcode::kWord32AtomicCompareExchange: | |
| case IrOpcode::kWord32AtomicAdd: | |
| case IrOpcode::kWord32AtomicSub: | |
| case IrOpcode::kWord32AtomicAnd: | |
| case IrOpcode::kWord32AtomicOr: | |
| case IrOpcode::kWord32AtomicXor: | |
| case IrOpcode::kWord64AtomicLoad: | |
| case IrOpcode::kWord64AtomicStore: | |
| case IrOpcode::kWord64AtomicAdd: | |
| case IrOpcode::kWord64AtomicSub: | |
| case IrOpcode::kWord64AtomicAnd: | |
| case IrOpcode::kWord64AtomicOr: | |
| case IrOpcode::kWord64AtomicXor: | |
| case IrOpcode::kWord64AtomicExchange: | |
| case IrOpcode::kWord64AtomicCompareExchange: | |
| 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: | |
| case IrOpcode::kWord64AtomicNarrowAdd: | |
| case IrOpcode::kWord64AtomicNarrowSub: | |
| case IrOpcode::kWord64AtomicNarrowAnd: | |
| case IrOpcode::kWord64AtomicNarrowOr: | |
| case IrOpcode::kWord64AtomicNarrowXor: | |
| case IrOpcode::kWord64AtomicNarrowExchange: | |
| case IrOpcode::kWord64AtomicNarrowCompareExchange: | |
| case IrOpcode::kSpeculationFence: | |
| case IrOpcode::kSignExtendWord8ToInt32: | |
| case IrOpcode::kSignExtendWord16ToInt32: | |
| case IrOpcode::kSignExtendWord8ToInt64: | |
| case IrOpcode::kSignExtendWord16ToInt64: | |
| case IrOpcode::kSignExtendWord32ToInt64: | |
| #define SIMD_MACHINE_OP_CASE(Name) case IrOpcode::k##Name: | |
| MACHINE_SIMD_OP_LIST(SIMD_MACHINE_OP_CASE) | |
| #undef SIMD_MACHINE_OP_CASE | |
| // TODO(rossberg): Check. | |
| break; | |
| } | |
| } // NOLINT(readability/fn_size) | |
| void Verifier::Run(Graph* graph, Typing typing, CheckInputs check_inputs, | |
| CodeType code_type) { | |
| CHECK_NOT_NULL(graph->start()); | |
| CHECK_NOT_NULL(graph->end()); | |
| Zone zone(graph->zone()->allocator(), ZONE_NAME); | |
| Visitor visitor(&zone, typing, check_inputs, code_type); | |
| AllNodes all(&zone, graph); | |
| for (Node* node : all.reachable) visitor.Check(node, all); | |
| // Check the uniqueness of projections. | |
| for (Node* proj : all.reachable) { | |
| if (proj->opcode() != IrOpcode::kProjection) continue; | |
| Node* node = proj->InputAt(0); | |
| for (Node* other : node->uses()) { | |
| if (all.IsLive(other) && other != proj && | |
| other->opcode() == IrOpcode::kProjection && | |
| other->InputAt(0) == node && | |
| ProjectionIndexOf(other->op()) == ProjectionIndexOf(proj->op())) { | |
| FATAL("Node #%d:%s has duplicate projections #%d and #%d", node->id(), | |
| node->op()->mnemonic(), proj->id(), other->id()); | |
| } | |
| } | |
| } | |
| } | |
| // ----------------------------------------------------------------------------- | |
| static bool HasDominatingDef(Schedule* schedule, Node* node, | |
| BasicBlock* container, BasicBlock* use_block, | |
| int use_pos) { | |
| BasicBlock* block = use_block; | |
| while (true) { | |
| while (use_pos >= 0) { | |
| if (block->NodeAt(use_pos) == node) return true; | |
| use_pos--; | |
| } | |
| block = block->dominator(); | |
| if (block == nullptr) break; | |
| use_pos = static_cast<int>(block->NodeCount()) - 1; | |
| if (node == block->control_input()) return true; | |
| } | |
| return false; | |
| } | |
| static bool Dominates(Schedule* schedule, Node* dominator, Node* dominatee) { | |
| BasicBlock* dom = schedule->block(dominator); | |
| BasicBlock* sub = schedule->block(dominatee); | |
| while (sub != nullptr) { | |
| if (sub == dom) { | |
| return true; | |
| } | |
| sub = sub->dominator(); | |
| } | |
| return false; | |
| } | |
| static void CheckInputsDominate(Schedule* schedule, BasicBlock* block, | |
| Node* node, int use_pos) { | |
| for (int j = node->op()->ValueInputCount() - 1; j >= 0; j--) { | |
| BasicBlock* use_block = block; | |
| if (node->opcode() == IrOpcode::kPhi) { | |
| use_block = use_block->PredecessorAt(j); | |
| use_pos = static_cast<int>(use_block->NodeCount()) - 1; | |
| } | |
| Node* input = node->InputAt(j); | |
| if (!HasDominatingDef(schedule, node->InputAt(j), block, use_block, | |
| use_pos)) { | |
| FATAL("Node #%d:%s in B%d is not dominated by input@%d #%d:%s", | |
| node->id(), node->op()->mnemonic(), block->rpo_number(), j, | |
| input->id(), input->op()->mnemonic()); | |
| } | |
| } | |
| // Ensure that nodes are dominated by their control inputs; | |
| // kEnd is an exception, as unreachable blocks resulting from kMerge | |
| // are not in the RPO. | |
| if (node->op()->ControlInputCount() == 1 && | |
| node->opcode() != IrOpcode::kEnd) { | |
| Node* ctl = NodeProperties::GetControlInput(node); | |
| if (!Dominates(schedule, ctl, node)) { | |
| FATAL("Node #%d:%s in B%d is not dominated by control input #%d:%s", | |
| node->id(), node->op()->mnemonic(), block->rpo_number(), ctl->id(), | |
| ctl->op()->mnemonic()); | |
| } | |
| } | |
| } | |
| void ScheduleVerifier::Run(Schedule* schedule) { | |
| const size_t count = schedule->BasicBlockCount(); | |
| Zone tmp_zone(schedule->zone()->allocator(), ZONE_NAME); | |
| Zone* zone = &tmp_zone; | |
| BasicBlock* start = schedule->start(); | |
| BasicBlockVector* rpo_order = schedule->rpo_order(); | |
| // Verify the RPO order contains only blocks from this schedule. | |
| CHECK_GE(count, rpo_order->size()); | |
| for (BasicBlockVector::iterator b = rpo_order->begin(); b != rpo_order->end(); | |
| ++b) { | |
| CHECK_EQ((*b), schedule->GetBlockById((*b)->id())); | |
| // All predecessors and successors should be in rpo and in this schedule. | |
| for (BasicBlock const* predecessor : (*b)->predecessors()) { | |
| CHECK_GE(predecessor->rpo_number(), 0); | |
| CHECK_EQ(predecessor, schedule->GetBlockById(predecessor->id())); | |
| } | |
| for (BasicBlock const* successor : (*b)->successors()) { | |
| CHECK_GE(successor->rpo_number(), 0); | |
| CHECK_EQ(successor, schedule->GetBlockById(successor->id())); | |
| } | |
| } | |
| // Verify RPO numbers of blocks. | |
| CHECK_EQ(start, rpo_order->at(0)); // Start should be first. | |
| for (size_t b = 0; b < rpo_order->size(); b++) { | |
| BasicBlock* block = rpo_order->at(b); | |
| CHECK_EQ(static_cast<int>(b), block->rpo_number()); | |
| BasicBlock* dom = block->dominator(); | |
| if (b == 0) { | |
| // All blocks except start should have a dominator. | |
| CHECK_NULL(dom); | |
| } else { | |
| // Check that the immediate dominator appears somewhere before the block. | |
| CHECK_NOT_NULL(dom); | |
| CHECK_LT(dom->rpo_number(), block->rpo_number()); | |
| } | |
| } | |
| // Verify that all blocks reachable from start are in the RPO. | |
| BoolVector marked(static_cast<int>(count), false, zone); | |
| { | |
| ZoneQueue<BasicBlock*> queue(zone); | |
| queue.push(start); | |
| marked[start->id().ToSize()] = true; | |
| while (!queue.empty()) { | |
| BasicBlock* block = queue.front(); | |
| queue.pop(); | |
| for (size_t s = 0; s < block->SuccessorCount(); s++) { | |
| BasicBlock* succ = block->SuccessorAt(s); | |
| if (!marked[succ->id().ToSize()]) { | |
| marked[succ->id().ToSize()] = true; | |
| queue.push(succ); | |
| } | |
| } | |
| } | |
| } | |
| // Verify marked blocks are in the RPO. | |
| for (size_t i = 0; i < count; i++) { | |
| BasicBlock* block = schedule->GetBlockById(BasicBlock::Id::FromSize(i)); | |
| if (marked[i]) { | |
| CHECK_GE(block->rpo_number(), 0); | |
| CHECK_EQ(block, rpo_order->at(block->rpo_number())); | |
| } | |
| } | |
| // Verify RPO blocks are marked. | |
| for (size_t b = 0; b < rpo_order->size(); b++) { | |
| CHECK(marked[rpo_order->at(b)->id().ToSize()]); | |
| } | |
| { | |
| // Verify the dominance relation. | |
| ZoneVector<BitVector*> dominators(zone); | |
| dominators.resize(count, nullptr); | |
| // Compute a set of all the nodes that dominate a given node by using | |
| // a forward fixpoint. O(n^2). | |
| ZoneQueue<BasicBlock*> queue(zone); | |
| queue.push(start); | |
| dominators[start->id().ToSize()] = | |
| new (zone) BitVector(static_cast<int>(count), zone); | |
| while (!queue.empty()) { | |
| BasicBlock* block = queue.front(); | |
| queue.pop(); | |
| BitVector* block_doms = dominators[block->id().ToSize()]; | |
| BasicBlock* idom = block->dominator(); | |
| if (idom != nullptr && !block_doms->Contains(idom->id().ToInt())) { | |
| FATAL("Block B%d is not dominated by B%d", block->rpo_number(), | |
| idom->rpo_number()); | |
| } | |
| for (size_t s = 0; s < block->SuccessorCount(); s++) { | |
| BasicBlock* succ = block->SuccessorAt(s); | |
| BitVector* succ_doms = dominators[succ->id().ToSize()]; | |
| if (succ_doms == nullptr) { | |
| // First time visiting the node. S.doms = B U B.doms | |
| succ_doms = new (zone) BitVector(static_cast<int>(count), zone); | |
| succ_doms->CopyFrom(*block_doms); | |
| succ_doms->Add(block->id().ToInt()); | |
| dominators[succ->id().ToSize()] = succ_doms; | |
| queue.push(succ); | |
| } else { | |
| // Nth time visiting the successor. S.doms = S.doms ^ (B U B.doms) | |
| bool had = succ_doms->Contains(block->id().ToInt()); | |
| if (had) succ_doms->Remove(block->id().ToInt()); | |
| if (succ_doms->IntersectIsChanged(*block_doms)) queue.push(succ); | |
| if (had) succ_doms->Add(block->id().ToInt()); | |
| } | |
| } | |
| } | |
| // Verify the immediateness of dominators. | |
| for (BasicBlockVector::iterator b = rpo_order->begin(); | |
| b != rpo_order->end(); ++b) { | |
| BasicBlock* block = *b; | |
| BasicBlock* idom = block->dominator(); | |
| if (idom == nullptr) continue; | |
| BitVector* block_doms = dominators[block->id().ToSize()]; | |
| for (BitVector::Iterator it(block_doms); !it.Done(); it.Advance()) { | |
| BasicBlock* dom = | |
| schedule->GetBlockById(BasicBlock::Id::FromInt(it.Current())); | |
| if (dom != idom && | |
| !dominators[idom->id().ToSize()]->Contains(dom->id().ToInt())) { | |
| FATAL("Block B%d is not immediately dominated by B%d", | |
| block->rpo_number(), idom->rpo_number()); | |
| } | |
| } | |
| } | |
| } | |
| // Verify phis are placed in the block of their control input. | |
| for (BasicBlockVector::iterator b = rpo_order->begin(); b != rpo_order->end(); | |
| ++b) { | |
| for (BasicBlock::const_iterator i = (*b)->begin(); i != (*b)->end(); ++i) { | |
| Node* phi = *i; | |
| if (phi->opcode() != IrOpcode::kPhi) continue; | |
| // TODO(titzer): Nasty special case. Phis from RawMachineAssembler | |
| // schedules don't have control inputs. | |
| if (phi->InputCount() > phi->op()->ValueInputCount()) { | |
| Node* control = NodeProperties::GetControlInput(phi); | |
| CHECK(control->opcode() == IrOpcode::kMerge || | |
| control->opcode() == IrOpcode::kLoop); | |
| CHECK_EQ((*b), schedule->block(control)); | |
| } | |
| } | |
| } | |
| // Verify that all uses are dominated by their definitions. | |
| for (BasicBlockVector::iterator b = rpo_order->begin(); b != rpo_order->end(); | |
| ++b) { | |
| BasicBlock* block = *b; | |
| // Check inputs to control for this block. | |
| Node* control = block->control_input(); | |
| if (control != nullptr) { | |
| CHECK_EQ(block, schedule->block(control)); | |
| CheckInputsDominate(schedule, block, control, | |
| static_cast<int>(block->NodeCount()) - 1); | |
| } | |
| // Check inputs for all nodes in the block. | |
| for (size_t i = 0; i < block->NodeCount(); i++) { | |
| Node* node = block->NodeAt(i); | |
| CheckInputsDominate(schedule, block, node, static_cast<int>(i) - 1); | |
| } | |
| } | |
| } | |
| #ifdef DEBUG | |
| // static | |
| void Verifier::VerifyNode(Node* node) { | |
| DCHECK_EQ(OperatorProperties::GetTotalInputCount(node->op()), | |
| node->InputCount()); | |
| // If this node has no effect or no control outputs, | |
| // we check that none of its uses are effect or control inputs. | |
| bool check_no_control = node->op()->ControlOutputCount() == 0; | |
| bool check_no_effect = node->op()->EffectOutputCount() == 0; | |
| bool check_no_frame_state = node->opcode() != IrOpcode::kFrameState; | |
| int effect_edges = 0; | |
| if (check_no_effect || check_no_control) { | |
| for (Edge edge : node->use_edges()) { | |
| Node* const user = edge.from(); | |
| DCHECK(!user->IsDead()); | |
| if (NodeProperties::IsControlEdge(edge)) { | |
| DCHECK(!check_no_control); | |
| } else if (NodeProperties::IsEffectEdge(edge)) { | |
| DCHECK(!check_no_effect); | |
| effect_edges++; | |
| } else if (NodeProperties::IsFrameStateEdge(edge)) { | |
| DCHECK(!check_no_frame_state); | |
| } | |
| } | |
| } | |
| // Frame state input should be a frame state (or sentinel). | |
| if (OperatorProperties::GetFrameStateInputCount(node->op()) > 0) { | |
| Node* input = NodeProperties::GetFrameStateInput(node); | |
| DCHECK(input->opcode() == IrOpcode::kFrameState || | |
| input->opcode() == IrOpcode::kStart || | |
| input->opcode() == IrOpcode::kDead || | |
| input->opcode() == IrOpcode::kDeadValue); | |
| } | |
| // Effect inputs should be effect-producing nodes (or sentinels). | |
| for (int i = 0; i < node->op()->EffectInputCount(); i++) { | |
| Node* input = NodeProperties::GetEffectInput(node, i); | |
| DCHECK(input->op()->EffectOutputCount() > 0 || | |
| input->opcode() == IrOpcode::kDead); | |
| } | |
| // Control inputs should be control-producing nodes (or sentinels). | |
| for (int i = 0; i < node->op()->ControlInputCount(); i++) { | |
| Node* input = NodeProperties::GetControlInput(node, i); | |
| DCHECK(input->op()->ControlOutputCount() > 0 || | |
| input->opcode() == IrOpcode::kDead); | |
| } | |
| } | |
| void Verifier::VerifyEdgeInputReplacement(const Edge& edge, | |
| const Node* replacement) { | |
| // Check that the user does not misuse the replacement. | |
| DCHECK(!NodeProperties::IsControlEdge(edge) || | |
| replacement->op()->ControlOutputCount() > 0); | |
| DCHECK(!NodeProperties::IsEffectEdge(edge) || | |
| replacement->op()->EffectOutputCount() > 0); | |
| DCHECK(!NodeProperties::IsFrameStateEdge(edge) || | |
| replacement->opcode() == IrOpcode::kFrameState || | |
| replacement->opcode() == IrOpcode::kDead || | |
| replacement->opcode() == IrOpcode::kDeadValue); | |
| } | |
| #endif // DEBUG | |
| } // namespace compiler | |
| } // namespace internal | |
| } // namespace v8 |