v8/v8
0

[turboshaft] Port operations from ECL to MachineLoweringReducer (26)

This CL ports operations from Turbofan's EffectControlLinearizer to
Turboshaft's MachineLoweringReducer:
 - FindOrderedHashMapEntry
 - FindOrderedHashMapEntryForInt32Key
 - FindOrderedHashSetEntry
 - TransitionAndStoreElement
 - TransitionAndStoreNumberElement
 - TransitionAndStoreNonNumberElement

Bug: v8:12783
Change-Id: Ic7c051ac879ac8b71615b173f0175c05a36e3aa4
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4414805
Commit-Queue: Nico Hartmann <nicohartmann@chromium.org>
Reviewed-by: Darius Mercadier <dmercadier@chromium.org>
Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/main@{#87065}
This commit is contained in:
Nico Hartmann
2023-04-12 14:42:03 +02:00
committed by V8 LUCI CQ
parent 2d4c2f3d42
commit 03594553f1
12 changed files with 578 additions and 72 deletions

@ -3,3 +3,4 @@
BasedOnStyle: Google
DerivePointerAlignment: false
MaxEmptyLinesToKeep: 1
IfMacros: ['IF', 'IF_NOT', 'ELSE', 'ELSE_IF']

@ -1855,21 +1855,27 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
LowerStoreSignedSmallElement(node);
break;
case IrOpcode::kFindOrderedHashMapEntry:
if (v8_flags.turboshaft) return false;
result = LowerFindOrderedHashMapEntry(node);
break;
case IrOpcode::kFindOrderedHashMapEntryForInt32Key:
if (v8_flags.turboshaft) return false;
result = LowerFindOrderedHashMapEntryForInt32Key(node);
break;
case IrOpcode::kFindOrderedHashSetEntry:
if (v8_flags.turboshaft) return false;
result = LowerFindOrderedHashSetEntry(node);
break;
case IrOpcode::kTransitionAndStoreNumberElement:
if (v8_flags.turboshaft) return false;
LowerTransitionAndStoreNumberElement(node);
break;
case IrOpcode::kTransitionAndStoreNonNumberElement:
if (v8_flags.turboshaft) return false;
LowerTransitionAndStoreNonNumberElement(node);
break;
case IrOpcode::kTransitionAndStoreElement:
if (v8_flags.turboshaft) return false;
LowerTransitionAndStoreElement(node);
break;
case IrOpcode::kRuntimeAbort:
@ -7446,6 +7452,7 @@ Node* EffectControlLinearizer::IsElementsKindGreaterThan(
}
void EffectControlLinearizer::LowerTransitionAndStoreElement(Node* node) {
DCHECK(!v8_flags.turboshaft);
Node* array = node->InputAt(0);
Node* index = node->InputAt(1);
Node* value = node->InputAt(2);
@ -7587,6 +7594,7 @@ void EffectControlLinearizer::LowerTransitionAndStoreElement(Node* node) {
}
void EffectControlLinearizer::LowerTransitionAndStoreNumberElement(Node* node) {
DCHECK(!v8_flags.turboshaft);
Node* array = node->InputAt(0);
Node* index = node->InputAt(1);
Node* value = node->InputAt(2); // This is a Float64, not tagged.
@ -7647,6 +7655,7 @@ void EffectControlLinearizer::LowerTransitionAndStoreNumberElement(Node* node) {
void EffectControlLinearizer::LowerTransitionAndStoreNonNumberElement(
Node* node) {
DCHECK(!v8_flags.turboshaft);
Node* array = node->InputAt(0);
Node* index = node->InputAt(1);
Node* value = node->InputAt(2);
@ -8272,6 +8281,7 @@ Maybe<Node*> EffectControlLinearizer::LowerFloat64RoundTruncate(Node* node) {
}
Node* EffectControlLinearizer::LowerFindOrderedHashMapEntry(Node* node) {
DCHECK(!v8_flags.turboshaft);
Node* table = NodeProperties::GetValueInput(node, 0);
Node* key = NodeProperties::GetValueInput(node, 1);
@ -8289,6 +8299,7 @@ Node* EffectControlLinearizer::LowerFindOrderedHashMapEntry(Node* node) {
}
Node* EffectControlLinearizer::LowerFindOrderedHashSetEntry(Node* node) {
DCHECK(!v8_flags.turboshaft);
Node* table = NodeProperties::GetValueInput(node, 0);
Node* key = NodeProperties::GetValueInput(node, 1);
@ -8320,6 +8331,7 @@ Node* EffectControlLinearizer::ComputeUnseededHash(Node* value) {
Node* EffectControlLinearizer::LowerFindOrderedHashMapEntryForInt32Key(
Node* node) {
DCHECK(!v8_flags.turboshaft);
Node* table = NodeProperties::GetValueInput(node, 0);
Node* key = NodeProperties::GetValueInput(node, 1);

@ -636,6 +636,7 @@ class AssemblerOpInterface {
DECL_MULTI_REP_BINOP(WordMul, WordBinop, WordRepresentation, Mul)
DECL_SINGLE_REP_BINOP_V(Word32Mul, WordBinop, Mul, Word32)
DECL_SINGLE_REP_BINOP_V(Word64Mul, WordBinop, Mul, Word64)
DECL_SINGLE_REP_BINOP_V(WordPtrMul, WordBinop, Mul, WordPtr)
DECL_MULTI_REP_BINOP(WordBitwiseAnd, WordBinop, WordRepresentation,
BitwiseAnd)
@ -1874,6 +1875,18 @@ class AssemblerOpInterface {
typename BuiltinCallDescriptor::CopyFastSmiOrObjectElements>(
isolate, context, {object});
}
V<Smi> CallBuiltin_FindOrderedHashMapEntry(Isolate* isolate,
V<Context> context,
V<Object> table, V<Smi> key) {
return CallBuiltin<typename BuiltinCallDescriptor::FindOrderedHashMapEntry>(
isolate, context, {table, key});
}
V<Smi> CallBuiltin_FindOrderedHashSetEntry(Isolate* isolate,
V<Context> context, V<Object> set,
V<Smi> key) {
return CallBuiltin<typename BuiltinCallDescriptor::FindOrderedHashSetEntry>(
isolate, context, {set, key});
}
V<Object> CallBuiltin_GrowFastDoubleElements(Isolate* isolate,
V<Context> context,
V<Object> object, V<Smi> size) {
@ -2570,12 +2583,22 @@ class AssemblerOpInterface {
is_little_endian, element_type);
}
void StoreSignedSmallElement(V<Object> array, V<WordPtr> index,
V<Word32> value) {
void TransitionAndStoreArrayElement(
V<Object> array, V<WordPtr> index, OpIndex value,
TransitionAndStoreArrayElementOp::Kind kind, Handle<Map> fast_map,
Handle<Map> double_map) {
if (V8_UNLIKELY(stack().generating_unreachable_operations())) {
return;
}
stack().ReduceStoreSignedSmallElement(array, index, value);
stack().ReduceTransitionAndStoreArrayElement(array, index, value, kind,
fast_map, double_map);
}
void StoreSignedSmallElement(V<Object> array, V<WordPtr> index,
V<Word32> value) {
TransitionAndStoreArrayElement(
array, index, value,
TransitionAndStoreArrayElementOp::Kind::kSignedSmallElement, {}, {});
}
V<Word32> CompareMaps(V<HeapObject> heap_object,
@ -2683,6 +2706,28 @@ class AssemblerOpInterface {
stack().ReduceTransitionElementsKind(object, transition);
}
OpIndex FindOrderedHashEntry(V<Object> data_structure, OpIndex key,
FindOrderedHashEntryOp::Kind kind) {
if (V8_UNLIKELY(stack().generating_unreachable_operations())) {
return OpIndex::Invalid();
}
return stack().ReduceFindOrderedHashEntry(data_structure, key, kind);
}
V<Smi> FindOrderedHashMapEntry(V<Object> table, V<Smi> key) {
return FindOrderedHashEntry(
table, key, FindOrderedHashEntryOp::Kind::kFindOrderedHashMapEntry);
}
V<Smi> FindOrderedHashSetEntry(V<Object> table, V<Smi> key) {
return FindOrderedHashEntry(
table, key, FindOrderedHashEntryOp::Kind::kFindOrderedHashSetEntry);
}
V<WordPtr> FindOrderedHashMapEntryForInt32Key(V<Object> table,
V<Word32> key) {
return FindOrderedHashEntry(
table, key,
FindOrderedHashEntryOp::Kind::kFindOrderedHashMapEntryForInt32Key);
}
template <typename Rep>
V<Rep> resolve(const V<Rep>& v) {
return v;
@ -2769,7 +2814,8 @@ class AssemblerOpInterface {
if (!stack().Bind(else_block)) return false;
Block* then_block = stack().NewBlock();
info.else_block = stack().NewBlock();
stack().Branch(condition_builder(), then_block, info.else_block);
stack().Branch(ConditionWithHint{condition_builder()}, then_block,
info.else_block);
return stack().Bind(then_block);
}

@ -95,6 +95,21 @@ struct BuiltinCallDescriptor {
static constexpr Operator::Properties Properties = Operator::kEliminatable;
};
template <Builtin B>
struct FindOrderedHashEntry : public Descriptor<FindOrderedHashEntry<B>> {
static constexpr auto Function = B;
using arguments_t = std::tuple<V<Object>, V<Smi>>;
using result_t = V<Smi>;
static constexpr bool NeedsFrameState = false;
static constexpr bool NeedsContext = true;
static constexpr Operator::Properties Properties = Operator::kEliminatable;
};
using FindOrderedHashMapEntry =
FindOrderedHashEntry<Builtin::kFindOrderedHashMapEntry>;
using FindOrderedHashSetEntry =
FindOrderedHashEntry<Builtin::kFindOrderedHashSetEntry>;
template <Builtin B>
struct GrowFastElements : public Descriptor<GrowFastElements<B>> {
static constexpr auto Function = B;

@ -1851,6 +1851,30 @@ OpIndex GraphBuilder::Process(
Map(node->InputAt(4)),
ExternalArrayTypeOf(node->op()));
return OpIndex::Invalid();
case IrOpcode::kTransitionAndStoreElement:
__ TransitionAndStoreArrayElement(
Map(node->InputAt(0)), Map(node->InputAt(1)), Map(node->InputAt(2)),
TransitionAndStoreArrayElementOp::Kind::kElement,
FastMapParameterOf(node->op()).object(),
DoubleMapParameterOf(node->op()).object());
return OpIndex::Invalid();
case IrOpcode::kTransitionAndStoreNumberElement:
__ TransitionAndStoreArrayElement(
Map(node->InputAt(0)), Map(node->InputAt(1)), Map(node->InputAt(2)),
TransitionAndStoreArrayElementOp::Kind::kNumberElement, {},
DoubleMapParameterOf(node->op()).object());
return OpIndex::Invalid();
case IrOpcode::kTransitionAndStoreNonNumberElement: {
auto kind =
ValueTypeParameterOf(node->op())
.Is(compiler::Type::BooleanOrNullOrUndefined())
? TransitionAndStoreArrayElementOp::Kind::kOddballElement
: TransitionAndStoreArrayElementOp::Kind::kNonNumberElement;
__ TransitionAndStoreArrayElement(
Map(node->InputAt(0)), Map(node->InputAt(1)), Map(node->InputAt(2)),
kind, FastMapParameterOf(node->op()).object(), {});
return OpIndex::Invalid();
}
case IrOpcode::kStoreSignedSmallElement:
__ StoreSignedSmallElement(Map(node->InputAt(0)), Map(node->InputAt(1)),
Map(node->InputAt(2)));
@ -2070,6 +2094,16 @@ OpIndex GraphBuilder::Process(
return OpIndex::Invalid();
}
case IrOpcode::kFindOrderedHashMapEntry:
return __ FindOrderedHashMapEntry(Map(node->InputAt(0)),
Map(node->InputAt(1)));
case IrOpcode::kFindOrderedHashSetEntry:
return __ FindOrderedHashSetEntry(Map(node->InputAt(0)),
Map(node->InputAt(1)));
case IrOpcode::kFindOrderedHashMapEntryForInt32Key:
return __ FindOrderedHashMapEntryForInt32Key(Map(node->InputAt(0)),
Map(node->InputAt(1)));
case IrOpcode::kBeginRegion:
return OpIndex::Invalid();
case IrOpcode::kFinishRegion:

@ -2028,50 +2028,244 @@ class MachineLoweringReducer : public Next {
return {};
}
OpIndex REDUCE(StoreSignedSmallElement)(V<JSArray> array, V<WordPtr> index,
V<Word32> value) {
// Store a signed small in an output array.
//
// kind = ElementsKind(array)
//
// -- STORE PHASE ----------------------
// if kind == HOLEY_DOUBLE_ELEMENTS {
// float_value = convert int32 to float
// Store array[index] = float_value
// } else {
// // kind is HOLEY_SMI_ELEMENTS or HOLEY_ELEMENTS
// smi_value = convert int32 to smi
// Store array[index] = smi_value
// }
//
OpIndex REDUCE(TransitionAndStoreArrayElement)(
V<JSArray> array, V<WordPtr> index, OpIndex value,
TransitionAndStoreArrayElementOp::Kind kind, Handle<Map> fast_map,
Handle<Map> double_map) {
V<Map> map = __ LoadMapField(array);
V<Word32> bitfield2 =
__ template LoadField<Word32>(map, AccessBuilder::ForMapBitField2());
V<Word32> kind = __ Word32ShiftRightLogical(
V<Word32> elements_kind = __ Word32ShiftRightLogical(
__ Word32BitwiseAnd(bitfield2, Map::Bits2::ElementsKindBits::kMask),
Map::Bits2::ElementsKindBits::kShift);
V<Object> elements = __ template LoadField<Object>(
array, AccessBuilder::ForJSObjectElements());
IF(__ Int32LessThan(HOLEY_ELEMENTS, kind)) {
// Our ElementsKind is HOLEY_DOUBLE_ELEMENTS.
V<Float64> f64 = __ ChangeInt32ToFloat64(value);
__ StoreElement(elements, AccessBuilder::ForFixedDoubleArrayElement(),
index, f64);
}
ELSE {
// Our ElementsKind is HOLEY_SMI_ELEMENTS or HOLEY_ELEMENTS.
// In this case, we know our value is a signed small, and we can optimize
// the ElementAccess information.
ElementAccess access = AccessBuilder::ForFixedArrayElement();
access.type = compiler::Type::SignedSmall();
access.machine_type = MachineType::TaggedSigned();
access.write_barrier_kind = kNoWriteBarrier;
__ StoreElement(elements, access, index, __ SmiTag(value));
}
END_IF
switch (kind) {
case TransitionAndStoreArrayElementOp::Kind::kElement: {
// Possibly transition array based on input and store.
//
// -- TRANSITION PHASE -----------------
// kind = ElementsKind(array)
// if value is not smi {
// if kind == HOLEY_SMI_ELEMENTS {
// if value is heap number {
// Transition array to HOLEY_DOUBLE_ELEMENTS
// kind = HOLEY_DOUBLE_ELEMENTS
// } else {
// Transition array to HOLEY_ELEMENTS
// kind = HOLEY_ELEMENTS
// }
// } else if kind == HOLEY_DOUBLE_ELEMENTS {
// if value is not heap number {
// Transition array to HOLEY_ELEMENTS
// kind = HOLEY_ELEMENTS
// }
// }
// }
//
// -- STORE PHASE ----------------------
// [make sure {kind} is up-to-date]
// if kind == HOLEY_DOUBLE_ELEMENTS {
// if value is smi {
// float_value = convert smi to float
// Store array[index] = float_value
// } else {
// float_value = value
// Store array[index] = float_value
// }
// } else {
// // kind is HOLEY_SMI_ELEMENTS or HOLEY_ELEMENTS
// Store array[index] = value
// }
//
Label<Word32> do_store(this);
// We can store a smi anywhere.
GOTO_IF(__ ObjectIsSmi(value), do_store, elements_kind);
return {};
// {value} is a HeapObject.
IF_NOT (LIKELY(__ Int32LessThan(HOLEY_SMI_ELEMENTS, elements_kind))) {
// Transition {array} from HOLEY_SMI_ELEMENTS to HOLEY_DOUBLE_ELEMENTS
// or to HOLEY_ELEMENTS.
V<Map> value_map = __ LoadMapField(value);
IF (__ TaggedEqual(value_map,
__ HeapConstant(factory_->heap_number_map()))) {
// {value} is a HeapNumber.
TransitionElementsTo(array, HOLEY_SMI_ELEMENTS,
HOLEY_DOUBLE_ELEMENTS, double_map);
GOTO(do_store, HOLEY_DOUBLE_ELEMENTS);
}
ELSE {
TransitionElementsTo(array, HOLEY_SMI_ELEMENTS, HOLEY_ELEMENTS,
fast_map);
GOTO(do_store, HOLEY_ELEMENTS);
}
END_IF
}
END_IF
GOTO_IF_NOT(LIKELY(__ Int32LessThan(HOLEY_ELEMENTS, elements_kind)),
do_store, elements_kind);
// We have double elements kind. Only a HeapNumber can be stored
// without effecting a transition.
V<Map> value_map = __ LoadMapField(value);
IF_NOT (UNLIKELY(__ TaggedEqual(
value_map, __ HeapConstant(factory_->heap_number_map())))) {
TransitionElementsTo(array, HOLEY_DOUBLE_ELEMENTS, HOLEY_ELEMENTS,
fast_map);
GOTO(do_store, HOLEY_ELEMENTS);
}
END_IF
GOTO(do_store, elements_kind);
BIND(do_store, store_kind);
V<Object> elements = __ template LoadField<Object>(
array, AccessBuilder::ForJSObjectElements());
IF (__ Int32LessThan(HOLEY_ELEMENTS, store_kind)) {
// Our ElementsKind is HOLEY_DOUBLE_ELEMENTS.
IF (__ ObjectIsSmi(value)) {
V<Float64> float_value =
__ ChangeInt32ToFloat64(__ SmiUntag(value));
__ StoreElement(elements,
AccessBuilder::ForFixedDoubleArrayElement(), index,
float_value);
}
ELSE {
V<Float64> float_value = __ template LoadField<Float64>(
V<HeapObject>::Cast(value),
AccessBuilder::ForHeapNumberValue());
__ StoreElement(elements,
AccessBuilder::ForFixedDoubleArrayElement(), index,
__ Float64SilenceNaN(float_value));
}
END_IF
}
ELSE {
// Our ElementsKind is HOLEY_SMI_ELEMENTS or HOLEY_ELEMENTS.
__ StoreElement(elements,
AccessBuilder::ForFixedArrayElement(HOLEY_ELEMENTS),
index, value);
}
END_IF
break;
}
case TransitionAndStoreArrayElementOp::Kind::kNumberElement: {
// Possibly transition array based on input and store.
//
// -- TRANSITION PHASE -----------------
// kind = ElementsKind(array)
// if kind == HOLEY_SMI_ELEMENTS {
// Transition array to HOLEY_DOUBLE_ELEMENTS
// } else if kind != HOLEY_DOUBLE_ELEMENTS {
// This is UNREACHABLE, execute a debug break.
// }
//
// -- STORE PHASE ----------------------
// Store array[index] = value (it's a float)
//
// {value} is a float64.
IF_NOT (LIKELY(__ Int32LessThan(HOLEY_SMI_ELEMENTS, elements_kind))) {
// Transition {array} from HOLEY_SMI_ELEMENTS to
// HOLEY_DOUBLE_ELEMENTS.
TransitionElementsTo(array, HOLEY_SMI_ELEMENTS, HOLEY_DOUBLE_ELEMENTS,
double_map);
}
ELSE {
// We expect that our input array started at HOLEY_SMI_ELEMENTS, and
// climbs the lattice up to HOLEY_DOUBLE_ELEMENTS. Force a debug break
// if this assumption is broken. It also would be the case that
// loop peeling can break this assumption.
IF_NOT (LIKELY(
__ Word32Equal(elements_kind, HOLEY_DOUBLE_ELEMENTS))) {
__ Unreachable();
}
END_IF
}
END_IF
V<Object> elements = __ template LoadField<Object>(
array, AccessBuilder::ForJSObjectElements());
__ StoreElement(elements, AccessBuilder::ForFixedDoubleArrayElement(),
index, __ Float64SilenceNaN(value));
break;
}
case TransitionAndStoreArrayElementOp::Kind::kOddballElement:
case TransitionAndStoreArrayElementOp::Kind::kNonNumberElement: {
// Possibly transition array based on input and store.
//
// -- TRANSITION PHASE -----------------
// kind = ElementsKind(array)
// if kind == HOLEY_SMI_ELEMENTS {
// Transition array to HOLEY_ELEMENTS
// } else if kind == HOLEY_DOUBLE_ELEMENTS {
// Transition array to HOLEY_ELEMENTS
// }
//
// -- STORE PHASE ----------------------
// // kind is HOLEY_ELEMENTS
// Store array[index] = value
//
IF_NOT (LIKELY(__ Int32LessThan(HOLEY_SMI_ELEMENTS, elements_kind))) {
// Transition {array} from HOLEY_SMI_ELEMENTS to HOLEY_ELEMENTS.
TransitionElementsTo(array, HOLEY_SMI_ELEMENTS, HOLEY_ELEMENTS,
fast_map);
}
ELSE_IF (UNLIKELY(__ Int32LessThan(HOLEY_ELEMENTS, elements_kind))) {
TransitionElementsTo(array, HOLEY_DOUBLE_ELEMENTS, HOLEY_ELEMENTS,
fast_map);
}
END_IF
V<Object> elements = __ template LoadField<Object>(
array, AccessBuilder::ForJSObjectElements());
ElementAccess access =
AccessBuilder::ForFixedArrayElement(HOLEY_ELEMENTS);
if (kind == TransitionAndStoreArrayElementOp::Kind::kOddballElement) {
access.type = compiler::Type::BooleanOrNullOrUndefined();
access.write_barrier_kind = kNoWriteBarrier;
}
__ StoreElement(elements, access, index, value);
break;
}
case TransitionAndStoreArrayElementOp::Kind::kSignedSmallElement: {
// Store a signed small in an output array.
//
// kind = ElementsKind(array)
//
// -- STORE PHASE ----------------------
// if kind == HOLEY_DOUBLE_ELEMENTS {
// float_value = convert int32 to float
// Store array[index] = float_value
// } else {
// // kind is HOLEY_SMI_ELEMENTS or HOLEY_ELEMENTS
// smi_value = convert int32 to smi
// Store array[index] = smi_value
// }
//
V<Object> elements = __ template LoadField<Object>(
array, AccessBuilder::ForJSObjectElements());
IF (__ Int32LessThan(HOLEY_ELEMENTS, elements_kind)) {
// Our ElementsKind is HOLEY_DOUBLE_ELEMENTS.
V<Float64> f64 = __ ChangeInt32ToFloat64(value);
__ StoreElement(elements, AccessBuilder::ForFixedDoubleArrayElement(),
index, f64);
}
ELSE {
// Our ElementsKind is HOLEY_SMI_ELEMENTS or HOLEY_ELEMENTS.
// In this case, we know our value is a signed small, and we can
// optimize the ElementAccess information.
ElementAccess access = AccessBuilder::ForFixedArrayElement();
access.type = compiler::Type::SignedSmall();
access.machine_type = MachineType::TaggedSigned();
access.write_barrier_kind = kNoWriteBarrier;
__ StoreElement(elements, access, index, __ SmiTag(value));
}
END_IF
break;
}
}
return OpIndex::Invalid();
}
V<Word32> REDUCE(CompareMaps)(V<HeapObject> heap_object,
@ -2553,24 +2747,78 @@ class MachineLoweringReducer : public Next {
return OpIndex::Invalid();
}
// TODO(nicohartmann@): Remove this once ECL has been fully ported.
// ECL: ChangeInt64ToSmi(input) ==> MLR: __ SmiTag(input)
// ECL: ChangeInt32ToSmi(input) ==> MLR: __ SmiTag(input)
// ECL: ChangeUint32ToSmi(input) ==> MLR: __ SmiTag(input)
// ECL: ChangeUint64ToSmi(input) ==> MLR: __ SmiTag(input)
// ECL: ChangeIntPtrToSmi(input) ==> MLR: __ SmiTag(input)
// ECL: ChangeFloat64ToTagged(i, m) ==> MLR: __ ConvertFloat64ToNumber(i, m)
// ECL: ChangeSmiToIntPtr(input)
// ==> MLR: __ ChangeInt32ToIntPtr(__ SmiUntag(input))
// ECL: ChangeSmiToInt32(input) ==> MLR: __ SmiUntag(input)
// ECL: ChangeSmiToInt64(input) ==> MLR: __ ChangeInt32ToInt64(__
// SmiUntag(input))
// ECL: BuildCheckedHeapNumberOrOddballToFloat64 ==> MLR:
// ConvertHeapObjectToFloat64OrDeopt
OpIndex REDUCE(FindOrderedHashEntry)(V<Object> data_structure, OpIndex key,
FindOrderedHashEntryOp::Kind kind) {
switch (kind) {
case FindOrderedHashEntryOp::Kind::kFindOrderedHashMapEntry:
return __ CallBuiltin_FindOrderedHashMapEntry(
isolate_, __ NoContextConstant(), data_structure, key);
case FindOrderedHashEntryOp::Kind::kFindOrderedHashMapEntryForInt32Key: {
// Compute the integer hash code.
V<WordPtr> hash = __ ChangeUint32ToUintPtr(ComputeUnseededHash(key));
V<WordPtr> number_of_buckets =
__ ChangeInt32ToIntPtr(__ SmiUntag(__ template LoadField<Smi>(
data_structure,
AccessBuilder::ForOrderedHashMapOrSetNumberOfBuckets())));
hash = __ WordPtrBitwiseAnd(hash, __ WordPtrSub(number_of_buckets, 1));
V<WordPtr> first_entry = __ ChangeInt32ToIntPtr(__ SmiUntag(__ Load(
data_structure,
__ WordPtrAdd(__ WordPtrShiftLeft(hash, kTaggedSizeLog2),
OrderedHashMap::HashTableStartOffset()),
LoadOp::Kind::TaggedBase(), MemoryRepresentation::TaggedSigned())));
Label<WordPtr> done(this);
LoopLabel<WordPtr> loop(this);
GOTO(loop, first_entry);
LOOP(loop, entry) {
GOTO_IF(__ WordPtrEqual(entry, OrderedHashMap::kNotFound), done,
entry);
V<WordPtr> candidate =
__ WordPtrAdd(__ WordPtrMul(entry, OrderedHashMap::kEntrySize),
number_of_buckets);
V<Object> candidate_key = __ Load(
data_structure,
__ WordPtrAdd(__ WordPtrShiftLeft(candidate, kTaggedSizeLog2),
OrderedHashMap::HashTableStartOffset()),
LoadOp::Kind::TaggedBase(), MemoryRepresentation::AnyTagged());
IF (LIKELY(__ ObjectIsSmi(candidate_key))) {
GOTO_IF(__ Word32Equal(__ SmiUntag(candidate_key), key), done,
candidate);
}
ELSE_IF (__ TaggedEqual(
__ LoadMapField(candidate_key),
__ HeapConstant(factory_->heap_number_map()))) {
GOTO_IF(__ Float64Equal(
__ template LoadField<Float64>(
candidate_key, AccessBuilder::ForHeapNumberValue()),
__ ChangeInt32ToFloat64(key)),
done, candidate);
}
END_IF
V<WordPtr> next_entry = __ ChangeInt32ToIntPtr(__ SmiUntag(__ Load(
data_structure,
__ WordPtrAdd(__ WordPtrShiftLeft(entry, kTaggedSizeLog2),
(OrderedHashMap::HashTableStartOffset() +
OrderedHashMap::kChainOffset * kTaggedSize)),
LoadOp::Kind::TaggedBase(),
MemoryRepresentation::TaggedSigned())));
GOTO(loop, next_entry);
}
BIND(done, result);
return result;
}
case FindOrderedHashEntryOp::Kind::kFindOrderedHashSetEntry:
return __ CallBuiltin_FindOrderedHashSetEntry(
isolate_, __ NoContextConstant(), data_structure, key);
}
}
private:
// TODO(nicohartmann@): Might move some of those helpers into the assembler
// interface.
// Pass {bitfield} = {digit} = OpIndex::Invalid() to construct the canonical
// 0n BigInt.
V<BigInt> AllocateBigInt(V<Word32> bitfield, V<Word64> digit) {
@ -2819,6 +3067,34 @@ class MachineLoweringReducer : public Next {
}
}
V<Word32> ComputeUnseededHash(V<Word32> value) {
// See v8::internal::ComputeUnseededHash()
value = __ Word32Add(__ Word32BitwiseXor(value, 0xFFFFFFFF),
__ Word32ShiftLeft(value, 15));
value = __ Word32BitwiseXor(value, __ Word32ShiftRightLogical(value, 12));
value = __ Word32Add(value, __ Word32ShiftLeft(value, 2));
value = __ Word32BitwiseXor(value, __ Word32ShiftRightLogical(value, 4));
value = __ Word32Mul(value, 2057);
value = __ Word32BitwiseXor(value, __ Word32ShiftRightLogical(value, 16));
value = __ Word32BitwiseAnd(value, 0x3FFFFFFF);
return value;
}
void TransitionElementsTo(V<JSArray> array, ElementsKind from,
ElementsKind to, Handle<Map> target_map) {
DCHECK(IsMoreGeneralElementsKindTransition(from, to));
DCHECK(to == HOLEY_ELEMENTS || to == HOLEY_DOUBLE_ELEMENTS);
if (IsSimpleMapChangeTransition(from, to)) {
__ StoreField(array, AccessBuilder::ForMap(),
__ HeapConstant(target_map));
} else {
// Instance migration, call out to the runtime for {array}.
__ CallRuntime_TransitionElementsKind(isolate_, __ NoContextConstant(),
array, __ HeapConstant(target_map));
}
}
Factory* factory_;
Isolate* isolate_;
};

@ -1060,6 +1060,22 @@ std::ostream& operator<<(std::ostream& os, ArgumentsLengthOp::Kind kind) {
}
}
std::ostream& operator<<(std::ostream& os,
TransitionAndStoreArrayElementOp::Kind kind) {
switch (kind) {
case TransitionAndStoreArrayElementOp::Kind::kElement:
return os << "Element";
case TransitionAndStoreArrayElementOp::Kind::kNumberElement:
return os << "NumberElement";
case TransitionAndStoreArrayElementOp::Kind::kOddballElement:
return os << "OddballElement";
case TransitionAndStoreArrayElementOp::Kind::kNonNumberElement:
return os << "NonNumberElement";
case TransitionAndStoreArrayElementOp::Kind::kSignedSmallElement:
return os << "SignedSmallElement";
}
}
std::ostream& operator<<(std::ostream& os, SameValueOp::Mode mode) {
switch (mode) {
case SameValueOp::Mode::kSameValue:
@ -1069,6 +1085,17 @@ std::ostream& operator<<(std::ostream& os, SameValueOp::Mode mode) {
}
}
std::ostream& operator<<(std::ostream& os, FindOrderedHashEntryOp::Kind kind) {
switch (kind) {
case FindOrderedHashEntryOp::Kind::kFindOrderedHashMapEntry:
return os << "FindOrderedHashMapEntry";
case FindOrderedHashEntryOp::Kind::kFindOrderedHashMapEntryForInt32Key:
return os << "FindOrderedHashMapEntryForInt32Key";
case FindOrderedHashEntryOp::Kind::kFindOrderedHashSetEntry:
return os << "FindOrderedHashSetEntry";
}
}
std::string Operation::ToString() const {
std::stringstream ss;
ss << *this;

@ -163,7 +163,7 @@ struct FrameStateOp;
V(LoadStackArgument) \
V(StoreTypedElement) \
V(StoreDataViewElement) \
V(StoreSignedSmallElement) \
V(TransitionAndStoreArrayElement) \
V(CompareMaps) \
V(CheckMaps) \
V(CheckedClosure) \
@ -176,7 +176,8 @@ struct FrameStateOp;
V(RuntimeAbort) \
V(EnsureWritableFastElements) \
V(MaybeGrowFastElements) \
V(TransitionElementsKind)
V(TransitionElementsKind) \
V(FindOrderedHashEntry)
enum class Opcode : uint8_t {
#define ENUM_CONSTANT(Name) k##Name,
@ -3672,8 +3673,19 @@ struct StoreDataViewElementOp
auto options() const { return std::tuple{element_type}; }
};
struct StoreSignedSmallElementOp
: FixedArityOperationT<3, StoreSignedSmallElementOp> {
struct TransitionAndStoreArrayElementOp
: FixedArityOperationT<3, TransitionAndStoreArrayElementOp> {
enum class Kind : uint8_t {
kElement,
kNumberElement,
kOddballElement,
kNonNumberElement,
kSignedSmallElement,
};
Kind kind;
Handle<Map> fast_map;
Handle<Map> double_map;
static constexpr OpProperties properties = OpProperties::Writing();
base::Vector<const RegisterRepresentation> outputs_rep() const { return {}; }
@ -3681,18 +3693,52 @@ struct StoreSignedSmallElementOp
OpIndex index() const { return Base::input(1); }
OpIndex value() const { return Base::input(2); }
StoreSignedSmallElementOp(OpIndex array, OpIndex index, OpIndex value)
: Base(array, index, value) {}
TransitionAndStoreArrayElementOp(OpIndex array, OpIndex index, OpIndex value,
Kind kind, Handle<Map> fast_map,
Handle<Map> double_map)
: Base(array, index, value),
kind(kind),
fast_map(fast_map),
double_map(double_map) {}
void Validate(const Graph& graph) const {
DCHECK(ValidOpInputRep(graph, array(), RegisterRepresentation::Tagged()));
DCHECK(ValidOpInputRep(graph, index(),
RegisterRepresentation::PointerSized()));
DCHECK(ValidOpInputRep(graph, value(), RegisterRepresentation::Word32()));
switch (kind) {
case Kind::kElement:
DCHECK(
ValidOpInputRep(graph, value(), RegisterRepresentation::Tagged()));
break;
case Kind::kNumberElement:
DCHECK(
ValidOpInputRep(graph, value(), RegisterRepresentation::Float64()));
break;
case Kind::kOddballElement:
case Kind::kNonNumberElement:
DCHECK(
ValidOpInputRep(graph, value(), RegisterRepresentation::Tagged()));
break;
case Kind::kSignedSmallElement:
DCHECK(
ValidOpInputRep(graph, value(), RegisterRepresentation::Word32()));
break;
}
}
auto options() const { return std::tuple{}; }
size_t hash_value() const {
return fast_hash_combine(kind, fast_map.address(), double_map.address());
}
bool operator==(const TransitionAndStoreArrayElementOp& other) const {
return kind == other.kind && fast_map.equals(other.fast_map) &&
double_map.equals(other.double_map);
}
auto options() const { return std::tuple{kind, fast_map, double_map}; }
};
std::ostream& operator<<(std::ostream& os,
TransitionAndStoreArrayElementOp::Kind kind);
struct CompareMapsOp : FixedArityOperationT<1, CompareMapsOp> {
ZoneRefSet<Map> maps;
@ -4085,6 +4131,45 @@ struct TransitionElementsKindOp
auto options() const { return std::tuple{transition}; }
};
struct FindOrderedHashEntryOp
: FixedArityOperationT<2, FindOrderedHashEntryOp> {
enum class Kind : uint8_t {
kFindOrderedHashMapEntry,
kFindOrderedHashMapEntryForInt32Key,
kFindOrderedHashSetEntry,
};
Kind kind;
static constexpr OpProperties properties = OpProperties::AnySideEffects();
base::Vector<const RegisterRepresentation> outputs_rep() const {
switch (kind) {
case Kind::kFindOrderedHashMapEntry:
case Kind::kFindOrderedHashSetEntry:
return RepVector<RegisterRepresentation::Tagged()>();
case Kind::kFindOrderedHashMapEntryForInt32Key:
return RepVector<RegisterRepresentation::PointerSized()>();
}
}
OpIndex data_structure() const { return Base::input(0); }
OpIndex key() const { return Base::input(1); }
FindOrderedHashEntryOp(OpIndex data_structure, OpIndex key, Kind kind)
: Base(data_structure, key), kind(kind) {}
void Validate(const Graph& graph) const {
DCHECK(ValidOpInputRep(graph, data_structure(),
RegisterRepresentation::Tagged()));
DCHECK(ValidOpInputRep(graph, key(),
kind == Kind::kFindOrderedHashMapEntryForInt32Key
? RegisterRepresentation::Word32()
: RegisterRepresentation::Tagged()));
}
auto options() const { return std::tuple{kind}; }
};
std::ostream& operator<<(std::ostream& os, FindOrderedHashEntryOp::Kind kind);
#define OPERATION_PROPERTIES_CASE(Name) Name##Op::PropertiesIfStatic(),
static constexpr base::Optional<OpProperties>
kOperationPropertiesTable[kNumberOfOpcodes] = {

@ -862,11 +862,11 @@ class GraphVisitor {
MapToNewGraph(op.index()), MapToNewGraph(op.value()),
MapToNewGraph(op.is_little_endian()), op.element_type);
}
OpIndex AssembleOutputGraphStoreSignedSmallElement(
const StoreSignedSmallElementOp& op) {
return assembler().ReduceStoreSignedSmallElement(MapToNewGraph(op.array()),
MapToNewGraph(op.index()),
MapToNewGraph(op.value()));
OpIndex AssembleOutputGraphTransitionAndStoreArrayElement(
const TransitionAndStoreArrayElementOp& op) {
return assembler().ReduceTransitionAndStoreArrayElement(
MapToNewGraph(op.array()), MapToNewGraph(op.index()),
MapToNewGraph(op.value()), op.kind, op.fast_map, op.double_map);
}
OpIndex AssembleOutputGraphCompareMaps(const CompareMapsOp& op) {
return assembler().ReduceCompareMaps(MapToNewGraph(op.heap_object()),
@ -929,6 +929,11 @@ class GraphVisitor {
return assembler().ReduceTransitionElementsKind(MapToNewGraph(op.object()),
op.transition);
}
OpIndex AssembleOutputGraphFindOrderedHashEntry(
const FindOrderedHashEntryOp& op) {
return assembler().ReduceFindOrderedHashEntry(
MapToNewGraph(op.data_structure()), MapToNewGraph(op.key()), op.kind);
}
void CreateOldToNewMapping(OpIndex old_index, OpIndex new_index) {
if (current_block_needs_variables_) {

@ -202,6 +202,7 @@ SHOULD_HAVE_BEEN_LOWERED(DecodeExternalPointer)
SHOULD_HAVE_BEEN_LOWERED(DoubleArrayMinMax)
SHOULD_HAVE_BEEN_LOWERED(EnsureWritableFastElements)
SHOULD_HAVE_BEEN_LOWERED(FastApiCall)
SHOULD_HAVE_BEEN_LOWERED(FindOrderedHashEntry)
SHOULD_HAVE_BEEN_LOWERED(FloatIs)
SHOULD_HAVE_BEEN_LOWERED(Float64SameValue)
SHOULD_HAVE_BEEN_LOWERED(LoadDataViewElement)
@ -219,7 +220,6 @@ SHOULD_HAVE_BEEN_LOWERED(RuntimeAbort)
SHOULD_HAVE_BEEN_LOWERED(SameValue)
SHOULD_HAVE_BEEN_LOWERED(StoreDataViewElement)
SHOULD_HAVE_BEEN_LOWERED(StoreMessage)
SHOULD_HAVE_BEEN_LOWERED(StoreSignedSmallElement)
SHOULD_HAVE_BEEN_LOWERED(StoreTypedElement)
SHOULD_HAVE_BEEN_LOWERED(StringAt)
SHOULD_HAVE_BEEN_LOWERED(StringComparison)
@ -233,6 +233,7 @@ SHOULD_HAVE_BEEN_LOWERED(StringSubstring)
SHOULD_HAVE_BEEN_LOWERED(StringToCaseIntl)
#endif // V8_INTL_SUPPORT
SHOULD_HAVE_BEEN_LOWERED(Tag)
SHOULD_HAVE_BEEN_LOWERED(TransitionAndStoreArrayElement)
SHOULD_HAVE_BEEN_LOWERED(TransitionElementsKind)
SHOULD_HAVE_BEEN_LOWERED(TruncateObjectToPrimitive)
SHOULD_HAVE_BEEN_LOWERED(TruncateObjectToPrimitiveOrDeopt)

@ -262,7 +262,7 @@ class TypeInferenceAnalysis {
case Opcode::kLoadStackArgument:
case Opcode::kStoreTypedElement:
case Opcode::kStoreDataViewElement:
case Opcode::kStoreSignedSmallElement:
case Opcode::kTransitionAndStoreArrayElement:
case Opcode::kCompareMaps:
case Opcode::kCheckMaps:
case Opcode::kCheckedClosure:
@ -276,6 +276,7 @@ class TypeInferenceAnalysis {
case Opcode::kEnsureWritableFastElements:
case Opcode::kMaybeGrowFastElements:
case Opcode::kTransitionElementsKind:
case Opcode::kFindOrderedHashEntry:
// TODO(nicohartmann@): Support remaining operations. For now we
// compute fallback types.
if (op.outputs_rep().size() > 0) {

@ -59,6 +59,8 @@ def decode(arg, encoding="utf-8"):
# https://google.github.io/styleguide/cppguide.html#Inputs_and_Outputs.
# whitespace/braces: Doesn't handle {}-initialization for custom types
# well; also should be subsumed by clang-format.
# whitespace/parens: Conflicts with clang-format rule to treat Turboshaft's
# IF, IF_NOT, ... as IfMacros and insert a whitespace before the parenthesis.
LINT_RULES = """
-build/header_guard
@ -68,6 +70,7 @@ LINT_RULES = """
-runtime/references
-whitespace/braces
-whitespace/comments
-whitespace/parens
""".split()
LINT_OUTPUT_PATTERN = re.compile(r'^.+[:(]\d+[:)]')