Commit ad73bbe9 authored by Georg Neis's avatar Georg Neis Committed by Commit Bot

Revert "[TurboFan] Brokerize remaining feedback vector slots"

This reverts commit 9499ec0d.

Reason for revert: Breaks lots of stuff, e.g.
https://ci.chromium.org/p/v8/builders/ci/V8-Blink%20Linux%2064%20-%20future/9401

Original change's description:
> [TurboFan] Brokerize remaining feedback vector slots
> 
> This CL adds new ProcessedFeedback subclasses, corresponding to various IC
> types:
> 
> * ForIn
> * Comparison ops
> * Binary/Unary ops
> * InstanceOf
> * Calls
> 
> The feedback is gathered at serialization time and used in several places,
> namely:
> 
> * Bytecode graph building,
> * and its helper class JSTypeHintLowering (with its "early lowering")
> * Native context specialization
> * JSCallReducer
> 
> Design doc: https://docs.google.com/document/d/1JLG0VFV8xmsAIJexU19xzlbNyP51ONqfo_Gf_2DcPC8/edit?usp=sharing
> 
> Bug: v8:7790
> Change-Id: I53c3d7a17f844384f38c4ee0f0b082c114217a02
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1710663
> Commit-Queue: Georg Neis <neis@chromium.org>
> Reviewed-by: Maya Lekova <mslekova@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#63028}

TBR=mvstanton@chromium.org,neis@chromium.org,mslekova@chromium.org

# Not skipping CQ checks because original CL landed > 1 day ago.

