Commit 44eb526e authored by Frank Emrich's avatar Frank Emrich Committed by Commit Bot

[dict-proto] CSA/Torque implementation of SwissNameDictionary, pt. 4

This CL adds:

a) FindEntry, Add, and Delete operations on SwissNameDictionary in
   Torque

b) Helpers needed for that on both the CSA and Torque side

Bug: v8:11330
Change-Id: I549b9039108217a34ec1e95af84eb28af7dfc9d3
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2778175Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Commit-Queue: Frank Emrich <emrich@google.com>
Cr-Commit-Position: refs/heads/master@{#73674}
parent 71f1f0d4
......@@ -1019,6 +1019,8 @@ ConstexprInt31Equal(
constexpr InstanceType, constexpr InstanceType): constexpr bool;
extern operator '-' macro ConstexprUint32Sub(
constexpr InstanceType, constexpr InstanceType): constexpr int32;
extern operator '-' macro ConstexprInt32Sub(
constexpr int32, constexpr int32): constexpr int32;
extern operator '.instanceType' macro LoadInstanceType(HeapObject):
InstanceType;
......@@ -1145,7 +1147,7 @@ extern macro BitcastWordToTaggedSigned(intptr): Smi;
extern macro BitcastWordToTaggedSigned(uintptr): Smi;
extern macro BitcastWordToTagged(intptr): Object;
extern macro BitcastWordToTagged(uintptr): Object;
extern macro BitcastTaggedToWord(Tagged): intptr;
extern macro BitcastTaggedToWord(Object): intptr;
extern macro BitcastTaggedToWordForTagAndSmiBits(Tagged): intptr;
extern macro FixedArrayMapConstant(): Map;
......@@ -1839,3 +1841,7 @@ extern macro FeedbackIteratorEntrySize(): intptr;
extern macro FeedbackIteratorHandlerOffset(): intptr;
extern operator '[]' macro LoadWeakFixedArrayElement(
WeakFixedArray, intptr): MaybeObject;
const kNoHashSentinel:
constexpr int32 generates 'PropertyArray::kNoHashSentinel';
extern macro LoadNameHash(Name): uint32;
......@@ -14810,17 +14810,16 @@ TNode<SwissNameDictionary> CodeStubAssembler::CopySwissNameDictionary(
IntPtrConstant(0));
// Offset to property details entry
TVARIABLE(IntPtrT, details_table_offset,
TVARIABLE(IntPtrT, details_table_offset_minus_tag,
property_details_start_offset_minus_tag);
TNode<IntPtrT> start = ctrl_table_start_offset_minus_tag;
VariableList in_loop_variables({&details_table_offset}, zone());
VariableList in_loop_variables({&details_table_offset_minus_tag}, zone());
BuildFastLoop<IntPtrT>(
in_loop_variables, start, IntPtrAdd(start, ctrl_table_size_bytes),
[&](TNode<IntPtrT> ctrl_table_offset) {
TNode<Uint8T> ctrl = UncheckedCast<Uint8T>(LoadFromObject(
MachineType::Uint8(), original, ctrl_table_offset));
TNode<Uint8T> ctrl = Load<Uint8T>(original, ctrl_table_offset);
// TODO(v8:11330) Entries in the PropertyDetails table may be
// uninitialized if the corresponding buckets in the data/ctrl table
......@@ -14835,17 +14834,18 @@ TNode<SwissNameDictionary> CodeStubAssembler::CopySwissNameDictionary(
// entries that we don't want to copy the PropertyDetails for.
GotoIf(IsSetWord32(ctrl, swiss_table::kNotFullMask), &done);
TNode<Uint8T> details = UncheckedCast<Uint8T>(LoadFromObject(
MachineType::Uint8(), original, details_table_offset.value()));
TNode<Uint8T> details =
Load<Uint8T>(original, details_table_offset_minus_tag.value());
StoreToObject(MachineRepresentation::kWord8, table,
details_table_offset.value(), details,
details_table_offset_minus_tag.value(), details,
StoreToObjectWriteBarrier::kNone);
Goto(&done);
BIND(&done);
details_table_offset = IntPtrAdd(details_table_offset.value(),
IntPtrConstant(kOneByteSize));
details_table_offset_minus_tag =
IntPtrAdd(details_table_offset_minus_tag.value(),
IntPtrConstant(kOneByteSize));
},
kOneByteSize, IndexAdvanceMode::kPost);
}
......@@ -14909,6 +14909,56 @@ void CodeStubAssembler::StoreSwissNameDictionaryCapacity(
table, SwissNameDictionary::CapacityOffset(), capacity);
}
TNode<Name> CodeStubAssembler::LoadSwissNameDictionaryKey(
TNode<SwissNameDictionary> dict, TNode<IntPtrT> entry) {
TNode<IntPtrT> offset_minus_tag = SwissNameDictionaryOffsetIntoDataTableMT(
dict, entry, SwissNameDictionary::kDataTableKeyEntryIndex);
// TODO(v8:11330) Consider using LoadObjectField here.
return CAST(Load<Object>(dict, offset_minus_tag));
}
TNode<Uint8T> CodeStubAssembler::LoadSwissNameDictionaryPropertyDetails(
TNode<SwissNameDictionary> table, TNode<IntPtrT> capacity,
TNode<IntPtrT> entry) {
TNode<IntPtrT> offset_minus_tag =
SwissNameDictionaryOffsetIntoPropertyDetailsTableMT(table, capacity,
entry);
// TODO(v8:11330) Consider using LoadObjectField here.
return Load<Uint8T>(table, offset_minus_tag);
}
void CodeStubAssembler::StoreSwissNameDictionaryPropertyDetails(
TNode<SwissNameDictionary> table, TNode<IntPtrT> capacity,
TNode<IntPtrT> entry, TNode<Uint8T> details) {
TNode<IntPtrT> offset_minus_tag =
SwissNameDictionaryOffsetIntoPropertyDetailsTableMT(table, capacity,
entry);
// TODO(v8:11330) Consider using StoreObjectField here.
StoreToObject(MachineRepresentation::kWord8, table, offset_minus_tag, details,
StoreToObjectWriteBarrier::kNone);
}
void CodeStubAssembler::StoreSwissNameDictionaryKeyAndValue(
TNode<SwissNameDictionary> dict, TNode<IntPtrT> entry, TNode<Object> key,
TNode<Object> value) {
STATIC_ASSERT(SwissNameDictionary::kDataTableKeyEntryIndex == 0);
STATIC_ASSERT(SwissNameDictionary::kDataTableValueEntryIndex == 1);
// TODO(v8:11330) Consider using StoreObjectField here.
TNode<IntPtrT> key_offset_minus_tag =
SwissNameDictionaryOffsetIntoDataTableMT(
dict, entry, SwissNameDictionary::kDataTableKeyEntryIndex);
StoreToObject(MachineRepresentation::kTagged, dict, key_offset_minus_tag, key,
StoreToObjectWriteBarrier::kFull);
TNode<IntPtrT> value_offset_minus_tag =
IntPtrAdd(key_offset_minus_tag, IntPtrConstant(kTaggedSize));
StoreToObject(MachineRepresentation::kTagged, dict, value_offset_minus_tag,
value, StoreToObjectWriteBarrier::kFull);
}
TNode<Uint64T> CodeStubAssembler::LoadSwissNameDictionaryCtrlTableGroup(
TNode<IntPtrT> address) {
TNode<RawPtrT> ptr = ReinterpretCast<RawPtrT>(address);
......@@ -14941,5 +14991,51 @@ TNode<Uint64T> CodeStubAssembler::LoadSwissNameDictionaryCtrlTableGroup(
return result;
#endif
}
void CodeStubAssembler::SwissNameDictionarySetCtrl(
TNode<SwissNameDictionary> table, TNode<IntPtrT> capacity,
TNode<IntPtrT> entry, TNode<Uint8T> ctrl) {
CSA_ASSERT(this,
WordEqual(capacity, ChangeUint32ToWord(
LoadSwissNameDictionaryCapacity(table))));
CSA_ASSERT(this, UintPtrLessThan(entry, capacity));
TNode<IntPtrT> one = IntPtrConstant(1);
TNode<IntPtrT> offset = SwissNameDictionaryCtrlTableStartOffsetMT(capacity);
CSA_ASSERT(this,
WordEqual(FieldSliceSwissNameDictionaryCtrlTable(table).offset,
IntPtrAdd(offset, one)));
TNode<IntPtrT> offset_entry = IntPtrAdd(offset, entry);
StoreToObject(MachineRepresentation::kWord8, table, offset_entry, ctrl,
StoreToObjectWriteBarrier::kNone);
TNode<IntPtrT> mask = IntPtrSub(capacity, one);
TNode<IntPtrT> group_width = IntPtrConstant(SwissNameDictionary::kGroupWidth);
// See SwissNameDictionary::SetCtrl for description of what's going on here.
// ((entry - Group::kWidth) & mask) + 1
TNode<IntPtrT> copy_entry_lhs =
IntPtrAdd(WordAnd(IntPtrSub(entry, group_width), mask), one);
// ((Group::kWidth - 1) & mask)
TNode<IntPtrT> copy_entry_rhs = WordAnd(IntPtrSub(group_width, one), mask);
TNode<IntPtrT> copy_entry = IntPtrAdd(copy_entry_lhs, copy_entry_rhs);
TNode<IntPtrT> offset_copy_entry = IntPtrAdd(offset, copy_entry);
// |entry| < |kGroupWidth| implies |copy_entry| == |capacity| + |entry|
CSA_ASSERT(this, Word32Or(UintPtrGreaterThanOrEqual(entry, group_width),
WordEqual(copy_entry, IntPtrAdd(capacity, entry))));
// |entry| >= |kGroupWidth| implies |copy_entry| == |entry|
CSA_ASSERT(this, Word32Or(UintPtrLessThan(entry, group_width),
WordEqual(copy_entry, entry)));
// TODO(v8:11330): consider using StoreObjectFieldNoWriteBarrier here.
StoreToObject(MachineRepresentation::kWord8, table, offset_copy_entry, ctrl,
StoreToObjectWriteBarrier::kNone);
}
} // namespace internal
} // namespace v8
......@@ -3766,8 +3766,31 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TNode<SwissNameDictionary> table, TNode<IntPtrT> capacity,
TNode<IntPtrT> enum_index, TNode<Int32T> entry);
TNode<Name> LoadSwissNameDictionaryKey(TNode<SwissNameDictionary> dict,
TNode<IntPtrT> entry);
void StoreSwissNameDictionaryKeyAndValue(TNode<SwissNameDictionary> dict,
TNode<IntPtrT> entry,
TNode<Object> key,
TNode<Object> value);
// Equivalent to SwissNameDictionary::SetCtrl, therefore preserves the copy of
// the first group at the end of the control table.
void SwissNameDictionarySetCtrl(TNode<SwissNameDictionary> table,
TNode<IntPtrT> capacity, TNode<IntPtrT> entry,
TNode<Uint8T> ctrl);
TNode<Uint64T> LoadSwissNameDictionaryCtrlTableGroup(TNode<IntPtrT> address);
TNode<Uint8T> LoadSwissNameDictionaryPropertyDetails(
TNode<SwissNameDictionary> table, TNode<IntPtrT> capacity,
TNode<IntPtrT> entry);
void StoreSwissNameDictionaryPropertyDetails(TNode<SwissNameDictionary> table,
TNode<IntPtrT> capacity,
TNode<IntPtrT> entry,
TNode<Uint8T> details);
TNode<SwissNameDictionary> CopySwissNameDictionary(
TNode<SwissNameDictionary> original);
......
......@@ -25,6 +25,47 @@ const kMax1ByteMetaTableCapacity: constexpr int32
const kMax2ByteMetaTableCapacity: constexpr int32
generates 'SwissNameDictionary::kMax2ByteMetaTableCapacity';
const kNotFoundSentinel:
constexpr int32 generates 'SwissNameDictionary::kNotFoundSentinel';
extern macro LoadSwissNameDictionaryNumberOfElements(
SwissNameDictionary, intptr): intptr;
extern macro LoadSwissNameDictionaryNumberOfDeletedElements(
SwissNameDictionary, intptr): intptr;
extern macro LoadSwissNameDictionaryKey(SwissNameDictionary, intptr): Name;
extern macro StoreSwissNameDictionaryKeyAndValue(
SwissNameDictionary, intptr, Object, Object);
extern macro SwissNameDictionarySetCtrl(
SwissNameDictionary, intptr, intptr, uint8);
extern macro StoreSwissNameDictionaryPropertyDetails(
SwissNameDictionary, intptr, intptr, uint8);
extern macro
SwissNameDictionaryIncreaseElementCountOrBailout(
ByteArray, intptr, uint32): uint32 labels Bailout;
extern macro
StoreSwissNameDictionaryEnumToEntryMapping(
SwissNameDictionary, intptr, intptr, int32);
extern macro
SwissNameDictionaryUpdateCountsForDeletion(ByteArray, intptr): uint32;
namespace runtime {
extern runtime SwissTableFindEntry(NoContext, SwissNameDictionary, Name): Smi;
extern runtime SwissTableAdd(
NoContext, SwissNameDictionary, Name, Object, Smi): SwissNameDictionary;
extern runtime ShrinkSwissNameDictionary(
NoContext, SwissNameDictionary): SwissNameDictionary;
}
// Counterpart for SwissNameDictionary::CapacityFor in C++.
@export
macro SwissNameDictionaryCapacityFor(atLeastSpaceFor: intptr): intptr {
......@@ -108,4 +149,192 @@ macro SwissNameDictionaryCtrlTableStartOffsetMT(capacity: intptr): intptr {
return kDataTableStartOffsetMT +
kDataTableEntryCount * FromConstexpr<intptr>(kTaggedSize) * capacity;
}
macro Probe(hash: uint32, mask: uint32): ProbeSequence {
// Mask must be a power of 2 minus 1.
assert(((mask + 1) & mask) == 0);
return ProbeSequence{mask: mask, offset: H1(hash) & mask, index: 0};
}
macro FindEntry<GroupLoader: type>(
table: SwissNameDictionary, key: Name): never labels
Found(intptr), NotFound {
const hash: uint32 = LoadNameHash(key);
const capacity: int32 = table.capacity;
const nonZeroCapacity: int32 = capacity | Convert<int32>(capacity == 0);
const mask: uint32 = Unsigned(nonZeroCapacity - 1);
const ctrlTableStart: intptr =
SwissNameDictionaryCtrlTableStartOffsetMT(Convert<intptr>(capacity)) +
BitcastTaggedToWord(table);
let seq = Probe(hash, mask);
while (true) {
const group =
GroupLoader{}.LoadGroup(ctrlTableStart + Convert<intptr>(seq.offset));
let match = group.Match(H2(hash));
while (match.HasBitsSet()) {
const inGroupIndex = match.LowestBitSet();
const candidateEntry = Convert<intptr>(seq.Offset(inGroupIndex));
const candidateKey: Object =
LoadSwissNameDictionaryKey(table, candidateEntry);
if (TaggedEqual(key, candidateKey)) {
goto Found(candidateEntry);
}
match.ClearLowestSetBit();
}
if (group.MatchEmpty().HasBitsSet()) {
goto NotFound;
}
seq.Next();
}
unreachable;
}
macro FindFirstEmpty<GroupLoader: type>(
table: SwissNameDictionary, capacity: intptr, hash: uint32): int32 {
const nonZeroCapacity: int32 =
Convert<int32>(capacity) | Convert<int32>(capacity == 0);
const mask: uint32 = Unsigned(nonZeroCapacity - 1);
const ctrlTableStart: intptr =
SwissNameDictionaryCtrlTableStartOffsetMT(capacity) +
BitcastTaggedToWord(table);
let seq = Probe(hash, mask);
while (true) {
const group =
GroupLoader{}.LoadGroup(ctrlTableStart + Convert<intptr>(seq.offset));
const match = group.MatchEmpty();
if (match.HasBitsSet()) {
const inGroupIndex = match.LowestBitSet();
return Signed(seq.Offset(inGroupIndex));
}
seq.Next();
}
unreachable;
}
macro Add<GroupLoader: type>(
table: SwissNameDictionary, key: Name, value: Object,
propertyDetails: uint8)
labels Bailout {
const capacity: intptr = Convert<intptr>(table.capacity);
const maxUsable: uint32 =
Unsigned(Convert<int32>(SwissNameDictionaryMaxUsableCapacity(capacity)));
try {
// We read the used capacity (present + deleted elements), compare it
// against the max usable capacity to determine if a bailout is necessary,
// and in case of no bailout increase the present element count all in one
// go using the following macro. This way we don't have to do the branching
// needed for meta table accesses multiple times.
const used: uint32 = SwissNameDictionaryIncreaseElementCountOrBailout(
table.meta_table, capacity, maxUsable) otherwise Bailout;
const hash: uint32 = LoadNameHash(key);
const newEntry32 = FindFirstEmpty<GroupLoader>(table, capacity, hash);
const newEntry = Convert<intptr>(newEntry32);
StoreSwissNameDictionaryKeyAndValue(table, newEntry, key, value);
StoreSwissNameDictionaryEnumToEntryMapping(
table, capacity, Convert<intptr>(used), newEntry32);
const h2 = Convert<uint8>(Convert<intptr>(H2(hash)));
SwissNameDictionarySetCtrl(table, capacity, newEntry, h2);
StoreSwissNameDictionaryPropertyDetails(
table, capacity, newEntry, propertyDetails);
} label Bailout {
goto Bailout;
}
}
@export
macro SwissNameDictionaryDelete(table: SwissNameDictionary, entry: intptr)
labels
Shrunk(SwissNameDictionary) {
const capacity = Convert<intptr>(table.capacity);
// Update present and deleted element counts at once, without needing to do
// the meta table access related branching more than once.
const newElementCount =
SwissNameDictionaryUpdateCountsForDeletion(table.meta_table, capacity);
StoreSwissNameDictionaryKeyAndValue(table, entry, TheHole, TheHole);
const kDeleted = FromConstexpr<uint8>(ctrl::kDeleted);
SwissNameDictionarySetCtrl(table, capacity, entry, kDeleted);
// Same logic for deciding when to shrink as in SwissNameDictionary::Delete.
if (Convert<intptr>(Signed(newElementCount)) < (capacity >> 2)) {
const shrunkTable = runtime::ShrinkSwissNameDictionary(kNoContext, table);
goto Shrunk(shrunkTable);
}
}
@export
macro SwissNameDictionaryFindEntry(table: SwissNameDictionary, key: Name):
never labels Found(intptr), NotFound {
try {
if (kUseSIMD) {
// TODO(v8:11330) Not implemented in Torque, yet, doing runtime call
// instead.
const res = runtime::SwissTableFindEntry(kNoContext, table, key);
if (res == kNotFoundSentinel) {
goto NotFound;
} else {
goto Found(Convert<intptr>(res));
}
} else {
FindEntry<GroupPortableLoader>(table, key)
otherwise Found, NotFound;
}
} label Found(index: intptr) {
goto Found(index);
} label NotFound {
goto NotFound;
}
}
@export
macro SwissNameDictionaryAdd(
table: SwissNameDictionary, key: Name, value: Object,
propertyDetails: uint8) labels Bailout {
try {
if (kUseSIMD) {
// TODO(v8:11330) Not implemented in Torque, yet, doing runtime call
// instead. However, must bailout if the runtime call would allocate a new
// dictionary.
// Determine if bailout needed:
const capacity = Convert<intptr>(table.capacity);
const maxUsable = SwissNameDictionaryMaxUsableCapacity(capacity);
// Doing two independent accesses to the meta table here (repeating the
// branching), rather than combining the accesses. Accepting that due to
// the fact that this is a slow placeholder until the SIMD version
// replaces it.
const nof = LoadSwissNameDictionaryNumberOfElements(table, capacity);
const nod =
LoadSwissNameDictionaryNumberOfDeletedElements(table, capacity);
const used = nof + nod;
if (used >= maxUsable) {
goto Bailout;
}
runtime::SwissTableAdd(
kNoContext, table, key, value,
Convert<Smi>(Convert<int32>(propertyDetails)));
} else {
Add<GroupPortableLoader>(table, key, value, propertyDetails)
otherwise Bailout;
}
} label Bailout {
goto Bailout;
}
}
}
......@@ -825,10 +825,17 @@ RUNTIME_FUNCTION(Runtime_ShrinkPropertyDictionary) {
NameDictionary::Shrink(isolate, dictionary);
receiver->SetProperties(*new_properties);
}
return Smi::zero();
}
RUNTIME_FUNCTION(Runtime_ShrinkSwissNameDictionary) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED(SwissNameDictionary, dictionary, 0);
return *SwissNameDictionary::Shrink(isolate, dictionary);
}
// ES6 section 12.9.3, operator in.
RUNTIME_FUNCTION(Runtime_HasProperty) {
HandleScope scope(isolate);
......
......@@ -336,6 +336,7 @@ namespace internal {
F(SetOwnPropertyIgnoreAttributes, 4, 1) \
F(StoreDataPropertyInLiteral, 3, 1) \
F(ShrinkPropertyDictionary, 1, 1) \
F(ShrinkSwissNameDictionary, 1, 1) \
F(ToFastProperties, 1, 1) \
I(ToLength, 1, 1) \
F(ToName, 1, 1) \
......
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