[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:
parent
35a802ea9e
commit
fc3c197562
@ -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
|
||||||
|
@ -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);
|
||||||
};
|
};
|
||||||
|
@ -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);
|
||||||
|
@ -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) {
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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 {
|
||||||
|
@ -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);
|
||||||
|
@ -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*>;
|
||||||
|
Loading…
Reference in New Issue
Block a user