| // Copyright 2018 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/codegen/arm/assembler-arm-inl.h" |
| #include "src/codegen/macro-assembler.h" |
| #include "src/execution/simulator.h" |
| #include "src/utils/ostreams.h" |
| #include "test/common/assembler-tester.h" |
| #include "test/unittests/test-utils.h" |
| #include "testing/gtest-support.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| #define __ tasm. |
| |
| // If we are running on android and the output is not redirected (i.e. ends up |
| // in the android log) then we cannot find the error message in the output. This |
| // macro just returns the empty string in that case. |
| #if defined(ANDROID) && !defined(V8_ANDROID_LOG_STDOUT) |
| #define ERROR_MESSAGE(msg) "" |
| #else |
| #define ERROR_MESSAGE(msg) msg |
| #endif |
| |
| // Test the x64 assembler by compiling some simple functions into |
| // a buffer and executing them. These tests do not initialize the |
| // V8 library, create a context, or use any V8 objects. |
| |
| class TurboAssemblerTest : public TestWithIsolate {}; |
| |
| TEST_F(TurboAssemblerTest, TestHardAbort) { |
| auto buffer = AllocateAssemblerBuffer(); |
| TurboAssembler tasm(isolate(), AssemblerOptions{}, CodeObjectRequired::kNo, |
| buffer->CreateView()); |
| __ set_root_array_available(false); |
| __ set_abort_hard(true); |
| |
| __ Abort(AbortReason::kNoReason); |
| |
| CodeDesc desc; |
| tasm.GetCode(isolate(), &desc); |
| buffer->MakeExecutable(); |
| // We need an isolate here to execute in the simulator. |
| auto f = GeneratedCode<void>::FromBuffer(isolate(), buffer->start()); |
| |
| ASSERT_DEATH_IF_SUPPORTED({ f.Call(); }, ERROR_MESSAGE("abort: no reason")); |
| } |
| |
| TEST_F(TurboAssemblerTest, TestCheck) { |
| auto buffer = AllocateAssemblerBuffer(); |
| TurboAssembler tasm(isolate(), AssemblerOptions{}, CodeObjectRequired::kNo, |
| buffer->CreateView()); |
| __ set_root_array_available(false); |
| __ set_abort_hard(true); |
| |
| // Fail if the first parameter is 17. |
| __ Move32BitImmediate(r1, Operand(17)); |
| __ cmp(r0, r1); // 1st parameter is in {r0}. |
| __ Check(Condition::ne, AbortReason::kNoReason); |
| __ Ret(); |
| |
| CodeDesc desc; |
| tasm.GetCode(isolate(), &desc); |
| buffer->MakeExecutable(); |
| // We need an isolate here to execute in the simulator. |
| auto f = GeneratedCode<void, int>::FromBuffer(isolate(), buffer->start()); |
| |
| f.Call(0); |
| f.Call(18); |
| ASSERT_DEATH_IF_SUPPORTED({ f.Call(17); }, ERROR_MESSAGE("abort: no reason")); |
| } |
| |
| struct MoveObjectAndSlotTestCase { |
| const char* comment; |
| Register dst_object; |
| Register dst_slot; |
| Register object; |
| Register offset_register = no_reg; |
| }; |
| |
| const MoveObjectAndSlotTestCase kMoveObjectAndSlotTestCases[] = { |
| {"no overlap", r0, r1, r2}, |
| {"no overlap", r0, r1, r2, r3}, |
| |
| {"object == dst_object", r2, r1, r2}, |
| {"object == dst_object", r2, r1, r2, r3}, |
| |
| {"object == dst_slot", r1, r2, r2}, |
| {"object == dst_slot", r1, r2, r2, r3}, |
| |
| {"offset == dst_object", r0, r1, r2, r0}, |
| |
| {"offset == dst_object && object == dst_slot", r0, r1, r1, r0}, |
| |
| {"offset == dst_slot", r0, r1, r2, r1}, |
| |
| {"offset == dst_slot && object == dst_object", r0, r1, r0, r1}}; |
| |
| // Make sure we include offsets that cannot be encoded in an add instruction. |
| const int kOffsets[] = {0, 42, kMaxRegularHeapObjectSize, 0x101001}; |
| |
| template <typename T> |
| class TurboAssemblerTestWithParam : public TurboAssemblerTest, |
| public ::testing::WithParamInterface<T> {}; |
| |
| using TurboAssemblerTestMoveObjectAndSlot = |
| TurboAssemblerTestWithParam<MoveObjectAndSlotTestCase>; |
| |
| TEST_P(TurboAssemblerTestMoveObjectAndSlot, MoveObjectAndSlot) { |
| const MoveObjectAndSlotTestCase test_case = GetParam(); |
| TRACED_FOREACH(int32_t, offset, kOffsets) { |
| auto buffer = AllocateAssemblerBuffer(); |
| TurboAssembler tasm(nullptr, AssemblerOptions{}, CodeObjectRequired::kNo, |
| buffer->CreateView()); |
| __ Push(r0); |
| __ Move(test_case.object, r1); |
| |
| Register src_object = test_case.object; |
| Register dst_object = test_case.dst_object; |
| Register dst_slot = test_case.dst_slot; |
| |
| Operand offset_operand(0); |
| if (test_case.offset_register == no_reg) { |
| offset_operand = Operand(offset); |
| } else { |
| __ mov(test_case.offset_register, Operand(offset)); |
| offset_operand = Operand(test_case.offset_register); |
| } |
| |
| std::stringstream comment; |
| comment << "-- " << test_case.comment << ": MoveObjectAndSlot(" |
| << dst_object << ", " << dst_slot << ", " << src_object << ", "; |
| if (test_case.offset_register == no_reg) { |
| comment << "#" << offset; |
| } else { |
| comment << test_case.offset_register; |
| } |
| comment << ") --"; |
| __ RecordComment(comment.str().c_str()); |
| __ MoveObjectAndSlot(dst_object, dst_slot, src_object, offset_operand); |
| __ RecordComment("--"); |
| |
| // The `result` pointer was saved on the stack. |
| UseScratchRegisterScope temps(&tasm); |
| Register scratch = temps.Acquire(); |
| __ Pop(scratch); |
| __ str(dst_object, MemOperand(scratch)); |
| __ str(dst_slot, MemOperand(scratch, kSystemPointerSize)); |
| |
| __ Ret(); |
| |
| CodeDesc desc; |
| tasm.GetCode(nullptr, &desc); |
| if (FLAG_print_code) { |
| Handle<Code> code = |
| Factory::CodeBuilder(isolate(), desc, CodeKind::FOR_TESTING).Build(); |
| StdoutStream os; |
| code->Print(os); |
| } |
| |
| buffer->MakeExecutable(); |
| // We need an isolate here to execute in the simulator. |
| auto f = GeneratedCode<void, byte**, byte*>::FromBuffer(isolate(), |
| buffer->start()); |
| |
| byte* object = new byte[offset]; |
| byte* result[] = {nullptr, nullptr}; |
| |
| f.Call(result, object); |
| |
| // The first element must be the address of the object, and the second the |
| // slot addressed by `offset`. |
| EXPECT_EQ(result[0], &object[0]); |
| EXPECT_EQ(result[1], &object[offset]); |
| |
| delete[] object; |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(TurboAssemblerTest, |
| TurboAssemblerTestMoveObjectAndSlot, |
| ::testing::ValuesIn(kMoveObjectAndSlotTestCases)); |
| |
| #undef __ |
| #undef ERROR_MESSAGE |
| |
| } // namespace internal |
| } // namespace v8 |