Commit 4e9f6513 authored by Paolo Severini's avatar Paolo Severini Committed by Commit Bot

[test][turbofan] Add %ObserveNode intrinsic for node-specific tests

(Initially copied from nicohartmann@ CL
https://chromium-review.googlesource.com/c/v8/v8/+/2135631)

This CL adds a new intrinsic %ObserveNode(expr) which has noop semantics
but triggers the new NodeObserver set on the OptimizedCompilationInfo
when the node generated for expr is created or changed in any phase
(until EffectControlLinearization).

This provides the infrastructure to write reasonable unit tests that
check for the construction of or lowering to specific nodes (e.g.
depending on feedback).

When %ObserveNode(expr) is used an object of class ObserveNodeManager is
registered to every Reducer/GraphReducer and is notified by the Reducer
with all node changes. The same logic is added to classes
SimplifiedLowering/RepresentationSelector, which do not inherit from
class Reducer.

Observed Node modifications currently are:
 * The Node Operator
 * The Node type
 * Node replacements

A first use case (cctest/test-sloppy-equality.cc) is included in this CL.

Change-Id: Idc5a5e38af8b1d9a2ec5021bf821c4e4e1406220
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2555219
Commit-Queue: Paolo Severini <paolosev@microsoft.com>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Reviewed-by: 's avatarNico Hartmann <nicohartmann@chromium.org>
Cr-Commit-Position: refs/heads/master@{#72331}
parent 80b97562
......@@ -2301,6 +2301,8 @@ v8_compiler_sources = [
"src/compiler/node-marker.h",
"src/compiler/node-matchers.cc",
"src/compiler/node-matchers.h",
"src/compiler/node-observer.cc",
"src/compiler/node-observer.h",
"src/compiler/node-origin-table.cc",
"src/compiler/node-origin-table.h",
"src/compiler/node-properties.cc",
......
......@@ -34,6 +34,10 @@ class JavaScriptFrame;
class JSGlobalObject;
class Zone;
namespace compiler {
class NodeObserver;
}
namespace wasm {
struct WasmCompilationResult;
} // namespace wasm
......@@ -121,6 +125,11 @@ class V8_EXPORT_PRIVATE OptimizedCompilationInfo final {
void set_builtin_index(int32_t index) { builtin_index_ = index; }
BytecodeOffset osr_offset() const { return osr_offset_; }
JavaScriptFrame* osr_frame() const { return osr_frame_; }
void SetNodeObserver(compiler::NodeObserver* observer) {
DCHECK_NULL(node_observer_);
node_observer_ = observer;
}
compiler::NodeObserver* node_observer() const { return node_observer_; }
void SetPoisoningMitigationLevel(PoisoningMitigationLevel poisoning_level) {
poisoning_level_ = poisoning_level;
......@@ -284,6 +293,8 @@ class V8_EXPORT_PRIVATE OptimizedCompilationInfo final {
// OptimizedCompilationInfo allocates.
Zone* const zone_;
compiler::NodeObserver* node_observer_ = nullptr;
BailoutReason bailout_reason_ = BailoutReason::kNoReason;
InlinedFunctionList inlined_functions_;
......
......@@ -15,6 +15,7 @@
#include "src/compiler/js-heap-broker.h"
#include "src/compiler/linkage.h"
#include "src/compiler/node-matchers.h"
#include "src/compiler/node-observer.h"
#include "src/compiler/operator-properties.h"
#include "src/compiler/simplified-operator.h"
#include "src/compiler/state-values-utils.h"
......@@ -42,7 +43,8 @@ class BytecodeGraphBuilder {
CallFrequency const& invocation_frequency,
SourcePositionTable* source_positions, int inlining_id,
CodeKind code_kind, BytecodeGraphBuilderFlags flags,
TickCounter* tick_counter);
TickCounter* tick_counter,
ObserveNodeInfo const& observe_node_info);
BytecodeGraphBuilder(const BytecodeGraphBuilder&) = delete;
BytecodeGraphBuilder& operator=(const BytecodeGraphBuilder&) = delete;
......@@ -530,6 +532,8 @@ class BytecodeGraphBuilder {
TickCounter* const tick_counter_;
ObserveNodeInfo const observe_node_info_;
static constexpr int kBinaryOperationHintIndex = 1;
static constexpr int kBinaryOperationSmiHintIndex = 1;
static constexpr int kCompareOperationHintIndex = 1;
......@@ -1041,7 +1045,8 @@ BytecodeGraphBuilder::BytecodeGraphBuilder(
FeedbackCellRef const& feedback_cell, BytecodeOffset osr_offset,
JSGraph* jsgraph, CallFrequency const& invocation_frequency,
SourcePositionTable* source_positions, int inlining_id, CodeKind code_kind,
BytecodeGraphBuilderFlags flags, TickCounter* tick_counter)
BytecodeGraphBuilderFlags flags, TickCounter* tick_counter,
ObserveNodeInfo const& observe_node_info)
: broker_(broker),
local_zone_(local_zone),
jsgraph_(jsgraph),
......@@ -1087,7 +1092,8 @@ BytecodeGraphBuilder::BytecodeGraphBuilder(
state_values_cache_(jsgraph),
source_positions_(source_positions),
start_position_(shared_info.StartPosition(), inlining_id),
tick_counter_(tick_counter) {}
tick_counter_(tick_counter),
observe_node_info_(observe_node_info) {}
Node* BytecodeGraphBuilder::GetFunctionClosure() {
if (!function_closure_.is_set()) {
......@@ -2742,16 +2748,25 @@ void BytecodeGraphBuilder::VisitCallRuntime() {
interpreter::Register receiver = bytecode_iterator().GetRegisterOperand(1);
size_t reg_count = bytecode_iterator().GetRegisterCountOperand(2);
// Create node to perform the runtime call.
const Operator* call = javascript()->CallRuntime(function_id, reg_count);
Node* value = ProcessCallRuntimeArguments(call, receiver, reg_count);
environment()->BindAccumulator(value, Environment::kAttachFrameState);
// Connect to the end if {function_id} is non-returning.
if (Runtime::IsNonReturning(function_id)) {
// TODO(7099): Investigate if we need LoopExit node here.
Node* control = NewNode(common()->Throw());
MergeControlToLeaveFunction(control);
// Handle %ObserveNode here (rather than in JSIntrinsicLowering) to observe
// the node as early as possible.
if (function_id == Runtime::FunctionId::kObserveNode) {
DCHECK_EQ(1, reg_count);
Node* value = environment()->LookupRegister(receiver);
observe_node_info_.StartObserving(value);
environment()->BindAccumulator(value);
} else {
// Create node to perform the runtime call.
const Operator* call = javascript()->CallRuntime(function_id, reg_count);
Node* value = ProcessCallRuntimeArguments(call, receiver, reg_count);
environment()->BindAccumulator(value, Environment::kAttachFrameState);
// Connect to the end if {function_id} is non-returning.
if (Runtime::IsNonReturning(function_id)) {
// TODO(7099): Investigate if we need LoopExit node here.
Node* control = NewNode(common()->Throw());
MergeControlToLeaveFunction(control);
}
}
}
......@@ -4559,14 +4574,16 @@ void BuildGraphFromBytecode(JSHeapBroker* broker, Zone* local_zone,
SourcePositionTable* source_positions,
int inlining_id, CodeKind code_kind,
BytecodeGraphBuilderFlags flags,
TickCounter* tick_counter) {
TickCounter* tick_counter,
ObserveNodeInfo const& observe_node_info) {
DCHECK(broker->IsSerializedForCompilation(
shared_info, feedback_cell.value()->AsFeedbackVector()));
DCHECK(feedback_cell.value()->AsFeedbackVector().serialized());
BytecodeGraphBuilder builder(
broker, local_zone, broker->target_native_context(), shared_info,
feedback_cell, osr_offset, jsgraph, invocation_frequency,
source_positions, inlining_id, code_kind, flags, tick_counter);
source_positions, inlining_id, code_kind, flags, tick_counter,
observe_node_info);
builder.CreateGraph();
}
......
......@@ -7,6 +7,7 @@
#include "src/compiler/js-operator.h"
#include "src/compiler/js-type-hint-lowering.h"
#include "src/compiler/node-observer.h"
#include "src/handles/handles.h"
#include "src/objects/code-kind.h"
#include "src/utils/utils.h"
......@@ -25,6 +26,7 @@ class Zone;
namespace compiler {
class JSGraph;
class NodeObserver;
class SourcePositionTable;
enum class BytecodeGraphBuilderFlag : uint8_t {
......@@ -47,7 +49,8 @@ void BuildGraphFromBytecode(JSHeapBroker* broker, Zone* local_zone,
SourcePositionTable* source_positions,
int inlining_id, CodeKind code_kind,
BytecodeGraphBuilderFlags flags,
TickCounter* tick_counter);
TickCounter* tick_counter,
ObserveNodeInfo const& observe_node_info = {});
} // namespace compiler
} // namespace internal
......
......@@ -10,6 +10,7 @@
#include "src/codegen/tick-counter.h"
#include "src/compiler/graph.h"
#include "src/compiler/js-heap-broker.h"
#include "src/compiler/node-observer.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/node.h"
#include "src/compiler/verifier.h"
......@@ -28,8 +29,19 @@ enum class GraphReducer::State : uint8_t {
void Reducer::Finalize() {}
Reduction Reducer::Reduce(Node* node,
ObserveNodeManager* observe_node_manager) {
Reduction reduction = Reduce(node);
if (V8_UNLIKELY(observe_node_manager && reduction.Changed())) {
observe_node_manager->OnNodeChanged(reducer_name(), node,
reduction.replacement());
}
return reduction;
}
GraphReducer::GraphReducer(Zone* zone, Graph* graph, TickCounter* tick_counter,
JSHeapBroker* broker, Node* dead)
JSHeapBroker* broker, Node* dead,
ObserveNodeManager* observe_node_manager)
: graph_(graph),
dead_(dead),
state_(graph, 4),
......@@ -37,7 +49,8 @@ GraphReducer::GraphReducer(Zone* zone, Graph* graph, TickCounter* tick_counter,
revisit_(zone),
stack_(zone),
tick_counter_(tick_counter),
broker_(broker) {
broker_(broker),
observe_node_manager_(observe_node_manager) {
if (dead != nullptr) {
NodeProperties::SetType(dead_, Type::None());
}
......@@ -89,7 +102,7 @@ Reduction GraphReducer::Reduce(Node* const node) {
for (auto i = reducers_.begin(); i != reducers_.end();) {
if (i != skip) {
tick_counter_->TickAndMaybeEnterSafepoint();
Reduction reduction = (*i)->Reduce(node);
Reduction reduction = (*i)->Reduce(node, observe_node_manager_);
if (!reduction.Changed()) {
// No change from this reducer.
} else if (reduction.replacement() == node) {
......
......@@ -20,6 +20,7 @@ namespace compiler {
class Graph;
class JSHeapBroker;
class Node;
class ObserveNodeManager;
// NodeIds are identifying numbers for nodes that can be used to index auxiliary
// out-of-line data associated with each node.
......@@ -58,7 +59,7 @@ class V8_EXPORT_PRIVATE Reducer {
virtual const char* reducer_name() const = 0;
// Try to reduce a node if possible.
virtual Reduction Reduce(Node* node) = 0;
Reduction Reduce(Node* node, ObserveNodeManager* observe_node_manager);
// Invoked by the {GraphReducer} when all nodes are done. Can be used to
// do additional reductions at the end, which in turn can cause a new round
......@@ -69,6 +70,9 @@ class V8_EXPORT_PRIVATE Reducer {
static Reduction NoChange() { return Reduction(); }
static Reduction Replace(Node* node) { return Reduction(node); }
static Reduction Changed(Node* node) { return Reduction(node); }
private:
virtual Reduction Reduce(Node* node) = 0;
};
......@@ -136,7 +140,8 @@ class V8_EXPORT_PRIVATE GraphReducer
: public NON_EXPORTED_BASE(AdvancedReducer::Editor) {
public:
GraphReducer(Zone* zone, Graph* graph, TickCounter* tick_counter,
JSHeapBroker* broker, Node* dead = nullptr);
JSHeapBroker* broker, Node* dead = nullptr,
ObserveNodeManager* observe_node_manager = nullptr);
~GraphReducer() override;
GraphReducer(const GraphReducer&) = delete;
......@@ -193,6 +198,7 @@ class V8_EXPORT_PRIVATE GraphReducer
ZoneStack<NodeState> stack_;
TickCounter* const tick_counter_;
JSHeapBroker* const broker_;
ObserveNodeManager* const observe_node_manager_;
};
} // namespace compiler
......
// Copyright 2021 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/node-observer.h"
#include "src/compiler/node-properties.h"
namespace v8 {
namespace internal {
namespace compiler {
ObservableNodeState::ObservableNodeState(const Node* node, Zone* zone)
: id_(node->id()),
op_(node->op()),
type_(NodeProperties::GetTypeOrAny(node)) {}
void ObserveNodeManager::StartObserving(Node* node, NodeObserver* observer) {
DCHECK_NOT_NULL(node);
DCHECK_NOT_NULL(observer);
DCHECK(observations_.find(node->id()) == observations_.end());
NodeObserver::Observation observation = observer->OnNodeCreated(node);
if (observation == NodeObserver::Observation::kContinue) {
observations_[node->id()] =
zone_->New<NodeObservation>(observer, node, zone_);
} else {
DCHECK_EQ(observation, NodeObserver::Observation::kStop);
}
}
void ObserveNodeManager::OnNodeChanged(const char* reducer_name,
const Node* old_node,
const Node* new_node) {
const auto it = observations_.find(old_node->id());
if (it == observations_.end()) return;
ObservableNodeState new_state{new_node, zone_};
NodeObservation* observation = it->second;
if (observation->state == new_state) return;
ObservableNodeState old_state = observation->state;
observation->state = new_state;
NodeObserver::Observation result =
observation->observer->OnNodeChanged(reducer_name, new_node, old_state);
if (result == NodeObserver::Observation::kStop) {
observations_.erase(old_node->id());
} else {
DCHECK_EQ(result, NodeObserver::Observation::kContinue);
if (old_node != new_node) {
observations_.erase(old_node->id());
observations_[new_node->id()] = observation;
}
}
}
} // namespace compiler
} // namespace internal
} // namespace v8
// Copyright 2021 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.
// This file declares the implementation of a new intrinsic %ObserveNode(expr),
// which has noop semantics but triggers the invocation of callbacks on a
// NodeObserver object. The NodeObserver is set on the OptimizedCompilationInfo
// and callbacks are called when the node generated for 'expr' is created or
// changed in any phase, until EffectControlLinearization.
//
// The modifications currently observed are changes to the observed Node
// operator and type and its replacement with another Node.
//
// This provides the infrastructure to write unit tests that check for the
// construction of or the lowering to specific nodes in the TurboFan graphs.
#ifndef V8_COMPILER_NODE_OBSERVER_H_
#define V8_COMPILER_NODE_OBSERVER_H_
#include "src/compiler/node.h"
#include "src/compiler/operator.h"
#include "src/zone/zone.h"
namespace v8 {
namespace internal {
namespace compiler {
class Node;
class Operator;
class ObservableNodeState {
public:
ObservableNodeState(const Node* node, Zone* zone);
uint32_t id() const { return id_; }
const Operator* op() const { return op_; }
int16_t opcode() const { return op_->opcode(); }
Type type() const { return type_; }
private:
uint32_t id_;
const Operator* op_;
Type type_;
};
inline bool operator==(const ObservableNodeState& lhs,
const ObservableNodeState& rhs) {
return lhs.id() == rhs.id() && lhs.op() == rhs.op() &&
lhs.type() == rhs.type();
}
inline bool operator!=(const ObservableNodeState& lhs,
const ObservableNodeState& rhs) {
return !operator==(lhs, rhs);
}
class NodeObserver : public ZoneObject {
public:
enum class Observation {
kContinue,
kStop,
};
NodeObserver() = default;
virtual ~NodeObserver() = 0;
NodeObserver(const NodeObserver&) = delete;
NodeObserver& operator=(const NodeObserver&) = delete;
virtual Observation OnNodeCreated(const Node* node) {
return Observation::kContinue;
}
virtual Observation OnNodeChanged(const char* reducer_name, const Node* node,
const ObservableNodeState& old_state) {
return Observation::kContinue;
}
};
inline NodeObserver::~NodeObserver() = default;
struct NodeObservation : public ZoneObject {
NodeObservation(NodeObserver* node_observer, const Node* node, Zone* zone)
: observer(node_observer), state(node, zone) {
DCHECK_NOT_NULL(node_observer);
}
NodeObserver* observer;
ObservableNodeState state;
};
class ObserveNodeManager : public ZoneObject {
public:
explicit ObserveNodeManager(Zone* zone) : zone_(zone), observations_(zone) {}
void StartObserving(Node* node, NodeObserver* observer);
void OnNodeChanged(const char* reducer_name, const Node* old_node,
const Node* new_node);
private:
Zone* zone_;
ZoneMap<NodeId, NodeObservation*> observations_;
};
struct ObserveNodeInfo {
ObserveNodeInfo() : observe_node_manager(nullptr), node_observer(nullptr) {}
ObserveNodeInfo(ObserveNodeManager* manager, NodeObserver* observer)
: observe_node_manager(manager), node_observer(observer) {}
void StartObserving(Node* node) const {
if (observe_node_manager) {
DCHECK_NOT_NULL(node_observer);
observe_node_manager->StartObserving(node, node_observer);
}
}
ObserveNodeManager* observe_node_manager;
NodeObserver* node_observer;
};
} // namespace compiler
} // namespace internal
} // namespace v8
#endif // V8_COMPILER_NODE_OBSERVER_H_
......@@ -568,11 +568,10 @@ Node* NodeProperties::GetOuterContext(Node* node, size_t* depth) {
}
// static
Type NodeProperties::GetTypeOrAny(Node* node) {
Type NodeProperties::GetTypeOrAny(const Node* node) {
return IsTyped(node) ? node->type() : Type::Any();
}
// static
bool NodeProperties::AllValueInputsAreTyped(Node* node) {
int input_count = node->op()->ValueInputCount();
......
......@@ -21,7 +21,7 @@ class Operator;
class CommonOperatorBuilder;
// A facade that simplifies access to the different kinds of inputs to a node.
class V8_EXPORT_PRIVATE NodeProperties final {
class V8_EXPORT_PRIVATE NodeProperties {
public:
// ---------------------------------------------------------------------------
// Input layout.
......@@ -244,12 +244,12 @@ class V8_EXPORT_PRIVATE NodeProperties final {
// ---------------------------------------------------------------------------
// Type.
static bool IsTyped(Node* node) { return !node->type().IsInvalid(); }
static bool IsTyped(const Node* node) { return !node->type().IsInvalid(); }
static Type GetType(Node* node) {
DCHECK(IsTyped(node));
return node->type();
}
static Type GetTypeOrAny(Node* node);
static Type GetTypeOrAny(const Node* node);
static void SetType(Node* node, Type type) {
DCHECK(!type.IsInvalid());
node->set_type(type);
......
......@@ -60,6 +60,7 @@
#include "src/compiler/machine-graph-verifier.h"
#include "src/compiler/machine-operator-reducer.h"
#include "src/compiler/memory-optimizer.h"
#include "src/compiler/node-observer.h"
#include "src/compiler/node-origin-table.h"
#include "src/compiler/osr.h"
#include "src/compiler/pipeline-statistics.h"
......@@ -174,6 +175,10 @@ class PipelineData {
javascript_ = graph_zone_->New<JSOperatorBuilder>(graph_zone_);
jsgraph_ = graph_zone_->New<JSGraph>(isolate_, graph_, common_, javascript_,
simplified_, machine_);
observe_node_manager_ =
info->node_observer()
? graph_zone_->New<ObserveNodeManager>(graph_zone_)
: nullptr;
dependencies_ =
info_->zone()->New<CompilationDependencies>(broker_, info_->zone());
}
......@@ -346,6 +351,10 @@ class PipelineData {
}
void reset_schedule() { schedule_ = nullptr; }
ObserveNodeManager* observe_node_manager() const {
return observe_node_manager_;
}
Zone* instruction_zone() const { return instruction_zone_; }
Zone* codegen_zone() const { return codegen_zone_; }
InstructionSequence* sequence() const { return sequence_; }
......@@ -600,6 +609,7 @@ class PipelineData {
JSGraph* jsgraph_ = nullptr;
MachineGraph* mcgraph_ = nullptr;
Schedule* schedule_ = nullptr;
ObserveNodeManager* observe_node_manager_ = nullptr;
// All objects in the following group of fields are allocated in
// instruction_zone_. They are all set to nullptr when the instruction_zone_
......@@ -693,6 +703,8 @@ class PipelineImpl final {
Isolate* isolate() const;
CodeGenerator* code_generator() const;
ObserveNodeManager* observe_node_manager() const;
private:
PipelineData* const data_;
};
......@@ -712,7 +724,7 @@ class SourcePositionWrapper final : public Reducer {
Reduction Reduce(Node* node) final {
SourcePosition const pos = table_->GetSourcePosition(node);
SourcePositionTable::Scope position(table_, pos);
return reducer_->Reduce(node);
return reducer_->Reduce(node, nullptr);
}
void Finalize() final { reducer_->Finalize(); }
......@@ -734,7 +746,7 @@ class NodeOriginsWrapper final : public Reducer {
Reduction Reduce(Node* node) final {
NodeOriginTable::Scope position(table_, reducer_name(), node);
return reducer_->Reduce(node);
return reducer_->Reduce(node, nullptr);
}
void Finalize() final { reducer_->Finalize(); }
......@@ -1422,7 +1434,9 @@ struct GraphBuilderPhase {
closure.raw_feedback_cell(), data->info()->osr_offset(),
data->jsgraph(), frequency, data->source_positions(),
SourcePosition::kNotInlined, data->info()->code_kind(), flags,
&data->info()->tick_counter());
&data->info()->tick_counter(),
ObserveNodeInfo{data->observe_node_manager(),
data->info()->node_observer()});
}
};
......@@ -1432,7 +1446,8 @@ struct InliningPhase {
void Run(PipelineData* data, Zone* temp_zone) {
OptimizedCompilationInfo* info = data->info();
GraphReducer graph_reducer(temp_zone, data->graph(), &info->tick_counter(),
data->broker(), data->jsgraph()->Dead());
data->broker(), data->jsgraph()->Dead(),
data->observe_node_manager());
DeadCodeElimination dead_code_elimination(&graph_reducer, data->graph(),
data->common(), temp_zone);
CheckpointElimination checkpoint_elimination(&graph_reducer);
......@@ -1530,9 +1545,9 @@ struct UntyperPhase {
NodeProperties::RemoveType(node);
}
GraphReducer graph_reducer(temp_zone, data->graph(),
&data->info()->tick_counter(), data->broker(),
data->jsgraph()->Dead());
GraphReducer graph_reducer(
temp_zone, data->graph(), &data->info()->tick_counter(), data->broker(),
data->jsgraph()->Dead(), data->observe_node_manager());
RemoveTypeReducer remove_type_reducer;
AddReducer(data, &graph_reducer, &remove_type_reducer);
graph_reducer.ReduceGraph();
......@@ -1551,9 +1566,9 @@ struct CopyMetadataForConcurrentCompilePhase {
DECL_MAIN_THREAD_PIPELINE_PHASE_CONSTANTS(SerializeMetadata)
void Run(PipelineData* data, Zone* temp_zone) {
GraphReducer graph_reducer(temp_zone, data->graph(),
&data->info()->tick_counter(), data->broker(),
data->jsgraph()->Dead());
GraphReducer graph_reducer(
temp_zone, data->graph(), &data->info()->tick_counter(), data->broker(),
data->jsgraph()->Dead(), data->observe_node_manager());
JSHeapCopyReducer heap_copy_reducer(data->broker());
AddReducer(data, &graph_reducer, &heap_copy_reducer);
graph_reducer.ReduceGraph();
......@@ -1597,9 +1612,9 @@ struct TypedLoweringPhase {
DECL_PIPELINE_PHASE_CONSTANTS(TypedLowering)
void Run(PipelineData* data, Zone* temp_zone) {
GraphReducer graph_reducer(temp_zone, data->graph(),
&data->info()->tick_counter(), data->broker(),
data->jsgraph()->Dead());
GraphReducer graph_reducer(
temp_zone, data->graph(), &data->info()->tick_counter(), data->broker(),
data->jsgraph()->Dead(), data->observe_node_manager());
DeadCodeElimination dead_code_elimination(&graph_reducer, data->graph(),
data->common(), temp_zone);
JSCreateLowering create_lowering(&graph_reducer, data->dependencies(),
......@@ -1648,7 +1663,7 @@ struct EscapeAnalysisPhase {
GraphReducer reducer(temp_zone, data->graph(),
&data->info()->tick_counter(), data->broker(),
data->jsgraph()->Dead());
data->jsgraph()->Dead(), data->observe_node_manager());
EscapeAnalysisReducer escape_reducer(&reducer, data->jsgraph(),
escape_analysis.analysis_result(),
temp_zone);
......@@ -1669,9 +1684,9 @@ struct TypeAssertionsPhase {
DECL_PIPELINE_PHASE_CONSTANTS(TypeAssertions)
void Run(PipelineData* data, Zone* temp_zone) {
GraphReducer graph_reducer(temp_zone, data->graph(),
&data->info()->tick_counter(), data->broker(),
data->jsgraph()->Dead());
GraphReducer graph_reducer(
temp_zone, data->graph(), &data->info()->tick_counter(), data->broker(),
data->jsgraph()->Dead(), data->observe_node_manager());
AddTypeAssertionsReducer type_assertions(&graph_reducer, data->jsgraph(),
temp_zone);
AddReducer(data, &graph_reducer, &type_assertions);
......@@ -1683,10 +1698,10 @@ struct SimplifiedLoweringPhase {
DECL_PIPELINE_PHASE_CONSTANTS(SimplifiedLowering)
void Run(PipelineData* data, Zone* temp_zone, Linkage* linkage) {
SimplifiedLowering lowering(data->jsgraph(), data->broker(), temp_zone,
data->source_positions(), data->node_origins(),
data->info()->GetPoisoningMitigationLevel(),
&data->info()->tick_counter(), linkage);
SimplifiedLowering lowering(
data->jsgraph(), data->broker(), temp_zone, data->source_positions(),
data->node_origins(), data->info()->GetPoisoningMitigationLevel(),
&data->info()->tick_counter(), linkage, data->observe_node_manager());
// RepresentationChanger accesses the heap.
UnparkedScopeIfNeeded scope(data->broker());
......@@ -1730,9 +1745,9 @@ struct GenericLoweringPhase {
DECL_PIPELINE_PHASE_CONSTANTS(GenericLowering)
void Run(PipelineData* data, Zone* temp_zone) {
GraphReducer graph_reducer(temp_zone, data->graph(),
&data->info()->tick_counter(), data->broker(),
data->jsgraph()->Dead());
GraphReducer graph_reducer(
temp_zone, data->graph(), &data->info()->tick_counter(), data->broker(),
data->jsgraph()->Dead(), data->observe_node_manager());
JSGenericLowering generic_lowering(data->jsgraph(), &graph_reducer,
data->broker());
AddReducer(data, &graph_reducer, &generic_lowering);
......@@ -1748,9 +1763,9 @@ struct EarlyOptimizationPhase {
DECL_PIPELINE_PHASE_CONSTANTS(EarlyOptimization)
void Run(PipelineData* data, Zone* temp_zone) {
GraphReducer graph_reducer(temp_zone, data->graph(),
&data->info()->tick_counter(), data->broker(),
data->jsgraph()->Dead());
GraphReducer graph_reducer(
temp_zone, data->graph(), &data->info()->tick_counter(), data->broker(),
data->jsgraph()->Dead(), data->observe_node_manager());
DeadCodeElimination dead_code_elimination(&graph_reducer, data->graph(),
data->common(), temp_zone);
SimplifiedOperatorReducer simple_reducer(&graph_reducer, data->jsgraph(),
......@@ -1831,7 +1846,8 @@ struct EffectControlLinearizationPhase {
// it, to eliminate conditional deopts with a constant condition.
GraphReducer graph_reducer(temp_zone, data->graph(),
&data->info()->tick_counter(), data->broker(),
data->jsgraph()->Dead());
data->jsgraph()->Dead(),
data->observe_node_manager());
DeadCodeElimination dead_code_elimination(&graph_reducer, data->graph(),
data->common(), temp_zone);
CommonOperatorReducer common_reducer(&graph_reducer, data->graph(),
......@@ -1865,9 +1881,9 @@ struct LoadEliminationPhase {
DECL_PIPELINE_PHASE_CONSTANTS(LoadElimination)
void Run(PipelineData* data, Zone* temp_zone) {
GraphReducer graph_reducer(temp_zone, data->graph(),
&data->info()->tick_counter(), data->broker(),
data->jsgraph()->Dead());
GraphReducer graph_reducer(
temp_zone, data->graph(), &data->info()->tick_counter(), data->broker(),
data->jsgraph()->Dead(), data->observe_node_manager());
BranchElimination branch_condition_elimination(&graph_reducer,
data->jsgraph(), temp_zone,
BranchElimination::kEARLY);
......@@ -1934,9 +1950,9 @@ struct LateOptimizationPhase {
DECL_PIPELINE_PHASE_CONSTANTS(LateOptimization)
void Run(PipelineData* data, Zone* temp_zone) {
GraphReducer graph_reducer(temp_zone, data->graph(),
&data->info()->tick_counter(), data->broker(),
data->jsgraph()->Dead());
GraphReducer graph_reducer(
temp_zone, data->graph(), &data->info()->tick_counter(), data->broker(),
data->jsgraph()->Dead(), data->observe_node_manager());
BranchElimination branch_condition_elimination(&graph_reducer,
data->jsgraph(), temp_zone);
DeadCodeElimination dead_code_elimination(&graph_reducer, data->graph(),
......@@ -1962,9 +1978,9 @@ struct MachineOperatorOptimizationPhase {
DECL_PIPELINE_PHASE_CONSTANTS(MachineOperatorOptimization)
void Run(PipelineData* data, Zone* temp_zone) {
GraphReducer graph_reducer(temp_zone, data->graph(),
&data->info()->tick_counter(), data->broker(),
data->jsgraph()->Dead());
GraphReducer graph_reducer(
temp_zone, data->graph(), &data->info()->tick_counter(), data->broker(),
data->jsgraph()->Dead(), data->observe_node_manager());
ValueNumberingReducer value_numbering(temp_zone, data->graph()->zone());
MachineOperatorReducer machine_reducer(&graph_reducer, data->jsgraph());
......@@ -2034,9 +2050,9 @@ struct CsaEarlyOptimizationPhase {
DECL_PIPELINE_PHASE_CONSTANTS(CSAEarlyOptimization)
void Run(PipelineData* data, Zone* temp_zone) {
GraphReducer graph_reducer(temp_zone, data->graph(),
&data->info()->tick_counter(), data->broker(),
data->jsgraph()->Dead());
GraphReducer graph_reducer(
temp_zone, data->graph(), &data->info()->tick_counter(), data->broker(),
data->jsgraph()->Dead(), data->observe_node_manager());
MachineOperatorReducer machine_reducer(&graph_reducer, data->jsgraph());
BranchElimination branch_condition_elimination(&graph_reducer,
data->jsgraph(), temp_zone);
......@@ -2062,9 +2078,9 @@ struct CsaOptimizationPhase {
DECL_PIPELINE_PHASE_CONSTANTS(CSAOptimization)
void Run(PipelineData* data, Zone* temp_zone) {
GraphReducer graph_reducer(temp_zone, data->graph(),
&data->info()->tick_counter(), data->broker(),
data->jsgraph()->Dead());
GraphReducer graph_reducer(
temp_zone, data->graph(), &data->info()->tick_counter(), data->broker(),
data->jsgraph()->Dead(), data->observe_node_manager());
BranchElimination branch_condition_elimination(&graph_reducer,
data->jsgraph(), temp_zone);
DeadCodeElimination dead_code_elimination(&graph_reducer, data->graph(),
......@@ -3183,9 +3199,9 @@ void Pipeline::GenerateCodeForWasmFunction(
if (FLAG_wasm_opt || is_asm_js) {
PipelineRunScope scope(&data, "V8.WasmFullOptimization",
RuntimeCallCounterId::kOptimizeWasmFullOptimization);
GraphReducer graph_reducer(scope.zone(), data.graph(),
&data.info()->tick_counter(), data.broker(),
data.mcgraph()->Dead());
GraphReducer graph_reducer(
scope.zone(), data.graph(), &data.info()->tick_counter(), data.broker(),
data.mcgraph()->Dead(), data.observe_node_manager());
DeadCodeElimination dead_code_elimination(&graph_reducer, data.graph(),
data.common(), scope.zone());
ValueNumberingReducer value_numbering(scope.zone(), data.graph()->zone());
......@@ -3203,9 +3219,9 @@ void Pipeline::GenerateCodeForWasmFunction(
} else {
PipelineRunScope scope(&data, "V8.OptimizeWasmBaseOptimization",
RuntimeCallCounterId::kOptimizeWasmBaseOptimization);
GraphReducer graph_reducer(scope.zone(), data.graph(),
&data.info()->tick_counter(), data.broker(),
data.mcgraph()->Dead());
GraphReducer graph_reducer(
scope.zone(), data.graph(), &data.info()->tick_counter(), data.broker(),
data.mcgraph()->Dead(), data.observe_node_manager());
ValueNumberingReducer value_numbering(scope.zone(), data.graph()->zone());
AddReducer(&data, &graph_reducer, &value_numbering);
graph_reducer.ReduceGraph();
......@@ -3774,6 +3790,10 @@ CodeGenerator* PipelineImpl::code_generator() const {
return data_->code_generator();
}
ObserveNodeManager* PipelineImpl::observe_node_manager() const {
return data_->observe_node_manager();
}
} // namespace compiler
} // namespace internal
} // namespace v8
......@@ -38,7 +38,7 @@ void ScheduledMachineLowering::Run() {
Node* node = *instr;
Reduction reduction;
for (auto reducer : reducers_) {
reduction = reducer->Reduce(node);
reduction = reducer->Reduce(node, nullptr);
if (reduction.Changed()) break;
}
if (reduction.Changed()) {
......
......@@ -18,8 +18,8 @@
#include "src/compiler/diamond.h"
#include "src/compiler/linkage.h"
#include "src/compiler/node-matchers.h"
#include "src/compiler/node-observer.h"
#include "src/compiler/node-origin-table.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/operation-typer.h"
#include "src/compiler/operator-properties.h"
#include "src/compiler/representation-change.h"
......@@ -39,6 +39,8 @@ namespace compiler {
if (FLAG_trace_representation) PrintF(__VA_ARGS__); \
} while (false)
const char* kSimplifiedLoweringReducerName = "SimplifiedLowering";
// Representation selection and lowering of {Simplified} operators to machine
// operators are interwined. We use a fixpoint calculation to compute both the
// output representation and the best possible lowering for {Simplified} nodes.
......@@ -241,6 +243,16 @@ class InputUseInfos {
#endif // DEBUG
class RepresentationSelector {
// The purpose of this nested class is to hide method
// v8::internal::compiler::NodeProperties::ChangeOp which should not be
// directly used by code in RepresentationSelector and SimplifiedLowering.
// RepresentationSelector code should call RepresentationSelector::ChangeOp in
// place of NodeProperties::ChangeOp, in order to notify the changes to a
// registered ObserveNodeManager and support the %ObserveNode intrinsic.
class NodeProperties : public compiler::NodeProperties {
static void ChangeOp(Node* node, const Operator* new_op) { UNREACHABLE(); }
};
public:
// Information for each node tracked during the fixpoint.
class NodeInfo final {
......@@ -290,7 +302,8 @@ class RepresentationSelector {
RepresentationChanger* changer,
SourcePositionTable* source_positions,
NodeOriginTable* node_origins,
TickCounter* tick_counter, Linkage* linkage)
TickCounter* tick_counter, Linkage* linkage,
ObserveNodeManager* observe_node_manager)
: jsgraph_(jsgraph),
zone_(zone),
might_need_revisit_(zone),
......@@ -308,7 +321,8 @@ class RepresentationSelector {
type_cache_(TypeCache::Get()),
op_typer_(broker, graph_zone()),
tick_counter_(tick_counter),
linkage_(linkage) {
linkage_(linkage),
observe_node_manager_(observe_node_manager) {
}
void ResetNodeInfoState() {
......@@ -772,7 +786,7 @@ class RepresentationSelector {
node->ReplaceInput(0, unreachable);
node->TrimInputCount(dead_value->ValueInputCount());
ReplaceEffectControlUses(node, effect, control);
NodeProperties::ChangeOp(node, dead_value);
ChangeOp(node, dead_value);
}
void ChangeToPureOp(Node* node, const Operator* new_op) {
......@@ -792,7 +806,7 @@ class RepresentationSelector {
} else {
DCHECK_EQ(0, node->op()->ControlInputCount());
}
NodeProperties::ChangeOp(node, new_op);
ChangeOp(node, new_op);
}
void ChangeUnaryToPureBinaryOp(Node* node, const Operator* new_op,
......@@ -816,7 +830,7 @@ class RepresentationSelector {
DCHECK_EQ(0, node->op()->ControlInputCount());
}
node->InsertInput(jsgraph_->zone(), new_input_index, new_input);
NodeProperties::ChangeOp(node, new_op);
ChangeOp(node, new_op);
}
// Converts input {index} of {node} according to given UseInfo {use},
......@@ -1038,8 +1052,7 @@ class RepresentationSelector {
// Update the select operator.
SelectParameters p = SelectParametersOf(node->op());
if (output != p.representation()) {
NodeProperties::ChangeOp(node,
lowering->common()->Select(output, p.hint()));
ChangeOp(node, lowering->common()->Select(output, p.hint()));
}
}
// Convert inputs to the output representation of this phi, pass the
......@@ -1063,7 +1076,7 @@ class RepresentationSelector {
if (lower<T>()) {
// Update the phi operator.
if (output != PhiRepresentationOf(node->op())) {
NodeProperties::ChangeOp(node, lowering->common()->Phi(output, values));
ChangeOp(node, lowering->common()->Phi(output, values));
}
}
......@@ -1216,8 +1229,7 @@ class RepresentationSelector {
DeoptMachineTypeOf(GetInfo(input)->representation(), TypeOf(input));
}
SparseInputMask mask = SparseInputMaskOf(node->op());
NodeProperties::ChangeOp(
node, jsgraph_->common()->TypedStateValues(types, mask));
ChangeOp(node, jsgraph_->common()->TypedStateValues(types, mask));
}
SetOutput<T>(node, MachineRepresentation::kTagged);
}
......@@ -1307,8 +1319,8 @@ class RepresentationSelector {
ConvertInput(node, i, UseInfo::AnyTagged());
}
}
NodeProperties::ChangeOp(node, jsgraph_->common()->TypedObjectState(
ObjectIdOf(node->op()), types));
ChangeOp(node, jsgraph_->common()->TypedObjectState(
ObjectIdOf(node->op()), types));
}
SetOutput<T>(node, MachineRepresentation::kTagged);
}
......@@ -1417,15 +1429,15 @@ class RepresentationSelector {
IsSomePositiveOrderedNumber(input1_type)
? CheckForMinusZeroMode::kDontCheckForMinusZero
: CheckForMinusZeroMode::kCheckForMinusZero;
NodeProperties::ChangeOp(node, simplified()->CheckedInt32Mul(mz_mode));
ChangeOp(node, simplified()->CheckedInt32Mul(mz_mode));
}
void ChangeToInt32OverflowOp(Node* node) {
NodeProperties::ChangeOp(node, Int32OverflowOp(node));
ChangeOp(node, Int32OverflowOp(node));
}
void ChangeToUint32OverflowOp(Node* node) {
NodeProperties::ChangeOp(node, Uint32OverflowOp(node));
ChangeOp(node, Uint32OverflowOp(node));
}
template <Phase T>
......@@ -1685,19 +1697,19 @@ class RepresentationSelector {
// TODO(neis): Move this into TypedOptimization?
new_flags |= CheckBoundsFlag::kAbortOnOutOfBounds;
}
NodeProperties::ChangeOp(
node, simplified()->CheckedUint32Bounds(feedback, new_flags));
ChangeOp(node,
simplified()->CheckedUint32Bounds(feedback, new_flags));
}
} else if (p.flags() & CheckBoundsFlag::kConvertStringAndMinusZero) {
VisitBinop<T>(node, UseInfo::CheckedTaggedAsArrayIndex(feedback),
UseInfo::Word(), MachineType::PointerRepresentation());
if (lower<T>()) {
if (jsgraph_->machine()->Is64()) {
NodeProperties::ChangeOp(
node, simplified()->CheckedUint64Bounds(feedback, new_flags));
ChangeOp(node,
simplified()->CheckedUint64Bounds(feedback, new_flags));
} else {
NodeProperties::ChangeOp(
node, simplified()->CheckedUint32Bounds(feedback, new_flags));
ChangeOp(node,
simplified()->CheckedUint32Bounds(feedback, new_flags));
}
}
} else {
......@@ -1705,8 +1717,8 @@ class RepresentationSelector {
node, UseInfo::CheckedSigned32AsWord32(kDistinguishZeros, feedback),
UseInfo::TruncatingWord32(), MachineRepresentation::kWord32);
if (lower<T>()) {
NodeProperties::ChangeOp(
node, simplified()->CheckedUint32Bounds(feedback, new_flags));
ChangeOp(node,
simplified()->CheckedUint32Bounds(feedback, new_flags));
}
}
} else {
......@@ -1719,8 +1731,7 @@ class RepresentationSelector {
UseInfo::CheckedSigned64AsWord64(zero_handling, feedback),
UseInfo::Word64(), MachineRepresentation::kWord64);
if (lower<T>()) {
NodeProperties::ChangeOp(
node, simplified()->CheckedUint64Bounds(feedback, new_flags));
ChangeOp(node, simplified()->CheckedUint64Bounds(feedback, new_flags));
}
}
}
......@@ -1947,11 +1958,11 @@ class RepresentationSelector {
if (input_info->representation() == MachineRepresentation::kBit) {
// BooleanNot(x: kRepBit) => Word32Equal(x, #0)
node->AppendInput(jsgraph_->zone(), jsgraph_->Int32Constant(0));
NodeProperties::ChangeOp(node, lowering->machine()->Word32Equal());
ChangeOp(node, lowering->machine()->Word32Equal());
} else if (CanBeTaggedPointer(input_info->representation())) {
// BooleanNot(x: kRepTagged) => WordEqual(x, #false)
node->AppendInput(jsgraph_->zone(), jsgraph_->FalseConstant());
NodeProperties::ChangeOp(node, lowering->machine()->WordEqual());
ChangeOp(node, lowering->machine()->WordEqual());
} else {
DCHECK(TypeOf(node->InputAt(0)).IsNone());
DeferReplacement(node, lowering->jsgraph()->Int32Constant(0));
......@@ -1979,7 +1990,7 @@ class RepresentationSelector {
// => unsigned Int32Cmp
VisitBinop<T>(node, UseInfo::TruncatingWord32(),
MachineRepresentation::kBit);
if (lower<T>()) NodeProperties::ChangeOp(node, Uint32Op(node));
if (lower<T>()) ChangeOp(node, Uint32Op(node));
return;
}
if ((lhs_type.Is(Type::Signed32OrMinusZero()) &&
......@@ -1990,13 +2001,13 @@ class RepresentationSelector {
// => signed Int32Cmp
VisitBinop<T>(node, UseInfo::TruncatingWord32(),
MachineRepresentation::kBit);
if (lower<T>()) NodeProperties::ChangeOp(node, Int32Op(node));
if (lower<T>()) ChangeOp(node, Int32Op(node));
return;
}
// => Float64Cmp
VisitBinop<T>(node, UseInfo::TruncatingFloat64(kIdentifyZeros),
MachineRepresentation::kBit);
if (lower<T>()) NodeProperties::ChangeOp(node, Float64Op(node));
if (lower<T>()) ChangeOp(node, Float64Op(node));
return;
}
case IrOpcode::kNumberLessThan:
......@@ -2011,18 +2022,18 @@ class RepresentationSelector {
// => unsigned Int32Cmp
VisitBinop<T>(node, UseInfo::TruncatingWord32(),
MachineRepresentation::kBit);
if (lower<T>()) NodeProperties::ChangeOp(node, Uint32Op(node));
if (lower<T>()) ChangeOp(node, Uint32Op(node));
} else if (lhs_type.Is(Type::Signed32OrMinusZero()) &&
rhs_type.Is(Type::Signed32OrMinusZero())) {
// => signed Int32Cmp
VisitBinop<T>(node, UseInfo::TruncatingWord32(),
MachineRepresentation::kBit);
if (lower<T>()) NodeProperties::ChangeOp(node, Int32Op(node));
if (lower<T>()) ChangeOp(node, Int32Op(node));
} else {
// => Float64Cmp
VisitBinop<T>(node, UseInfo::TruncatingFloat64(kIdentifyZeros),
MachineRepresentation::kBit);
if (lower<T>()) NodeProperties::ChangeOp(node, Float64Op(node));
if (lower<T>()) ChangeOp(node, Float64Op(node));
}
return;
}
......@@ -2348,7 +2359,7 @@ class RepresentationSelector {
case IrOpcode::kNumberBitwiseXor:
case IrOpcode::kNumberBitwiseAnd: {
VisitWord32TruncatingBinop<T>(node);
if (lower<T>()) NodeProperties::ChangeOp(node, Int32Op(node));
if (lower<T>()) ChangeOp(node, Int32Op(node));
return;
}
case IrOpcode::kSpeculativeNumberBitwiseOr:
......@@ -2450,8 +2461,8 @@ class RepresentationSelector {
MachineRepresentation::kWord32, Type::Unsigned31());
if (lower<T>()) {
node->RemoveInput(1);
NodeProperties::ChangeOp(
node, simplified()->CheckedUint32ToInt32(FeedbackSource()));
ChangeOp(node,
simplified()->CheckedUint32ToInt32(FeedbackSource()));
}
return;
}
......@@ -2493,27 +2504,27 @@ class RepresentationSelector {
} else {
VisitUnop<T>(node, UseInfo::TruncatingFloat64(kIdentifyZeros),
MachineRepresentation::kFloat64);
if (lower<T>()) NodeProperties::ChangeOp(node, Float64Op(node));
if (lower<T>()) ChangeOp(node, Float64Op(node));
}
return;
}
case IrOpcode::kNumberClz32: {
VisitUnop<T>(node, UseInfo::TruncatingWord32(),
MachineRepresentation::kWord32);
if (lower<T>()) NodeProperties::ChangeOp(node, Uint32Op(node));
if (lower<T>()) ChangeOp(node, Uint32Op(node));
return;
}
case IrOpcode::kNumberImul: {
VisitBinop<T>(node, UseInfo::TruncatingWord32(),
UseInfo::TruncatingWord32(),
MachineRepresentation::kWord32);
if (lower<T>()) NodeProperties::ChangeOp(node, Uint32Op(node));
if (lower<T>()) ChangeOp(node, Uint32Op(node));
return;
}
case IrOpcode::kNumberFround: {
VisitUnop<T>(node, UseInfo::TruncatingFloat64(),
MachineRepresentation::kFloat32);
if (lower<T>()) NodeProperties::ChangeOp(node, Float64Op(node));
if (lower<T>()) ChangeOp(node, Float64Op(node));
return;
}
case IrOpcode::kNumberMax: {
......@@ -2568,7 +2579,7 @@ class RepresentationSelector {
lowering->DoMax(node, lowering->machine()->Float64LessThan(),
MachineRepresentation::kFloat64);
} else {
NodeProperties::ChangeOp(node, Float64Op(node));
ChangeOp(node, Float64Op(node));
}
}
}
......@@ -2627,7 +2638,7 @@ class RepresentationSelector {
lowering->machine()->Float64LessThanOrEqual(),
MachineRepresentation::kFloat64);
} else {
NodeProperties::ChangeOp(node, Float64Op(node));
ChangeOp(node, Float64Op(node));
}
}
}
......@@ -2637,7 +2648,7 @@ class RepresentationSelector {
case IrOpcode::kNumberPow: {
VisitBinop<T>(node, UseInfo::TruncatingFloat64(),
MachineRepresentation::kFloat64);
if (lower<T>()) NodeProperties::ChangeOp(node, Float64Op(node));
if (lower<T>()) ChangeOp(node, Float64Op(node));
return;
}
case IrOpcode::kNumberCeil:
......@@ -2658,7 +2669,7 @@ class RepresentationSelector {
} else if (node->opcode() == IrOpcode::kNumberRound) {
DeferReplacement(node, lowering->Float64Round(node));
} else {
NodeProperties::ChangeOp(node, Float64Op(node));
ChangeOp(node, Float64Op(node));
}
}
return;
......@@ -2698,7 +2709,7 @@ class RepresentationSelector {
case IrOpcode::kNumberTanh: {
VisitUnop<T>(node, UseInfo::TruncatingFloat64(),
MachineRepresentation::kFloat64);
if (lower<T>()) NodeProperties::ChangeOp(node, Float64Op(node));
if (lower<T>()) ChangeOp(node, Float64Op(node));
return;
}
case IrOpcode::kNumberSign: {
......@@ -2723,14 +2734,14 @@ class RepresentationSelector {
} else {
VisitUnop<T>(node, UseInfo::TruncatingFloat64(),
MachineRepresentation::kFloat64);
if (lower<T>()) NodeProperties::ChangeOp(node, Float64Op(node));
if (lower<T>()) ChangeOp(node, Float64Op(node));
}
return;
}
case IrOpcode::kNumberSqrt: {
VisitUnop<T>(node, UseInfo::TruncatingFloat64(),
MachineRepresentation::kFloat64);
if (lower<T>()) NodeProperties::ChangeOp(node, Float64Op(node));
if (lower<T>()) ChangeOp(node, Float64Op(node));
return;
}
case IrOpcode::kNumberToBoolean: {
......@@ -2803,9 +2814,9 @@ class RepresentationSelector {
VisitBinop<T>(node, UseInfo::AnyTagged(), MachineRepresentation::kBit);
if (lower<T>()) {
if (COMPRESS_POINTERS_BOOL) {
NodeProperties::ChangeOp(node, lowering->machine()->Word32Equal());
ChangeOp(node, lowering->machine()->Word32Equal());
} else {
NodeProperties::ChangeOp(node, lowering->machine()->WordEqual());
ChangeOp(node, lowering->machine()->WordEqual());
}
}
return;
......@@ -2821,8 +2832,7 @@ class RepresentationSelector {
VisitBinop<T>(node, UseInfo::TruncatingFloat64(),
MachineRepresentation::kBit);
if (lower<T>()) {
NodeProperties::ChangeOp(node,
lowering->simplified()->NumberSameValue());
ChangeOp(node, lowering->simplified()->NumberSameValue());
}
} else {
VisitBinop<T>(node, UseInfo::AnyTagged(),
......@@ -2875,7 +2885,7 @@ class RepresentationSelector {
UseInfo::CheckedBigIntAsTaggedPointer(FeedbackSource{}),
MachineRepresentation::kTaggedPointer);
if (lower<T>()) {
NodeProperties::ChangeOp(node, lowering->simplified()->BigIntAdd());
ChangeOp(node, lowering->simplified()->BigIntAdd());
}
}
return;
......@@ -2893,8 +2903,7 @@ class RepresentationSelector {
UseInfo::CheckedBigIntAsTaggedPointer(FeedbackSource{}),
MachineRepresentation::kTaggedPointer);
if (lower<T>()) {
NodeProperties::ChangeOp(node,
lowering->simplified()->BigIntSubtract());
ChangeOp(node, lowering->simplified()->BigIntSubtract());
}
}
return;
......@@ -3129,8 +3138,7 @@ class RepresentationSelector {
if (lower<T>()) {
if (write_barrier_kind < access.write_barrier_kind) {
access.write_barrier_kind = write_barrier_kind;
NodeProperties::ChangeOp(
node, jsgraph_->simplified()->StoreField(access));
ChangeOp(node, jsgraph_->simplified()->StoreField(access));
}
}
return;
......@@ -3172,8 +3180,7 @@ class RepresentationSelector {
if (lower<T>()) {
if (write_barrier_kind < access.write_barrier_kind) {
access.write_barrier_kind = write_barrier_kind;
NodeProperties::ChangeOp(
node, jsgraph_->simplified()->StoreElement(access));
ChangeOp(node, jsgraph_->simplified()->StoreElement(access));
}
}
return;
......@@ -3192,24 +3199,21 @@ class RepresentationSelector {
if (value_type.Is(Type::SignedSmall())) {
ProcessInput<T>(node, 2, UseInfo::TruncatingWord32()); // value
if (lower<T>()) {
NodeProperties::ChangeOp(node,
simplified()->StoreSignedSmallElement());
ChangeOp(node, simplified()->StoreSignedSmallElement());
}
} else if (value_type.Is(Type::Number())) {
ProcessInput<T>(node, 2, UseInfo::TruncatingFloat64()); // value
if (lower<T>()) {
Handle<Map> double_map = DoubleMapParameterOf(node->op());
NodeProperties::ChangeOp(
node,
simplified()->TransitionAndStoreNumberElement(double_map));
ChangeOp(node,
simplified()->TransitionAndStoreNumberElement(double_map));
}
} else if (value_type.Is(Type::NonNumber())) {
ProcessInput<T>(node, 2, UseInfo::AnyTagged()); // value
if (lower<T>()) {
Handle<Map> fast_map = FastMapParameterOf(node->op());
NodeProperties::ChangeOp(
node, simplified()->TransitionAndStoreNonNumberElement(
fast_map, value_type));
ChangeOp(node, simplified()->TransitionAndStoreNonNumberElement(
fast_map, value_type));
}
} else {
ProcessInput<T>(node, 2, UseInfo::AnyTagged()); // value
......@@ -3278,9 +3282,8 @@ class RepresentationSelector {
} else if (input_type.Is(Type::NullOrUndefined())) {
DeferReplacement(node, node->InputAt(1));
} else if (!input_type.Maybe(Type::NullOrUndefined())) {
NodeProperties::ChangeOp(
node, lowering->simplified()->ConvertReceiver(
ConvertReceiverMode::kNotNullOrUndefined));
ChangeOp(node, lowering->simplified()->ConvertReceiver(
ConvertReceiverMode::kNotNullOrUndefined));
}
}
return;
......@@ -3293,7 +3296,7 @@ class RepresentationSelector {
VisitUnop<T>(node, UseInfo::AnyTagged(),
MachineRepresentation::kTagged);
if (lower<T>()) {
NodeProperties::ChangeOp(node, simplified()->StringToNumber());
ChangeOp(node, simplified()->StringToNumber());
}
} else if (truncation.IsUsedAsWord32()) {
if (InputIs(node, Type::NumberOrOddball())) {
......@@ -3304,8 +3307,7 @@ class RepresentationSelector {
VisitUnop<T>(node, UseInfo::AnyTagged(),
MachineRepresentation::kWord32);
if (lower<T>()) {
NodeProperties::ChangeOp(node,
simplified()->PlainPrimitiveToWord32());
ChangeOp(node, simplified()->PlainPrimitiveToWord32());
}
}
} else if (truncation.TruncatesOddballAndBigIntToNumber()) {
......@@ -3317,8 +3319,7 @@ class RepresentationSelector {
VisitUnop<T>(node, UseInfo::AnyTagged(),
MachineRepresentation::kFloat64);
if (lower<T>()) {
NodeProperties::ChangeOp(node,
simplified()->PlainPrimitiveToFloat64());
ChangeOp(node, simplified()->PlainPrimitiveToFloat64());
}
}
} else {
......@@ -3386,8 +3387,7 @@ class RepresentationSelector {
VisitUnop<T>(node, UseInfo::TruncatingFloat64(),
MachineRepresentation::kBit);
if (lower<T>()) {
NodeProperties::ChangeOp(node,
lowering->simplified()->NumberIsFinite());
ChangeOp(node, lowering->simplified()->NumberIsFinite());
}
} else {
VisitUnop<T>(node, UseInfo::AnyTagged(), MachineRepresentation::kBit);
......@@ -3415,8 +3415,7 @@ class RepresentationSelector {
VisitUnop<T>(node, UseInfo::TruncatingFloat64(),
MachineRepresentation::kBit);
if (lower<T>()) {
NodeProperties::ChangeOp(
node, lowering->simplified()->NumberIsSafeInteger());
ChangeOp(node, lowering->simplified()->NumberIsSafeInteger());
}
} else {
VisitUnop<T>(node, UseInfo::AnyTagged(), MachineRepresentation::kBit);
......@@ -3442,8 +3441,7 @@ class RepresentationSelector {
VisitUnop<T>(node, UseInfo::TruncatingFloat64(),
MachineRepresentation::kBit);
if (lower<T>()) {
NodeProperties::ChangeOp(node,
lowering->simplified()->NumberIsInteger());
ChangeOp(node, lowering->simplified()->NumberIsInteger());
}
} else {
VisitUnop<T>(node, UseInfo::AnyTagged(), MachineRepresentation::kBit);
......@@ -3471,7 +3469,7 @@ class RepresentationSelector {
VisitUnop<T>(node, UseInfo::TruncatingFloat64(),
MachineRepresentation::kBit);
if (lower<T>()) {
NodeProperties::ChangeOp(node, simplified()->NumberIsMinusZero());
ChangeOp(node, simplified()->NumberIsMinusZero());
}
} else {
VisitUnop<T>(node, UseInfo::AnyTagged(), MachineRepresentation::kBit);
......@@ -3494,7 +3492,7 @@ class RepresentationSelector {
VisitUnop<T>(node, UseInfo::TruncatingFloat64(),
MachineRepresentation::kBit);
if (lower<T>()) {
NodeProperties::ChangeOp(node, simplified()->NumberIsNaN());
ChangeOp(node, simplified()->NumberIsNaN());
}
} else {
VisitUnop<T>(node, UseInfo::AnyTagged(), MachineRepresentation::kBit);
......@@ -3703,7 +3701,7 @@ class RepresentationSelector {
VisitBinop<T>(node, UseInfo::AnyTagged(), UseInfo::TruncatingWord32(),
MachineType::PointerRepresentation());
if (lower<T>()) {
NodeProperties::ChangeOp(
ChangeOp(
node,
lowering->simplified()->FindOrderedHashMapEntryForInt32Key());
}
......@@ -3797,6 +3795,8 @@ class RepresentationSelector {
replacements_.push_back(replacement);
node->NullAllInputs(); // Node is now dead.
NotifyNodeReplaced(node, replacement);
}
void Kill(Node* node) {
......@@ -3820,6 +3820,20 @@ class RepresentationSelector {
}
private:
void ChangeOp(Node* node, const Operator* new_op) {
compiler::NodeProperties::ChangeOp(node, new_op);
if (V8_UNLIKELY(observe_node_manager_ != nullptr))
observe_node_manager_->OnNodeChanged(kSimplifiedLoweringReducerName, node,
node);
}
void NotifyNodeReplaced(Node* node, Node* replacement) {
if (V8_UNLIKELY(observe_node_manager_ != nullptr))
observe_node_manager_->OnNodeChanged(kSimplifiedLoweringReducerName, node,
replacement);
}
JSGraph* jsgraph_;
Zone* zone_; // Temporary zone.
// Map from node to its uses that might need to be revisited.
......@@ -3850,6 +3864,7 @@ class RepresentationSelector {
OperationTyper op_typer_; // helper for the feedback typer
TickCounter* const tick_counter_;
Linkage* const linkage_;
ObserveNodeManager* const observe_node_manager_;
NodeInfo* GetInfo(Node* node) {
DCHECK(node->id() < count_);
......@@ -4016,13 +4031,11 @@ void RepresentationSelector::InsertUnreachableIfNecessary<LOWER>(Node* node) {
}
}
SimplifiedLowering::SimplifiedLowering(JSGraph* jsgraph, JSHeapBroker* broker,
Zone* zone,
SourcePositionTable* source_positions,
NodeOriginTable* node_origins,
PoisoningMitigationLevel poisoning_level,
TickCounter* tick_counter,
Linkage* linkage)
SimplifiedLowering::SimplifiedLowering(
JSGraph* jsgraph, JSHeapBroker* broker, Zone* zone,
SourcePositionTable* source_positions, NodeOriginTable* node_origins,
PoisoningMitigationLevel poisoning_level, TickCounter* tick_counter,
Linkage* linkage, ObserveNodeManager* observe_node_manager)
: jsgraph_(jsgraph),
broker_(broker),
zone_(zone),
......@@ -4031,13 +4044,14 @@ SimplifiedLowering::SimplifiedLowering(JSGraph* jsgraph, JSHeapBroker* broker,
node_origins_(node_origins),
poisoning_level_(poisoning_level),
tick_counter_(tick_counter),
linkage_(linkage) {}
linkage_(linkage),
observe_node_manager_(observe_node_manager) {}
void SimplifiedLowering::LowerAllNodes() {
RepresentationChanger changer(jsgraph(), broker_);
RepresentationSelector selector(jsgraph(), broker_, zone_, &changer,
source_positions_, node_origins_,
tick_counter_, linkage_);
RepresentationSelector selector(
jsgraph(), broker_, zone_, &changer, source_positions_, node_origins_,
tick_counter_, linkage_, observe_node_manager_);
selector.Run(this);
}
......@@ -4558,7 +4572,7 @@ void SimplifiedLowering::DoMax(Node* node, Operator const* op,
node->ReplaceInput(0, graph()->NewNode(op, lhs, rhs));
DCHECK_EQ(rhs, node->InputAt(1));
node->AppendInput(graph()->zone(), lhs);
NodeProperties::ChangeOp(node, common()->Select(rep));
ChangeOp(node, common()->Select(rep));
}
void SimplifiedLowering::DoMin(Node* node, Operator const* op,
......@@ -4569,7 +4583,7 @@ void SimplifiedLowering::DoMin(Node* node, Operator const* op,
node->InsertInput(graph()->zone(), 0, graph()->NewNode(op, lhs, rhs));
DCHECK_EQ(lhs, node->InputAt(1));
DCHECK_EQ(rhs, node->InputAt(2));
NodeProperties::ChangeOp(node, common()->Select(rep));
ChangeOp(node, common()->Select(rep));
}
void SimplifiedLowering::DoIntegral32ToBit(Node* node) {
......@@ -4579,7 +4593,7 @@ void SimplifiedLowering::DoIntegral32ToBit(Node* node) {
node->ReplaceInput(0, graph()->NewNode(op, input, zero));
node->AppendInput(graph()->zone(), zero);
NodeProperties::ChangeOp(node, op);
ChangeOp(node, op);
}
void SimplifiedLowering::DoOrderedNumberToBit(Node* node) {
......@@ -4588,7 +4602,7 @@ void SimplifiedLowering::DoOrderedNumberToBit(Node* node) {
node->ReplaceInput(0, graph()->NewNode(machine()->Float64Equal(), input,
jsgraph()->Float64Constant(0.0)));
node->AppendInput(graph()->zone(), jsgraph()->Int32Constant(0));
NodeProperties::ChangeOp(node, machine()->Word32Equal());
ChangeOp(node, machine()->Word32Equal());
}
void SimplifiedLowering::DoNumberToBit(Node* node) {
......@@ -4597,7 +4611,7 @@ void SimplifiedLowering::DoNumberToBit(Node* node) {
node->ReplaceInput(0, jsgraph()->Float64Constant(0.0));
node->AppendInput(graph()->zone(),
graph()->NewNode(machine()->Float64Abs(), input));
NodeProperties::ChangeOp(node, machine()->Float64LessThan());
ChangeOp(node, machine()->Float64LessThan());
}
void SimplifiedLowering::DoIntegerToUint8Clamped(Node* node) {
......@@ -4614,8 +4628,7 @@ void SimplifiedLowering::DoIntegerToUint8Clamped(Node* node) {
graph()->NewNode(machine()->Float64LessThan(), input, max), input,
max));
node->AppendInput(graph()->zone(), min);
NodeProperties::ChangeOp(node,
common()->Select(MachineRepresentation::kFloat64));
ChangeOp(node, common()->Select(MachineRepresentation::kFloat64));
}
void SimplifiedLowering::DoNumberToUint8Clamped(Node* node) {
......@@ -4632,8 +4645,7 @@ void SimplifiedLowering::DoNumberToUint8Clamped(Node* node) {
graph()->NewNode(machine()->Float64LessThan(), input, max),
input, max),
min));
NodeProperties::ChangeOp(node,
machine()->Float64RoundTiesEven().placeholder());
ChangeOp(node, machine()->Float64RoundTiesEven().placeholder());
}
void SimplifiedLowering::DoSigned32ToUint8Clamped(Node* node) {
......@@ -4649,8 +4661,7 @@ void SimplifiedLowering::DoSigned32ToUint8Clamped(Node* node) {
graph()->NewNode(machine()->Int32LessThan(), input, min),
min, input));
node->AppendInput(graph()->zone(), max);
NodeProperties::ChangeOp(node,
common()->Select(MachineRepresentation::kWord32));
ChangeOp(node, common()->Select(MachineRepresentation::kWord32));
}
void SimplifiedLowering::DoUnsigned32ToUint8Clamped(Node* node) {
......@@ -4661,8 +4672,7 @@ void SimplifiedLowering::DoUnsigned32ToUint8Clamped(Node* node) {
0, graph()->NewNode(machine()->Uint32LessThanOrEqual(), input, max));
node->AppendInput(graph()->zone(), input);
node->AppendInput(graph()->zone(), max);
NodeProperties::ChangeOp(node,
common()->Select(MachineRepresentation::kWord32));
ChangeOp(node, common()->Select(MachineRepresentation::kWord32));
}
Node* SimplifiedLowering::ToNumberCode() {
......@@ -4731,6 +4741,14 @@ Operator const* SimplifiedLowering::ToNumericOperator() {
return to_numeric_operator_.get();
}
void SimplifiedLowering::ChangeOp(Node* node, const Operator* new_op) {
compiler::NodeProperties::ChangeOp(node, new_op);
if (V8_UNLIKELY(observe_node_manager_ != nullptr))
observe_node_manager_->OnNodeChanged(kSimplifiedLoweringReducerName, node,
node);
}
#undef TRACE
} // namespace compiler
......
......@@ -7,6 +7,7 @@
#include "src/compiler/js-graph.h"
#include "src/compiler/machine-operator.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/node.h"
#include "src/compiler/simplified-operator.h"
......@@ -19,6 +20,7 @@ namespace compiler {
// Forward declarations.
class NodeOriginTable;
class ObserveNodeManager;
class RepresentationChanger;
class RepresentationSelector;
class SourcePositionTable;
......@@ -30,7 +32,8 @@ class V8_EXPORT_PRIVATE SimplifiedLowering final {
SourcePositionTable* source_position,
NodeOriginTable* node_origins,
PoisoningMitigationLevel poisoning_level,
TickCounter* tick_counter, Linkage* linkage);
TickCounter* tick_counter, Linkage* linkage,
ObserveNodeManager* observe_node_manager = nullptr);
~SimplifiedLowering() = default;
void LowerAllNodes();
......@@ -50,6 +53,17 @@ class V8_EXPORT_PRIVATE SimplifiedLowering final {
void DoUnsigned32ToUint8Clamped(Node* node);
private:
// The purpose of this nested class is to hide method
// v8::internal::compiler::NodeProperties::ChangeOp which should not be
// directly used by code in SimplifiedLowering.
// SimplifiedLowering code should call SimplifiedLowering::ChangeOp instead,
// in order to notify the changes to ObserveNodeManager and support the
// %ObserveNode intrinsic.
class NodeProperties : public compiler::NodeProperties {
static void ChangeOp(Node* node, const Operator* new_op) { UNREACHABLE(); }
};
void ChangeOp(Node* node, const Operator* new_op);
JSGraph* const jsgraph_;
JSHeapBroker* broker_;
Zone* const zone_;
......@@ -74,6 +88,8 @@ class V8_EXPORT_PRIVATE SimplifiedLowering final {
TickCounter* const tick_counter_;
Linkage* const linkage_;
ObserveNodeManager* const observe_node_manager_;
Node* Float64Round(Node* const node);
Node* Float64Sign(Node* const node);
Node* Int32Abs(Node* const node);
......
......@@ -257,6 +257,14 @@ RUNTIME_FUNCTION(Runtime_NotifyDeoptimized) {
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_ObserveNode) {
// The %ObserveNode intrinsic only tracks the changes to an observed node in
// code compiled by TurboFan.
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED(Object, obj, 0);
return *obj;
}
static bool IsSuitableForOnStackReplacement(Isolate* isolate,
Handle<JSFunction> function) {
......
......@@ -111,6 +111,7 @@ namespace internal {
F(FunctionFirstExecution, 1, 1) \
F(InstantiateAsmJs, 4, 1) \
F(NotifyDeoptimized, 0, 1) \
F(ObserveNode, 1, 1) \
F(ResolvePossiblyDirectEval, 6, 1) \
F(TryInstallNCICode, 1, 1)
......
......@@ -89,6 +89,8 @@ v8_source_set("cctest_sources") {
"compiler/function-tester.cc",
"compiler/function-tester.h",
"compiler/graph-builder-tester.h",
"compiler/node-observer-tester.cc",
"compiler/node-observer-tester.h",
"compiler/serializer-tester.cc",
"compiler/serializer-tester.h",
"compiler/test-basic-block-profiler.cc",
......@@ -128,6 +130,7 @@ v8_source_set("cctest_sources") {
"compiler/test-run-tail-calls.cc",
"compiler/test-run-unwinding-info.cc",
"compiler/test-run-variables.cc",
"compiler/test-sloppy-equality.cc",
"compiler/value-helper.cc",
"compiler/value-helper.h",
"disasm-regex-helper.cc",
......
......@@ -546,6 +546,7 @@
'test-run-unwinding-info/*': [SKIP],
'test-run-variables/*': [SKIP],
'test-serialize/*': [SKIP],
'test-sloppy-equality/*' : [SKIP],
'test-torque/*': [SKIP],
'test-unwinder-code-pages/PCIsInV8_LargeCodeObject_CodePagesAPI': [SKIP],
......
// Copyright 2021 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 "test/cctest/compiler/node-observer-tester.h"
#include "src/api/api-inl.h"
#include "src/codegen/optimized-compilation-info.h"
#include "src/compiler/pipeline.h"
namespace v8 {
namespace internal {
namespace compiler {
void TestWithObserveNode::OptimizeFunctionWithObserver(
const char* function_name, NodeObserver* observer) {
DCHECK_NOT_NULL(function_name);
DCHECK_NOT_NULL(observer);
Local<Function> api_function = Local<Function>::Cast(
CcTest::global()
->Get(CcTest::isolate()->GetCurrentContext(), v8_str(function_name))
.ToLocalChecked());
Handle<JSFunction> function =
Handle<JSFunction>::cast(v8::Utils::OpenHandle(*api_function));
CHECK(function->shared().HasBytecodeArray());
Handle<SharedFunctionInfo> sfi(function->shared(), isolate_);
IsCompiledScope is_compiled_scope(sfi->is_compiled_scope(isolate_));
JSFunction::EnsureFeedbackVector(function, &is_compiled_scope);
OptimizedCompilationInfo compilation_info(main_zone(), isolate_, sfi,
function, CodeKind::TURBOFAN);
compilation_info.SetNodeObserver(observer);
compilation_info.ReopenHandlesInNewHandleScope(isolate_);
Handle<Code> code =
Pipeline::GenerateCodeForTesting(&compilation_info, isolate_)
.ToHandleChecked();
function->set_code(*code);
}
} // namespace compiler
} // namespace internal
} // namespace v8
// Copyright 2021 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.
#ifndef V8_CCTEST_COMPILER_NODEOBSERVER_TESTER_H_
#define V8_CCTEST_COMPILER_NODEOBSERVER_TESTER_H_
#include "src/compiler/node-observer.h"
#include "src/compiler/simplified-operator.h"
#include "src/objects/type-hints.h"
#include "test/cctest/cctest.h"
#include "test/common/wasm/flag-utils.h"
namespace v8 {
namespace internal {
namespace compiler {
// Test TurboFan compilation using the %ObserveNode intrinsic.
class TestWithObserveNode : public HandleAndZoneScope {
public:
explicit TestWithObserveNode(Isolate* isolate, const char* script)
: isolate_(isolate), script_(script) {
DCHECK_NOT_NULL(isolate_);
DCHECK_NOT_NULL(script_);
CompileRun(script_);
}
void OptimizeFunctionWithObserver(const char* function_name,
NodeObserver* observer);
private:
Isolate* isolate_;
const char* script_;
};
class CreationObserver : public NodeObserver {
public:
explicit CreationObserver(std::function<void(const Node*)> handler)
: handler_(handler) {
DCHECK(handler_);
}
Observation OnNodeCreated(const Node* node) override {
handler_(node);
return Observation::kStop;
}
private:
std::function<void(const Node*)> handler_;
};
class ModificationObserver : public NodeObserver {
public:
explicit ModificationObserver(
std::function<void(const Node*)> on_created_handler,
std::function<void(const Node*, const ObservableNodeState& old_state)>
on_changed_handler)
: on_created_handler_(on_created_handler),
on_changed_handler_(on_changed_handler) {
DCHECK(on_created_handler_);
DCHECK(on_changed_handler_);
}
Observation OnNodeCreated(const Node* node) override {
on_created_handler_(node);
return Observation::kContinue;
}
Observation OnNodeChanged(const char* reducer_name, const Node* node,
const ObservableNodeState& old_state) override {
on_changed_handler_(node, old_state);
return Observation::kContinue;
}
private:
std::function<void(const Node*)> on_created_handler_;
std::function<void(const Node*, const ObservableNodeState& old_state)>
on_changed_handler_;
};
} // namespace compiler
} // namespace internal
} // namespace v8
#endif // V8_CCTEST_COMPILER_NODEOBSERVER_TESTER_H_
// Copyright 2021 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 "test/cctest/compiler/node-observer-tester.h"
namespace v8 {
namespace internal {
namespace compiler {
struct TestCase {
TestCase(const char* l, const char* r, NodeObserver* observer)
: warmup{std::make_pair(l, r)}, observer(observer) {
DCHECK_NOT_NULL(observer);
}
std::vector<std::pair<const char*, const char*>> warmup;
NodeObserver* observer;
};
class TestSloppyEqualityFactory {
public:
explicit TestSloppyEqualityFactory(Zone* zone) : zone_(zone) {}
NodeObserver* SpeculativeNumberEqual(NumberOperationHint hint) {
return zone_->New<CreationObserver>([hint](const Node* node) {
CHECK_EQ(IrOpcode::kSpeculativeNumberEqual, node->opcode());
CHECK_EQ(hint, NumberOperationHintOf(node->op()));
});
}
NodeObserver* JSEqual(CompareOperationHint /*hint*/) {
return zone_->New<CreationObserver>([](const Node* node) {
CHECK_EQ(IrOpcode::kJSEqual, node->opcode());
// TODO(paolosev): compare hint
});
}
NodeObserver* OperatorChange(IrOpcode::Value created_op,
IrOpcode::Value modified_op) {
return zone_->New<ModificationObserver>(
[created_op](const Node* node) {
CHECK_EQ(created_op, node->opcode());
},
[modified_op](const Node* node, const ObservableNodeState& old_state) {
if (old_state.opcode() != node->opcode()) {
CHECK_EQ(modified_op, node->opcode());
}
});
}
private:
Zone* zone_;
};
TEST(TestSloppyEquality) {
FlagScope<bool> allow_natives_syntax(&i::FLAG_allow_natives_syntax, true);
FlagScope<bool> always_opt(&i::FLAG_always_opt, false);
HandleAndZoneScope scope;
Isolate* isolate = scope.main_isolate();
Zone zone(isolate->allocator(), ZONE_NAME);
TestSloppyEqualityFactory f(&zone);
// TODO(nicohartmann@, v8:5660): Collect more precise feedback for some useful
// cases.
TestCase cases[] = {
{"3", "8", f.SpeculativeNumberEqual(NumberOperationHint::kSignedSmall)},
//{"3", "null",
// f.SpeculativeNumberEqual(NumberOperationHint::kNumberOrOddball)},
//{"3", "undefined",
// f.SpeculativeNumberEqual(NumberOperationHint::kNumberOrOddball)},
//{"3", "true",
// f.SpeculativeNumberEqual(NumberOperationHint::kNumberOrOddball)},
{"3", "\"abc\"", f.JSEqual(CompareOperationHint::kAny)},
{"3.14", "3", f.SpeculativeNumberEqual(NumberOperationHint::kNumber)},
//{"3.14", "null",
// f.SpeculativeNumberEqual(NumberOperationHint::kNumberOrOddball)},
//{"3.14", "undefined",
// f.SpeculativeNumberEqual(NumberOperationHint::kNumberOrOddball)},
//{"3.14", "true",
// f.SpeculativeNumberEqual(NumberOperationHint::kNumberOrOddball)},
{"3.14", "\"abc\"", f.JSEqual(CompareOperationHint::kAny)},
{"\"abc\"", "3", f.JSEqual(CompareOperationHint::kAny)},
{"\"abc\"", "null", f.JSEqual(CompareOperationHint::kAny)},
{"\"abc\"", "undefined", f.JSEqual(CompareOperationHint::kAny)},
{"\"abc\"", "true", f.JSEqual(CompareOperationHint::kAny)},
{"\"abc\"", "\"xy\"",
f.JSEqual(CompareOperationHint::kInternalizedString)},
//{"true", "3",
// f.SpeculativeNumberEqual(NumberOperationHint::kNumberOrOddball)},
//{"true", "null",
// f.SpeculativeNumberEqual(NumberOperationHint::kNumberOrOddball)},
//{"true", "undefined",
// f.SpeculativeNumberEqual(NumberOperationHint::kNumberOrOddball)},
//{"true", "true",
// f.SpeculativeNumberEqual(NumberOperationHint::kNumberOrOddball)},
{"true", "\"abc\"", f.JSEqual(CompareOperationHint::kAny)},
//{"undefined", "3",
// f.SpeculativeNumberEqual(NumberOperationHint::kNumberOrOddball)},
{"undefined", "null",
f.JSEqual(CompareOperationHint::kReceiverOrNullOrUndefined)},
{"undefined", "undefined",
f.JSEqual(CompareOperationHint::kReceiverOrNullOrUndefined)},
//{"undefined", "true",
// f.SpeculativeNumberEqual(NumberOperationHint::kNumberOrOddball)},
{"undefined", "\"abc\"", f.JSEqual(CompareOperationHint::kAny)},
{"{}", "3", f.JSEqual(CompareOperationHint::kAny)},
{"{}", "null",
f.JSEqual(CompareOperationHint::kReceiverOrNullOrUndefined)},
{"{}", "undefined",
f.JSEqual(CompareOperationHint::kReceiverOrNullOrUndefined)},
{"{}", "true", f.JSEqual(CompareOperationHint::kAny)},
{"{}", "\"abc\"", f.JSEqual(CompareOperationHint::kAny)},
{"3.14", "3",
f.OperatorChange(IrOpcode::kSpeculativeNumberEqual,
IrOpcode::kFloat64Equal)}};
for (const auto& c : cases) {
std::ostringstream src;
src << "function test(a, b) {\n"
<< " return %ObserveNode(a == b);\n"
<< "}\n"
<< "%PrepareFunctionForOptimization(test);\n";
for (const auto& args : c.warmup) {
src << "test(" << args.first << ", " << args.second << ");\n"
<< "test(" << args.first << ", " << args.second << ");\n";
}
TestWithObserveNode tester(isolate, src.str().c_str());
tester.OptimizeFunctionWithObserver("test", c.observer);
}
}
} // namespace compiler
} // namespace internal
} // namespace v8
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment