[atomics] Implement 64-bit Atomics operations
Atomics.{load,store,add,sub,and,or,xor,exchange,compareExchange} are updated to support BigInt and BigInt64Array/BigUint64Array inputs. Atomics.{wait,wake,isLockFree} are left unchanged for now. Bug: v8:8100 Cq-Include-Trybots: luci.v8.try:v8_linux_noi18n_rel_ng Change-Id: I8862d7e18c58ae08784535e9c010ba94f067a0ee Reviewed-on: https://chromium-review.googlesource.com/1237294 Commit-Queue: Jakob Kummerow <jkummerow@chromium.org> Reviewed-by: Deepti Gandluri <gdeepti@chromium.org> Reviewed-by: Georg Neis <neis@chromium.org> Cr-Commit-Position: refs/heads/master@{#56228}
This commit is contained in:
parent
d34ffa6a38
commit
c2088790ae
@ -11,6 +11,8 @@ namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
using compiler::Node;
|
||||
template <typename T>
|
||||
using TNode = compiler::TNode<T>;
|
||||
|
||||
class SharedArrayBufferBuiltinsAssembler : public CodeStubAssembler {
|
||||
public:
|
||||
@ -21,7 +23,8 @@ class SharedArrayBufferBuiltinsAssembler : public CodeStubAssembler {
|
||||
protected:
|
||||
typedef Node* (CodeAssembler::*AssemblerFunction)(MachineType type,
|
||||
Node* base, Node* offset,
|
||||
Node* value);
|
||||
Node* value,
|
||||
Node* value_high);
|
||||
void ValidateSharedTypedArray(Node* tagged, Node* context,
|
||||
Node** out_instance_type,
|
||||
Node** out_backing_store);
|
||||
@ -35,6 +38,11 @@ class SharedArrayBufferBuiltinsAssembler : public CodeStubAssembler {
|
||||
void AtomicBinopBuiltinCommon(Node* array, Node* index, Node* value,
|
||||
Node* context, AssemblerFunction function,
|
||||
Runtime::FunctionId runtime_function);
|
||||
|
||||
// Create a BigInt from the result of a 64-bit atomic operation, using
|
||||
// projections on 32-bit platforms.
|
||||
TNode<BigInt> BigIntFromSigned64(Node* signed64);
|
||||
TNode<BigInt> BigIntFromUnsigned64(Node* unsigned64);
|
||||
};
|
||||
|
||||
void SharedArrayBufferBuiltinsAssembler::ValidateSharedTypedArray(
|
||||
@ -62,8 +70,13 @@ void SharedArrayBufferBuiltinsAssembler::ValidateSharedTypedArray(
|
||||
STATIC_ASSERT(FIXED_UINT8_ARRAY_TYPE < FIXED_FLOAT32_ARRAY_TYPE);
|
||||
STATIC_ASSERT(FIXED_UINT16_ARRAY_TYPE < FIXED_FLOAT32_ARRAY_TYPE);
|
||||
STATIC_ASSERT(FIXED_UINT32_ARRAY_TYPE < FIXED_FLOAT32_ARRAY_TYPE);
|
||||
Branch(Int32LessThan(elements_instance_type,
|
||||
GotoIf(Int32LessThan(elements_instance_type,
|
||||
Int32Constant(FIXED_FLOAT32_ARRAY_TYPE)),
|
||||
¬_float_or_clamped);
|
||||
STATIC_ASSERT(FIXED_BIGINT64_ARRAY_TYPE > FIXED_UINT8_CLAMPED_ARRAY_TYPE);
|
||||
STATIC_ASSERT(FIXED_BIGUINT64_ARRAY_TYPE > FIXED_UINT8_CLAMPED_ARRAY_TYPE);
|
||||
Branch(Int32GreaterThan(elements_instance_type,
|
||||
Int32Constant(FIXED_UINT8_CLAMPED_ARRAY_TYPE)),
|
||||
¬_float_or_clamped, &invalid);
|
||||
|
||||
BIND(&invalid);
|
||||
@ -80,7 +93,7 @@ void SharedArrayBufferBuiltinsAssembler::ValidateSharedTypedArray(
|
||||
*out_backing_store = IntPtrAdd(backing_store, byte_offset);
|
||||
}
|
||||
|
||||
// https://tc39.github.io/ecmascript_sharedmem/shmem.html#Atomics.ValidateAtomicAccess
|
||||
// https://tc39.github.io/ecma262/#sec-validateatomicaccess
|
||||
Node* SharedArrayBufferBuiltinsAssembler::ConvertTaggedAtomicIndexToWord32(
|
||||
Node* tagged, Node* context, Node** number_index) {
|
||||
VARIABLE(var_result, MachineRepresentation::kWord32);
|
||||
@ -130,6 +143,28 @@ void SharedArrayBufferBuiltinsAssembler::DebugSanityCheckAtomicIndex(
|
||||
}
|
||||
#endif
|
||||
|
||||
TNode<BigInt> SharedArrayBufferBuiltinsAssembler::BigIntFromSigned64(
|
||||
Node* signed64) {
|
||||
if (Is64()) {
|
||||
return BigIntFromInt64(UncheckedCast<IntPtrT>(signed64));
|
||||
} else {
|
||||
TNode<IntPtrT> low = UncheckedCast<IntPtrT>(Projection(0, signed64));
|
||||
TNode<IntPtrT> high = UncheckedCast<IntPtrT>(Projection(1, signed64));
|
||||
return BigIntFromInt32Pair(low, high);
|
||||
}
|
||||
}
|
||||
|
||||
TNode<BigInt> SharedArrayBufferBuiltinsAssembler::BigIntFromUnsigned64(
|
||||
Node* unsigned64) {
|
||||
if (Is64()) {
|
||||
return BigIntFromUint64(UncheckedCast<UintPtrT>(unsigned64));
|
||||
} else {
|
||||
TNode<UintPtrT> low = UncheckedCast<UintPtrT>(Projection(0, unsigned64));
|
||||
TNode<UintPtrT> high = UncheckedCast<UintPtrT>(Projection(1, unsigned64));
|
||||
return BigIntFromUint32Pair(low, high);
|
||||
}
|
||||
}
|
||||
|
||||
TF_BUILTIN(AtomicsLoad, SharedArrayBufferBuiltinsAssembler) {
|
||||
Node* array = Parameter(Descriptor::kArray);
|
||||
Node* index = Parameter(Descriptor::kIndex);
|
||||
@ -146,14 +181,14 @@ TF_BUILTIN(AtomicsLoad, SharedArrayBufferBuiltinsAssembler) {
|
||||
Node* index_word = ChangeUint32ToWord(index_word32);
|
||||
|
||||
Label i8(this), u8(this), i16(this), u16(this), i32(this), u32(this),
|
||||
other(this);
|
||||
i64(this), u64(this), other(this);
|
||||
int32_t case_values[] = {
|
||||
FIXED_INT8_ARRAY_TYPE, FIXED_UINT8_ARRAY_TYPE, FIXED_INT16_ARRAY_TYPE,
|
||||
FIXED_UINT16_ARRAY_TYPE, FIXED_INT32_ARRAY_TYPE, FIXED_UINT32_ARRAY_TYPE,
|
||||
};
|
||||
Label* case_labels[] = {
|
||||
&i8, &u8, &i16, &u16, &i32, &u32,
|
||||
FIXED_INT8_ARRAY_TYPE, FIXED_UINT8_ARRAY_TYPE,
|
||||
FIXED_INT16_ARRAY_TYPE, FIXED_UINT16_ARRAY_TYPE,
|
||||
FIXED_INT32_ARRAY_TYPE, FIXED_UINT32_ARRAY_TYPE,
|
||||
FIXED_BIGINT64_ARRAY_TYPE, FIXED_BIGUINT64_ARRAY_TYPE,
|
||||
};
|
||||
Label* case_labels[] = {&i8, &u8, &i16, &u16, &i32, &u32, &i64, &u64};
|
||||
Switch(instance_type, &other, case_values, case_labels,
|
||||
arraysize(case_labels));
|
||||
|
||||
@ -181,6 +216,17 @@ TF_BUILTIN(AtomicsLoad, SharedArrayBufferBuiltinsAssembler) {
|
||||
Return(ChangeUint32ToTagged(AtomicLoad(MachineType::Uint32(), backing_store,
|
||||
WordShl(index_word, 2))));
|
||||
|
||||
BIND(&i64);
|
||||
// This uses Uint64() intentionally: AtomicLoad is not implemented for
|
||||
// Int64(), which is fine because the machine instruction only cares
|
||||
// about words.
|
||||
Return(BigIntFromSigned64(AtomicLoad(MachineType::Uint64(), backing_store,
|
||||
WordShl(index_word, 3))));
|
||||
|
||||
BIND(&u64);
|
||||
Return(BigIntFromUnsigned64(AtomicLoad(MachineType::Uint64(), backing_store,
|
||||
WordShl(index_word, 3))));
|
||||
|
||||
// This shouldn't happen, we've already validated the type.
|
||||
BIND(&other);
|
||||
Unreachable();
|
||||
@ -202,6 +248,13 @@ TF_BUILTIN(AtomicsStore, SharedArrayBufferBuiltinsAssembler) {
|
||||
ValidateAtomicIndex(array, index_word32, context);
|
||||
Node* index_word = ChangeUint32ToWord(index_word32);
|
||||
|
||||
Label u8(this), u16(this), u32(this), u64(this), other(this);
|
||||
STATIC_ASSERT(FIXED_BIGINT64_ARRAY_TYPE > FIXED_UINT32_ARRAY_TYPE);
|
||||
STATIC_ASSERT(FIXED_BIGUINT64_ARRAY_TYPE > FIXED_UINT32_ARRAY_TYPE);
|
||||
GotoIf(
|
||||
Int32GreaterThan(instance_type, Int32Constant(FIXED_UINT32_ARRAY_TYPE)),
|
||||
&u64);
|
||||
|
||||
Node* value_integer = ToInteger_Inline(CAST(context), CAST(value));
|
||||
Node* value_word32 = TruncateTaggedToWord32(context, value_integer);
|
||||
|
||||
@ -209,7 +262,6 @@ TF_BUILTIN(AtomicsStore, SharedArrayBufferBuiltinsAssembler) {
|
||||
DebugSanityCheckAtomicIndex(array, index_word32, context);
|
||||
#endif
|
||||
|
||||
Label u8(this), u16(this), u32(this), other(this);
|
||||
int32_t case_values[] = {
|
||||
FIXED_INT8_ARRAY_TYPE, FIXED_UINT8_ARRAY_TYPE, FIXED_INT16_ARRAY_TYPE,
|
||||
FIXED_UINT16_ARRAY_TYPE, FIXED_INT32_ARRAY_TYPE, FIXED_UINT32_ARRAY_TYPE,
|
||||
@ -235,6 +287,19 @@ TF_BUILTIN(AtomicsStore, SharedArrayBufferBuiltinsAssembler) {
|
||||
WordShl(index_word, 2), value_word32);
|
||||
Return(value_integer);
|
||||
|
||||
BIND(&u64);
|
||||
TNode<BigInt> value_bigint = ToBigInt(CAST(context), CAST(value));
|
||||
#if DEBUG
|
||||
DebugSanityCheckAtomicIndex(array, index_word32, context);
|
||||
#endif
|
||||
TVARIABLE(UintPtrT, var_low);
|
||||
TVARIABLE(UintPtrT, var_high);
|
||||
BigIntToRawBytes(value_bigint, &var_low, &var_high);
|
||||
Node* high = Is64() ? nullptr : static_cast<Node*>(var_high.value());
|
||||
AtomicStore(MachineRepresentation::kWord64, backing_store,
|
||||
WordShl(index_word, 3), var_low.value(), high);
|
||||
Return(value_bigint);
|
||||
|
||||
// This shouldn't happen, we've already validated the type.
|
||||
BIND(&other);
|
||||
Unreachable();
|
||||
@ -255,22 +320,26 @@ TF_BUILTIN(AtomicsExchange, SharedArrayBufferBuiltinsAssembler) {
|
||||
ConvertTaggedAtomicIndexToWord32(index, context, &index_integer);
|
||||
ValidateAtomicIndex(array, index_word32, context);
|
||||
|
||||
Node* value_integer = ToInteger_Inline(CAST(context), CAST(value));
|
||||
|
||||
#if DEBUG
|
||||
DebugSanityCheckAtomicIndex(array, index_word32, context);
|
||||
#endif
|
||||
|
||||
#if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64
|
||||
Return(CallRuntime(Runtime::kAtomicsExchange, context, array, index_integer,
|
||||
value_integer));
|
||||
value));
|
||||
#else
|
||||
Node* index_word = ChangeUint32ToWord(index_word32);
|
||||
|
||||
Label i8(this), u8(this), i16(this), u16(this), i32(this), u32(this),
|
||||
i64(this), u64(this), big(this), other(this);
|
||||
STATIC_ASSERT(FIXED_BIGINT64_ARRAY_TYPE > FIXED_UINT32_ARRAY_TYPE);
|
||||
STATIC_ASSERT(FIXED_BIGUINT64_ARRAY_TYPE > FIXED_UINT32_ARRAY_TYPE);
|
||||
GotoIf(
|
||||
Int32GreaterThan(instance_type, Int32Constant(FIXED_UINT32_ARRAY_TYPE)),
|
||||
&big);
|
||||
|
||||
Node* value_integer = ToInteger_Inline(CAST(context), CAST(value));
|
||||
#if DEBUG
|
||||
DebugSanityCheckAtomicIndex(array, index_word32, context);
|
||||
#endif
|
||||
Node* value_word32 = TruncateTaggedToWord32(context, value_integer);
|
||||
|
||||
Label i8(this), u8(this), i16(this), u16(this), i32(this), u32(this),
|
||||
other(this);
|
||||
int32_t case_values[] = {
|
||||
FIXED_INT8_ARRAY_TYPE, FIXED_UINT8_ARRAY_TYPE, FIXED_INT16_ARRAY_TYPE,
|
||||
FIXED_UINT16_ARRAY_TYPE, FIXED_INT32_ARRAY_TYPE, FIXED_UINT32_ARRAY_TYPE,
|
||||
@ -307,6 +376,34 @@ TF_BUILTIN(AtomicsExchange, SharedArrayBufferBuiltinsAssembler) {
|
||||
AtomicExchange(MachineType::Uint32(), backing_store,
|
||||
WordShl(index_word, 2), value_word32)));
|
||||
|
||||
BIND(&big);
|
||||
TNode<BigInt> value_bigint = ToBigInt(CAST(context), CAST(value));
|
||||
#if DEBUG
|
||||
DebugSanityCheckAtomicIndex(array, index_word32, context);
|
||||
#endif
|
||||
TVARIABLE(UintPtrT, var_low);
|
||||
TVARIABLE(UintPtrT, var_high);
|
||||
BigIntToRawBytes(value_bigint, &var_low, &var_high);
|
||||
Node* high = Is64() ? nullptr : static_cast<Node*>(var_high.value());
|
||||
GotoIf(Word32Equal(instance_type, Int32Constant(FIXED_BIGINT64_ARRAY_TYPE)),
|
||||
&i64);
|
||||
GotoIf(Word32Equal(instance_type, Int32Constant(FIXED_BIGUINT64_ARRAY_TYPE)),
|
||||
&u64);
|
||||
Unreachable();
|
||||
|
||||
BIND(&i64);
|
||||
// This uses Uint64() intentionally: AtomicExchange is not implemented for
|
||||
// Int64(), which is fine because the machine instruction only cares
|
||||
// about words.
|
||||
Return(BigIntFromSigned64(AtomicExchange(MachineType::Uint64(), backing_store,
|
||||
WordShl(index_word, 3),
|
||||
var_low.value(), high)));
|
||||
|
||||
BIND(&u64);
|
||||
Return(BigIntFromUnsigned64(
|
||||
AtomicExchange(MachineType::Uint64(), backing_store,
|
||||
WordShl(index_word, 3), var_low.value(), high)));
|
||||
|
||||
// This shouldn't happen, we've already validated the type.
|
||||
BIND(&other);
|
||||
Unreachable();
|
||||
@ -329,26 +426,29 @@ TF_BUILTIN(AtomicsCompareExchange, SharedArrayBufferBuiltinsAssembler) {
|
||||
ConvertTaggedAtomicIndexToWord32(index, context, &index_integer);
|
||||
ValidateAtomicIndex(array, index_word32, context);
|
||||
|
||||
Node* old_value_integer = ToInteger_Inline(CAST(context), CAST(old_value));
|
||||
Node* new_value_integer = ToInteger_Inline(CAST(context), CAST(new_value));
|
||||
|
||||
#if DEBUG
|
||||
DebugSanityCheckAtomicIndex(array, index_word32, context);
|
||||
#endif
|
||||
|
||||
#if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_PPC64 || \
|
||||
V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_S390X
|
||||
Return(CallRuntime(Runtime::kAtomicsCompareExchange, context, array,
|
||||
index_integer, old_value_integer, new_value_integer));
|
||||
index_integer, old_value, new_value));
|
||||
#else
|
||||
Node* index_word = ChangeUint32ToWord(index_word32);
|
||||
|
||||
Node* old_value_word32 = TruncateTaggedToWord32(context, old_value_integer);
|
||||
Label i8(this), u8(this), i16(this), u16(this), i32(this), u32(this),
|
||||
i64(this), u64(this), big(this), other(this);
|
||||
STATIC_ASSERT(FIXED_BIGINT64_ARRAY_TYPE > FIXED_UINT32_ARRAY_TYPE);
|
||||
STATIC_ASSERT(FIXED_BIGUINT64_ARRAY_TYPE > FIXED_UINT32_ARRAY_TYPE);
|
||||
GotoIf(
|
||||
Int32GreaterThan(instance_type, Int32Constant(FIXED_UINT32_ARRAY_TYPE)),
|
||||
&big);
|
||||
|
||||
Node* old_value_integer = ToInteger_Inline(CAST(context), CAST(old_value));
|
||||
Node* new_value_integer = ToInteger_Inline(CAST(context), CAST(new_value));
|
||||
#if DEBUG
|
||||
DebugSanityCheckAtomicIndex(array, index_word32, context);
|
||||
#endif
|
||||
Node* old_value_word32 = TruncateTaggedToWord32(context, old_value_integer);
|
||||
Node* new_value_word32 = TruncateTaggedToWord32(context, new_value_integer);
|
||||
|
||||
Label i8(this), u8(this), i16(this), u16(this), i32(this), u32(this),
|
||||
other(this);
|
||||
int32_t case_values[] = {
|
||||
FIXED_INT8_ARRAY_TYPE, FIXED_UINT8_ARRAY_TYPE, FIXED_INT16_ARRAY_TYPE,
|
||||
FIXED_UINT16_ARRAY_TYPE, FIXED_INT32_ARRAY_TYPE, FIXED_UINT32_ARRAY_TYPE,
|
||||
@ -389,6 +489,39 @@ TF_BUILTIN(AtomicsCompareExchange, SharedArrayBufferBuiltinsAssembler) {
|
||||
MachineType::Uint32(), backing_store, WordShl(index_word, 2),
|
||||
old_value_word32, new_value_word32)));
|
||||
|
||||
BIND(&big);
|
||||
TNode<BigInt> old_value_bigint = ToBigInt(CAST(context), CAST(old_value));
|
||||
TNode<BigInt> new_value_bigint = ToBigInt(CAST(context), CAST(new_value));
|
||||
#if DEBUG
|
||||
DebugSanityCheckAtomicIndex(array, index_word32, context);
|
||||
#endif
|
||||
TVARIABLE(UintPtrT, var_old_low);
|
||||
TVARIABLE(UintPtrT, var_old_high);
|
||||
TVARIABLE(UintPtrT, var_new_low);
|
||||
TVARIABLE(UintPtrT, var_new_high);
|
||||
BigIntToRawBytes(old_value_bigint, &var_old_low, &var_old_high);
|
||||
BigIntToRawBytes(new_value_bigint, &var_new_low, &var_new_high);
|
||||
Node* old_high = Is64() ? nullptr : static_cast<Node*>(var_old_high.value());
|
||||
Node* new_high = Is64() ? nullptr : static_cast<Node*>(var_new_high.value());
|
||||
GotoIf(Word32Equal(instance_type, Int32Constant(FIXED_BIGINT64_ARRAY_TYPE)),
|
||||
&i64);
|
||||
GotoIf(Word32Equal(instance_type, Int32Constant(FIXED_BIGUINT64_ARRAY_TYPE)),
|
||||
&u64);
|
||||
Unreachable();
|
||||
|
||||
BIND(&i64);
|
||||
// This uses Uint64() intentionally: AtomicCompareExchange is not implemented
|
||||
// for Int64(), which is fine because the machine instruction only cares
|
||||
// about words.
|
||||
Return(BigIntFromSigned64(AtomicCompareExchange(
|
||||
MachineType::Uint64(), backing_store, WordShl(index_word, 3),
|
||||
var_old_low.value(), var_new_low.value(), old_high, new_high)));
|
||||
|
||||
BIND(&u64);
|
||||
Return(BigIntFromUnsigned64(AtomicCompareExchange(
|
||||
MachineType::Uint64(), backing_store, WordShl(index_word, 3),
|
||||
var_old_low.value(), var_new_low.value(), old_high, new_high)));
|
||||
|
||||
// This shouldn't happen, we've already validated the type.
|
||||
BIND(&other);
|
||||
Unreachable();
|
||||
@ -425,27 +558,27 @@ void SharedArrayBufferBuiltinsAssembler::AtomicBinopBuiltinCommon(
|
||||
ConvertTaggedAtomicIndexToWord32(index, context, &index_integer);
|
||||
ValidateAtomicIndex(array, index_word32, context);
|
||||
|
||||
Node* value_integer = ToInteger_Inline(CAST(context), CAST(value));
|
||||
|
||||
#if DEBUG
|
||||
// In Debug mode, we re-validate the index as a sanity check because
|
||||
// ToInteger above calls out to JavaScript. A SharedArrayBuffer can't be
|
||||
// neutered and the TypedArray length can't change either, so skipping this
|
||||
// check in Release mode is safe.
|
||||
ValidateAtomicIndex(array, index_word32, context);
|
||||
#endif
|
||||
|
||||
#if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_PPC64 || \
|
||||
V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_S390X
|
||||
Return(CallRuntime(runtime_function, context, array, index_integer,
|
||||
value_integer));
|
||||
Return(CallRuntime(runtime_function, context, array, index_integer, value));
|
||||
#else
|
||||
Node* index_word = ChangeUint32ToWord(index_word32);
|
||||
|
||||
Label i8(this), u8(this), i16(this), u16(this), i32(this), u32(this),
|
||||
i64(this), u64(this), big(this), other(this);
|
||||
|
||||
STATIC_ASSERT(FIXED_BIGINT64_ARRAY_TYPE > FIXED_UINT32_ARRAY_TYPE);
|
||||
STATIC_ASSERT(FIXED_BIGUINT64_ARRAY_TYPE > FIXED_UINT32_ARRAY_TYPE);
|
||||
GotoIf(
|
||||
Int32GreaterThan(instance_type, Int32Constant(FIXED_UINT32_ARRAY_TYPE)),
|
||||
&big);
|
||||
|
||||
Node* value_integer = ToInteger_Inline(CAST(context), CAST(value));
|
||||
#if DEBUG
|
||||
DebugSanityCheckAtomicIndex(array, index_word32, context);
|
||||
#endif
|
||||
Node* value_word32 = TruncateTaggedToWord32(context, value_integer);
|
||||
|
||||
Label i8(this), u8(this), i16(this), u16(this), i32(this), u32(this),
|
||||
other(this);
|
||||
int32_t case_values[] = {
|
||||
FIXED_INT8_ARRAY_TYPE, FIXED_UINT8_ARRAY_TYPE, FIXED_INT16_ARRAY_TYPE,
|
||||
FIXED_UINT16_ARRAY_TYPE, FIXED_INT32_ARRAY_TYPE, FIXED_UINT32_ARRAY_TYPE,
|
||||
@ -458,29 +591,59 @@ void SharedArrayBufferBuiltinsAssembler::AtomicBinopBuiltinCommon(
|
||||
|
||||
BIND(&i8);
|
||||
Return(SmiFromInt32((this->*function)(MachineType::Int8(), backing_store,
|
||||
index_word, value_word32)));
|
||||
index_word, value_word32, nullptr)));
|
||||
|
||||
BIND(&u8);
|
||||
Return(SmiFromInt32((this->*function)(MachineType::Uint8(), backing_store,
|
||||
index_word, value_word32)));
|
||||
index_word, value_word32, nullptr)));
|
||||
|
||||
BIND(&i16);
|
||||
Return(SmiFromInt32((this->*function)(MachineType::Int16(), backing_store,
|
||||
WordShl(index_word, 1), value_word32)));
|
||||
WordShl(index_word, 1), value_word32,
|
||||
nullptr)));
|
||||
|
||||
BIND(&u16);
|
||||
Return(SmiFromInt32((this->*function)(MachineType::Uint16(), backing_store,
|
||||
WordShl(index_word, 1), value_word32)));
|
||||
WordShl(index_word, 1), value_word32,
|
||||
nullptr)));
|
||||
|
||||
BIND(&i32);
|
||||
Return(ChangeInt32ToTagged(
|
||||
(this->*function)(MachineType::Int32(), backing_store,
|
||||
WordShl(index_word, 2), value_word32)));
|
||||
WordShl(index_word, 2), value_word32, nullptr)));
|
||||
|
||||
BIND(&u32);
|
||||
Return(ChangeUint32ToTagged(
|
||||
(this->*function)(MachineType::Uint32(), backing_store,
|
||||
WordShl(index_word, 2), value_word32)));
|
||||
WordShl(index_word, 2), value_word32, nullptr)));
|
||||
|
||||
BIND(&big);
|
||||
TNode<BigInt> value_bigint = ToBigInt(CAST(context), CAST(value));
|
||||
#if DEBUG
|
||||
DebugSanityCheckAtomicIndex(array, index_word32, context);
|
||||
#endif
|
||||
TVARIABLE(UintPtrT, var_low);
|
||||
TVARIABLE(UintPtrT, var_high);
|
||||
BigIntToRawBytes(value_bigint, &var_low, &var_high);
|
||||
Node* high = Is64() ? nullptr : static_cast<Node*>(var_high.value());
|
||||
GotoIf(Word32Equal(instance_type, Int32Constant(FIXED_BIGINT64_ARRAY_TYPE)),
|
||||
&i64);
|
||||
GotoIf(Word32Equal(instance_type, Int32Constant(FIXED_BIGUINT64_ARRAY_TYPE)),
|
||||
&u64);
|
||||
Unreachable();
|
||||
|
||||
BIND(&i64);
|
||||
// This uses Uint64() intentionally: Atomic* ops are not implemented for
|
||||
// Int64(), which is fine because the machine instructions only care
|
||||
// about words.
|
||||
Return(BigIntFromSigned64(
|
||||
(this->*function)(MachineType::Uint64(), backing_store,
|
||||
WordShl(index_word, 3), var_low.value(), high)));
|
||||
|
||||
BIND(&u64);
|
||||
Return(BigIntFromUnsigned64(
|
||||
(this->*function)(MachineType::Uint64(), backing_store,
|
||||
WordShl(index_word, 3), var_low.value(), high)));
|
||||
|
||||
// This shouldn't happen, we've already validated the type.
|
||||
BIND(&other);
|
||||
|
@ -2035,108 +2035,88 @@ TNode<RawPtrT> CodeStubAssembler::LoadFixedTypedArrayBackingStore(
|
||||
|
||||
Node* CodeStubAssembler::LoadFixedBigInt64ArrayElementAsTagged(
|
||||
Node* data_pointer, Node* offset) {
|
||||
TVARIABLE(BigInt, var_result);
|
||||
Label done(this), if_zero(this);
|
||||
if (Is64()) {
|
||||
TNode<IntPtrT> value = UncheckedCast<IntPtrT>(
|
||||
Load(MachineType::IntPtr(), data_pointer, offset));
|
||||
Label if_positive(this), if_negative(this);
|
||||
GotoIf(IntPtrEqual(value, IntPtrConstant(0)), &if_zero);
|
||||
var_result = AllocateRawBigInt(IntPtrConstant(1));
|
||||
Branch(IntPtrGreaterThan(value, IntPtrConstant(0)), &if_positive,
|
||||
&if_negative);
|
||||
|
||||
BIND(&if_positive);
|
||||
{
|
||||
StoreBigIntBitfield(var_result.value(),
|
||||
IntPtrConstant(BigInt::SignBits::encode(false) |
|
||||
BigInt::LengthBits::encode(1)));
|
||||
StoreBigIntDigit(var_result.value(), 0, Unsigned(value));
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
BIND(&if_negative);
|
||||
{
|
||||
StoreBigIntBitfield(var_result.value(),
|
||||
IntPtrConstant(BigInt::SignBits::encode(true) |
|
||||
BigInt::LengthBits::encode(1)));
|
||||
StoreBigIntDigit(var_result.value(), 0,
|
||||
Unsigned(IntPtrSub(IntPtrConstant(0), value)));
|
||||
Goto(&done);
|
||||
}
|
||||
return BigIntFromInt64(value);
|
||||
} else {
|
||||
DCHECK(!Is64());
|
||||
TVARIABLE(WordT, var_sign, IntPtrConstant(BigInt::SignBits::encode(false)));
|
||||
TVARIABLE(IntPtrT, var_low);
|
||||
TVARIABLE(IntPtrT, var_high);
|
||||
#if defined(V8_TARGET_BIG_ENDIAN)
|
||||
var_high = UncheckedCast<IntPtrT>(
|
||||
TNode<IntPtrT> high = UncheckedCast<IntPtrT>(
|
||||
Load(MachineType::UintPtr(), data_pointer, offset));
|
||||
var_low = UncheckedCast<IntPtrT>(
|
||||
TNode<IntPtrT> low = UncheckedCast<IntPtrT>(
|
||||
Load(MachineType::UintPtr(), data_pointer,
|
||||
Int32Add(offset, Int32Constant(kPointerSize))));
|
||||
#else
|
||||
var_low = UncheckedCast<IntPtrT>(
|
||||
TNode<IntPtrT> low = UncheckedCast<IntPtrT>(
|
||||
Load(MachineType::UintPtr(), data_pointer, offset));
|
||||
var_high = UncheckedCast<IntPtrT>(
|
||||
TNode<IntPtrT> high = UncheckedCast<IntPtrT>(
|
||||
Load(MachineType::UintPtr(), data_pointer,
|
||||
Int32Add(offset, Int32Constant(kPointerSize))));
|
||||
#endif
|
||||
|
||||
Label high_zero(this), negative(this), allocate_one_digit(this),
|
||||
allocate_two_digits(this);
|
||||
|
||||
GotoIf(WordEqual(var_high.value(), IntPtrConstant(0)), &high_zero);
|
||||
Branch(IntPtrLessThan(var_high.value(), IntPtrConstant(0)), &negative,
|
||||
&allocate_two_digits);
|
||||
|
||||
BIND(&high_zero);
|
||||
Branch(WordEqual(var_low.value(), IntPtrConstant(0)), &if_zero,
|
||||
&allocate_one_digit);
|
||||
|
||||
BIND(&negative);
|
||||
{
|
||||
var_sign = IntPtrConstant(BigInt::SignBits::encode(true));
|
||||
// We must negate the value by computing "0 - (high|low)", performing
|
||||
// both parts of the subtraction separately and manually taking care
|
||||
// of the carry bit (which is 1 iff low != 0).
|
||||
var_high = IntPtrSub(IntPtrConstant(0), var_high.value());
|
||||
Label carry(this), no_carry(this);
|
||||
Branch(WordEqual(var_low.value(), IntPtrConstant(0)), &no_carry, &carry);
|
||||
BIND(&carry);
|
||||
var_high = IntPtrSub(var_high.value(), IntPtrConstant(1));
|
||||
Goto(&no_carry);
|
||||
BIND(&no_carry);
|
||||
var_low = IntPtrSub(IntPtrConstant(0), var_low.value());
|
||||
// var_high was non-zero going into this block, but subtracting the
|
||||
// carry bit from it could bring us back onto the "one digit" path.
|
||||
Branch(WordEqual(var_high.value(), IntPtrConstant(0)),
|
||||
&allocate_one_digit, &allocate_two_digits);
|
||||
}
|
||||
|
||||
BIND(&allocate_one_digit);
|
||||
{
|
||||
var_result = AllocateRawBigInt(IntPtrConstant(1));
|
||||
StoreBigIntBitfield(
|
||||
var_result.value(),
|
||||
WordOr(var_sign.value(),
|
||||
IntPtrConstant(BigInt::LengthBits::encode(1))));
|
||||
StoreBigIntDigit(var_result.value(), 0, Unsigned(var_low.value()));
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
BIND(&allocate_two_digits);
|
||||
{
|
||||
var_result = AllocateRawBigInt(IntPtrConstant(2));
|
||||
StoreBigIntBitfield(
|
||||
var_result.value(),
|
||||
WordOr(var_sign.value(),
|
||||
IntPtrConstant(BigInt::LengthBits::encode(2))));
|
||||
StoreBigIntDigit(var_result.value(), 0, Unsigned(var_low.value()));
|
||||
StoreBigIntDigit(var_result.value(), 1, Unsigned(var_high.value()));
|
||||
Goto(&done);
|
||||
}
|
||||
return BigIntFromInt32Pair(low, high);
|
||||
}
|
||||
}
|
||||
|
||||
TNode<BigInt> CodeStubAssembler::BigIntFromInt32Pair(TNode<IntPtrT> low,
|
||||
TNode<IntPtrT> high) {
|
||||
DCHECK(!Is64());
|
||||
TVARIABLE(BigInt, var_result);
|
||||
TVARIABLE(WordT, var_sign, IntPtrConstant(BigInt::SignBits::encode(false)));
|
||||
TVARIABLE(IntPtrT, var_high, high);
|
||||
TVARIABLE(IntPtrT, var_low, low);
|
||||
Label high_zero(this), negative(this), allocate_one_digit(this),
|
||||
allocate_two_digits(this), if_zero(this), done(this);
|
||||
|
||||
GotoIf(WordEqual(var_high.value(), IntPtrConstant(0)), &high_zero);
|
||||
Branch(IntPtrLessThan(var_high.value(), IntPtrConstant(0)), &negative,
|
||||
&allocate_two_digits);
|
||||
|
||||
BIND(&high_zero);
|
||||
Branch(WordEqual(var_low.value(), IntPtrConstant(0)), &if_zero,
|
||||
&allocate_one_digit);
|
||||
|
||||
BIND(&negative);
|
||||
{
|
||||
var_sign = IntPtrConstant(BigInt::SignBits::encode(true));
|
||||
// We must negate the value by computing "0 - (high|low)", performing
|
||||
// both parts of the subtraction separately and manually taking care
|
||||
// of the carry bit (which is 1 iff low != 0).
|
||||
var_high = IntPtrSub(IntPtrConstant(0), var_high.value());
|
||||
Label carry(this), no_carry(this);
|
||||
Branch(WordEqual(var_low.value(), IntPtrConstant(0)), &no_carry, &carry);
|
||||
BIND(&carry);
|
||||
var_high = IntPtrSub(var_high.value(), IntPtrConstant(1));
|
||||
Goto(&no_carry);
|
||||
BIND(&no_carry);
|
||||
var_low = IntPtrSub(IntPtrConstant(0), var_low.value());
|
||||
// var_high was non-zero going into this block, but subtracting the
|
||||
// carry bit from it could bring us back onto the "one digit" path.
|
||||
Branch(WordEqual(var_high.value(), IntPtrConstant(0)), &allocate_one_digit,
|
||||
&allocate_two_digits);
|
||||
}
|
||||
|
||||
BIND(&allocate_one_digit);
|
||||
{
|
||||
var_result = AllocateRawBigInt(IntPtrConstant(1));
|
||||
StoreBigIntBitfield(var_result.value(),
|
||||
WordOr(var_sign.value(),
|
||||
IntPtrConstant(BigInt::LengthBits::encode(1))));
|
||||
StoreBigIntDigit(var_result.value(), 0, Unsigned(var_low.value()));
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
BIND(&allocate_two_digits);
|
||||
{
|
||||
var_result = AllocateRawBigInt(IntPtrConstant(2));
|
||||
StoreBigIntBitfield(var_result.value(),
|
||||
WordOr(var_sign.value(),
|
||||
IntPtrConstant(BigInt::LengthBits::encode(2))));
|
||||
StoreBigIntDigit(var_result.value(), 0, Unsigned(var_low.value()));
|
||||
StoreBigIntDigit(var_result.value(), 1, Unsigned(var_high.value()));
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
BIND(&if_zero);
|
||||
var_result = AllocateBigInt(IntPtrConstant(0));
|
||||
Goto(&done);
|
||||
@ -2145,21 +2125,53 @@ Node* CodeStubAssembler::LoadFixedBigInt64ArrayElementAsTagged(
|
||||
return var_result.value();
|
||||
}
|
||||
|
||||
TNode<BigInt> CodeStubAssembler::BigIntFromInt64(TNode<IntPtrT> value) {
|
||||
DCHECK(Is64());
|
||||
TVARIABLE(BigInt, var_result);
|
||||
Label done(this), if_positive(this), if_negative(this), if_zero(this);
|
||||
GotoIf(WordEqual(value, IntPtrConstant(0)), &if_zero);
|
||||
var_result = AllocateRawBigInt(IntPtrConstant(1));
|
||||
Branch(IntPtrGreaterThan(value, IntPtrConstant(0)), &if_positive,
|
||||
&if_negative);
|
||||
|
||||
BIND(&if_positive);
|
||||
{
|
||||
StoreBigIntBitfield(var_result.value(),
|
||||
IntPtrConstant(BigInt::SignBits::encode(false) |
|
||||
BigInt::LengthBits::encode(1)));
|
||||
StoreBigIntDigit(var_result.value(), 0, Unsigned(value));
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
BIND(&if_negative);
|
||||
{
|
||||
StoreBigIntBitfield(var_result.value(),
|
||||
IntPtrConstant(BigInt::SignBits::encode(true) |
|
||||
BigInt::LengthBits::encode(1)));
|
||||
StoreBigIntDigit(var_result.value(), 0,
|
||||
Unsigned(IntPtrSub(IntPtrConstant(0), value)));
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
BIND(&if_zero);
|
||||
{
|
||||
var_result = AllocateBigInt(IntPtrConstant(0));
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
BIND(&done);
|
||||
return var_result.value();
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::LoadFixedBigUint64ArrayElementAsTagged(
|
||||
Node* data_pointer, Node* offset) {
|
||||
TVARIABLE(BigInt, var_result);
|
||||
Label if_zero(this), done(this);
|
||||
if (Is64()) {
|
||||
TNode<UintPtrT> value = UncheckedCast<UintPtrT>(
|
||||
Load(MachineType::UintPtr(), data_pointer, offset));
|
||||
GotoIf(IntPtrEqual(value, IntPtrConstant(0)), &if_zero);
|
||||
var_result = AllocateBigInt(IntPtrConstant(1));
|
||||
StoreBigIntDigit(var_result.value(), 0, value);
|
||||
Goto(&done);
|
||||
return BigIntFromUint64(value);
|
||||
} else {
|
||||
DCHECK(!Is64());
|
||||
Label high_zero(this);
|
||||
|
||||
#if defined(V8_TARGET_BIG_ENDIAN)
|
||||
TNode<UintPtrT> high = UncheckedCast<UintPtrT>(
|
||||
Load(MachineType::UintPtr(), data_pointer, offset));
|
||||
@ -2173,19 +2185,28 @@ Node* CodeStubAssembler::LoadFixedBigUint64ArrayElementAsTagged(
|
||||
Load(MachineType::UintPtr(), data_pointer,
|
||||
Int32Add(offset, Int32Constant(kPointerSize))));
|
||||
#endif
|
||||
|
||||
GotoIf(WordEqual(high, IntPtrConstant(0)), &high_zero);
|
||||
var_result = AllocateBigInt(IntPtrConstant(2));
|
||||
StoreBigIntDigit(var_result.value(), 0, low);
|
||||
StoreBigIntDigit(var_result.value(), 1, high);
|
||||
Goto(&done);
|
||||
|
||||
BIND(&high_zero);
|
||||
GotoIf(WordEqual(low, IntPtrConstant(0)), &if_zero);
|
||||
var_result = AllocateBigInt(IntPtrConstant(1));
|
||||
StoreBigIntDigit(var_result.value(), 0, low);
|
||||
Goto(&done);
|
||||
return BigIntFromUint32Pair(low, high);
|
||||
}
|
||||
}
|
||||
|
||||
TNode<BigInt> CodeStubAssembler::BigIntFromUint32Pair(TNode<UintPtrT> low,
|
||||
TNode<UintPtrT> high) {
|
||||
DCHECK(!Is64());
|
||||
TVARIABLE(BigInt, var_result);
|
||||
Label high_zero(this), if_zero(this), done(this);
|
||||
|
||||
GotoIf(WordEqual(high, IntPtrConstant(0)), &high_zero);
|
||||
var_result = AllocateBigInt(IntPtrConstant(2));
|
||||
StoreBigIntDigit(var_result.value(), 0, low);
|
||||
StoreBigIntDigit(var_result.value(), 1, high);
|
||||
Goto(&done);
|
||||
|
||||
BIND(&high_zero);
|
||||
GotoIf(WordEqual(low, IntPtrConstant(0)), &if_zero);
|
||||
var_result = AllocateBigInt(IntPtrConstant(1));
|
||||
StoreBigIntDigit(var_result.value(), 0, low);
|
||||
Goto(&done);
|
||||
|
||||
BIND(&if_zero);
|
||||
var_result = AllocateBigInt(IntPtrConstant(0));
|
||||
Goto(&done);
|
||||
@ -2194,6 +2215,22 @@ Node* CodeStubAssembler::LoadFixedBigUint64ArrayElementAsTagged(
|
||||
return var_result.value();
|
||||
}
|
||||
|
||||
TNode<BigInt> CodeStubAssembler::BigIntFromUint64(TNode<UintPtrT> value) {
|
||||
DCHECK(Is64());
|
||||
TVARIABLE(BigInt, var_result);
|
||||
Label done(this), if_zero(this);
|
||||
GotoIf(WordEqual(value, IntPtrConstant(0)), &if_zero);
|
||||
var_result = AllocateBigInt(IntPtrConstant(1));
|
||||
StoreBigIntDigit(var_result.value(), 0, value);
|
||||
Goto(&done);
|
||||
|
||||
BIND(&if_zero);
|
||||
var_result = AllocateBigInt(IntPtrConstant(0));
|
||||
Goto(&done);
|
||||
BIND(&done);
|
||||
return var_result.value();
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::LoadFixedTypedArrayElementAsTagged(
|
||||
Node* data_pointer, Node* index_node, ElementsKind elements_kind,
|
||||
ParameterMode parameter_mode) {
|
||||
@ -9835,38 +9872,46 @@ void CodeStubAssembler::EmitBigTypedArrayElementStore(
|
||||
EmitBigTypedArrayElementStore(elements, backing_store, offset, bigint_value);
|
||||
}
|
||||
|
||||
void CodeStubAssembler::EmitBigTypedArrayElementStore(
|
||||
TNode<FixedTypedArrayBase> elements, TNode<RawPtrT> backing_store,
|
||||
TNode<IntPtrT> offset, TNode<BigInt> bigint_value) {
|
||||
TNode<WordT> bitfield = LoadBigIntBitfield(bigint_value);
|
||||
void CodeStubAssembler::BigIntToRawBytes(TNode<BigInt> bigint,
|
||||
TVariable<UintPtrT>* var_low,
|
||||
TVariable<UintPtrT>* var_high) {
|
||||
Label done(this);
|
||||
*var_low = Unsigned(IntPtrConstant(0));
|
||||
*var_high = Unsigned(IntPtrConstant(0));
|
||||
TNode<WordT> bitfield = LoadBigIntBitfield(bigint);
|
||||
TNode<UintPtrT> length = DecodeWord<BigIntBase::LengthBits>(bitfield);
|
||||
TNode<UintPtrT> sign = DecodeWord<BigIntBase::SignBits>(bitfield);
|
||||
TVARIABLE(UintPtrT, var_low, Unsigned(IntPtrConstant(0)));
|
||||
// Only used on 32-bit platforms.
|
||||
TVARIABLE(UintPtrT, var_high, Unsigned(IntPtrConstant(0)));
|
||||
Label do_store(this);
|
||||
GotoIf(WordEqual(length, IntPtrConstant(0)), &do_store);
|
||||
var_low = LoadBigIntDigit(bigint_value, 0);
|
||||
GotoIf(WordEqual(length, IntPtrConstant(0)), &done);
|
||||
*var_low = LoadBigIntDigit(bigint, 0);
|
||||
if (!Is64()) {
|
||||
Label load_done(this);
|
||||
GotoIf(WordEqual(length, IntPtrConstant(1)), &load_done);
|
||||
var_high = LoadBigIntDigit(bigint_value, 1);
|
||||
*var_high = LoadBigIntDigit(bigint, 1);
|
||||
Goto(&load_done);
|
||||
BIND(&load_done);
|
||||
}
|
||||
GotoIf(WordEqual(sign, IntPtrConstant(0)), &do_store);
|
||||
GotoIf(WordEqual(sign, IntPtrConstant(0)), &done);
|
||||
// Negative value. Simulate two's complement.
|
||||
if (!Is64()) {
|
||||
var_high = Unsigned(IntPtrSub(IntPtrConstant(0), var_high.value()));
|
||||
*var_high = Unsigned(IntPtrSub(IntPtrConstant(0), var_high->value()));
|
||||
Label no_carry(this);
|
||||
GotoIf(WordEqual(var_low.value(), IntPtrConstant(0)), &no_carry);
|
||||
var_high = Unsigned(IntPtrSub(var_high.value(), IntPtrConstant(1)));
|
||||
GotoIf(WordEqual(var_low->value(), IntPtrConstant(0)), &no_carry);
|
||||
*var_high = Unsigned(IntPtrSub(var_high->value(), IntPtrConstant(1)));
|
||||
Goto(&no_carry);
|
||||
BIND(&no_carry);
|
||||
}
|
||||
var_low = Unsigned(IntPtrSub(IntPtrConstant(0), var_low.value()));
|
||||
Goto(&do_store);
|
||||
BIND(&do_store);
|
||||
*var_low = Unsigned(IntPtrSub(IntPtrConstant(0), var_low->value()));
|
||||
Goto(&done);
|
||||
BIND(&done);
|
||||
}
|
||||
|
||||
void CodeStubAssembler::EmitBigTypedArrayElementStore(
|
||||
TNode<FixedTypedArrayBase> elements, TNode<RawPtrT> backing_store,
|
||||
TNode<IntPtrT> offset, TNode<BigInt> bigint_value) {
|
||||
TVARIABLE(UintPtrT, var_low);
|
||||
// Only used on 32-bit platforms.
|
||||
TVARIABLE(UintPtrT, var_high);
|
||||
BigIntToRawBytes(bigint_value, &var_low, &var_high);
|
||||
|
||||
// Assert that offset < elements.length. Given that it's an offset for a raw
|
||||
// pointer we correct it by the usual kHeapObjectTag offset.
|
||||
|
@ -1103,6 +1103,12 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
||||
Node* LoadFixedBigInt64ArrayElementAsTagged(Node* data_pointer, Node* offset);
|
||||
Node* LoadFixedBigUint64ArrayElementAsTagged(Node* data_pointer,
|
||||
Node* offset);
|
||||
// 64-bit platforms only:
|
||||
TNode<BigInt> BigIntFromInt64(TNode<IntPtrT> value);
|
||||
TNode<BigInt> BigIntFromUint64(TNode<UintPtrT> value);
|
||||
// 32-bit platforms only:
|
||||
TNode<BigInt> BigIntFromInt32Pair(TNode<IntPtrT> low, TNode<IntPtrT> high);
|
||||
TNode<BigInt> BigIntFromUint32Pair(TNode<UintPtrT> low, TNode<UintPtrT> high);
|
||||
|
||||
void StoreFixedTypedArrayElementFromTagged(
|
||||
TNode<Context> context, TNode<FixedTypedArrayBase> elements,
|
||||
@ -2647,11 +2653,17 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
||||
TNode<Object> value,
|
||||
TNode<Context> context,
|
||||
Label* opt_if_neutered);
|
||||
// Part of the above, refactored out to reuse in another place
|
||||
// Part of the above, refactored out to reuse in another place.
|
||||
void EmitBigTypedArrayElementStore(TNode<FixedTypedArrayBase> elements,
|
||||
TNode<RawPtrT> backing_store,
|
||||
TNode<IntPtrT> offset,
|
||||
TNode<BigInt> bigint_value);
|
||||
// Implements the BigInt part of
|
||||
// https://tc39.github.io/proposal-bigint/#sec-numbertorawbytes,
|
||||
// including truncation to 64 bits (i.e. modulo 2^64).
|
||||
// {var_high} is only used on 32-bit platforms.
|
||||
void BigIntToRawBytes(TNode<BigInt> bigint, TVariable<UintPtrT>* var_low,
|
||||
TVariable<UintPtrT>* var_high);
|
||||
|
||||
void EmitElementStore(Node* object, Node* key, Node* value, bool is_jsarray,
|
||||
ElementsKind elements_kind,
|
||||
|
@ -1010,14 +1010,16 @@ Node* CodeAssembler::StoreNoWriteBarrier(MachineRepresentation rep, Node* base,
|
||||
}
|
||||
|
||||
Node* CodeAssembler::AtomicStore(MachineRepresentation rep, Node* base,
|
||||
Node* offset, Node* value) {
|
||||
return raw_assembler()->AtomicStore(rep, base, offset, value);
|
||||
Node* offset, Node* value, Node* value_high) {
|
||||
return raw_assembler()->AtomicStore(rep, base, offset, value, value_high);
|
||||
}
|
||||
|
||||
#define ATOMIC_FUNCTION(name) \
|
||||
Node* CodeAssembler::Atomic##name(MachineType type, Node* base, \
|
||||
Node* offset, Node* value) { \
|
||||
return raw_assembler()->Atomic##name(type, base, offset, value); \
|
||||
#define ATOMIC_FUNCTION(name) \
|
||||
Node* CodeAssembler::Atomic##name(MachineType type, Node* base, \
|
||||
Node* offset, Node* value, \
|
||||
Node* value_high) { \
|
||||
return raw_assembler()->Atomic##name(type, base, offset, value, \
|
||||
value_high); \
|
||||
}
|
||||
ATOMIC_FUNCTION(Exchange);
|
||||
ATOMIC_FUNCTION(Add);
|
||||
@ -1029,9 +1031,11 @@ ATOMIC_FUNCTION(Xor);
|
||||
|
||||
Node* CodeAssembler::AtomicCompareExchange(MachineType type, Node* base,
|
||||
Node* offset, Node* old_value,
|
||||
Node* new_value) {
|
||||
return raw_assembler()->AtomicCompareExchange(type, base, offset, old_value,
|
||||
new_value);
|
||||
Node* new_value,
|
||||
Node* old_value_high,
|
||||
Node* new_value_high) {
|
||||
return raw_assembler()->AtomicCompareExchange(
|
||||
type, base, offset, old_value, old_value_high, new_value, new_value_high);
|
||||
}
|
||||
|
||||
Node* CodeAssembler::StoreRoot(RootIndex root_index, Node* value) {
|
||||
@ -1048,6 +1052,7 @@ Node* CodeAssembler::Retain(Node* value) {
|
||||
}
|
||||
|
||||
Node* CodeAssembler::Projection(int index, Node* value) {
|
||||
DCHECK(index < value->op()->ValueOutputCount());
|
||||
return raw_assembler()->Projection(index, value);
|
||||
}
|
||||
|
||||
|
@ -833,25 +833,35 @@ class V8_EXPORT_PRIVATE CodeAssembler {
|
||||
Node* StoreNoWriteBarrier(MachineRepresentation rep, Node* base, Node* value);
|
||||
Node* StoreNoWriteBarrier(MachineRepresentation rep, Node* base, Node* offset,
|
||||
Node* value);
|
||||
// {value_high} is used for 64-bit stores on 32-bit platforms, must be
|
||||
// nullptr in other cases.
|
||||
Node* AtomicStore(MachineRepresentation rep, Node* base, Node* offset,
|
||||
Node* value);
|
||||
Node* value, Node* value_high = nullptr);
|
||||
|
||||
// Exchange value at raw memory location
|
||||
Node* AtomicExchange(MachineType type, Node* base, Node* offset, Node* value);
|
||||
Node* AtomicExchange(MachineType type, Node* base, Node* offset, Node* value,
|
||||
Node* value_high = nullptr);
|
||||
|
||||
// Compare and Exchange value at raw memory location
|
||||
Node* AtomicCompareExchange(MachineType type, Node* base, Node* offset,
|
||||
Node* old_value, Node* new_value);
|
||||
Node* old_value, Node* new_value,
|
||||
Node* old_value_high = nullptr,
|
||||
Node* new_value_high = nullptr);
|
||||
|
||||
Node* AtomicAdd(MachineType type, Node* base, Node* offset, Node* value);
|
||||
Node* AtomicAdd(MachineType type, Node* base, Node* offset, Node* value,
|
||||
Node* value_high = nullptr);
|
||||
|
||||
Node* AtomicSub(MachineType type, Node* base, Node* offset, Node* value);
|
||||
Node* AtomicSub(MachineType type, Node* base, Node* offset, Node* value,
|
||||
Node* value_high = nullptr);
|
||||
|
||||
Node* AtomicAnd(MachineType type, Node* base, Node* offset, Node* value);
|
||||
Node* AtomicAnd(MachineType type, Node* base, Node* offset, Node* value,
|
||||
Node* value_high = nullptr);
|
||||
|
||||
Node* AtomicOr(MachineType type, Node* base, Node* offset, Node* value);
|
||||
Node* AtomicOr(MachineType type, Node* base, Node* offset, Node* value,
|
||||
Node* value_high = nullptr);
|
||||
|
||||
Node* AtomicXor(MachineType type, Node* base, Node* offset, Node* value);
|
||||
Node* AtomicXor(MachineType type, Node* base, Node* offset, Node* value,
|
||||
Node* value_high = nullptr);
|
||||
|
||||
// Store a value to the root array.
|
||||
Node* StoreRoot(RootIndex root_index, Node* value);
|
||||
|
@ -65,6 +65,16 @@ class MachineRepresentationInferrer {
|
||||
auto call_descriptor = CallDescriptorOf(input->op());
|
||||
return call_descriptor->GetReturnType(index).representation();
|
||||
}
|
||||
case IrOpcode::kWord32AtomicPairLoad:
|
||||
case IrOpcode::kWord32AtomicPairAdd:
|
||||
case IrOpcode::kWord32AtomicPairSub:
|
||||
case IrOpcode::kWord32AtomicPairAnd:
|
||||
case IrOpcode::kWord32AtomicPairOr:
|
||||
case IrOpcode::kWord32AtomicPairXor:
|
||||
case IrOpcode::kWord32AtomicPairExchange:
|
||||
case IrOpcode::kWord32AtomicPairCompareExchange:
|
||||
CHECK_LE(index, static_cast<size_t>(1));
|
||||
return MachineRepresentation::kWord32;
|
||||
default:
|
||||
return MachineRepresentation::kNone;
|
||||
}
|
||||
@ -111,6 +121,7 @@ class MachineRepresentationInferrer {
|
||||
representation_vector_[node->id()] = MachineRepresentation::kNone;
|
||||
break;
|
||||
case IrOpcode::kWord32AtomicLoad:
|
||||
case IrOpcode::kWord64AtomicLoad:
|
||||
case IrOpcode::kLoad:
|
||||
case IrOpcode::kProtectedLoad:
|
||||
case IrOpcode::kPoisonedLoad:
|
||||
@ -144,9 +155,21 @@ class MachineRepresentationInferrer {
|
||||
break;
|
||||
}
|
||||
case IrOpcode::kWord32AtomicStore:
|
||||
case IrOpcode::kWord64AtomicStore:
|
||||
representation_vector_[node->id()] =
|
||||
PromoteRepresentation(AtomicStoreRepresentationOf(node->op()));
|
||||
break;
|
||||
case IrOpcode::kWord32AtomicPairLoad:
|
||||
case IrOpcode::kWord32AtomicPairStore:
|
||||
case IrOpcode::kWord32AtomicPairAdd:
|
||||
case IrOpcode::kWord32AtomicPairSub:
|
||||
case IrOpcode::kWord32AtomicPairAnd:
|
||||
case IrOpcode::kWord32AtomicPairOr:
|
||||
case IrOpcode::kWord32AtomicPairXor:
|
||||
case IrOpcode::kWord32AtomicPairExchange:
|
||||
case IrOpcode::kWord32AtomicPairCompareExchange:
|
||||
representation_vector_[node->id()] = MachineRepresentation::kWord32;
|
||||
break;
|
||||
case IrOpcode::kWord32AtomicExchange:
|
||||
case IrOpcode::kWord32AtomicCompareExchange:
|
||||
case IrOpcode::kWord32AtomicAdd:
|
||||
@ -154,6 +177,13 @@ class MachineRepresentationInferrer {
|
||||
case IrOpcode::kWord32AtomicAnd:
|
||||
case IrOpcode::kWord32AtomicOr:
|
||||
case IrOpcode::kWord32AtomicXor:
|
||||
case IrOpcode::kWord64AtomicExchange:
|
||||
case IrOpcode::kWord64AtomicCompareExchange:
|
||||
case IrOpcode::kWord64AtomicAdd:
|
||||
case IrOpcode::kWord64AtomicSub:
|
||||
case IrOpcode::kWord64AtomicAnd:
|
||||
case IrOpcode::kWord64AtomicOr:
|
||||
case IrOpcode::kWord64AtomicXor:
|
||||
representation_vector_[node->id()] = PromoteRepresentation(
|
||||
AtomicOpType(node->op()).representation());
|
||||
break;
|
||||
@ -487,11 +517,23 @@ class MachineRepresentationChecker {
|
||||
break;
|
||||
case IrOpcode::kLoad:
|
||||
case IrOpcode::kWord32AtomicLoad:
|
||||
case IrOpcode::kWord32AtomicPairLoad:
|
||||
case IrOpcode::kWord64AtomicLoad:
|
||||
case IrOpcode::kPoisonedLoad:
|
||||
CheckValueInputIsTaggedOrPointer(node, 0);
|
||||
CheckValueInputRepresentationIs(
|
||||
node, 1, MachineType::PointerRepresentation());
|
||||
break;
|
||||
case IrOpcode::kWord32AtomicPairAdd:
|
||||
case IrOpcode::kWord32AtomicPairSub:
|
||||
case IrOpcode::kWord32AtomicPairAnd:
|
||||
case IrOpcode::kWord32AtomicPairOr:
|
||||
case IrOpcode::kWord32AtomicPairXor:
|
||||
case IrOpcode::kWord32AtomicPairStore:
|
||||
case IrOpcode::kWord32AtomicPairExchange:
|
||||
CheckValueInputRepresentationIs(node, 3,
|
||||
MachineRepresentation::kWord32);
|
||||
V8_FALLTHROUGH;
|
||||
case IrOpcode::kStore:
|
||||
case IrOpcode::kWord32AtomicStore:
|
||||
case IrOpcode::kWord32AtomicExchange:
|
||||
@ -500,6 +542,13 @@ class MachineRepresentationChecker {
|
||||
case IrOpcode::kWord32AtomicAnd:
|
||||
case IrOpcode::kWord32AtomicOr:
|
||||
case IrOpcode::kWord32AtomicXor:
|
||||
case IrOpcode::kWord64AtomicStore:
|
||||
case IrOpcode::kWord64AtomicExchange:
|
||||
case IrOpcode::kWord64AtomicAdd:
|
||||
case IrOpcode::kWord64AtomicSub:
|
||||
case IrOpcode::kWord64AtomicAnd:
|
||||
case IrOpcode::kWord64AtomicOr:
|
||||
case IrOpcode::kWord64AtomicXor:
|
||||
CheckValueInputIsTaggedOrPointer(node, 0);
|
||||
CheckValueInputRepresentationIs(
|
||||
node, 1, MachineType::PointerRepresentation());
|
||||
@ -514,7 +563,14 @@ class MachineRepresentationChecker {
|
||||
node, 2, inferrer_->GetRepresentation(node));
|
||||
}
|
||||
break;
|
||||
case IrOpcode::kWord32AtomicPairCompareExchange:
|
||||
CheckValueInputRepresentationIs(node, 4,
|
||||
MachineRepresentation::kWord32);
|
||||
CheckValueInputRepresentationIs(node, 5,
|
||||
MachineRepresentation::kWord32);
|
||||
V8_FALLTHROUGH;
|
||||
case IrOpcode::kWord32AtomicCompareExchange:
|
||||
case IrOpcode::kWord64AtomicCompareExchange:
|
||||
CheckValueInputIsTaggedOrPointer(node, 0);
|
||||
CheckValueInputRepresentationIs(
|
||||
node, 1, MachineType::PointerRepresentation());
|
||||
|
@ -81,8 +81,7 @@ StackSlotRepresentation const& StackSlotRepresentationOf(Operator const* op) {
|
||||
|
||||
MachineRepresentation AtomicStoreRepresentationOf(Operator const* op) {
|
||||
DCHECK(IrOpcode::kWord32AtomicStore == op->opcode() ||
|
||||
IrOpcode::kWord64AtomicStore == op->opcode() ||
|
||||
IrOpcode::kWord32AtomicPairStore == op->opcode());
|
||||
IrOpcode::kWord64AtomicStore == op->opcode());
|
||||
return OpParameter<MachineRepresentation>(op);
|
||||
}
|
||||
|
||||
|
@ -173,15 +173,51 @@ class V8_EXPORT_PRIVATE RawMachineAssembler {
|
||||
|
||||
// Atomic memory operations.
|
||||
Node* AtomicLoad(MachineType type, Node* base, Node* index) {
|
||||
if (type.representation() == MachineRepresentation::kWord64) {
|
||||
if (machine()->Is64()) {
|
||||
return AddNode(machine()->Word64AtomicLoad(type), base, index);
|
||||
} else {
|
||||
return AddNode(machine()->Word32AtomicPairLoad(), base, index);
|
||||
}
|
||||
}
|
||||
return AddNode(machine()->Word32AtomicLoad(type), base, index);
|
||||
}
|
||||
|
||||
#if defined(V8_TARGET_BIG_ENDIAN)
|
||||
#define VALUE_HALVES value_high, value
|
||||
#else
|
||||
#define VALUE_HALVES value, value_high
|
||||
#endif
|
||||
|
||||
Node* AtomicStore(MachineRepresentation rep, Node* base, Node* index,
|
||||
Node* value) {
|
||||
Node* value, Node* value_high) {
|
||||
if (rep == MachineRepresentation::kWord64) {
|
||||
if (machine()->Is64()) {
|
||||
DCHECK_NULL(value_high);
|
||||
return AddNode(machine()->Word64AtomicStore(rep), base, index, value);
|
||||
} else {
|
||||
return AddNode(machine()->Word32AtomicPairStore(), base, index,
|
||||
VALUE_HALVES);
|
||||
}
|
||||
}
|
||||
DCHECK_NULL(value_high);
|
||||
return AddNode(machine()->Word32AtomicStore(rep), base, index, value);
|
||||
}
|
||||
#define ATOMIC_FUNCTION(name) \
|
||||
Node* Atomic##name(MachineType rep, Node* base, Node* index, Node* value) { \
|
||||
return AddNode(machine()->Word32Atomic##name(rep), base, index, value); \
|
||||
#define ATOMIC_FUNCTION(name) \
|
||||
Node* Atomic##name(MachineType rep, Node* base, Node* index, Node* value, \
|
||||
Node* value_high) { \
|
||||
if (rep.representation() == MachineRepresentation::kWord64) { \
|
||||
if (machine()->Is64()) { \
|
||||
DCHECK_NULL(value_high); \
|
||||
return AddNode(machine()->Word64Atomic##name(rep), base, index, \
|
||||
value); \
|
||||
} else { \
|
||||
return AddNode(machine()->Word32AtomicPair##name(), base, index, \
|
||||
VALUE_HALVES); \
|
||||
} \
|
||||
} \
|
||||
DCHECK_NULL(value_high); \
|
||||
return AddNode(machine()->Word32Atomic##name(rep), base, index, value); \
|
||||
}
|
||||
ATOMIC_FUNCTION(Exchange);
|
||||
ATOMIC_FUNCTION(Add);
|
||||
@ -190,9 +226,25 @@ class V8_EXPORT_PRIVATE RawMachineAssembler {
|
||||
ATOMIC_FUNCTION(Or);
|
||||
ATOMIC_FUNCTION(Xor);
|
||||
#undef ATOMIC_FUNCTION
|
||||
#undef VALUE_HALVES
|
||||
|
||||
Node* AtomicCompareExchange(MachineType rep, Node* base, Node* index,
|
||||
Node* old_value, Node* new_value) {
|
||||
Node* old_value, Node* old_value_high,
|
||||
Node* new_value, Node* new_value_high) {
|
||||
if (rep.representation() == MachineRepresentation::kWord64) {
|
||||
if (machine()->Is64()) {
|
||||
DCHECK_NULL(old_value_high);
|
||||
DCHECK_NULL(new_value_high);
|
||||
return AddNode(machine()->Word64AtomicCompareExchange(rep), base, index,
|
||||
old_value, new_value);
|
||||
} else {
|
||||
return AddNode(machine()->Word32AtomicPairCompareExchange(), base,
|
||||
index, old_value, old_value_high, new_value,
|
||||
new_value_high);
|
||||
}
|
||||
}
|
||||
DCHECK_NULL(old_value_high);
|
||||
DCHECK_NULL(new_value_high);
|
||||
return AddNode(machine()->Word32AtomicCompareExchange(rep), base, index,
|
||||
old_value, new_value);
|
||||
}
|
||||
|
@ -17,10 +17,21 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
// Other platforms have CSA support, see builtins-sharedarraybuffer-gen.h.
|
||||
#if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_PPC64 || \
|
||||
V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_S390X
|
||||
|
||||
// Note: 32-bit platforms may need ldflags += [ "-latomic" ] in BUILD.gn.
|
||||
|
||||
namespace {
|
||||
|
||||
#if V8_CC_GNU
|
||||
|
||||
// GCC/Clang helpfully warn us that using 64-bit atomics on 32-bit platforms
|
||||
// can be slow. Good to know, but we don't have a choice.
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Watomic-alignment"
|
||||
|
||||
template <typename T>
|
||||
inline T ExchangeSeqCst(T* p, T value) {
|
||||
return __atomic_exchange_n(p, value, __ATOMIC_SEQ_CST);
|
||||
@ -58,6 +69,8 @@ inline T XorSeqCst(T* p, T value) {
|
||||
return __atomic_fetch_xor(p, value, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
#elif V8_CC_MSVC
|
||||
|
||||
#define InterlockedExchange32 _InterlockedExchange
|
||||
@ -67,6 +80,7 @@ inline T XorSeqCst(T* p, T value) {
|
||||
#define InterlockedExchangeAdd16 _InterlockedExchangeAdd16
|
||||
#define InterlockedExchangeAdd8 _InterlockedExchangeAdd8
|
||||
#define InterlockedAnd32 _InterlockedAnd
|
||||
#define InterlockedOr64 _InterlockedOr64
|
||||
#define InterlockedOr32 _InterlockedOr
|
||||
#define InterlockedXor32 _InterlockedXor
|
||||
|
||||
@ -107,6 +121,8 @@ ATOMIC_OPS(int16_t, 16, short) /* NOLINT(runtime/int) */
|
||||
ATOMIC_OPS(uint16_t, 16, short) /* NOLINT(runtime/int) */
|
||||
ATOMIC_OPS(int32_t, 32, long) /* NOLINT(runtime/int) */
|
||||
ATOMIC_OPS(uint32_t, 32, long) /* NOLINT(runtime/int) */
|
||||
ATOMIC_OPS(int64_t, 64, __int64)
|
||||
ATOMIC_OPS(uint64_t, 64, __int64)
|
||||
|
||||
#undef ATOMIC_OPS
|
||||
|
||||
@ -117,6 +133,7 @@ ATOMIC_OPS(uint32_t, 32, long) /* NOLINT(runtime/int) */
|
||||
#undef InterlockedExchangeAdd16
|
||||
#undef InterlockedExchangeAdd8
|
||||
#undef InterlockedAnd32
|
||||
#undef InterlockedOr64
|
||||
#undef InterlockedOr32
|
||||
#undef InterlockedXor32
|
||||
|
||||
@ -159,6 +176,15 @@ inline int32_t FromObject<int32_t>(Handle<Object> number) {
|
||||
return NumberToInt32(*number);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline uint64_t FromObject<uint64_t>(Handle<Object> bigint) {
|
||||
return Handle<BigInt>::cast(bigint)->AsUint64();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline int64_t FromObject<int64_t>(Handle<Object> bigint) {
|
||||
return Handle<BigInt>::cast(bigint)->AsInt64();
|
||||
}
|
||||
|
||||
inline Object* ToObject(Isolate* isolate, int8_t t) { return Smi::FromInt(t); }
|
||||
|
||||
@ -178,14 +204,24 @@ inline Object* ToObject(Isolate* isolate, uint32_t t) {
|
||||
return *isolate->factory()->NewNumber(t);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline Object* DoExchange(Isolate* isolate, void* buffer, size_t index,
|
||||
Handle<Object> obj) {
|
||||
T value = FromObject<T>(obj);
|
||||
T result = ExchangeSeqCst(static_cast<T*>(buffer) + index, value);
|
||||
return ToObject(isolate, result);
|
||||
inline Object* ToObject(Isolate* isolate, int64_t t) {
|
||||
return *BigInt::FromInt64(isolate, t);
|
||||
}
|
||||
|
||||
inline Object* ToObject(Isolate* isolate, uint64_t t) {
|
||||
return *BigInt::FromUint64(isolate, t);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct Exchange {
|
||||
static inline Object* Do(Isolate* isolate, void* buffer, size_t index,
|
||||
Handle<Object> obj) {
|
||||
T value = FromObject<T>(obj);
|
||||
T result = ExchangeSeqCst(static_cast<T*>(buffer) + index, value);
|
||||
return ToObject(isolate, result);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline Object* DoCompareExchange(Isolate* isolate, void* buffer, size_t index,
|
||||
Handle<Object> oldobj, Handle<Object> newobj) {
|
||||
@ -197,44 +233,54 @@ inline Object* DoCompareExchange(Isolate* isolate, void* buffer, size_t index,
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline Object* DoAdd(Isolate* isolate, void* buffer, size_t index,
|
||||
Handle<Object> obj) {
|
||||
T value = FromObject<T>(obj);
|
||||
T result = AddSeqCst(static_cast<T*>(buffer) + index, value);
|
||||
return ToObject(isolate, result);
|
||||
}
|
||||
struct Add {
|
||||
static inline Object* Do(Isolate* isolate, void* buffer, size_t index,
|
||||
Handle<Object> obj) {
|
||||
T value = FromObject<T>(obj);
|
||||
T result = AddSeqCst(static_cast<T*>(buffer) + index, value);
|
||||
return ToObject(isolate, result);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline Object* DoSub(Isolate* isolate, void* buffer, size_t index,
|
||||
Handle<Object> obj) {
|
||||
T value = FromObject<T>(obj);
|
||||
T result = SubSeqCst(static_cast<T*>(buffer) + index, value);
|
||||
return ToObject(isolate, result);
|
||||
}
|
||||
struct Sub {
|
||||
static inline Object* Do(Isolate* isolate, void* buffer, size_t index,
|
||||
Handle<Object> obj) {
|
||||
T value = FromObject<T>(obj);
|
||||
T result = SubSeqCst(static_cast<T*>(buffer) + index, value);
|
||||
return ToObject(isolate, result);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline Object* DoAnd(Isolate* isolate, void* buffer, size_t index,
|
||||
Handle<Object> obj) {
|
||||
T value = FromObject<T>(obj);
|
||||
T result = AndSeqCst(static_cast<T*>(buffer) + index, value);
|
||||
return ToObject(isolate, result);
|
||||
}
|
||||
struct And {
|
||||
static inline Object* Do(Isolate* isolate, void* buffer, size_t index,
|
||||
Handle<Object> obj) {
|
||||
T value = FromObject<T>(obj);
|
||||
T result = AndSeqCst(static_cast<T*>(buffer) + index, value);
|
||||
return ToObject(isolate, result);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline Object* DoOr(Isolate* isolate, void* buffer, size_t index,
|
||||
Handle<Object> obj) {
|
||||
T value = FromObject<T>(obj);
|
||||
T result = OrSeqCst(static_cast<T*>(buffer) + index, value);
|
||||
return ToObject(isolate, result);
|
||||
}
|
||||
struct Or {
|
||||
static inline Object* Do(Isolate* isolate, void* buffer, size_t index,
|
||||
Handle<Object> obj) {
|
||||
T value = FromObject<T>(obj);
|
||||
T result = OrSeqCst(static_cast<T*>(buffer) + index, value);
|
||||
return ToObject(isolate, result);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline Object* DoXor(Isolate* isolate, void* buffer, size_t index,
|
||||
Handle<Object> obj) {
|
||||
T value = FromObject<T>(obj);
|
||||
T result = XorSeqCst(static_cast<T*>(buffer) + index, value);
|
||||
return ToObject(isolate, result);
|
||||
}
|
||||
struct Xor {
|
||||
static inline Object* Do(Isolate* isolate, void* buffer, size_t index,
|
||||
Handle<Object> obj) {
|
||||
T value = FromObject<T>(obj);
|
||||
T result = XorSeqCst(static_cast<T*>(buffer) + index, value);
|
||||
return ToObject(isolate, result);
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
@ -248,22 +294,44 @@ inline Object* DoXor(Isolate* isolate, void* buffer, size_t index,
|
||||
V(Uint32, uint32, UINT32, uint32_t) \
|
||||
V(Int32, int32, INT32, int32_t)
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_AtomicsExchange) {
|
||||
// This is https://tc39.github.io/ecma262/#sec-getmodifysetvalueinbuffer
|
||||
// but also includes the ToInteger/ToBigInt conversion that's part of
|
||||
// https://tc39.github.io/ecma262/#sec-atomicreadmodifywrite
|
||||
template <template <typename> class Op>
|
||||
Object* GetModifySetValueInBuffer(Arguments args, Isolate* isolate) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(3, args.length());
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, sta, 0);
|
||||
CONVERT_SIZE_ARG_CHECKED(index, 1);
|
||||
CONVERT_NUMBER_ARG_HANDLE_CHECKED(value, 2);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, value_obj, 2);
|
||||
CHECK(sta->GetBuffer()->is_shared());
|
||||
CHECK_LT(index, NumberToSize(sta->length()));
|
||||
|
||||
uint8_t* source = static_cast<uint8_t*>(sta->GetBuffer()->backing_store()) +
|
||||
sta->byte_offset();
|
||||
|
||||
if (sta->type() >= kExternalBigInt64Array) {
|
||||
Handle<BigInt> bigint;
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, bigint,
|
||||
BigInt::FromObject(isolate, value_obj));
|
||||
// SharedArrayBuffers are not neuterable.
|
||||
CHECK_LT(index, NumberToSize(sta->length()));
|
||||
if (sta->type() == kExternalBigInt64Array) {
|
||||
return Op<int64_t>::Do(isolate, source, index, bigint);
|
||||
}
|
||||
DCHECK(sta->type() == kExternalBigUint64Array);
|
||||
return Op<uint64_t>::Do(isolate, source, index, bigint);
|
||||
}
|
||||
|
||||
Handle<Object> value;
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, value,
|
||||
Object::ToInteger(isolate, value_obj));
|
||||
// SharedArrayBuffers are not neuterable.
|
||||
CHECK_LT(index, NumberToSize(sta->length()));
|
||||
|
||||
switch (sta->type()) {
|
||||
#define TYPED_ARRAY_CASE(Type, typeName, TYPE, ctype) \
|
||||
case kExternal##Type##Array: \
|
||||
return DoExchange<ctype>(isolate, source, index, value);
|
||||
return Op<ctype>::Do(isolate, source, index, value);
|
||||
|
||||
INTEGER_TYPED_ARRAYS(TYPED_ARRAY_CASE)
|
||||
#undef TYPED_ARRAY_CASE
|
||||
@ -275,23 +343,55 @@ RUNTIME_FUNCTION(Runtime_AtomicsExchange) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_AtomicsExchange) {
|
||||
return GetModifySetValueInBuffer<Exchange>(args, isolate);
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_AtomicsCompareExchange) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(4, args.length());
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, sta, 0);
|
||||
CONVERT_SIZE_ARG_CHECKED(index, 1);
|
||||
CONVERT_NUMBER_ARG_HANDLE_CHECKED(oldobj, 2);
|
||||
CONVERT_NUMBER_ARG_HANDLE_CHECKED(newobj, 3);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, old_value_obj, 2);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, new_value_obj, 3);
|
||||
CHECK(sta->GetBuffer()->is_shared());
|
||||
CHECK_LT(index, NumberToSize(sta->length()));
|
||||
|
||||
uint8_t* source = static_cast<uint8_t*>(sta->GetBuffer()->backing_store()) +
|
||||
sta->byte_offset();
|
||||
|
||||
if (sta->type() >= kExternalBigInt64Array) {
|
||||
Handle<BigInt> old_bigint;
|
||||
Handle<BigInt> new_bigint;
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate, old_bigint, BigInt::FromObject(isolate, old_value_obj));
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate, new_bigint, BigInt::FromObject(isolate, new_value_obj));
|
||||
// SharedArrayBuffers are not neuterable.
|
||||
CHECK_LT(index, NumberToSize(sta->length()));
|
||||
if (sta->type() == kExternalBigInt64Array) {
|
||||
return DoCompareExchange<int64_t>(isolate, source, index, old_bigint,
|
||||
new_bigint);
|
||||
}
|
||||
DCHECK(sta->type() == kExternalBigUint64Array);
|
||||
return DoCompareExchange<uint64_t>(isolate, source, index, old_bigint,
|
||||
new_bigint);
|
||||
}
|
||||
|
||||
Handle<Object> old_value;
|
||||
Handle<Object> new_value;
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, old_value,
|
||||
Object::ToInteger(isolate, old_value_obj));
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, new_value,
|
||||
Object::ToInteger(isolate, new_value_obj));
|
||||
// SharedArrayBuffers are not neuterable.
|
||||
CHECK_LT(index, NumberToSize(sta->length()));
|
||||
|
||||
switch (sta->type()) {
|
||||
#define TYPED_ARRAY_CASE(Type, typeName, TYPE, ctype) \
|
||||
case kExternal##Type##Array: \
|
||||
return DoCompareExchange<ctype>(isolate, source, index, oldobj, newobj);
|
||||
#define TYPED_ARRAY_CASE(Type, typeName, TYPE, ctype) \
|
||||
case kExternal##Type##Array: \
|
||||
return DoCompareExchange<ctype>(isolate, source, index, old_value, \
|
||||
new_value);
|
||||
|
||||
INTEGER_TYPED_ARRAYS(TYPED_ARRAY_CASE)
|
||||
#undef TYPED_ARRAY_CASE
|
||||
@ -306,149 +406,53 @@ RUNTIME_FUNCTION(Runtime_AtomicsCompareExchange) {
|
||||
// ES #sec-atomics.add
|
||||
// Atomics.add( typedArray, index, value )
|
||||
RUNTIME_FUNCTION(Runtime_AtomicsAdd) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(3, args.length());
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, sta, 0);
|
||||
CONVERT_SIZE_ARG_CHECKED(index, 1);
|
||||
CONVERT_NUMBER_ARG_HANDLE_CHECKED(value, 2);
|
||||
CHECK(sta->GetBuffer()->is_shared());
|
||||
CHECK_LT(index, NumberToSize(sta->length()));
|
||||
|
||||
uint8_t* source = static_cast<uint8_t*>(sta->GetBuffer()->backing_store()) +
|
||||
sta->byte_offset();
|
||||
|
||||
switch (sta->type()) {
|
||||
#define TYPED_ARRAY_CASE(Type, typeName, TYPE, ctype) \
|
||||
case kExternal##Type##Array: \
|
||||
return DoAdd<ctype>(isolate, source, index, value);
|
||||
|
||||
INTEGER_TYPED_ARRAYS(TYPED_ARRAY_CASE)
|
||||
#undef TYPED_ARRAY_CASE
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
return GetModifySetValueInBuffer<Add>(args, isolate);
|
||||
}
|
||||
|
||||
// ES #sec-atomics.sub
|
||||
// Atomics.sub( typedArray, index, value )
|
||||
RUNTIME_FUNCTION(Runtime_AtomicsSub) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(3, args.length());
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, sta, 0);
|
||||
CONVERT_SIZE_ARG_CHECKED(index, 1);
|
||||
CONVERT_NUMBER_ARG_HANDLE_CHECKED(value, 2);
|
||||
CHECK(sta->GetBuffer()->is_shared());
|
||||
CHECK_LT(index, NumberToSize(sta->length()));
|
||||
|
||||
uint8_t* source = static_cast<uint8_t*>(sta->GetBuffer()->backing_store()) +
|
||||
sta->byte_offset();
|
||||
|
||||
switch (sta->type()) {
|
||||
#define TYPED_ARRAY_CASE(Type, typeName, TYPE, ctype) \
|
||||
case kExternal##Type##Array: \
|
||||
return DoSub<ctype>(isolate, source, index, value);
|
||||
|
||||
INTEGER_TYPED_ARRAYS(TYPED_ARRAY_CASE)
|
||||
#undef TYPED_ARRAY_CASE
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
return GetModifySetValueInBuffer<Sub>(args, isolate);
|
||||
}
|
||||
|
||||
// ES #sec-atomics.and
|
||||
// Atomics.and( typedArray, index, value )
|
||||
RUNTIME_FUNCTION(Runtime_AtomicsAnd) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(3, args.length());
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, sta, 0);
|
||||
CONVERT_SIZE_ARG_CHECKED(index, 1);
|
||||
CONVERT_NUMBER_ARG_HANDLE_CHECKED(value, 2);
|
||||
CHECK(sta->GetBuffer()->is_shared());
|
||||
CHECK_LT(index, NumberToSize(sta->length()));
|
||||
|
||||
uint8_t* source = static_cast<uint8_t*>(sta->GetBuffer()->backing_store()) +
|
||||
sta->byte_offset();
|
||||
|
||||
switch (sta->type()) {
|
||||
#define TYPED_ARRAY_CASE(Type, typeName, TYPE, ctype) \
|
||||
case kExternal##Type##Array: \
|
||||
return DoAnd<ctype>(isolate, source, index, value);
|
||||
|
||||
INTEGER_TYPED_ARRAYS(TYPED_ARRAY_CASE)
|
||||
#undef TYPED_ARRAY_CASE
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
return GetModifySetValueInBuffer<And>(args, isolate);
|
||||
}
|
||||
|
||||
// ES #sec-atomics.or
|
||||
// Atomics.or( typedArray, index, value )
|
||||
RUNTIME_FUNCTION(Runtime_AtomicsOr) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(3, args.length());
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, sta, 0);
|
||||
CONVERT_SIZE_ARG_CHECKED(index, 1);
|
||||
CONVERT_NUMBER_ARG_HANDLE_CHECKED(value, 2);
|
||||
CHECK(sta->GetBuffer()->is_shared());
|
||||
CHECK_LT(index, NumberToSize(sta->length()));
|
||||
|
||||
uint8_t* source = static_cast<uint8_t*>(sta->GetBuffer()->backing_store()) +
|
||||
sta->byte_offset();
|
||||
|
||||
switch (sta->type()) {
|
||||
#define TYPED_ARRAY_CASE(Type, typeName, TYPE, ctype) \
|
||||
case kExternal##Type##Array: \
|
||||
return DoOr<ctype>(isolate, source, index, value);
|
||||
|
||||
INTEGER_TYPED_ARRAYS(TYPED_ARRAY_CASE)
|
||||
#undef TYPED_ARRAY_CASE
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
return GetModifySetValueInBuffer<Or>(args, isolate);
|
||||
}
|
||||
|
||||
// ES #sec-atomics.xor
|
||||
// Atomics.xor( typedArray, index, value )
|
||||
RUNTIME_FUNCTION(Runtime_AtomicsXor) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(3, args.length());
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, sta, 0);
|
||||
CONVERT_SIZE_ARG_CHECKED(index, 1);
|
||||
CONVERT_NUMBER_ARG_HANDLE_CHECKED(value, 2);
|
||||
CHECK(sta->GetBuffer()->is_shared());
|
||||
CHECK_LT(index, NumberToSize(sta->length()));
|
||||
|
||||
uint8_t* source = static_cast<uint8_t*>(sta->GetBuffer()->backing_store()) +
|
||||
sta->byte_offset();
|
||||
|
||||
switch (sta->type()) {
|
||||
#define TYPED_ARRAY_CASE(Type, typeName, TYPE, ctype) \
|
||||
case kExternal##Type##Array: \
|
||||
return DoXor<ctype>(isolate, source, index, value);
|
||||
|
||||
INTEGER_TYPED_ARRAYS(TYPED_ARRAY_CASE)
|
||||
#undef TYPED_ARRAY_CASE
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
return GetModifySetValueInBuffer<Xor>(args, isolate);
|
||||
}
|
||||
|
||||
#undef INTEGER_TYPED_ARRAYS
|
||||
|
||||
#else
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_AtomicsExchange) { UNREACHABLE(); }
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_AtomicsCompareExchange) { UNREACHABLE(); }
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_AtomicsAdd) { UNREACHABLE(); }
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_AtomicsSub) { UNREACHABLE(); }
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_AtomicsAnd) { UNREACHABLE(); }
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_AtomicsOr) { UNREACHABLE(); }
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_AtomicsXor) { UNREACHABLE(); }
|
||||
|
||||
#endif // V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_PPC64
|
||||
// || V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_S390X
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -502,17 +502,8 @@
|
||||
'built-ins/TypedArray/prototype/set/typedarray-arg-set-values-same-buffer-same-type-sab': ['--harmony-sharedarraybuffer'],
|
||||
|
||||
# https://bugs.chromium.org/p/v8/issues/detail?id=8100
|
||||
'built-ins/Atomics/add/bigint/*': [SKIP],
|
||||
'built-ins/Atomics/and/bigint/*': [SKIP],
|
||||
'built-ins/Atomics/compareExchange/bigint/*': [SKIP],
|
||||
'built-ins/Atomics/exchange/bigint/*': [SKIP],
|
||||
'built-ins/Atomics/load/bigint/*': [SKIP],
|
||||
'built-ins/Atomics/notify/bigint/*': [SKIP],
|
||||
'built-ins/Atomics/or/bigint/*': [SKIP],
|
||||
'built-ins/Atomics/store/bigint/*': [SKIP],
|
||||
'built-ins/Atomics/sub/bigint/*': [SKIP],
|
||||
'built-ins/Atomics/wait/bigint/*': [SKIP],
|
||||
'built-ins/Atomics/xor/bigint/*': [SKIP],
|
||||
|
||||
# https://bugs.chromium.org/p/v8/issues/detail?id=6049
|
||||
'built-ins/Object/internals/DefineOwnProperty/consistent-value-function-caller': [FAIL_SLOPPY],
|
||||
|
Loading…
Reference in New Issue
Block a user