[torque] Allow storing to bitfield structs that are stored in Smis
This change: 1. Updates the Torque compiler to allow direct access to bitfields that are packed within Smi values, which previously would have required a separate untagging step, 2. Updates JSRegExpStringIterator to represent its flags in Torque, 3. Adds reduction cases in MachineOperatorReducer for when the input to a branch or the left-hand side of a Word32Equals is based on a 64-bit shift-and-mask operation which has been truncated to 32 bits, as is the case in the code generated by step 1, and 4. Adds a reduction case in MachineOperatorReducer to remove an extra Word64And operation added by step 1. Bug: v8:7793 Change-Id: Ib4ac2def6211b3cae6be25a8b2a644be5c7d6d3f Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2119225 Commit-Queue: Seth Brenith <seth.brenith@microsoft.com> Reviewed-by: Tobias Tebbi <tebbi@chromium.org> Cr-Commit-Position: refs/heads/master@{#67290}
This commit is contained in:
parent
0d01850efb
commit
80843eda31
@ -1336,10 +1336,10 @@ TNode<Object> RegExpMatchAllAssembler::CreateRegExpStringIterator(
|
||||
// 9. Set iterator.[[Done]] to false.
|
||||
TNode<Int32T> global_flag =
|
||||
Word32Shl(ReinterpretCast<Int32T>(global),
|
||||
Int32Constant(JSRegExpStringIterator::kGlobalBit));
|
||||
Int32Constant(JSRegExpStringIterator::GlobalBit::kShift));
|
||||
TNode<Int32T> unicode_flag =
|
||||
Word32Shl(ReinterpretCast<Int32T>(full_unicode),
|
||||
Int32Constant(JSRegExpStringIterator::kUnicodeBit));
|
||||
Int32Constant(JSRegExpStringIterator::UnicodeBit::kShift));
|
||||
TNode<Int32T> iterator_flags = Word32Or(global_flag, unicode_flag);
|
||||
StoreObjectFieldNoWriteBarrier(iterator, JSRegExpStringIterator::kFlagsOffset,
|
||||
SmiFromInt32(iterator_flags));
|
||||
|
@ -12,7 +12,7 @@ namespace internal_coverage {
|
||||
const debugInfo = Cast<DebugInfo>(shared.script_or_debug_info)
|
||||
otherwise goto IfNoCoverageInfo;
|
||||
|
||||
if (!SmiUntag(debugInfo.flags).has_coverage_info) goto IfNoCoverageInfo;
|
||||
if (!debugInfo.flags.has_coverage_info) goto IfNoCoverageInfo;
|
||||
return UnsafeCast<CoverageInfo>(debugInfo.coverage_info);
|
||||
}
|
||||
|
||||
|
@ -104,32 +104,6 @@ namespace regexp {
|
||||
return RegExpPrototypeMatchAllImpl(context, receiver, string);
|
||||
}
|
||||
|
||||
const kJSRegExpStringIteratorDone:
|
||||
constexpr int31 generates '1 << JSRegExpStringIterator::kDoneBit';
|
||||
const kJSRegExpStringIteratorGlobal: constexpr int31
|
||||
generates '1 << JSRegExpStringIterator::kGlobalBit';
|
||||
const kJSRegExpStringIteratorUnicode: constexpr int31
|
||||
generates '1 << JSRegExpStringIterator::kUnicodeBit';
|
||||
|
||||
extern macro IsSetSmi(Smi, constexpr int31): bool;
|
||||
|
||||
macro HasDoneFlag(flags: Smi): bool {
|
||||
return IsSetSmi(flags, kJSRegExpStringIteratorDone);
|
||||
}
|
||||
|
||||
macro HasGlobalFlag(flags: Smi): bool {
|
||||
return IsSetSmi(flags, kJSRegExpStringIteratorGlobal);
|
||||
}
|
||||
|
||||
macro HasUnicodeFlag(flags: Smi): bool {
|
||||
return IsSetSmi(flags, kJSRegExpStringIteratorUnicode);
|
||||
}
|
||||
|
||||
macro SetDoneFlag(iterator: JSRegExpStringIterator, flags: Smi) {
|
||||
const newFlags: Smi = flags | kJSRegExpStringIteratorDone;
|
||||
iterator.flags = newFlags;
|
||||
}
|
||||
|
||||
// https://tc39.github.io/proposal-string-matchall/
|
||||
// %RegExpStringIteratorPrototype%.next ( )
|
||||
transitioning javascript builtin RegExpStringIteratorPrototypeNext(
|
||||
@ -147,8 +121,8 @@ namespace regexp {
|
||||
try {
|
||||
// 4. If O.[[Done]] is true, then
|
||||
// a. Return ! CreateIterResultObject(undefined, true).
|
||||
const flags: Smi = receiver.flags;
|
||||
if (HasDoneFlag(flags)) goto ReturnEmptyDoneResult;
|
||||
const flags: SmiTagged<JSRegExpStringIteratorFlags> = receiver.flags;
|
||||
if (flags.done) goto ReturnEmptyDoneResult;
|
||||
|
||||
// 5. Let R be O.[[iteratingRegExp]].
|
||||
const iteratingRegExp: JSReceiver = receiver.iterating_reg_exp;
|
||||
@ -180,15 +154,15 @@ namespace regexp {
|
||||
}
|
||||
// 11. Else,
|
||||
// b. Else, handle non-global case first.
|
||||
if (!HasGlobalFlag(flags)) {
|
||||
if (!flags.global) {
|
||||
// i. Set O.[[Done]] to true.
|
||||
SetDoneFlag(receiver, flags);
|
||||
receiver.flags.done = true;
|
||||
|
||||
// ii. Return ! CreateIterResultObject(match, false).
|
||||
return AllocateJSIteratorResult(UnsafeCast<JSAny>(match), False);
|
||||
}
|
||||
// a. If global is true,
|
||||
assert(HasGlobalFlag(flags));
|
||||
assert(flags.global);
|
||||
if (isFastRegExp) {
|
||||
// i. Let matchStr be ? ToString(? Get(match, "0")).
|
||||
const match = UnsafeCast<JSRegExpResult>(match);
|
||||
@ -206,7 +180,7 @@ namespace regexp {
|
||||
// 2. Let nextIndex be ! AdvanceStringIndex(S, thisIndex,
|
||||
// fullUnicode).
|
||||
const nextIndex: Smi = AdvanceStringIndexFast(
|
||||
iteratingString, thisIndex, HasUnicodeFlag(flags));
|
||||
iteratingString, thisIndex, flags.unicode);
|
||||
|
||||
// 3. Perform ? Set(R, "lastIndex", nextIndex, true).
|
||||
FastStoreLastIndex(iteratingRegExp, nextIndex);
|
||||
@ -227,8 +201,8 @@ namespace regexp {
|
||||
|
||||
// 2. Let nextIndex be ! AdvanceStringIndex(S, thisIndex,
|
||||
// fullUnicode).
|
||||
const nextIndex: Number = AdvanceStringIndexSlow(
|
||||
iteratingString, thisIndex, HasUnicodeFlag(flags));
|
||||
const nextIndex: Number =
|
||||
AdvanceStringIndexSlow(iteratingString, thisIndex, flags.unicode);
|
||||
|
||||
// 3. Perform ? Set(R, "lastIndex", nextIndex, true).
|
||||
SlowStoreLastIndex(iteratingRegExp, nextIndex);
|
||||
@ -239,7 +213,7 @@ namespace regexp {
|
||||
// 10. If match is null, then
|
||||
label IfNoMatch {
|
||||
// a. Set O.[[Done]] to true.
|
||||
SetDoneFlag(receiver, flags);
|
||||
receiver.flags.done = true;
|
||||
|
||||
// b. Return ! CreateIterResultObject(undefined, true).
|
||||
goto ReturnEmptyDoneResult;
|
||||
|
@ -7348,7 +7348,7 @@ TNode<Uint32T> CodeStubAssembler::DecodeWord32(SloppyTNode<Word32T> word32,
|
||||
}
|
||||
|
||||
TNode<UintPtrT> CodeStubAssembler::DecodeWord(SloppyTNode<WordT> word,
|
||||
uint32_t shift, uint32_t mask) {
|
||||
uint32_t shift, uintptr_t mask) {
|
||||
DCHECK_EQ((mask >> shift) << shift, mask);
|
||||
return Unsigned(WordAnd(WordShr(word, static_cast<int>(shift)),
|
||||
IntPtrConstant(mask >> shift)));
|
||||
@ -7367,7 +7367,7 @@ TNode<Word32T> CodeStubAssembler::UpdateWord32(TNode<Word32T> word,
|
||||
|
||||
TNode<WordT> CodeStubAssembler::UpdateWord(TNode<WordT> word,
|
||||
TNode<UintPtrT> value,
|
||||
uint32_t shift, uint32_t mask) {
|
||||
uint32_t shift, uintptr_t mask) {
|
||||
DCHECK_EQ((mask >> shift) << shift, mask);
|
||||
// Ensure the {value} fits fully in the mask.
|
||||
CSA_ASSERT(this,
|
||||
|
@ -2814,7 +2814,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
|
||||
|
||||
// Decodes an unsigned (!) value from |word| to a word-size node.
|
||||
TNode<UintPtrT> DecodeWord(SloppyTNode<WordT> word, uint32_t shift,
|
||||
uint32_t mask);
|
||||
uintptr_t mask);
|
||||
|
||||
// Returns a node that contains the updated values of a |BitField|.
|
||||
template <typename BitField>
|
||||
@ -2850,7 +2850,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
|
||||
// Returns a node that contains the updated {value} inside {word} starting
|
||||
// at {shift} and fitting in {mask}.
|
||||
TNode<WordT> UpdateWord(TNode<WordT> word, TNode<UintPtrT> value,
|
||||
uint32_t shift, uint32_t mask);
|
||||
uint32_t shift, uintptr_t mask);
|
||||
|
||||
// Returns true if any of the |T|'s bits in given |word32| are set.
|
||||
template <typename T>
|
||||
|
@ -43,6 +43,14 @@ class Word32Adapter {
|
||||
return x.IsWord32Shl();
|
||||
}
|
||||
template <typename T>
|
||||
static bool IsWordNShr(const T& x) {
|
||||
return x.IsWord32Shr();
|
||||
}
|
||||
template <typename T>
|
||||
static bool IsWordNSar(const T& x) {
|
||||
return x.IsWord32Sar();
|
||||
}
|
||||
template <typename T>
|
||||
static bool IsWordNXor(const T& x) {
|
||||
return x.IsWord32Xor();
|
||||
}
|
||||
@ -65,6 +73,7 @@ class Word32Adapter {
|
||||
Reduction TryMatchWordNRor(Node* node) { return r_->TryMatchWord32Ror(node); }
|
||||
|
||||
Node* IntNConstant(int32_t value) { return r_->Int32Constant(value); }
|
||||
Node* UintNConstant(uint32_t value) { return r_->Uint32Constant(value); }
|
||||
Node* WordNAnd(Node* lhs, Node* rhs) { return r_->Word32And(lhs, rhs); }
|
||||
|
||||
private:
|
||||
@ -94,6 +103,14 @@ class Word64Adapter {
|
||||
return x.IsWord64Shl();
|
||||
}
|
||||
template <typename T>
|
||||
static bool IsWordNShr(const T& x) {
|
||||
return x.IsWord64Shr();
|
||||
}
|
||||
template <typename T>
|
||||
static bool IsWordNSar(const T& x) {
|
||||
return x.IsWord64Sar();
|
||||
}
|
||||
template <typename T>
|
||||
static bool IsWordNXor(const T& x) {
|
||||
return x.IsWord64Xor();
|
||||
}
|
||||
@ -119,6 +136,7 @@ class Word64Adapter {
|
||||
}
|
||||
|
||||
Node* IntNConstant(int64_t value) { return r_->Int64Constant(value); }
|
||||
Node* UintNConstant(uint64_t value) { return r_->Uint64Constant(value); }
|
||||
Node* WordNAnd(Node* lhs, Node* rhs) { return r_->Word64And(lhs, rhs); }
|
||||
|
||||
private:
|
||||
@ -246,6 +264,12 @@ Node* MachineOperatorReducer::Uint32Div(Node* dividend, uint32_t divisor) {
|
||||
return quotient;
|
||||
}
|
||||
|
||||
Node* MachineOperatorReducer::TruncateInt64ToInt32(Node* value) {
|
||||
Node* const node = graph()->NewNode(machine()->TruncateInt64ToInt32(), value);
|
||||
Reduction const reduction = ReduceTruncateInt64ToInt32(node);
|
||||
return reduction.Changed() ? reduction.replacement() : node;
|
||||
}
|
||||
|
||||
// Perform constant folding and strength reduction on machine operators.
|
||||
Reduction MachineOperatorReducer::Reduce(Node* node) {
|
||||
switch (node->opcode()) {
|
||||
@ -297,25 +321,20 @@ Reduction MachineOperatorReducer::Reduce(Node* node) {
|
||||
}
|
||||
// TODO(turbofan): fold HeapConstant, ExternalReference, pointer compares
|
||||
if (m.LeftEqualsRight()) return ReplaceBool(true); // x == x => true
|
||||
if (m.left().IsWord32And() && m.right().HasValue()) {
|
||||
Uint32BinopMatcher mand(m.left().node());
|
||||
if ((mand.left().IsWord32Shr() || mand.left().IsWord32Sar()) &&
|
||||
mand.right().HasValue()) {
|
||||
Uint32BinopMatcher mshift(mand.left().node());
|
||||
// ((x >> K1) & K2) == K3 => (x & (K2 << K1)) == (K3 << K1)
|
||||
if (mshift.right().HasValue()) {
|
||||
auto shift_bits = mshift.right().Value();
|
||||
auto mask = mand.right().Value();
|
||||
auto rhs = static_cast<uint32_t>(m.right().Value());
|
||||
// Make sure that we won't shift data off the end.
|
||||
if (shift_bits <= base::bits::CountLeadingZeros(mask) &&
|
||||
shift_bits <= base::bits::CountLeadingZeros(rhs)) {
|
||||
node->ReplaceInput(
|
||||
0, Word32And(mshift.left().node(), mask << shift_bits));
|
||||
node->ReplaceInput(1, Int32Constant(rhs << shift_bits));
|
||||
return Changed(node);
|
||||
}
|
||||
}
|
||||
if (m.right().HasValue()) {
|
||||
base::Optional<std::pair<Node*, uint32_t>> replacements;
|
||||
if (m.left().IsTruncateInt64ToInt32()) {
|
||||
replacements = ReduceWord32EqualForConstantRhs<Word64Adapter>(
|
||||
NodeProperties::GetValueInput(m.left().node(), 0),
|
||||
static_cast<uint32_t>(m.right().Value()));
|
||||
} else {
|
||||
replacements = ReduceWord32EqualForConstantRhs<Word32Adapter>(
|
||||
m.left().node(), static_cast<uint32_t>(m.right().Value()));
|
||||
}
|
||||
if (replacements) {
|
||||
node->ReplaceInput(0, replacements->first);
|
||||
node->ReplaceInput(1, Uint32Constant(replacements->second));
|
||||
return Changed(node);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -803,12 +822,8 @@ Reduction MachineOperatorReducer::Reduce(Node* node) {
|
||||
if (m.IsChangeInt32ToFloat64()) return Replace(m.node()->InputAt(0));
|
||||
return NoChange();
|
||||
}
|
||||
case IrOpcode::kTruncateInt64ToInt32: {
|
||||
Int64Matcher m(node->InputAt(0));
|
||||
if (m.HasValue()) return ReplaceInt32(static_cast<int32_t>(m.Value()));
|
||||
if (m.IsChangeInt32ToInt64()) return Replace(m.node()->InputAt(0));
|
||||
break;
|
||||
}
|
||||
case IrOpcode::kTruncateInt64ToInt32:
|
||||
return ReduceTruncateInt64ToInt32(node);
|
||||
case IrOpcode::kTruncateFloat64ToFloat32: {
|
||||
Float64Matcher m(node->InputAt(0));
|
||||
if (m.HasValue()) {
|
||||
@ -864,6 +879,13 @@ Reduction MachineOperatorReducer::Reduce(Node* node) {
|
||||
return NoChange();
|
||||
}
|
||||
|
||||
Reduction MachineOperatorReducer::ReduceTruncateInt64ToInt32(Node* node) {
|
||||
Int64Matcher m(node->InputAt(0));
|
||||
if (m.HasValue()) return ReplaceInt32(static_cast<int32_t>(m.Value()));
|
||||
if (m.IsChangeInt32ToInt64()) return Replace(m.node()->InputAt(0));
|
||||
return NoChange();
|
||||
}
|
||||
|
||||
Reduction MachineOperatorReducer::ReduceInt32Add(Node* node) {
|
||||
DCHECK_EQ(IrOpcode::kInt32Add, node->opcode());
|
||||
Int32BinopMatcher m(node);
|
||||
@ -1529,6 +1551,20 @@ Reduction MachineOperatorReducer::ReduceWordNOr(Node* node) {
|
||||
}
|
||||
if (m.LeftEqualsRight()) return Replace(m.left().node()); // x | x => x
|
||||
|
||||
// (x & K1) | K2 => x | K2 if K2 has ones for every zero bit in K1.
|
||||
// This case can be constructed by UpdateWord and UpdateWord32 in CSA.
|
||||
if (m.right().HasValue()) {
|
||||
if (A::IsWordNAnd(m.left())) {
|
||||
typename A::IntNBinopMatcher mand(m.left().node());
|
||||
if (mand.right().HasValue()) {
|
||||
if ((m.right().Value() | mand.right().Value()) == -1) {
|
||||
node->ReplaceInput(0, mand.left().node());
|
||||
return Changed(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return a.TryMatchWordNRor(node);
|
||||
}
|
||||
|
||||
@ -1682,25 +1718,64 @@ Reduction MachineOperatorReducer::ReduceConditional(Node* node) {
|
||||
// Reductions involving control flow happen elsewhere. Non-zero inputs are
|
||||
// considered true in all conditional ops.
|
||||
NodeMatcher condition(NodeProperties::GetValueInput(node, 0));
|
||||
if (condition.IsWord32And()) {
|
||||
Uint32BinopMatcher mand(condition.node());
|
||||
if ((mand.left().IsWord32Shr() || mand.left().IsWord32Sar()) &&
|
||||
if (condition.IsTruncateInt64ToInt32()) {
|
||||
if (auto replacement =
|
||||
ReduceConditionalN<Word64Adapter>(condition.node())) {
|
||||
NodeProperties::ReplaceValueInput(node, *replacement, 0);
|
||||
return Changed(node);
|
||||
}
|
||||
} else if (auto replacement = ReduceConditionalN<Word32Adapter>(node)) {
|
||||
NodeProperties::ReplaceValueInput(node, *replacement, 0);
|
||||
return Changed(node);
|
||||
}
|
||||
return NoChange();
|
||||
}
|
||||
|
||||
template <typename WordNAdapter>
|
||||
base::Optional<Node*> MachineOperatorReducer::ReduceConditionalN(Node* node) {
|
||||
NodeMatcher condition(NodeProperties::GetValueInput(node, 0));
|
||||
// Branch conditions are 32-bit comparisons against zero, so they are the
|
||||
// opposite of a 32-bit `x == 0` node. To avoid repetition, we can reuse logic
|
||||
// for Word32Equal: if `x == 0` can reduce to `y == 0`, then branch(x) can
|
||||
// reduce to branch(y).
|
||||
auto replacements =
|
||||
ReduceWord32EqualForConstantRhs<WordNAdapter>(condition.node(), 0);
|
||||
if (replacements && replacements->second == 0) return replacements->first;
|
||||
return {};
|
||||
}
|
||||
|
||||
template <typename WordNAdapter>
|
||||
base::Optional<std::pair<Node*, uint32_t>>
|
||||
MachineOperatorReducer::ReduceWord32EqualForConstantRhs(Node* lhs,
|
||||
uint32_t rhs) {
|
||||
if (WordNAdapter::IsWordNAnd(NodeMatcher(lhs))) {
|
||||
typename WordNAdapter::UintNBinopMatcher mand(lhs);
|
||||
if ((WordNAdapter::IsWordNShr(mand.left()) ||
|
||||
WordNAdapter::IsWordNSar(mand.left())) &&
|
||||
mand.right().HasValue()) {
|
||||
Uint32BinopMatcher mshift(mand.left().node());
|
||||
// Branch condition (x >> K1) & K2 => x & (K2 << K1)
|
||||
typename WordNAdapter::UintNBinopMatcher mshift(mand.left().node());
|
||||
// ((x >> K1) & K2) == K3 => (x & (K2 << K1)) == (K3 << K1)
|
||||
if (mshift.right().HasValue()) {
|
||||
auto shift_bits = mshift.right().Value();
|
||||
auto mask = mand.right().Value();
|
||||
// Make sure that we won't shift data off the end.
|
||||
if (shift_bits <= base::bits::CountLeadingZeros(mask)) {
|
||||
NodeProperties::ReplaceValueInput(
|
||||
node, Word32And(mshift.left().node(), mask << shift_bits), 0);
|
||||
return Changed(node);
|
||||
// Make sure that we won't shift data off the end, and that all of the
|
||||
// data ends up in the lower 32 bits for 64-bit mode.
|
||||
if (shift_bits <= base::bits::CountLeadingZeros(mask) &&
|
||||
shift_bits <= base::bits::CountLeadingZeros(rhs) &&
|
||||
mask << shift_bits <= std::numeric_limits<uint32_t>::max()) {
|
||||
Node* new_input = mshift.left().node();
|
||||
uint32_t new_mask = static_cast<uint32_t>(mask << shift_bits);
|
||||
uint32_t new_rhs = rhs << shift_bits;
|
||||
if (WordNAdapter::WORD_SIZE == 64) {
|
||||
// We can truncate before performing the And.
|
||||
new_input = TruncateInt64ToInt32(new_input);
|
||||
}
|
||||
return std::make_pair(Word32And(new_input, new_mask), new_rhs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return NoChange();
|
||||
return {};
|
||||
}
|
||||
|
||||
CommonOperatorBuilder* MachineOperatorReducer::common() const {
|
||||
|
@ -62,6 +62,7 @@ class V8_EXPORT_PRIVATE MachineOperatorReducer final
|
||||
Node* Int32Mul(Node* lhs, Node* rhs);
|
||||
Node* Int32Div(Node* dividend, int32_t divisor);
|
||||
Node* Uint32Div(Node* dividend, uint32_t divisor);
|
||||
Node* TruncateInt64ToInt32(Node* value);
|
||||
|
||||
Reduction ReplaceBool(bool value) { return ReplaceInt32(value ? 1 : 0); }
|
||||
Reduction ReplaceFloat32(volatile float value) {
|
||||
@ -109,6 +110,7 @@ class V8_EXPORT_PRIVATE MachineOperatorReducer final
|
||||
Reduction ReduceFloat64InsertHighWord32(Node* node);
|
||||
Reduction ReduceFloat64Compare(Node* node);
|
||||
Reduction ReduceFloat64RoundDown(Node* node);
|
||||
Reduction ReduceTruncateInt64ToInt32(Node* node);
|
||||
Reduction ReduceConditional(Node* node);
|
||||
|
||||
Graph* graph() const;
|
||||
@ -125,6 +127,18 @@ class V8_EXPORT_PRIVATE MachineOperatorReducer final
|
||||
template <typename WordNAdapter>
|
||||
Reduction ReduceWordNXor(Node* node);
|
||||
|
||||
// Helper for ReduceConditional. Does not perform the actual reduction; just
|
||||
// returns a new Node that could be used as the input to the condition.
|
||||
template <typename WordNAdapter>
|
||||
base::Optional<Node*> ReduceConditionalN(Node* node);
|
||||
|
||||
// Helper for finding a reduced equality condition. Does not perform the
|
||||
// actual reduction; just returns a new pair that could be compared for the
|
||||
// same outcome.
|
||||
template <typename WordNAdapter>
|
||||
base::Optional<std::pair<Node*, uint32_t>> ReduceWord32EqualForConstantRhs(
|
||||
Node* lhs, uint32_t rhs);
|
||||
|
||||
MachineGraph* mcgraph_;
|
||||
bool allow_signalling_nan_;
|
||||
};
|
||||
|
@ -17,9 +17,9 @@ namespace internal {
|
||||
|
||||
TQ_OBJECT_CONSTRUCTORS_IMPL(JSRegExpStringIterator)
|
||||
|
||||
BOOL_ACCESSORS(JSRegExpStringIterator, flags, done, kDoneBit)
|
||||
BOOL_ACCESSORS(JSRegExpStringIterator, flags, global, kGlobalBit)
|
||||
BOOL_ACCESSORS(JSRegExpStringIterator, flags, unicode, kUnicodeBit)
|
||||
BOOL_ACCESSORS(JSRegExpStringIterator, flags, done, DoneBit::kShift)
|
||||
BOOL_ACCESSORS(JSRegExpStringIterator, flags, global, GlobalBit::kShift)
|
||||
BOOL_ACCESSORS(JSRegExpStringIterator, flags, unicode, UnicodeBit::kShift)
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -6,6 +6,7 @@
|
||||
#define V8_OBJECTS_JS_REGEXP_STRING_ITERATOR_H_
|
||||
|
||||
#include "src/objects/js-objects.h"
|
||||
#include "torque-generated/bit-fields-tq.h"
|
||||
|
||||
// Has to be the last include (doesn't have include guards):
|
||||
#include "src/objects/object-macros.h"
|
||||
@ -28,9 +29,7 @@ class JSRegExpStringIterator
|
||||
|
||||
DECL_PRINTER(JSRegExpStringIterator)
|
||||
|
||||
static const int kDoneBit = 0;
|
||||
static const int kGlobalBit = 1;
|
||||
static const int kUnicodeBit = 2;
|
||||
DEFINE_TORQUE_GENERATED_JS_REG_EXP_STRING_ITERATOR_FLAGS()
|
||||
|
||||
TQ_OBJECT_CONSTRUCTORS(JSRegExpStringIterator)
|
||||
};
|
||||
|
@ -2,11 +2,17 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
bitfield struct JSRegExpStringIteratorFlags extends uint31 {
|
||||
done: bool: 1 bit;
|
||||
global: bool: 1 bit;
|
||||
unicode: bool: 1 bit;
|
||||
}
|
||||
|
||||
@generateCppClass
|
||||
extern class JSRegExpStringIterator extends JSObject {
|
||||
// The [[IteratingRegExp]] internal property.
|
||||
iterating_reg_exp: JSReceiver;
|
||||
// The [[IteratedString]] internal property.
|
||||
iterated_string: String;
|
||||
flags: Smi;
|
||||
flags: SmiTagged<JSRegExpStringIteratorFlags>;
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ class ValueTypeFieldIterator {
|
||||
if (const auto type_wrapped_in_smi =
|
||||
Type::MatchUnaryGeneric(type_, TypeOracle::GetSmiTaggedGeneric())) {
|
||||
type = *type_wrapped_in_smi;
|
||||
bitfield_start_offset = kSmiTagSize + kSmiShiftSize;
|
||||
bitfield_start_offset = TargetArchitecture::SmiTagAndShiftSize();
|
||||
}
|
||||
if (const BitFieldStructType* bit_field_struct_type =
|
||||
BitFieldStructType::DynamicCast(type)) {
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "src/torque/csa-generator.h"
|
||||
|
||||
#include "src/common/globals.h"
|
||||
#include "src/torque/global-context.h"
|
||||
#include "src/torque/type-oracle.h"
|
||||
#include "src/torque/types.h"
|
||||
#include "src/torque/utils.h"
|
||||
@ -858,13 +859,20 @@ void CSAGenerator::EmitInstruction(const StoreReferenceInstruction& instruction,
|
||||
}
|
||||
|
||||
namespace {
|
||||
std::string GetBitFieldSpecialization(const BitFieldStructType* container,
|
||||
std::string GetBitFieldSpecialization(const Type* container,
|
||||
const BitField& field) {
|
||||
auto smi_tagged_type =
|
||||
Type::MatchUnaryGeneric(container, TypeOracle::GetSmiTaggedGeneric());
|
||||
std::string container_type = smi_tagged_type
|
||||
? "uintptr_t"
|
||||
: container->GetConstexprGeneratedTypeName();
|
||||
int offset = smi_tagged_type
|
||||
? field.offset + TargetArchitecture::SmiTagAndShiftSize()
|
||||
: field.offset;
|
||||
std::stringstream stream;
|
||||
stream << "base::BitField<"
|
||||
<< field.name_and_type.type->GetConstexprGeneratedTypeName() << ", "
|
||||
<< field.offset << ", " << field.num_bits << ", "
|
||||
<< container->GetConstexprGeneratedTypeName() << ">";
|
||||
<< offset << ", " << field.num_bits << ", " << container_type << ">";
|
||||
return stream.str();
|
||||
}
|
||||
} // namespace
|
||||
@ -877,23 +885,36 @@ void CSAGenerator::EmitInstruction(const LoadBitFieldInstruction& instruction,
|
||||
std::string bit_field_struct = stack->Pop();
|
||||
stack->Push(result_name);
|
||||
|
||||
const BitFieldStructType* source_type = instruction.bit_field_struct_type;
|
||||
const Type* result_type = instruction.bit_field.name_and_type.type;
|
||||
bool source_uintptr = source_type->IsSubtypeOf(TypeOracle::GetUIntPtrType());
|
||||
bool result_uintptr = result_type->IsSubtypeOf(TypeOracle::GetUIntPtrType());
|
||||
std::string source_word_type = source_uintptr ? "WordT" : "Word32T";
|
||||
const Type* struct_type = instruction.bit_field_struct_type;
|
||||
const Type* field_type = instruction.bit_field.name_and_type.type;
|
||||
auto smi_tagged_type =
|
||||
Type::MatchUnaryGeneric(struct_type, TypeOracle::GetSmiTaggedGeneric());
|
||||
bool struct_is_pointer_size =
|
||||
IsPointerSizeIntegralType(struct_type) || smi_tagged_type;
|
||||
DCHECK_IMPLIES(!struct_is_pointer_size, Is32BitIntegralType(struct_type));
|
||||
bool field_is_pointer_size = IsPointerSizeIntegralType(field_type);
|
||||
DCHECK_IMPLIES(!field_is_pointer_size, Is32BitIntegralType(field_type));
|
||||
std::string struct_word_type = struct_is_pointer_size ? "WordT" : "Word32T";
|
||||
std::string decoder =
|
||||
source_uintptr
|
||||
? (result_uintptr ? "DecodeWord" : "DecodeWord32FromWord")
|
||||
: (result_uintptr ? "DecodeWordFromWord32" : "DecodeWord32");
|
||||
struct_is_pointer_size
|
||||
? (field_is_pointer_size ? "DecodeWord" : "DecodeWord32FromWord")
|
||||
: (field_is_pointer_size ? "DecodeWordFromWord32" : "DecodeWord32");
|
||||
|
||||
decls() << " " << result_type->GetGeneratedTypeName() << " " << result_name
|
||||
decls() << " " << field_type->GetGeneratedTypeName() << " " << result_name
|
||||
<< ";\n";
|
||||
|
||||
if (smi_tagged_type) {
|
||||
// If the container is a SMI, then UncheckedCast is insufficient and we must
|
||||
// use a bit cast.
|
||||
bit_field_struct =
|
||||
"ca_.BitcastTaggedToWordForTagAndSmiBits(" + bit_field_struct + ")";
|
||||
}
|
||||
|
||||
out() << " " << result_name << " = ca_.UncheckedCast<"
|
||||
<< result_type->GetGeneratedTNodeTypeName()
|
||||
<< field_type->GetGeneratedTNodeTypeName()
|
||||
<< ">(CodeStubAssembler(state_)." << decoder << "<"
|
||||
<< GetBitFieldSpecialization(source_type, instruction.bit_field)
|
||||
<< ">(ca_.UncheckedCast<" << source_word_type << ">("
|
||||
<< GetBitFieldSpecialization(struct_type, instruction.bit_field)
|
||||
<< ">(ca_.UncheckedCast<" << struct_word_type << ">("
|
||||
<< bit_field_struct << ")));\n";
|
||||
}
|
||||
|
||||
@ -906,25 +927,46 @@ void CSAGenerator::EmitInstruction(const StoreBitFieldInstruction& instruction,
|
||||
std::string bit_field_struct = stack->Pop();
|
||||
stack->Push(result_name);
|
||||
|
||||
const BitFieldStructType* struct_type = instruction.bit_field_struct_type;
|
||||
const Type* struct_type = instruction.bit_field_struct_type;
|
||||
const Type* field_type = instruction.bit_field.name_and_type.type;
|
||||
bool struct_uintptr = struct_type->IsSubtypeOf(TypeOracle::GetUIntPtrType());
|
||||
bool field_uintptr = field_type->IsSubtypeOf(TypeOracle::GetUIntPtrType());
|
||||
std::string struct_word_type = struct_uintptr ? "WordT" : "Word32T";
|
||||
std::string field_word_type = field_uintptr ? "UintPtrT" : "Uint32T";
|
||||
auto smi_tagged_type =
|
||||
Type::MatchUnaryGeneric(struct_type, TypeOracle::GetSmiTaggedGeneric());
|
||||
bool struct_is_pointer_size =
|
||||
IsPointerSizeIntegralType(struct_type) || smi_tagged_type;
|
||||
DCHECK_IMPLIES(!struct_is_pointer_size, Is32BitIntegralType(struct_type));
|
||||
bool field_is_pointer_size = IsPointerSizeIntegralType(field_type);
|
||||
DCHECK_IMPLIES(!field_is_pointer_size, Is32BitIntegralType(field_type));
|
||||
std::string struct_word_type = struct_is_pointer_size ? "WordT" : "Word32T";
|
||||
std::string field_word_type = field_is_pointer_size ? "UintPtrT" : "Uint32T";
|
||||
std::string encoder =
|
||||
struct_uintptr ? (field_uintptr ? "UpdateWord" : "UpdateWord32InWord")
|
||||
: (field_uintptr ? "UpdateWordInWord32" : "UpdateWord32");
|
||||
struct_is_pointer_size
|
||||
? (field_is_pointer_size ? "UpdateWord" : "UpdateWord32InWord")
|
||||
: (field_is_pointer_size ? "UpdateWordInWord32" : "UpdateWord32");
|
||||
|
||||
decls() << " " << struct_type->GetGeneratedTypeName() << " " << result_name
|
||||
<< ";\n";
|
||||
|
||||
if (smi_tagged_type) {
|
||||
// If the container is a SMI, then UncheckedCast is insufficient and we must
|
||||
// use a bit cast.
|
||||
bit_field_struct =
|
||||
"ca_.BitcastTaggedToWordForTagAndSmiBits(" + bit_field_struct + ")";
|
||||
}
|
||||
|
||||
std::string result_expression =
|
||||
"CodeStubAssembler(state_)." + encoder + "<" +
|
||||
GetBitFieldSpecialization(struct_type, instruction.bit_field) +
|
||||
">(ca_.UncheckedCast<" + struct_word_type + ">(" + bit_field_struct +
|
||||
"), ca_.UncheckedCast<" + field_word_type + ">(" + value + "))";
|
||||
|
||||
if (smi_tagged_type) {
|
||||
result_expression =
|
||||
"ca_.BitcastWordToTaggedSigned(" + result_expression + ")";
|
||||
}
|
||||
|
||||
out() << " " << result_name << " = ca_.UncheckedCast<"
|
||||
<< struct_type->GetGeneratedTNodeTypeName()
|
||||
<< ">(CodeStubAssembler(state_)." << encoder << "<"
|
||||
<< GetBitFieldSpecialization(struct_type, instruction.bit_field)
|
||||
<< ">(ca_.UncheckedCast<" << struct_word_type << ">("
|
||||
<< bit_field_struct << "), ca_.UncheckedCast<" << field_word_type
|
||||
<< ">(" << value << ")));\n";
|
||||
<< struct_type->GetGeneratedTNodeTypeName() << ">(" << result_expression
|
||||
<< ");\n";
|
||||
}
|
||||
|
||||
// static
|
||||
|
@ -24,7 +24,10 @@ GlobalContext::GlobalContext(Ast ast)
|
||||
|
||||
TargetArchitecture::TargetArchitecture(bool force_32bit)
|
||||
: tagged_size_(force_32bit ? sizeof(int32_t) : kTaggedSize),
|
||||
raw_ptr_size_(force_32bit ? sizeof(int32_t) : kSystemPointerSize) {}
|
||||
raw_ptr_size_(force_32bit ? sizeof(int32_t) : kSystemPointerSize),
|
||||
smi_tag_and_shift_size_(
|
||||
kSmiTagSize + (force_32bit ? SmiTagging<kApiInt32Size>::kSmiShiftSize
|
||||
: kSmiShiftSize)) {}
|
||||
|
||||
} // namespace torque
|
||||
} // namespace internal
|
||||
|
@ -93,10 +93,12 @@ class TargetArchitecture : public ContextualClass<TargetArchitecture> {
|
||||
static size_t RawPtrSize() { return Get().raw_ptr_size_; }
|
||||
static size_t MaxHeapAlignment() { return TaggedSize(); }
|
||||
static bool ArePointersCompressed() { return TaggedSize() < RawPtrSize(); }
|
||||
static int SmiTagAndShiftSize() { return Get().smi_tag_and_shift_size_; }
|
||||
|
||||
private:
|
||||
const size_t tagged_size_;
|
||||
const size_t raw_ptr_size_;
|
||||
const int smi_tag_and_shift_size_;
|
||||
};
|
||||
|
||||
} // namespace torque
|
||||
|
@ -2002,6 +2002,20 @@ LocationReference ImplementationVisitor::GenerateFieldAccess(
|
||||
const BitField& field = bitfield_struct->LookupField(fieldname);
|
||||
return LocationReference::BitFieldAccess(reference, field);
|
||||
}
|
||||
if (const auto type_wrapped_in_smi = Type::MatchUnaryGeneric(
|
||||
reference.ReferencedType(), TypeOracle::GetSmiTaggedGeneric())) {
|
||||
const BitFieldStructType* bitfield_struct =
|
||||
BitFieldStructType::DynamicCast(*type_wrapped_in_smi);
|
||||
if (bitfield_struct == nullptr) {
|
||||
ReportError(
|
||||
"When a value of type SmiTagged<T> is used in a field access "
|
||||
"expression, T is expected to be a bitfield struct type. Instead, T "
|
||||
"is ",
|
||||
**type_wrapped_in_smi);
|
||||
}
|
||||
const BitField& field = bitfield_struct->LookupField(fieldname);
|
||||
return LocationReference::BitFieldAccess(reference, field);
|
||||
}
|
||||
if (reference.IsHeapReference()) {
|
||||
VisitResult ref = reference.heap_reference();
|
||||
bool is_const;
|
||||
@ -2190,9 +2204,8 @@ VisitResult ImplementationVisitor::GenerateFetchFromLocation(
|
||||
// First fetch the bitfield struct, then get the bits out of it.
|
||||
VisitResult bit_field_struct =
|
||||
GenerateFetchFromLocation(reference.bit_field_struct_location());
|
||||
assembler().Emit(LoadBitFieldInstruction{
|
||||
BitFieldStructType::cast(bit_field_struct.type()),
|
||||
reference.bit_field()});
|
||||
assembler().Emit(LoadBitFieldInstruction{bit_field_struct.type(),
|
||||
reference.bit_field()});
|
||||
return VisitResult(reference.ReferencedType(), assembler().TopRange(1));
|
||||
} else {
|
||||
if (reference.IsHeapSlice()) {
|
||||
@ -2271,9 +2284,8 @@ void ImplementationVisitor::GenerateAssignToLocation(
|
||||
GenerateImplicitConvert(reference.ReferencedType(), assignment_value);
|
||||
GenerateCopy(bit_field_struct);
|
||||
GenerateCopy(converted_value);
|
||||
assembler().Emit(StoreBitFieldInstruction{
|
||||
BitFieldStructType::cast(bit_field_struct.type()),
|
||||
reference.bit_field()});
|
||||
assembler().Emit(StoreBitFieldInstruction{bit_field_struct.type(),
|
||||
reference.bit_field()});
|
||||
GenerateAssignToLocation(
|
||||
reference.bit_field_struct_location(),
|
||||
VisitResult(bit_field_struct.type(), assembler().TopRange(1)));
|
||||
|
@ -349,14 +349,13 @@ struct StoreReferenceInstruction : InstructionBase {
|
||||
// Pops a bitfield struct; pushes a bitfield value extracted from it.
|
||||
struct LoadBitFieldInstruction : InstructionBase {
|
||||
TORQUE_INSTRUCTION_BOILERPLATE()
|
||||
LoadBitFieldInstruction(const BitFieldStructType* bit_field_struct_type,
|
||||
BitField bit_field)
|
||||
LoadBitFieldInstruction(const Type* bit_field_struct_type, BitField bit_field)
|
||||
: bit_field_struct_type(bit_field_struct_type),
|
||||
bit_field(std::move(bit_field)) {}
|
||||
|
||||
DefinitionLocation GetValueDefinition() const;
|
||||
|
||||
const BitFieldStructType* bit_field_struct_type;
|
||||
const Type* bit_field_struct_type;
|
||||
BitField bit_field;
|
||||
};
|
||||
|
||||
@ -364,14 +363,14 @@ struct LoadBitFieldInstruction : InstructionBase {
|
||||
// containing the updated value.
|
||||
struct StoreBitFieldInstruction : InstructionBase {
|
||||
TORQUE_INSTRUCTION_BOILERPLATE()
|
||||
StoreBitFieldInstruction(const BitFieldStructType* bit_field_struct_type,
|
||||
StoreBitFieldInstruction(const Type* bit_field_struct_type,
|
||||
BitField bit_field)
|
||||
: bit_field_struct_type(bit_field_struct_type),
|
||||
bit_field(std::move(bit_field)) {}
|
||||
|
||||
DefinitionLocation GetValueDefinition() const;
|
||||
|
||||
const BitFieldStructType* bit_field_struct_type;
|
||||
const Type* bit_field_struct_type;
|
||||
BitField bit_field;
|
||||
};
|
||||
|
||||
|
@ -928,10 +928,17 @@ bool IsAllowedAsBitField(const Type* type) {
|
||||
// Any integer-ish type, including bools and enums which inherit from integer
|
||||
// types, are allowed. Note, however, that we always zero-extend during
|
||||
// decoding regardless of signedness.
|
||||
return IsPointerSizeIntegralType(type) || Is32BitIntegralType(type);
|
||||
}
|
||||
|
||||
bool IsPointerSizeIntegralType(const Type* type) {
|
||||
return type->IsSubtypeOf(TypeOracle::GetUIntPtrType()) ||
|
||||
type->IsSubtypeOf(TypeOracle::GetIntPtrType());
|
||||
}
|
||||
|
||||
bool Is32BitIntegralType(const Type* type) {
|
||||
return type->IsSubtypeOf(TypeOracle::GetUint32Type()) ||
|
||||
type->IsSubtypeOf(TypeOracle::GetUIntPtrType()) ||
|
||||
type->IsSubtypeOf(TypeOracle::GetInt32Type()) ||
|
||||
type->IsSubtypeOf(TypeOracle::GetIntPtrType()) ||
|
||||
type->IsSubtypeOf(TypeOracle::GetBoolType());
|
||||
}
|
||||
|
||||
|
@ -816,6 +816,8 @@ TypeVector LowerParameterTypes(const ParameterTypes& parameter_types,
|
||||
base::Optional<std::tuple<size_t, std::string>> SizeOf(const Type* type);
|
||||
bool IsAnyUnsignedInteger(const Type* type);
|
||||
bool IsAllowedAsBitField(const Type* type);
|
||||
bool IsPointerSizeIntegralType(const Type* type);
|
||||
bool Is32BitIntegralType(const Type* type);
|
||||
|
||||
base::Optional<NameAndType> ExtractSimpleFieldArraySize(
|
||||
const ClassType& class_type, Expression* array_size);
|
||||
|
@ -36,7 +36,7 @@ TestNoMatch('a', 'b');
|
||||
|
||||
|
||||
function TestGlobalRegex(regex_or_string) {
|
||||
const iter = 'ab'.matchAll(/./g);
|
||||
const iter = 'ab'.matchAll(regex_or_string);
|
||||
let next_result = iter.next();
|
||||
assertEquals(['a'], next_result.value);
|
||||
assertFalse(next_result.done);
|
||||
|
@ -763,6 +763,44 @@ TEST_F(MachineOperatorReducerTest, Word32AndWithComparisonAndConstantOne) {
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Word32Or
|
||||
|
||||
TEST_F(MachineOperatorReducerTest, Word32OrWithWord32And) {
|
||||
Node* const p0 = Parameter(0);
|
||||
TRACED_FOREACH(int32_t, m, kUint32Values) {
|
||||
TRACED_FOREACH(int32_t, rhs, kUint32Values) {
|
||||
// To get better coverage of interesting cases, run this test twice:
|
||||
// once with the mask from kUint32Values, and once with its inverse.
|
||||
for (int32_t mask : {m, ~m}) {
|
||||
Reduction const r = Reduce(graph()->NewNode(
|
||||
machine()->Word32Or(),
|
||||
graph()->NewNode(machine()->Word32And(), p0, Int32Constant(mask)),
|
||||
Int32Constant(rhs)));
|
||||
switch (rhs) {
|
||||
case 0: // x | 0 => x
|
||||
ASSERT_TRUE(r.Changed());
|
||||
EXPECT_THAT(r.replacement(),
|
||||
IsWord32And(p0, IsInt32Constant(mask)));
|
||||
break;
|
||||
case -1: // x | -1 => -1
|
||||
ASSERT_TRUE(r.Changed());
|
||||
EXPECT_THAT(r.replacement(), IsInt32Constant(-1));
|
||||
break;
|
||||
default: // (x & K1) | K2 => x | K2, if K1 | K2 == -1
|
||||
if ((mask | rhs) == -1) {
|
||||
ASSERT_TRUE(r.Changed());
|
||||
EXPECT_THAT(r.replacement(),
|
||||
IsWord32Or(p0, IsInt32Constant(rhs)));
|
||||
} else {
|
||||
ASSERT_TRUE(!r.Changed());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Word32Xor
|
||||
|
Loading…
Reference in New Issue
Block a user