#ifndef V8_MAGLEV_MAGLEV_PHI_REPRESENTATION_SELECTOR_H_
#define V8_MAGLEV_MAGLEV_PHI_REPRESENTATION_SELECTOR_H_
#include <optional>
#include "src/base/logging.h"
#include "src/base/small-vector.h"
#include "src/compiler/turboshaft/snapshot-table.h"
#include "src/maglev/maglev-compilation-info.h"
#include "src/maglev/maglev-graph-processor.h"
#include "src/maglev/maglev-reducer.h"
namespace v8 {
namespace internal {
namespace maglev {
class Graph;
constexpr bool IsUntagging(Opcode op) {
switch (op) {
case Opcode::kCheckedSmiUntag:
case Opcode::kUnsafeSmiUntag:
case Opcode::kCheckedNumberToInt32:
case Opcode::kCheckedObjectToIndex:
case Opcode::kTruncateCheckedNumberOrOddballToInt32:
case Opcode::kTruncateUnsafeNumberOrOddballToInt32:
case Opcode::kCheckedNumberOrOddballToFloat64:
case Opcode::kCheckedNumberToFloat64:
case Opcode::kUnsafeNumberOrOddballToFloat64:
case Opcode::kUnsafeNumberToFloat64:
case Opcode::kCheckedNumberOrOddballToHoleyFloat64:
case Opcode::kCheckedNumberToShiftedInt53:
return true;
default:
return false;
}
}
template <typename T>
concept IsUntaggingT = IsUntagging(NodeBase::opcode_of<T>);
class MaglevPhiRepresentationSelector {
template <class Value>
using SnapshotTable = compiler::turboshaft::SnapshotTable<Value>;
using Key = SnapshotTable<ValueNode*>::Key;
using Snapshot = SnapshotTable<ValueNode*>::Snapshot;
public:
explicit MaglevPhiRepresentationSelector(Graph* graph);
void PreProcessGraph(Graph* graph) {
if (v8_flags.trace_maglev_phi_untagging) {
StdoutStream{} << "\nMaglevPhiRepresentationSelector\n";
}
graph_ = graph;
}
void PostProcessGraph(Graph* graph) {
if (v8_flags.trace_maglev_phi_untagging) {
StdoutStream{} << "\n";
}
}
BlockProcessResult PreProcessBasicBlock(BasicBlock* block);
void PostProcessBasicBlock(BasicBlock* block);
void PostPhiProcessing() {}
enum ProcessPhiResult { kNone, kRetryOnChange, kChanged };
ProcessPhiResult ProcessPhi(Phi* node);
ProcessResult Process(Phi* node, const ProcessingState&) {
return ProcessResult::kContinue;
}
ProcessResult Process(JumpLoop* node, const ProcessingState&) {
FixLoopPhisBackedge(node->target());
return ProcessResult::kContinue;
}
ProcessResult Process(Dead* node, const ProcessingState&) {
return ProcessResult::kRemove;
}
template <class NodeT>
ProcessResult Process(NodeT* node, const ProcessingState& state) {
return UpdateNodeInputs(node, &state);
}
DeoptFrame* GetDeoptFrameForEagerDeopt() {
DCHECK_NOT_NULL(eager_deopt_frame_);
return eager_deopt_frame_;
}
private:
enum class HoistType : uint8_t {
kNone,
kLoopEntry,
kLoopEntryUnchecked,
kPrologue,
};
using HoistTypeList = base::SmallVector<HoistType, 8>;
void ConvertTaggedPhiTo(Phi* phi, ValueRepresentation repr,
const HoistTypeList& hoist_untagging);
template <class NodeT>
ValueNode* GetReplacementForPhiInputConversion(ValueNode* input, Phi* phi,
uint32_t input_index);
template <IsUntaggingT UntaggingNodeT>
ProcessResult UpdateNodeInputs(UntaggingNodeT* node,
const ProcessingState* state) {
if (node->NodeBase::input(0).node()->template Is<Phi>() &&
node->NodeBase::input(0).node()->value_representation() !=
ValueRepresentation::kTagged) {
DCHECK_EQ(node->input_count(), 1);
return UpdateUntaggingOfPhi(
node->NodeBase::input(0).node()->template Cast<Phi>(),
node->template Cast<ValueNode>());
}
return ProcessResult::kContinue;
}
template <typename NodeT>
ProcessResult UpdateNodeInputs(NodeT* node, const ProcessingState* state) {
return UpdateNonUntaggingNodeInputs(node, state);
}
template <class NodeT>
ProcessResult UpdateNonUntaggingNodeInputs(NodeT* node,
const ProcessingState* state) {
static_assert(!IsUntagging(NodeBase::opcode_of<NodeT>));
for (int i = 0; i < node->input_count(); i++) {
ValueNode* input = node->NodeBase::input(i).node();
if (input->Is<Identity>()) {
node->change_input(i, input->NodeBase::input(0).node());
} else if (Phi* phi = input->TryCast<Phi>()) {
ProcessResult result = UpdateNodePhiInput(node, phi, i, state);
if (V8_UNLIKELY(result == ProcessResult::kRemove)) {
return ProcessResult::kRemove;
}
}
}
return ProcessResult::kContinue;
}
ProcessResult UpdateNodePhiInput(NumberToString* node, Phi* phi,
int input_index,
const ProcessingState* state);
ProcessResult UpdateNodePhiInput(CheckSmi* node, Phi* phi, int input_index,
const ProcessingState* state);
ProcessResult UpdateNodePhiInput(CheckNumber* node, Phi* phi, int input_index,
const ProcessingState* state);
ProcessResult UpdateNodePhiInput(StoreTaggedFieldNoWriteBarrier* node,
Phi* phi, int input_index,
const ProcessingState* state);
ProcessResult UpdateNodePhiInput(StoreFixedArrayElementNoWriteBarrier* node,
Phi* phi, int input_index,
const ProcessingState* state);
ProcessResult UpdateNodePhiInput(BranchIfToBooleanTrue* node, Phi* phi,
int input_index,
const ProcessingState* state);
ProcessResult UpdateNodePhiInput(NodeBase* node, Phi* phi, int input_index,
const ProcessingState* state);
void EnsurePhiInputsTagged(Phi* phi);
ProcessResult UpdateUntaggingOfPhi(Phi* phi, ValueNode* old_untagging);
ValueNode* EnsurePhiTagged(
Phi* phi, BasicBlock* block, BasicBlockPosition pos,
const ProcessingState* state,
std::optional<int> predecessor_index = std::nullopt);
template <typename NodeT, typename... Args>
NodeT* AddNewNodeNoInputConversion(BasicBlock* block, BasicBlockPosition pos,
std::initializer_list<ValueNode*> inputs,
Args&&... args);
template <typename NodeT, typename... Args>
NodeT* AddNewNodeNoInputConversionAtBlockEnd(
BasicBlock* block, std::initializer_list<ValueNode*> inputs,
Args&&... args) {
return AddNewNodeNoInputConversion<NodeT>(
block, BasicBlockPosition::End(), inputs, std::forward<Args>(args)...);
}
void FixLoopPhisBackedge(BasicBlock* block);
void PreparePhiTaggings(BasicBlock* old_block, const BasicBlock* new_block);
MaglevGraphLabeller* graph_labeller() const {
return graph_->graph_labeller();
}
bool CanHoistUntaggingTo(BasicBlock* block);
Zone* zone() const { return graph_->zone(); }
Graph* graph_;
MaglevReducer<MaglevPhiRepresentationSelector> reducer_;
SnapshotTable<ValueNode*> phi_taggings_;
ZoneVector<Snapshot> predecessors_;
absl::flat_hash_map<BasicBlock::Id, Snapshot> snapshots_;
#ifdef DEBUG
std::unordered_set<NodeBase*> new_nodes_;
#endif
DeoptFrame* eager_deopt_frame_ = nullptr;
};
}
}
}
#endif