// Copyright 2016 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 #include #include #include "src/base/bits.h" #include "src/base/utils/random-number-generator.h" #include "src/codegen.h" #include "test/cctest/cctest.h" #include "test/cctest/compiler/codegen-tester.h" #include "test/cctest/compiler/graph-builder-tester.h" #include "test/cctest/compiler/value-helper.h" using namespace v8::base; namespace { template void CheckOobValue(Type val) { UNREACHABLE(); } template <> void CheckOobValue(int32_t val) { CHECK_EQ(0, val); } template <> void CheckOobValue(int64_t val) { CHECK_EQ(0, val); } template <> void CheckOobValue(float val) { CHECK(std::isnan(val)); } template <> void CheckOobValue(double val) { CHECK(std::isnan(val)); } } // namespace namespace v8 { namespace internal { namespace compiler { // This is a America! #define A_BILLION 1000000000ULL #define A_GIG (1024ULL * 1024ULL * 1024ULL) TEST(RunLoadInt32) { RawMachineAssemblerTester m; int32_t p1 = 0; // loads directly from this location. m.Return(m.LoadFromPointer(&p1, MachineType::Int32())); FOR_INT32_INPUTS(i) { p1 = *i; CHECK_EQ(p1, m.Call()); } } TEST(RunLoadInt32Offset) { int32_t p1 = 0; // loads directly from this location. int32_t offsets[] = {-2000000, -100, -101, 1, 3, 7, 120, 2000, 2000000000, 0xff}; for (size_t i = 0; i < arraysize(offsets); i++) { RawMachineAssemblerTester m; int32_t offset = offsets[i]; byte* pointer = reinterpret_cast(&p1) - offset; // generate load [#base + #index] m.Return(m.LoadFromPointer(pointer, MachineType::Int32(), offset)); FOR_INT32_INPUTS(j) { p1 = *j; CHECK_EQ(p1, m.Call()); } } } TEST(RunLoadStoreFloat32Offset) { float p1 = 0.0f; // loads directly from this location. float p2 = 0.0f; // and stores directly into this location. FOR_INT32_INPUTS(i) { int32_t magic = 0x2342aabb + *i * 3; RawMachineAssemblerTester m; int32_t offset = *i; byte* from = reinterpret_cast(&p1) - offset; byte* to = reinterpret_cast(&p2) - offset; // generate load [#base + #index] Node* load = m.Load(MachineType::Float32(), m.PointerConstant(from), m.IntPtrConstant(offset)); m.Store(MachineRepresentation::kFloat32, m.PointerConstant(to), m.IntPtrConstant(offset), load, kNoWriteBarrier); m.Return(m.Int32Constant(magic)); FOR_FLOAT32_INPUTS(j) { p1 = *j; p2 = *j - 5; CHECK_EQ(magic, m.Call()); CheckDoubleEq(p1, p2); } } } TEST(RunLoadStoreFloat64Offset) { double p1 = 0; // loads directly from this location. double p2 = 0; // and stores directly into this location. FOR_INT32_INPUTS(i) { int32_t magic = 0x2342aabb + *i * 3; RawMachineAssemblerTester m; int32_t offset = *i; byte* from = reinterpret_cast(&p1) - offset; byte* to = reinterpret_cast(&p2) - offset; // generate load [#base + #index] Node* load = m.Load(MachineType::Float64(), m.PointerConstant(from), m.IntPtrConstant(offset)); m.Store(MachineRepresentation::kFloat64, m.PointerConstant(to), m.IntPtrConstant(offset), load, kNoWriteBarrier); m.Return(m.Int32Constant(magic)); FOR_FLOAT64_INPUTS(j) { p1 = *j; p2 = *j - 5; CHECK_EQ(magic, m.Call()); CheckDoubleEq(p1, p2); } } } namespace { template void RunLoadImmIndex(MachineType rep) { const int kNumElems = 3; Type buffer[kNumElems]; // initialize the buffer with some raw data. byte* raw = reinterpret_cast(buffer); for (size_t i = 0; i < sizeof(buffer); i++) { raw[i] = static_cast((i + sizeof(buffer)) ^ 0xAA); } // Test with various large and small offsets. for (int offset = -1; offset <= 200000; offset *= -5) { for (int i = 0; i < kNumElems; i++) { BufferedRawMachineAssemblerTester m; Node* base = m.PointerConstant(buffer - offset); Node* index = m.Int32Constant((offset + i) * sizeof(buffer[0])); m.Return(m.Load(rep, base, index)); volatile Type expected = buffer[i]; volatile Type actual = m.Call(); CHECK_EQ(expected, actual); } } } template void RunLoadStore(MachineType rep) { const int kNumElems = 4; CType buffer[kNumElems]; for (int32_t x = 0; x < kNumElems; x++) { int32_t y = kNumElems - x - 1; // initialize the buffer with raw data. byte* raw = reinterpret_cast(buffer); for (size_t i = 0; i < sizeof(buffer); i++) { raw[i] = static_cast((i + sizeof(buffer)) ^ 0xAA); } RawMachineAssemblerTester m; int32_t OK = 0x29000 + x; Node* base = m.PointerConstant(buffer); Node* index0 = m.IntPtrConstant(x * sizeof(buffer[0])); Node* load = m.Load(rep, base, index0); Node* index1 = m.IntPtrConstant(y * sizeof(buffer[0])); m.Store(rep.representation(), base, index1, load, kNoWriteBarrier); m.Return(m.Int32Constant(OK)); CHECK(buffer[x] != buffer[y]); CHECK_EQ(OK, m.Call()); CHECK(buffer[x] == buffer[y]); } } } // namespace TEST(RunLoadImmIndex) { RunLoadImmIndex(MachineType::Int8()); RunLoadImmIndex(MachineType::Uint8()); RunLoadImmIndex(MachineType::Int16()); RunLoadImmIndex(MachineType::Uint16()); RunLoadImmIndex(MachineType::Int32()); RunLoadImmIndex(MachineType::Uint32()); RunLoadImmIndex(MachineType::AnyTagged()); RunLoadImmIndex(MachineType::Float32()); RunLoadImmIndex(MachineType::Float64()); #if V8_TARGET_ARCH_64_BIT RunLoadImmIndex(MachineType::Int64()); #endif // TODO(titzer): test various indexing modes. } TEST(RunLoadStore) { RunLoadStore(MachineType::Int8()); RunLoadStore(MachineType::Uint8()); RunLoadStore(MachineType::Int16()); RunLoadStore(MachineType::Uint16()); RunLoadStore(MachineType::Int32()); RunLoadStore(MachineType::Uint32()); RunLoadStore(MachineType::AnyTagged()); RunLoadStore(MachineType::Float32()); RunLoadStore(MachineType::Float64()); #if V8_TARGET_ARCH_64_BIT RunLoadStore(MachineType::Int64()); #endif } #if V8_TARGET_LITTLE_ENDIAN #define LSB(addr, bytes) addr #elif V8_TARGET_BIG_ENDIAN #define LSB(addr, bytes) reinterpret_cast(addr + 1) - bytes #else #error "Unknown Architecture" #endif TEST(RunLoadStoreSignExtend32) { int32_t buffer[4]; RawMachineAssemblerTester m; Node* load8 = m.LoadFromPointer(LSB(&buffer[0], 1), MachineType::Int8()); Node* load16 = m.LoadFromPointer(LSB(&buffer[0], 2), MachineType::Int16()); Node* load32 = m.LoadFromPointer(&buffer[0], MachineType::Int32()); m.StoreToPointer(&buffer[1], MachineRepresentation::kWord32, load8); m.StoreToPointer(&buffer[2], MachineRepresentation::kWord32, load16); m.StoreToPointer(&buffer[3], MachineRepresentation::kWord32, load32); m.Return(load8); FOR_INT32_INPUTS(i) { buffer[0] = *i; CHECK_EQ(static_cast(*i & 0xff), m.Call()); CHECK_EQ(static_cast(*i & 0xff), buffer[1]); CHECK_EQ(static_cast(*i & 0xffff), buffer[2]); CHECK_EQ(*i, buffer[3]); } } TEST(RunLoadStoreZeroExtend32) { uint32_t buffer[4]; RawMachineAssemblerTester m; Node* load8 = m.LoadFromPointer(LSB(&buffer[0], 1), MachineType::Uint8()); Node* load16 = m.LoadFromPointer(LSB(&buffer[0], 2), MachineType::Uint16()); Node* load32 = m.LoadFromPointer(&buffer[0], MachineType::Uint32()); m.StoreToPointer(&buffer[1], MachineRepresentation::kWord32, load8); m.StoreToPointer(&buffer[2], MachineRepresentation::kWord32, load16); m.StoreToPointer(&buffer[3], MachineRepresentation::kWord32, load32); m.Return(load8); FOR_UINT32_INPUTS(i) { buffer[0] = *i; CHECK_EQ((*i & 0xff), m.Call()); CHECK_EQ((*i & 0xff), buffer[1]); CHECK_EQ((*i & 0xffff), buffer[2]); CHECK_EQ(*i, buffer[3]); } } #if V8_TARGET_ARCH_64_BIT TEST(RunCheckedLoadInt64) { int64_t buffer[] = {0x66bbccddeeff0011LL, 0x1122334455667788LL}; RawMachineAssemblerTester m(MachineType::Int32()); Node* base = m.PointerConstant(buffer); Node* index = m.Parameter(0); Node* length = m.Int32Constant(16); Node* load = m.AddNode(m.machine()->CheckedLoad(MachineType::Int64()), base, index, length); m.Return(load); CHECK_EQ(buffer[0], m.Call(0)); CHECK_EQ(buffer[1], m.Call(8)); CheckOobValue(m.Call(16)); } TEST(RunLoadStoreSignExtend64) { if (true) return; // TODO(titzer): sign extension of loads to 64-bit. int64_t buffer[5]; RawMachineAssemblerTester m; Node* load8 = m.LoadFromPointer(LSB(&buffer[0], 1), MachineType::Int8()); Node* load16 = m.LoadFromPointer(LSB(&buffer[0], 2), MachineType::Int16()); Node* load32 = m.LoadFromPointer(LSB(&buffer[0], 4), MachineType::Int32()); Node* load64 = m.LoadFromPointer(&buffer[0], MachineType::Int64()); m.StoreToPointer(&buffer[1], MachineRepresentation::kWord64, load8); m.StoreToPointer(&buffer[2], MachineRepresentation::kWord64, load16); m.StoreToPointer(&buffer[3], MachineRepresentation::kWord64, load32); m.StoreToPointer(&buffer[4], MachineRepresentation::kWord64, load64); m.Return(load8); FOR_INT64_INPUTS(i) { buffer[0] = *i; CHECK_EQ(static_cast(*i & 0xff), m.Call()); CHECK_EQ(static_cast(*i & 0xff), buffer[1]); CHECK_EQ(static_cast(*i & 0xffff), buffer[2]); CHECK_EQ(static_cast(*i & 0xffffffff), buffer[3]); CHECK_EQ(*i, buffer[4]); } } TEST(RunLoadStoreZeroExtend64) { if (kPointerSize < 8) return; uint64_t buffer[5]; RawMachineAssemblerTester m; Node* load8 = m.LoadFromPointer(LSB(&buffer[0], 1), MachineType::Uint8()); Node* load16 = m.LoadFromPointer(LSB(&buffer[0], 2), MachineType::Uint16()); Node* load32 = m.LoadFromPointer(LSB(&buffer[0], 4), MachineType::Uint32()); Node* load64 = m.LoadFromPointer(&buffer[0], MachineType::Uint64()); m.StoreToPointer(&buffer[1], MachineRepresentation::kWord64, load8); m.StoreToPointer(&buffer[2], MachineRepresentation::kWord64, load16); m.StoreToPointer(&buffer[3], MachineRepresentation::kWord64, load32); m.StoreToPointer(&buffer[4], MachineRepresentation::kWord64, load64); m.Return(load8); FOR_UINT64_INPUTS(i) { buffer[0] = *i; CHECK_EQ((*i & 0xff), m.Call()); CHECK_EQ((*i & 0xff), buffer[1]); CHECK_EQ((*i & 0xffff), buffer[2]); CHECK_EQ((*i & 0xffffffff), buffer[3]); CHECK_EQ(*i, buffer[4]); } } TEST(RunCheckedStoreInt64) { const int64_t write = 0x5566778899aabbLL; const int64_t before = 0x33bbccddeeff0011LL; int64_t buffer[] = {before, before}; RawMachineAssemblerTester m(MachineType::Int32()); Node* base = m.PointerConstant(buffer); Node* index = m.Parameter(0); Node* length = m.Int32Constant(16); Node* value = m.Int64Constant(write); Node* store = m.AddNode(m.machine()->CheckedStore(MachineRepresentation::kWord64), base, index, length, value); USE(store); m.Return(m.Int32Constant(11)); CHECK_EQ(11, m.Call(16)); CHECK_EQ(before, buffer[0]); CHECK_EQ(before, buffer[1]); CHECK_EQ(11, m.Call(0)); CHECK_EQ(write, buffer[0]); CHECK_EQ(before, buffer[1]); CHECK_EQ(11, m.Call(8)); CHECK_EQ(write, buffer[0]); CHECK_EQ(write, buffer[1]); } #endif namespace { template void LoadStoreTruncation(MachineType kRepresentation) { IntType input; RawMachineAssemblerTester m; Node* a = m.LoadFromPointer(&input, kRepresentation); Node* ap1 = m.Int32Add(a, m.Int32Constant(1)); m.StoreToPointer(&input, kRepresentation.representation(), ap1); m.Return(ap1); const IntType max = std::numeric_limits::max(); const IntType min = std::numeric_limits::min(); // Test upper bound. input = max; CHECK_EQ(max + 1, m.Call()); CHECK_EQ(min, input); // Test lower bound. input = min; CHECK_EQ(static_cast(max + 2), m.Call()); CHECK_EQ(min + 1, input); // Test all one byte values that are not one byte bounds. for (int i = -127; i < 127; i++) { input = i; int expected = i >= 0 ? i + 1 : max + (i - min) + 2; CHECK_EQ(static_cast(expected), m.Call()); CHECK_EQ(static_cast(i + 1), input); } } } // namespace TEST(RunLoadStoreTruncation) { LoadStoreTruncation(MachineType::Int8()); LoadStoreTruncation(MachineType::Int16()); } void TestRunOobCheckedLoad(bool length_is_immediate) { USE(CheckOobValue); USE(CheckOobValue); USE(CheckOobValue); USE(CheckOobValue); RawMachineAssemblerTester m(MachineType::Int32(), MachineType::Int32()); MachineOperatorBuilder machine(m.zone()); const int32_t kNumElems = 27; const int32_t kLength = kNumElems * 4; int32_t buffer[kNumElems]; Node* base = m.PointerConstant(buffer); Node* offset = m.Parameter(0); Node* len = length_is_immediate ? m.Int32Constant(kLength) : m.Parameter(1); Node* node = m.AddNode(machine.CheckedLoad(MachineType::Int32()), base, offset, len); m.Return(node); { // randomize memory. v8::base::RandomNumberGenerator rng; rng.SetSeed(100); rng.NextBytes(&buffer[0], sizeof(buffer)); } // in-bounds accesses. for (int32_t i = 0; i < kNumElems; i++) { int32_t offset = static_cast(i * sizeof(int32_t)); int32_t expected = buffer[i]; CHECK_EQ(expected, m.Call(offset, kLength)); } // slightly out-of-bounds accesses. for (int32_t i = kLength; i < kNumElems + 30; i++) { int32_t offset = static_cast(i * sizeof(int32_t)); CheckOobValue(m.Call(offset, kLength)); } // way out-of-bounds accesses. for (int32_t offset = -2000000000; offset <= 2000000000; offset += 100000000) { if (offset == 0) continue; CheckOobValue(m.Call(offset, kLength)); } } TEST(RunOobCheckedLoad) { TestRunOobCheckedLoad(false); } TEST(RunOobCheckedLoadImm) { TestRunOobCheckedLoad(true); } void TestRunOobCheckedStore(bool length_is_immediate) { RawMachineAssemblerTester m(MachineType::Int32(), MachineType::Int32()); MachineOperatorBuilder machine(m.zone()); const int32_t kNumElems = 29; const int32_t kValue = -78227234; const int32_t kLength = kNumElems * 4; int32_t buffer[kNumElems + kNumElems]; Node* base = m.PointerConstant(buffer); Node* offset = m.Parameter(0); Node* len = length_is_immediate ? m.Int32Constant(kLength) : m.Parameter(1); Node* val = m.Int32Constant(kValue); m.AddNode(machine.CheckedStore(MachineRepresentation::kWord32), base, offset, len, val); m.Return(val); // in-bounds accesses. for (int32_t i = 0; i < kNumElems; i++) { memset(buffer, 0, sizeof(buffer)); int32_t offset = static_cast(i * sizeof(int32_t)); CHECK_EQ(kValue, m.Call(offset, kLength)); for (int32_t j = 0; j < kNumElems + kNumElems; j++) { if (i == j) { CHECK_EQ(kValue, buffer[j]); } else { CHECK_EQ(0, buffer[j]); } } } memset(buffer, 0, sizeof(buffer)); // slightly out-of-bounds accesses. for (int32_t i = kLength; i < kNumElems + 30; i++) { int32_t offset = static_cast(i * sizeof(int32_t)); CHECK_EQ(kValue, m.Call(offset, kLength)); for (int32_t j = 0; j < kNumElems + kNumElems; j++) { CHECK_EQ(0, buffer[j]); } } // way out-of-bounds accesses. for (int32_t offset = -2000000000; offset <= 2000000000; offset += 100000000) { if (offset == 0) continue; CHECK_EQ(kValue, m.Call(offset, kLength)); for (int32_t j = 0; j < kNumElems + kNumElems; j++) { CHECK_EQ(0, buffer[j]); } } } TEST(RunOobCheckedStore) { TestRunOobCheckedStore(false); } TEST(RunOobCheckedStoreImm) { TestRunOobCheckedStore(true); } // TODO(titzer): CheckedLoad/CheckedStore don't support 64-bit offsets. #define ALLOW_64_BIT_OFFSETS 0 #if V8_TARGET_ARCH_64_BIT && ALLOW_64_BIT_OFFSETS void TestRunOobCheckedLoad64(uint32_t pseudo_base, bool length_is_immediate) { RawMachineAssemblerTester m(MachineType::Uint64(), MachineType::Uint64()); MachineOperatorBuilder machine(m.zone()); const uint32_t kNumElems = 25; const uint32_t kLength = kNumElems * 4; int32_t real_buffer[kNumElems]; // Simulate the end of a large buffer. int32_t* buffer = real_buffer - (pseudo_base / 4); uint64_t length = kLength + pseudo_base; Node* base = m.PointerConstant(buffer); Node* offset = m.Parameter(0); Node* len = length_is_immediate ? m.Int64Constant(length) : m.Parameter(1); Node* node = m.AddNode(machine.CheckedLoad(MachineType::Int32()), base, offset, len); m.Return(node); { // randomize memory. v8::base::RandomNumberGenerator rng; rng.SetSeed(100); rng.NextBytes(&real_buffer[0], sizeof(real_buffer)); } // in-bounds accesses. for (uint32_t i = 0; i < kNumElems; i++) { uint64_t offset = pseudo_base + i * 4; int32_t expected = real_buffer[i]; CHECK_EQ(expected, m.Call(offset, length)); } // in-bounds accesses w.r.t lower 32-bits, but upper bits set. for (uint64_t i = 0x100000000ULL; i != 0; i <<= 1) { uint64_t offset = pseudo_base + i; CheckOobValue(m.Call(offset, length)); } // slightly out-of-bounds accesses. for (uint32_t i = kLength; i < kNumElems + 30; i++) { uint64_t offset = pseudo_base + i * 4; CheckOobValue(0, m.Call(offset, length)); } // way out-of-bounds accesses. for (uint64_t offset = length; offset < 100 * A_BILLION; offset += A_GIG) { if (offset < length) continue; CheckOobValue(0, m.Call(offset, length)); } } TEST(RunOobCheckedLoad64_0) { TestRunOobCheckedLoad64(0, false); TestRunOobCheckedLoad64(0, true); } TEST(RunOobCheckedLoad64_1) { TestRunOobCheckedLoad64(1 * A_BILLION, false); TestRunOobCheckedLoad64(1 * A_BILLION, true); } TEST(RunOobCheckedLoad64_2) { TestRunOobCheckedLoad64(2 * A_BILLION, false); TestRunOobCheckedLoad64(2 * A_BILLION, true); } TEST(RunOobCheckedLoad64_3) { TestRunOobCheckedLoad64(3 * A_BILLION, false); TestRunOobCheckedLoad64(3 * A_BILLION, true); } TEST(RunOobCheckedLoad64_4) { TestRunOobCheckedLoad64(4 * A_BILLION, false); TestRunOobCheckedLoad64(4 * A_BILLION, true); } void TestRunOobCheckedStore64(uint32_t pseudo_base, bool length_is_immediate) { RawMachineAssemblerTester m(MachineType::Uint64(), MachineType::Uint64()); MachineOperatorBuilder machine(m.zone()); const uint32_t kNumElems = 21; const uint32_t kLength = kNumElems * 4; const uint32_t kValue = 897234987; int32_t real_buffer[kNumElems + kNumElems]; // Simulate the end of a large buffer. int32_t* buffer = real_buffer - (pseudo_base / 4); uint64_t length = kLength + pseudo_base; Node* base = m.PointerConstant(buffer); Node* offset = m.Parameter(0); Node* len = length_is_immediate ? m.Int64Constant(length) : m.Parameter(1); Node* val = m.Int32Constant(kValue); m.AddNode(machine.CheckedStore(MachineRepresentation::kWord32), base, offset, len, val); m.Return(val); // in-bounds accesses. for (uint32_t i = 0; i < kNumElems; i++) { memset(real_buffer, 0, sizeof(real_buffer)); uint64_t offset = pseudo_base + i * 4; CHECK_EQ(kValue, m.Call(offset, length)); for (uint32_t j = 0; j < kNumElems + kNumElems; j++) { if (i == j) { CHECK_EQ(kValue, real_buffer[j]); } else { CHECK_EQ(0, real_buffer[j]); } } } memset(real_buffer, 0, sizeof(real_buffer)); // in-bounds accesses w.r.t lower 32-bits, but upper bits set. for (uint64_t i = 0x100000000ULL; i != 0; i <<= 1) { uint64_t offset = pseudo_base + i; CHECK_EQ(kValue, m.Call(offset, length)); for (int32_t j = 0; j < kNumElems + kNumElems; j++) { CHECK_EQ(0, real_buffer[j]); } } // slightly out-of-bounds accesses. for (uint32_t i = kLength; i < kNumElems + 30; i++) { uint64_t offset = pseudo_base + i * 4; CHECK_EQ(kValue, m.Call(offset, length)); for (int32_t j = 0; j < kNumElems + kNumElems; j++) { CHECK_EQ(0, real_buffer[j]); } } // way out-of-bounds accesses. for (uint64_t offset = length; offset < 100 * A_BILLION; offset += A_GIG) { if (offset < length) continue; CHECK_EQ(kValue, m.Call(offset, length)); for (int32_t j = 0; j < kNumElems + kNumElems; j++) { CHECK_EQ(0, real_buffer[j]); } } } TEST(RunOobCheckedStore64_0) { TestRunOobCheckedStore64(0, false); TestRunOobCheckedStore64(0, true); } TEST(RunOobCheckedStore64_1) { TestRunOobCheckedStore64(1 * A_BILLION, false); TestRunOobCheckedStore64(1 * A_BILLION, true); } TEST(RunOobCheckedStore64_2) { TestRunOobCheckedStore64(2 * A_BILLION, false); TestRunOobCheckedStore64(2 * A_BILLION, true); } TEST(RunOobCheckedStore64_3) { TestRunOobCheckedStore64(3 * A_BILLION, false); TestRunOobCheckedStore64(3 * A_BILLION, true); } TEST(RunOobCheckedStore64_4) { TestRunOobCheckedStore64(4 * A_BILLION, false); TestRunOobCheckedStore64(4 * A_BILLION, true); } #endif void TestRunOobCheckedLoad_pseudo(uint64_t x, bool length_is_immediate) { RawMachineAssemblerTester m(MachineType::Uint32(), MachineType::Uint32()); uint32_t pseudo_base = static_cast(x); MachineOperatorBuilder machine(m.zone()); const uint32_t kNumElems = 29; const uint32_t kLength = pseudo_base + kNumElems * 4; int32_t buffer[kNumElems]; Node* base = m.PointerConstant(reinterpret_cast(buffer) - pseudo_base); Node* offset = m.Parameter(0); Node* len = length_is_immediate ? m.Int32Constant(kLength) : m.Parameter(1); Node* node = m.AddNode(machine.CheckedLoad(MachineType::Int32()), base, offset, len); m.Return(node); { // randomize memory. v8::base::RandomNumberGenerator rng; rng.SetSeed(100); rng.NextBytes(&buffer[0], sizeof(buffer)); } // in-bounds accesses. for (uint32_t i = 0; i < kNumElems; i++) { uint32_t offset = static_cast(i * sizeof(int32_t)); uint32_t expected = buffer[i]; CHECK_EQ(expected, m.Call(offset + pseudo_base, kLength)); } // slightly out-of-bounds accesses. for (int32_t i = kNumElems; i < kNumElems + 30; i++) { uint32_t offset = static_cast(i * sizeof(int32_t)); CheckOobValue(m.Call(offset + pseudo_base, kLength)); } // way out-of-bounds accesses. for (uint64_t i = pseudo_base + sizeof(buffer); i < 0xFFFFFFFF; i += A_BILLION) { uint32_t offset = static_cast(i); CheckOobValue(m.Call(offset, kLength)); } } TEST(RunOobCheckedLoad_pseudo0) { TestRunOobCheckedLoad_pseudo(0, false); TestRunOobCheckedLoad_pseudo(0, true); } TEST(RunOobCheckedLoad_pseudo1) { TestRunOobCheckedLoad_pseudo(100000, false); TestRunOobCheckedLoad_pseudo(100000, true); } TEST(RunOobCheckedLoad_pseudo2) { TestRunOobCheckedLoad_pseudo(A_BILLION, false); TestRunOobCheckedLoad_pseudo(A_BILLION, true); } TEST(RunOobCheckedLoad_pseudo3) { TestRunOobCheckedLoad_pseudo(A_GIG, false); TestRunOobCheckedLoad_pseudo(A_GIG, true); } TEST(RunOobCheckedLoad_pseudo4) { TestRunOobCheckedLoad_pseudo(2 * A_BILLION, false); TestRunOobCheckedLoad_pseudo(2 * A_BILLION, true); } TEST(RunOobCheckedLoad_pseudo5) { TestRunOobCheckedLoad_pseudo(2 * A_GIG, false); TestRunOobCheckedLoad_pseudo(2 * A_GIG, true); } TEST(RunOobCheckedLoad_pseudo6) { TestRunOobCheckedLoad_pseudo(3 * A_BILLION, false); TestRunOobCheckedLoad_pseudo(3 * A_BILLION, true); } TEST(RunOobCheckedLoad_pseudo7) { TestRunOobCheckedLoad_pseudo(3 * A_GIG, false); TestRunOobCheckedLoad_pseudo(3 * A_GIG, true); } TEST(RunOobCheckedLoad_pseudo8) { TestRunOobCheckedLoad_pseudo(4 * A_BILLION, false); TestRunOobCheckedLoad_pseudo(4 * A_BILLION, true); } template void TestRunOobCheckedLoadT_pseudo(uint64_t x, bool length_is_immediate) { const int32_t kReturn = 11999; const uint32_t kNumElems = 29; MemType buffer[kNumElems]; uint32_t pseudo_base = static_cast(x); const uint32_t kLength = static_cast(pseudo_base + sizeof(buffer)); MemType result; RawMachineAssemblerTester m(MachineType::Uint32(), MachineType::Uint32()); MachineOperatorBuilder machine(m.zone()); Node* base = m.PointerConstant(reinterpret_cast(buffer) - pseudo_base); Node* offset = m.Parameter(0); Node* len = length_is_immediate ? m.Int32Constant(kLength) : m.Parameter(1); Node* node = m.AddNode(machine.CheckedLoad(MachineTypeForC()), base, offset, len); Node* store = m.StoreToPointer( &result, MachineTypeForC().representation(), node); USE(store); m.Return(m.Int32Constant(kReturn)); { // randomize memory. v8::base::RandomNumberGenerator rng; rng.SetSeed(103); rng.NextBytes(&buffer[0], sizeof(buffer)); } // in-bounds accesses. for (uint32_t i = 0; i < kNumElems; i++) { uint32_t offset = static_cast(i * sizeof(MemType)); MemType expected = buffer[i]; CHECK_EQ(kReturn, m.Call(offset + pseudo_base, kLength)); CHECK_EQ(expected, result); } // slightly out-of-bounds accesses. for (int32_t i = kNumElems; i < kNumElems + 30; i++) { uint32_t offset = static_cast(i * sizeof(MemType)); CHECK_EQ(kReturn, m.Call(offset + pseudo_base, kLength)); CheckOobValue(result); } // way out-of-bounds accesses. for (uint64_t i = pseudo_base + sizeof(buffer); i < 0xFFFFFFFF; i += A_BILLION) { uint32_t offset = static_cast(i); CHECK_EQ(kReturn, m.Call(offset, kLength)); CheckOobValue(result); } } TEST(RunOobCheckedLoadT_pseudo0) { TestRunOobCheckedLoadT_pseudo(0, false); TestRunOobCheckedLoadT_pseudo(0, true); TestRunOobCheckedLoadT_pseudo(0, false); TestRunOobCheckedLoadT_pseudo(0, true); TestRunOobCheckedLoadT_pseudo(0, false); TestRunOobCheckedLoadT_pseudo(0, true); } TEST(RunOobCheckedLoadT_pseudo1) { TestRunOobCheckedLoadT_pseudo(100000, false); TestRunOobCheckedLoadT_pseudo(100000, true); TestRunOobCheckedLoadT_pseudo(100000, false); TestRunOobCheckedLoadT_pseudo(100000, true); TestRunOobCheckedLoadT_pseudo(100000, false); TestRunOobCheckedLoadT_pseudo(100000, true); } TEST(RunOobCheckedLoadT_pseudo2) { TestRunOobCheckedLoadT_pseudo(A_BILLION, false); TestRunOobCheckedLoadT_pseudo(A_BILLION, true); TestRunOobCheckedLoadT_pseudo(A_BILLION, false); TestRunOobCheckedLoadT_pseudo(A_BILLION, true); TestRunOobCheckedLoadT_pseudo(A_BILLION, false); TestRunOobCheckedLoadT_pseudo(A_BILLION, true); } TEST(RunOobCheckedLoadT_pseudo3) { TestRunOobCheckedLoadT_pseudo(A_GIG, false); TestRunOobCheckedLoadT_pseudo(A_GIG, true); TestRunOobCheckedLoadT_pseudo(A_GIG, false); TestRunOobCheckedLoadT_pseudo(A_GIG, true); TestRunOobCheckedLoadT_pseudo(A_GIG, false); TestRunOobCheckedLoadT_pseudo(A_GIG, true); } TEST(RunOobCheckedLoadT_pseudo4) { TestRunOobCheckedLoadT_pseudo(2 * A_BILLION, false); TestRunOobCheckedLoadT_pseudo(2 * A_BILLION, true); TestRunOobCheckedLoadT_pseudo(2 * A_BILLION, false); TestRunOobCheckedLoadT_pseudo(2 * A_BILLION, true); TestRunOobCheckedLoadT_pseudo(2 * A_BILLION, false); TestRunOobCheckedLoadT_pseudo(2 * A_BILLION, true); } TEST(RunOobCheckedLoadT_pseudo5) { TestRunOobCheckedLoadT_pseudo(2 * A_GIG, false); TestRunOobCheckedLoadT_pseudo(2 * A_GIG, true); TestRunOobCheckedLoadT_pseudo(2 * A_GIG, false); TestRunOobCheckedLoadT_pseudo(2 * A_GIG, true); TestRunOobCheckedLoadT_pseudo(2 * A_GIG, false); TestRunOobCheckedLoadT_pseudo(2 * A_GIG, true); } TEST(RunOobCheckedLoadT_pseudo6) { TestRunOobCheckedLoadT_pseudo(3 * A_BILLION, false); TestRunOobCheckedLoadT_pseudo(3 * A_BILLION, true); TestRunOobCheckedLoadT_pseudo(3 * A_BILLION, false); TestRunOobCheckedLoadT_pseudo(3 * A_BILLION, true); TestRunOobCheckedLoadT_pseudo(3 * A_BILLION, false); TestRunOobCheckedLoadT_pseudo(3 * A_BILLION, true); } TEST(RunOobCheckedLoadT_pseudo7) { TestRunOobCheckedLoadT_pseudo(3 * A_GIG, false); TestRunOobCheckedLoadT_pseudo(3 * A_GIG, true); TestRunOobCheckedLoadT_pseudo(3 * A_GIG, false); TestRunOobCheckedLoadT_pseudo(3 * A_GIG, true); TestRunOobCheckedLoadT_pseudo(3 * A_GIG, false); TestRunOobCheckedLoadT_pseudo(3 * A_GIG, true); } TEST(RunOobCheckedLoadT_pseudo8) { TestRunOobCheckedLoadT_pseudo(4 * A_BILLION, false); TestRunOobCheckedLoadT_pseudo(4 * A_BILLION, true); TestRunOobCheckedLoadT_pseudo(4 * A_BILLION, false); TestRunOobCheckedLoadT_pseudo(4 * A_BILLION, true); TestRunOobCheckedLoadT_pseudo(4 * A_BILLION, false); TestRunOobCheckedLoadT_pseudo(4 * A_BILLION, true); } } // namespace compiler } // namespace internal } // namespace v8