blob: aaa0644da62f096c3df97a48ccdf0bd7aa4f21dc [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/graph-assembler.h"
#include "src/codegen/code-factory.h"
#include "src/compiler/linkage.h"
#include "src/compiler/schedule.h"
// For TNode types.
#include "src/objects/heap-number.h"
#include "src/objects/oddball.h"
#include "src/objects/smi.h"
#include "src/objects/string.h"
namespace v8 {
namespace internal {
namespace compiler {
class GraphAssembler::BasicBlockUpdater {
public:
BasicBlockUpdater(Schedule* schedule, Graph* graph,
CommonOperatorBuilder* common, Zone* temp_zone);
Node* AddNode(Node* node);
Node* AddNode(Node* node, BasicBlock* to);
Node* AddClonedNode(Node* node);
BasicBlock* NewBasicBlock(bool deferred);
BasicBlock* SplitBasicBlock();
void AddBind(BasicBlock* block);
void AddBranch(Node* branch, BasicBlock* tblock, BasicBlock* fblock);
void AddGoto(BasicBlock* to);
void AddGoto(BasicBlock* from, BasicBlock* to);
void AddTailCall(Node* node);
void StartBlock(BasicBlock* block);
BasicBlock* Finalize(BasicBlock* original);
BasicBlock* original_block() { return original_block_; }
BasicBlock::Control original_control() { return original_control_; }
Node* original_control_input() { return original_control_input_; }
private:
enum State { kUnchanged, kChanged };
Zone* temp_zone() { return temp_zone_; }
bool IsOriginalNode(Node* node);
void UpdateSuccessors(BasicBlock* block);
void SetBlockDeferredFromPredecessors();
void RemoveSuccessorsFromSchedule();
void CopyForChange();
Zone* temp_zone_;
// Current basic block we are scheduling.
BasicBlock* current_block_;
// The original block that we are lowering.
BasicBlock* original_block_;
// Position in the current block, only applicable in the 'unchanged' state.
BasicBlock::iterator node_it_;
BasicBlock::iterator end_it_;
Schedule* schedule_;
Graph* graph_;
CommonOperatorBuilder* common_;
// The nodes in the original block if we are in 'changed' state. Retained to
// avoid invalidating iterators that are iterating over the original nodes of
// the block.
NodeVector saved_nodes_;
// The original control, control input and successors, to enable recovery of
// them when we finalize the block.
struct SuccessorInfo {
BasicBlock* block;
size_t index;
};
ZoneVector<SuccessorInfo> saved_successors_;
BasicBlock::Control original_control_;
Node* original_control_input_;
bool original_deferred_;
size_t original_node_count_;
State state_;
};
GraphAssembler::BasicBlockUpdater::BasicBlockUpdater(
Schedule* schedule, Graph* graph, CommonOperatorBuilder* common,
Zone* temp_zone)
: temp_zone_(temp_zone),
current_block_(nullptr),
original_block_(nullptr),
schedule_(schedule),
graph_(graph),
common_(common),
saved_nodes_(schedule->zone()),
saved_successors_(schedule->zone()),
original_control_(BasicBlock::kNone),
original_control_input_(nullptr),
original_deferred_(false),
original_node_count_(graph->NodeCount()),
state_(kUnchanged) {}
Node* GraphAssembler::BasicBlockUpdater::AddNode(Node* node) {
return AddNode(node, current_block_);
}
Node* GraphAssembler::BasicBlockUpdater::AddNode(Node* node, BasicBlock* to) {
if (state_ == kUnchanged) {
DCHECK_EQ(to, original_block());
if (node_it_ != end_it_ && *node_it_ == node) {
node_it_++;
return node;
}
CopyForChange();
}
// Add the node to the basic block.
DCHECK(!schedule_->IsScheduled(node));
schedule_->AddNode(to, node);
return node;
}
Node* GraphAssembler::BasicBlockUpdater::AddClonedNode(Node* node) {
DCHECK(node->op()->HasProperty(Operator::kPure));
if (state_ == kUnchanged) {
CopyForChange();
}
if (schedule_->IsScheduled(node) &&
schedule_->block(node) == current_block_) {
// Node is already scheduled for the current block, don't add it again.
return node;
} else if (!schedule_->IsScheduled(node) && !IsOriginalNode(node)) {
// Node is not scheduled yet, so we can add it directly.
return AddNode(node);
} else {
// TODO(9684): Potentially add some per-block caching so we can avoid
// cloning if we've already cloned for this block.
return AddNode(graph_->CloneNode(node));
}
}
bool GraphAssembler::BasicBlockUpdater::IsOriginalNode(Node* node) {
// Return true if node was part of the original schedule and might currently
// be re-added to the schedule after a CopyForChange.
return node->id() < original_node_count_;
}
void GraphAssembler::BasicBlockUpdater::CopyForChange() {
DCHECK_EQ(kUnchanged, state_);
// Save successor.
DCHECK(saved_successors_.empty());
for (BasicBlock* successor : original_block()->successors()) {
for (size_t i = 0; i < successor->PredecessorCount(); i++) {
if (successor->PredecessorAt(i) == original_block()) {
saved_successors_.push_back({successor, i});
break;
}
}
}
DCHECK_EQ(saved_successors_.size(), original_block()->SuccessorCount());
// Save control.
original_control_ = original_block()->control();
original_control_input_ = original_block()->control_input();
// Save original nodes (to allow them to continue to be iterated by the user
// of graph assembler).
original_block()->nodes()->swap(saved_nodes_);
DCHECK(original_block()->nodes()->empty());
// Re-insert the nodes from the front of the block.
original_block()->InsertNodes(original_block()->begin(), saved_nodes_.begin(),
node_it_);
// Remove the tail from the schedule.
for (; node_it_ != end_it_; node_it_++) {
schedule_->SetBlockForNode(nullptr, *node_it_);
}
// Reset the control.
if (original_block()->control() != BasicBlock::kGoto) {
schedule_->SetBlockForNode(nullptr, original_block()->control_input());
}
original_block()->set_control_input(nullptr);
original_block()->set_control(BasicBlock::kNone);
original_block()->ClearSuccessors();
state_ = kChanged;
end_it_ = {};
node_it_ = {};
}
BasicBlock* GraphAssembler::BasicBlockUpdater::NewBasicBlock(bool deferred) {
BasicBlock* block = schedule_->NewBasicBlock();
block->set_deferred(deferred || original_deferred_);
return block;
}
BasicBlock* GraphAssembler::BasicBlockUpdater::SplitBasicBlock() {
return NewBasicBlock(current_block_->deferred());
}
void GraphAssembler::BasicBlockUpdater::AddBind(BasicBlock* to) {
DCHECK_NOT_NULL(to);
current_block_ = to;
// Basic block should only have the control node, if any.
DCHECK_LE(current_block_->NodeCount(), 1);
SetBlockDeferredFromPredecessors();
}
void GraphAssembler::BasicBlockUpdater::SetBlockDeferredFromPredecessors() {
if (!current_block_->deferred()) {
bool deferred = true;
for (BasicBlock* pred : current_block_->predecessors()) {
if (!pred->deferred()) {
deferred = false;
break;
}
}
current_block_->set_deferred(deferred);
}
}
void GraphAssembler::BasicBlockUpdater::AddBranch(Node* node,
BasicBlock* tblock,
BasicBlock* fblock) {
if (state_ == kUnchanged) {
DCHECK_EQ(current_block_, original_block());
CopyForChange();
}
DCHECK_EQ(state_, kChanged);
schedule_->AddBranch(current_block_, node, tblock, fblock);
current_block_ = nullptr;
}
void GraphAssembler::BasicBlockUpdater::AddGoto(BasicBlock* to) {
DCHECK_NOT_NULL(current_block_);
AddGoto(current_block_, to);
}
void GraphAssembler::BasicBlockUpdater::AddGoto(BasicBlock* from,
BasicBlock* to) {
if (state_ == kUnchanged) {
CopyForChange();
}
if (to->deferred() && !from->deferred()) {
// Add a new block with the correct deferred hint to avoid merges into the
// target block with different deferred hints.
// TODO(9684): Only split the current basic block if the label's target
// block has multiple merges.
BasicBlock* new_block = NewBasicBlock(to->deferred());
schedule_->AddGoto(from, new_block);
from = new_block;
}
schedule_->AddGoto(from, to);
current_block_ = nullptr;
}
void GraphAssembler::BasicBlockUpdater::AddTailCall(Node* node) {
DCHECK_EQ(node->opcode(), IrOpcode::kTailCall);
DCHECK_NOT_NULL(current_block_);
if (state_ == kUnchanged) {
CopyForChange();
}
schedule_->AddTailCall(current_block_, node);
current_block_ = nullptr;
}
void GraphAssembler::BasicBlockUpdater::UpdateSuccessors(BasicBlock* block) {
for (SuccessorInfo succ : saved_successors_) {
(succ.block->predecessors())[succ.index] = block;
block->AddSuccessor(succ.block);
}
saved_successors_.clear();
block->set_control(original_control_);
block->set_control_input(original_control_input_);
if (original_control_input_ != nullptr) {
schedule_->SetBlockForNode(block, original_control_input_);
} else {
DCHECK_EQ(BasicBlock::kGoto, original_control_);
}
}
void GraphAssembler::BasicBlockUpdater::StartBlock(BasicBlock* block) {
DCHECK_NULL(current_block_);
DCHECK_NULL(original_block_);
DCHECK(saved_nodes_.empty());
block->ResetRPOInfo();
current_block_ = block;
original_block_ = block;
original_deferred_ = block->deferred();
node_it_ = block->begin();
end_it_ = block->end();
state_ = kUnchanged;
}
BasicBlock* GraphAssembler::BasicBlockUpdater::Finalize(BasicBlock* original) {
DCHECK_EQ(original, original_block());
BasicBlock* block = current_block_;
if (state_ == kChanged) {
UpdateSuccessors(block);
} else {
DCHECK_EQ(block, original_block());
if (node_it_ != end_it_) {
// We have not got to the end of the node list, we need to trim.
block->TrimNodes(node_it_);
}
}
original_control_ = BasicBlock::kNone;
saved_nodes_.clear();
original_deferred_ = false;
original_control_input_ = nullptr;
original_block_ = nullptr;
current_block_ = nullptr;
return block;
}
GraphAssembler::GraphAssembler(
MachineGraph* mcgraph, Zone* zone,
base::Optional<NodeChangedCallback> node_changed_callback,
Schedule* schedule, bool mark_loop_exits)
: temp_zone_(zone),
mcgraph_(mcgraph),
effect_(nullptr),
control_(nullptr),
node_changed_callback_(node_changed_callback),
block_updater_(schedule != nullptr
? new BasicBlockUpdater(schedule, mcgraph->graph(),
mcgraph->common(), zone)
: nullptr),
loop_headers_(zone),
mark_loop_exits_(mark_loop_exits) {}
GraphAssembler::~GraphAssembler() { DCHECK_EQ(loop_nesting_level_, 0); }
Node* GraphAssembler::IntPtrConstant(intptr_t value) {
return AddClonedNode(mcgraph()->IntPtrConstant(value));
}
Node* GraphAssembler::UintPtrConstant(uintptr_t value) {
return AddClonedNode(mcgraph()->UintPtrConstant(value));
}
Node* GraphAssembler::Int32Constant(int32_t value) {
return AddClonedNode(mcgraph()->Int32Constant(value));
}
Node* GraphAssembler::Int64Constant(int64_t value) {
return AddClonedNode(mcgraph()->Int64Constant(value));
}
Node* GraphAssembler::UniqueIntPtrConstant(intptr_t value) {
return AddNode(graph()->NewNode(
machine()->Is64()
? common()->Int64Constant(value)
: common()->Int32Constant(static_cast<int32_t>(value))));
}
Node* JSGraphAssembler::SmiConstant(int32_t value) {
return AddClonedNode(jsgraph()->SmiConstant(value));
}
Node* GraphAssembler::Uint32Constant(uint32_t value) {
return AddClonedNode(mcgraph()->Uint32Constant(value));
}
Node* GraphAssembler::Float64Constant(double value) {
return AddClonedNode(mcgraph()->Float64Constant(value));
}
TNode<HeapObject> JSGraphAssembler::HeapConstant(Handle<HeapObject> object) {
return TNode<HeapObject>::UncheckedCast(
AddClonedNode(jsgraph()->HeapConstant(object)));
}
TNode<Object> JSGraphAssembler::Constant(const ObjectRef& ref) {
return TNode<Object>::UncheckedCast(AddClonedNode(jsgraph()->Constant(ref)));
}
TNode<Number> JSGraphAssembler::NumberConstant(double value) {
return TNode<Number>::UncheckedCast(
AddClonedNode(jsgraph()->Constant(value)));
}
Node* GraphAssembler::ExternalConstant(ExternalReference ref) {
return AddClonedNode(mcgraph()->ExternalConstant(ref));
}
Node* GraphAssembler::Parameter(int index) {
return AddNode(
graph()->NewNode(common()->Parameter(index), graph()->start()));
}
Node* JSGraphAssembler::CEntryStubConstant(int result_size) {
return AddClonedNode(jsgraph()->CEntryStubConstant(result_size));
}
Node* GraphAssembler::LoadFramePointer() {
return AddNode(graph()->NewNode(machine()->LoadFramePointer()));
}
Node* GraphAssembler::LoadHeapNumberValue(Node* heap_number) {
return Load(MachineType::Float64(), heap_number,
IntPtrConstant(HeapNumber::kValueOffset - kHeapObjectTag));
}
#define SINGLETON_CONST_DEF(Name, Type) \
TNode<Type> JSGraphAssembler::Name##Constant() { \
return TNode<Type>::UncheckedCast( \
AddClonedNode(jsgraph()->Name##Constant())); \
}
JSGRAPH_SINGLETON_CONSTANT_LIST(SINGLETON_CONST_DEF)
#undef SINGLETON_CONST_DEF
#define SINGLETON_CONST_TEST_DEF(Name, ...) \
TNode<Boolean> JSGraphAssembler::Is##Name(TNode<Object> value) { \
return TNode<Boolean>::UncheckedCast( \
ReferenceEqual(value, Name##Constant())); \
}
JSGRAPH_SINGLETON_CONSTANT_LIST(SINGLETON_CONST_TEST_DEF)
#undef SINGLETON_CONST_TEST_DEF
#define PURE_UNOP_DEF(Name) \
Node* GraphAssembler::Name(Node* input) { \
return AddNode(graph()->NewNode(machine()->Name(), input)); \
}
PURE_ASSEMBLER_MACH_UNOP_LIST(PURE_UNOP_DEF)
#undef PURE_UNOP_DEF
#define PURE_BINOP_DEF(Name) \
Node* GraphAssembler::Name(Node* left, Node* right) { \
return AddNode(graph()->NewNode(machine()->Name(), left, right)); \
}
PURE_ASSEMBLER_MACH_BINOP_LIST(PURE_BINOP_DEF)
#undef PURE_BINOP_DEF
#define CHECKED_BINOP_DEF(Name) \
Node* GraphAssembler::Name(Node* left, Node* right) { \
return AddNode( \
graph()->NewNode(machine()->Name(), left, right, control())); \
}
CHECKED_ASSEMBLER_MACH_BINOP_LIST(CHECKED_BINOP_DEF)
#undef CHECKED_BINOP_DEF
Node* GraphAssembler::IntPtrEqual(Node* left, Node* right) {
return WordEqual(left, right);
}
Node* GraphAssembler::TaggedEqual(Node* left, Node* right) {
if (COMPRESS_POINTERS_BOOL) {
return Word32Equal(left, right);
} else {
return WordEqual(left, right);
}
}
Node* GraphAssembler::SmiSub(Node* left, Node* right) {
if (COMPRESS_POINTERS_BOOL) {
return Int32Sub(left, right);
} else {
return IntSub(left, right);
}
}
Node* GraphAssembler::SmiLessThan(Node* left, Node* right) {
if (COMPRESS_POINTERS_BOOL) {
return Int32LessThan(left, right);
} else {
return IntLessThan(left, right);
}
}
Node* GraphAssembler::Float64RoundDown(Node* value) {
CHECK(machine()->Float64RoundDown().IsSupported());
return AddNode(graph()->NewNode(machine()->Float64RoundDown().op(), value));
}
Node* GraphAssembler::Float64RoundTruncate(Node* value) {
CHECK(machine()->Float64RoundTruncate().IsSupported());
return AddNode(
graph()->NewNode(machine()->Float64RoundTruncate().op(), value));
}
Node* GraphAssembler::Projection(int index, Node* value) {
return AddNode(
graph()->NewNode(common()->Projection(index), value, control()));
}
Node* JSGraphAssembler::Allocate(AllocationType allocation, Node* size) {
return AddNode(
graph()->NewNode(simplified()->AllocateRaw(Type::Any(), allocation), size,
effect(), control()));
}
Node* JSGraphAssembler::LoadField(FieldAccess const& access, Node* object) {
Node* value = AddNode(graph()->NewNode(simplified()->LoadField(access),
object, effect(), control()));
return value;
}
Node* JSGraphAssembler::LoadElement(ElementAccess const& access, Node* object,
Node* index) {
Node* value = AddNode(graph()->NewNode(simplified()->LoadElement(access),
object, index, effect(), control()));
return value;
}
Node* JSGraphAssembler::StoreField(FieldAccess const& access, Node* object,
Node* value) {
return AddNode(graph()->NewNode(simplified()->StoreField(access), object,
value, effect(), control()));
}
Node* JSGraphAssembler::StoreElement(ElementAccess const& access, Node* object,
Node* index, Node* value) {
return AddNode(graph()->NewNode(simplified()->StoreElement(access), object,
index, value, effect(), control()));
}
void JSGraphAssembler::TransitionAndStoreElement(MapRef double_map,
MapRef fast_map,
TNode<HeapObject> object,
TNode<Number> index,
TNode<Object> value) {
AddNode(graph()->NewNode(simplified()->TransitionAndStoreElement(
double_map.object(), fast_map.object()),
object, index, value, effect(), control()));
}
TNode<Number> JSGraphAssembler::StringLength(TNode<String> string) {
return AddNode<Number>(
graph()->NewNode(simplified()->StringLength(), string));
}
TNode<Boolean> JSGraphAssembler::ReferenceEqual(TNode<Object> lhs,
TNode<Object> rhs) {
return AddNode<Boolean>(
graph()->NewNode(simplified()->ReferenceEqual(), lhs, rhs));
}
TNode<Number> JSGraphAssembler::NumberMin(TNode<Number> lhs,
TNode<Number> rhs) {
return AddNode<Number>(graph()->NewNode(simplified()->NumberMin(), lhs, rhs));
}
TNode<Number> JSGraphAssembler::NumberMax(TNode<Number> lhs,
TNode<Number> rhs) {
return AddNode<Number>(graph()->NewNode(simplified()->NumberMax(), lhs, rhs));
}
TNode<Number> JSGraphAssembler::NumberAdd(TNode<Number> lhs,
TNode<Number> rhs) {
return AddNode<Number>(graph()->NewNode(simplified()->NumberAdd(), lhs, rhs));
}
TNode<Number> JSGraphAssembler::NumberSubtract(TNode<Number> lhs,
TNode<Number> rhs) {
return AddNode<Number>(
graph()->NewNode(simplified()->NumberSubtract(), lhs, rhs));
}
TNode<Boolean> JSGraphAssembler::NumberLessThan(TNode<Number> lhs,
TNode<Number> rhs) {
return AddNode<Boolean>(
graph()->NewNode(simplified()->NumberLessThan(), lhs, rhs));
}
TNode<Boolean> JSGraphAssembler::NumberLessThanOrEqual(TNode<Number> lhs,
TNode<Number> rhs) {
return AddNode<Boolean>(
graph()->NewNode(simplified()->NumberLessThanOrEqual(), lhs, rhs));
}
TNode<String> JSGraphAssembler::StringSubstring(TNode<String> string,
TNode<Number> from,
TNode<Number> to) {
return AddNode<String>(graph()->NewNode(
simplified()->StringSubstring(), string, from, to, effect(), control()));
}
TNode<Boolean> JSGraphAssembler::ObjectIsCallable(TNode<Object> value) {
return AddNode<Boolean>(
graph()->NewNode(simplified()->ObjectIsCallable(), value));
}
TNode<Boolean> JSGraphAssembler::ObjectIsUndetectable(TNode<Object> value) {
return AddNode<Boolean>(
graph()->NewNode(simplified()->ObjectIsUndetectable(), value));
}
Node* JSGraphAssembler::CheckIf(Node* cond, DeoptimizeReason reason) {
return AddNode(graph()->NewNode(simplified()->CheckIf(reason), cond, effect(),
control()));
}
TNode<Boolean> JSGraphAssembler::NumberIsFloat64Hole(TNode<Number> value) {
return AddNode<Boolean>(
graph()->NewNode(simplified()->NumberIsFloat64Hole(), value));
}
TNode<Boolean> JSGraphAssembler::ToBoolean(TNode<Object> value) {
return AddNode<Boolean>(graph()->NewNode(simplified()->ToBoolean(), value));
}
TNode<Object> JSGraphAssembler::ConvertTaggedHoleToUndefined(
TNode<Object> value) {
return AddNode<Object>(
graph()->NewNode(simplified()->ConvertTaggedHoleToUndefined(), value));
}
TNode<FixedArrayBase> JSGraphAssembler::MaybeGrowFastElements(
ElementsKind kind, const FeedbackSource& feedback, TNode<JSArray> array,
TNode<FixedArrayBase> elements, TNode<Number> new_length,
TNode<Number> old_length) {
GrowFastElementsMode mode = IsDoubleElementsKind(kind)
? GrowFastElementsMode::kDoubleElements
: GrowFastElementsMode::kSmiOrObjectElements;
return AddNode<FixedArrayBase>(graph()->NewNode(
simplified()->MaybeGrowFastElements(mode, feedback), array, elements,
new_length, old_length, effect(), control()));
}
Node* GraphAssembler::TypeGuard(Type type, Node* value) {
return AddNode(
graph()->NewNode(common()->TypeGuard(type), value, effect(), control()));
}
Node* GraphAssembler::Checkpoint(FrameState frame_state) {
return AddNode(graph()->NewNode(common()->Checkpoint(), frame_state, effect(),
control()));
}
Node* GraphAssembler::DebugBreak() {
return AddNode(
graph()->NewNode(machine()->DebugBreak(), effect(), control()));
}
Node* GraphAssembler::Unreachable(
GraphAssemblerLabel<0u>* block_updater_successor) {
Node* result = UnreachableWithoutConnectToEnd();
if (block_updater_ == nullptr) {
ConnectUnreachableToEnd();
InitializeEffectControl(nullptr, nullptr);
} else {
DCHECK_NOT_NULL(block_updater_successor);
Goto(block_updater_successor);
}
return result;
}
Node* GraphAssembler::UnreachableWithoutConnectToEnd() {
return AddNode(
graph()->NewNode(common()->Unreachable(), effect(), control()));
}
TNode<RawPtrT> GraphAssembler::StackSlot(int size, int alignment) {
return AddNode<RawPtrT>(
graph()->NewNode(machine()->StackSlot(size, alignment)));
}
Node* GraphAssembler::Store(StoreRepresentation rep, Node* object, Node* offset,
Node* value) {
return AddNode(graph()->NewNode(machine()->Store(rep), object, offset, value,
effect(), control()));
}
Node* GraphAssembler::Store(StoreRepresentation rep, Node* object, int offset,
Node* value) {
return Store(rep, object, Int32Constant(offset), value);
}
Node* GraphAssembler::Load(MachineType type, Node* object, Node* offset) {
return AddNode(graph()->NewNode(machine()->Load(type), object, offset,
effect(), control()));
}
Node* GraphAssembler::Load(MachineType type, Node* object, int offset) {
return Load(type, object, Int32Constant(offset));
}
Node* GraphAssembler::StoreUnaligned(MachineRepresentation rep, Node* object,
Node* offset, Node* value) {
Operator const* const op =
(rep == MachineRepresentation::kWord8 ||
machine()->UnalignedStoreSupported(rep))
? machine()->Store(StoreRepresentation(rep, kNoWriteBarrier))
: machine()->UnalignedStore(rep);
return AddNode(
graph()->NewNode(op, object, offset, value, effect(), control()));
}
Node* GraphAssembler::LoadUnaligned(MachineType type, Node* object,
Node* offset) {
Operator const* const op =
(type.representation() == MachineRepresentation::kWord8 ||
machine()->UnalignedLoadSupported(type.representation()))
? machine()->Load(type)
: machine()->UnalignedLoad(type);
return AddNode(graph()->NewNode(op, object, offset, effect(), control()));
}
Node* GraphAssembler::ProtectedStore(MachineRepresentation rep, Node* object,
Node* offset, Node* value) {
return AddNode(graph()->NewNode(machine()->ProtectedStore(rep), object,
offset, value, effect(), control()));
}
Node* GraphAssembler::ProtectedLoad(MachineType type, Node* object,
Node* offset) {
return AddNode(graph()->NewNode(machine()->ProtectedLoad(type), object,
offset, effect(), control()));
}
Node* GraphAssembler::Retain(Node* buffer) {
return AddNode(graph()->NewNode(common()->Retain(), buffer, effect()));
}
Node* GraphAssembler::UnsafePointerAdd(Node* base, Node* external) {
return AddNode(graph()->NewNode(machine()->UnsafePointerAdd(), base, external,
effect(), control()));
}
TNode<Number> JSGraphAssembler::PlainPrimitiveToNumber(TNode<Object> value) {
return AddNode<Number>(graph()->NewNode(
PlainPrimitiveToNumberOperator(), PlainPrimitiveToNumberBuiltinConstant(),
value, effect()));
}
Node* GraphAssembler::BitcastWordToTaggedSigned(Node* value) {
return AddNode(
graph()->NewNode(machine()->BitcastWordToTaggedSigned(), value));
}
Node* GraphAssembler::BitcastWordToTagged(Node* value) {
return AddNode(graph()->NewNode(machine()->BitcastWordToTagged(), value,
effect(), control()));
}
Node* GraphAssembler::BitcastTaggedToWord(Node* value) {
return AddNode(graph()->NewNode(machine()->BitcastTaggedToWord(), value,
effect(), control()));
}
Node* GraphAssembler::BitcastTaggedToWordForTagAndSmiBits(Node* value) {
return AddNode(graph()->NewNode(
machine()->BitcastTaggedToWordForTagAndSmiBits(), value));
}
Node* GraphAssembler::BitcastMaybeObjectToWord(Node* value) {
return AddNode(graph()->NewNode(machine()->BitcastMaybeObjectToWord(), value,
effect(), control()));
}
Node* GraphAssembler::Word32PoisonOnSpeculation(Node* value) {
return AddNode(graph()->NewNode(machine()->Word32PoisonOnSpeculation(), value,
effect(), control()));
}
Node* GraphAssembler::DeoptimizeIf(DeoptimizeReason reason,
FeedbackSource const& feedback,
Node* condition, Node* frame_state,
IsSafetyCheck is_safety_check) {
return AddNode(
graph()->NewNode(common()->DeoptimizeIf(DeoptimizeKind::kEager, reason,
feedback, is_safety_check),
condition, frame_state, effect(), control()));
}
Node* GraphAssembler::DeoptimizeIf(DeoptimizeKind kind, DeoptimizeReason reason,
FeedbackSource const& feedback,
Node* condition, Node* frame_state,
IsSafetyCheck is_safety_check) {
return AddNode(graph()->NewNode(
common()->DeoptimizeIf(kind, reason, feedback, is_safety_check),
condition, frame_state, effect(), control()));
}
Node* GraphAssembler::DeoptimizeIfNot(DeoptimizeKind kind,
DeoptimizeReason reason,
FeedbackSource const& feedback,
Node* condition, Node* frame_state,
IsSafetyCheck is_safety_check) {
return AddNode(graph()->NewNode(
common()->DeoptimizeUnless(kind, reason, feedback, is_safety_check),
condition, frame_state, effect(), control()));
}
Node* GraphAssembler::DeoptimizeIfNot(DeoptimizeReason reason,
FeedbackSource const& feedback,
Node* condition, Node* frame_state,
IsSafetyCheck is_safety_check) {
return DeoptimizeIfNot(DeoptimizeKind::kEager, reason, feedback, condition,
frame_state, is_safety_check);
}
TNode<Object> GraphAssembler::Call(const CallDescriptor* call_descriptor,
int inputs_size, Node** inputs) {
return Call(common()->Call(call_descriptor), inputs_size, inputs);
}
TNode<Object> GraphAssembler::Call(const Operator* op, int inputs_size,
Node** inputs) {
DCHECK_EQ(IrOpcode::kCall, op->opcode());
return AddNode<Object>(graph()->NewNode(op, inputs_size, inputs));
}
void GraphAssembler::TailCall(const CallDescriptor* call_descriptor,
int inputs_size, Node** inputs) {
#ifdef DEBUG
static constexpr int kTargetEffectControl = 3;
DCHECK_EQ(inputs_size,
call_descriptor->ParameterCount() + kTargetEffectControl);
#endif // DEBUG
Node* node = AddNode(graph()->NewNode(common()->TailCall(call_descriptor),
inputs_size, inputs));
if (block_updater_) block_updater_->AddTailCall(node);
// Unlike ConnectUnreachableToEnd, the TailCall node terminates a block; to
// keep it live, it *must* be connected to End (also in Turboprop schedules).
NodeProperties::MergeControlToEnd(graph(), common(), node);
// Setting effect, control to nullptr effectively terminates the current block
// by disallowing the addition of new nodes until a new label has been bound.
InitializeEffectControl(nullptr, nullptr);
}
void GraphAssembler::BranchWithCriticalSafetyCheck(
Node* condition, GraphAssemblerLabel<0u>* if_true,
GraphAssemblerLabel<0u>* if_false) {
BranchHint hint = BranchHint::kNone;
if (if_true->IsDeferred() != if_false->IsDeferred()) {
hint = if_false->IsDeferred() ? BranchHint::kTrue : BranchHint::kFalse;
}
BranchImpl(condition, if_true, if_false, hint,
IsSafetyCheck::kCriticalSafetyCheck);
}
void GraphAssembler::RecordBranchInBlockUpdater(Node* branch,
Node* if_true_control,
Node* if_false_control,
BasicBlock* if_true_block,
BasicBlock* if_false_block) {
DCHECK_NOT_NULL(block_updater_);
// TODO(9684): Only split the current basic block if the label's target
// block has multiple merges.
BasicBlock* if_true_target = block_updater_->SplitBasicBlock();
BasicBlock* if_false_target = block_updater_->SplitBasicBlock();
block_updater_->AddBranch(branch, if_true_target, if_false_target);
block_updater_->AddNode(if_true_control, if_true_target);
block_updater_->AddGoto(if_true_target, if_true_block);
block_updater_->AddNode(if_false_control, if_false_target);
block_updater_->AddGoto(if_false_target, if_false_block);
}
void GraphAssembler::BindBasicBlock(BasicBlock* block) {
if (block_updater_) {
block_updater_->AddBind(block);
}
}
BasicBlock* GraphAssembler::NewBasicBlock(bool deferred) {
if (!block_updater_) return nullptr;
return block_updater_->NewBasicBlock(deferred);
}
void GraphAssembler::GotoBasicBlock(BasicBlock* block) {
if (block_updater_) {
block_updater_->AddGoto(block);
}
}
void GraphAssembler::GotoIfBasicBlock(BasicBlock* block, Node* branch,
IrOpcode::Value goto_if) {
if (block_updater_) {
// TODO(9684): Only split the current basic block for the goto_target
// if block has multiple merges.
BasicBlock* goto_target = block_updater_->SplitBasicBlock();
BasicBlock* fallthrough_target = block_updater_->SplitBasicBlock();
if (goto_if == IrOpcode::kIfTrue) {
block_updater_->AddBranch(branch, goto_target, fallthrough_target);
} else {
DCHECK_EQ(goto_if, IrOpcode::kIfFalse);
block_updater_->AddBranch(branch, fallthrough_target, goto_target);
}
block_updater_->AddNode(control(), goto_target);
block_updater_->AddGoto(goto_target, block);
block_updater_->AddBind(fallthrough_target);
}
}
BasicBlock* GraphAssembler::FinalizeCurrentBlock(BasicBlock* block) {
if (block_updater_) {
block = block_updater_->Finalize(block);
if (control() == mcgraph()->Dead()) {
// If the block's end is unreachable, then reset current effect and
// control to that of the block's throw control node.
DCHECK(block->control() == BasicBlock::kThrow);
Node* throw_node = block->control_input();
control_ = NodeProperties::GetControlInput(throw_node);
effect_ = NodeProperties::GetEffectInput(throw_node);
}
}
return block;
}
void GraphAssembler::ConnectUnreachableToEnd() {
DCHECK_EQ(effect()->opcode(), IrOpcode::kUnreachable);
// When maintaining the schedule we can't easily rewire the successor blocks
// to disconnect them from the graph, so we just leave the unreachable nodes
// in the schedule.
// TODO(9684): Add a scheduled dead-code elimination phase to remove all the
// subsequent unreachable code from the schedule.
if (!block_updater_) {
Node* throw_node = graph()->NewNode(common()->Throw(), effect(), control());
NodeProperties::MergeControlToEnd(graph(), common(), throw_node);
if (node_changed_callback_.has_value()) {
(*node_changed_callback_)(graph()->end());
}
effect_ = control_ = mcgraph()->Dead();
}
}
Node* GraphAssembler::AddClonedNode(Node* node) {
DCHECK(node->op()->HasProperty(Operator::kPure));
if (block_updater_) {
node = block_updater_->AddClonedNode(node);
}
UpdateEffectControlWith(node);
return node;
}
Node* GraphAssembler::AddNode(Node* node) {
if (block_updater_) {
block_updater_->AddNode(node);
}
if (node->opcode() == IrOpcode::kTerminate) {
return node;
}
UpdateEffectControlWith(node);
return node;
}
void GraphAssembler::Reset(BasicBlock* block) {
effect_ = nullptr;
control_ = nullptr;
if (block_updater_) {
block_updater_->StartBlock(block);
}
}
void GraphAssembler::InitializeEffectControl(Node* effect, Node* control) {
effect_ = effect;
control_ = control;
}
Operator const* JSGraphAssembler::PlainPrimitiveToNumberOperator() {
if (!to_number_operator_.is_set()) {
Callable callable =
Builtins::CallableFor(isolate(), Builtins::kPlainPrimitiveToNumber);
CallDescriptor::Flags flags = CallDescriptor::kNoFlags;
auto call_descriptor = Linkage::GetStubCallDescriptor(
graph()->zone(), callable.descriptor(),
callable.descriptor().GetStackParameterCount(), flags,
Operator::kEliminatable);
to_number_operator_.set(common()->Call(call_descriptor));
}
return to_number_operator_.get();
}
} // namespace compiler
} // namespace internal
} // namespace v8