Bug: v8:7790
Change-Id: Id0c4d6651611fc3964010f7615d0ad0485169ebc
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1735315Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Reviewed-by: 's avatarMichael Achenbach <machenbach@chromium.org>
Reviewed-by: 's avatarTamer Tas <tmrts@chromium.org>
Commit-Queue: Georg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#63073}
parent cdfadf4a
...@@ -245,12 +245,11 @@ class BytecodeGraphBuilder { ...@@ -245,12 +245,11 @@ class BytecodeGraphBuilder {
ForInMode GetForInMode(int operand_index); ForInMode GetForInMode(int operand_index);
// Helper function to compute call frequency from the recorded type // Helper function to compute call frequency from the recorded type
// feedback. Returns unknown if invocation count is unknown. Returns 0 if // feedback.
// feedback is insufficient.
CallFrequency ComputeCallFrequency(int slot_id) const; CallFrequency ComputeCallFrequency(int slot_id) const;
// Helper function to extract the speculation mode from the recorded type // Helper function to extract the speculation mode from the recorded type
// feedback. Returns kDisallowSpeculation if feedback is insufficient. // feedback.
SpeculationMode GetSpeculationMode(int slot_id) const; SpeculationMode GetSpeculationMode(int slot_id) const;
// Control flow plumbing. // Control flow plumbing.
...@@ -951,7 +950,7 @@ BytecodeGraphBuilder::BytecodeGraphBuilder( ...@@ -951,7 +950,7 @@ BytecodeGraphBuilder::BytecodeGraphBuilder(
bytecode_array_(bytecode_array), bytecode_array_(bytecode_array),
feedback_vector_(feedback_vector), feedback_vector_(feedback_vector),
type_hint_lowering_( type_hint_lowering_(
broker, jsgraph, feedback_vector, jsgraph, feedback_vector.object(),
(flags & BytecodeGraphBuilderFlag::kBailoutOnUninitialized) (flags & BytecodeGraphBuilderFlag::kBailoutOnUninitialized)
? JSTypeHintLowering::kBailoutOnUninitialized ? JSTypeHintLowering::kBailoutOnUninitialized
: JSTypeHintLowering::kNoFlags), : JSTypeHintLowering::kNoFlags),
...@@ -1017,7 +1016,6 @@ Node* BytecodeGraphBuilder::BuildLoadNativeContextField(int index) { ...@@ -1017,7 +1016,6 @@ Node* BytecodeGraphBuilder::BuildLoadNativeContextField(int index) {
VectorSlotPair BytecodeGraphBuilder::CreateVectorSlotPair(int slot_id) { VectorSlotPair BytecodeGraphBuilder::CreateVectorSlotPair(int slot_id) {
FeedbackSlot slot = FeedbackVector::ToSlot(slot_id); FeedbackSlot slot = FeedbackVector::ToSlot(slot_id);
// TODO(mvstanton): eliminate this use of a FeedbackNexus.
FeedbackNexus nexus(feedback_vector().object(), slot); FeedbackNexus nexus(feedback_vector().object(), slot);
return VectorSlotPair(feedback_vector().object(), slot, nexus.ic_state()); return VectorSlotPair(feedback_vector().object(), slot, nexus.ic_state());
} }
...@@ -2143,11 +2141,11 @@ void BytecodeGraphBuilder::BuildCall(ConvertReceiverMode receiver_mode, ...@@ -2143,11 +2141,11 @@ void BytecodeGraphBuilder::BuildCall(ConvertReceiverMode receiver_mode,
PrepareEagerCheckpoint(); PrepareEagerCheckpoint();
VectorSlotPair feedback = CreateVectorSlotPair(slot_id); VectorSlotPair feedback = CreateVectorSlotPair(slot_id);
CallFrequency frequency = ComputeCallFrequency(slot_id);
SpeculationMode speculation_mode = GetSpeculationMode(slot_id);
const Operator* op = javascript()->Call(arg_count, frequency, feedback,
receiver_mode, speculation_mode);
CallFrequency frequency = ComputeCallFrequency(slot_id);
const Operator* op =
javascript()->Call(arg_count, frequency, feedback, receiver_mode,
GetSpeculationMode(slot_id));
JSTypeHintLowering::LoweringResult lowering = TryBuildSimplifiedCall( JSTypeHintLowering::LoweringResult lowering = TryBuildSimplifiedCall(
op, args, static_cast<int>(arg_count), feedback.slot()); op, args, static_cast<int>(arg_count), feedback.slot());
if (lowering.IsExit()) return; if (lowering.IsExit()) return;
...@@ -2328,6 +2326,7 @@ void BytecodeGraphBuilder::VisitCallWithSpread() { ...@@ -2328,6 +2326,7 @@ void BytecodeGraphBuilder::VisitCallWithSpread() {
first_arg, arg_count); first_arg, arg_count);
int const slot_id = bytecode_iterator().GetIndexOperand(3); int const slot_id = bytecode_iterator().GetIndexOperand(3);
VectorSlotPair feedback = CreateVectorSlotPair(slot_id); VectorSlotPair feedback = CreateVectorSlotPair(slot_id);
CallFrequency frequency = ComputeCallFrequency(slot_id); CallFrequency frequency = ComputeCallFrequency(slot_id);
const Operator* op = javascript()->CallWithSpread( const Operator* op = javascript()->CallWithSpread(
static_cast<int>(reg_count + 1), frequency, feedback); static_cast<int>(reg_count + 1), frequency, feedback);
...@@ -2641,23 +2640,23 @@ void BytecodeGraphBuilder::BuildBinaryOp(const Operator* op) { ...@@ -2641,23 +2640,23 @@ void BytecodeGraphBuilder::BuildBinaryOp(const Operator* op) {
BinaryOperationHint BytecodeGraphBuilder::GetBinaryOperationHint( BinaryOperationHint BytecodeGraphBuilder::GetBinaryOperationHint(
int operand_index) { int operand_index) {
FeedbackSlot slot = bytecode_iterator().GetSlotOperand(operand_index); FeedbackSlot slot = bytecode_iterator().GetSlotOperand(operand_index);
FeedbackSource source(feedback_vector(), slot); FeedbackNexus nexus(feedback_vector().object(), slot);
return broker()->GetFeedbackForBinaryOperation(source); return nexus.GetBinaryOperationFeedback();
} }
// Helper function to create compare operation hint from the recorded type // Helper function to create compare operation hint from the recorded type
// feedback. // feedback.
CompareOperationHint BytecodeGraphBuilder::GetCompareOperationHint() { CompareOperationHint BytecodeGraphBuilder::GetCompareOperationHint() {
FeedbackSlot slot = bytecode_iterator().GetSlotOperand(1); FeedbackSlot slot = bytecode_iterator().GetSlotOperand(1);
FeedbackSource source(feedback_vector(), slot); FeedbackNexus nexus(feedback_vector().object(), slot);
return broker()->GetFeedbackForCompareOperation(source); return nexus.GetCompareOperationFeedback();
} }
// Helper function to create for-in mode from the recorded type feedback. // Helper function to create for-in mode from the recorded type feedback.
ForInMode BytecodeGraphBuilder::GetForInMode(int operand_index) { ForInMode BytecodeGraphBuilder::GetForInMode(int operand_index) {
FeedbackSlot slot = bytecode_iterator().GetSlotOperand(operand_index); FeedbackSlot slot = bytecode_iterator().GetSlotOperand(operand_index);
FeedbackSource source(feedback_vector(), slot); FeedbackNexus nexus(feedback_vector().object(), slot);
switch (broker()->GetFeedbackForForIn(source)) { switch (nexus.GetForInFeedback()) {
case ForInHint::kNone: case ForInHint::kNone:
case ForInHint::kEnumCacheKeysAndIndices: case ForInHint::kEnumCacheKeysAndIndices:
return ForInMode::kUseEnumCacheKeysAndIndices; return ForInMode::kUseEnumCacheKeysAndIndices;
...@@ -2671,13 +2670,11 @@ ForInMode BytecodeGraphBuilder::GetForInMode(int operand_index) { ...@@ -2671,13 +2670,11 @@ ForInMode BytecodeGraphBuilder::GetForInMode(int operand_index) {
CallFrequency BytecodeGraphBuilder::ComputeCallFrequency(int slot_id) const { CallFrequency BytecodeGraphBuilder::ComputeCallFrequency(int slot_id) const {
if (invocation_frequency_.IsUnknown()) return CallFrequency(); if (invocation_frequency_.IsUnknown()) return CallFrequency();
FeedbackSlot slot = FeedbackVector::ToSlot(slot_id); FeedbackNexus nexus(feedback_vector().object(),
FeedbackSource source(feedback_vector(), slot); FeedbackVector::ToSlot(slot_id));
ProcessedFeedback const* feedback = broker()->GetFeedbackForCall(source); float feedback_frequency = nexus.ComputeCallFrequency();
CHECK_NOT_NULL(feedback); if (feedback_frequency == 0.0f) {
float feedback_frequency = // This is to prevent multiplying zero and infinity.
feedback->IsInsufficient() ? 0.0f : feedback->AsCall()->frequency();
if (feedback_frequency == 0.0f) { // Prevent multiplying zero and infinity.
return CallFrequency(0.0f); return CallFrequency(0.0f);
} else { } else {
return CallFrequency(feedback_frequency * invocation_frequency_.value()); return CallFrequency(feedback_frequency * invocation_frequency_.value());
...@@ -2685,12 +2682,9 @@ CallFrequency BytecodeGraphBuilder::ComputeCallFrequency(int slot_id) const { ...@@ -2685,12 +2682,9 @@ CallFrequency BytecodeGraphBuilder::ComputeCallFrequency(int slot_id) const {
} }
SpeculationMode BytecodeGraphBuilder::GetSpeculationMode(int slot_id) const { SpeculationMode BytecodeGraphBuilder::GetSpeculationMode(int slot_id) const {
FeedbackSlot slot = FeedbackVector::ToSlot(slot_id); FeedbackNexus nexus(feedback_vector().object(),
FeedbackSource source(feedback_vector(), slot); FeedbackVector::ToSlot(slot_id));
ProcessedFeedback const* feedback = broker()->GetFeedbackForCall(source); return nexus.GetSpeculationMode();
CHECK_NOT_NULL(feedback);
return feedback->IsInsufficient() ? SpeculationMode::kDisallowSpeculation
: feedback->AsCall()->speculation_mode();
} }
void BytecodeGraphBuilder::VisitBitwiseNot() { void BytecodeGraphBuilder::VisitBitwiseNot() {
......
...@@ -801,6 +801,121 @@ class InternalizedStringRef : public StringRef { ...@@ -801,6 +801,121 @@ class InternalizedStringRef : public StringRef {
using StringRef::StringRef; using StringRef::StringRef;
Handle<InternalizedString> object() const; Handle<InternalizedString> object() const;
}; };
class ElementAccessFeedback;
class NamedAccessFeedback;
class ProcessedFeedback : public ZoneObject {
public:
enum Kind { kInsufficient, kGlobalAccess, kNamedAccess, kElementAccess };
Kind kind() const { return kind_; }
ElementAccessFeedback const* AsElementAccess() const;
NamedAccessFeedback const* AsNamedAccess() const;
protected:
explicit ProcessedFeedback(Kind kind) : kind_(kind) {}
private:
Kind const kind_;
};
class InsufficientFeedback final : public ProcessedFeedback {
public:
InsufficientFeedback();
};
class GlobalAccessFeedback : public ProcessedFeedback {
public:
explicit GlobalAccessFeedback(PropertyCellRef cell);
GlobalAccessFeedback(ContextRef script_context, int slot_index,
bool immutable);
bool IsPropertyCell() const;
PropertyCellRef property_cell() const;
bool IsScriptContextSlot() const { return !IsPropertyCell(); }
ContextRef script_context() const;
int slot_index() const;
bool immutable() const;
base::Optional<ObjectRef> GetConstantHint() const;
private:
ObjectRef const cell_or_context_;
int const index_and_immutable_;
};
class KeyedAccessMode {
public:
static KeyedAccessMode FromNexus(FeedbackNexus const& nexus);
AccessMode access_mode() const;
bool IsLoad() const;
bool IsStore() const;
KeyedAccessLoadMode load_mode() const;
KeyedAccessStoreMode store_mode() const;
private:
AccessMode const access_mode_;
union LoadStoreMode {
LoadStoreMode(KeyedAccessLoadMode load_mode);
LoadStoreMode(KeyedAccessStoreMode store_mode);
KeyedAccessLoadMode load_mode;
KeyedAccessStoreMode store_mode;
} const load_store_mode_;
KeyedAccessMode(AccessMode access_mode, KeyedAccessLoadMode load_mode);
KeyedAccessMode(AccessMode access_mode, KeyedAccessStoreMode store_mode);
};
class ElementAccessFeedback : public ProcessedFeedback {
public:
ElementAccessFeedback(Zone* zone, KeyedAccessMode const& keyed_mode);
// No transition sources appear in {receiver_maps}.
// All transition targets appear in {receiver_maps}.
ZoneVector<Handle<Map>> receiver_maps;
ZoneVector<std::pair<Handle<Map>, Handle<Map>>> transitions;
KeyedAccessMode const keyed_mode;
class MapIterator {
public:
bool done() const;
void advance();
MapRef current() const;
private:
friend class ElementAccessFeedback;
explicit MapIterator(ElementAccessFeedback const& processed,
JSHeapBroker* broker);
ElementAccessFeedback const& processed_;
JSHeapBroker* const broker_;
size_t index_ = 0;
};
// Iterator over all maps: first {receiver_maps}, then transition sources.
MapIterator all_maps(JSHeapBroker* broker) const;
};
class NamedAccessFeedback : public ProcessedFeedback {
public:
NamedAccessFeedback(NameRef const& name,
ZoneVector<PropertyAccessInfo> const& access_infos);
NameRef const& name() const { return name_; }
ZoneVector<PropertyAccessInfo> const& access_infos() const {
return access_infos_;
}
private:
NameRef const name_;
ZoneVector<PropertyAccessInfo> const access_infos_;
};
} // namespace compiler } // namespace compiler
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
......
...@@ -3226,6 +3226,13 @@ bool ShouldUseCallICFeedback(Node* node) { ...@@ -3226,6 +3226,13 @@ bool ShouldUseCallICFeedback(Node* node) {
return true; return true;
} }
base::Optional<HeapObjectRef> GetHeapObjectFeedback(
JSHeapBroker* broker, const FeedbackNexus& nexus) {
HeapObject object;
if (!nexus.GetFeedback()->GetHeapObject(&object)) return base::nullopt;
return HeapObjectRef(broker, handle(object, broker->isolate()));
}
} // namespace } // namespace
Reduction JSCallReducer::ReduceJSCall(Node* node) { Reduction JSCallReducer::ReduceJSCall(Node* node) {
...@@ -3344,18 +3351,19 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) { ...@@ -3344,18 +3351,19 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) {
return reduction.Changed() ? reduction : Changed(node); return reduction.Changed() ? reduction : Changed(node);
} }
// Extract feedback from the {node} using the FeedbackNexus.
if (!p.feedback().IsValid()) return NoChange(); if (!p.feedback().IsValid()) return NoChange();
ProcessedFeedback const* feedback = FeedbackNexus nexus(p.feedback().vector(), p.feedback().slot());
broker()->GetFeedbackForCall(FeedbackSource(p.feedback())); if (nexus.IsUninitialized()) {
if (feedback->IsInsufficient()) {
return ReduceSoftDeoptimize( return ReduceSoftDeoptimize(
node, DeoptimizeReason::kInsufficientTypeFeedbackForCall); node, DeoptimizeReason::kInsufficientTypeFeedbackForCall);
} }
base::Optional<HeapObjectRef> feedback_target = feedback->AsCall()->target(); base::Optional<HeapObjectRef> feedback =
if (feedback_target.has_value() && ShouldUseCallICFeedback(target) && GetHeapObjectFeedback(broker(), nexus);
feedback_target->map().is_callable()) { if (feedback.has_value() && ShouldUseCallICFeedback(target) &&
Node* target_function = jsgraph()->Constant(*feedback_target); feedback->map().is_callable()) {
Node* target_function = jsgraph()->Constant(*feedback);
// Check that the {target} is still the {target_function}. // Check that the {target} is still the {target_function}.
Node* check = graph()->NewNode(simplified()->ReferenceEqual(), target, Node* check = graph()->NewNode(simplified()->ReferenceEqual(), target,
...@@ -3758,10 +3766,6 @@ Reduction JSCallReducer::ReduceJSCallWithSpread(Node* node) { ...@@ -3758,10 +3766,6 @@ Reduction JSCallReducer::ReduceJSCallWithSpread(Node* node) {
} }
Reduction JSCallReducer::ReduceJSConstruct(Node* node) { Reduction JSCallReducer::ReduceJSConstruct(Node* node) {
// TODO(mslekova): Remove once ReduceJSConstruct is brokerized.
AllowHandleDereference allow_handle_dereference;
AllowHandleAllocation allow_handle_allocation;
DCHECK_EQ(IrOpcode::kJSConstruct, node->opcode()); DCHECK_EQ(IrOpcode::kJSConstruct, node->opcode());
ConstructParameters const& p = ConstructParametersOf(node->op()); ConstructParameters const& p = ConstructParametersOf(node->op());
DCHECK_LE(2u, p.arity()); DCHECK_LE(2u, p.arity());
...@@ -3771,17 +3775,17 @@ Reduction JSCallReducer::ReduceJSConstruct(Node* node) { ...@@ -3771,17 +3775,17 @@ Reduction JSCallReducer::ReduceJSConstruct(Node* node) {
Node* effect = NodeProperties::GetEffectInput(node); Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node); Node* control = NodeProperties::GetControlInput(node);
// Extract feedback from the {node} using the FeedbackNexus.
if (p.feedback().IsValid()) { if (p.feedback().IsValid()) {
ProcessedFeedback const* feedback = FeedbackNexus nexus(p.feedback().vector(), p.feedback().slot());
broker()->GetFeedbackForCall(FeedbackSource(p.feedback())); if (nexus.IsUninitialized()) {
if (feedback->IsInsufficient()) {
return ReduceSoftDeoptimize( return ReduceSoftDeoptimize(
node, DeoptimizeReason::kInsufficientTypeFeedbackForConstruct); node, DeoptimizeReason::kInsufficientTypeFeedbackForConstruct);
} }
base::Optional<HeapObjectRef> feedback_target = base::Optional<HeapObjectRef> feedback =
feedback->AsCall()->target(); GetHeapObjectFeedback(broker(), nexus);
if (feedback_target.has_value() && feedback_target->IsAllocationSite()) { if (feedback.has_value() && feedback->IsAllocationSite()) {
// The feedback is an AllocationSite, which means we have called the // The feedback is an AllocationSite, which means we have called the
// Array function and collected transition (and pretenuring) feedback // Array function and collected transition (and pretenuring) feedback
// for the resulting arrays. This has to be kept in sync with the // for the resulting arrays. This has to be kept in sync with the
...@@ -3806,12 +3810,12 @@ Reduction JSCallReducer::ReduceJSConstruct(Node* node) { ...@@ -3806,12 +3810,12 @@ Reduction JSCallReducer::ReduceJSConstruct(Node* node) {
NodeProperties::ReplaceValueInput(node, array_function, 1); NodeProperties::ReplaceValueInput(node, array_function, 1);
NodeProperties::ChangeOp( NodeProperties::ChangeOp(
node, javascript()->CreateArray( node, javascript()->CreateArray(
arity, feedback_target->AsAllocationSite().object())); arity, feedback->AsAllocationSite().object()));
return Changed(node); return Changed(node);
} else if (feedback_target.has_value() && } else if (feedback.has_value() &&
!HeapObjectMatcher(new_target).HasValue() && !HeapObjectMatcher(new_target).HasValue() &&
feedback_target->map().is_constructor()) { feedback->map().is_constructor()) {
Node* new_target_feedback = jsgraph()->Constant(*feedback_target); Node* new_target_feedback = jsgraph()->Constant(*feedback);
// Check that the {new_target} is still the {new_target_feedback}. // Check that the {new_target} is still the {new_target_feedback}.
Node* check = graph()->NewNode(simplified()->ReferenceEqual(), new_target, Node* check = graph()->NewNode(simplified()->ReferenceEqual(), new_target,
...@@ -6076,7 +6080,7 @@ Reduction JSCallReducer::ReducePromisePrototypeFinally(Node* node) { ...@@ -6076,7 +6080,7 @@ Reduction JSCallReducer::ReducePromisePrototypeFinally(Node* node) {
} }
Reduction JSCallReducer::ReducePromisePrototypeThen(Node* node) { Reduction JSCallReducer::ReducePromisePrototypeThen(Node* node) {
DisallowHeapAccessIf no_heap_access(FLAG_concurrent_inlining); DisallowHeapAccessIf no_heap_acess(FLAG_concurrent_inlining);
DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
CallParameters const& p = CallParametersOf(node->op()); CallParameters const& p = CallParametersOf(node->op());
...@@ -6144,7 +6148,7 @@ Reduction JSCallReducer::ReducePromisePrototypeThen(Node* node) { ...@@ -6144,7 +6148,7 @@ Reduction JSCallReducer::ReducePromisePrototypeThen(Node* node) {
// ES section #sec-promise.resolve // ES section #sec-promise.resolve
Reduction JSCallReducer::ReducePromiseResolveTrampoline(Node* node) { Reduction JSCallReducer::ReducePromiseResolveTrampoline(Node* node) {
DisallowHeapAccessIf no_heap_access(FLAG_concurrent_inlining); DisallowHeapAccessIf no_heap_acess(FLAG_concurrent_inlining);
DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
Node* receiver = NodeProperties::GetValueInput(node, 1); Node* receiver = NodeProperties::GetValueInput(node, 1);
......
...@@ -3998,20 +3998,11 @@ NamedAccessFeedback::NamedAccessFeedback( ...@@ -3998,20 +3998,11 @@ NamedAccessFeedback::NamedAccessFeedback(
CHECK(!access_infos.empty()); CHECK(!access_infos.empty());
} }
FeedbackSource::FeedbackSource(Handle<FeedbackVector> vector_,
FeedbackSlot slot_)
: vector(vector_), slot(slot_) {
DCHECK(!slot.IsInvalid());
}
FeedbackSource::FeedbackSource(FeedbackVectorRef vector_, FeedbackSlot slot_)
: FeedbackSource(vector_.object(), slot_) {}
FeedbackSource::FeedbackSource(FeedbackNexus const& nexus) FeedbackSource::FeedbackSource(FeedbackNexus const& nexus)
: FeedbackSource(nexus.vector_handle(), nexus.slot()) {} : vector(nexus.vector_handle()), slot(nexus.slot()) {}
FeedbackSource::FeedbackSource(VectorSlotPair const& pair) FeedbackSource::FeedbackSource(VectorSlotPair const& pair)
: FeedbackSource(pair.vector(), pair.slot()) {} : vector(pair.vector()), slot(pair.slot()) {}
void JSHeapBroker::SetFeedback(FeedbackSource const& source, void JSHeapBroker::SetFeedback(FeedbackSource const& source,
ProcessedFeedback const* feedback) { ProcessedFeedback const* feedback) {
...@@ -4135,155 +4126,6 @@ GlobalAccessFeedback const* JSHeapBroker::ProcessFeedbackForGlobalAccess( ...@@ -4135,155 +4126,6 @@ GlobalAccessFeedback const* JSHeapBroker::ProcessFeedbackForGlobalAccess(
return new (zone()) GlobalAccessFeedback(cell); return new (zone()) GlobalAccessFeedback(cell);
} }
bool JSHeapBroker::FeedbackIsInsufficient(FeedbackSource const& source) const {
if (FLAG_concurrent_inlining) {
ProcessedFeedback const* feedback = GetFeedback(source);
// TODO(mvstanton): handling nullptr is a workaround until
// we find a way to express megamorphic feedback.
return feedback != nullptr && feedback->IsInsufficient();
}
return FeedbackNexus(source.vector, source.slot).IsUninitialized();
}
BinaryOperationHint JSHeapBroker::ReadBinaryOperationFeedback(
FeedbackSource const& source) const {
return FeedbackNexus(source.vector, source.slot).GetBinaryOperationFeedback();
}
CompareOperationHint JSHeapBroker::ReadCompareOperationFeedback(
FeedbackSource const& source) const {
return FeedbackNexus(source.vector, source.slot)
.GetCompareOperationFeedback();
}
ForInHint JSHeapBroker::ReadForInFeedback(FeedbackSource const& source) const {
return FeedbackNexus(source.vector, source.slot).GetForInFeedback();
}
ProcessedFeedback const* JSHeapBroker::ReadInstanceOfFeedback(
FeedbackSource const& source) {
FeedbackNexus nexus(source.vector, source.slot);
if (nexus.IsUninitialized()) return new (zone()) InsufficientFeedback();
base::Optional<JSObjectRef> optional_constructor;
{
MaybeHandle<JSObject> maybe_constructor = nexus.GetConstructorFeedback();
Handle<JSObject> constructor;
if (maybe_constructor.ToHandle(&constructor)) {
optional_constructor = JSObjectRef(this, constructor);
}
}
return new (zone()) InstanceOfFeedback(optional_constructor);
}
ProcessedFeedback const* JSHeapBroker::ReadCallFeedback(
FeedbackSource const& source) {
FeedbackNexus nexus(source.vector, source.slot);
if (nexus.IsUninitialized()) return new (zone()) InsufficientFeedback();
base::Optional<HeapObjectRef> target_ref;
{
MaybeObject maybe_target = nexus.GetFeedback();
HeapObject target_object;
if (maybe_target->GetHeapObject(&target_object)) {
target_ref = HeapObjectRef(this, handle(target_object, isolate()));
}
}
float frequency = nexus.ComputeCallFrequency();
SpeculationMode mode = nexus.GetSpeculationMode();
return new (zone()) CallFeedback(target_ref, frequency, mode);
}
BinaryOperationHint JSHeapBroker::GetFeedbackForBinaryOperation(
FeedbackSource const& source) const {
if (!FLAG_concurrent_inlining) return ReadBinaryOperationFeedback(source);
ProcessedFeedback const* feedback = GetFeedback(source);
return feedback->IsInsufficient() ? BinaryOperationHint::kNone
: feedback->AsBinaryOperation()->value();
}
CompareOperationHint JSHeapBroker::GetFeedbackForCompareOperation(
FeedbackSource const& source) const {
if (!FLAG_concurrent_inlining) return ReadCompareOperationFeedback(source);
ProcessedFeedback const* feedback = GetFeedback(source);
return feedback->IsInsufficient() ? CompareOperationHint::kNone
: feedback->AsCompareOperation()->value();
}
ForInHint JSHeapBroker::GetFeedbackForForIn(
FeedbackSource const& source) const {
if (!FLAG_concurrent_inlining) return ReadForInFeedback(source);
ProcessedFeedback const* feedback = GetFeedback(source);
return feedback->IsInsufficient() ? ForInHint::kNone
: feedback->AsForIn()->value();
}
ProcessedFeedback const* JSHeapBroker::GetFeedbackForInstanceOf(
FeedbackSource const& source) {
return FLAG_concurrent_inlining ? GetFeedback(source)
: ReadInstanceOfFeedback(source);
}
ProcessedFeedback const* JSHeapBroker::GetFeedbackForCall(
FeedbackSource const& source) {
return FLAG_concurrent_inlining ? GetFeedback(source)
: ReadCallFeedback(source);
}
void JSHeapBroker::ProcessFeedbackForBinaryOperation(
FeedbackSource const& source) {
if (HasFeedback(source)) return;
BinaryOperationHint hint = ReadBinaryOperationFeedback(source);
ProcessedFeedback const* feedback;
if (hint == BinaryOperationHint::kNone) {
feedback = new (zone()) InsufficientFeedback();
} else {
feedback = new (zone()) BinaryOperationFeedback(hint);
}
SetFeedback(source, feedback);
}
void JSHeapBroker::ProcessFeedbackForCompareOperation(
FeedbackSource const& source) {
if (HasFeedback(source)) return;
CompareOperationHint hint = ReadCompareOperationFeedback(source);
ProcessedFeedback const* feedback;
if (hint == CompareOperationHint::kNone) {
feedback = new (zone()) InsufficientFeedback();
} else {
feedback = new (zone()) CompareOperationFeedback(hint);
}
SetFeedback(source, feedback);
}
void JSHeapBroker::ProcessFeedbackForForIn(FeedbackSource const& source) {
if (HasFeedback(source)) return;
ForInHint hint = ReadForInFeedback(source);
ProcessedFeedback const* feedback;
if (hint == ForInHint::kNone) {
feedback = new (zone()) InsufficientFeedback();
} else {
feedback = new (zone()) ForInFeedback(hint);
}
SetFeedback(source, feedback);
}
ProcessedFeedback const* JSHeapBroker::ProcessFeedbackForInstanceOf(
FeedbackSource const& source) {
if (HasFeedback(source)) return GetFeedback(source);
ProcessedFeedback const* feedback = ReadInstanceOfFeedback(source);
SetFeedback(source, feedback);
return feedback;
}
ProcessedFeedback const* JSHeapBroker::ProcessFeedbackForCall(
FeedbackSource const& source) {
if (HasFeedback(source)) return GetFeedback(source);
ProcessedFeedback const* feedback = ReadCallFeedback(source);
SetFeedback(source, feedback);
return feedback;
}
std::ostream& operator<<(std::ostream& os, const ObjectRef& ref) { std::ostream& operator<<(std::ostream& os, const ObjectRef& ref) {
return os << ref.data(); return os << ref.data();
} }
...@@ -4385,31 +4227,6 @@ NamedAccessFeedback const* ProcessedFeedback::AsNamedAccess() const { ...@@ -4385,31 +4227,6 @@ NamedAccessFeedback const* ProcessedFeedback::AsNamedAccess() const {
return static_cast<NamedAccessFeedback const*>(this); return static_cast<NamedAccessFeedback const*>(this);
} }
CallFeedback const* ProcessedFeedback::AsCall() const {
CHECK_EQ(kCall, kind());
return static_cast<CallFeedback const*>(this);
}
InstanceOfFeedback const* ProcessedFeedback::AsInstanceOf() const {
CHECK_EQ(kInstanceOf, kind());
return static_cast<InstanceOfFeedback const*>(this);
}
BinaryOperationFeedback const* ProcessedFeedback::AsBinaryOperation() const {
CHECK_EQ(kBinaryOperation, kind());
return static_cast<BinaryOperationFeedback const*>(this);
}
CompareOperationFeedback const* ProcessedFeedback::AsCompareOperation() const {
CHECK_EQ(kCompareOperation, kind());
return static_cast<CompareOperationFeedback const*>(this);
}
ForInFeedback const* ProcessedFeedback::AsForIn() const {
CHECK_EQ(kForIn, kind());
return static_cast<ForInFeedback const*>(this);
}
BytecodeAnalysis const& JSHeapBroker::GetBytecodeAnalysis( BytecodeAnalysis const& JSHeapBroker::GetBytecodeAnalysis(
Handle<BytecodeArray> bytecode_array, BailoutId osr_bailout_id, Handle<BytecodeArray> bytecode_array, BailoutId osr_bailout_id,
bool analyze_liveness, bool serialize) { bool analyze_liveness, bool serialize) {
......
...@@ -9,7 +9,6 @@ ...@@ -9,7 +9,6 @@
#include "src/base/optional.h" #include "src/base/optional.h"
#include "src/common/globals.h" #include "src/common/globals.h"
#include "src/compiler/access-info.h" #include "src/compiler/access-info.h"
#include "src/compiler/processed-feedback.h"
#include "src/compiler/refs-map.h" #include "src/compiler/refs-map.h"
#include "src/handles/handles.h" #include "src/handles/handles.h"
#include "src/interpreter/bytecode-array-accessor.h" #include "src/interpreter/bytecode-array-accessor.h"
...@@ -28,8 +27,8 @@ class ObjectRef; ...@@ -28,8 +27,8 @@ class ObjectRef;
std::ostream& operator<<(std::ostream& os, const ObjectRef& ref); std::ostream& operator<<(std::ostream& os, const ObjectRef& ref);
struct FeedbackSource { struct FeedbackSource {
FeedbackSource(Handle<FeedbackVector> vector_, FeedbackSlot slot_); FeedbackSource(Handle<FeedbackVector> vector_, FeedbackSlot slot_)
FeedbackSource(FeedbackVectorRef vector_, FeedbackSlot slot_); : vector(vector_), slot(slot_) {}
explicit FeedbackSource(FeedbackNexus const& nexus); explicit FeedbackSource(FeedbackNexus const& nexus);
explicit FeedbackSource(VectorSlotPair const& pair); explicit FeedbackSource(VectorSlotPair const& pair);
...@@ -127,32 +126,11 @@ class V8_EXPORT_PRIVATE JSHeapBroker { ...@@ -127,32 +126,11 @@ class V8_EXPORT_PRIVATE JSHeapBroker {
MapHandles const& maps, KeyedAccessMode const& keyed_mode); MapHandles const& maps, KeyedAccessMode const& keyed_mode);
GlobalAccessFeedback const* ProcessFeedbackForGlobalAccess( GlobalAccessFeedback const* ProcessFeedbackForGlobalAccess(
FeedbackSource const& source); FeedbackSource const& source);
BytecodeAnalysis const& GetBytecodeAnalysis( BytecodeAnalysis const& GetBytecodeAnalysis(
Handle<BytecodeArray> bytecode_array, BailoutId osr_offset, Handle<BytecodeArray> bytecode_array, BailoutId osr_offset,
bool analyze_liveness, bool serialize); bool analyze_liveness, bool serialize);
// Binary, comparison and for-in hints can be fully expressed via
// an enum. Insufficient feedback is signaled by <Hint enum>::kNone.
BinaryOperationHint GetFeedbackForBinaryOperation(
FeedbackSource const& source) const;
CompareOperationHint GetFeedbackForCompareOperation(
FeedbackSource const& source) const;
ForInHint GetFeedbackForForIn(FeedbackSource const& source) const;
ProcessedFeedback const* GetFeedbackForCall(FeedbackSource const& source);
ProcessedFeedback const* GetFeedbackForInstanceOf(
FeedbackSource const& source);
void ProcessFeedbackForBinaryOperation(FeedbackSource const& source);
void ProcessFeedbackForCompareOperation(FeedbackSource const& source);
void ProcessFeedbackForForIn(FeedbackSource const& source);
ProcessedFeedback const* ProcessFeedbackForCall(FeedbackSource const& source);
ProcessedFeedback const* ProcessFeedbackForInstanceOf(
FeedbackSource const& source);
bool FeedbackIsInsufficient(FeedbackSource const& source) const;
base::Optional<NameRef> GetNameFeedback(FeedbackNexus const& nexus); base::Optional<NameRef> GetNameFeedback(FeedbackNexus const& nexus);
// If there is no result stored for {map}, we return an Invalid // If there is no result stored for {map}, we return an Invalid
...@@ -178,17 +156,6 @@ class V8_EXPORT_PRIVATE JSHeapBroker { ...@@ -178,17 +156,6 @@ class V8_EXPORT_PRIVATE JSHeapBroker {
friend class ObjectRef; friend class ObjectRef;
friend class ObjectData; friend class ObjectData;
// Bottleneck FeedbackNexus access here, for storage in the broker
// or on-the-fly usage elsewhere in the compiler.
ForInHint ReadForInFeedback(FeedbackSource const& source) const;
CompareOperationHint ReadCompareOperationFeedback(
FeedbackSource const& source) const;
BinaryOperationHint ReadBinaryOperationFeedback(
FeedbackSource const& source) const;
ProcessedFeedback const* ReadCallFeedback(FeedbackSource const& source);
ProcessedFeedback const* ReadInstanceOfFeedback(FeedbackSource const& source);
void SerializeShareableObjects(); void SerializeShareableObjects();
void CollectArrayAndObjectPrototypes(); void CollectArrayAndObjectPrototypes();
......
...@@ -381,7 +381,9 @@ Reduction JSNativeContextSpecialization::ReduceJSGetSuperConstructor( ...@@ -381,7 +381,9 @@ Reduction JSNativeContextSpecialization::ReduceJSGetSuperConstructor(
} }
Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) { Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
DisallowHeapAccessIf no_heap_access(FLAG_concurrent_inlining); // TODO(neis): Eliminate heap accesses.
AllowHandleDereference allow_handle_dereference;
AllowHandleAllocation allow_handle_allocation;
DCHECK_EQ(IrOpcode::kJSInstanceOf, node->opcode()); DCHECK_EQ(IrOpcode::kJSInstanceOf, node->opcode());
FeedbackParameter const& p = FeedbackParameterOf(node->op()); FeedbackParameter const& p = FeedbackParameterOf(node->op());
...@@ -399,13 +401,10 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) { ...@@ -399,13 +401,10 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
if (m.HasValue() && m.Ref(broker()).IsJSObject()) { if (m.HasValue() && m.Ref(broker()).IsJSObject()) {
receiver = m.Ref(broker()).AsJSObject().object(); receiver = m.Ref(broker()).AsJSObject().object();
} else if (p.feedback().IsValid()) { } else if (p.feedback().IsValid()) {
ProcessedFeedback const* feedback = FeedbackNexus nexus(p.feedback().vector(), p.feedback().slot());
broker()->GetFeedbackForInstanceOf(FeedbackSource(p.feedback())); if (!nexus.GetConstructorFeedback().ToHandle(&receiver)) {
if (feedback->IsInsufficient()) return NoChange(); return NoChange();
base::Optional<JSObjectRef> maybe_receiver = }
feedback->AsInstanceOf()->value();
if (!maybe_receiver.has_value()) return NoChange();
receiver = maybe_receiver->object();
} else { } else {
return NoChange(); return NoChange();
} }
...@@ -1810,7 +1809,7 @@ Reduction JSNativeContextSpecialization::ReducePropertyAccess( ...@@ -1810,7 +1809,7 @@ Reduction JSNativeContextSpecialization::ReducePropertyAccess(
access_mode); access_mode);
return ReduceElementAccess(node, key, value, return ReduceElementAccess(node, key, value,
*processed->AsElementAccess()); *processed->AsElementAccess());
default: case ProcessedFeedback::kGlobalAccess:
UNREACHABLE(); UNREACHABLE();
} }
} }
......
This diff is collapsed.
...@@ -41,8 +41,8 @@ class JSTypeHintLowering { ...@@ -41,8 +41,8 @@ class JSTypeHintLowering {
enum Flag { kNoFlags = 0u, kBailoutOnUninitialized = 1u << 1 }; enum Flag { kNoFlags = 0u, kBailoutOnUninitialized = 1u << 1 };
using Flags = base::Flags<Flag>; using Flags = base::Flags<Flag>;
JSTypeHintLowering(JSHeapBroker* broker, JSGraph* jsgraph, JSTypeHintLowering(JSGraph* jsgraph, Handle<FeedbackVector> feedback_vector,
FeedbackVectorRef feedback_vector, Flags flags); Flags flags);
// {LoweringResult} describes the result of lowering. The following outcomes // {LoweringResult} describes the result of lowering. The following outcomes
// are possible: // are possible:
...@@ -153,22 +153,19 @@ class JSTypeHintLowering { ...@@ -153,22 +153,19 @@ class JSTypeHintLowering {
private: private:
friend class JSSpeculativeBinopBuilder; friend class JSSpeculativeBinopBuilder;
Node* TryBuildSoftDeopt(FeedbackNexus const& nexus, Node* effect,
Node* control, DeoptimizeReason reson) const;
BinaryOperationHint GetBinaryOperationHint(FeedbackSlot slot) const;
CompareOperationHint GetCompareOperationHint(FeedbackSlot slot) const;
Node* TryBuildSoftDeopt(FeedbackSlot slot, Node* effect, Node* control,
DeoptimizeReason reson) const;
JSHeapBroker* broker() const { return broker_; }
JSGraph* jsgraph() const { return jsgraph_; } JSGraph* jsgraph() const { return jsgraph_; }
Isolate* isolate() const; Isolate* isolate() const;
Flags flags() const { return flags_; } Flags flags() const { return flags_; }
FeedbackVectorRef const& feedback_vector() const { return feedback_vector_; } const Handle<FeedbackVector>& feedback_vector() const {
return feedback_vector_;
}
JSHeapBroker* const broker_; JSGraph* jsgraph_;
JSGraph* const jsgraph_;
Flags const flags_; Flags const flags_;
FeedbackVectorRef const feedback_vector_; Handle<FeedbackVector> feedback_vector_;
DISALLOW_COPY_AND_ASSIGN(JSTypeHintLowering); DISALLOW_COPY_AND_ASSIGN(JSTypeHintLowering);
}; };
......
// Copyright 2019 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_COMPILER_PROCESSED_FEEDBACK_H_
#define V8_COMPILER_PROCESSED_FEEDBACK_H_
#include "src/compiler/heap-refs.h"
namespace v8 {
namespace internal {
namespace compiler {
class BinaryOperationFeedback;
class CallFeedback;
class CompareOperationFeedback;
class ElementAccessFeedback;
class ForInFeedback;
class InstanceOfFeedback;
class NamedAccessFeedback;
class ProcessedFeedback : public ZoneObject {
public:
enum Kind {
kInsufficient,
kBinaryOperation,
kCall,
kCompareOperation,
kElementAccess,
kForIn,
kGlobalAccess,
kInstanceOf,
kNamedAccess,
};
Kind kind() const { return kind_; }
bool IsInsufficient() const { return kind() == kInsufficient; }
BinaryOperationFeedback const* AsBinaryOperation() const;
CallFeedback const* AsCall() const;
CompareOperationFeedback const* AsCompareOperation() const;
ElementAccessFeedback const* AsElementAccess() const;
ForInFeedback const* AsForIn() const;
InstanceOfFeedback const* AsInstanceOf() const;
NamedAccessFeedback const* AsNamedAccess() const;
protected:
explicit ProcessedFeedback(Kind kind) : kind_(kind) {}
private:
Kind const kind_;
};
class InsufficientFeedback final : public ProcessedFeedback {
public:
InsufficientFeedback();
};
class GlobalAccessFeedback : public ProcessedFeedback {
public:
explicit GlobalAccessFeedback(PropertyCellRef cell);
GlobalAccessFeedback(ContextRef script_context, int slot_index,
bool immutable);
bool IsPropertyCell() const;
PropertyCellRef property_cell() const;
bool IsScriptContextSlot() const { return !IsPropertyCell(); }
ContextRef script_context() const;
int slot_index() const;
bool immutable() const;
base::Optional<ObjectRef> GetConstantHint() const;
private:
ObjectRef const cell_or_context_;
int const index_and_immutable_;
};
class KeyedAccessMode {
public:
static KeyedAccessMode FromNexus(FeedbackNexus const& nexus);
AccessMode access_mode() const;
bool IsLoad() const;
bool IsStore() const;
KeyedAccessLoadMode load_mode() const;
KeyedAccessStoreMode store_mode() const;
private:
AccessMode const access_mode_;
union LoadStoreMode {
LoadStoreMode(KeyedAccessLoadMode load_mode);
LoadStoreMode(KeyedAccessStoreMode store_mode);
KeyedAccessLoadMode load_mode;
KeyedAccessStoreMode store_mode;
} const load_store_mode_;
KeyedAccessMode(AccessMode access_mode, KeyedAccessLoadMode load_mode);
KeyedAccessMode(AccessMode access_mode, KeyedAccessStoreMode store_mode);
};
class ElementAccessFeedback : public ProcessedFeedback {
public:
ElementAccessFeedback(Zone* zone, KeyedAccessMode const& keyed_mode);
// No transition sources appear in {receiver_maps}.
// All transition targets appear in {receiver_maps}.
ZoneVector<Handle<Map>> receiver_maps;
ZoneVector<std::pair<Handle<Map>, Handle<Map>>> transitions;
KeyedAccessMode const keyed_mode;
class MapIterator {
public:
bool done() const;
void advance();
MapRef current() const;
private:
friend class ElementAccessFeedback;
explicit MapIterator(ElementAccessFeedback const& processed,
JSHeapBroker* broker);
ElementAccessFeedback const& processed_;
JSHeapBroker* const broker_;
size_t index_ = 0;
};
// Iterator over all maps: first {receiver_maps}, then transition sources.
MapIterator all_maps(JSHeapBroker* broker) const;
};
class NamedAccessFeedback : public ProcessedFeedback {
public:
NamedAccessFeedback(NameRef const& name,
ZoneVector<PropertyAccessInfo> const& access_infos);
NameRef const& name() const { return name_; }
ZoneVector<PropertyAccessInfo> const& access_infos() const {
return access_infos_;
}
private:
NameRef const name_;
ZoneVector<PropertyAccessInfo> const access_infos_;
};
class CallFeedback : public ProcessedFeedback {
public:
CallFeedback(base::Optional<HeapObjectRef> target, float frequency,
SpeculationMode mode)
: ProcessedFeedback(kCall),
target_(target),
frequency_(frequency),
mode_(mode) {}
base::Optional<HeapObjectRef> target() const { return target_; }
float frequency() const { return frequency_; }
SpeculationMode speculation_mode() const { return mode_; }
private:
base::Optional<HeapObjectRef> const target_;
float const frequency_;
SpeculationMode const mode_;
};
template <class T, ProcessedFeedback::Kind K>
class SingleValueFeedback : public ProcessedFeedback {
public:
explicit SingleValueFeedback(T value) : ProcessedFeedback(K), value_(value) {}
T value() const { return value_; }
private:
T const value_;
};
class InstanceOfFeedback
: public SingleValueFeedback<base::Optional<JSObjectRef>,
ProcessedFeedback::kInstanceOf> {
using SingleValueFeedback::SingleValueFeedback;
};
class BinaryOperationFeedback
: public SingleValueFeedback<BinaryOperationHint,
ProcessedFeedback::kBinaryOperation> {
using SingleValueFeedback::SingleValueFeedback;
};
class CompareOperationFeedback
: public SingleValueFeedback<CompareOperationHint,
ProcessedFeedback::kCompareOperation> {
using SingleValueFeedback::SingleValueFeedback;
};
class ForInFeedback
: public SingleValueFeedback<ForInHint, ProcessedFeedback::kForIn> {
using SingleValueFeedback::SingleValueFeedback;
};
} // namespace compiler
} // namespace internal
} // namespace v8
#endif // V8_COMPILER_PROCESSED_FEEDBACK_H_
...@@ -887,7 +887,8 @@ float FeedbackNexus::ComputeCallFrequency() { ...@@ -887,7 +887,8 @@ float FeedbackNexus::ComputeCallFrequency() {
double const invocation_count = vector().invocation_count(); double const invocation_count = vector().invocation_count();
double const call_count = GetCallCount(); double const call_count = GetCallCount();
if (invocation_count == 0.0) { // Prevent division by 0. if (invocation_count == 0) {
// Prevent division by 0.
return 0.0f; return 0.0f;
} }
return static_cast<float>(call_count / invocation_count); return static_cast<float>(call_count / invocation_count);
......
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