[nci] Add feedback vector as input to unary ops

In native context independent code we cannot embed the (native context
dependent) feedback vector as a constant. Instead, we will load it
from the JSFunction once and pass it to all users. This CL makes this
change for all unary operators. All other {binary,compare} operators
will need similar work in the future.

Bug: v8:8888
Change-Id: I4d49a6e0effc84dcdf3599814e5c2708b16bcc44
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2246576
Reviewed-by: Georg Neis <neis@chromium.org>
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68448}
This commit is contained in:
Jakob Gruber 2020-06-18 12:47:11 +02:00 committed by Commit Bot
parent 35a802ea9e
commit fc3c197562
9 changed files with 108 additions and 15 deletions

View File

@ -1168,6 +1168,15 @@ FieldAccess AccessBuilder::ForDictionaryObjectHashIndex() {
return access; return access;
} }
// static
FieldAccess AccessBuilder::ForFeedbackCellValue() {
FieldAccess access = {kTaggedBase, FeedbackCell::kValueOffset,
Handle<Name>(), MaybeHandle<Map>(),
Type::Any(), MachineType::TaggedPointer(),
kFullWriteBarrier};
return access;
}
} // namespace compiler } // namespace compiler
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8

View File

@ -327,6 +327,9 @@ class V8_EXPORT_PRIVATE AccessBuilder final
static FieldAccess ForDictionaryNextEnumerationIndex(); static FieldAccess ForDictionaryNextEnumerationIndex();
static FieldAccess ForDictionaryObjectHashIndex(); static FieldAccess ForDictionaryObjectHashIndex();
// Provides access to a FeedbackCell's value.
static FieldAccess ForFeedbackCellValue();
private: private:
DISALLOW_IMPLICIT_CONSTRUCTORS(AccessBuilder); DISALLOW_IMPLICIT_CONSTRUCTORS(AccessBuilder);
}; };

View File

