[maglev] Support Load/StoreDataView for Float64

Bug: v8:7700
Change-Id: Ie6239a5ed51c294252ae980de1b0eccbea1b233a
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4017832
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Auto-Submit: Victor Gomes <victorgomes@chromium.org>
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/main@{#84173}
This commit is contained in:
Victor Gomes 2022-11-10 11:43:17 +01:00 committed by V8 LUCI CQ
parent 1a935b3c28
commit adc29dd514
7 changed files with 406 additions and 1 deletions

View File

@ -93,6 +93,15 @@ inline void MaglevAssembler::DefineExceptionHandlerAndLazyDeoptPoint(
DefineLazyDeoptPoint(node->lazy_deopt_info());
}
inline void MaglevAssembler::LoadBoundedSizeFromObject(Register result,
Register object,
int offset) {
movq(result, FieldOperand(object, offset));
#ifdef V8_ENABLE_SANDBOX
shrq(result, Immediate(kBoundedSizeShift));
#endif // V8_ENABLE_SANDBOX
}
// ---
// Deferred code handling.
// ---

View File

@ -78,6 +78,9 @@ class MaglevAssembler : public MacroAssembler {
inline void PushInput(const Input& input);
inline Register FromAnyToRegister(const Input& input, Register scratch);
inline void LoadBoundedSizeFromObject(Register result, Register object,
int offset);
// Warning: Input registers {string} and {index} will be scratched.
// {result} is allowed to alias with one the other 3 input registers.
// {result} is an int32.

View File

@ -2605,6 +2605,64 @@ void MaglevGraphBuilder::InlineCallFromRegisters(
->SetToBlockAndReturnNext(current_block_);
}
bool MaglevGraphBuilder::TryReduceDataViewPrototypeGetFloat64(
compiler::JSFunctionRef target, const CallArguments& args) {
if (!broker()->dependencies()->DependOnArrayBufferDetachingProtector()) {
// TODO(victorgomes): Add checks whether the array has been detached.
return false;
}
// TODO(victorgomes): Add data view to known types.
ValueNode* receiver = GetTaggedReceiver(args);
AddNewNode<CheckInstanceType>({receiver}, CheckType::kCheckHeapObject,
JS_DATA_VIEW_TYPE);
auto offset_register = args.maybe_at(0);
ValueNode* offset = offset_register ? GetInt32ElementIndex(*offset_register)
: GetInt32Constant(0);
AddNewNode<CheckJSDataViewBounds>({receiver, offset},
ExternalArrayType::kExternalFloat64Array);
auto is_little_endian_offset = args.maybe_at(1);
ValueNode* is_little_endian = is_little_endian_offset
? GetTaggedValue(*is_little_endian_offset)
: GetBooleanConstant(false);
SetAccumulator(AddNewNode<LoadDoubleDataViewElement>(
{receiver, offset, is_little_endian}));
return true;
}
bool MaglevGraphBuilder::TryReduceDataViewPrototypeSetFloat64(
compiler::JSFunctionRef target, const CallArguments& args) {
if (!broker()->dependencies()->DependOnArrayBufferDetachingProtector()) {
// TODO(victorgomes): Add checks whether the array has been detached.
return false;
}
// TODO(victorgomes): Add data view to known types.
ValueNode* receiver = GetTaggedReceiver(args);
AddNewNode<CheckInstanceType>({receiver}, CheckType::kCheckHeapObject,
JS_DATA_VIEW_TYPE);
auto offset_register = args.maybe_at(0);
ValueNode* offset = offset_register ? GetInt32ElementIndex(*offset_register)
: GetInt32Constant(0);
AddNewNode<CheckJSDataViewBounds>({receiver, offset},
ExternalArrayType::kExternalFloat64Array);
auto value_register = args.maybe_at(1);
ValueNode* value =
value_register ? GetFloat64(*value_register) : GetFloat64Constant(0);
auto is_little_endian_register = args.maybe_at(2);
ValueNode* is_little_endian = is_little_endian_register
? GetTaggedValue(*is_little_endian_register)
: GetBooleanConstant(false);
AddNewNode<StoreDoubleDataViewElement>(
{receiver, offset, value, is_little_endian});
return true;
}
bool MaglevGraphBuilder::TryReduceStringFromCharCode(
compiler::JSFunctionRef target, const CallArguments& args) {
if (args.count() != 1) return false;

View File

@ -1045,7 +1045,7 @@ class MaglevGraphBuilder {
int count_with_receiver() const { return count() + 1; }
const interpreter::Register operator[](size_t i) const {
interpreter::Register at(size_t i) const {
if (receiver_mode_ != ConvertReceiverMode::kNullOrUndefined) {
i++;
}
@ -1057,6 +1057,13 @@ class MaglevGraphBuilder {
return reglist_[i];
}
interpreter::Register operator[](size_t i) const { return at(i); }
base::Optional<interpreter::Register> maybe_at(int i) const {
if (i < count()) return at(i);
return {};
}
ConvertReceiverMode receiver_mode() const { return receiver_mode_; }
CallArguments PopReceiver(ConvertReceiverMode new_receiver_mode) const {
@ -1098,6 +1105,8 @@ class MaglevGraphBuilder {
const CallArguments& args);
#define MAGLEV_REDUCED_BUILTIN(V) \
V(DataViewPrototypeGetFloat64) \
V(DataViewPrototypeSetFloat64) \
V(FunctionPrototypeCall) \
V(StringFromCharCode) \
V(StringPrototypeCharCodeAt)

View File

@ -122,6 +122,7 @@ class MaglevGraphVerifier {
case Opcode::kCheckNumber:
case Opcode::kCheckString:
case Opcode::kCheckSymbol:
case Opcode::kCheckInstanceType:
case Opcode::kCheckedInternalizedString:
case Opcode::kCheckedObjectToIndex:
case Opcode::kConvertReceiver:
@ -288,6 +289,7 @@ class MaglevGraphVerifier {
}
break;
case Opcode::kCheckJSArrayBounds:
case Opcode::kCheckJSDataViewBounds:
case Opcode::kCheckJSObjectElementsBounds:
case Opcode::kLoadTaggedElement:
case Opcode::kLoadDoubleElement:
@ -297,6 +299,19 @@ class MaglevGraphVerifier {
CheckValueInputIs(node, 0, ValueRepresentation::kTagged);
CheckValueInputIs(node, 1, ValueRepresentation::kInt32);
break;
case Opcode::kLoadDoubleDataViewElement:
DCHECK_EQ(node->input_count(), 3);
CheckValueInputIs(node, 0, ValueRepresentation::kTagged);
CheckValueInputIs(node, 1, ValueRepresentation::kInt32);
CheckValueInputIs(node, 2, ValueRepresentation::kTagged);
break;
case Opcode::kStoreDoubleDataViewElement:
DCHECK_EQ(node->input_count(), 4);
CheckValueInputIs(node, 0, ValueRepresentation::kTagged);
CheckValueInputIs(node, 1, ValueRepresentation::kInt32);
CheckValueInputIs(node, 2, ValueRepresentation::kFloat64);
CheckValueInputIs(node, 3, ValueRepresentation::kTagged);
break;
case Opcode::kCallBuiltin: {
CallBuiltin* call_builtin = node->Cast<CallBuiltin>();
auto descriptor =

View File

@ -29,6 +29,7 @@
#include "src/maglev/maglev-ir-inl.h"
#include "src/maglev/maglev-vreg-allocator.h"
#include "src/objects/instance-type.h"
#include "src/objects/js-array-buffer.h"
namespace v8 {
namespace internal {
@ -1224,6 +1225,27 @@ void CheckSymbol::GenerateCode(MaglevAssembler* masm,
void CheckSymbol::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {}
void CheckInstanceType::AllocateVreg(MaglevVregAllocationState* vreg_state) {
UseRegister(receiver_input());
}
void CheckInstanceType::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register object = ToRegister(receiver_input());
if (check_type_ == CheckType::kOmitHeapObjectCheck) {
__ AssertNotSmi(object);
} else {
Condition is_smi = __ CheckSmi(object);
__ EmitEagerDeoptIf(is_smi, DeoptimizeReason::kWrongInstanceType, this);
}
__ LoadMap(kScratchRegister, object);
__ CmpInstanceType(kScratchRegister, instance_type());
__ EmitEagerDeoptIf(not_equal, DeoptimizeReason::kWrongInstanceType, this);
}
void CheckInstanceType::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(" << instance_type() << ")";
}
void CheckString::AllocateVreg(MaglevVregAllocationState* vreg_state) {
UseRegister(receiver_input());
}
@ -1396,6 +1418,47 @@ void CheckJSArrayBounds::GenerateCode(MaglevAssembler* masm,
__ EmitEagerDeoptIf(above_equal, DeoptimizeReason::kOutOfBounds, this);
}
namespace {
int ExternalArrayElementSize(const ExternalArrayType element_type) {
switch (element_type) {
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
case kExternal##Type##Array: \
DCHECK_LE(sizeof(ctype), 8); \
return sizeof(ctype);
TYPED_ARRAYS(TYPED_ARRAY_CASE)
default:
UNREACHABLE();
#undef TYPED_ARRAY_CASE
}
}
} // namespace
void CheckJSDataViewBounds::AllocateVreg(
MaglevVregAllocationState* vreg_state) {
UseRegister(receiver_input());
UseRegister(index_input());
}
void CheckJSDataViewBounds::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register object = ToRegister(receiver_input());
Register index = ToRegister(index_input());
Register byte_length = kScratchRegister;
if (v8_flags.debug_code) {
__ AssertNotSmi(object);
__ CmpObjectType(object, JS_DATA_VIEW_TYPE, kScratchRegister);
__ Assert(equal, AbortReason::kUnexpectedValue);
}
__ LoadBoundedSizeFromObject(byte_length, object,
JSDataView::kRawByteLengthOffset);
int element_size = ExternalArrayElementSize(element_type_);
if (element_size > 1) {
__ subq(byte_length, Immediate(element_size - 1));
__ EmitEagerDeoptIf(negative, DeoptimizeReason::kOutOfBounds, this);
}
__ cmpl(index, byte_length);
__ EmitEagerDeoptIf(above_equal, DeoptimizeReason::kOutOfBounds, this);
}
void CheckJSObjectElementsBounds::AllocateVreg(
MaglevVregAllocationState* vreg_state) {
UseRegister(receiver_input());
@ -1719,6 +1782,149 @@ void LoadDoubleElement::GenerateCode(MaglevAssembler* masm,
FixedDoubleArray::kHeaderSize));
}
namespace {
bool FromConstantToBool(MaglevAssembler* masm, ValueNode* node) {
DCHECK(IsConstantNode(node->opcode()));
LocalIsolate* local_isolate = masm->isolate()->AsLocalIsolate();
switch (node->opcode()) {
#define CASE(Name) \
case Opcode::k##Name: { \
return node->Cast<Name>()->ToBoolean(local_isolate); \
}
CONSTANT_VALUE_NODE_LIST(CASE)
#undef CASE
default:
UNREACHABLE();
}
}
} // namespace
void LoadDoubleDataViewElement::AllocateVreg(
MaglevVregAllocationState* vreg_state) {
UseRegister(object_input());
UseRegister(index_input());
if (is_little_endian_constant()) {
UseAny(is_little_endian_input());
} else {
UseRegister(is_little_endian_input());
}
set_temporaries_needed(1);
DefineAsRegister(vreg_state, this);
}
void LoadDoubleDataViewElement::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register object = ToRegister(object_input());
Register index = ToRegister(index_input());
DoubleRegister result_reg = ToDoubleRegister(result());
Register data_pointer = general_temporaries().PopFirst();
__ AssertNotSmi(object);
if (v8_flags.debug_code) {
__ CmpObjectType(object, JS_DATA_VIEW_TYPE, kScratchRegister);
__ Assert(above_equal, AbortReason::kUnexpectedValue);
}
// Load data pointer.
#ifdef V8_ENABLE_SANDBOX
__ LoadSandboxedPointerField(
data_pointer, FieldOperand(object, JSDataView::kDataPointerOffset));
#else
__ movq(data_pointer, FieldOperand(object, JSDataView::kDataPointerOffset));
#endif
if (is_little_endian_constant()) {
if (FromConstantToBool(masm, is_little_endian_input().node())) {
__ Movsd(result_reg, Operand(data_pointer, index, times_8, 0));
} else {
__ movq(kScratchRegister, Operand(data_pointer, index, times_8, 0));
__ bswapq(kScratchRegister);
__ Movq(result_reg, kScratchRegister);
}
} else {
Label done;
ZoneLabelRef is_little_endian(masm), is_big_endian(masm);
// TODO(leszeks): We're likely to be calling this on an existing boolean --
// maybe that's a case we should fast-path here and re-use that boolean
// value?
__ ToBoolean(ToRegister(is_little_endian_input()), is_little_endian,
is_big_endian, true);
// x64 is little endian.
static_assert(V8_TARGET_LITTLE_ENDIAN == 1);
__ bind(*is_little_endian);
__ Movsd(result_reg, Operand(data_pointer, index, times_8, 0));
__ jmp(&done);
// We should swap the bytes if big endian.
__ bind(*is_big_endian);
__ movq(kScratchRegister, Operand(data_pointer, index, times_8, 0));
__ bswapq(kScratchRegister);
__ Movq(result_reg, kScratchRegister);
__ bind(&done);
}
}
void StoreDoubleDataViewElement::AllocateVreg(
MaglevVregAllocationState* vreg_state) {
UseRegister(object_input());
UseRegister(index_input());
UseRegister(value_input());
if (is_little_endian_constant()) {
UseAny(is_little_endian_input());
} else {
UseRegister(is_little_endian_input());
}
set_temporaries_needed(1);
}
void StoreDoubleDataViewElement::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register object = ToRegister(object_input());
Register index = ToRegister(index_input());
DoubleRegister value = ToDoubleRegister(value_input());
Register data_pointer = general_temporaries().PopFirst();
__ AssertNotSmi(object);
if (v8_flags.debug_code) {
__ CmpObjectType(object, JS_DATA_VIEW_TYPE, kScratchRegister);
__ Assert(above_equal, AbortReason::kUnexpectedValue);
}
// Load data pointer.
#ifdef V8_ENABLE_SANDBOX
__ LoadSandboxedPointerField(
data_pointer, FieldOperand(object, JSDataView::kDataPointerOffset));
#else
__ movq(data_pointer, FieldOperand(object, JSDataView::kDataPointerOffset));
#endif
if (is_little_endian_constant()) {
if (FromConstantToBool(masm, is_little_endian_input().node())) {
__ Movsd(Operand(data_pointer, index, times_8, 0), value);
} else {
__ Movq(kScratchRegister, value);
__ bswapq(kScratchRegister);
__ movq(Operand(data_pointer, index, times_8, 0), kScratchRegister);
}
} else {
Label done;
ZoneLabelRef is_little_endian(masm), is_big_endian(masm);
// TODO(leszeks): We're likely to be calling this on an existing boolean --
// maybe that's a case we should fast-path here and re-use that boolean
// value?
__ ToBoolean(ToRegister(is_little_endian_input()), is_little_endian,
is_big_endian, true);
// x64 is little endian.
static_assert(V8_TARGET_LITTLE_ENDIAN == 1);
__ bind(*is_little_endian);
__ Movsd(Operand(data_pointer, index, times_8, 0), value);
__ jmp(&done);
// We should swap the bytes if big endian.
__ bind(*is_big_endian);
__ Movq(kScratchRegister, value);
__ bswapq(kScratchRegister);
__ movq(Operand(data_pointer, index, times_8, 0), kScratchRegister);
__ bind(&done);
}
}
void StoreDoubleField::AllocateVreg(MaglevVregAllocationState* vreg_state) {
UseRegister(object_input());
UseRegister(value_input());

View File

@ -154,6 +154,7 @@ class CompactInterpreterFrameState;
V(LoadTaggedField) \
V(LoadDoubleField) \
V(LoadTaggedElement) \
V(LoadDoubleDataViewElement) \
V(LoadDoubleElement) \
V(LoadGlobal) \
V(LoadNamedGeneric) \
@ -208,6 +209,7 @@ class CompactInterpreterFrameState;
V(CheckInt32Condition) \
V(CheckJSArrayBounds) \
V(CheckJSObjectElementsBounds) \
V(CheckJSDataViewBounds) \
V(CheckMaps) \
V(CheckMapsWithMigration) \
V(CheckNumber) \
@ -215,11 +217,13 @@ class CompactInterpreterFrameState;
V(CheckString) \
V(CheckSymbol) \
V(CheckValue) \
V(CheckInstanceType) \
V(DebugBreak) \
V(GeneratorStore) \
V(JumpLoopPrologue) \
V(StoreMap) \
V(StoreDoubleField) \
V(StoreDoubleDataViewElement) \
V(StoreTaggedFieldNoWriteBarrier) \
V(StoreTaggedFieldWithWriteBarrier) \
V(IncreaseInterruptBudget) \
@ -3011,6 +3015,32 @@ class CheckSymbol : public FixedInputNodeT<1, CheckSymbol> {
const CheckType check_type_;
};
class CheckInstanceType : public FixedInputNodeT<1, CheckInstanceType> {
using Base = FixedInputNodeT<1, CheckInstanceType>;
public:
explicit CheckInstanceType(uint64_t bitfield, CheckType check_type,
InstanceType instance_type)
: Base(bitfield),
check_type_(check_type),
instance_type_(instance_type) {}
static constexpr OpProperties kProperties = OpProperties::EagerDeopt();
static constexpr int kReceiverIndex = 0;
Input& receiver_input() { return input(kReceiverIndex); }
InstanceType instance_type() const { return instance_type_; }
void AllocateVreg(MaglevVregAllocationState*);
void GenerateCode(MaglevAssembler*, const ProcessingState&);
void PrintParams(std::ostream&, MaglevGraphLabeller*) const;
private:
const CheckType check_type_;
const InstanceType instance_type_;
};
class CheckString : public FixedInputNodeT<1, CheckString> {
using Base = FixedInputNodeT<1, CheckString>;
@ -3063,6 +3093,25 @@ class CheckJSArrayBounds : public FixedInputNodeT<2, CheckJSArrayBounds> {
public:
explicit CheckJSArrayBounds(uint64_t bitfield) : Base(bitfield) {}
static constexpr OpProperties kProperties = OpProperties::EagerDeopt();
static constexpr int kReceiverIndex = 0;
static constexpr int kIndexIndex = 1;
Input& receiver_input() { return input(kReceiverIndex); }
Input& index_input() { return input(kIndexIndex); }
void AllocateVreg(MaglevVregAllocationState*);
void GenerateCode(MaglevAssembler*, const ProcessingState&);
void PrintParams(std::ostream&, MaglevGraphLabeller*) const {}
};
class CheckJSDataViewBounds : public FixedInputNodeT<2, CheckJSDataViewBounds> {
using Base = FixedInputNodeT<2, CheckJSDataViewBounds>;
public:
explicit CheckJSDataViewBounds(uint64_t bitfield,
ExternalArrayType element_type)
: Base(bitfield), element_type_(element_type) {}
static constexpr OpProperties kProperties = OpProperties::EagerDeopt();
@ -3074,6 +3123,9 @@ class CheckJSArrayBounds : public FixedInputNodeT<2, CheckJSArrayBounds> {
void AllocateVreg(MaglevVregAllocationState*);
void GenerateCode(MaglevAssembler*, const ProcessingState&);
void PrintParams(std::ostream&, MaglevGraphLabeller*) const {}
private:
ExternalArrayType element_type_;
};
class CheckInt32Condition : public FixedInputNodeT<2, CheckInt32Condition> {
@ -3326,6 +3378,59 @@ class LoadDoubleElement : public FixedInputValueNodeT<2, LoadDoubleElement> {
void PrintParams(std::ostream&, MaglevGraphLabeller*) const {}
};
class LoadDoubleDataViewElement
: public FixedInputValueNodeT<3, LoadDoubleDataViewElement> {
using Base = FixedInputValueNodeT<3, LoadDoubleDataViewElement>;
public:
explicit LoadDoubleDataViewElement(uint64_t bitfield) : Base(bitfield) {}
static constexpr OpProperties kProperties =
OpProperties::Reading() | OpProperties::Float64();
static constexpr int kObjectIndex = 0;
static constexpr int kIndexIndex = 1;
static constexpr int kIsLittleEndianIndex = 2;
Input& object_input() { return input(kObjectIndex); }
Input& index_input() { return input(kIndexIndex); }
Input& is_little_endian_input() { return input(kIsLittleEndianIndex); }
bool is_little_endian_constant() {
return IsConstantNode(is_little_endian_input().node()->opcode());
}
void AllocateVreg(MaglevVregAllocationState*);
void GenerateCode(MaglevAssembler*, const ProcessingState&);
void PrintParams(std::ostream&, MaglevGraphLabeller*) const {}
};
class StoreDoubleDataViewElement
: public FixedInputNodeT<4, StoreDoubleDataViewElement> {
using Base = FixedInputNodeT<4, StoreDoubleDataViewElement>;
public:
explicit StoreDoubleDataViewElement(uint64_t bitfield) : Base(bitfield) {}
static constexpr OpProperties kProperties = OpProperties::Writing();
static constexpr int kObjectIndex = 0;
static constexpr int kIndexIndex = 1;
static constexpr int kValueIndex = 2;
static constexpr int kIsLittleEndianIndex = 3;
Input& object_input() { return input(kObjectIndex); }
Input& index_input() { return input(kIndexIndex); }
Input& value_input() { return input(kValueIndex); }
Input& is_little_endian_input() { return input(kIsLittleEndianIndex); }
bool is_little_endian_constant() {
return IsConstantNode(is_little_endian_input().node()->opcode());
}
void AllocateVreg(MaglevVregAllocationState*);
void GenerateCode(MaglevAssembler*, const ProcessingState&);
void PrintParams(std::ostream&, MaglevGraphLabeller*) const {}
};
class StoreDoubleField : public FixedInputNodeT<2, StoreDoubleField> {
using Base = FixedInputNodeT<2, StoreDoubleField>;