Commit 537b32a7 authored by Sathya Gunasekaran's avatar Sathya Gunasekaran Committed by Commit Bot

[turboprop] Add new DynamicMapChecks operator

This CL introduces a new operator that loads the feedback vector and
checks against maps at runtime, rather than embedding the map directly
in the generated code.

A follow on CL will use this operator when generating code for named
property access.

Bug: v8:10582, v8:9684
Change-Id: I372a01586d3048427760f0cb27619a59afc3f59e
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2241518Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Reviewed-by: 's avatarMythri Alle <mythria@chromium.org>
Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Commit-Queue: Sathya Gunasekaran  <gsathya@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68930}
parent c5845b47
......@@ -530,6 +530,18 @@ FieldAccess AccessBuilder::ForFixedArrayLength() {
return access;
}
// static
FieldAccess AccessBuilder::ForWeakFixedArrayLength() {
FieldAccess access = {kTaggedBase,
WeakFixedArray::kLengthOffset,
MaybeHandle<Name>(),
MaybeHandle<Map>(),
TypeCache::Get()->kWeakFixedArrayLengthType,
MachineType::TaggedSigned(),
kNoWriteBarrier};
return access;
}
// static
FieldAccess AccessBuilder::ForSloppyArgumentsElementsContext() {
FieldAccess access = {
......@@ -848,6 +860,16 @@ FieldAccess AccessBuilder::ForFixedArraySlot(
return access;
}
// static
FieldAccess AccessBuilder::ForFeedbackVectorSlot(int index) {
int offset = FeedbackVector::OffsetOfElementAt(index);
FieldAccess access = {kTaggedBase, offset,
Handle<Name>(), MaybeHandle<Map>(),
Type::Any(), MachineType::AnyTagged(),
kFullWriteBarrier};
return access;
}
// static
FieldAccess AccessBuilder::ForCellValue() {
FieldAccess access = {kTaggedBase, Cell::kValueOffset,
......@@ -897,6 +919,14 @@ ElementAccess AccessBuilder::ForFixedArrayElement() {
return access;
}
// static
ElementAccess AccessBuilder::ForWeakFixedArrayElement() {
ElementAccess const access = {kTaggedBase, WeakFixedArray::kHeaderSize,
Type::Any(), MachineType::AnyTagged(),
kFullWriteBarrier};
return access;
}
// static
ElementAccess AccessBuilder::ForSloppyArgumentsElementsMappedEntry() {
ElementAccess access = {
......
......@@ -179,6 +179,9 @@ class V8_EXPORT_PRIVATE AccessBuilder final
// Provides access to FixedArray::length() field.
static FieldAccess ForFixedArrayLength();
// Provides access to WeakFixedArray::length() field.
static FieldAccess ForWeakFixedArrayLength();
// Provides access to SloppyArgumentsElements::context() field.
static FieldAccess ForSloppyArgumentsElementsContext();
......@@ -277,6 +280,8 @@ class V8_EXPORT_PRIVATE AccessBuilder final
static FieldAccess ForFixedArraySlot(
size_t index, WriteBarrierKind write_barrier_kind = kFullWriteBarrier);
static FieldAccess ForFeedbackVectorSlot(int index);
// Provides access to ScopeInfo flags.
static FieldAccess ForScopeInfoFlags();
......@@ -286,6 +291,9 @@ class V8_EXPORT_PRIVATE AccessBuilder final
// Provides access to Context slots that are known to be pointers.
static FieldAccess ForContextSlotKnownPointer(size_t index);
// Provides access to WeakFixedArray elements.
static ElementAccess ForWeakFixedArrayElement();
// Provides access to FixedArray elements.
static ElementAccess ForFixedArrayElement();
static ElementAccess ForFixedArrayElement(
......
......@@ -74,6 +74,7 @@ class EffectControlLinearizer {
Node* LowerPoisonIndex(Node* node);
Node* LowerCheckInternalizedString(Node* node, Node* frame_state);
void LowerCheckMaps(Node* node, Node* frame_state);
void LowerDynamicCheckMaps(Node* node, Node* frame_state);
Node* LowerCompareMaps(Node* node);
Node* LowerCheckNumber(Node* node, Node* frame_state);
Node* LowerCheckClosure(Node* node, Node* frame_state);
......@@ -261,7 +262,9 @@ class EffectControlLinearizer {
Node* ChangeSmiToInt64(Node* value);
Node* ObjectIsSmi(Node* value);
Node* LoadFromSeqString(Node* receiver, Node* position, Node* is_one_byte);
Node* TruncateWordToInt32(Node* value);
Node* BuildIsWeakReferenceTo(Node* maybe_object, Node* value);
Node* BuildIsStrongReference(Node* value);
Node* SmiMaxValueConstant();
Node* SmiShiftBitsConstant();
void TransitionElementsTo(Node* node, Node* array, ElementsKind from,
......@@ -923,6 +926,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kCheckMaps:
LowerCheckMaps(node, frame_state);
break;
case IrOpcode::kDynamicCheckMaps:
LowerDynamicCheckMaps(node, frame_state);
break;
case IrOpcode::kCompareMaps:
result = LowerCompareMaps(node);
break;
......@@ -1845,6 +1851,103 @@ void EffectControlLinearizer::LowerCheckMaps(Node* node, Node* frame_state) {
}
}
void EffectControlLinearizer::LowerDynamicCheckMaps(Node* node,
Node* frame_state) {
DynamicCheckMapsParameters const& p =
DynamicCheckMapsParametersOf(node->op());
Node* value = node->InputAt(0);
FeedbackSource const& feedback = p.feedback();
Node* vector = __ HeapConstant(feedback.vector);
Node* feedback_slot = __ LoadField(
AccessBuilder::ForFeedbackVectorSlot(feedback.index()), vector);
Node* value_map = __ LoadField(AccessBuilder::ForMap(), value);
Node* handler = p.handler()->IsSmi()
? __ SmiConstant(Smi::ToInt(*p.handler()))
: __ HeapConstant(Handle<HeapObject>::cast(p.handler()));
auto done = __ MakeLabel();
auto maybe_poly = __ MakeLabel();
// Emit monomorphic checks only if current state is monomorphic. In
// case the current state is polymorphic, and if we ever go back to
// monomorphic start, we will deopt and reoptimize the code.
if (p.state() == DynamicCheckMapsParameters::kMonomorphic) {
Node* mono_check = BuildIsWeakReferenceTo(feedback_slot, value_map);
__ GotoIfNot(mono_check, &maybe_poly);
Node* feedback_slot_handler = __ LoadField(
AccessBuilder::ForFeedbackVectorSlot(feedback.index() + 1), vector);
mono_check = __ TaggedEqual(handler, feedback_slot_handler);
__ DeoptimizeIfNot(DeoptimizeReason::kWrongHandler, FeedbackSource(),
mono_check, frame_state,
IsSafetyCheck::kCriticalSafetyCheck);
__ Goto(&done);
} else {
DCHECK(p.state() == DynamicCheckMapsParameters::kPolymorphic);
__ Goto(&maybe_poly);
}
__ Bind(&maybe_poly);
{
Node* poly_check = BuildIsStrongReference(feedback_slot);
if (p.state() == DynamicCheckMapsParameters::kMonomorphic) {
__ DeoptimizeIfNot(DeoptimizeReason::kMissingMap, FeedbackSource(),
poly_check, frame_state,
IsSafetyCheck::kCriticalSafetyCheck);
} else {
__ DeoptimizeIfNot(DeoptimizeReason::kTransitionedToMonomorphicIC,
FeedbackSource(), poly_check, frame_state,
IsSafetyCheck::kCriticalSafetyCheck);
}
Node* feedback_slot_map =
__ LoadField(AccessBuilder::ForMap(), feedback_slot);
Node* is_weak_fixed_array_check =
__ TaggedEqual(feedback_slot_map, __ WeakFixedArrayMapConstant());
__ DeoptimizeIfNot(DeoptimizeReason::kTransitionedToMegamorphicIC,
FeedbackSource(), is_weak_fixed_array_check, frame_state,
IsSafetyCheck::kCriticalSafetyCheck);
Node* length =
__ LoadField(AccessBuilder::ForWeakFixedArrayLength(), feedback_slot);
auto loop = __ MakeLoopLabel(MachineRepresentation::kWord32);
__ Goto(&loop, __ Int32Constant(0));
__ Bind(&loop);
{
Node* index = loop.PhiAt(0);
Node* check = __ Int32LessThan(index, length);
__ DeoptimizeIfNot(DeoptimizeReason::kMissingMap, FeedbackSource(), check,
frame_state, IsSafetyCheck::kCriticalSafetyCheck);
Node* maybe_map = __ LoadElement(
AccessBuilder::ForWeakFixedArrayElement(), feedback_slot, index);
auto continue_loop = __ MakeLabel();
__ GotoIfNot(BuildIsWeakReferenceTo(maybe_map, value_map),
&continue_loop);
constexpr int kHandlerOffsetInEntry = 1;
Node* maybe_handler = __ LoadElement(
AccessBuilder::ForWeakFixedArrayElement(), feedback_slot,
__ Int32Add(index, __ Int32Constant(kHandlerOffsetInEntry)));
Node* handler_check = __ TaggedEqual(maybe_handler, handler);
__ DeoptimizeIfNot(DeoptimizeReason::kWrongHandler, FeedbackSource(),
handler_check, frame_state,
IsSafetyCheck::kCriticalSafetyCheck);
__ Goto(&done);
__ Bind(&continue_loop);
constexpr int kEntrySize = 2;
index = __ Int32Add(index, __ Int32Constant(kEntrySize));
__ Goto(&loop, index);
}
}
__ Bind(&done);
}
Node* EffectControlLinearizer::LowerCompareMaps(Node* node) {
ZoneHandleSet<Map> const& maps = CompareMapsParametersOf(node->op());
size_t const map_count = maps.size();
......@@ -6189,6 +6292,36 @@ Node* EffectControlLinearizer::LowerDateNow(Node* node) {
__ Int32Constant(0), __ NoContextConstant());
}
Node* EffectControlLinearizer::TruncateWordToInt32(Node* value) {
if (machine()->Is64()) {
return __ TruncateInt64ToInt32(value);
}
return value;
}
Node* EffectControlLinearizer::BuildIsStrongReference(Node* value) {
return __ Word32Equal(
__ Word32And(
TruncateWordToInt32(__ BitcastTaggedToWordForTagAndSmiBits(value)),
__ Int32Constant(kHeapObjectTagMask)),
__ Int32Constant(kHeapObjectTag));
}
Node* EffectControlLinearizer::BuildIsWeakReferenceTo(Node* maybe_object,
Node* value) {
if (COMPRESS_POINTERS_BOOL) {
return __ Word32Equal(
__ Word32And(
TruncateWordToInt32(__ BitcastMaybeObjectToWord(maybe_object)),
__ Uint32Constant(~static_cast<uint32_t>(kWeakHeapObjectMask))),
TruncateWordToInt32(__ BitcastTaggedToWord(value)));
} else {
return __ WordEqual(__ WordAnd(__ BitcastMaybeObjectToWord(maybe_object),
__ IntPtrConstant(~kWeakHeapObjectMask)),
__ BitcastTaggedToWord(value));
}
}
#undef __
void LinearizeEffectControl(JSGraph* graph, Schedule* schedule, Zone* temp_zone,
......
......@@ -706,9 +706,13 @@ Node* GraphAssembler::BitcastTaggedToWord(Node* value) {
}
Node* GraphAssembler::BitcastTaggedToWordForTagAndSmiBits(Node* value) {
return AddNode(
graph()->NewNode(machine()->BitcastTaggedToWordForTagAndSmiBits(), value,
effect(), control()));
return AddNode(graph()->NewNode(
machine()->BitcastTaggedToWordForTagAndSmiBits(), value));
}
Node* GraphAssembler::BitcastMaybeObjectToWord(Node* value) {
return AddNode(graph()->NewNode(machine()->BitcastMaybeObjectToWord(), value,
effect(), control()));
}
Node* GraphAssembler::Word32PoisonOnSpeculation(Node* value) {
......
......@@ -117,6 +117,7 @@ class BasicBlock;
V(False, Boolean) \
V(FixedArrayMap, Map) \
V(FixedDoubleArrayMap, Map) \
V(WeakFixedArrayMap, Map) \
V(HeapNumberMap, Map) \
V(MinusOne, Number) \
V(NaN, Number) \
......@@ -269,6 +270,7 @@ class V8_EXPORT_PRIVATE GraphAssembler {
Node* BitcastWordToTaggedSigned(Node* value);
Node* BitcastTaggedToWord(Node* value);
Node* BitcastTaggedToWordForTagAndSmiBits(Node* value);
Node* BitcastMaybeObjectToWord(Node* value);
Node* TypeGuard(Type type, Node* value);
Node* Checkpoint(FrameState frame_state);
......
......@@ -142,6 +142,9 @@ DEFINE_GETTER(PropertyArrayMapConstant,
DEFINE_GETTER(FixedDoubleArrayMapConstant,
HeapConstant(factory()->fixed_double_array_map()))
DEFINE_GETTER(WeakFixedArrayMapConstant,
HeapConstant(factory()->weak_fixed_array_map()))
DEFINE_GETTER(HeapNumberMapConstant, HeapConstant(factory()->heap_number_map()))
DEFINE_GETTER(OptimizedOutConstant, HeapConstant(factory()->optimized_out()))
......
......@@ -90,6 +90,7 @@ class V8_EXPORT_PRIVATE JSGraph : public MachineGraph {
V(FixedArrayMapConstant) \
V(PropertyArrayMapConstant) \
V(FixedDoubleArrayMapConstant) \
V(WeakFixedArrayMapConstant) \
V(HeapNumberMapConstant) \
V(OptimizedOutConstant) \
V(StaleRegisterConstant) \
......
......@@ -408,6 +408,7 @@
V(CheckBounds) \
V(CheckIf) \
V(CheckMaps) \
V(DynamicCheckMaps) \
V(CheckNumber) \
V(CheckInternalizedString) \
V(CheckReceiver) \
......
......@@ -3600,6 +3600,11 @@ class RepresentationSelector {
node, UseInfo::CheckedHeapObjectAsTaggedPointer(p.feedback()),
MachineRepresentation::kNone);
}
case IrOpcode::kDynamicCheckMaps: {
return VisitUnop<T>(
node, UseInfo::CheckedHeapObjectAsTaggedPointer(FeedbackSource()),
MachineRepresentation::kNone);
}
case IrOpcode::kTransitionElementsKind: {
return VisitUnop<T>(
node, UseInfo::CheckedHeapObjectAsTaggedPointer(FeedbackSource()),
......
......@@ -287,6 +287,29 @@ CheckMapsParameters const& CheckMapsParametersOf(Operator const* op) {
return OpParameter<CheckMapsParameters>(op);
}
bool operator==(DynamicCheckMapsParameters const& lhs,
DynamicCheckMapsParameters const& rhs) {
return lhs.handler().address() == rhs.handler().address() &&
lhs.feedback() == rhs.feedback() && lhs.state() == rhs.state();
}
size_t hash_value(DynamicCheckMapsParameters const& p) {
FeedbackSource::Hash feedback_hash;
return base::hash_combine(p.handler().address(), feedback_hash(p.feedback()),
p.state());
}
std::ostream& operator<<(std::ostream& os,
DynamicCheckMapsParameters const& p) {
return os << p.handler() << ", " << p.feedback() << "," << p.state();
}
DynamicCheckMapsParameters const& DynamicCheckMapsParametersOf(
Operator const* op) {
DCHECK_EQ(IrOpcode::kDynamicCheckMaps, op->opcode());
return OpParameter<DynamicCheckMapsParameters>(op);
}
ZoneHandleSet<Map> const& CompareMapsParametersOf(Operator const* op) {
DCHECK_EQ(IrOpcode::kCompareMaps, op->opcode());
return OpParameter<ZoneHandleSet<Map>>(op);
......@@ -1444,6 +1467,18 @@ const Operator* SimplifiedOperatorBuilder::CheckMaps(
parameters); // parameter
}
const Operator* SimplifiedOperatorBuilder::DynamicCheckMaps(
Handle<Object> handler, const FeedbackSource& feedback,
DynamicCheckMapsParameters::ICState ic_state) {
DynamicCheckMapsParameters const parameters(handler, feedback, ic_state);
return zone()->New<Operator1<DynamicCheckMapsParameters>>( // --
IrOpcode::kDynamicCheckMaps, // opcode
Operator::kNoThrow | Operator::kNoWrite, // flags
"DynamicCheckMaps", // name
1, 1, 1, 0, 1, 0, // counts
parameters); // parameter
}
const Operator* SimplifiedOperatorBuilder::MapGuard(ZoneHandleSet<Map> maps) {
DCHECK_LT(0, maps.size());
return zone()->New<Operator1<ZoneHandleSet<Map>>>( // --
......
......@@ -427,6 +427,35 @@ std::ostream& operator<<(std::ostream&, CheckMapsParameters const&);
CheckMapsParameters const& CheckMapsParametersOf(Operator const*)
V8_WARN_UNUSED_RESULT;
// A descriptor for dynamic map checks.
class DynamicCheckMapsParameters final {
public:
enum ICState { kMonomorphic, kPolymorphic };
DynamicCheckMapsParameters(Handle<Object> handler,
const FeedbackSource& feedback, ICState state)
: handler_(handler), feedback_(feedback), state_(state) {}
Handle<Object> handler() const { return handler_; }
FeedbackSource const& feedback() const { return feedback_; }
ICState const& state() const { return state_; }
private:
Handle<Object> const handler_;
FeedbackSource const feedback_;
ICState const state_;
};
bool operator==(DynamicCheckMapsParameters const&,
DynamicCheckMapsParameters const&);
size_t hash_value(DynamicCheckMapsParameters const&);
std::ostream& operator<<(std::ostream&, DynamicCheckMapsParameters const&);
DynamicCheckMapsParameters const& DynamicCheckMapsParametersOf(Operator const*)
V8_WARN_UNUSED_RESULT;
ZoneHandleSet<Map> const& MapGuardMapsOf(Operator const*) V8_WARN_UNUSED_RESULT;
// Parameters for CompareMaps operator.
......@@ -837,6 +866,9 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
const Operator* CheckInternalizedString();
const Operator* CheckMaps(CheckMapsFlags, ZoneHandleSet<Map>,
const FeedbackSource& = FeedbackSource());
const Operator* DynamicCheckMaps(
Handle<Object> handler, const FeedbackSource& feedback,
DynamicCheckMapsParameters::ICState ic_state);
const Operator* CheckNotTaggedHole();
const Operator* CheckNumber(const FeedbackSource& feedback);
const Operator* CheckReceiver();
......
......@@ -92,6 +92,11 @@ class V8_EXPORT_PRIVATE TypeCache final {
// [0, FixedArray::kMaxLength].
Type const kFixedArrayLengthType = CreateRange(0.0, FixedArray::kMaxLength);
// The WeakFixedArray::length property always containts a smi in the range
// [0, WeakFixedArray::kMaxLength].
Type const kWeakFixedArrayLengthType =
CreateRange(0.0, WeakFixedArray::kMaxLength);
// The FixedDoubleArray::length property always containts a smi in the range
// [0, FixedDoubleArray::kMaxLength].
Type const kFixedDoubleArrayLengthType =
......
......@@ -2084,6 +2084,7 @@ Type Typer::Visitor::TypeCheckInternalizedString(Node* node) {
}
Type Typer::Visitor::TypeCheckMaps(Node* node) { UNREACHABLE(); }
Type Typer::Visitor::TypeDynamicCheckMaps(Node* node) { UNREACHABLE(); }
Type Typer::Visitor::TypeCompareMaps(Node* node) { return Type::Boolean(); }
......
......@@ -1434,6 +1434,10 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
CheckValueInputIs(node, 0, Type::Any());
CheckNotTyped(node);
break;
case IrOpcode::kDynamicCheckMaps:
CheckValueInputIs(node, 0, Type::Any());
CheckNotTyped(node);
break;
case IrOpcode::kCompareMaps:
CheckValueInputIs(node, 0, Type::Any());
CheckTypeIs(node, Type::Boolean());
......
......@@ -53,6 +53,8 @@ namespace internal {
V(OutOfBounds, "out of bounds") \
V(Overflow, "overflow") \
V(Smi, "Smi") \
V(TransitionedToMonomorphicIC, "IC transitioned to monomorphic") \
V(TransitionedToMegamorphicIC, "IC transitioned to megamorphic") \
V(Unknown, "(unknown)") \
V(ValueMismatch, "value mismatch") \
V(WrongCallTarget, "wrong call target") \
......@@ -60,6 +62,8 @@ namespace internal {
V(WrongFeedbackCell, "wrong feedback cell") \
V(WrongInstanceType, "wrong instance type") \
V(WrongMap, "wrong map") \
V(MissingMap, "missing map") \
V(WrongHandler, "wrong handler") \
V(WrongName, "wrong name") \
V(WrongValue, "wrong value") \
V(NoInitialElement, "no initial element")
......
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