@ -63,6 +63,23 @@ class BytecodeGraphBuilder {
// Get or create the node that represents the outer function closure. // Get or create the node that represents the outer function closure.
Node* GetFunctionClosure(); Node* GetFunctionClosure();
bool native_context_independent() const {
return native_context_independent_;
}
// The node representing the current feedback vector is generated once prior
// to visiting bytecodes, and is later passed as input to other nodes that
// may need it.
// TODO(jgruber): Remove feedback_vector() and rename feedback_vector_node()
// to feedback_vector() once all uses of the direct heap object reference
// have been replaced with a Node* reference.
void CreateFeedbackVectorNode();
Node* BuildLoadFeedbackVector();
Node* feedback_vector_node() const {
DCHECK_NOT_NULL(feedback_vector_node_);
return feedback_vector_node_;
}
// Builder for loading the a native context field. // Builder for loading the a native context field.
Node* BuildLoadNativeContextField(int index); Node* BuildLoadNativeContextField(int index);
@ -416,6 +433,9 @@ class BytecodeGraphBuilder {
int input_buffer_size_; int input_buffer_size_;
Node** input_buffer_; Node** input_buffer_;
const bool native_context_independent_;
Node* feedback_vector_node_;
// Optimization to only create checkpoints when the current position in the // Optimization to only create checkpoints when the current position in the
// control-flow is not effect-dominated by another checkpoint already. All // control-flow is not effect-dominated by another checkpoint already. All
// operations that do not have observable side-effects can be re-evaluated. // operations that do not have observable side-effects can be re-evaluated.
@ -978,6 +998,9 @@ BytecodeGraphBuilder::BytecodeGraphBuilder(
current_exception_handler_(0), current_exception_handler_(0),
input_buffer_size_(0), input_buffer_size_(0),
input_buffer_(nullptr), input_buffer_(nullptr),
native_context_independent_(
flags & BytecodeGraphBuilderFlag::kNativeContextIndependent),
feedback_vector_node_(nullptr),
needs_eager_checkpoint_(true), needs_eager_checkpoint_(true),
exit_controls_(local_zone), exit_controls_(local_zone),
state_values_cache_(jsgraph), state_values_cache_(jsgraph),
@ -1008,6 +1031,36 @@ Node* BytecodeGraphBuilder::GetFunctionClosure() {
return function_closure_.get(); return function_closure_.get();
} }
void BytecodeGraphBuilder::CreateFeedbackVectorNode() {
DCHECK_NULL(feedback_vector_node_);
feedback_vector_node_ = native_context_independent()
? BuildLoadFeedbackVector()
: jsgraph()->Constant(feedback_vector());
}
Node* BytecodeGraphBuilder::BuildLoadFeedbackVector() {
DCHECK(native_context_independent());
DCHECK_NULL(feedback_vector_node_);
// The feedback vector must exist and remain live while the generated code
// lives. Specifically that means it must be created when NCI code is
// installed, and must not be flushed.
Environment* env = environment();
Node* control = env->GetControlDependency();
Node* effect = env->GetEffectDependency();
Node* feedback_cell = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSFunctionFeedbackCell()),
GetFunctionClosure(), effect, control);
Node* vector = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForFeedbackCellValue()),
feedback_cell, effect, control);
env->UpdateEffectDependency(effect);
return vector;
}
Node* BytecodeGraphBuilder::BuildLoadNativeContextField(int index) { Node* BytecodeGraphBuilder::BuildLoadNativeContextField(int index) {
Node* result = NewNode(javascript()->LoadContext(0, index, true)); Node* result = NewNode(javascript()->LoadContext(0, index, true));
NodeProperties::ReplaceContextInput(result, NodeProperties::ReplaceContextInput(result,
@ -1039,6 +1092,7 @@ void BytecodeGraphBuilder::CreateGraph() {
graph()->start()); graph()->start());
set_environment(&env); set_environment(&env);
CreateFeedbackVectorNode();
VisitBytecodes(); VisitBytecodes();
// Finish the basic structure of the graph. // Finish the basic structure of the graph.
@ -2719,7 +2773,7 @@ void BytecodeGraphBuilder::BuildUnaryOp(const Operator* op) {
node = lowering.value(); node = lowering.value();
} else { } else {
DCHECK(!lowering.Changed()); DCHECK(!lowering.Changed());
node = NewNode(op, operand); node = NewNode(op, operand, feedback_vector_node());
} }
environment()->BindAccumulator(node, Environment::kAttachFrameState); environment()->BindAccumulator(node, Environment::kAttachFrameState);

View File

@ -133,16 +133,6 @@ class GraphAssembler;
// Wrapper classes for special node/edge types (effect, control, frame states) // Wrapper classes for special node/edge types (effect, control, frame states)
// that otherwise don't fit into the type system. // that otherwise don't fit into the type system.
class NodeWrapper {
public:
explicit constexpr NodeWrapper(Node* node) : node_(node) {}
operator Node*() const { return node_; }
Node* operator->() const { return node_; }
private:
Node* node_;
};
class Effect : public NodeWrapper { class Effect : public NodeWrapper {
public: public:
explicit constexpr Effect(Node* node) : NodeWrapper(node) { explicit constexpr Effect(Node* node) : NodeWrapper(node) {

View File

@ -130,7 +130,6 @@ void JSGenericLowering::ReplaceUnaryOpWithBuiltinCall(
const FeedbackParameter& p = FeedbackParameterOf(node->op()); const FeedbackParameter& p = FeedbackParameterOf(node->op());
if (CollectFeedbackInGenericLowering() && p.feedback().IsValid()) { if (CollectFeedbackInGenericLowering() && p.feedback().IsValid()) {
Callable callable = Builtins::CallableFor(isolate(), builtin_with_feedback); Callable callable = Builtins::CallableFor(isolate(), builtin_with_feedback);
Node* feedback_vector = jsgraph()->HeapConstant(p.feedback().vector);
Node* slot = jsgraph()->UintPtrConstant(p.feedback().slot.ToInt()); Node* slot = jsgraph()->UintPtrConstant(p.feedback().slot.ToInt());
const CallInterfaceDescriptor& descriptor = callable.descriptor(); const CallInterfaceDescriptor& descriptor = callable.descriptor();
CallDescriptor::Flags flags = FrameStateFlagForCall(node); CallDescriptor::Flags flags = FrameStateFlagForCall(node);
@ -138,11 +137,14 @@ void JSGenericLowering::ReplaceUnaryOpWithBuiltinCall(
zone(), descriptor, descriptor.GetStackParameterCount(), flags, zone(), descriptor, descriptor.GetStackParameterCount(), flags,
node->op()->properties()); node->op()->properties());
Node* stub_code = jsgraph()->HeapConstant(callable.code()); Node* stub_code = jsgraph()->HeapConstant(callable.code());
STATIC_ASSERT(JSUnaryOpNode::ValueIndex() == 0);
STATIC_ASSERT(JSUnaryOpNode::FeedbackVectorIndex() == 1);
DCHECK_EQ(node->op()->ValueInputCount(), 2);
node->InsertInput(zone(), 0, stub_code); node->InsertInput(zone(), 0, stub_code);
node->InsertInput(zone(), 2, slot); node->InsertInput(zone(), 2, slot);
node->InsertInput(zone(), 3, feedback_vector);
NodeProperties::ChangeOp(node, common()->Call(call_descriptor)); NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
} else { } else {
node->RemoveInput(JSUnaryOpNode::FeedbackVectorIndex());
ReplaceWithBuiltinCall(node, builtin_without_feedback); ReplaceWithBuiltinCall(node, builtin_without_feedback);
} }
} }

View File

@ -7,7 +7,6 @@
#include <limits> #include <limits>
#include "src/base/lazy-instance.h" #include "src/base/lazy-instance.h"
#include "src/compiler/opcodes.h"
#include "src/compiler/operator.h" #include "src/compiler/operator.h"
#include "src/handles/handles-inl.h" #include "src/handles/handles-inl.h"
#include "src/objects/objects-inl.h" #include "src/objects/objects-inl.h"
@ -737,7 +736,7 @@ CACHED_OP_LIST(CACHED_OP)
const Operator* JSOperatorBuilder::Name(FeedbackSource const& feedback) { \ const Operator* JSOperatorBuilder::Name(FeedbackSource const& feedback) { \
FeedbackParameter parameters(feedback); \ FeedbackParameter parameters(feedback); \
return new (zone()) Operator1<FeedbackParameter>( \ return new (zone()) Operator1<FeedbackParameter>( \
IrOpcode::kJS##Name, Operator::kNoProperties, "JS" #Name, 1, 1, 1, 1, \ IrOpcode::kJS##Name, Operator::kNoProperties, "JS" #Name, 2, 1, 1, 1, \
1, 2, parameters); \ 1, 2, parameters); \
} }
UNARY_OP_LIST(UNARY_OP) UNARY_OP_LIST(UNARY_OP)

View File

@ -8,6 +8,8 @@
#include "src/base/compiler-specific.h" #include "src/base/compiler-specific.h"
#include "src/compiler/feedback-source.h" #include "src/compiler/feedback-source.h"
#include "src/compiler/globals.h" #include "src/compiler/globals.h"
#include "src/compiler/node.h"
#include "src/compiler/opcodes.h"
#include "src/handles/maybe-handles.h" #include "src/handles/maybe-handles.h"
#include "src/objects/type-hints.h" #include "src/objects/type-hints.h"
#include "src/runtime/runtime.h" #include "src/runtime/runtime.h"
@ -27,6 +29,26 @@ namespace compiler {
class Operator; class Operator;
struct JSOperatorGlobalCache; struct JSOperatorGlobalCache;
// Node wrappers.
class JSUnaryOpNode final : public NodeWrapper {
public:
explicit constexpr JSUnaryOpNode(Node* node) : NodeWrapper(node) {
CONSTEXPR_DCHECK(node->opcode() == IrOpcode::kJSBitwiseNot ||
node->opcode() == IrOpcode::kJSDecrement ||
node->opcode() == IrOpcode::kJSIncrement ||
node->opcode() == IrOpcode::kJSNegate);
}
static constexpr int ValueIndex() { return 0; }
static constexpr int FeedbackVectorIndex() { return 1; }
};
using JSBitwiseNotNode = JSUnaryOpNode;
using JSDecrementNode = JSUnaryOpNode;
using JSIncrementNode = JSUnaryOpNode;
using JSNegateNode = JSUnaryOpNode;
// Defines the frequency a given Call/Construct site was executed. For some // Defines the frequency a given Call/Construct site was executed. For some
// call sites the frequency is not known. // call sites the frequency is not known.
class CallFrequency final { class CallFrequency final {

View File

@ -455,6 +455,7 @@ Reduction JSTypedLowering::ReduceJSBitwiseNot(Node* node) {
if (input_type.Is(Type::PlainPrimitive())) { if (input_type.Is(Type::PlainPrimitive())) {
// JSBitwiseNot(x) => NumberBitwiseXor(ToInt32(x), -1) // JSBitwiseNot(x) => NumberBitwiseXor(ToInt32(x), -1)
const FeedbackParameter& p = FeedbackParameterOf(node->op()); const FeedbackParameter& p = FeedbackParameterOf(node->op());
node->RemoveInput(JSBitwiseNotNode::FeedbackVectorIndex());
node->InsertInput(graph()->zone(), 1, jsgraph()->SmiConstant(-1)); node->InsertInput(graph()->zone(), 1, jsgraph()->SmiConstant(-1));
NodeProperties::ChangeOp(node, javascript()->BitwiseXor(p.feedback())); NodeProperties::ChangeOp(node, javascript()->BitwiseXor(p.feedback()));
JSBinopReduction r(this, node); JSBinopReduction r(this, node);
@ -471,6 +472,7 @@ Reduction JSTypedLowering::ReduceJSDecrement(Node* node) {
if (input_type.Is(Type::PlainPrimitive())) { if (input_type.Is(Type::PlainPrimitive())) {
// JSDecrement(x) => NumberSubtract(ToNumber(x), 1) // JSDecrement(x) => NumberSubtract(ToNumber(x), 1)
const FeedbackParameter& p = FeedbackParameterOf(node->op()); const FeedbackParameter& p = FeedbackParameterOf(node->op());
node->RemoveInput(JSDecrementNode::FeedbackVectorIndex());
node->InsertInput(graph()->zone(), 1, jsgraph()->OneConstant()); node->InsertInput(graph()->zone(), 1, jsgraph()->OneConstant());
NodeProperties::ChangeOp(node, javascript()->Subtract(p.feedback())); NodeProperties::ChangeOp(node, javascript()->Subtract(p.feedback()));
JSBinopReduction r(this, node); JSBinopReduction r(this, node);
@ -487,6 +489,7 @@ Reduction JSTypedLowering::ReduceJSIncrement(Node* node) {
if (input_type.Is(Type::PlainPrimitive())) { if (input_type.Is(Type::PlainPrimitive())) {
// JSIncrement(x) => NumberAdd(ToNumber(x), 1) // JSIncrement(x) => NumberAdd(ToNumber(x), 1)
const FeedbackParameter& p = FeedbackParameterOf(node->op()); const FeedbackParameter& p = FeedbackParameterOf(node->op());
node->RemoveInput(JSIncrementNode::FeedbackVectorIndex());
node->InsertInput(graph()->zone(), 1, jsgraph()->OneConstant()); node->InsertInput(graph()->zone(), 1, jsgraph()->OneConstant());
NodeProperties::ChangeOp(node, javascript()->Add(p.feedback())); NodeProperties::ChangeOp(node, javascript()->Add(p.feedback()));
JSBinopReduction r(this, node); JSBinopReduction r(this, node);
@ -503,6 +506,7 @@ Reduction JSTypedLowering::ReduceJSNegate(Node* node) {
if (input_type.Is(Type::PlainPrimitive())) { if (input_type.Is(Type::PlainPrimitive())) {
// JSNegate(x) => NumberMultiply(ToNumber(x), -1) // JSNegate(x) => NumberMultiply(ToNumber(x), -1)
const FeedbackParameter& p = FeedbackParameterOf(node->op()); const FeedbackParameter& p = FeedbackParameterOf(node->op());
node->RemoveInput(JSNegateNode::FeedbackVectorIndex());
node->InsertInput(graph()->zone(), 1, jsgraph()->SmiConstant(-1)); node->InsertInput(graph()->zone(), 1, jsgraph()->SmiConstant(-1));
NodeProperties::ChangeOp(node, javascript()->Multiply(p.feedback())); NodeProperties::ChangeOp(node, javascript()->Multiply(p.feedback()));
JSBinopReduction r(this, node); JSBinopReduction r(this, node);

View File

@ -303,6 +303,16 @@ Node** Node::OutOfLineInputs::inputs() {
std::ostream& operator<<(std::ostream& os, const Node& n); std::ostream& operator<<(std::ostream& os, const Node& n);
// Base class for node wrappers.
class NodeWrapper {
public:
explicit constexpr NodeWrapper(Node* node) : node_(node) {}
operator Node*() const { return node_; }
Node* operator->() const { return node_; }
private:
Node* node_;
};
// Typedefs to shorten commonly used Node containers. // Typedefs to shorten commonly used Node containers.
using NodeDeque = ZoneDeque<Node*>; using NodeDeque = ZoneDeque<Node*>;