From 4d8e1846a7d4c6ea068bbf8fe2d681a78550693f Mon Sep 17 00:00:00 2001 From: Igor Sheludko Date: Thu, 14 Apr 2022 16:05:07 +0200 Subject: [PATCH] [rwx][mac] Introduce RwxMemoryWriteScope ... as a single bottleneck that encapsulates the semantics and implementation of fast per-thread W^X permission switching supported by Apple Silicon (arm64 M1). On other architectures this class is a no-op. Bug: v8:12797 Change-Id: Ica842ff9f843e20b7f61fd7e80591e7a1fd29771 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3586986 Reviewed-by: Jakob Kummerow Commit-Queue: Igor Sheludko Cr-Commit-Position: refs/heads/main@{#79994} --- BUILD.bazel | 3 + BUILD.gn | 5 + src/common/code-memory-access-inl.h | 52 ++++++++ src/common/code-memory-access.cc | 17 +++ src/common/code-memory-access.h | 62 +++++++++ src/wasm/code-space-access.cc | 12 +- test/cctest/test-assembler-arm64.cc | 50 ++++--- test/cctest/test-icache.cc | 49 +++---- test/cctest/test-macro-assembler-arm64.cc | 4 + test/cctest/wasm/test-jump-table-assembler.cc | 15 +-- test/common/assembler-tester.h | 29 +++- .../turbo-assembler-arm64-unittest.cc | 126 ++++++++++-------- 12 files changed, 286 insertions(+), 138 deletions(-) create mode 100644 src/common/code-memory-access-inl.h create mode 100644 src/common/code-memory-access.cc create mode 100644 src/common/code-memory-access.h diff --git a/BUILD.bazel b/BUILD.bazel index 279e3e55a0..c5210f3df0 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -1205,6 +1205,9 @@ filegroup( "src/common/assert-scope.h", "src/common/allow-deprecated.h", "src/common/checks.h", + "src/common/code-memory-access-inl.h", + "src/common/code-memory-access.cc", + "src/common/code-memory-access.h", "src/common/high-allocation-throughput-scope.h", "src/common/message-template.h", "src/common/operation.h", diff --git a/BUILD.gn b/BUILD.gn index 988c907d96..864af565e3 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -2750,6 +2750,8 @@ v8_header_set("v8_internal_headers") { "src/common/allow-deprecated.h", "src/common/assert-scope.h", "src/common/checks.h", + "src/common/code-memory-access-inl.h", + "src/common/code-memory-access.h", "src/common/high-allocation-throughput-scope.h", "src/common/message-template.h", "src/common/operation.h", @@ -4163,6 +4165,9 @@ v8_source_set("v8_base_without_compiler") { "src/codegen/turbo-assembler.cc", "src/codegen/unoptimized-compilation-info.cc", "src/common/assert-scope.cc", + "src/common/code-memory-access-inl.h", + "src/common/code-memory-access.cc", + "src/common/code-memory-access.h", "src/compiler-dispatcher/lazy-compile-dispatcher.cc", "src/compiler-dispatcher/optimizing-compile-dispatcher.cc", "src/date/date.cc", diff --git a/src/common/code-memory-access-inl.h b/src/common/code-memory-access-inl.h new file mode 100644 index 0000000000..dcab8fb29b --- /dev/null +++ b/src/common/code-memory-access-inl.h @@ -0,0 +1,52 @@ +// Copyright 2022 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. + +#ifndef V8_COMMON_CODE_MEMORY_ACCESS_INL_H_ +#define V8_COMMON_CODE_MEMORY_ACCESS_INL_H_ + +#include "src/common/code-memory-access.h" +#include "src/logging/log.h" + +namespace v8 { +namespace internal { + +RwxMemoryWriteScope::RwxMemoryWriteScope() { SetWritable(); } + +RwxMemoryWriteScope::~RwxMemoryWriteScope() { SetExecutable(); } + +#if V8_HAS_PTHREAD_JIT_WRITE_PROTECT + +// Ignoring this warning is considered better than relying on +// __builtin_available. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunguarded-availability-new" +// static +void RwxMemoryWriteScope::SetWritable() { + if (code_space_write_nesting_level_ == 0) { + pthread_jit_write_protect_np(0); + } + code_space_write_nesting_level_++; +} + +// static +void RwxMemoryWriteScope::SetExecutable() { + code_space_write_nesting_level_--; + if (code_space_write_nesting_level_ == 0) { + pthread_jit_write_protect_np(1); + } +} +#pragma clang diagnostic pop + +#else // !V8_HAS_PTHREAD_JIT_WRITE_PROTECT + +void RwxMemoryWriteScope::SetWritable() {} + +void RwxMemoryWriteScope::SetExecutable() {} + +#endif // V8_HAS_PTHREAD_JIT_WRITE_PROTECT + +} // namespace internal +} // namespace v8 + +#endif // V8_COMMON_CODE_MEMORY_ACCESS_INL_H_ diff --git a/src/common/code-memory-access.cc b/src/common/code-memory-access.cc new file mode 100644 index 0000000000..9fe3e2fd3a --- /dev/null +++ b/src/common/code-memory-access.cc @@ -0,0 +1,17 @@ +// Copyright 2022 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/common/code-memory-access.h" + +namespace v8 { +namespace internal { + +#if V8_HAS_PTHREAD_JIT_WRITE_PROTECT + +thread_local int RwxMemoryWriteScope::code_space_write_nesting_level_ = 0; + +#endif // V8_HAS_PTHREAD_JIT_WRITE_PROTECT + +} // namespace internal +} // namespace v8 diff --git a/src/common/code-memory-access.h b/src/common/code-memory-access.h new file mode 100644 index 0000000000..d7aabf20ac --- /dev/null +++ b/src/common/code-memory-access.h @@ -0,0 +1,62 @@ +// Copyright 2022 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. + +#ifndef V8_COMMON_CODE_MEMORY_ACCESS_H_ +#define V8_COMMON_CODE_MEMORY_ACCESS_H_ + +#include "src/base/build_config.h" + +namespace v8 { +namespace internal { + +class CodePageCollectionMemoryModificationScope; +class CodePageMemoryModificationScope; +class CodeSpaceMemoryModificationScope; +namespace wasm { +class CodeSpaceWriteScope; +} + +// This scope is a wrapper for APRR/MAP_JIT machinery on MacOS on ARM64 +// ("Apple M1"/Apple Silicon) with respective semantics. +// See pthread_jit_write_protect_np() for details. +// On other platforms the scope is a no-op. +// +// The semantics is the following: the scope switches permissions between +// writable and executable for all the pages allocated with RWX permissions. +// Only current thread is affected. This achieves "real" W^X and it's fast. +// By default it is assumed that the state is executable. +// +// The scope is reentrant and thread safe. +class V8_NODISCARD RwxMemoryWriteScope final { + public: + V8_INLINE explicit RwxMemoryWriteScope(); + V8_INLINE virtual ~RwxMemoryWriteScope(); + + // Disable copy constructor and copy-assignment operator, since this manages + // a resource and implicit copying of the scope can yield surprising errors. + RwxMemoryWriteScope(const RwxMemoryWriteScope&) = delete; + RwxMemoryWriteScope& operator=(const RwxMemoryWriteScope&) = delete; + + private: + friend class CodePageCollectionMemoryModificationScope; + friend class CodePageMemoryModificationScope; + friend class CodeSpaceMemoryModificationScope; + friend class wasm::CodeSpaceWriteScope; + + // {SetWritable} and {SetExecutable} implicitly enters/exits the scope. + // These methods are exposed only for the purpose of implementing other + // scope classes that affect executable pages permissions. + V8_INLINE static void SetWritable(); + V8_INLINE static void SetExecutable(); + +#if V8_HAS_PTHREAD_JIT_WRITE_PROTECT + // This counter is used for supporting scope reentrance. + static thread_local int code_space_write_nesting_level_; +#endif // V8_HAS_PTHREAD_JIT_WRITE_PROTECT || defined(DEBUG) +}; + +} // namespace internal +} // namespace v8 + +#endif // V8_COMMON_CODE_MEMORY_ACCESS_H_ diff --git a/src/wasm/code-space-access.cc b/src/wasm/code-space-access.cc index f473439441..9fd48855a3 100644 --- a/src/wasm/code-space-access.cc +++ b/src/wasm/code-space-access.cc @@ -4,6 +4,7 @@ #include "src/wasm/code-space-access.h" +#include "src/common/code-memory-access-inl.h" #include "src/wasm/wasm-code-manager.h" #include "src/wasm/wasm-engine.h" @@ -36,20 +37,13 @@ CodeSpaceWriteScope::~CodeSpaceWriteScope() { #if V8_HAS_PTHREAD_JIT_WRITE_PROTECT -// Ignoring this warning is considered better than relying on -// __builtin_available. -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunguarded-availability-new" // static -void CodeSpaceWriteScope::SetWritable() { - pthread_jit_write_protect_np(0); -} +void CodeSpaceWriteScope::SetWritable() { RwxMemoryWriteScope::SetWritable(); } // static void CodeSpaceWriteScope::SetExecutable() { - pthread_jit_write_protect_np(1); + RwxMemoryWriteScope::SetExecutable(); } -#pragma clang diagnostic pop // static bool CodeSpaceWriteScope::SwitchingPerNativeModule() { return false; } diff --git a/test/cctest/test-assembler-arm64.cc b/test/cctest/test-assembler-arm64.cc index 897a4a70f9..c2c2b804ea 100644 --- a/test/cctest/test-assembler-arm64.cc +++ b/test/cctest/test-assembler-arm64.cc @@ -118,23 +118,25 @@ static void InitializeVM() { #ifdef USE_SIMULATOR // Run tests with the simulator. -#define SETUP_SIZE(buf_size) \ - Isolate* isolate = CcTest::i_isolate(); \ - HandleScope scope(isolate); \ - CHECK_NOT_NULL(isolate); \ - std::unique_ptr owned_buf{new byte[buf_size]}; \ - MacroAssembler masm(isolate, v8::internal::CodeObjectRequired::kYes, \ - ExternalAssemblerBuffer(owned_buf.get(), buf_size)); \ - Decoder* decoder = \ - new Decoder(); \ - Simulator simulator(decoder); \ - std::unique_ptr pdis; \ - RegisterDump core; \ - HandleScope handle_scope(isolate); \ - Handle code; \ - if (i::FLAG_trace_sim) { \ - pdis.reset(new PrintDisassembler(stdout)); \ - decoder->PrependVisitor(pdis.get()); \ +#define SETUP_SIZE(buf_size) \ + Isolate* isolate = CcTest::i_isolate(); \ + HandleScope scope(isolate); \ + CHECK_NOT_NULL(isolate); \ + auto owned_buf = \ + AllocateAssemblerBuffer(buf_size, nullptr, VirtualMemory::kNoJit); \ + MacroAssembler masm(isolate, v8::internal::CodeObjectRequired::kYes, \ + ExternalAssemblerBuffer(owned_buf->start(), buf_size)); \ + std::optional rw_buffer_scope; \ + Decoder* decoder = \ + new Decoder(); \ + Simulator simulator(decoder); \ + std::unique_ptr pdis; \ + RegisterDump core; \ + HandleScope handle_scope(isolate); \ + Handle code; \ + if (i::FLAG_trace_sim) { \ + pdis.reset(new PrintDisassembler(stdout)); \ + decoder->PrependVisitor(pdis.get()); \ } // Reset the assembler and simulator, so that instructions can be generated, @@ -177,6 +179,7 @@ static void InitializeVM() { HandleScope scope(isolate); \ CHECK_NOT_NULL(isolate); \ auto owned_buf = AllocateAssemblerBuffer(buf_size); \ + std::optional rw_buffer_scope; \ MacroAssembler masm(isolate, v8::internal::CodeObjectRequired::kYes, \ owned_buf->CreateView()); \ HandleScope handle_scope(isolate); \ @@ -184,7 +187,7 @@ static void InitializeVM() { RegisterDump core; #define RESET() \ - owned_buf->MakeWritable(); \ + rw_buffer_scope.emplace(*owned_buf); \ __ Reset(); \ __ CodeEntry(); \ /* Reset the machine state (like simulator.ResetState()). */ \ @@ -198,10 +201,12 @@ static void InitializeVM() { RESET(); \ START_AFTER_RESET(); -#define RUN() \ - { \ - auto f = GeneratedCode::FromCode(*code); \ - f.Call(); \ +#define RUN() \ + { \ + /* Reset the scope and thus make the buffer executable. */ \ + rw_buffer_scope.reset(); \ + auto f = GeneratedCode::FromCode(*code); \ + f.Call(); \ } #define END() \ @@ -14880,6 +14885,7 @@ TEST(pool_size) { // This test does not execute any code. It only tests that the size of the // pools is read correctly from the RelocInfo. + rw_buffer_scope.emplace(*owned_buf); Label exit; __ b(&exit); diff --git a/test/cctest/test-icache.cc b/test/cctest/test-icache.cc index ed757fc5ee..b18a4512ee 100644 --- a/test/cctest/test-icache.cc +++ b/test/cctest/test-icache.cc @@ -113,19 +113,18 @@ TEST(TestFlushICacheOfWritable) { // Allow calling the function from C++. auto f = GeneratedCode::FromBuffer(isolate, buffer->start()); - CHECK(SetPermissions(GetPlatformPageAllocator(), buffer->start(), - buffer->size(), v8::PageAllocator::kReadWrite)); - FloodWithInc(isolate, buffer.get()); - FlushInstructionCache(buffer->start(), buffer->size()); - CHECK(SetPermissions(GetPlatformPageAllocator(), buffer->start(), - buffer->size(), v8::PageAllocator::kReadExecute)); + { + AssemblerBufferWriteScope rw_buffer_scope(*buffer); + FloodWithInc(isolate, buffer.get()); + FlushInstructionCache(buffer->start(), buffer->size()); + } CHECK_EQ(23 + kNumInstr, f.Call(23)); // Call into generated code. - CHECK(SetPermissions(GetPlatformPageAllocator(), buffer->start(), - buffer->size(), v8::PageAllocator::kReadWrite)); - FloodWithNop(isolate, buffer.get()); - FlushInstructionCache(buffer->start(), buffer->size()); - CHECK(SetPermissions(GetPlatformPageAllocator(), buffer->start(), - buffer->size(), v8::PageAllocator::kReadExecute)); + + { + AssemblerBufferWriteScope rw_buffer_scope(*buffer); + FloodWithNop(isolate, buffer.get()); + FlushInstructionCache(buffer->start(), buffer->size()); + } CHECK_EQ(23, f.Call(23)); // Call into generated code. } } @@ -184,24 +183,6 @@ TEST(TestFlushICacheOfWritableAndExecutable) { Isolate* isolate = CcTest::i_isolate(); HandleScope handles(isolate); - struct V8_NODISCARD EnableWritePermissionsOnMacArm64Scope { -#if defined(V8_OS_DARWIN) && defined(V8_HOST_ARCH_ARM64) -// Ignoring this warning is considered better than relying on -// __builtin_available. -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunguarded-availability-new" - EnableWritePermissionsOnMacArm64Scope() { pthread_jit_write_protect_np(0); } - ~EnableWritePermissionsOnMacArm64Scope() { - pthread_jit_write_protect_np(1); - } -#pragma clang diagnostic pop -#else - EnableWritePermissionsOnMacArm64Scope() { - // Define a constructor to avoid unused variable warnings. - } -#endif - }; - for (int i = 0; i < kNumIterations; ++i) { auto buffer = AllocateAssemblerBuffer(kBufferSize, nullptr, VirtualMemory::kMapAsJittable); @@ -209,16 +190,16 @@ TEST(TestFlushICacheOfWritableAndExecutable) { // Allow calling the function from C++. auto f = GeneratedCode::FromBuffer(isolate, buffer->start()); - CHECK(SetPermissions(GetPlatformPageAllocator(), buffer->start(), - buffer->size(), v8::PageAllocator::kReadWriteExecute)); + buffer->MakeWritableAndExecutable(); + { - EnableWritePermissionsOnMacArm64Scope write_scope; + RwxMemoryWriteScope rw_scope; FloodWithInc(isolate, buffer.get()); FlushInstructionCache(buffer->start(), buffer->size()); } CHECK_EQ(23 + kNumInstr, f.Call(23)); // Call into generated code. { - EnableWritePermissionsOnMacArm64Scope write_scope; + RwxMemoryWriteScope rw_scope; FloodWithNop(isolate, buffer.get()); FlushInstructionCache(buffer->start(), buffer->size()); } diff --git a/test/cctest/test-macro-assembler-arm64.cc b/test/cctest/test-macro-assembler-arm64.cc index eec16c0529..4d37a6ac2a 100644 --- a/test/cctest/test-macro-assembler-arm64.cc +++ b/test/cctest/test-macro-assembler-arm64.cc @@ -57,6 +57,8 @@ TEST(EmbeddedObj) { MacroAssembler masm(isolate, v8::internal::CodeObjectRequired::kYes, buffer->CreateView()); + AssemblerBufferWriteScope rw_scope(*buffer); + Handle old_array = isolate->factory()->NewFixedArray(2000); Handle my_array = isolate->factory()->NewFixedArray(1000); __ Mov(w4, Immediate(my_array, RelocInfo::COMPRESSED_EMBEDDED_OBJECT)); @@ -100,6 +102,8 @@ TEST(DeoptExitSizeIsFixed) { MacroAssembler masm(isolate, v8::internal::CodeObjectRequired::kYes, buffer->CreateView()); + AssemblerBufferWriteScope rw_scope(*buffer); + STATIC_ASSERT(static_cast(kFirstDeoptimizeKind) == 0); for (int i = 0; i < kDeoptimizeKindCount; i++) { DeoptimizeKind kind = static_cast(i); diff --git a/test/cctest/wasm/test-jump-table-assembler.cc b/test/cctest/wasm/test-jump-table-assembler.cc index 56619e7763..48c1dcda95 100644 --- a/test/cctest/wasm/test-jump-table-assembler.cc +++ b/test/cctest/wasm/test-jump-table-assembler.cc @@ -55,17 +55,6 @@ constexpr uint32_t kAvailableBufferSlots = 0; constexpr uint32_t kBufferSlotStartOffset = 0; #endif -void EnsureThreadHasWritePermissions() { -#if defined(V8_OS_DARWIN) && defined(V8_HOST_ARCH_ARM64) -// Ignoring this warning is considered better than relying on -// __builtin_available. -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunguarded-availability-new" - pthread_jit_write_protect_np(0); -#pragma clang diagnostic pop -#endif -} - Address AllocateJumpTableThunk( Address jump_target, byte* thunk_slot_buffer, std::bitset* used_slots, @@ -214,7 +203,7 @@ class JumpTablePatcher : public v8::base::Thread { void Run() override { TRACE("Patcher %p is starting ...\n", this); - EnsureThreadHasWritePermissions(); + RwxMemoryWriteScope rwx_write_scope; Address slot_address = slot_start_ + JumpTableAssembler::JumpSlotIndexToOffset(slot_index_); // First, emit code to the two thunks. @@ -265,11 +254,11 @@ TEST(JumpTablePatchingStress) { std::bitset used_thunk_slots; buffer->MakeWritableAndExecutable(); + RwxMemoryWriteScope rwx_write_scope; // Iterate through jump-table slots to hammer at different alignments within // the jump-table, thereby increasing stress for variable-length ISAs. Address slot_start = reinterpret_cast
(buffer->start()); - EnsureThreadHasWritePermissions(); for (int slot = 0; slot < kJumpTableSlotCount; ++slot) { TRACE("Hammering on jump table slot #%d ...\n", slot); uint32_t slot_offset = JumpTableAssembler::JumpSlotIndexToOffset(slot); diff --git a/test/common/assembler-tester.h b/test/common/assembler-tester.h index 38dcbdee38..2b8fb33161 100644 --- a/test/common/assembler-tester.h +++ b/test/common/assembler-tester.h @@ -9,6 +9,7 @@ #include "src/codegen/assembler.h" #include "src/codegen/code-desc.h" +#include "src/common/code-memory-access-inl.h" namespace v8 { namespace internal { @@ -27,7 +28,7 @@ class TestingAssemblerBuffer : public AssemblerBuffer { MakeWritable(); } - ~TestingAssemblerBuffer() { reservation_.Free(); } + ~TestingAssemblerBuffer() override { reservation_.Free(); } byte* start() const override { return reinterpret_cast(reservation_.address()); @@ -62,7 +63,6 @@ class TestingAssemblerBuffer : public AssemblerBuffer { CHECK(result); } - // TODO(wasm): Only needed for the "test-jump-table-assembler.cc" tests. void MakeWritableAndExecutable() { bool result = SetPermissions(GetPlatformPageAllocator(), start(), size(), v8::PageAllocator::kReadWriteExecute); @@ -73,6 +73,31 @@ class TestingAssemblerBuffer : public AssemblerBuffer { VirtualMemory reservation_; }; +// This scope class is mostly necesasry for arm64 tests running on Apple Silicon +// (M1) which prohibits reconfiguration of page permissions for RWX pages. +// Instead of altering the page permissions one must flip the X-W state by +// calling pthread_jit_write_protect_np() function. +// See RwxMemoryWriteScope for details. +class V8_NODISCARD AssemblerBufferWriteScope final { + public: + explicit AssemblerBufferWriteScope(TestingAssemblerBuffer& buffer) + : buffer_(buffer) { + buffer_.MakeWritable(); + } + + ~AssemblerBufferWriteScope() { buffer_.MakeExecutable(); } + + // Disable copy constructor and copy-assignment operator, since this manages + // a resource and implicit copying of the scope can yield surprising errors. + AssemblerBufferWriteScope(const AssemblerBufferWriteScope&) = delete; + AssemblerBufferWriteScope& operator=(const AssemblerBufferWriteScope&) = + delete; + + private: + RwxMemoryWriteScope rwx_write_scope_; + TestingAssemblerBuffer& buffer_; +}; + static inline std::unique_ptr AllocateAssemblerBuffer( size_t requested = v8::internal::AssemblerBase::kDefaultBufferSize, void* address = nullptr, diff --git a/test/unittests/assembler/turbo-assembler-arm64-unittest.cc b/test/unittests/assembler/turbo-assembler-arm64-unittest.cc index 5ccb706d50..199d9902e8 100644 --- a/test/unittests/assembler/turbo-assembler-arm64-unittest.cc +++ b/test/unittests/assembler/turbo-assembler-arm64-unittest.cc @@ -37,13 +37,16 @@ TEST_F(TurboAssemblerTest, TestHardAbort) { __ set_root_array_available(false); __ set_abort_hard(true); - __ CodeEntry(); + { + AssemblerBufferWriteScope rw_scope(*buffer); - __ Abort(AbortReason::kNoReason); + __ CodeEntry(); - CodeDesc desc; - tasm.GetCode(isolate(), &desc); - buffer->MakeExecutable(); + __ Abort(AbortReason::kNoReason); + + CodeDesc desc; + tasm.GetCode(isolate(), &desc); + } // We need an isolate here to execute in the simulator. auto f = GeneratedCode::FromBuffer(isolate(), buffer->start()); @@ -57,17 +60,20 @@ TEST_F(TurboAssemblerTest, TestCheck) { __ set_root_array_available(false); __ set_abort_hard(true); - __ CodeEntry(); + { + AssemblerBufferWriteScope rw_scope(*buffer); - // Fail if the first parameter is 17. - __ Mov(w1, Immediate(17)); - __ Cmp(w0, w1); // 1st parameter is in {w0}. - __ Check(Condition::ne, AbortReason::kNoReason); - __ Ret(); + __ CodeEntry(); - CodeDesc desc; - tasm.GetCode(isolate(), &desc); - buffer->MakeExecutable(); + // Fail if the first parameter is 17. + __ Mov(w1, Immediate(17)); + __ Cmp(w0, w1); // 1st parameter is in {w0}. + __ Check(Condition::ne, AbortReason::kNoReason); + __ Ret(); + + CodeDesc desc; + tasm.GetCode(isolate(), &desc); + } // We need an isolate here to execute in the simulator. auto f = GeneratedCode::FromBuffer(isolate(), buffer->start()); @@ -119,54 +125,58 @@ TEST_P(TurboAssemblerTestMoveObjectAndSlot, MoveObjectAndSlot) { TurboAssembler tasm(nullptr, AssemblerOptions{}, CodeObjectRequired::kNo, buffer->CreateView()); - __ CodeEntry(); - __ Push(x0, padreg); - __ Mov(test_case.object, x1); + { + AssemblerBufferWriteScope rw_buffer_scope(*buffer); - Register src_object = test_case.object; - Register dst_object = test_case.dst_object; - Register dst_slot = test_case.dst_slot; + __ CodeEntry(); + __ Push(x0, padreg); + __ Mov(test_case.object, x1); - 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); + 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.AcquireX(); + __ Pop(padreg, 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 = + Factory::CodeBuilder(isolate(), desc, CodeKind::FOR_TESTING) + .Build(); + StdoutStream os; + code->Print(os); + } } - 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.AcquireX(); - __ Pop(padreg, 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 = - 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::FromBuffer(isolate(), buffer->start());