| // Copyright 2015 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/effect-control-linearizer.h" |
| |
| #include "src/code-factory.h" |
| #include "src/compiler/access-builder.h" |
| #include "src/compiler/compiler-source-position-table.h" |
| #include "src/compiler/js-graph.h" |
| #include "src/compiler/linkage.h" |
| #include "src/compiler/node-matchers.h" |
| #include "src/compiler/node-properties.h" |
| #include "src/compiler/node.h" |
| #include "src/compiler/schedule.h" |
| #include "src/factory-inl.h" |
| |
| namespace v8 { |
| namespace internal { |
| namespace compiler { |
| |
| EffectControlLinearizer::EffectControlLinearizer( |
| JSGraph* js_graph, Schedule* schedule, Zone* temp_zone, |
| SourcePositionTable* source_positions, |
| MaskArrayIndexEnable mask_array_index) |
| : js_graph_(js_graph), |
| schedule_(schedule), |
| temp_zone_(temp_zone), |
| mask_array_index_(mask_array_index), |
| source_positions_(source_positions), |
| graph_assembler_(js_graph, nullptr, nullptr, temp_zone), |
| frame_state_zapper_(nullptr) {} |
| |
| Graph* EffectControlLinearizer::graph() const { return js_graph_->graph(); } |
| CommonOperatorBuilder* EffectControlLinearizer::common() const { |
| return js_graph_->common(); |
| } |
| SimplifiedOperatorBuilder* EffectControlLinearizer::simplified() const { |
| return js_graph_->simplified(); |
| } |
| MachineOperatorBuilder* EffectControlLinearizer::machine() const { |
| return js_graph_->machine(); |
| } |
| |
| namespace { |
| |
| struct BlockEffectControlData { |
| Node* current_effect = nullptr; // New effect. |
| Node* current_control = nullptr; // New control. |
| Node* current_frame_state = nullptr; // New frame state. |
| }; |
| |
| class BlockEffectControlMap { |
| public: |
| explicit BlockEffectControlMap(Zone* temp_zone) : map_(temp_zone) {} |
| |
| BlockEffectControlData& For(BasicBlock* from, BasicBlock* to) { |
| return map_[std::make_pair(from->rpo_number(), to->rpo_number())]; |
| } |
| |
| const BlockEffectControlData& For(BasicBlock* from, BasicBlock* to) const { |
| return map_.at(std::make_pair(from->rpo_number(), to->rpo_number())); |
| } |
| |
| private: |
| typedef std::pair<int32_t, int32_t> Key; |
| typedef ZoneMap<Key, BlockEffectControlData> Map; |
| |
| Map map_; |
| }; |
| |
| // Effect phis that need to be updated after the first pass. |
| struct PendingEffectPhi { |
| Node* effect_phi; |
| BasicBlock* block; |
| |
| PendingEffectPhi(Node* effect_phi, BasicBlock* block) |
| : effect_phi(effect_phi), block(block) {} |
| }; |
| |
| void ConnectUnreachableToEnd(Node* effect, Node* control, JSGraph* jsgraph) { |
| Graph* graph = jsgraph->graph(); |
| CommonOperatorBuilder* common = jsgraph->common(); |
| if (effect->opcode() == IrOpcode::kDead) return; |
| if (effect->opcode() != IrOpcode::kUnreachable) { |
| effect = graph->NewNode(common->Unreachable(), effect, control); |
| } |
| Node* throw_node = graph->NewNode(common->Throw(), effect, control); |
| NodeProperties::MergeControlToEnd(graph, common, throw_node); |
| } |
| |
| void UpdateEffectPhi(Node* node, BasicBlock* block, |
| BlockEffectControlMap* block_effects, JSGraph* jsgraph) { |
| // Update all inputs to an effect phi with the effects from the given |
| // block->effect map. |
| DCHECK_EQ(IrOpcode::kEffectPhi, node->opcode()); |
| DCHECK_EQ(static_cast<size_t>(node->op()->EffectInputCount()), |
| block->PredecessorCount()); |
| for (int i = 0; i < node->op()->EffectInputCount(); i++) { |
| Node* input = node->InputAt(i); |
| BasicBlock* predecessor = block->PredecessorAt(static_cast<size_t>(i)); |
| const BlockEffectControlData& block_effect = |
| block_effects->For(predecessor, block); |
| Node* effect = block_effect.current_effect; |
| if (input != effect) { |
| node->ReplaceInput(i, effect); |
| } |
| } |
| } |
| |
| void UpdateBlockControl(BasicBlock* block, |
| BlockEffectControlMap* block_effects) { |
| Node* control = block->NodeAt(0); |
| DCHECK(NodeProperties::IsControl(control)); |
| |
| // Do not rewire the end node. |
| if (control->opcode() == IrOpcode::kEnd) return; |
| |
| // Update all inputs to the given control node with the correct control. |
| DCHECK(control->opcode() == IrOpcode::kMerge || |
| static_cast<size_t>(control->op()->ControlInputCount()) == |
| block->PredecessorCount()); |
| if (static_cast<size_t>(control->op()->ControlInputCount()) != |
| block->PredecessorCount()) { |
| return; // We already re-wired the control inputs of this node. |
| } |
| for (int i = 0; i < control->op()->ControlInputCount(); i++) { |
| Node* input = NodeProperties::GetControlInput(control, i); |
| BasicBlock* predecessor = block->PredecessorAt(static_cast<size_t>(i)); |
| const BlockEffectControlData& block_effect = |
| block_effects->For(predecessor, block); |
| if (input != block_effect.current_control) { |
| NodeProperties::ReplaceControlInput(control, block_effect.current_control, |
| i); |
| } |
| } |
| } |
| |
| bool HasIncomingBackEdges(BasicBlock* block) { |
| for (BasicBlock* pred : block->predecessors()) { |
| if (pred->rpo_number() >= block->rpo_number()) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| void RemoveRenameNode(Node* node) { |
| DCHECK(IrOpcode::kFinishRegion == node->opcode() || |
| IrOpcode::kBeginRegion == node->opcode() || |
| IrOpcode::kTypeGuard == node->opcode()); |
| // Update the value/context uses to the value input of the finish node and |
| // the effect uses to the effect input. |
| for (Edge edge : node->use_edges()) { |
| DCHECK(!edge.from()->IsDead()); |
| if (NodeProperties::IsEffectEdge(edge)) { |
| edge.UpdateTo(NodeProperties::GetEffectInput(node)); |
| } else { |
| DCHECK(!NodeProperties::IsControlEdge(edge)); |
| DCHECK(!NodeProperties::IsFrameStateEdge(edge)); |
| edge.UpdateTo(node->InputAt(0)); |
| } |
| } |
| node->Kill(); |
| } |
| |
| void TryCloneBranch(Node* node, BasicBlock* block, Zone* temp_zone, |
| Graph* graph, CommonOperatorBuilder* common, |
| BlockEffectControlMap* block_effects, |
| SourcePositionTable* source_positions) { |
| DCHECK_EQ(IrOpcode::kBranch, node->opcode()); |
| |
| // This optimization is a special case of (super)block cloning. It takes an |
| // input graph as shown below and clones the Branch node for every predecessor |
| // to the Merge, essentially removing the Merge completely. This avoids |
| // materializing the bit for the Phi and may offer potential for further |
| // branch folding optimizations (i.e. because one or more inputs to the Phi is |
| // a constant). Note that there may be more Phi nodes hanging off the Merge, |
| // but we can only a certain subset of them currently (actually only Phi and |
| // EffectPhi nodes whose uses have either the IfTrue or IfFalse as control |
| // input). |
| |
| // Control1 ... ControlN |
| // ^ ^ |
| // | | Cond1 ... CondN |
| // +----+ +----+ ^ ^ |
| // | | | | |
| // | | +----+ | |
| // Merge<--+ | +------------+ |
| // ^ \|/ |
| // | Phi |
| // | | |
| // Branch----+ |
| // ^ |
| // | |
| // +-----+-----+ |
| // | | |
| // IfTrue IfFalse |
| // ^ ^ |
| // | | |
| |
| // The resulting graph (modulo the Phi and EffectPhi nodes) looks like this: |
| |
| // Control1 Cond1 ... ControlN CondN |
| // ^ ^ ^ ^ |
| // \ / \ / |
| // Branch ... Branch |
| // ^ ^ |
| // | | |
| // +---+---+ +---+----+ |
| // | | | | |
| // IfTrue IfFalse ... IfTrue IfFalse |
| // ^ ^ ^ ^ |
| // | | | | |
| // +--+ +-------------+ | |
| // | | +--------------+ +--+ |
| // | | | | |
| // Merge Merge |
| // ^ ^ |
| // | | |
| |
| SourcePositionTable::Scope scope(source_positions, |
| source_positions->GetSourcePosition(node)); |
| Node* branch = node; |
| Node* cond = NodeProperties::GetValueInput(branch, 0); |
| if (!cond->OwnedBy(branch) || cond->opcode() != IrOpcode::kPhi) return; |
| Node* merge = NodeProperties::GetControlInput(branch); |
| if (merge->opcode() != IrOpcode::kMerge || |
| NodeProperties::GetControlInput(cond) != merge) { |
| return; |
| } |
| // Grab the IfTrue/IfFalse projections of the Branch. |
| BranchMatcher matcher(branch); |
| // Check/collect other Phi/EffectPhi nodes hanging off the Merge. |
| NodeVector phis(temp_zone); |
| for (Node* const use : merge->uses()) { |
| if (use == branch || use == cond) continue; |
| // We cannot currently deal with non-Phi/EffectPhi nodes hanging off the |
| // Merge. Ideally, we would just clone the nodes (and everything that |
| // depends on it to some distant join point), but that requires knowledge |
| // about dominance/post-dominance. |
| if (!NodeProperties::IsPhi(use)) return; |
| for (Edge edge : use->use_edges()) { |
| // Right now we can only handle Phi/EffectPhi nodes whose uses are |
| // directly control-dependend on either the IfTrue or the IfFalse |
| // successor, because we know exactly how to update those uses. |
| if (edge.from()->op()->ControlInputCount() != 1) return; |
| Node* control = NodeProperties::GetControlInput(edge.from()); |
| if (NodeProperties::IsPhi(edge.from())) { |
| control = NodeProperties::GetControlInput(control, edge.index()); |
| } |
| if (control != matcher.IfTrue() && control != matcher.IfFalse()) return; |
| } |
| phis.push_back(use); |
| } |
| BranchHint const hint = BranchHintOf(branch->op()); |
| int const input_count = merge->op()->ControlInputCount(); |
| DCHECK_LE(1, input_count); |
| Node** const inputs = graph->zone()->NewArray<Node*>(2 * input_count); |
| Node** const merge_true_inputs = &inputs[0]; |
| Node** const merge_false_inputs = &inputs[input_count]; |
| for (int index = 0; index < input_count; ++index) { |
| Node* cond1 = NodeProperties::GetValueInput(cond, index); |
| Node* control1 = NodeProperties::GetControlInput(merge, index); |
| Node* branch1 = graph->NewNode(common->Branch(hint), cond1, control1); |
| merge_true_inputs[index] = graph->NewNode(common->IfTrue(), branch1); |
| merge_false_inputs[index] = graph->NewNode(common->IfFalse(), branch1); |
| } |
| Node* const merge_true = matcher.IfTrue(); |
| Node* const merge_false = matcher.IfFalse(); |
| merge_true->TrimInputCount(0); |
| merge_false->TrimInputCount(0); |
| for (int i = 0; i < input_count; ++i) { |
| merge_true->AppendInput(graph->zone(), merge_true_inputs[i]); |
| merge_false->AppendInput(graph->zone(), merge_false_inputs[i]); |
| } |
| DCHECK_EQ(2u, block->SuccessorCount()); |
| NodeProperties::ChangeOp(matcher.IfTrue(), common->Merge(input_count)); |
| NodeProperties::ChangeOp(matcher.IfFalse(), common->Merge(input_count)); |
| int const true_index = |
| block->SuccessorAt(0)->NodeAt(0) == matcher.IfTrue() ? 0 : 1; |
| BlockEffectControlData* true_block_data = |
| &block_effects->For(block, block->SuccessorAt(true_index)); |
| BlockEffectControlData* false_block_data = |
| &block_effects->For(block, block->SuccessorAt(true_index ^ 1)); |
| for (Node* const phi : phis) { |
| for (int index = 0; index < input_count; ++index) { |
| inputs[index] = phi->InputAt(index); |
| } |
| inputs[input_count] = merge_true; |
| Node* phi_true = graph->NewNode(phi->op(), input_count + 1, inputs); |
| inputs[input_count] = merge_false; |
| Node* phi_false = graph->NewNode(phi->op(), input_count + 1, inputs); |
| if (phi->UseCount() == 0) { |
| DCHECK_EQ(phi->opcode(), IrOpcode::kEffectPhi); |
| } else { |
| for (Edge edge : phi->use_edges()) { |
| Node* control = NodeProperties::GetControlInput(edge.from()); |
| if (NodeProperties::IsPhi(edge.from())) { |
| control = NodeProperties::GetControlInput(control, edge.index()); |
| } |
| DCHECK(control == matcher.IfTrue() || control == matcher.IfFalse()); |
| edge.UpdateTo((control == matcher.IfTrue()) ? phi_true : phi_false); |
| } |
| } |
| if (phi->opcode() == IrOpcode::kEffectPhi) { |
| true_block_data->current_effect = phi_true; |
| false_block_data->current_effect = phi_false; |
| } |
| phi->Kill(); |
| } |
| // Fix up IfTrue and IfFalse and kill all dead nodes. |
| if (branch == block->control_input()) { |
| true_block_data->current_control = merge_true; |
| false_block_data->current_control = merge_false; |
| } |
| branch->Kill(); |
| cond->Kill(); |
| merge->Kill(); |
| } |
| |
| } // namespace |
| |
| void EffectControlLinearizer::Run() { |
| BlockEffectControlMap block_effects(temp_zone()); |
| ZoneVector<PendingEffectPhi> pending_effect_phis(temp_zone()); |
| ZoneVector<BasicBlock*> pending_block_controls(temp_zone()); |
| NodeVector inputs_buffer(temp_zone()); |
| |
| for (BasicBlock* block : *(schedule()->rpo_order())) { |
| size_t instr = 0; |
| |
| // The control node should be the first. |
| Node* control = block->NodeAt(instr); |
| DCHECK(NodeProperties::IsControl(control)); |
| // Update the control inputs. |
| if (HasIncomingBackEdges(block)) { |
| // If there are back edges, we need to update later because we have not |
| // computed the control yet. This should only happen for loops. |
| DCHECK_EQ(IrOpcode::kLoop, control->opcode()); |
| pending_block_controls.push_back(block); |
| } else { |
| // If there are no back edges, we can update now. |
| UpdateBlockControl(block, &block_effects); |
| } |
| instr++; |
| |
| // Iterate over the phis and update the effect phis. |
| Node* effect_phi = nullptr; |
| Node* terminate = nullptr; |
| for (; instr < block->NodeCount(); instr++) { |
| Node* node = block->NodeAt(instr); |
| // Only go through the phis and effect phis. |
| if (node->opcode() == IrOpcode::kEffectPhi) { |
| // There should be at most one effect phi in a block. |
| DCHECK_NULL(effect_phi); |
| // IfException blocks should not have effect phis. |
| DCHECK_NE(IrOpcode::kIfException, control->opcode()); |
| effect_phi = node; |
| } else if (node->opcode() == IrOpcode::kPhi) { |
| // Just skip phis. |
| } else if (node->opcode() == IrOpcode::kTerminate) { |
| DCHECK_NULL(terminate); |
| terminate = node; |
| } else { |
| break; |
| } |
| } |
| |
| if (effect_phi) { |
| // Make sure we update the inputs to the incoming blocks' effects. |
| if (HasIncomingBackEdges(block)) { |
| // In case of loops, we do not update the effect phi immediately |
| // because the back predecessor has not been handled yet. We just |
| // record the effect phi for later processing. |
| pending_effect_phis.push_back(PendingEffectPhi(effect_phi, block)); |
| } else { |
| UpdateEffectPhi(effect_phi, block, &block_effects, jsgraph()); |
| } |
| } |
| |
| Node* effect = effect_phi; |
| if (effect == nullptr) { |
| // There was no effect phi. |
| |
| // Since a loop should have at least a StackCheck, only loops in |
| // unreachable code can have no effect phi. |
| DCHECK_IMPLIES( |
| HasIncomingBackEdges(block), |
| block_effects.For(block->PredecessorAt(0), block) |
| .current_effect->opcode() == IrOpcode::kUnreachable); |
| if (block == schedule()->start()) { |
| // Start block => effect is start. |
| DCHECK_EQ(graph()->start(), control); |
| effect = graph()->start(); |
| } else if (control->opcode() == IrOpcode::kEnd) { |
| // End block is just a dummy, no effect needed. |
| DCHECK_EQ(BasicBlock::kNone, block->control()); |
| DCHECK_EQ(1u, block->size()); |
| effect = nullptr; |
| } else { |
| // If all the predecessors have the same effect, we can use it as our |
| // current effect. |
| for (size_t i = 0; i < block->PredecessorCount(); ++i) { |
| const BlockEffectControlData& data = |
| block_effects.For(block->PredecessorAt(i), block); |
| if (!effect) effect = data.current_effect; |
| if (data.current_effect != effect) { |
| effect = nullptr; |
| break; |
| } |
| } |
| if (effect == nullptr) { |
| DCHECK_NE(IrOpcode::kIfException, control->opcode()); |
| // The input blocks do not have the same effect. We have |
| // to create an effect phi node. |
| inputs_buffer.clear(); |
| inputs_buffer.resize(block->PredecessorCount(), jsgraph()->Dead()); |
| inputs_buffer.push_back(control); |
| effect = graph()->NewNode( |
| common()->EffectPhi(static_cast<int>(block->PredecessorCount())), |
| static_cast<int>(inputs_buffer.size()), &(inputs_buffer.front())); |
| // For loops, we update the effect phi node later to break cycles. |
| if (control->opcode() == IrOpcode::kLoop) { |
| pending_effect_phis.push_back(PendingEffectPhi(effect, block)); |
| } else { |
| UpdateEffectPhi(effect, block, &block_effects, jsgraph()); |
| } |
| } else if (control->opcode() == IrOpcode::kIfException) { |
| // The IfException is connected into the effect chain, so we need |
| // to update the effect here. |
| NodeProperties::ReplaceEffectInput(control, effect); |
| effect = control; |
| } |
| } |
| } |
| |
| // Fixup the Terminate node. |
| if (terminate != nullptr) { |
| NodeProperties::ReplaceEffectInput(terminate, effect); |
| } |
| |
| // The frame state at block entry is determined by the frame states leaving |
| // all predecessors. In case there is no frame state dominating this block, |
| // we can rely on a checkpoint being present before the next deoptimization. |
| // TODO(mstarzinger): Eventually we will need to go hunt for a frame state |
| // once deoptimizing nodes roam freely through the schedule. |
| Node* frame_state = nullptr; |
| if (block != schedule()->start()) { |
| // If all the predecessors have the same effect, we can use it |
| // as our current effect. |
| frame_state = |
| block_effects.For(block->PredecessorAt(0), block).current_frame_state; |
| for (size_t i = 1; i < block->PredecessorCount(); i++) { |
| if (block_effects.For(block->PredecessorAt(i), block) |
| .current_frame_state != frame_state) { |
| frame_state = nullptr; |
| frame_state_zapper_ = graph()->end(); |
| break; |
| } |
| } |
| } |
| |
| // Process the ordinary instructions. |
| for (; instr < block->NodeCount(); instr++) { |
| Node* node = block->NodeAt(instr); |
| ProcessNode(node, &frame_state, &effect, &control); |
| } |
| |
| switch (block->control()) { |
| case BasicBlock::kGoto: |
| case BasicBlock::kNone: |
| break; |
| |
| case BasicBlock::kCall: |
| case BasicBlock::kTailCall: |
| case BasicBlock::kSwitch: |
| case BasicBlock::kReturn: |
| case BasicBlock::kDeoptimize: |
| case BasicBlock::kThrow: |
| ProcessNode(block->control_input(), &frame_state, &effect, &control); |
| break; |
| |
| case BasicBlock::kBranch: |
| ProcessNode(block->control_input(), &frame_state, &effect, &control); |
| TryCloneBranch(block->control_input(), block, temp_zone(), graph(), |
| common(), &block_effects, source_positions_); |
| break; |
| } |
| |
| // Store the effect, control and frame state for later use. |
| for (BasicBlock* successor : block->successors()) { |
| BlockEffectControlData* data = &block_effects.For(block, successor); |
| if (data->current_effect == nullptr) { |
| data->current_effect = effect; |
| } |
| if (data->current_control == nullptr) { |
| data->current_control = control; |
| } |
| data->current_frame_state = frame_state; |
| } |
| } |
| |
| for (BasicBlock* pending_block_control : pending_block_controls) { |
| UpdateBlockControl(pending_block_control, &block_effects); |
| } |
| // Update the incoming edges of the effect phis that could not be processed |
| // during the first pass (because they could have incoming back edges). |
| for (const PendingEffectPhi& pending_effect_phi : pending_effect_phis) { |
| UpdateEffectPhi(pending_effect_phi.effect_phi, pending_effect_phi.block, |
| &block_effects, jsgraph()); |
| } |
| } |
| |
| void EffectControlLinearizer::ProcessNode(Node* node, Node** frame_state, |
| Node** effect, Node** control) { |
| SourcePositionTable::Scope scope(source_positions_, |
| source_positions_->GetSourcePosition(node)); |
| |
| // If the node needs to be wired into the effect/control chain, do this |
| // here. Pass current frame state for lowering to eager deoptimization. |
| if (TryWireInStateEffect(node, *frame_state, effect, control)) { |
| return; |
| } |
| |
| // If the node has a visible effect, then there must be a checkpoint in the |
| // effect chain before we are allowed to place another eager deoptimization |
| // point. We zap the frame state to ensure this invariant is maintained. |
| if (region_observability_ == RegionObservability::kObservable && |
| !node->op()->HasProperty(Operator::kNoWrite)) { |
| *frame_state = nullptr; |
| frame_state_zapper_ = node; |
| } |
| |
| // Remove the end markers of 'atomic' allocation region because the |
| // region should be wired-in now. |
| if (node->opcode() == IrOpcode::kFinishRegion) { |
| // Reset the current region observability. |
| region_observability_ = RegionObservability::kObservable; |
| // Update the value uses to the value input of the finish node and |
| // the effect uses to the effect input. |
| return RemoveRenameNode(node); |
| } |
| if (node->opcode() == IrOpcode::kBeginRegion) { |
| // Determine the observability for this region and use that for all |
| // nodes inside the region (i.e. ignore the absence of kNoWrite on |
| // StoreField and other operators). |
| DCHECK_NE(RegionObservability::kNotObservable, region_observability_); |
| region_observability_ = RegionObservabilityOf(node->op()); |
| // Update the value uses to the value input of the finish node and |
| // the effect uses to the effect input. |
| return RemoveRenameNode(node); |
| } |
| if (node->opcode() == IrOpcode::kTypeGuard) { |
| return RemoveRenameNode(node); |
| } |
| |
| // Special treatment for checkpoint nodes. |
| if (node->opcode() == IrOpcode::kCheckpoint) { |
| // Unlink the check point; effect uses will be updated to the incoming |
| // effect that is passed. The frame state is preserved for lowering. |
| DCHECK_EQ(RegionObservability::kObservable, region_observability_); |
| *frame_state = NodeProperties::GetFrameStateInput(node); |
| return; |
| } |
| |
| // The IfSuccess nodes should always start a basic block (and basic block |
| // start nodes are not handled in the ProcessNode method). |
| DCHECK_NE(IrOpcode::kIfSuccess, node->opcode()); |
| |
| // If the node takes an effect, replace with the current one. |
| if (node->op()->EffectInputCount() > 0) { |
| DCHECK_EQ(1, node->op()->EffectInputCount()); |
| Node* input_effect = NodeProperties::GetEffectInput(node); |
| |
| if (input_effect != *effect) { |
| NodeProperties::ReplaceEffectInput(node, *effect); |
| } |
| |
| // If the node produces an effect, update our current effect. (However, |
| // ignore new effect chains started with ValueEffect.) |
| if (node->op()->EffectOutputCount() > 0) { |
| DCHECK_EQ(1, node->op()->EffectOutputCount()); |
| *effect = node; |
| } |
| } else { |
| // New effect chain is only started with a Start or ValueEffect node. |
| DCHECK(node->op()->EffectOutputCount() == 0 || |
| node->opcode() == IrOpcode::kStart); |
| } |
| |
| // Rewire control inputs. |
| for (int i = 0; i < node->op()->ControlInputCount(); i++) { |
| NodeProperties::ReplaceControlInput(node, *control, i); |
| } |
| // Update the current control. |
| if (node->op()->ControlOutputCount() > 0) { |
| *control = node; |
| } |
| |
| // Break the effect chain on {Unreachable} and reconnect to the graph end. |
| // Mark the following code for deletion by connecting to the {Dead} node. |
| if (node->opcode() == IrOpcode::kUnreachable) { |
| ConnectUnreachableToEnd(*effect, *control, jsgraph()); |
| *effect = *control = jsgraph()->Dead(); |
| } |
| } |
| |
| bool EffectControlLinearizer::TryWireInStateEffect(Node* node, |
| Node* frame_state, |
| Node** effect, |
| Node** control) { |
| gasm()->Reset(*effect, *control); |
| Node* result = nullptr; |
| switch (node->opcode()) { |
| case IrOpcode::kChangeBitToTagged: |
| result = LowerChangeBitToTagged(node); |
| break; |
| case IrOpcode::kChangeInt31ToTaggedSigned: |
| result = LowerChangeInt31ToTaggedSigned(node); |
| break; |
| case IrOpcode::kChangeInt32ToTagged: |
| result = LowerChangeInt32ToTagged(node); |
| break; |
| case IrOpcode::kChangeUint32ToTagged: |
| result = LowerChangeUint32ToTagged(node); |
| break; |
| case IrOpcode::kChangeFloat64ToTagged: |
| result = LowerChangeFloat64ToTagged(node); |
| break; |
| case IrOpcode::kChangeFloat64ToTaggedPointer: |
| result = LowerChangeFloat64ToTaggedPointer(node); |
| break; |
| case IrOpcode::kChangeTaggedSignedToInt32: |
| result = LowerChangeTaggedSignedToInt32(node); |
| break; |
| case IrOpcode::kChangeTaggedToBit: |
| result = LowerChangeTaggedToBit(node); |
| break; |
| case IrOpcode::kChangeTaggedToInt32: |
| result = LowerChangeTaggedToInt32(node); |
| break; |
| case IrOpcode::kChangeTaggedToUint32: |
| result = LowerChangeTaggedToUint32(node); |
| break; |
| case IrOpcode::kChangeTaggedToFloat64: |
| result = LowerChangeTaggedToFloat64(node); |
| break; |
| case IrOpcode::kChangeTaggedToTaggedSigned: |
| result = LowerChangeTaggedToTaggedSigned(node); |
| break; |
| case IrOpcode::kTruncateTaggedToBit: |
| result = LowerTruncateTaggedToBit(node); |
| break; |
| case IrOpcode::kTruncateTaggedPointerToBit: |
| result = LowerTruncateTaggedPointerToBit(node); |
| break; |
| case IrOpcode::kTruncateTaggedToFloat64: |
| result = LowerTruncateTaggedToFloat64(node); |
| break; |
| case IrOpcode::kCheckBounds: |
| result = LowerCheckBounds(node, frame_state); |
| break; |
| case IrOpcode::kMaskIndexWithBound: |
| result = LowerMaskIndexWithBound(node); |
| break; |
| case IrOpcode::kCheckMaps: |
| LowerCheckMaps(node, frame_state); |
| break; |
| case IrOpcode::kCompareMaps: |
| result = LowerCompareMaps(node); |
| break; |
| case IrOpcode::kCheckNumber: |
| result = LowerCheckNumber(node, frame_state); |
| break; |
| case IrOpcode::kCheckReceiver: |
| result = LowerCheckReceiver(node, frame_state); |
| break; |
| case IrOpcode::kCheckSymbol: |
| result = LowerCheckSymbol(node, frame_state); |
| break; |
| case IrOpcode::kCheckString: |
| result = LowerCheckString(node, frame_state); |
| break; |
| case IrOpcode::kCheckSeqString: |
| result = LowerCheckSeqString(node, frame_state); |
| break; |
| case IrOpcode::kCheckInternalizedString: |
| result = LowerCheckInternalizedString(node, frame_state); |
| break; |
| case IrOpcode::kCheckIf: |
| LowerCheckIf(node, frame_state); |
| break; |
| case IrOpcode::kCheckedInt32Add: |
| result = LowerCheckedInt32Add(node, frame_state); |
| break; |
| case IrOpcode::kCheckedInt32Sub: |
| result = LowerCheckedInt32Sub(node, frame_state); |
| break; |
| case IrOpcode::kCheckedInt32Div: |
| result = LowerCheckedInt32Div(node, frame_state); |
| break; |
| case IrOpcode::kCheckedInt32Mod: |
| result = LowerCheckedInt32Mod(node, frame_state); |
| break; |
| case IrOpcode::kCheckedUint32Div: |
| result = LowerCheckedUint32Div(node, frame_state); |
| break; |
| case IrOpcode::kCheckedUint32Mod: |
| result = LowerCheckedUint32Mod(node, frame_state); |
| break; |
| case IrOpcode::kCheckedInt32Mul: |
| result = LowerCheckedInt32Mul(node, frame_state); |
| break; |
| case IrOpcode::kCheckedInt32ToTaggedSigned: |
| result = LowerCheckedInt32ToTaggedSigned(node, frame_state); |
| break; |
| case IrOpcode::kCheckedUint32ToInt32: |
| result = LowerCheckedUint32ToInt32(node, frame_state); |
| break; |
| case IrOpcode::kCheckedUint32ToTaggedSigned: |
| result = LowerCheckedUint32ToTaggedSigned(node, frame_state); |
| break; |
| case IrOpcode::kCheckedFloat64ToInt32: |
| result = LowerCheckedFloat64ToInt32(node, frame_state); |
| break; |
| case IrOpcode::kCheckedTaggedSignedToInt32: |
| if (frame_state == nullptr) { |
| V8_Fatal(__FILE__, __LINE__, "No frame state (zapped by #%d: %s)", |
| frame_state_zapper_->id(), |
| frame_state_zapper_->op()->mnemonic()); |
| } |
| result = LowerCheckedTaggedSignedToInt32(node, frame_state); |
| break; |
| case IrOpcode::kCheckedTaggedToInt32: |
| result = LowerCheckedTaggedToInt32(node, frame_state); |
| break; |
| case IrOpcode::kCheckedTaggedToFloat64: |
| result = LowerCheckedTaggedToFloat64(node, frame_state); |
| break; |
| case IrOpcode::kCheckedTaggedToTaggedSigned: |
| result = LowerCheckedTaggedToTaggedSigned(node, frame_state); |
| break; |
| case IrOpcode::kCheckedTaggedToTaggedPointer: |
| result = LowerCheckedTaggedToTaggedPointer(node, frame_state); |
| break; |
| case IrOpcode::kTruncateTaggedToWord32: |
| result = LowerTruncateTaggedToWord32(node); |
| break; |
| case IrOpcode::kCheckedTruncateTaggedToWord32: |
| result = LowerCheckedTruncateTaggedToWord32(node, frame_state); |
| break; |
| case IrOpcode::kNumberToString: |
| result = LowerNumberToString(node); |
| break; |
| case IrOpcode::kObjectIsArrayBufferView: |
| result = LowerObjectIsArrayBufferView(node); |
| break; |
| case IrOpcode::kObjectIsBigInt: |
| result = LowerObjectIsBigInt(node); |
| break; |
| case IrOpcode::kObjectIsCallable: |
| result = LowerObjectIsCallable(node); |
| break; |
| case IrOpcode::kObjectIsConstructor: |
| result = LowerObjectIsConstructor(node); |
| break; |
| case IrOpcode::kObjectIsDetectableCallable: |
| result = LowerObjectIsDetectableCallable(node); |
| break; |
| case IrOpcode::kObjectIsMinusZero: |
| result = LowerObjectIsMinusZero(node); |
| break; |
| case IrOpcode::kObjectIsNaN: |
| result = LowerObjectIsNaN(node); |
| break; |
| case IrOpcode::kObjectIsNonCallable: |
| result = LowerObjectIsNonCallable(node); |
| break; |
| case IrOpcode::kObjectIsNumber: |
| result = LowerObjectIsNumber(node); |
| break; |
| case IrOpcode::kObjectIsReceiver: |
| result = LowerObjectIsReceiver(node); |
| break; |
| case IrOpcode::kObjectIsSmi: |
| result = LowerObjectIsSmi(node); |
| break; |
| case IrOpcode::kObjectIsString: |
| result = LowerObjectIsString(node); |
| break; |
| case IrOpcode::kObjectIsSymbol: |
| result = LowerObjectIsSymbol(node); |
| break; |
| case IrOpcode::kObjectIsUndetectable: |
| result = LowerObjectIsUndetectable(node); |
| break; |
| case IrOpcode::kArgumentsFrame: |
| result = LowerArgumentsFrame(node); |
| break; |
| case IrOpcode::kArgumentsLength: |
| result = LowerArgumentsLength(node); |
| break; |
| case IrOpcode::kToBoolean: |
| result = LowerToBoolean(node); |
| break; |
| case IrOpcode::kTypeOf: |
| result = LowerTypeOf(node); |
| break; |
| case IrOpcode::kClassOf: |
| result = LowerClassOf(node); |
| break; |
| case IrOpcode::kNewDoubleElements: |
| result = LowerNewDoubleElements(node); |
| break; |
| case IrOpcode::kNewSmiOrObjectElements: |
| result = LowerNewSmiOrObjectElements(node); |
| break; |
| case IrOpcode::kNewArgumentsElements: |
| result = LowerNewArgumentsElements(node); |
| break; |
| case IrOpcode::kNewConsString: |
| result = LowerNewConsString(node); |
| break; |
| case IrOpcode::kArrayBufferWasNeutered: |
| result = LowerArrayBufferWasNeutered(node); |
| break; |
| case IrOpcode::kSameValue: |
| result = LowerSameValue(node); |
| break; |
| case IrOpcode::kDeadValue: |
| result = LowerDeadValue(node); |
| case IrOpcode::kStringFromCharCode: |
| result = LowerStringFromCharCode(node); |
| break; |
| case IrOpcode::kStringFromCodePoint: |
| result = LowerStringFromCodePoint(node); |
| break; |
| case IrOpcode::kStringIndexOf: |
| result = LowerStringIndexOf(node); |
| break; |
| case IrOpcode::kStringLength: |
| result = LowerStringLength(node); |
| break; |
| case IrOpcode::kStringToNumber: |
| result = LowerStringToNumber(node); |
| break; |
| case IrOpcode::kStringCharAt: |
| result = LowerStringCharAt(node); |
| break; |
| case IrOpcode::kStringCharCodeAt: |
| result = LowerStringCharCodeAt(node); |
| break; |
| case IrOpcode::kSeqStringCharCodeAt: |
| result = LowerSeqStringCharCodeAt(node); |
| break; |
| case IrOpcode::kStringCodePointAt: |
| result = LowerStringCodePointAt(node); |
| break; |
| case IrOpcode::kSeqStringCodePointAt: |
| result = LowerSeqStringCharCodeAt(node); |
| break; |
| case IrOpcode::kStringToLowerCaseIntl: |
| result = LowerStringToLowerCaseIntl(node); |
| break; |
| case IrOpcode::kStringToUpperCaseIntl: |
| result = LowerStringToUpperCaseIntl(node); |
| break; |
| case IrOpcode::kStringEqual: |
| result = LowerStringEqual(node); |
| break; |
| case IrOpcode::kStringLessThan: |
| result = LowerStringLessThan(node); |
| break; |
| case IrOpcode::kStringLessThanOrEqual: |
| result = LowerStringLessThanOrEqual(node); |
| break; |
| case IrOpcode::kNumberIsFloat64Hole: |
| result = LowerNumberIsFloat64Hole(node); |
| break; |
| case IrOpcode::kCheckFloat64Hole: |
| result = LowerCheckFloat64Hole(node, frame_state); |
| break; |
| case IrOpcode::kCheckNotTaggedHole: |
| result = LowerCheckNotTaggedHole(node, frame_state); |
| break; |
| case IrOpcode::kConvertTaggedHoleToUndefined: |
| result = LowerConvertTaggedHoleToUndefined(node); |
| break; |
| case IrOpcode::kCheckEqualsInternalizedString: |
| LowerCheckEqualsInternalizedString(node, frame_state); |
| break; |
| case IrOpcode::kAllocate: |
| result = LowerAllocate(node); |
| break; |
| case IrOpcode::kCheckEqualsSymbol: |
| LowerCheckEqualsSymbol(node, frame_state); |
| break; |
| case IrOpcode::kPlainPrimitiveToNumber: |
| result = LowerPlainPrimitiveToNumber(node); |
| break; |
| case IrOpcode::kPlainPrimitiveToWord32: |
| result = LowerPlainPrimitiveToWord32(node); |
| break; |
| case IrOpcode::kPlainPrimitiveToFloat64: |
| result = LowerPlainPrimitiveToFloat64(node); |
| break; |
| case IrOpcode::kEnsureWritableFastElements: |
| result = LowerEnsureWritableFastElements(node); |
| break; |
| case IrOpcode::kMaybeGrowFastElements: |
| result = LowerMaybeGrowFastElements(node, frame_state); |
| break; |
| case IrOpcode::kTransitionElementsKind: |
| LowerTransitionElementsKind(node); |
| break; |
| case IrOpcode::kLoadFieldByIndex: |
| result = LowerLoadFieldByIndex(node); |
| break; |
| case IrOpcode::kLoadTypedElement: |
| result = LowerLoadTypedElement(node); |
| break; |
| case IrOpcode::kStoreTypedElement: |
| LowerStoreTypedElement(node); |
| break; |
| case IrOpcode::kStoreSignedSmallElement: |
| LowerStoreSignedSmallElement(node); |
| break; |
| case IrOpcode::kFindOrderedHashMapEntry: |
| result = LowerFindOrderedHashMapEntry(node); |
| break; |
| case IrOpcode::kFindOrderedHashMapEntryForInt32Key: |
| result = LowerFindOrderedHashMapEntryForInt32Key(node); |
| break; |
| case IrOpcode::kTransitionAndStoreNumberElement: |
| LowerTransitionAndStoreNumberElement(node); |
| break; |
| case IrOpcode::kTransitionAndStoreNonNumberElement: |
| LowerTransitionAndStoreNonNumberElement(node); |
| break; |
| case IrOpcode::kTransitionAndStoreElement: |
| LowerTransitionAndStoreElement(node); |
| break; |
| case IrOpcode::kRuntimeAbort: |
| LowerRuntimeAbort(node); |
| break; |
| case IrOpcode::kConvertReceiver: |
| result = LowerConvertReceiver(node); |
| break; |
| case IrOpcode::kFloat64RoundUp: |
| if (!LowerFloat64RoundUp(node).To(&result)) { |
| return false; |
| } |
| break; |
| case IrOpcode::kFloat64RoundDown: |
| if (!LowerFloat64RoundDown(node).To(&result)) { |
| return false; |
| } |
| break; |
| case IrOpcode::kFloat64RoundTruncate: |
| if (!LowerFloat64RoundTruncate(node).To(&result)) { |
| return false; |
| } |
| break; |
| case IrOpcode::kFloat64RoundTiesEven: |
| if (!LowerFloat64RoundTiesEven(node).To(&result)) { |
| return false; |
| } |
| break; |
| default: |
| return false; |
| } |
| |
| if ((result ? 1 : 0) != node->op()->ValueOutputCount()) { |
| V8_Fatal(__FILE__, __LINE__, |
| "Effect control linearizer lowering of '%s':" |
| " value output count does not agree.", |
| node->op()->mnemonic()); |
| } |
| |
| *effect = gasm()->ExtractCurrentEffect(); |
| *control = gasm()->ExtractCurrentControl(); |
| NodeProperties::ReplaceUses(node, result, *effect, *control); |
| return true; |
| } |
| |
| #define __ gasm()-> |
| |
| Node* EffectControlLinearizer::LowerChangeFloat64ToTagged(Node* node) { |
| CheckForMinusZeroMode mode = CheckMinusZeroModeOf(node->op()); |
| Node* value = node->InputAt(0); |
| |
| auto done = __ MakeLabel(MachineRepresentation::kTagged); |
| auto if_heapnumber = __ MakeDeferredLabel(); |
| auto if_int32 = __ MakeLabel(); |
| |
| Node* value32 = __ RoundFloat64ToInt32(value); |
| __ GotoIf(__ Float64Equal(value, __ ChangeInt32ToFloat64(value32)), |
| &if_int32); |
| __ Goto(&if_heapnumber); |
| |
| __ Bind(&if_int32); |
| { |
| if (mode == CheckForMinusZeroMode::kCheckForMinusZero) { |
| Node* zero = __ Int32Constant(0); |
| auto if_zero = __ MakeDeferredLabel(); |
| auto if_smi = __ MakeLabel(); |
| |
| __ GotoIf(__ Word32Equal(value32, zero), &if_zero); |
| __ Goto(&if_smi); |
| |
| __ Bind(&if_zero); |
| { |
| // In case of 0, we need to check the high bits for the IEEE -0 pattern. |
| __ GotoIf(__ Int32LessThan(__ Float64ExtractHighWord32(value), zero), |
| &if_heapnumber); |
| __ Goto(&if_smi); |
| } |
| |
| __ Bind(&if_smi); |
| } |
| |
| if (machine()->Is64()) { |
| Node* value_smi = ChangeInt32ToSmi(value32); |
| __ Goto(&done, value_smi); |
| } else { |
| Node* add = __ Int32AddWithOverflow(value32, value32); |
| Node* ovf = __ Projection(1, add); |
| __ GotoIf(ovf, &if_heapnumber); |
| Node* value_smi = __ Projection(0, add); |
| __ Goto(&done, value_smi); |
| } |
| } |
| |
| __ Bind(&if_heapnumber); |
| { |
| Node* value_number = AllocateHeapNumberWithValue(value); |
| __ Goto(&done, value_number); |
| } |
| |
| __ Bind(&done); |
| return done.PhiAt(0); |
| } |
| |
| Node* EffectControlLinearizer::LowerChangeFloat64ToTaggedPointer(Node* node) { |
| Node* value = node->InputAt(0); |
| return AllocateHeapNumberWithValue(value); |
| } |
| |
| Node* EffectControlLinearizer::LowerChangeBitToTagged(Node* node) { |
| Node* value = node->InputAt(0); |
| |
| auto if_true = __ MakeLabel(); |
| auto done = __ MakeLabel(MachineRepresentation::kTagged); |
| |
| __ GotoIf(value, &if_true); |
| __ Goto(&done, __ FalseConstant()); |
| |
| __ Bind(&if_true); |
| __ Goto(&done, __ TrueConstant()); |
| |
| __ Bind(&done); |
| return done.PhiAt(0); |
| } |
| |
| Node* EffectControlLinearizer::LowerChangeInt31ToTaggedSigned(Node* node) { |
| Node* value = node->InputAt(0); |
| return ChangeInt32ToSmi(value); |
| } |
| |
| Node* EffectControlLinearizer::LowerChangeInt32ToTagged(Node* node) { |
| Node* value = node->InputAt(0); |
| |
| if (machine()->Is64()) { |
| return ChangeInt32ToSmi(value); |
| } |
| |
| auto if_overflow = __ MakeDeferredLabel(); |
| auto done = __ MakeLabel(MachineRepresentation::kTagged); |
| |
| Node* add = __ Int32AddWithOverflow(value, value); |
| Node* ovf = __ Projection(1, add); |
| __ GotoIf(ovf, &if_overflow); |
| __ Goto(&done, __ Projection(0, add)); |
| |
| __ Bind(&if_overflow); |
| Node* number = AllocateHeapNumberWithValue(__ ChangeInt32ToFloat64(value)); |
| __ Goto(&done, number); |
| |
| __ Bind(&done); |
| return done.PhiAt(0); |
| } |
| |
| Node* EffectControlLinearizer::LowerChangeUint32ToTagged(Node* node) { |
| Node* value = node->InputAt(0); |
| |
| auto if_not_in_smi_range = __ MakeDeferredLabel(); |
| auto done = __ MakeLabel(MachineRepresentation::kTagged); |
| |
| Node* check = __ Uint32LessThanOrEqual(value, SmiMaxValueConstant()); |
| __ GotoIfNot(check, &if_not_in_smi_range); |
| __ Goto(&done, ChangeUint32ToSmi(value)); |
| |
| __ Bind(&if_not_in_smi_range); |
| Node* number = AllocateHeapNumberWithValue(__ ChangeUint32ToFloat64(value)); |
| |
| __ Goto(&done, number); |
| __ Bind(&done); |
| |
| return done.PhiAt(0); |
| } |
| |
| Node* EffectControlLinearizer::LowerChangeTaggedSignedToInt32(Node* node) { |
| Node* value = node->InputAt(0); |
| return ChangeSmiToInt32(value); |
| } |
| |
| Node* EffectControlLinearizer::LowerChangeTaggedToBit(Node* node) { |
| Node* value = node->InputAt(0); |
| return __ WordEqual(value, __ TrueConstant()); |
| } |
| |
| void EffectControlLinearizer::TruncateTaggedPointerToBit( |
| Node* node, GraphAssemblerLabel<1>* done) { |
| Node* value = node->InputAt(0); |
| |
| auto if_heapnumber = __ MakeDeferredLabel(); |
| auto if_bigint = __ MakeDeferredLabel(); |
| |
| Node* zero = __ Int32Constant(0); |
| Node* fzero = __ Float64Constant(0.0); |
| |
| // Check if {value} is false. |
| __ GotoIf(__ WordEqual(value, __ FalseConstant()), done, zero); |
| |
| // Check if {value} is the empty string. |
| __ GotoIf(__ WordEqual(value, __ EmptyStringConstant()), done, zero); |
| |
| // Load the map of {value}. |
| Node* value_map = __ LoadField(AccessBuilder::ForMap(), value); |
| |
| // Check if the {value} is undetectable and immediately return false. |
| // This includes undefined and null. |
| Node* value_map_bitfield = |
| __ LoadField(AccessBuilder::ForMapBitField(), value_map); |
| __ GotoIfNot( |
| __ Word32Equal( |
| __ Word32And(value_map_bitfield, |
| __ Int32Constant(Map::IsUndetectableBit::kMask)), |
| zero), |
| done, zero); |
| |
| // Check if {value} is a HeapNumber. |
| __ GotoIf(__ WordEqual(value_map, __ HeapNumberMapConstant()), |
| &if_heapnumber); |
| |
| // Check if {value} is a BigInt. |
| Node* value_instance_type = |
| __ LoadField(AccessBuilder::ForMapInstanceType(), value_map); |
| __ GotoIf(__ Word32Equal(value_instance_type, __ Int32Constant(BIGINT_TYPE)), |
| &if_bigint); |
| |
| // All other values that reach here are true. |
| __ Goto(done, __ Int32Constant(1)); |
| |
| __ Bind(&if_heapnumber); |
| { |
| // For HeapNumber {value}, just check that its value is not 0.0, -0.0 or |
| // NaN. |
| Node* value_value = |
| __ LoadField(AccessBuilder::ForHeapNumberValue(), value); |
| __ Goto(done, __ Float64LessThan(fzero, __ Float64Abs(value_value))); |
| } |
| |
| __ Bind(&if_bigint); |
| { |
| Node* bitfield = __ LoadField(AccessBuilder::ForBigIntBitfield(), value); |
| Node* length_is_zero = __ WordEqual( |
| __ WordAnd(bitfield, __ IntPtrConstant(BigInt::LengthBits::kMask)), |
| __ IntPtrConstant(0)); |
| __ Goto(done, __ Word32Equal(length_is_zero, zero)); |
| } |
| } |
| |
| Node* EffectControlLinearizer::LowerTruncateTaggedToBit(Node* node) { |
| auto done = __ MakeLabel(MachineRepresentation::kBit); |
| auto if_smi = __ MakeDeferredLabel(); |
| |
| Node* value = node->InputAt(0); |
| __ GotoIf(ObjectIsSmi(value), &if_smi); |
| |
| TruncateTaggedPointerToBit(node, &done); |
| |
| __ Bind(&if_smi); |
| { |
| // If {value} is a Smi, then we only need to check that it's not zero. |
| __ Goto(&done, __ Word32Equal(__ WordEqual(value, __ IntPtrConstant(0)), |
| __ Int32Constant(0))); |
| } |
| |
| __ Bind(&done); |
| return done.PhiAt(0); |
| } |
| |
| Node* EffectControlLinearizer::LowerTruncateTaggedPointerToBit(Node* node) { |
| auto done = __ MakeLabel(MachineRepresentation::kBit); |
| |
| TruncateTaggedPointerToBit(node, &done); |
| |
| __ Bind(&done); |
| return done.PhiAt(0); |
| } |
| |
| Node* EffectControlLinearizer::LowerChangeTaggedToInt32(Node* node) { |
| Node* value = node->InputAt(0); |
| |
| auto if_not_smi = __ MakeDeferredLabel(); |
| auto done = __ MakeLabel(MachineRepresentation::kWord32); |
| |
| Node* check = ObjectIsSmi(value); |
| __ GotoIfNot(check, &if_not_smi); |
| __ Goto(&done, ChangeSmiToInt32(value)); |
| |
| __ Bind(&if_not_smi); |
| STATIC_ASSERT(HeapNumber::kValueOffset == Oddball::kToNumberRawOffset); |
| Node* vfalse = __ LoadField(AccessBuilder::ForHeapNumberValue(), value); |
| vfalse = __ ChangeFloat64ToInt32(vfalse); |
| __ Goto(&done, vfalse); |
| |
| __ Bind(&done); |
| return done.PhiAt(0); |
| } |
| |
| Node* EffectControlLinearizer::LowerChangeTaggedToUint32(Node* node) { |
| Node* value = node->InputAt(0); |
| |
| auto if_not_smi = __ MakeDeferredLabel(); |
| auto done = __ MakeLabel(MachineRepresentation::kWord32); |
| |
| Node* check = ObjectIsSmi(value); |
| __ GotoIfNot(check, &if_not_smi); |
| __ Goto(&done, ChangeSmiToInt32(value)); |
| |
| __ Bind(&if_not_smi); |
| STATIC_ASSERT(HeapNumber::kValueOffset == Oddball::kToNumberRawOffset); |
| Node* vfalse = __ LoadField(AccessBuilder::ForHeapNumberValue(), value); |
| vfalse = __ ChangeFloat64ToUint32(vfalse); |
| __ Goto(&done, vfalse); |
| |
| __ Bind(&done); |
| return done.PhiAt(0); |
| } |
| |
| Node* EffectControlLinearizer::LowerChangeTaggedToFloat64(Node* node) { |
| return LowerTruncateTaggedToFloat64(node); |
| } |
| |
| Node* EffectControlLinearizer::LowerChangeTaggedToTaggedSigned(Node* node) { |
| Node* value = node->InputAt(0); |
| |
| auto if_not_smi = __ MakeDeferredLabel(); |
| auto done = __ MakeLabel(MachineRepresentation::kWord32); |
| |
| Node* check = ObjectIsSmi(value); |
| __ GotoIfNot(check, &if_not_smi); |
| __ Goto(&done, value); |
| |
| __ Bind(&if_not_smi); |
| STATIC_ASSERT(HeapNumber::kValueOffset == Oddball::kToNumberRawOffset); |
| Node* vfalse = __ LoadField(AccessBuilder::ForHeapNumberValue(), value); |
| vfalse = __ ChangeFloat64ToInt32(vfalse); |
| vfalse = ChangeInt32ToSmi(vfalse); |
| __ Goto(&done, vfalse); |
| |
| __ Bind(&done); |
| return done.PhiAt(0); |
| } |
| |
| Node* EffectControlLinearizer::LowerTruncateTaggedToFloat64(Node* node) { |
| Node* value = node->InputAt(0); |
| |
| auto if_not_smi = __ MakeDeferredLabel(); |
| auto done = __ MakeLabel(MachineRepresentation::kFloat64); |
| |
| Node* check = ObjectIsSmi(value); |
| __ GotoIfNot(check, &if_not_smi); |
| Node* vtrue = ChangeSmiToInt32(value); |
| vtrue = __ ChangeInt32ToFloat64(vtrue); |
| __ Goto(&done, vtrue); |
| |
| __ Bind(&if_not_smi); |
| STATIC_ASSERT(HeapNumber::kValueOffset == Oddball::kToNumberRawOffset); |
| Node* vfalse = __ LoadField(AccessBuilder::ForHeapNumberValue(), value); |
| __ Goto(&done, vfalse); |
| |
| __ Bind(&done); |
| return done.PhiAt(0); |
| } |
| |
| Node* EffectControlLinearizer::LowerCheckBounds(Node* node, Node* frame_state) { |
| Node* index = node->InputAt(0); |
| Node* limit = node->InputAt(1); |
| const CheckParameters& params = CheckParametersOf(node->op()); |
| |
| Node* check = __ Uint32LessThan(index, limit); |
| __ DeoptimizeIfNot(DeoptimizeReason::kOutOfBounds, params.feedback(), check, |
| frame_state); |
| return index; |
| } |
| |
| Node* EffectControlLinearizer::LowerMaskIndexWithBound(Node* node) { |
| Node* index = node->InputAt(0); |
| if (mask_array_index_ == kMaskArrayIndex) { |
| Node* limit = node->InputAt(1); |
| |
| // mask = ((index - limit) & ~index) >> 31 |
| // index = index & mask |
| Node* neg_index = __ Word32Xor(index, __ Int32Constant(-1)); |
| Node* mask = |
| __ Word32Sar(__ Word32And(__ Int32Sub(index, limit), neg_index), |
| __ Int32Constant(31)); |
| index = __ Word32And(index, mask); |
| } |
| return index; |
| } |
| |
| void EffectControlLinearizer::LowerCheckMaps(Node* node, Node* frame_state) { |
| CheckMapsParameters const& p = CheckMapsParametersOf(node->op()); |
| Node* value = node->InputAt(0); |
| |
| ZoneHandleSet<Map> const& maps = p.maps(); |
| size_t const map_count = maps.size(); |
| |
| if (p.flags() & CheckMapsFlag::kTryMigrateInstance) { |
| auto done = __ MakeDeferredLabel(); |
| auto migrate = __ MakeDeferredLabel(); |
| |
| // Load the current map of the {value}. |
| Node* value_map = __ LoadField(AccessBuilder::ForMap(), value); |
| |
| // Perform the map checks. |
| for (size_t i = 0; i < map_count; ++i) { |
| Node* map = __ HeapConstant(maps[i]); |
| Node* check = __ WordEqual(value_map, map); |
| if (i == map_count - 1) { |
| __ GotoIfNot(check, &migrate); |
| __ Goto(&done); |
| } else { |
| __ GotoIf(check, &done); |
| } |
| } |
| |
| // Perform the (deferred) instance migration. |
| __ Bind(&migrate); |
| { |
| // If map is not deprecated the migration attempt does not make sense. |
| Node* bitfield3 = |
| __ LoadField(AccessBuilder::ForMapBitField3(), value_map); |
| Node* if_not_deprecated = __ WordEqual( |
| __ Word32And(bitfield3, |
| __ Int32Constant(Map::IsDeprecatedBit::kMask)), |
| __ Int32Constant(0)); |
| __ DeoptimizeIf(DeoptimizeReason::kWrongMap, p.feedback(), |
| if_not_deprecated, frame_state); |
| |
| Operator::Properties properties = Operator::kNoDeopt | Operator::kNoThrow; |
| Runtime::FunctionId id = Runtime::kTryMigrateInstance; |
| CallDescriptor const* desc = Linkage::GetRuntimeCallDescriptor( |
| graph()->zone(), id, 1, properties, CallDescriptor::kNoFlags); |
| Node* result = |
| __ Call(desc, __ CEntryStubConstant(1), value, |
| __ ExternalConstant(ExternalReference(id, isolate())), |
| __ Int32Constant(1), __ NoContextConstant()); |
| Node* check = ObjectIsSmi(result); |
| __ DeoptimizeIf(DeoptimizeReason::kInstanceMigrationFailed, p.feedback(), |
| check, frame_state); |
| } |
| |
| // Reload the current map of the {value}. |
| value_map = __ LoadField(AccessBuilder::ForMap(), value); |
| |
| // Perform the map checks again. |
| for (size_t i = 0; i < map_count; ++i) { |
| Node* map = __ HeapConstant(maps[i]); |
| Node* check = __ WordEqual(value_map, map); |
| if (i == map_count - 1) { |
| __ DeoptimizeIfNot(DeoptimizeReason::kWrongMap, p.feedback(), check, |
| frame_state); |
| } else { |
| __ GotoIf(check, &done); |
| } |
| } |
| |
| __ Goto(&done); |
| __ Bind(&done); |
| } else { |
| auto done = __ MakeLabel(); |
| |
| // Load the current map of the {value}. |
| Node* value_map = __ LoadField(AccessBuilder::ForMap(), value); |
| |
| for (size_t i = 0; i < map_count; ++i) { |
| Node* map = __ HeapConstant(maps[i]); |
| Node* check = __ WordEqual(value_map, map); |
| if (i == map_count - 1) { |
| __ DeoptimizeIfNot(DeoptimizeReason::kWrongMap, p.feedback(), check, |
| frame_state); |
| } else { |
| __ GotoIf(check, &done); |
| } |
| } |
| __ Goto(&done); |
| __ Bind(&done); |
| } |
| } |
| |
| Node* EffectControlLinearizer::LowerCompareMaps(Node* node) { |
| ZoneHandleSet<Map> const& maps = CompareMapsParametersOf(node->op()).maps(); |
| size_t const map_count = maps.size(); |
| Node* value = node->InputAt(0); |
| |
| auto done = __ MakeLabel(MachineRepresentation::kBit); |
| |
| // Load the current map of the {value}. |
| Node* value_map = __ LoadField(AccessBuilder::ForMap(), value); |
| |
| for (size_t i = 0; i < map_count; ++i) { |
| Node* map = __ HeapConstant(maps[i]); |
| Node* check = __ WordEqual(value_map, map); |
| __ GotoIf(check, &done, __ Int32Constant(1)); |
| } |
| __ Goto(&done, __ Int32Constant(0)); |
| |
| __ Bind(&done); |
| return done.PhiAt(0); |
| } |
| |
| Node* EffectControlLinearizer::LowerCheckNumber(Node* node, Node* frame_state) { |
| Node* value = node->InputAt(0); |
| const CheckParameters& params = CheckParametersOf(node->op()); |
| |
| auto if_not_smi = __ MakeDeferredLabel(); |
| auto done = __ MakeLabel(); |
| |
| Node* check0 = ObjectIsSmi(value); |
| __ GotoIfNot(check0, &if_not_smi); |
| __ Goto(&done); |
| |
| __ Bind(&if_not_smi); |
| Node* value_map = __ LoadField(AccessBuilder::ForMap(), value); |
| Node* check1 = __ WordEqual(value_map, __ HeapNumberMapConstant()); |
| __ DeoptimizeIfNot(DeoptimizeReason::kNotAHeapNumber, params.feedback(), |
| check1, frame_state); |
| __ Goto(&done); |
| |
| __ Bind(&done); |
| return value; |
| } |
| |
| Node* EffectControlLinearizer::LowerCheckReceiver(Node* node, |
| Node* frame_state) { |
| Node* value = node->InputAt(0); |
| |
| Node* value_map = __ LoadField(AccessBuilder::ForMap(), value); |
| Node* value_instance_type = |
| __ LoadField(AccessBuilder::ForMapInstanceType(), value_map); |
| |
| STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE); |
| Node* check = __ Uint32LessThanOrEqual( |
| __ Uint32Constant(FIRST_JS_RECEIVER_TYPE), value_instance_type); |
| __ DeoptimizeIfNot(DeoptimizeReason::kNotAJavaScriptObject, VectorSlotPair(), |
| check, frame_state); |
| return value; |
| } |
| |
| Node* EffectControlLinearizer::LowerCheckSymbol(Node* node, Node* frame_state) { |
| Node* value = node->InputAt(0); |
| |
| Node* value_map = __ LoadField(AccessBuilder::ForMap(), value); |
| |
| Node* check = |
| __ WordEqual(value_map, __ HeapConstant(factory()->symbol_map())); |
| __ DeoptimizeIfNot(DeoptimizeReason::kNotASymbol, VectorSlotPair(), check, |
| frame_state); |
| return value; |
| } |
| |
| Node* EffectControlLinearizer::LowerCheckString(Node* node, Node* frame_state) { |
| Node* value = node->InputAt(0); |
| const CheckParameters& params = CheckParametersOf(node->op()); |
| |
| Node* value_map = __ LoadField(AccessBuilder::ForMap(), value); |
| Node* value_instance_type = |
| __ LoadField(AccessBuilder::ForMapInstanceType(), value_map); |
| |
| Node* check = __ Uint32LessThan(value_instance_type, |
| __ Uint32Constant(FIRST_NONSTRING_TYPE)); |
| __ DeoptimizeIfNot(DeoptimizeReason::kWrongInstanceType, params.feedback(), |
| check, frame_state); |
| return value; |
| } |
| |
| Node* EffectControlLinearizer::LowerCheckSeqString(Node* node, |
| Node* frame_state) { |
| Node* value = node->InputAt(0); |
| |
| Node* value_map = __ LoadField(AccessBuilder::ForMap(), value); |
| Node* value_instance_type = |
| __ LoadField(AccessBuilder::ForMapInstanceType(), value_map); |
| |
| Node* check = __ Word32Equal( |
| __ Word32And( |
| value_instance_type, |
| __ Int32Constant(kStringRepresentationMask | kIsNotStringMask)), |
| __ Int32Constant(kSeqStringTag | kStringTag)); |
| __ DeoptimizeIfNot(DeoptimizeReason::kWrongInstanceType, VectorSlotPair(), |
| check, frame_state); |
| return value; |
| } |
| |
| Node* EffectControlLinearizer::LowerCheckInternalizedString(Node* node, |
| Node* frame_state) { |
| Node* value = node->InputAt(0); |
| |
| Node* value_map = __ LoadField(AccessBuilder::ForMap(), value); |
| Node* value_instance_type = |
| __ LoadField(AccessBuilder::ForMapInstanceType(), value_map); |
| |
| Node* check = __ Word32Equal( |
| __ Word32And(value_instance_type, |
| __ Int32Constant(kIsNotStringMask | kIsNotInternalizedMask)), |
| __ Int32Constant(kInternalizedTag)); |
| __ DeoptimizeIfNot(DeoptimizeReason::kWrongInstanceType, VectorSlotPair(), |
| check, frame_state); |
| |
| return value; |
| } |
| |
| void EffectControlLinearizer::LowerCheckIf(Node* node, Node* frame_state) { |
| Node* value = node->InputAt(0); |
| __ DeoptimizeIfNot(DeoptimizeKind::kEager, DeoptimizeReasonOf(node->op()), |
| VectorSlotPair(), value, frame_state); |
| } |
| |
| Node* EffectControlLinearizer::LowerCheckedInt32Add(Node* node, |
| Node* frame_state) { |
| Node* lhs = node->InputAt(0); |
| Node* rhs = node->InputAt(1); |
| |
| Node* value = __ Int32AddWithOverflow(lhs, rhs); |
| Node* check = __ Projection(1, value); |
| __ DeoptimizeIf(DeoptimizeReason::kOverflow, VectorSlotPair(), check, |
| frame_state); |
| return __ Projection(0, value); |
| } |
| |
| Node* EffectControlLinearizer::LowerCheckedInt32Sub(Node* node, |
| Node* frame_state) { |
| Node* lhs = node->InputAt(0); |
| Node* rhs = node->InputAt(1); |
| |
| Node* value = __ Int32SubWithOverflow(lhs, rhs); |
| Node* check = __ Projection(1, value); |
| __ DeoptimizeIf(DeoptimizeReason::kOverflow, VectorSlotPair(), check, |
| frame_state); |
| return __ Projection(0, value); |
| } |
| |
| Node* EffectControlLinearizer::LowerCheckedInt32Div(Node* node, |
| Node* frame_state) { |
| Node* lhs = node->InputAt(0); |
| Node* rhs = node->InputAt(1); |
| |
| auto if_not_positive = __ MakeDeferredLabel(); |
| auto if_is_minint = __ MakeDeferredLabel(); |
| auto done = __ MakeLabel(MachineRepresentation::kWord32); |
| auto minint_check_done = __ MakeLabel(); |
| |
| Node* zero = __ Int32Constant(0); |
| |
| // Check if {rhs} is positive (and not zero). |
| Node* check0 = __ Int32LessThan(zero, rhs); |
| __ GotoIfNot(check0, &if_not_positive); |
| |
| // Fast case, no additional checking required. |
| __ Goto(&done, __ Int32Div(lhs, rhs)); |
| |
| { |
| __ Bind(&if_not_positive); |
| |
| // Check if {rhs} is zero. |
| Node* check = __ Word32Equal(rhs, zero); |
| __ DeoptimizeIf(DeoptimizeReason::kDivisionByZero, VectorSlotPair(), check, |
| frame_state); |
| |
| // Check if {lhs} is zero, as that would produce minus zero. |
| check = __ Word32Equal(lhs, zero); |
| __ DeoptimizeIf(DeoptimizeReason::kMinusZero, VectorSlotPair(), check, |
| frame_state); |
| |
| // Check if {lhs} is kMinInt and {rhs} is -1, in which case we'd have |
| // to return -kMinInt, which is not representable. |
| Node* minint = __ Int32Constant(std::numeric_limits<int32_t>::min()); |
| Node* check1 = graph()->NewNode(machine()->Word32Equal(), lhs, minint); |
| __ GotoIf(check1, &if_is_minint); |
| __ Goto(&minint_check_done); |
| |
| __ Bind(&if_is_minint); |
| // Check if {rhs} is -1. |
| Node* minusone = __ Int32Constant(-1); |
| Node* is_minus_one = __ Word32Equal(rhs, minusone); |
| __ DeoptimizeIf(DeoptimizeReason::kOverflow, VectorSlotPair(), is_minus_one, |
| frame_state); |
| __ Goto(&minint_check_done); |
| |
| __ Bind(&minint_check_done); |
| // Perform the actual integer division. |
| __ Goto(&done, __ Int32Div(lhs, rhs)); |
| } |
| |
| __ Bind(&done); |
| Node* value = done.PhiAt(0); |
| |
| // Check if the remainder is non-zero. |
| Node* check = __ Word32Equal(lhs, __ Int32Mul(rhs, value)); |
| __ DeoptimizeIfNot(DeoptimizeReason::kLostPrecision, VectorSlotPair(), check, |
| frame_state); |
| |
| return value; |
| } |
| |
| Node* EffectControlLinearizer::LowerCheckedInt32Mod(Node* node, |
| Node* frame_state) { |
| // General case for signed integer modulus, with optimization for (unknown) |
| // power of 2 right hand side. |
| // |
| // if rhs <= 0 then |
| // rhs = -rhs |
| // deopt if rhs == 0 |
| // if lhs < 0 then |
| // let res = lhs % rhs in |
| // deopt if res == 0 |
| // res |
| // else |
| // let msk = rhs - 1 in |
| // if rhs & msk == 0 then |
| // lhs & msk |
| // else |
| // lhs % rhs |
| // |
| Node* lhs = node->InputAt(0); |
| Node* rhs = node->InputAt(1); |
| |
| auto if_rhs_not_positive = __ MakeDeferredLabel(); |
| auto if_lhs_negative = __ MakeDeferredLabel(); |
| auto if_power_of_two = __ MakeLabel(); |
| auto rhs_checked = __ MakeLabel(MachineRepresentation::kWord32); |
| auto done = __ MakeLabel(MachineRepresentation::kWord32); |
| |
| Node* zero = __ Int32Constant(0); |
| |
| // Check if {rhs} is not strictly positive. |
| Node* check0 = __ Int32LessThanOrEqual(rhs, zero); |
| __ GotoIf(check0, &if_rhs_not_positive); |
| __ Goto(&rhs_checked, rhs); |
| |
| __ Bind(&if_rhs_not_positive); |
| { |
| // Negate {rhs}, might still produce a negative result in case of |
| // -2^31, but that is handled safely below. |
| Node* vtrue0 = __ Int32Sub(zero, rhs); |
| |
| // Ensure that {rhs} is not zero, otherwise we'd have to return NaN. |
| Node* check = __ Word32Equal(vtrue0, zero); |
| __ DeoptimizeIf(DeoptimizeReason::kDivisionByZero, VectorSlotPair(), check, |
| frame_state); |
| __ Goto(&rhs_checked, vtrue0); |
| } |
| |
| __ Bind(&rhs_checked); |
| rhs = rhs_checked.PhiAt(0); |
| |
| // Check if {lhs} is negative. |
| Node* check1 = __ Int32LessThan(lhs, zero); |
| __ GotoIf(check1, &if_lhs_negative); |
| |
| // {lhs} non-negative. |
| { |
| Node* one = __ Int32Constant(1); |
| Node* msk = __ Int32Sub(rhs, one); |
| |
| // Check if {rhs} minus one is a valid mask. |
| Node* check2 = __ Word32Equal(__ Word32And(rhs, msk), zero); |
| __ GotoIf(check2, &if_power_of_two); |
| // Compute the remainder using the generic {lhs % rhs}. |
| __ Goto(&done, __ Int32Mod(lhs, rhs)); |
| |
| __ Bind(&if_power_of_two); |
| // Compute the remainder using {lhs & msk}. |
| __ Goto(&done, __ Word32And(lhs, msk)); |
| } |
| |
| __ Bind(&if_lhs_negative); |
| { |
| // Compute the remainder using {lhs % msk}. |
| Node* vtrue1 = __ Int32Mod(lhs, rhs); |
| |
| // Check if we would have to return -0. |
| Node* check = __ Word32Equal(vtrue1, zero); |
| __ DeoptimizeIf(DeoptimizeReason::kMinusZero, VectorSlotPair(), check, |
| frame_state); |
| __ Goto(&done, vtrue1); |
| } |
| |
| __ Bind(&done); |
| return done.PhiAt(0); |
| } |
| |
| Node* EffectControlLinearizer::LowerCheckedUint32Div(Node* node, |
| Node* frame_state) { |
| Node* lhs = node->InputAt(0); |
| Node* rhs = node->InputAt(1); |
| |
| Node* zero = __ Int32Constant(0); |
| |
| // Ensure that {rhs} is not zero, otherwise we'd have to return NaN. |
| Node* check = __ Word32Equal(rhs, zero); |
| __ DeoptimizeIf(DeoptimizeReason::kDivisionByZero, VectorSlotPair(), check, |
| frame_state); |
| |
| // Perform the actual unsigned integer division. |
| Node* value = __ Uint32Div(lhs, rhs); |
| |
| // Check if the remainder is non-zero. |
| check = __ Word32Equal(lhs, __ Int32Mul(rhs, value)); |
| __ DeoptimizeIfNot(DeoptimizeReason::kLostPrecision, VectorSlotPair(), check, |
| frame_state); |
| return value; |
| } |
| |
| Node* EffectControlLinearizer::LowerCheckedUint32Mod(Node* node, |
| Node* frame_state) { |
| Node* lhs = node->InputAt(0); |
| Node* rhs = node->InputAt(1); |
| |
| Node* zero = __ Int32Constant(0); |
| |
| // Ensure that {rhs} is not zero, otherwise we'd have to return NaN. |
| Node* check = __ Word32Equal(rhs, zero); |
| __ DeoptimizeIf(DeoptimizeReason::kDivisionByZero, VectorSlotPair(), check, |
| frame_state); |
| |
| // Perform the actual unsigned integer modulus. |
| return __ Uint32Mod(lhs, rhs); |
| } |
| |
| Node* EffectControlLinearizer::LowerCheckedInt32Mul(Node* node, |
| Node* frame_state) { |
| CheckForMinusZeroMode mode = CheckMinusZeroModeOf(node->op()); |
| Node* lhs = node->InputAt(0); |
| Node* rhs = node->InputAt(1); |
| |
| Node* projection = __ Int32MulWithOverflow(lhs, rhs); |
| Node* check = __ Projection(1, projection); |
| __ DeoptimizeIf(DeoptimizeReason::kOverflow, VectorSlotPair(), check, |
| frame_state); |
| |
| Node* value = __ Projection(0, projection); |
| |
| if (mode == CheckForMinusZeroMode::kCheckForMinusZero) { |
| auto if_zero = __ MakeDeferredLabel(); |
| auto check_done = __ MakeLabel(); |
| Node* zero = __ Int32Constant(0); |
| Node* check_zero = __ Word32Equal(value, zero); |
| __ GotoIf(check_zero, &if_zero); |
| __ Goto(&check_done); |
| |
| __ Bind(&if_zero); |
| // We may need to return negative zero. |
| Node* check_or = __ Int32LessThan(__ Word32Or(lhs, rhs), zero); |
| __ DeoptimizeIf(DeoptimizeReason::kMinusZero, VectorSlotPair(), check_or, |
| frame_state); |
| __ Goto(&check_done); |
| |
| __ Bind(&check_done); |
| } |
| |
| return value; |
| } |
| |
| Node* EffectControlLinearizer::LowerCheckedInt32ToTaggedSigned( |
| Node* node, Node* frame_state) { |
| DCHECK(SmiValuesAre31Bits()); |
| Node* value = node->InputAt(0); |
| const CheckParameters& params = CheckParametersOf(node->op()); |
| |
| Node* add = __ Int32AddWithOverflow(value, value); |
| Node* check = __ Projection(1, add); |
| __ DeoptimizeIf(DeoptimizeReason::kOverflow, params.feedback(), check, |
| frame_state); |
| return __ Projection(0, add); |
| } |
| |
| Node* EffectControlLinearizer::LowerCheckedUint32ToInt32(Node* node, |
| Node* frame_state) { |
| Node* value = node->InputAt(0); |
| const CheckParameters& params = CheckParametersOf(node->op()); |
| Node* unsafe = __ Int32LessThan(value, __ Int32Constant(0)); |
| __ DeoptimizeIf(DeoptimizeReason::kLostPrecision, params.feedback(), unsafe, |
| frame_state); |
| return value; |
| } |
| |
| Node* EffectControlLinearizer::LowerCheckedUint32ToTaggedSigned( |
| Node* node, Node* frame_state) { |
| Node* value = node->InputAt(0); |
| const CheckParameters& params = CheckParametersOf(node->op()); |
| Node* check = __ Uint32LessThanOrEqual(value, SmiMaxValueConstant()); |
| __ DeoptimizeIfNot(DeoptimizeReason::kLostPrecision, params.feedback(), check, |
| frame_state); |
| return ChangeUint32ToSmi(value); |
| } |
| |
| Node* EffectControlLinearizer::BuildCheckedFloat64ToInt32( |
| CheckForMinusZeroMode mode, const VectorSlotPair& feedback, Node* value, |
| Node* frame_state) { |
| Node* value32 = __ RoundFloat64ToInt32(value); |
| Node* check_same = __ Float64Equal(value, __ ChangeInt32ToFloat64(value32)); |
| __ DeoptimizeIfNot(DeoptimizeReason::kLostPrecisionOrNaN, feedback, |
| check_same, frame_state); |
| |
| if (mode == CheckForMinusZeroMode::kCheckForMinusZero) { |
| // Check if {value} is -0. |
| auto if_zero = __ MakeDeferredLabel(); |
| auto check_done = __ MakeLabel(); |
| |
| Node* check_zero = __ Word32Equal(value32, __ Int32Constant(0)); |
| __ GotoIf(check_zero, &if_zero); |
| __ Goto(&check_done); |
| |
| __ Bind(&if_zero); |
| // In case of 0, we need to check the high bits for the IEEE -0 pattern. |
| Node* check_negative = __ Int32LessThan(__ Float64ExtractHighWord32(value), |
| __ Int32Constant(0)); |
| __ DeoptimizeIf(DeoptimizeReason::kMinusZero, feedback, check_negative, |
| frame_state); |
| __ Goto(&check_done); |
| |
| __ Bind(&check_done); |
| } |
| return value32; |
| } |
| |
| Node* EffectControlLinearizer::LowerCheckedFloat64ToInt32(Node* node, |
| Node* frame_state) { |
| const CheckMinusZeroParameters& params = |
| CheckMinusZeroParametersOf(node->op()); |
| Node* value = node->InputAt(0); |
| return BuildCheckedFloat64ToInt32(params.mode(), params.feedback(), value, |
| frame_state); |
| } |
| |
| Node* EffectControlLinearizer::LowerCheckedTaggedSignedToInt32( |
| Node* node, Node* frame_state) { |
| Node* value = node->InputAt(0); |
| const CheckParameters& params = CheckParametersOf(node->op()); |
| Node* check = ObjectIsSmi(value); |
| __ DeoptimizeIfNot(DeoptimizeReason::kNotASmi, params.feedback(), check, |
| frame_state); |
| return ChangeSmiToInt32(value); |
| } |
| |
| Node* EffectControlLinearizer::LowerCheckedTaggedToInt32(Node* node, |
| Node* frame_state) { |
| const CheckMinusZeroParameters& params = |
| CheckMinusZeroParametersOf(node->op()); |
| Node* value = node->InputAt(0); |
| |
| auto if_not_smi = __ MakeDeferredLabel(); |
| auto done = __ MakeLabel(MachineRepresentation::kWord32); |
| |
| Node* check = ObjectIsSmi(value); |
| __ GotoIfNot(check, &if_not_smi); |
| // In the Smi case, just convert to int32. |
| __ Goto(&done, ChangeSmiToInt32(value)); |
| |
| // In the non-Smi case, check the heap numberness, load the number and convert |
| // to int32. |
| __ Bind(&if_not_smi); |
| Node* value_map = __ LoadField(AccessBuilder::ForMap(), value); |
| Node* check_map = __ WordEqual(value_map, __ HeapNumberMapConstant()); |
| __ DeoptimizeIfNot(DeoptimizeReason::kNotAHeapNumber, params.feedback(), |
| check_map, frame_state); |
| Node* vfalse = __ LoadField(AccessBuilder::ForHeapNumberValue(), value); |
| vfalse = BuildCheckedFloat64ToInt32(params.mode(), params.feedback(), vfalse, |
| frame_state); |
| __ Goto(&done, vfalse); |
| |
| __ Bind(&done); |
| return done.PhiAt(0); |
| } |
| |
| Node* EffectControlLinearizer::BuildCheckedHeapNumberOrOddballToFloat64( |
| CheckTaggedInputMode mode, const VectorSlotPair& feedback, Node* value, |
| Node* frame_state) { |
| Node* value_map = __ LoadField(AccessBuilder::ForMap(), value); |
| Node* check_number = __ WordEqual(value_map, __ HeapNumberMapConstant()); |
| switch (mode) { |
| case CheckTaggedInputMode::kNumber: { |
| __ DeoptimizeIfNot(DeoptimizeReason::kNotAHeapNumber, feedback, |
| check_number, frame_state); |
| break; |
| } |
| case CheckTaggedInputMode::kNumberOrOddball: { |
| auto check_done = __ MakeLabel(); |
| |
| __ GotoIf(check_number, &check_done); |
| // For oddballs also contain the numeric value, let us just check that |
| // we have an oddball here. |
| Node* instance_type = |
| __ LoadField(AccessBuilder::ForMapInstanceType(), value_map); |
| Node* check_oddball = |
| __ Word32Equal(instance_type, __ Int32Constant(ODDBALL_TYPE)); |
| __ DeoptimizeIfNot(DeoptimizeReason::kNotANumberOrOddball, feedback, |
| check_oddball, frame_state); |
| STATIC_ASSERT(HeapNumber::kValueOffset == Oddball::kToNumberRawOffset); |
| __ Goto(&check_done); |
| |
| __ Bind(&check_done); |
| break; |
| } |
| } |
| return __ LoadField(AccessBuilder::ForHeapNumberValue(), value); |
| } |
| |
| Node* EffectControlLinearizer::LowerCheckedTaggedToFloat64(Node* node, |
| Node* frame_state) { |
| CheckTaggedInputMode mode = CheckTaggedInputModeOf(node->op()); |
| Node* value = node->InputAt(0); |
| |
| auto if_smi = __ MakeLabel(); |
| auto done = __ MakeLabel(MachineRepresentation::kFloat64); |
| |
| Node* check = ObjectIsSmi(value); |
| __ GotoIf(check, &if_smi); |
| |
| // In the Smi case, just convert to int32 and then float64. |
| // Otherwise, check heap numberness and load the number. |
| Node* number = BuildCheckedHeapNumberOrOddballToFloat64( |
| mode, VectorSlotPair(), value, frame_state); |
| __ Goto(&done, number); |
| |
| __ Bind(&if_smi); |
| Node* from_smi = ChangeSmiToInt32(value); |
| from_smi = __ ChangeInt32ToFloat64(from_smi); |
| __ Goto(&done, from_smi); |
| |
| __ Bind(&done); |
| return done.PhiAt(0); |
| } |
| |
| Node* EffectControlLinearizer::LowerCheckedTaggedToTaggedSigned( |
| Node* node, Node* frame_state) { |
| Node* value = node->InputAt(0); |
| const CheckParameters& params = CheckParametersOf(node->op()); |
| |
| Node* check = ObjectIsSmi(value); |
| __ DeoptimizeIfNot(DeoptimizeReason::kNotASmi, params.feedback(), check, |
| frame_state); |
| |
| return value; |
| } |
| |
| Node* EffectControlLinearizer::LowerCheckedTaggedToTaggedPointer( |
| Node* node, Node* frame_state) { |
| Node* value = node->InputAt(0); |
| const CheckParameters& params = CheckParametersOf(node->op()); |
| |
| Node* check = ObjectIsSmi(value); |
| __ DeoptimizeIf(DeoptimizeReason::kSmi, params.feedback(), check, |
| frame_state); |
| return value; |
| } |
| |
| Node* EffectControlLinearizer::LowerTruncateTaggedToWord32(Node* node) { |
| Node* value = node->InputAt(0); |
| |
| auto if_not_smi = __ MakeDeferredLabel(); |
| auto done = __ MakeLabel(MachineRepresentation::kWord32); |
| |
| Node* check = ObjectIsSmi(value); |
| __ GotoIfNot(check, &if_not_smi); |
| __ Goto(&done, ChangeSmiToInt32(value)); |
| |
| __ Bind(&if_not_smi); |
| STATIC_ASSERT(HeapNumber::kValueOffset == Oddball::kToNumberRawOffset); |
| Node* vfalse = __ LoadField(AccessBuilder::ForHeapNumberValue(), value); |
| vfalse = __ TruncateFloat64ToWord32(vfalse); |
| __ Goto(&done, vfalse); |
| |
| __ Bind(&done); |
| return done.PhiAt(0); |
| } |
| |
| Node* EffectControlLinearizer::LowerCheckedTruncateTaggedToWord32( |
| Node* node, Node* frame_state) { |
| const CheckTaggedInputParameters& params = |
| CheckTaggedInputParametersOf(node->op()); |
| Node* value = node->InputAt(0); |
| |
| auto if_not_smi = __ MakeLabel(); |
| auto done = __ MakeLabel(MachineRepresentation::kWord32); |
| |
| Node* check = ObjectIsSmi(value); |
| __ GotoIfNot(check, &if_not_smi); |
| // In the Smi case, just convert to int32. |
| __ Goto(&done, ChangeSmiToInt32(value)); |
| |
| // Otherwise, check that it's a heap number or oddball and truncate the value |
| // to int32. |
| __ Bind(&if_not_smi); |
| Node* number = BuildCheckedHeapNumberOrOddballToFloat64( |
| params.mode(), params.feedback(), value, frame_state); |
| number = __ TruncateFloat64ToWord32(number); |
| __ Goto(&done, number); |
| |
| __ Bind(&done); |
| return done.PhiAt(0); |
| } |
| |
| Node* EffectControlLinearizer::LowerAllocate(Node* node) { |
| Node* size = node->InputAt(0); |
| PretenureFlag pretenure = PretenureFlagOf(node->op()); |
| Node* new_node = __ Allocate(pretenure, size); |
| return new_node; |
| } |
| |
| Node* EffectControlLinearizer::LowerNumberToString(Node* node) { |
| Node* argument = node->InputAt(0); |
| |
| Callable const callable = |
| Builtins::CallableFor(isolate(), Builtins::kNumberToString); |
| Operator::Properties properties = Operator::kEliminatable; |
| CallDescriptor::Flags flags = CallDescriptor::kNoFlags; |
| CallDescriptor* desc = Linkage::GetStubCallDescriptor( |
| isolate(), graph()->zone(), callable.descriptor(), 0, flags, properties); |
| return __ Call(desc, __ HeapConstant(callable.code()), argument, |
| __ NoContextConstant()); |
| } |
| |
| Node* EffectControlLinearizer::LowerObjectIsArrayBufferView(Node* node) { |
| Node* value = node->InputAt(0); |
| |
| auto if_smi = __ MakeDeferredLabel(); |
| auto done = __ MakeLabel(MachineRepresentation::kBit); |
| |
| Node* check = ObjectIsSmi(value); |
| __ GotoIf(check, &if_smi); |
| |
| Node* value_map = __ LoadField(AccessBuilder::ForMap(), value); |
| Node* value_instance_type = |
| __ LoadField(AccessBuilder::ForMapInstanceType(), value_map); |
| STATIC_ASSERT(JS_TYPED_ARRAY_TYPE + 1 == JS_DATA_VIEW_TYPE); |
| Node* vfalse = __ Uint32LessThan( |
| __ Int32Sub(value_instance_type, __ Int32Constant(JS_TYPED_ARRAY_TYPE)), |
| __ Int32Constant(2)); |
| __ Goto(&done, vfalse); |
| |
| __ Bind(&if_smi); |
| __ Goto(&done, __ Int32Constant(0)); |
| |
| __ Bind(&done); |
| return done.PhiAt(0); |
| } |
| |
| Node* EffectControlLinearizer::LowerObjectIsBigInt(Node* node) { |
| Node* value = node->InputAt(0); |
| |
| auto if_smi = __ MakeDeferredLabel(); |
| auto done = __ MakeLabel(MachineRepresentation::kBit); |
| |
| Node* check = ObjectIsSmi(value); |
| __ GotoIf(check, &if_smi); |
| Node* value_map = __ LoadField(AccessBuilder::ForMap(), value); |
| Node* value_instance_type = |
| __ LoadField(AccessBuilder::ForMapInstanceType(), value_map); |
| Node* vfalse = |
| __ Word32Equal(value_instance_type, __ Uint32Constant(BIGINT_TYPE)); |
| __ Goto(&done, vfalse); |
| |
| __ Bind(&if_smi); |
| __ Goto(&done, __ Int32Constant(0)); |
| |
| __ Bind(&done); |
| return done.PhiAt(0); |
| } |
| |
| Node* EffectControlLinearizer::LowerObjectIsCallable(Node* node) { |
| Node* value = node->InputAt(0); |
| |
| auto if_smi = __ MakeDeferredLabel(); |
| auto done = __ MakeLabel(MachineRepresentation::kBit); |
| |
| Node* check = ObjectIsSmi(value); |
| __ GotoIf(check, &if_smi); |
| |
| Node* value_map = __ LoadField(AccessBuilder::ForMap(), value); |
| Node* value_bit_field = |
| __ LoadField(AccessBuilder::ForMapBitField(), value_map); |
| Node* vfalse = |
| __ Word32Equal(__ Int32Constant(Map::IsCallableBit::kMask), |
| __ Word32And(value_bit_field, |
| __ Int32Constant(Map::IsCallableBit::kMask))); |
| __ Goto(&done, vfalse); |
| |
| __ Bind(&if_smi); |
| __ Goto(&done, __ Int32Constant(0)); |
| |
| __ Bind(&done); |
| return done.PhiAt(0); |
| } |
| |
| Node* EffectControlLinearizer::LowerObjectIsConstructor(Node* node) { |
| Node* value = node->InputAt(0); |
| |
| auto if_smi = __ MakeDeferredLabel(); |
| auto done = __ MakeLabel(MachineRepresentation::kBit); |
| |
| Node* check = ObjectIsSmi(value); |
| __ GotoIf(check, &if_smi); |
| |
| Node* value_map = __ LoadField(AccessBuilder::ForMap(), value); |
| Node* value_bit_field = |
| __ LoadField(AccessBuilder::ForMapBitField(), value_map); |
| Node* vfalse = __ Word32Equal( |
| __ Int32Constant(Map::IsConstructorBit::kMask), |
| __ Word32And(value_bit_field, |
| __ Int32Constant(Map::IsConstructorBit::kMask))); |
| __ Goto(&done, vfalse); |
| |
| __ Bind(&if_smi); |
| __ Goto(&done, __ Int32Constant(0)); |
| |
| __ Bind(&done); |
| return done.PhiAt(0); |
| } |
| |
| Node* EffectControlLinearizer::LowerObjectIsDetectableCallable(Node* node) { |
| Node* value = node->InputAt(0); |
| |
| auto if_smi = __ MakeDeferredLabel(); |
| auto done = __ MakeLabel(MachineRepresentation::kBit); |
| |
| Node* check = ObjectIsSmi(value); |
| __ GotoIf(check, &if_smi); |
| |
| Node* value_map = __ LoadField(AccessBuilder::ForMap(), value); |
| Node* value_bit_field = |
| __ LoadField(AccessBuilder::ForMapBitField(), value_map); |
| Node* vfalse = __ Word32Equal( |
| __ Int32Constant(Map::IsCallableBit::kMask), |
| __ Word32And(value_bit_field, |
| __ Int32Constant((Map::IsCallableBit::kMask) | |
| (Map::IsUndetectableBit::kMask)))); |
| __ Goto(&done, vfalse); |
| |
| __ Bind(&if_smi); |
| __ Goto(&done, __ Int32Constant(0)); |
| |
| __ Bind(&done); |
| return done.PhiAt(0); |
| } |
| |
| Node* EffectControlLinearizer::LowerNumberIsFloat64Hole(Node* node) { |
| Node* value = node->InputAt(0); |
| Node* check = __ Word32Equal(__ Float64ExtractHighWord32(value), |
| __ Int32Constant(kHoleNanUpper32)); |
| return check; |
| } |
| |
| Node* EffectControlLinearizer::LowerObjectIsMinusZero(Node* node) { |
| Node* value = node->InputAt(0); |
| Node* zero = __ Int32Constant(0); |
| |
| auto done = __ MakeLabel(MachineRepresentation::kBit); |
| |
| // Check if {value} is a Smi. |
| __ GotoIf(ObjectIsSmi(value), &done, zero); |
| |
| // Check if {value} is a HeapNumber. |
| Node* value_map = __ LoadField(AccessBuilder::ForMap(), value); |
| __ GotoIfNot(__ WordEqual(value_map, __ HeapNumberMapConstant()), &done, |
| zero); |
| |
| // Check if {value} contains -0. |
| Node* value_value = __ LoadField(AccessBuilder::ForHeapNumberValue(), value); |
| __ Goto(&done, |
| __ Float64Equal( |
| __ Float64Div(__ Float64Constant(1.0), value_value), |
| __ Float64Constant(-std::numeric_limits<double>::infinity()))); |
| |
| __ Bind(&done); |
| return done.PhiAt(0); |
| } |
| |
| Node* EffectControlLinearizer::LowerObjectIsNaN(Node* node) { |
| Node* value = node->InputAt(0); |
| Node* zero = __ Int32Constant(0); |
| |
| auto done = __ MakeLabel(MachineRepresentation::kBit); |
| |
| // Check if {value} is a Smi. |
| __ GotoIf(ObjectIsSmi(value), &done, zero); |
| |
| // Check if {value} is a HeapNumber. |
| Node* value_map = __ LoadField(AccessBuilder::ForMap(), value); |
| __ GotoIfNot(__ WordEqual(value_map, __ HeapNumberMapConstant()), &done, |
| zero); |
| |
| // Check if {value} contains a NaN. |
| Node* value_value = __ LoadField(AccessBuilder::ForHeapNumberValue(), value); |
| __ Goto(&done, |
| __ Word32Equal(__ Float64Equal(value_value, value_value), zero)); |
| |
| __ Bind(&done); |
| return done.PhiAt(0); |
| } |
| |
| Node* EffectControlLinearizer::LowerObjectIsNonCallable(Node* node) { |
| Node* value = node->InputAt(0); |
| |
| auto if_primitive = __ MakeDeferredLabel(); |
| auto done = __ MakeLabel(MachineRepresentation::kBit); |
| |
| Node* check0 = ObjectIsSmi(value); |
| __ GotoIf(check0, &if_primitive); |
| |
| Node* value_map = __ LoadField(AccessBuilder::ForMap(), value); |
| Node* value_instance_type = |
| __ LoadField(AccessBuilder::ForMapInstanceType(), value_map); |
| STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE); |
| Node* check1 = __ Uint32LessThanOrEqual( |
| __ Uint32Constant(FIRST_JS_RECEIVER_TYPE), value_instance_type); |
| __ GotoIfNot(check1, &if_primitive); |
| |
| Node* value_bit_field = |
| __ LoadField(AccessBuilder::ForMapBitField(), value_map); |
| Node* check2 = |
| __ Word32Equal(__ Int32Constant(0), |
| __ Word32And(value_bit_field, |
| __ Int32Constant(Map::IsCallableBit::kMask))); |
| __ Goto(&done, check2); |
| |
| __ Bind(&if_primitive); |
| __ Goto(&done, __ Int32Constant(0)); |
| |
| __ Bind(&done); |
| return done.PhiAt(0); |
| } |
| |
| Node* EffectControlLinearizer::LowerObjectIsNumber(Node* node) { |
| Node* value = node->InputAt(0); |
| |
| auto if_smi = __ MakeLabel(); |
| auto done = __ MakeLabel(MachineRepresentation::kBit); |
| |
| __ GotoIf(ObjectIsSmi(value), &if_smi); |
| Node* value_map = __ LoadField(AccessBuilder::ForMap(), value); |
| __ Goto(&done, __ WordEqual(value_map, __ HeapNumberMapConstant())); |
| |
| __ Bind(&if_smi); |
| __ Goto(&done, __ Int32Constant(1)); |
| |
| __ Bind(&done); |
| return done.PhiAt(0); |
| } |
| |
| Node* EffectControlLinearizer::LowerObjectIsReceiver(Node* node) { |
| Node* value = node->InputAt(0); |
| |
| auto if_smi = __ MakeDeferredLabel(); |
| auto done = __ MakeLabel(MachineRepresentation::kBit); |
| |
| __ GotoIf(ObjectIsSmi(value), &if_smi); |
| |
| STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE); |
| Node* value_map = __ LoadField(AccessBuilder::ForMap(), value); |
| Node* value_instance_type = |
| __ LoadField(AccessBuilder::ForMapInstanceType(), value_map); |
| Node* result = __ Uint32LessThanOrEqual( |
| __ Uint32Constant(FIRST_JS_RECEIVER_TYPE), value_instance_type); |
| __ Goto(&done, result); |
| |
| __ Bind(&if_smi); |
| __ Goto(&done, __ Int32Constant(0)); |
| |
| __ Bind(&done); |
| return done.PhiAt(0); |
| } |
| |
| Node* EffectControlLinearizer::LowerObjectIsSmi(Node* node) { |
| Node* value = node->InputAt(0); |
| return ObjectIsSmi(value); |
| } |
| |
| Node* EffectControlLinearizer::LowerObjectIsString(Node* node) { |
| Node* value = node->InputAt(0); |
| |
| auto if_smi = __ MakeDeferredLabel(); |
| auto done = __ MakeLabel(MachineRepresentation::kBit); |
| |
| Node* check = ObjectIsSmi(value); |
| __ GotoIf(check, &if_smi); |
| Node* value_map = __ LoadField(AccessBuilder::ForMap(), value); |
| Node* value_instance_type = |
| __ LoadField(AccessBuilder::ForMapInstanceType(), value_map); |
| Node* vfalse = __ Uint32LessThan(value_instance_type, |
| __ Uint32Constant(FIRST_NONSTRING_TYPE)); |
| __ Goto(&done, vfalse); |
| |
| __ Bind(&if_smi); |
| __ Goto(&done, __ Int32Constant(0)); |
| |
| __ Bind(&done); |
| return done.PhiAt(0); |
| } |
| |
| Node* EffectControlLinearizer::LowerObjectIsSymbol(Node* node) { |
| Node* value = node->InputAt(0); |
| |
| auto if_smi = __ MakeDeferredLabel(); |
| auto done = __ MakeLabel(MachineRepresentation::kBit); |
| |
| Node* check = ObjectIsSmi(value); |
| __ GotoIf(check, &if_smi); |
| Node* value_map = __ LoadField(AccessBuilder::ForMap(), value); |
| Node* value_instance_type = |
| __ LoadField(AccessBuilder::ForMapInstanceType(), value_map); |
| Node* vfalse = |
| __ Word32Equal(value_instance_type, __ Uint32Constant(SYMBOL_TYPE)); |
| __ Goto(&done, vfalse); |
| |
| __ Bind(&if_smi); |
| __ Goto(&done, __ Int32Constant(0)); |
| |
| __ Bind(&done); |
| return done.PhiAt(0); |
| } |
| |
| Node* EffectControlLinearizer::LowerObjectIsUndetectable(Node* node) { |
| Node* value = node->InputAt(0); |
| |
| auto if_smi = __ MakeDeferredLabel(); |
| auto done = __ MakeLabel(MachineRepresentation::kBit); |
| |
| Node* check = ObjectIsSmi(value); |
| __ GotoIf(check, &if_smi); |
| |
| Node* value_map = __ LoadField(AccessBuilder::ForMap(), value); |
| Node* value_bit_field = |
| __ LoadField(AccessBuilder::ForMapBitField(), value_map); |
| Node* vfalse = __ Word32Equal( |
| __ Word32Equal( |
| __ Int32Constant(0), |
| __ Word32And(value_bit_field, |
| __ Int32Constant(Map::IsUndetectableBit::kMask))), |
| __ Int32Constant(0)); |
| __ Goto(&done, vfalse); |
| |
| __ Bind(&if_smi); |
| __ Goto(&done, __ Int32Constant(0)); |
| |
| __ Bind(&done); |
| return done.PhiAt(0); |
| } |
| |
| Node* EffectControlLinearizer::LowerTypeOf(Node* node) { |
| Node* obj = node->InputAt(0); |
| Callable const callable = Builtins::CallableFor(isolate(), Builtins::kTypeof); |
| Operator::Properties const properties = Operator::kEliminatable; |
| CallDescriptor::Flags const flags = CallDescriptor::kNoAllocate; |
| CallDescriptor* desc = Linkage::GetStubCallDescriptor( |
| isolate(), graph()->zone(), callable.descriptor(), 0, flags, properties); |
| return __ Call(desc, __ HeapConstant(callable.code()), obj, |
| __ NoContextConstant()); |
| } |
| |
| Node* EffectControlLinearizer::LowerClassOf(Node* node) { |
| Node* obj = node->InputAt(0); |
| Callable const callable = |
| Builtins::CallableFor(isolate(), Builtins::kClassOf); |
| Operator::Properties const properties = Operator::kEliminatable; |
| CallDescriptor::Flags const flags = CallDescriptor::kNoAllocate; |
| CallDescriptor* desc = Linkage::GetStubCallDescriptor( |
| isolate(), graph()->zone(), callable.descriptor(), 0, flags, properties); |
| return __ Call(desc, __ HeapConstant(callable.code()), obj, |
| __ NoContextConstant()); |
| } |
| |
| Node* EffectControlLinearizer::LowerToBoolean(Node* node) { |
| Node* obj = node->InputAt(0); |
| Callable const callable = |
| Builtins::CallableFor(isolate(), Builtins::kToBoolean); |
| Operator::Properties const properties = Operator::kEliminatable; |
| CallDescriptor::Flags const flags = CallDescriptor::kNoAllocate; |
| CallDescriptor* desc = Linkage::GetStubCallDescriptor( |
| isolate(), graph()->zone(), callable.descriptor(), 0, flags, properties); |
| return __ Call(desc, __ HeapConstant(callable.code()), obj, |
| __ NoContextConstant()); |
| } |
| |
| Node* EffectControlLinearizer::LowerArgumentsLength(Node* node) { |
| Node* arguments_frame = NodeProperties::GetValueInput(node, 0); |
| int formal_parameter_count = FormalParameterCountOf(node->op()); |
| bool is_rest_length = IsRestLengthOf(node->op()); |
| DCHECK_LE(0, formal_parameter_count); |
| |
| if (is_rest_length) { |
| // The ArgumentsLength node is computing the number of rest parameters, |
| // which is max(0, actual_parameter_count - formal_parameter_count). |
| // We have to distinguish the case, when there is an arguments adaptor frame |
| // (i.e., arguments_frame != LoadFramePointer()). |
| auto if_adaptor_frame = __ MakeLabel(); |
| auto done = __ MakeLabel(MachineRepresentation::kTaggedSigned); |
| |
| Node* frame = __ LoadFramePointer(); |
| __ GotoIf(__ WordEqual(arguments_frame, frame), &done, __ SmiConstant(0)); |
| __ Goto(&if_adaptor_frame); |
| |
| __ Bind(&if_adaptor_frame); |
| Node* arguments_length = __ Load( |
| MachineType::TaggedSigned(), arguments_frame, |
| __ IntPtrConstant(ArgumentsAdaptorFrameConstants::kLengthOffset)); |
| |
| Node* rest_length = |
| __ IntSub(arguments_length, __ SmiConstant(formal_parameter_count)); |
| __ GotoIf(__ IntLessThan(rest_length, __ SmiConstant(0)), &done, |
| __ SmiConstant(0)); |
| __ Goto(&done, rest_length); |
| |
| __ Bind(&done); |
| return done.PhiAt(0); |
| } else { |
| // The ArgumentsLength node is computing the actual number of arguments. |
| // We have to distinguish the case when there is an arguments adaptor frame |
| // (i.e., arguments_frame != LoadFramePointer()). |
| auto if_adaptor_frame = __ MakeLabel(); |
| auto done = __ MakeLabel(MachineRepresentation::kTaggedSigned); |
| |
| Node* frame = __ LoadFramePointer(); |
| __ GotoIf(__ WordEqual(arguments_frame, frame), &done, |
| __ SmiConstant(formal_parameter_count)); |
| __ Goto(&if_adaptor_frame); |
| |
| __ Bind(&if_adaptor_frame); |
| Node* arguments_length = __ Load( |
| MachineType::TaggedSigned(), arguments_frame, |
| __ IntPtrConstant(ArgumentsAdaptorFrameConstants::kLengthOffset)); |
| __ Goto(&done, arguments_length); |
| |
| __ Bind(&done); |
| return done.PhiAt(0); |
| } |
| } |
| |
| Node* EffectControlLinearizer::LowerArgumentsFrame(Node* node) { |
| auto done = __ MakeLabel(MachineType::PointerRepresentation()); |
| |
| Node* frame = __ LoadFramePointer(); |
| Node* parent_frame = |
| __ Load(MachineType::AnyTagged(), frame, |
| __ IntPtrConstant(StandardFrameConstants::kCallerFPOffset)); |
| Node* parent_frame_type = __ Load( |
| MachineType::AnyTagged(), parent_frame, |
| __ IntPtrConstant(CommonFrameConstants::kContextOrFrameTypeOffset)); |
| __ GotoIf(__ WordEqual(parent_frame_type, |
| __ IntPtrConstant(StackFrame::TypeToMarker( |
| StackFrame::ARGUMENTS_ADAPTOR))), |
| &done, parent_frame); |
| __ Goto(&done, frame); |
| |
| __ Bind(&done); |
| return done.PhiAt(0); |
| } |
| |
| Node* EffectControlLinearizer::LowerNewDoubleElements(Node* node) { |
| PretenureFlag const pretenure = PretenureFlagOf(node->op()); |
| Node* length = node->InputAt(0); |
| |
| // Compute the effective size of the backing store. |
| Node* size = |
| __ Int32Add(__ Word32Shl(length, __ Int32Constant(kDoubleSizeLog2)), |
| __ Int32Constant(FixedDoubleArray::kHeaderSize)); |
| |
| // Allocate the result and initialize the header. |
| Node* result = __ Allocate(pretenure, size); |
| __ StoreField(AccessBuilder::ForMap(), result, |
| __ FixedDoubleArrayMapConstant()); |
| __ StoreField(AccessBuilder::ForFixedArrayLength(), result, |
| ChangeInt32ToSmi(length)); |
| |
| // Initialize the backing store with holes. |
| STATIC_ASSERT(HeapNumber::kValueOffset == Oddball::kToNumberRawOffset); |
| Node* limit = ChangeUint32ToUintPtr(length); |
| Node* the_hole = |
| __ LoadField(AccessBuilder::ForHeapNumberValue(), __ TheHoleConstant()); |
| auto loop = __ MakeLoopLabel(MachineType::PointerRepresentation()); |
| auto done_loop = __ MakeLabel(); |
| __ Goto(&loop, __ IntPtrConstant(0)); |
| __ Bind(&loop); |
| { |
| // Check if we've initialized everything. |
| Node* index = loop.PhiAt(0); |
| Node* check = __ UintLessThan(index, limit); |
| __ GotoIfNot(check, &done_loop); |
| |
| // Storing "the_hole" doesn't need a write barrier. |
| StoreRepresentation rep(MachineRepresentation::kFloat64, kNoWriteBarrier); |
| Node* offset = __ IntAdd( |
| __ WordShl(index, __ IntPtrConstant(kDoubleSizeLog2)), |
| __ IntPtrConstant(FixedDoubleArray::kHeaderSize - kHeapObjectTag)); |
| __ Store(rep, result, offset, the_hole); |
| |
| // Advance the {index}. |
| index = __ IntAdd(index, __ IntPtrConstant(1)); |
| __ Goto(&loop, index); |
| } |
| |
| __ Bind(&done_loop); |
| return result; |
| } |
| |
| Node* EffectControlLinearizer::LowerNewSmiOrObjectElements(Node* node) { |
| PretenureFlag const pretenure = PretenureFlagOf(node->op()); |
| Node* length = node->InputAt(0); |
| |
| // Compute the effective size of the backing store. |
| Node* size = |
| __ Int32Add(__ Word32Shl(length, __ Int32Constant(kPointerSizeLog2)), |
| __ Int32Constant(FixedArray::kHeaderSize)); |
| |
| // Allocate the result and initialize the header. |
| Node* result = __ Allocate(pretenure, size); |
| __ StoreField(AccessBuilder::ForMap(), result, __ FixedArrayMapConstant()); |
| __ StoreField(AccessBuilder::ForFixedArrayLength(), result, |
| ChangeInt32ToSmi(length)); |
| |
| // Initialize the backing store with holes. |
| Node* limit = ChangeUint32ToUintPtr(length); |
| Node* the_hole = __ TheHoleConstant(); |
| auto loop = __ MakeLoopLabel(MachineType::PointerRepresentation()); |
| auto done_loop = __ MakeLabel(); |
| __ Goto(&loop, __ IntPtrConstant(0)); |
| __ Bind(&loop); |
| { |
| // Check if we've initialized everything. |
| Node* index = loop.PhiAt(0); |
| Node* check = __ UintLessThan(index, limit); |
| __ GotoIfNot(check, &done_loop); |
| |
| // Storing "the_hole" doesn't need a write barrier. |
| StoreRepresentation rep(MachineRepresentation::kTagged, kNoWriteBarrier); |
| Node* offset = |
| __ IntAdd(__ WordShl(index, __ IntPtrConstant(kPointerSizeLog2)), |
| __ IntPtrConstant(FixedArray::kHeaderSize - kHeapObjectTag)); |
| __ Store(rep, result, offset, the_hole); |
| |
| // Advance the {index}. |
| index = __ IntAdd(index, __ IntPtrConstant(1)); |
| __ Goto(&loop, index); |
| } |
| |
| __ Bind(&done_loop); |
| return result; |
| } |
| |
| Node* EffectControlLinearizer::LowerNewArgumentsElements(Node* node) { |
| Node* frame = NodeProperties::GetValueInput(node, 0); |
| Node* length = NodeProperties::GetValueInput(node, 1); |
| int mapped_count = OpParameter<int>(node); |
| |
| Callable const callable = |
| Builtins::CallableFor(isolate(), Builtins::kNewArgumentsElements); |
| Operator::Properties const properties = node->op()->properties(); |
| CallDescriptor::Flags const flags = CallDescriptor::kNoFlags; |
| CallDescriptor* desc = Linkage::GetStubCallDescriptor( |
| isolate(), graph()->zone(), callable.descriptor(), 0, flags, properties); |
| return __ Call(desc, __ HeapConstant(callable.code()), frame, length, |
| __ SmiConstant(mapped_count), __ NoContextConstant()); |
| } |
| |
| Node* EffectControlLinearizer::LowerNewConsString(Node* node) { |
| Node* length = node->InputAt(0); |
| Node* first = node->InputAt(1); |
| Node* second = node->InputAt(2); |
| |
| // Determine the instance types of {first} and {second}. |
| Node* first_map = __ LoadField(AccessBuilder::ForMap(), first); |
| Node* first_instance_type = |
| __ LoadField(AccessBuilder::ForMapInstanceType(), first_map); |
| Node* second_map = __ LoadField(AccessBuilder::ForMap(), second); |
| Node* second_instance_type = |
| __ LoadField(AccessBuilder::ForMapInstanceType(), second_map); |
| |
| // Determine the proper map for the resulting ConsString. |
| // If both {first} and {second} are one-byte strings, we |
| // create a new ConsOneByteString, otherwise we create a |
| // new ConsString instead. |
| auto if_onebyte = __ MakeLabel(); |
| auto if_twobyte = __ MakeLabel(); |
| auto done = __ MakeLabel(MachineRepresentation::kTaggedPointer); |
| STATIC_ASSERT(kOneByteStringTag != 0); |
| STATIC_ASSERT(kTwoByteStringTag == 0); |
| Node* instance_type = __ Word32And(first_instance_type, second_instance_type); |
| Node* encoding = |
| __ Word32And(instance_type, __ Int32Constant(kStringEncodingMask)); |
| __ Branch(__ Word32Equal(encoding, __ Int32Constant(kTwoByteStringTag)), |
| &if_twobyte, &if_onebyte); |
| __ Bind(&if_onebyte); |
| __ Goto(&done, |
| jsgraph()->HeapConstant(factory()->cons_one_byte_string_map())); |
| __ Bind(&if_twobyte); |
| __ Goto(&done, jsgraph()->HeapConstant(factory()->cons_string_map())); |
| __ Bind(&done); |
| Node* result_map = done.PhiAt(0); |
| |
| // Allocate the resulting ConsString. |
| Node* result = __ Allocate(NOT_TENURED, __ Int32Constant(ConsString::kSize)); |
| __ StoreField(AccessBuilder::ForMap(), result, result_map); |
| __ StoreField(AccessBuilder::ForNameHashField(), result, |
| jsgraph()->Int32Constant(Name::kEmptyHashField)); |
| __ StoreField(AccessBuilder::ForStringLength(), result, length); |
| __ StoreField(AccessBuilder::ForConsStringFirst(), result, first); |
| __ StoreField(AccessBuilder::ForConsStringSecond(), result, second); |
| return result; |
| } |
| |
| Node* EffectControlLinearizer::LowerArrayBufferWasNeutered(Node* node) { |
| Node* value = node->InputAt(0); |
| |
| Node* value_bit_field = |
| __ LoadField(AccessBuilder::ForJSArrayBufferBitField(), value); |
| return __ Word32Equal( |
| __ Word32Equal( |
| __ Word32And(value_bit_field, |
| __ Int32Constant(JSArrayBuffer::WasNeutered::kMask)), |
| __ Int32Constant(0)), |
| __ Int32Constant(0)); |
| } |
| |
| Node* EffectControlLinearizer::LowerSameValue(Node* node) { |
| Node* lhs = node->InputAt(0); |
| Node* rhs = node->InputAt(1); |
| |
| Callable const callable = |
| Builtins::CallableFor(isolate(), Builtins::kSameValue); |
| Operator::Properties properties = Operator::kEliminatable; |
| CallDescriptor::Flags flags = CallDescriptor::kNoFlags; |
| CallDescriptor* desc = Linkage::GetStubCallDescriptor( |
| isolate(), graph()->zone(), callable.descriptor(), 0, flags, properties); |
| return __ Call(desc, __ HeapConstant(callable.code()), lhs, rhs, |
| __ NoContextConstant()); |
| } |
| |
| Node* EffectControlLinearizer::LowerDeadValue(Node* node) { |
| Node* input = NodeProperties::GetValueInput(node, 0); |
| if (input->opcode() != IrOpcode::kUnreachable) { |
| Node* unreachable = __ Unreachable(); |
| NodeProperties::ReplaceValueInput(node, unreachable, 0); |
| } |
| return node; |
| } |
| |
| Node* EffectControlLinearizer::LowerStringToNumber(Node* node) { |
| Node* string = node->InputAt(0); |
| |
| Callable const callable = |
| Builtins::CallableFor(isolate(), Builtins::kStringToNumber); |
| Operator::Properties properties = Operator::kEliminatable; |
| CallDescriptor::Flags flags = CallDescriptor::kNoFlags; |
| CallDescriptor* desc = Linkage::GetStubCallDescriptor( |
| isolate(), graph()->zone(), callable.descriptor(), 0, flags, properties); |
| return __ Call(desc, __ HeapConstant(callable.code()), string, |
| __ NoContextConstant()); |
| } |
| |
| Node* EffectControlLinearizer::LowerStringCharAt(Node* node) { |
| Node* receiver = node->InputAt(0); |
| Node* position = node->InputAt(1); |
| |
| Callable const callable = |
| Builtins::CallableFor(isolate(), Builtins::kStringCharAt); |
| Operator::Properties properties = Operator::kNoThrow | Operator::kNoWrite; |
| CallDescriptor::Flags flags = CallDescriptor::kNoFlags; |
| CallDescriptor* desc = Linkage::GetStubCallDescriptor( |
| isolate(), graph()->zone(), callable.descriptor(), 0, flags, properties); |
| return __ Call(desc, __ HeapConstant(callable.code()), receiver, position, |
| __ NoContextConstant()); |
| } |
| |
| Node* EffectControlLinearizer::LowerStringCharCodeAt(Node* node) { |
| Node* receiver = node->InputAt(0); |
| Node* position = node->InputAt(1); |
| |
| Callable const callable = |
| Builtins::CallableFor(isolate(), Builtins::kStringCharCodeAt); |
| Operator::Properties properties = Operator::kNoThrow | Operator::kNoWrite; |
| CallDescriptor::Flags flags = CallDescriptor::kNoFlags; |
| CallDescriptor* desc = Linkage::GetStubCallDescriptor( |
| isolate(), graph()->zone(), callable.descriptor(), 0, flags, properties, |
| MachineType::TaggedSigned()); |
| return __ Call(desc, __ HeapConstant(callable.code()), receiver, position, |
| __ NoContextConstant()); |
| } |
| |
| Node* EffectControlLinearizer::LowerStringCodePointAt(Node* node) { |
| Node* receiver = node->InputAt(0); |
| Node* position = node->InputAt(1); |
| |
| Callable const callable = |
| Builtins::CallableFor(isolate(), Builtins::kStringCodePointAt); |
| Operator::Properties properties = Operator::kNoThrow | Operator::kNoWrite; |
| CallDescriptor::Flags flags = CallDescriptor::kNoFlags; |
| CallDescriptor* desc = Linkage::GetStubCallDescriptor( |
| isolate(), graph()->zone(), callable.descriptor(), 0, flags, properties, |
| MachineType::TaggedSigned()); |
| return __ Call(desc, __ HeapConstant(callable.code()), receiver, position, |
| __ NoContextConstant()); |
| } |
| |
| |