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 = [ ...@@ -2301,6 +2301,8 @@ v8_compiler_sources = [
"src/compiler/node-marker.h", "src/compiler/node-marker.h",
"src/compiler/node-matchers.cc", "src/compiler/node-matchers.cc",
"src/compiler/node-matchers.h", "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.cc",
"src/compiler/node-origin-table.h", "src/compiler/node-origin-table.h",
"src/compiler/node-properties.cc", "src/compiler/node-properties.cc",
......
...@@ -34,6 +34,10 @@ class JavaScriptFrame; ...@@ -34,6 +34,10 @@ class JavaScriptFrame;
class JSGlobalObject; class JSGlobalObject;
class Zone; class Zone;
namespace compiler {
class NodeObserver;
}
namespace wasm { namespace wasm {
struct WasmCompilationResult; struct WasmCompilationResult;
} // namespace wasm } // namespace wasm
...@@ -121,6 +125,11 @@ class V8_EXPORT_PRIVATE OptimizedCompilationInfo final { ...@@ -121,6 +125,11 @@ class V8_EXPORT_PRIVATE OptimizedCompilationInfo final {
void set_builtin_index(int32_t index) { builtin_index_ = index; } void set_builtin_index(int32_t index) { builtin_index_ = index; }
BytecodeOffset osr_offset() const { return osr_offset_; } BytecodeOffset osr_offset() const { return osr_offset_; }
JavaScriptFrame* osr_frame() const { return osr_frame_; } 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) { void SetPoisoningMitigationLevel(PoisoningMitigationLevel poisoning_level) {
poisoning_level_ = poisoning_level; poisoning_level_ = poisoning_level;
...@@ -284,6 +293,8 @@ class V8_EXPORT_PRIVATE OptimizedCompilationInfo final { ...@@ -284,6 +293,8 @@ class V8_EXPORT_PRIVATE OptimizedCompilationInfo final {
// OptimizedCompilationInfo allocates. // OptimizedCompilationInfo allocates.
Zone* const zone_; Zone* const zone_;
compiler::NodeObserver* node_observer_ = nullptr;
BailoutReason bailout_reason_ = BailoutReason::kNoReason; BailoutReason bailout_reason_ = BailoutReason::kNoReason;
InlinedFunctionList inlined_functions_; InlinedFunctionList inlined_functions_;
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "src/compiler/js-heap-broker.h" #include "src/compiler/js-heap-broker.h"
#include "src/compiler/linkage.h" #include "src/compiler/linkage.h"
#include "src/compiler/node-matchers.h" #include "src/compiler/node-matchers.h"
#include "src/compiler/node-observer.h"
#include "src/compiler/operator-properties.h" #include "src/compiler/operator-properties.h"
#include "src/compiler/simplified-operator.h" #include "src/compiler/simplified-operator.h"
#include "src/compiler/state-values-utils.h" #include "src/compiler/state-values-utils.h"
...@@ -42,7 +43,8 @@ class BytecodeGraphBuilder { ...@@ -42,7 +43,8 @@ class BytecodeGraphBuilder {
CallFrequency const& invocation_frequency, CallFrequency const& invocation_frequency,
SourcePositionTable* source_positions, int inlining_id, SourcePositionTable* source_positions, int inlining_id,
CodeKind code_kind, BytecodeGraphBuilderFlags flags, CodeKind code_kind, BytecodeGraphBuilderFlags flags,
TickCounter* tick_counter); TickCounter* tick_counter,
ObserveNodeInfo const& observe_node_info);
BytecodeGraphBuilder(const BytecodeGraphBuilder&) = delete; BytecodeGraphBuilder(const BytecodeGraphBuilder&) = delete;
BytecodeGraphBuilder& operator=(const BytecodeGraphBuilder&) = delete; BytecodeGraphBuilder& operator=(const BytecodeGraphBuilder&) = delete;
...@@ -530,6 +532,8 @@ class BytecodeGraphBuilder { ...@@ -530,6 +532,8 @@ class BytecodeGraphBuilder {
TickCounter* const tick_counter_; TickCounter* const tick_counter_;
ObserveNodeInfo const observe_node_info_;
static constexpr int kBinaryOperationHintIndex = 1; static constexpr int kBinaryOperationHintIndex = 1;
static constexpr int kBinaryOperationSmiHintIndex = 1; static constexpr int kBinaryOperationSmiHintIndex = 1;
static constexpr int kCompareOperationHintIndex = 1; static constexpr int kCompareOperationHintIndex = 1;
...@@ -1041,7 +1045,8 @@ BytecodeGraphBuilder::BytecodeGraphBuilder( ...@@ -1041,7 +1045,8 @@ BytecodeGraphBuilder::BytecodeGraphBuilder(
FeedbackCellRef const& feedback_cell, BytecodeOffset osr_offset, FeedbackCellRef const& feedback_cell, BytecodeOffset osr_offset,
JSGraph* jsgraph, CallFrequency const& invocation_frequency, JSGraph* jsgraph, CallFrequency const& invocation_frequency,
SourcePositionTable* source_positions, int inlining_id, CodeKind code_kind, 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), : broker_(broker),
local_zone_(local_zone), local_zone_(local_zone),
jsgraph_(jsgraph), jsgraph_(jsgraph),
...@@ -1087,7 +1092,8 @@ BytecodeGraphBuilder::BytecodeGraphBuilder( ...@@ -1087,7 +1092,8 @@ BytecodeGraphBuilder::BytecodeGraphBuilder(
state_values_cache_(jsgraph), state_values_cache_(jsgraph),
source_positions_(source_positions), source_positions_(source_positions),
start_position_(shared_info.StartPosition(), inlining_id), start_position_(shared_info.StartPosition(), inlining_id),
tick_counter_(tick_counter) {} tick_counter_(tick_counter),
observe_node_info_(observe_node_info) {}
Node* BytecodeGraphBuilder::GetFunctionClosure() { Node* BytecodeGraphBuilder::GetFunctionClosure() {
if (!function_closure_.is_set()) { if (!function_closure_.is_set()) {
...@@ -2742,16 +2748,25 @@ void BytecodeGraphBuilder::VisitCallRuntime() { ...@@ -2742,16 +2748,25 @@ void BytecodeGraphBuilder::VisitCallRuntime() {
interpreter::Register receiver = bytecode_iterator().GetRegisterOperand(1); interpreter::Register receiver = bytecode_iterator().GetRegisterOperand(1);
size_t reg_count = bytecode_iterator().GetRegisterCountOperand(2); size_t reg_count = bytecode_iterator().GetRegisterCountOperand(2);
// Create node to perform the runtime call. // Handle %ObserveNode here (rather than in JSIntrinsicLowering) to observe
const Operator* call = javascript()->CallRuntime(function_id, reg_count); // the node as early as possible.
Node* value = ProcessCallRuntimeArguments(call, receiver, reg_count); if (function_id == Runtime::FunctionId::kObserveNode) {
environment()->BindAccumulator(value, Environment::kAttachFrameState); DCHECK_EQ(1, reg_count);
Node* value = environment()->LookupRegister(receiver);
// Connect to the end if {function_id} is non-returning. observe_node_info_.StartObserving(value);
if (Runtime::IsNonReturning(function_id)) { environment()->BindAccumulator(value);
// TODO(7099): Investigate if we need LoopExit node here. } else {
Node* control = NewNode(common()->Throw()); // Create node to perform the runtime call.
MergeControlToLeaveFunction(control); 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, ...@@ -4559,14 +4574,16 @@ void BuildGraphFromBytecode(JSHeapBroker* broker, Zone* local_zone,
SourcePositionTable* source_positions, SourcePositionTable* source_positions,
int inlining_id, CodeKind code_kind, int inlining_id, CodeKind code_kind,
BytecodeGraphBuilderFlags flags, BytecodeGraphBuilderFlags flags,
TickCounter* tick_counter) { TickCounter* tick_counter,
ObserveNodeInfo const& observe_node_info) {
DCHECK(broker->IsSerializedForCompilation( DCHECK(broker->IsSerializedForCompilation(
shared_info, feedback_cell.value()->AsFeedbackVector())); shared_info, feedback_cell.value()->AsFeedbackVector()));
DCHECK(feedback_cell.value()->AsFeedbackVector().serialized()); DCHECK(feedback_cell.value()->AsFeedbackVector().serialized());
BytecodeGraphBuilder builder( BytecodeGraphBuilder builder(
broker, local_zone, broker->target_native_context(), shared_info, broker, local_zone, broker->target_native_context(), shared_info,
feedback_cell, osr_offset, jsgraph, invocation_frequency, 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(); builder.CreateGraph();
} }
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "src/compiler/js-operator.h" #include "src/compiler/js-operator.h"
#include "src/compiler/js-type-hint-lowering.h" #include "src/compiler/js-type-hint-lowering.h"
#include "src/compiler/node-observer.h"
#include "src/handles/handles.h" #include "src/handles/handles.h"
#include "src/objects/code-kind.h" #include "src/objects/code-kind.h"
#include "src/utils/utils.h" #include "src/utils/utils.h"
...@@ -25,6 +26,7 @@ class Zone; ...@@ -25,6 +26,7 @@ class Zone;
namespace compiler { namespace compiler {
class JSGraph; class JSGraph;
class NodeObserver;
class SourcePositionTable; class SourcePositionTable;
enum class BytecodeGraphBuilderFlag : uint8_t { enum class BytecodeGraphBuilderFlag : uint8_t {
...@@ -47,7 +49,8 @@ void BuildGraphFromBytecode(JSHeapBroker* broker, Zone* local_zone, ...@@ -47,7 +49,8 @@ void BuildGraphFromBytecode(JSHeapBroker* broker, Zone* local_zone,
SourcePositionTable* source_positions, SourcePositionTable* source_positions,
int inlining_id, CodeKind code_kind, int inlining_id, CodeKind code_kind,
BytecodeGraphBuilderFlags flags, BytecodeGraphBuilderFlags flags,
TickCounter* tick_counter); TickCounter* tick_counter,
ObserveNodeInfo const& observe_node_info = {});
} // namespace compiler } // namespace compiler
} // namespace internal } // namespace internal
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "src/codegen/tick-counter.h" #include "src/codegen/tick-counter.h"
#include "src/compiler/graph.h" #include "src/compiler/graph.h"
#include "src/compiler/js-heap-broker.h" #include "src/compiler/js-heap-broker.h"
#include "src/compiler/node-observer.h"
#include "src/compiler/node-properties.h" #include "src/compiler/node-properties.h"
#include "src/compiler/node.h" #include "src/compiler/node.h"
#include "src/compiler/verifier.h" #include "src/compiler/verifier.h"
...@@ -28,8 +29,19 @@ enum class GraphReducer::State : uint8_t { ...@@ -28,8 +29,19 @@ enum class GraphReducer::State : uint8_t {
void Reducer::Finalize() {} 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, GraphReducer::GraphReducer(Zone* zone, Graph* graph, TickCounter* tick_counter,
JSHeapBroker* broker, Node* dead) JSHeapBroker* broker, Node* dead,
ObserveNodeManager* observe_node_manager)
: graph_(graph), : graph_(graph),
dead_(dead), dead_(dead),
state_(graph, 4), state_(graph, 4),
...@@ -37,7 +49,8 @@ GraphReducer::GraphReducer(Zone* zone, Graph* graph, TickCounter* tick_counter, ...@@ -37,7 +49,8 @@ GraphReducer::GraphReducer(Zone* zone, Graph* graph, TickCounter* tick_counter,
revisit_(zone), revisit_(zone),
stack_(zone), stack_(zone),
tick_counter_(tick_counter), tick_counter_(tick_counter),
broker_(broker) { broker_(broker),
observe_node_manager_(observe_node_manager) {
if (dead != nullptr) { if (dead != nullptr) {
NodeProperties::SetType(dead_, Type::None()); NodeProperties::SetType(dead_, Type::None());
} }
...@@ -89,7 +102,7 @@ Reduction GraphReducer::Reduce(Node* const node) { ...@@ -89,7 +102,7 @@ Reduction GraphReducer::Reduce(Node* const node) {
for (auto i = reducers_.begin(); i != reducers_.end();) { for (auto i = reducers_.begin(); i != reducers_.end();) {
if (i != skip) { if (i != skip) {
tick_counter_->TickAndMaybeEnterSafepoint(); tick_counter_->TickAndMaybeEnterSafepoint();
Reduction reduction = (*i)->Reduce(node); Reduction reduction = (*i)->Reduce(node, observe_node_manager_);
if (!reduction.Changed()) { if (!reduction.Changed()) {
// No change from this reducer. // No change from this reducer.
} else if (reduction.replacement() == node) { } else if (reduction.replacement() == node) {
......
...@@ -20,6 +20,7 @@ namespace compiler { ...@@ -20,6 +20,7 @@ namespace compiler {
class Graph; class Graph;
class JSHeapBroker; class JSHeapBroker;
class Node; class Node;
class ObserveNodeManager;
// NodeIds are identifying numbers for nodes that can be used to index auxiliary // NodeIds are identifying numbers for nodes that can be used to index auxiliary
// out-of-line data associated with each node. // out-of-line data associated with each node.
...@@ -58,7 +59,7 @@ class V8_EXPORT_PRIVATE Reducer { ...@@ -58,7 +59,7 @@ class V8_EXPORT_PRIVATE Reducer {
virtual const char* reducer_name() const = 0; virtual const char* reducer_name() const = 0;
// Try to reduce a node if possible. // 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 // 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 // do additional reductions at the end, which in turn can cause a new round
...@@ -69,6 +70,9 @@ class V8_EXPORT_PRIVATE Reducer { ...@@ -69,6 +70,9 @@ class V8_EXPORT_PRIVATE Reducer {
static Reduction NoChange() { return Reduction(); } static Reduction NoChange() { return Reduction(); }
static Reduction Replace(Node* node) { return Reduction(node); } static Reduction Replace(Node* node) { return Reduction(node); }
static Reduction Changed(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 ...@@ -136,7 +140,8 @@ class V8_EXPORT_PRIVATE GraphReducer
: public NON_EXPORTED_BASE(AdvancedReducer::Editor) { : public NON_EXPORTED_BASE(AdvancedReducer::Editor) {
public: public:
GraphReducer(Zone* zone, Graph* graph, TickCounter* tick_counter, 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() override;
GraphReducer(const GraphReducer&) = delete; GraphReducer(const GraphReducer&) = delete;
...@@ -193,6 +198,7 @@ class V8_EXPORT_PRIVATE GraphReducer ...@@ -193,6 +198,7 @@ class V8_EXPORT_PRIVATE GraphReducer
ZoneStack<NodeState> stack_; ZoneStack<NodeState> stack_;
TickCounter* const tick_counter_; TickCounter* const tick_counter_;
JSHeapBroker* const broker_; JSHeapBroker* const broker_;
ObserveNodeManager* const observe_node_manager_;
}; };
} // namespace compiler } // 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) { ...@@ -568,11 +568,10 @@ Node* NodeProperties::GetOuterContext(Node* node, size_t* depth) {
} }
// static // static
Type NodeProperties::GetTypeOrAny(Node* node) { Type NodeProperties::GetTypeOrAny(const Node* node) {
return IsTyped(node) ? node->type() : Type::Any(); return IsTyped(node) ? node->type() : Type::Any();
} }
// static // static
bool NodeProperties::AllValueInputsAreTyped(Node* node) { bool NodeProperties::AllValueInputsAreTyped(Node* node) {
int input_count = node->op()->ValueInputCount(); int input_count = node->op()->ValueInputCount();
......
...@@ -21,7 +21,7 @@ class Operator; ...@@ -21,7 +21,7 @@ class Operator;
class CommonOperatorBuilder; class CommonOperatorBuilder;
// A facade that simplifies access to the different kinds of inputs to a node. // 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: public:
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Input layout. // Input layout.
...@@ -244,12 +244,12 @@ class V8_EXPORT_PRIVATE NodeProperties final { ...@@ -244,12 +244,12 @@ class V8_EXPORT_PRIVATE NodeProperties final {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Type. // 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) { static Type GetType(Node* node) {
DCHECK(IsTyped(node)); DCHECK(IsTyped(node));
return node->type(); return node->type();
} }
static Type GetTypeOrAny(Node* node); static Type GetTypeOrAny(const Node* node);
static void SetType(Node* node, Type type) { static void SetType(Node* node, Type type) {
DCHECK(!type.IsInvalid()); DCHECK(!type.IsInvalid());
node->set_type(type); node->set_type(type);
......
This diff is collapsed.
...@@ -38,7 +38,7 @@ void ScheduledMachineLowering::Run() { ...@@ -38,7 +38,7 @@ void ScheduledMachineLowering::Run() {
Node* node = *instr; Node* node = *instr;
Reduction reduction; Reduction reduction;
for (auto reducer : reducers_) { for (auto reducer : reducers_) {
reduction = reducer->Reduce(node); reduction = reducer->Reduce(node, nullptr);
if (reduction.Changed()) break; if (reduction.Changed()) break;
} }
if (reduction.Changed()) { if (reduction.Changed()) {
......
This diff is collapsed.
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "src/compiler/js-graph.h" #include "src/compiler/js-graph.h"
#include "src/compiler/machine-operator.h" #include "src/compiler/machine-operator.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/node.h" #include "src/compiler/node.h"
#include "src/compiler/simplified-operator.h" #include "src/compiler/simplified-operator.h"
...@@ -19,6 +20,7 @@ namespace compiler { ...@@ -19,6 +20,7 @@ namespace compiler {
// Forward declarations. // Forward declarations.
class NodeOriginTable; class NodeOriginTable;
class ObserveNodeManager;
class RepresentationChanger; class RepresentationChanger;
class RepresentationSelector; class RepresentationSelector;
class SourcePositionTable; class SourcePositionTable;
...@@ -30,7 +32,8 @@ class V8_EXPORT_PRIVATE SimplifiedLowering final { ...@@ -30,7 +32,8 @@ class V8_EXPORT_PRIVATE SimplifiedLowering final {
SourcePositionTable* source_position, SourcePositionTable* source_position,
NodeOriginTable* node_origins, NodeOriginTable* node_origins,
PoisoningMitigationLevel poisoning_level, PoisoningMitigationLevel poisoning_level,
TickCounter* tick_counter, Linkage* linkage); TickCounter* tick_counter, Linkage* linkage,
ObserveNodeManager* observe_node_manager = nullptr);
~SimplifiedLowering() = default; ~SimplifiedLowering() = default;
void LowerAllNodes(); void LowerAllNodes();
...@@ -50,6 +53,17 @@ class V8_EXPORT_PRIVATE SimplifiedLowering final { ...@@ -50,6 +53,17 @@ class V8_EXPORT_PRIVATE SimplifiedLowering final {
void DoUnsigned32ToUint8Clamped(Node* node); void DoUnsigned32ToUint8Clamped(Node* node);
private: 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_; JSGraph* const jsgraph_;
JSHeapBroker* broker_; JSHeapBroker* broker_;
Zone* const zone_; Zone* const zone_;
...@@ -74,6 +88,8 @@ class V8_EXPORT_PRIVATE SimplifiedLowering final { ...@@ -74,6 +88,8 @@ class V8_EXPORT_PRIVATE SimplifiedLowering final {
TickCounter* const tick_counter_; TickCounter* const tick_counter_;
Linkage* const linkage_; Linkage* const linkage_;
ObserveNodeManager* const observe_node_manager_;
Node* Float64Round(Node* const node); Node* Float64Round(Node* const node);
Node* Float64Sign(Node* const node); Node* Float64Sign(Node* const node);
Node* Int32Abs(Node* const node); Node* Int32Abs(Node* const node);
......
...@@ -257,6 +257,14 @@ RUNTIME_FUNCTION(Runtime_NotifyDeoptimized) { ...@@ -257,6 +257,14 @@ RUNTIME_FUNCTION(Runtime_NotifyDeoptimized) {
return ReadOnlyRoots(isolate).undefined_value(); 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, static bool IsSuitableForOnStackReplacement(Isolate* isolate,
Handle<JSFunction> function) { Handle<JSFunction> function) {
......
...@@ -111,6 +111,7 @@ namespace internal { ...@@ -111,6 +111,7 @@ namespace internal {
F(FunctionFirstExecution, 1, 1) \ F(FunctionFirstExecution, 1, 1) \
F(InstantiateAsmJs, 4, 1) \ F(InstantiateAsmJs, 4, 1) \
F(NotifyDeoptimized, 0, 1) \ F(NotifyDeoptimized, 0, 1) \
F(ObserveNode, 1, 1) \
F(ResolvePossiblyDirectEval, 6, 1) \ F(ResolvePossiblyDirectEval, 6, 1) \
F(TryInstallNCICode, 1, 1) F(TryInstallNCICode, 1, 1)
......
...@@ -89,6 +89,8 @@ v8_source_set("cctest_sources") { ...@@ -89,6 +89,8 @@ v8_source_set("cctest_sources") {
"compiler/function-tester.cc", "compiler/function-tester.cc",
"compiler/function-tester.h", "compiler/function-tester.h",
"compiler/graph-builder-tester.h", "compiler/graph-builder-tester.h",
"compiler/node-observer-tester.cc",
"compiler/node-observer-tester.h",
"compiler/serializer-tester.cc", "compiler/serializer-tester.cc",
"compiler/serializer-tester.h", "compiler/serializer-tester.h",
"compiler/test-basic-block-profiler.cc", "compiler/test-basic-block-profiler.cc",
...@@ -128,6 +130,7 @@ v8_source_set("cctest_sources") { ...@@ -128,6 +130,7 @@ v8_source_set("cctest_sources") {
"compiler/test-run-tail-calls.cc", "compiler/test-run-tail-calls.cc",
"compiler/test-run-unwinding-info.cc", "compiler/test-run-unwinding-info.cc",
"compiler/test-run-variables.cc", "compiler/test-run-variables.cc",
"compiler/test-sloppy-equality.cc",
"compiler/value-helper.cc", "compiler/value-helper.cc",
"compiler/value-helper.h", "compiler/value-helper.h",
"disasm-regex-helper.cc", "disasm-regex-helper.cc",
......
...@@ -546,6 +546,7 @@ ...@@ -546,6 +546,7 @@
'test-run-unwinding-info/*': [SKIP], 'test-run-unwinding-info/*': [SKIP],
'test-run-variables/*': [SKIP], 'test-run-variables/*': [SKIP],
'test-serialize/*': [SKIP], 'test-serialize/*': [SKIP],
'test-sloppy-equality/*' : [SKIP],
'test-torque/*': [SKIP], 'test-torque/*': [SKIP],
'test-unwinder-code-pages/PCIsInV8_LargeCodeObject_CodePagesAPI': [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