summaryrefslogtreecommitdiff
path: root/deps/v8/test/cctest/compiler/test-atomic-load-store-codegen.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/test/cctest/compiler/test-atomic-load-store-codegen.cc')
-rw-r--r--deps/v8/test/cctest/compiler/test-atomic-load-store-codegen.cc398
1 files changed, 398 insertions, 0 deletions
diff --git a/deps/v8/test/cctest/compiler/test-atomic-load-store-codegen.cc b/deps/v8/test/cctest/compiler/test-atomic-load-store-codegen.cc
new file mode 100644
index 0000000000..5e8372d538
--- /dev/null
+++ b/deps/v8/test/cctest/compiler/test-atomic-load-store-codegen.cc
@@ -0,0 +1,398 @@
+// Copyright 2021 the V8 project authors. All rights reserved. Use of this
+// source code is governed by a BSD-style license that can be found in the
+// LICENSE file.
+
+#include "src/base/bits.h"
+#include "src/objects/objects-inl.h"
+#include "test/cctest/cctest.h"
+#include "test/cctest/compiler/codegen-tester.h"
+#include "test/cctest/compiler/value-helper.h"
+
+namespace v8 {
+namespace internal {
+namespace compiler {
+
+#if V8_TARGET_LITTLE_ENDIAN
+#define LSB(addr, bytes) addr
+#elif V8_TARGET_BIG_ENDIAN
+#define LSB(addr, bytes) reinterpret_cast<byte*>(addr + 1) - (bytes)
+#else
+#error "Unknown Architecture"
+#endif
+
+#define TEST_ATOMIC_LOAD_INTEGER(ctype, itype, mach_type, order) \
+ do { \
+ ctype buffer[1]; \
+ \
+ RawMachineAssemblerTester<ctype> m; \
+ Node* base = m.PointerConstant(&buffer[0]); \
+ Node* index = m.Int32Constant(0); \
+ AtomicLoadParameters params(mach_type, order); \
+ if (mach_type.MemSize() == 8) { \
+ m.Return(m.AtomicLoad64(params, base, index)); \
+ } else { \
+ m.Return(m.AtomicLoad(params, base, index)); \
+ } \
+ \
+ FOR_INPUTS(ctype, itype, i) { \
+ buffer[0] = i; \
+ CHECK_EQ(i, m.Call()); \
+ } \
+ } while (false)
+
+TEST(AcquireLoadInteger) {
+ TEST_ATOMIC_LOAD_INTEGER(int8_t, int8, MachineType::Int8(),
+ AtomicMemoryOrder::kAcqRel);
+ TEST_ATOMIC_LOAD_INTEGER(uint8_t, uint8, MachineType::Uint8(),
+ AtomicMemoryOrder::kAcqRel);
+ TEST_ATOMIC_LOAD_INTEGER(int16_t, int16, MachineType::Int16(),
+ AtomicMemoryOrder::kAcqRel);
+ TEST_ATOMIC_LOAD_INTEGER(uint16_t, uint16, MachineType::Uint16(),
+ AtomicMemoryOrder::kAcqRel);
+ TEST_ATOMIC_LOAD_INTEGER(int32_t, int32, MachineType::Int32(),
+ AtomicMemoryOrder::kAcqRel);
+ TEST_ATOMIC_LOAD_INTEGER(uint32_t, uint32, MachineType::Uint32(),
+ AtomicMemoryOrder::kAcqRel);
+#if V8_TARGET_ARCH_64_BIT
+ TEST_ATOMIC_LOAD_INTEGER(uint64_t, uint64, MachineType::Uint64(),
+ AtomicMemoryOrder::kAcqRel);
+#endif
+}
+
+TEST(SeqCstLoadInteger) {
+ TEST_ATOMIC_LOAD_INTEGER(int8_t, int8, MachineType::Int8(),
+ AtomicMemoryOrder::kSeqCst);
+ TEST_ATOMIC_LOAD_INTEGER(uint8_t, uint8, MachineType::Uint8(),
+ AtomicMemoryOrder::kSeqCst);
+ TEST_ATOMIC_LOAD_INTEGER(int16_t, int16, MachineType::Int16(),
+ AtomicMemoryOrder::kSeqCst);
+ TEST_ATOMIC_LOAD_INTEGER(uint16_t, uint16, MachineType::Uint16(),
+ AtomicMemoryOrder::kSeqCst);
+ TEST_ATOMIC_LOAD_INTEGER(int32_t, int32, MachineType::Int32(),
+ AtomicMemoryOrder::kSeqCst);
+ TEST_ATOMIC_LOAD_INTEGER(uint32_t, uint32, MachineType::Uint32(),
+ AtomicMemoryOrder::kSeqCst);
+#if V8_TARGET_ARCH_64_BIT
+ TEST_ATOMIC_LOAD_INTEGER(uint64_t, uint64, MachineType::Uint64(),
+ AtomicMemoryOrder::kSeqCst);
+#endif
+}
+
+namespace {
+// Mostly same as CHECK_EQ() but customized for compressed tagged values.
+template <typename CType>
+void CheckEq(CType in_value, CType out_value) {
+ CHECK_EQ(in_value, out_value);
+}
+
+#ifdef V8_COMPRESS_POINTERS
+// Specializations for checking the result of compressing store.
+template <>
+void CheckEq<Object>(Object in_value, Object out_value) {
+ // Compare only lower 32-bits of the value because tagged load/stores are
+ // 32-bit operations anyway.
+ CHECK_EQ(static_cast<Tagged_t>(in_value.ptr()),
+ static_cast<Tagged_t>(out_value.ptr()));
+}
+
+template <>
+void CheckEq<HeapObject>(HeapObject in_value, HeapObject out_value) {
+ return CheckEq<Object>(in_value, out_value);
+}
+
+template <>
+void CheckEq<Smi>(Smi in_value, Smi out_value) {
+ return CheckEq<Object>(in_value, out_value);
+}
+#endif
+
+template <typename TaggedT>
+void InitBuffer(TaggedT* buffer, size_t length, MachineType type) {
+ const size_t kBufferSize = sizeof(TaggedT) * length;
+
+ // Tagged field loads require values to be properly tagged because of
+ // pointer decompression that may be happenning during load.
+ Isolate* isolate = CcTest::InitIsolateOnce();
+ Smi* smi_view = reinterpret_cast<Smi*>(&buffer[0]);
+ if (type.IsTaggedSigned()) {
+ for (size_t i = 0; i < length; i++) {
+ smi_view[i] = Smi::FromInt(static_cast<int>(i + kBufferSize) ^ 0xABCDEF0);
+ }
+ } else {
+ memcpy(&buffer[0], &isolate->roots_table(), kBufferSize);
+ if (!type.IsTaggedPointer()) {
+ // Also add some Smis if we are checking AnyTagged case.
+ for (size_t i = 0; i < length / 2; i++) {
+ smi_view[i] =
+ Smi::FromInt(static_cast<int>(i + kBufferSize) ^ 0xABCDEF0);
+ }
+ }
+ }
+}
+
+template <typename TaggedT>
+void AtomicLoadTagged(MachineType type, AtomicMemoryOrder order) {
+ const int kNumElems = 16;
+ TaggedT buffer[kNumElems];
+
+ InitBuffer(buffer, kNumElems, type);
+
+ for (int i = 0; i < kNumElems; i++) {
+ BufferedRawMachineAssemblerTester<TaggedT> m;
+ TaggedT* base_pointer = &buffer[0];
+ if (COMPRESS_POINTERS_BOOL) {
+ base_pointer = reinterpret_cast<TaggedT*>(LSB(base_pointer, kTaggedSize));
+ }
+ Node* base = m.PointerConstant(base_pointer);
+ Node* index = m.Int32Constant(i * sizeof(buffer[0]));
+ AtomicLoadParameters params(type, order);
+ Node* load;
+ if (kTaggedSize == 8) {
+ load = m.AtomicLoad64(params, base, index);
+ } else {
+ load = m.AtomicLoad(params, base, index);
+ }
+ m.Return(load);
+ CheckEq<TaggedT>(buffer[i], m.Call());
+ }
+}
+} // namespace
+
+TEST(AcquireLoadTagged) {
+ AtomicLoadTagged<Smi>(MachineType::TaggedSigned(),
+ AtomicMemoryOrder::kAcqRel);
+ AtomicLoadTagged<HeapObject>(MachineType::TaggedPointer(),
+ AtomicMemoryOrder::kAcqRel);
+ AtomicLoadTagged<Object>(MachineType::AnyTagged(),
+ AtomicMemoryOrder::kAcqRel);
+}
+
+TEST(SeqCstLoadTagged) {
+ AtomicLoadTagged<Smi>(MachineType::TaggedSigned(),
+ AtomicMemoryOrder::kSeqCst);
+ AtomicLoadTagged<HeapObject>(MachineType::TaggedPointer(),
+ AtomicMemoryOrder::kSeqCst);
+ AtomicLoadTagged<Object>(MachineType::AnyTagged(),
+ AtomicMemoryOrder::kSeqCst);
+}
+
+#define TEST_ATOMIC_STORE_INTEGER(ctype, itype, mach_type, order) \
+ do { \
+ ctype buffer[1]; \
+ buffer[0] = static_cast<ctype>(-1); \
+ \
+ BufferedRawMachineAssemblerTester<int32_t> m(mach_type); \
+ Node* value = m.Parameter(0); \
+ Node* base = m.PointerConstant(&buffer[0]); \
+ Node* index = m.Int32Constant(0); \
+ AtomicStoreParameters params(mach_type.representation(), kNoWriteBarrier, \
+ order); \
+ if (mach_type.MemSize() == 8) { \
+ m.AtomicStore64(params, base, index, value, nullptr); \
+ } else { \
+ m.AtomicStore(params, base, index, value); \
+ } \
+ \
+ int32_t OK = 0x29000; \
+ m.Return(m.Int32Constant(OK)); \
+ \
+ FOR_INPUTS(ctype, itype, i) { \
+ CHECK_EQ(OK, m.Call(i)); \
+ CHECK_EQ(i, buffer[0]); \
+ } \
+ } while (false)
+
+TEST(ReleaseStoreInteger) {
+ TEST_ATOMIC_STORE_INTEGER(int8_t, int8, MachineType::Int8(),
+ AtomicMemoryOrder::kAcqRel);
+ TEST_ATOMIC_STORE_INTEGER(uint8_t, uint8, MachineType::Uint8(),
+ AtomicMemoryOrder::kAcqRel);
+ TEST_ATOMIC_STORE_INTEGER(int16_t, int16, MachineType::Int16(),
+ AtomicMemoryOrder::kAcqRel);
+ TEST_ATOMIC_STORE_INTEGER(uint16_t, uint16, MachineType::Uint16(),
+ AtomicMemoryOrder::kAcqRel);
+ TEST_ATOMIC_STORE_INTEGER(int32_t, int32, MachineType::Int32(),
+ AtomicMemoryOrder::kAcqRel);
+ TEST_ATOMIC_STORE_INTEGER(uint32_t, uint32, MachineType::Uint32(),
+ AtomicMemoryOrder::kAcqRel);
+#if V8_TARGET_ARCH_64_BIT
+ TEST_ATOMIC_STORE_INTEGER(uint64_t, uint64, MachineType::Uint64(),
+ AtomicMemoryOrder::kAcqRel);
+#endif
+}
+
+TEST(SeqCstStoreInteger) {
+ TEST_ATOMIC_STORE_INTEGER(int8_t, int8, MachineType::Int8(),
+ AtomicMemoryOrder::kSeqCst);
+ TEST_ATOMIC_STORE_INTEGER(uint8_t, uint8, MachineType::Uint8(),
+ AtomicMemoryOrder::kSeqCst);
+ TEST_ATOMIC_STORE_INTEGER(int16_t, int16, MachineType::Int16(),
+ AtomicMemoryOrder::kSeqCst);
+ TEST_ATOMIC_STORE_INTEGER(uint16_t, uint16, MachineType::Uint16(),
+ AtomicMemoryOrder::kSeqCst);
+ TEST_ATOMIC_STORE_INTEGER(int32_t, int32, MachineType::Int32(),
+ AtomicMemoryOrder::kSeqCst);
+ TEST_ATOMIC_STORE_INTEGER(uint32_t, uint32, MachineType::Uint32(),
+ AtomicMemoryOrder::kSeqCst);
+#if V8_TARGET_ARCH_64_BIT
+ TEST_ATOMIC_STORE_INTEGER(uint64_t, uint64, MachineType::Uint64(),
+ AtomicMemoryOrder::kSeqCst);
+#endif
+}
+
+namespace {
+template <typename TaggedT>
+void AtomicStoreTagged(MachineType type, AtomicMemoryOrder order) {
+ // This tests that tagged values are correctly transferred by atomic loads and
+ // stores from in_buffer to out_buffer. For each particular element in
+ // in_buffer, it is copied to a different index in out_buffer, and all other
+ // indices are zapped, to test instructions of the correct width are emitted.
+
+ const int kNumElems = 16;
+ TaggedT in_buffer[kNumElems];
+ TaggedT out_buffer[kNumElems];
+ uintptr_t zap_data[] = {kZapValue, kZapValue};
+ TaggedT zap_value;
+
+ STATIC_ASSERT(sizeof(TaggedT) <= sizeof(zap_data));
+ MemCopy(&zap_value, &zap_data, sizeof(TaggedT));
+ InitBuffer(in_buffer, kNumElems, type);
+
+#ifdef V8_TARGET_BIG_ENDIAN
+ int offset = sizeof(TaggedT) - ElementSizeInBytes(type.representation());
+#else
+ int offset = 0;
+#endif
+
+ for (int32_t x = 0; x < kNumElems; x++) {
+ int32_t y = kNumElems - x - 1;
+
+ RawMachineAssemblerTester<int32_t> m;
+ int32_t OK = 0x29000 + x;
+ Node* in_base = m.PointerConstant(in_buffer);
+ Node* in_index = m.IntPtrConstant(x * sizeof(TaggedT) + offset);
+ Node* out_base = m.PointerConstant(out_buffer);
+ Node* out_index = m.IntPtrConstant(y * sizeof(TaggedT) + offset);
+
+ Node* load;
+ AtomicLoadParameters load_params(type, order);
+ AtomicStoreParameters store_params(type.representation(), kNoWriteBarrier,
+ order);
+ if (kTaggedSize == 4) {
+ load = m.AtomicLoad(load_params, in_base, in_index);
+ m.AtomicStore(store_params, out_base, out_index, load);
+ } else {
+ DCHECK(m.machine()->Is64());
+ load = m.AtomicLoad64(load_params, in_base, in_index);
+ m.AtomicStore64(store_params, out_base, out_index, load, nullptr);
+ }
+
+ m.Return(m.Int32Constant(OK));
+
+ for (int32_t z = 0; z < kNumElems; z++) {
+ out_buffer[z] = zap_value;
+ }
+ CHECK_NE(in_buffer[x], out_buffer[y]);
+ CHECK_EQ(OK, m.Call());
+ // Mostly same as CHECK_EQ() but customized for compressed tagged values.
+ CheckEq<TaggedT>(in_buffer[x], out_buffer[y]);
+ for (int32_t z = 0; z < kNumElems; z++) {
+ if (z != y) CHECK_EQ(zap_value, out_buffer[z]);
+ }
+ }
+}
+} // namespace
+
+TEST(ReleaseStoreTagged) {
+ AtomicStoreTagged<Smi>(MachineType::TaggedSigned(),
+ AtomicMemoryOrder::kAcqRel);
+ AtomicStoreTagged<HeapObject>(MachineType::TaggedPointer(),
+ AtomicMemoryOrder::kAcqRel);
+ AtomicStoreTagged<Object>(MachineType::AnyTagged(),
+ AtomicMemoryOrder::kAcqRel);
+}
+
+TEST(SeqCstStoreTagged) {
+ AtomicStoreTagged<Smi>(MachineType::TaggedSigned(),
+ AtomicMemoryOrder::kSeqCst);
+ AtomicStoreTagged<HeapObject>(MachineType::TaggedPointer(),
+ AtomicMemoryOrder::kSeqCst);
+ AtomicStoreTagged<Object>(MachineType::AnyTagged(),
+ AtomicMemoryOrder::kSeqCst);
+}
+
+#if V8_TARGET_ARCH_32_BIT
+
+namespace {
+void TestAtomicPairLoadInteger(AtomicMemoryOrder order) {
+ uint64_t buffer[1];
+ uint32_t high;
+ uint32_t low;
+
+ BufferedRawMachineAssemblerTester<int32_t> m;
+ Node* base = m.PointerConstant(&buffer[0]);
+ Node* index = m.Int32Constant(0);
+
+ Node* pair_load = m.AtomicLoad64(
+ AtomicLoadParameters(MachineType::Uint64(), order), base, index);
+ m.StoreToPointer(&low, MachineRepresentation::kWord32,
+ m.Projection(0, pair_load));
+ m.StoreToPointer(&high, MachineRepresentation::kWord32,
+ m.Projection(1, pair_load));
+
+ int32_t OK = 0x29000;
+ m.Return(m.Int32Constant(OK));
+
+ FOR_UINT64_INPUTS(i) {
+ buffer[0] = i;
+ CHECK_EQ(OK, m.Call());
+ CHECK_EQ(i, make_uint64(high, low));
+ }
+}
+} // namespace
+
+TEST(AcquirePairLoadInteger) {
+ TestAtomicPairLoadInteger(AtomicMemoryOrder::kAcqRel);
+}
+
+TEST(SeqCstPairLoadInteger) {
+ TestAtomicPairLoadInteger(AtomicMemoryOrder::kSeqCst);
+}
+
+namespace {
+void TestAtomicPairStoreInteger(AtomicMemoryOrder order) {
+ uint64_t buffer[1];
+
+ BufferedRawMachineAssemblerTester<int32_t> m(MachineType::Uint32(),
+ MachineType::Uint32());
+ Node* base = m.PointerConstant(&buffer[0]);
+ Node* index = m.Int32Constant(0);
+
+ m.AtomicStore64(AtomicStoreParameters(MachineRepresentation::kWord64,
+ kNoWriteBarrier, order),
+ base, index, m.Parameter(0), m.Parameter(1));
+
+ int32_t OK = 0x29000;
+ m.Return(m.Int32Constant(OK));
+
+ FOR_UINT64_INPUTS(i) {
+ CHECK_EQ(OK, m.Call(static_cast<uint32_t>(i & 0xFFFFFFFF),
+ static_cast<uint32_t>(i >> 32)));
+ CHECK_EQ(i, buffer[0]);
+ }
+}
+} // namespace
+
+TEST(ReleasePairStoreInteger) {
+ TestAtomicPairStoreInteger(AtomicMemoryOrder::kAcqRel);
+}
+
+TEST(SeqCstPairStoreInteger) {
+ TestAtomicPairStoreInteger(AtomicMemoryOrder::kSeqCst);
+}
+
+#endif // V8_TARGET_ARCH_32_BIT
+
+} // namespace compiler
+} // namespace internal
+} // namespace v8