blob: 4c28f0045599b0e0e6880633c8133dd6c4125a8b [file] [log] [blame]
// 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::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) {
FATAL("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::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);
break;
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::kStringCharCodeAt:
result = LowerStringCharCodeAt(node);
break;
case IrOpcode::kStringCodePointAt:
result = LowerStringCodePointAt(node, UnicodeEncodingOf(node->op()));
break;
case IrOpcode::kStringToLowerCaseIntl:
result = LowerStringToLowerCaseIntl(node);
break;
case IrOpcode::kStringToUpperCaseIntl:
result = LowerStringToUpperCaseIntl(node);
break;
case IrOpcode::kStringSubstring:
result = LowerStringSubstring(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::kNumberIsFinite:
result = LowerNumberIsFinite(node);
break;
case IrOpcode::kObjectIsFiniteNumber:
result = LowerObjectIsFiniteNumber(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()) {
FATAL(
"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;
auto call_descriptor = Linkage::GetRuntimeCallDescriptor(
graph()->zone(), id, 1, properties, CallDescriptor::kNoFlags);
Node* result =
__ Call(call_descriptor, __ 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::kNotAString, params.feedback(), 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) {
CheckTaggedInputParameters const& p =
CheckTaggedInputParametersOf(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(
p.mode(), p.feedback(), 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;
auto call_descriptor = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), callable.descriptor(), 0, flags, properties);
return __ Call(call_descriptor, __ 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::LowerNumberIsFinite(Node* node) {
Node* number = node->InputAt(0);
Node* diff = __ Float64Sub(number, number);
Node* check = __ Float64Equal(diff, diff);
return check;
}
Node* EffectControlLinearizer::LowerObjectIsFiniteNumber(Node* node) {
Node* object = node->InputAt(0);
Node* zero = __ Int32Constant(0);
Node* one = __ Int32Constant(1);
auto done = __ MakeLabel(MachineRepresentation::kBit);
// Check if {value} is a Smi.
__ GotoIf(ObjectIsSmi(object), &done, one);
// Check if {value} is a HeapNumber.
Node* value_map = __ LoadField(AccessBuilder::ForMap(), object);
__ GotoIfNot(__ WordEqual(value_map, __ HeapNumberMapConstant()), &done,
zero);
// Value is a HeapNumber.
Node* value = __ LoadField(AccessBuilder::ForHeapNumberValue(), object);
Node* diff = __ Float64Sub(value, value);
Node* check = __ Float64Equal(diff, diff);
__ Goto(&done, check);
__ Bind(&done);
return done.PhiAt(0);
}
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;
auto call_descriptor = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), callable.descriptor(), 0, flags, properties);
return __ Call(call_descriptor, __ 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;
auto call_descriptor = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), callable.descriptor(), 0, flags, properties);
return __ Call(call_descriptor, __ 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());