diff options
Diffstat (limited to 'deps/v8/test/cctest/test-simulator-arm.cc')
-rw-r--r-- | deps/v8/test/cctest/test-simulator-arm.cc | 387 |
1 files changed, 387 insertions, 0 deletions
diff --git a/deps/v8/test/cctest/test-simulator-arm.cc b/deps/v8/test/cctest/test-simulator-arm.cc new file mode 100644 index 0000000000..a34837665e --- /dev/null +++ b/deps/v8/test/cctest/test-simulator-arm.cc @@ -0,0 +1,387 @@ +// Copyright 2016 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "src/v8.h" +#include "test/cctest/cctest.h" + +#include "src/arm/simulator-arm.h" +#include "src/disassembler.h" +#include "src/factory.h" +#include "src/macro-assembler.h" + +#if defined(USE_SIMULATOR) + +#ifndef V8_TARGET_LITTLE_ENDIAN +#error Expected ARM to be little-endian +#endif + +using namespace v8::base; +using namespace v8::internal; + +// Define these function prototypes to match JSEntryFunction in execution.cc. +typedef Object* (*F1)(int x, int p1, int p2, int p3, int p4); +typedef Object* (*F3)(void* p0, int p1, int p2, int p3, int p4); + +#define __ assm. + +struct MemoryAccess { + enum class Kind { + None, + Load, + LoadExcl, + Store, + StoreExcl, + }; + + enum class Size { + Byte, + HalfWord, + Word, + }; + + MemoryAccess() : kind(Kind::None) {} + MemoryAccess(Kind kind, Size size, size_t offset, int value = 0) + : kind(kind), size(size), offset(offset), value(value) {} + + Kind kind; + Size size; + size_t offset; + int value; +}; + +struct TestData { + explicit TestData(int w) : w(w) {} + + union { + int32_t w; + int16_t h; + int8_t b; + }; + int dummy; +}; + +static void AssembleMemoryAccess(Assembler* assembler, MemoryAccess access, + Register dest_reg, Register value_reg, + Register addr_reg) { + Assembler& assm = *assembler; + __ add(addr_reg, r0, Operand(access.offset)); + + switch (access.kind) { + case MemoryAccess::Kind::None: + break; + + case MemoryAccess::Kind::Load: + switch (access.size) { + case MemoryAccess::Size::Byte: + __ ldrb(value_reg, MemOperand(addr_reg)); + break; + + case MemoryAccess::Size::HalfWord: + __ ldrh(value_reg, MemOperand(addr_reg)); + break; + + case MemoryAccess::Size::Word: + __ ldr(value_reg, MemOperand(addr_reg)); + break; + } + break; + + case MemoryAccess::Kind::LoadExcl: + switch (access.size) { + case MemoryAccess::Size::Byte: + __ ldrexb(value_reg, addr_reg); + break; + + case MemoryAccess::Size::HalfWord: + __ ldrexh(value_reg, addr_reg); + break; + + case MemoryAccess::Size::Word: + __ ldrex(value_reg, addr_reg); + break; + } + break; + + case MemoryAccess::Kind::Store: + switch (access.size) { + case MemoryAccess::Size::Byte: + __ mov(value_reg, Operand(access.value)); + __ strb(value_reg, MemOperand(addr_reg)); + break; + + case MemoryAccess::Size::HalfWord: + __ mov(value_reg, Operand(access.value)); + __ strh(value_reg, MemOperand(addr_reg)); + break; + + case MemoryAccess::Size::Word: + __ mov(value_reg, Operand(access.value)); + __ str(value_reg, MemOperand(addr_reg)); + break; + } + break; + + case MemoryAccess::Kind::StoreExcl: + switch (access.size) { + case MemoryAccess::Size::Byte: + __ mov(value_reg, Operand(access.value)); + __ strexb(dest_reg, value_reg, addr_reg); + break; + + case MemoryAccess::Size::HalfWord: + __ mov(value_reg, Operand(access.value)); + __ strexh(dest_reg, value_reg, addr_reg); + break; + + case MemoryAccess::Size::Word: + __ mov(value_reg, Operand(access.value)); + __ strex(dest_reg, value_reg, addr_reg); + break; + } + break; + } +} + +static void AssembleLoadExcl(Assembler* assembler, MemoryAccess access, + Register value_reg, Register addr_reg) { + DCHECK(access.kind == MemoryAccess::Kind::LoadExcl); + AssembleMemoryAccess(assembler, access, no_reg, value_reg, addr_reg); +} + +static void AssembleStoreExcl(Assembler* assembler, MemoryAccess access, + Register dest_reg, Register value_reg, + Register addr_reg) { + DCHECK(access.kind == MemoryAccess::Kind::StoreExcl); + AssembleMemoryAccess(assembler, access, dest_reg, value_reg, addr_reg); +} + +static void TestInvalidateExclusiveAccess( + TestData initial_data, MemoryAccess access1, MemoryAccess access2, + MemoryAccess access3, int expected_res, TestData expected_data) { + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + + Assembler assm(isolate, NULL, 0); + + AssembleLoadExcl(&assm, access1, r1, r1); + AssembleMemoryAccess(&assm, access2, r3, r2, r1); + AssembleStoreExcl(&assm, access3, r0, r3, r1); + + __ mov(pc, Operand(lr)); + + CodeDesc desc; + assm.GetCode(&desc); + Handle<Code> code = isolate->factory()->NewCode( + desc, Code::ComputeFlags(Code::STUB), Handle<Code>()); + F3 f = FUNCTION_CAST<F3>(code->entry()); + TestData t = initial_data; + + int res = + reinterpret_cast<int>(CALL_GENERATED_CODE(isolate, f, &t, 0, 0, 0, 0)); + CHECK_EQ(expected_res, res); + switch (access3.size) { + case MemoryAccess::Size::Byte: + CHECK_EQ(expected_data.b, t.b); + break; + + case MemoryAccess::Size::HalfWord: + CHECK_EQ(expected_data.h, t.h); + break; + + case MemoryAccess::Size::Word: + CHECK_EQ(expected_data.w, t.w); + break; + } +} + +TEST(simulator_invalidate_exclusive_access) { + using Kind = MemoryAccess::Kind; + using Size = MemoryAccess::Size; + + MemoryAccess ldrex_w(Kind::LoadExcl, Size::Word, offsetof(TestData, w)); + MemoryAccess strex_w(Kind::StoreExcl, Size::Word, offsetof(TestData, w), 7); + + // Address mismatch. + TestInvalidateExclusiveAccess( + TestData(1), ldrex_w, + MemoryAccess(Kind::LoadExcl, Size::Word, offsetof(TestData, dummy)), + strex_w, 1, TestData(1)); + + // Size mismatch. + TestInvalidateExclusiveAccess( + TestData(1), ldrex_w, MemoryAccess(), + MemoryAccess(Kind::StoreExcl, Size::HalfWord, offsetof(TestData, w), 7), + 1, TestData(1)); + + // Load between ldrex/strex. + TestInvalidateExclusiveAccess( + TestData(1), ldrex_w, + MemoryAccess(Kind::Load, Size::Word, offsetof(TestData, dummy)), strex_w, + 1, TestData(1)); + + // Store between ldrex/strex. + TestInvalidateExclusiveAccess( + TestData(1), ldrex_w, + MemoryAccess(Kind::Store, Size::Word, offsetof(TestData, dummy)), strex_w, + 1, TestData(1)); + + // Match + TestInvalidateExclusiveAccess(TestData(1), ldrex_w, MemoryAccess(), strex_w, + 0, TestData(7)); +} + +static int ExecuteMemoryAccess(Isolate* isolate, TestData* test_data, + MemoryAccess access) { + HandleScope scope(isolate); + Assembler assm(isolate, NULL, 0); + AssembleMemoryAccess(&assm, access, r0, r2, r1); + __ bx(lr); + + CodeDesc desc; + assm.GetCode(&desc); + Handle<Code> code = isolate->factory()->NewCode( + desc, Code::ComputeFlags(Code::STUB), Handle<Code>()); + F3 f = FUNCTION_CAST<F3>(code->entry()); + + return reinterpret_cast<int>( + CALL_GENERATED_CODE(isolate, f, test_data, 0, 0, 0, 0)); +} + +class MemoryAccessThread : public v8::base::Thread { + public: + MemoryAccessThread() + : Thread(Options("MemoryAccessThread")), + test_data_(NULL), + is_finished_(false), + has_request_(false), + did_request_(false) {} + + virtual void Run() { + v8::Isolate::CreateParams create_params; + create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); + v8::Isolate* isolate = v8::Isolate::New(create_params); + Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate); + v8::Isolate::Scope scope(isolate); + + v8::base::LockGuard<v8::base::Mutex> lock_guard(&mutex_); + while (!is_finished_) { + while (!(has_request_ || is_finished_)) { + has_request_cv_.Wait(&mutex_); + } + + if (is_finished_) { + break; + } + + ExecuteMemoryAccess(i_isolate, test_data_, access_); + has_request_ = false; + did_request_ = true; + did_request_cv_.NotifyOne(); + } + } + + void NextAndWait(TestData* test_data, MemoryAccess access) { + DCHECK(!has_request_); + v8::base::LockGuard<v8::base::Mutex> lock_guard(&mutex_); + test_data_ = test_data; + access_ = access; + has_request_ = true; + has_request_cv_.NotifyOne(); + while (!did_request_) { + did_request_cv_.Wait(&mutex_); + } + did_request_ = false; + } + + void Finish() { + v8::base::LockGuard<v8::base::Mutex> lock_guard(&mutex_); + is_finished_ = true; + has_request_cv_.NotifyOne(); + } + + private: + TestData* test_data_; + MemoryAccess access_; + bool is_finished_; + bool has_request_; + bool did_request_; + v8::base::Mutex mutex_; + v8::base::ConditionVariable has_request_cv_; + v8::base::ConditionVariable did_request_cv_; +}; + +TEST(simulator_invalidate_exclusive_access_threaded) { + using Kind = MemoryAccess::Kind; + using Size = MemoryAccess::Size; + + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + + TestData test_data(1); + + MemoryAccessThread thread; + thread.Start(); + + MemoryAccess ldrex_w(Kind::LoadExcl, Size::Word, offsetof(TestData, w)); + MemoryAccess strex_w(Kind::StoreExcl, Size::Word, offsetof(TestData, w), 7); + + // Exclusive store completed by another thread first. + test_data = TestData(1); + thread.NextAndWait(&test_data, MemoryAccess(Kind::LoadExcl, Size::Word, + offsetof(TestData, w))); + ExecuteMemoryAccess(isolate, &test_data, ldrex_w); + thread.NextAndWait(&test_data, MemoryAccess(Kind::StoreExcl, Size::Word, + offsetof(TestData, w), 5)); + CHECK_EQ(1, ExecuteMemoryAccess(isolate, &test_data, strex_w)); + CHECK_EQ(5, test_data.w); + + // Exclusive store completed by another thread; different address, but masked + // to same + test_data = TestData(1); + ExecuteMemoryAccess(isolate, &test_data, ldrex_w); + thread.NextAndWait(&test_data, MemoryAccess(Kind::LoadExcl, Size::Word, + offsetof(TestData, dummy))); + thread.NextAndWait(&test_data, MemoryAccess(Kind::StoreExcl, Size::Word, + offsetof(TestData, dummy), 5)); + CHECK_EQ(1, ExecuteMemoryAccess(isolate, &test_data, strex_w)); + CHECK_EQ(1, test_data.w); + + // Test failure when store between ldrex/strex. + test_data = TestData(1); + ExecuteMemoryAccess(isolate, &test_data, ldrex_w); + thread.NextAndWait(&test_data, MemoryAccess(Kind::Store, Size::Word, + offsetof(TestData, dummy))); + CHECK_EQ(1, ExecuteMemoryAccess(isolate, &test_data, strex_w)); + CHECK_EQ(1, test_data.w); + + thread.Finish(); + thread.Join(); +} + +#undef __ + +#endif // USE_SIMULATOR |