From e59a4a2b42a61ba10600e63a80e665db6fdb3dc2 Mon Sep 17 00:00:00 2001 From: Samir Jindel Date: Wed, 7 Aug 2019 11:37:20 +0000 Subject: [PATCH] [vm/ffi] Call natives through stubs if write-protection is enabled without dual-mapping. Addresses the Dart -> Native half of dartbug.com/37629 Change-Id: I242b7f3a14dd105334a8b6e5c089b173c398491e Cq-Include-Trybots: luci.dart.try:vm-ffi-android-debug-arm-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/111280 Commit-Queue: Samir Jindel Reviewed-by: Martin Kustermann --- runtime/bin/ffi_test/ffi_test_functions.cc | 76 ++++- runtime/include/dart_native_api.h | 2 +- runtime/vm/compiler/backend/il.cc | 9 +- runtime/vm/compiler/backend/il.h | 9 + runtime/vm/compiler/backend/il_arm.cc | 25 +- runtime/vm/compiler/backend/il_arm64.cc | 35 ++- runtime/vm/compiler/backend/il_ia32.cc | 28 +- runtime/vm/compiler/backend/il_x64.cc | 26 +- runtime/vm/compiler/runtime_api.cc | 4 + runtime/vm/compiler/runtime_api.h | 3 + .../vm/compiler/runtime_offsets_extracted.h | 278 +++++++++--------- runtime/vm/compiler/runtime_offsets_list.h | 2 + runtime/vm/compiler/stub_code_compiler_arm.cc | 38 ++- .../vm/compiler/stub_code_compiler_arm64.cc | 47 ++- .../vm/compiler/stub_code_compiler_ia32.cc | 27 ++ runtime/vm/compiler/stub_code_compiler_x64.cc | 36 ++- runtime/vm/constants_arm.h | 1 + runtime/vm/native_api_impl.cc | 43 ++- runtime/vm/runtime_entry.cc | 3 +- runtime/vm/simulator_dbc.cc | 10 +- runtime/vm/stub_code_list.h | 3 +- runtime/vm/thread.h | 8 +- runtime/vm/virtual_memory.h | 4 + runtime/vm/virtual_memory_fuchsia.cc | 4 + runtime/vm/virtual_memory_posix.cc | 4 + runtime/vm/virtual_memory_win.cc | 4 + tests/ffi/function_gc_test.dart | 11 + tests/ffi/function_test.dart | 2 + 28 files changed, 549 insertions(+), 193 deletions(-) diff --git a/runtime/bin/ffi_test/ffi_test_functions.cc b/runtime/bin/ffi_test/ffi_test_functions.cc index cc619b628d3..e6e516c6e45 100644 --- a/runtime/bin/ffi_test/ffi_test_functions.cc +++ b/runtime/bin/ffi_test/ffi_test_functions.cc @@ -14,6 +14,9 @@ #include #else #include + +// Only OK to use here because this is test code. +#include #endif #include @@ -26,6 +29,14 @@ namespace dart { +#define CHECK(X) \ + if (!(X)) { \ + fprintf(stderr, "%s\n", "Check failed: " #X); \ + return 1; \ + } + +#define CHECK_EQ(X, Y) CHECK((X) == (Y)) + //////////////////////////////////////////////////////////////////////////////// // Tests for Dart -> native calls. @@ -481,30 +492,30 @@ DART_EXPORT float InventFloatValue() { // Functions for stress-testing. DART_EXPORT int64_t MinInt64() { - Dart_ExecuteInternalCommand("gc-on-next-allocation"); + Dart_ExecuteInternalCommand("gc-on-next-allocation", nullptr); return 0x8000000000000000; } DART_EXPORT int64_t MinInt32() { - Dart_ExecuteInternalCommand("gc-on-next-allocation"); + Dart_ExecuteInternalCommand("gc-on-next-allocation", nullptr); return 0x80000000; } DART_EXPORT double SmallDouble() { - Dart_ExecuteInternalCommand("gc-on-next-allocation"); + Dart_ExecuteInternalCommand("gc-on-next-allocation", nullptr); return 0x80000000 * -1.0; } // Requires boxing on 32-bit and 64-bit systems, even if the top 32-bits are // truncated. DART_EXPORT void* LargePointer() { - Dart_ExecuteInternalCommand("gc-on-next-allocation"); + Dart_ExecuteInternalCommand("gc-on-next-allocation", nullptr); uint64_t origin = 0x8100000082000000; return reinterpret_cast(origin); } DART_EXPORT void TriggerGC(uint64_t count) { - Dart_ExecuteInternalCommand("gc-now"); + Dart_ExecuteInternalCommand("gc-now", nullptr); } // Triggers GC. Has 11 dummy arguments as unboxed odd integers which should be @@ -520,20 +531,57 @@ DART_EXPORT void Regress37069(uint64_t a, uint64_t i, uint64_t j, uint64_t k) { - Dart_ExecuteInternalCommand("gc-now"); + Dart_ExecuteInternalCommand("gc-now", nullptr); } +#if !defined(HOST_OS_WINDOWS) && !defined(TARGET_ARCH_DBC) +DART_EXPORT void* UnprotectCodeOtherThread(void* isolate, + std::condition_variable* var, + std::mutex* mut) { + std::function callback = [&]() { + mut->lock(); + var->notify_all(); + mut->unlock(); + + // Wait for mutator thread to continue (and block) before leaving the + // safepoint. + while (Dart_ExecuteInternalCommand("is-mutator-in-native", isolate) != + nullptr) { + usleep(10 * 1000 /*10 ms*/); + } + }; + + struct { + void* isolate; + std::function* callback; + } args = {.isolate = isolate, .callback = &callback}; + + Dart_ExecuteInternalCommand("run-in-safepoint-and-rw-code", &args); + return nullptr; +} + +DART_EXPORT int UnprotectCode() { + std::mutex mutex; + std::condition_variable cvar; + std::unique_lock lock(mutex); // locks the mutex + std::thread helper(UnprotectCodeOtherThread, Dart_CurrentIsolate(), &cvar, + &mutex); + + helper.detach(); + cvar.wait(lock); + + return 0; +} +#else +// Our version of VSC++ doesn't support std::thread yet. +DART_EXPORT int UnprotectCode() { + return 0; +} +#endif + //////////////////////////////////////////////////////////////////////////////// // Tests for callbacks. -#define CHECK(X) \ - if (!(X)) { \ - fprintf(stderr, "%s\n", "Check failed: " #X); \ - return 1; \ - } - -#define CHECK_EQ(X, Y) CHECK((X) == (Y)) - // Sanity test. DART_EXPORT int TestSimpleAddition(int (*add)(int, int)) { CHECK_EQ(add(10, 20), 30); diff --git a/runtime/include/dart_native_api.h b/runtime/include/dart_native_api.h index e45c01066c0..ae01fd799ec 100644 --- a/runtime/include/dart_native_api.h +++ b/runtime/include/dart_native_api.h @@ -179,6 +179,6 @@ DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle Dart_FinalizeAllClasses(); * * It should not be used outside internal tests. */ -DART_EXPORT void Dart_ExecuteInternalCommand(const char* command); +DART_EXPORT void* Dart_ExecuteInternalCommand(const char* command, void* arg); #endif /* INCLUDE_DART_NATIVE_API_H_ */ /* NOLINT */ diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc index b427836c227..14151fbb2c9 100644 --- a/runtime/vm/compiler/backend/il.cc +++ b/runtime/vm/compiler/backend/il.cc @@ -5524,9 +5524,10 @@ LocationSummary* FfiCallInstr::MakeLocationSummary(Zone* zone, ASSERT(((1 << CallingConventions::kFirstCalleeSavedCpuReg) & CallingConventions::kArgumentRegisters) == 0); -#if defined(TARGET_ARCH_ARM64) || defined(TARGET_ARCH_IA32) || \ - defined(TARGET_ARCH_ARM) +#if defined(TARGET_ARCH_ARM64) || defined(TARGET_ARCH_IA32) constexpr intptr_t kNumTemps = 2; +#elif defined(TARGET_ARCH_ARM) + constexpr intptr_t kNumTemps = 3; #else constexpr intptr_t kNumTemps = 1; #endif @@ -5544,6 +5545,10 @@ LocationSummary* FfiCallInstr::MakeLocationSummary(Zone* zone, defined(TARGET_ARCH_ARM) summary->set_temp(1, Location::RegisterLocation( CallingConventions::kFirstCalleeSavedCpuReg)); +#endif +#if defined(TARGET_ARCH_ARM) + summary->set_temp(2, Location::RegisterLocation( + CallingConventions::kSecondCalleeSavedCpuReg)); #endif summary->set_out(0, compiler::ffi::ResultLocation( compiler::ffi::ResultRepresentation(signature_))); diff --git a/runtime/vm/compiler/backend/il.h b/runtime/vm/compiler/backend/il.h index 813fd27055f..79a8e7dba1d 100644 --- a/runtime/vm/compiler/backend/il.h +++ b/runtime/vm/compiler/backend/il.h @@ -4400,6 +4400,15 @@ class FfiCallInstr : public Definition { virtual Representation RequiredInputRepresentation(intptr_t idx) const; virtual Representation representation() const; + // Returns true if we can assume generated code will be executable during a + // safepoint. + // + // TODO(#37739): This should be true when dual-mapping is enabled as well, but + // there are some bugs where it still switches code protections currently. + static bool CanExecuteGeneratedCodeInSafepoint() { + return FLAG_precompiled_mode; + } + PRINT_OPERANDS_TO_SUPPORT private: diff --git a/runtime/vm/compiler/backend/il_arm.cc b/runtime/vm/compiler/backend/il_arm.cc index a42f2f60a98..169e8c390ba 100644 --- a/runtime/vm/compiler/backend/il_arm.cc +++ b/runtime/vm/compiler/backend/il_arm.cc @@ -1036,13 +1036,28 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) { RawPcDescriptors::Kind::kOther, locs()); // Update information in the thread object and enter a safepoint. - __ TransitionGeneratedToNative(branch, FPREG, saved_fp, - locs()->temp(1).reg()); + const Register tmp = locs()->temp(1).reg(); + if (CanExecuteGeneratedCodeInSafepoint()) { + __ TransitionGeneratedToNative(branch, FPREG, saved_fp, tmp); - __ blx(branch); + __ blx(branch); - // Update information in the thread object and leave the safepoint. - __ TransitionNativeToGenerated(saved_fp, locs()->temp(1).reg()); + // Update information in the thread object and leave the safepoint. + __ TransitionNativeToGenerated(saved_fp, tmp); + } else { + // We cannot trust that this code will be executable within a safepoint. + // Therefore we delegate the responsibility of entering/exiting the + // safepoint to a stub which in the VM isolate's heap, which will never lose + // execute permission. + __ ldr(TMP, + compiler::Address( + THR, compiler::target::Thread:: + call_native_through_safepoint_entry_point_offset())); + + // Calls R8 in a safepoint and clobbers NOTFP and R4. + ASSERT(branch == R8 && tmp == NOTFP && locs()->temp(2).reg() == R4); + __ blx(TMP); + } // Restore the global object pool after returning from runtime (old space is // moving, so the GOP could have been relocated). diff --git a/runtime/vm/compiler/backend/il_arm64.cc b/runtime/vm/compiler/backend/il_arm64.cc index f768fddd3ab..970083c3227 100644 --- a/runtime/vm/compiler/backend/il_arm64.cc +++ b/runtime/vm/compiler/backend/il_arm64.cc @@ -912,20 +912,35 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) { __ StoreToOffset(temp, FPREG, kSavedCallerPcSlotFromFp * kWordSize); - // Update information in the thread object and enter a safepoint. - __ TransitionGeneratedToNative(branch, FPREG, temp); + if (CanExecuteGeneratedCodeInSafepoint()) { + // Update information in the thread object and enter a safepoint. + __ TransitionGeneratedToNative(branch, FPREG, temp); - // We are entering runtime code, so the C stack pointer must be restored from - // the stack limit to the top of the stack. - __ mov(CSP, SP); + // We are entering runtime code, so the C stack pointer must be restored + // from the stack limit to the top of the stack. + __ mov(CSP, SP); - __ blr(branch); + __ blr(branch); - // Restore the Dart stack pointer. - __ mov(SP, CSP); + // Restore the Dart stack pointer. + __ mov(SP, CSP); - // Update information in the thread object and leave the safepoint. - __ TransitionNativeToGenerated(temp); + // Update information in the thread object and leave the safepoint. + __ TransitionNativeToGenerated(temp); + } else { + // We cannot trust that this code will be executable within a safepoint. + // Therefore we delegate the responsibility of entering/exiting the + // safepoint to a stub which in the VM isolate's heap, which will never lose + // execute permission. + __ ldr(TMP, + compiler::Address( + THR, compiler::target::Thread:: + call_native_through_safepoint_entry_point_offset())); + + // Calls R8 and clobbers R19 (along with volatile registers). + ASSERT(branch == R8 && temp == R19); + __ blr(TMP); + } // Refresh write barrier mask. __ ldr(BARRIER_MASK, diff --git a/runtime/vm/compiler/backend/il_ia32.cc b/runtime/vm/compiler/backend/il_ia32.cc index 04d1ce6160c..cffcfa6ea38 100644 --- a/runtime/vm/compiler/backend/il_ia32.cc +++ b/runtime/vm/compiler/backend/il_ia32.cc @@ -953,8 +953,24 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) { __ popl(tmp); __ movl(compiler::Address(FPREG, kSavedCallerPcSlotFromFp * kWordSize), tmp); - __ TransitionGeneratedToNative(branch, FPREG, tmp); - __ call(branch); + if (CanExecuteGeneratedCodeInSafepoint()) { + __ TransitionGeneratedToNative(branch, FPREG, tmp); + __ call(branch); + __ TransitionNativeToGenerated(tmp); + } else { + // We cannot trust that this code will be executable within a safepoint. + // Therefore we delegate the responsibility of entering/exiting the + // safepoint to a stub which in the VM isolate's heap, which will never lose + // execute permission. + __ movl(tmp, + compiler::Address( + THR, compiler::target::Thread:: + call_native_through_safepoint_entry_point_offset())); + + // Calls EAX within a safepoint and clobbers EBX. + ASSERT(tmp == EBX && branch == EAX); + __ call(tmp); + } // The x86 calling convention requires floating point values to be returned on // the "floating-point stack" (aka. register ST0). We don't use the @@ -968,8 +984,6 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) { __ movss(XMM0, compiler::Address(SPREG, -kFloatSize)); } - __ TransitionNativeToGenerated(tmp); - // Leave dummy exit frame. __ LeaveFrame(); @@ -5610,7 +5624,7 @@ static void EmitShiftInt64ByConstant(FlowGraphCompiler* compiler, switch (op_kind) { case Token::kSHR: { if (shift > 31) { - __ movl(left_lo, left_hi); // Shift by 32. + __ movl(left_lo, left_hi); // Shift by 32. __ sarl(left_hi, compiler::Immediate(31)); // Sign extend left hi. if (shift > 32) { __ sarl(left_lo, compiler::Immediate(shift > 63 ? 31 : shift - 32)); @@ -5658,9 +5672,9 @@ static void EmitShiftInt64ByECX(FlowGraphCompiler* compiler, __ Bind(&large_shift); // No need to subtract 32 from CL, only 5 bits used by sarl. - __ movl(left_lo, left_hi); // Shift by 32. + __ movl(left_lo, left_hi); // Shift by 32. __ sarl(left_hi, compiler::Immediate(31)); // Sign extend left hi. - __ sarl(left_lo, ECX); // Shift count: CL % 32. + __ sarl(left_lo, ECX); // Shift count: CL % 32. break; } case Token::kSHL: { diff --git a/runtime/vm/compiler/backend/il_x64.cc b/runtime/vm/compiler/backend/il_x64.cc index b9ab63a5f8e..25f6dca8e9f 100644 --- a/runtime/vm/compiler/backend/il_x64.cc +++ b/runtime/vm/compiler/backend/il_x64.cc @@ -965,13 +965,29 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) { RawPcDescriptors::Kind::kOther, locs()); __ movq(compiler::Address(FPREG, kSavedCallerPcSlotFromFp * kWordSize), TMP); - // Update information in the thread object and enter a safepoint. - __ TransitionGeneratedToNative(target_address, FPREG); + if (CanExecuteGeneratedCodeInSafepoint()) { + // Update information in the thread object and enter a safepoint. + __ TransitionGeneratedToNative(target_address, FPREG); - __ CallCFunction(target_address); + __ CallCFunction(target_address); - // Update information in the thread object and leave the safepoint. - __ TransitionNativeToGenerated(); + // Update information in the thread object and leave the safepoint. + __ TransitionNativeToGenerated(); + } else { + // We cannot trust that this code will be executable within a safepoint. + // Therefore we delegate the responsibility of entering/exiting the + // safepoint to a stub which in the VM isolate's heap, which will never lose + // execute permission. + __ movq(TMP, + compiler::Address( + THR, compiler::target::Thread:: + call_native_through_safepoint_entry_point_offset())); + + // Calls RBX within a safepoint. + ASSERT(saved_fp == RBX); + __ movq(RBX, target_address); + __ call(TMP); + } // Although PP is a callee-saved register, it may have been moved by the GC. __ LeaveDartFrame(compiler::kRestoreCallerPP); diff --git a/runtime/vm/compiler/runtime_api.cc b/runtime/vm/compiler/runtime_api.cc index ff30fd91435..b5362e88084 100644 --- a/runtime/vm/compiler/runtime_api.cc +++ b/runtime/vm/compiler/runtime_api.cc @@ -532,6 +532,10 @@ uword Thread::native_execution_state() { return dart::Thread::ExecutionState::kThreadInNative; } +uword Thread::vm_execution_state() { + return dart::Thread::ExecutionState::kThreadInVM; +} + uword Thread::vm_tag_compiled_id() { return dart::VMTag::kDartCompiledTagId; } diff --git a/runtime/vm/compiler/runtime_api.h b/runtime/vm/compiler/runtime_api.h index a1ac4370b3c..8e85af1fd4b 100644 --- a/runtime/vm/compiler/runtime_api.h +++ b/runtime/vm/compiler/runtime_api.h @@ -660,6 +660,7 @@ class Thread : public AllStatic { static uword safepoint_state_acquired(); static word execution_state_offset(); + static uword vm_execution_state(); static uword native_execution_state(); static uword generated_execution_state(); static word stack_overflow_flags_offset(); @@ -695,6 +696,8 @@ class Thread : public AllStatic { static word deoptimize_stub_offset(); static word enter_safepoint_stub_offset(); static word exit_safepoint_stub_offset(); + static word call_native_through_safepoint_stub_offset(); + static word call_native_through_safepoint_entry_point_offset(); #endif // !defined(TARGET_ARCH_DBC) static word no_scope_native_wrapper_entry_point_offset(); diff --git a/runtime/vm/compiler/runtime_offsets_extracted.h b/runtime/vm/compiler/runtime_offsets_extracted.h index 7df33393eb6..507f2d96cf0 100644 --- a/runtime/vm/compiler/runtime_offsets_extracted.h +++ b/runtime/vm/compiler/runtime_offsets_extracted.h @@ -191,60 +191,64 @@ static constexpr dart::compiler::target::word String_hash_offset = 8; static constexpr dart::compiler::target::word String_length_offset = 4; static constexpr dart::compiler::target::word SubtypeTestCache_cache_offset = 4; static constexpr dart::compiler::target::word - Thread_AllocateArray_entry_point_offset = 280; + Thread_AllocateArray_entry_point_offset = 288; static constexpr dart::compiler::target::word Thread_active_exception_offset = - 616; + 624; static constexpr dart::compiler::target::word Thread_active_stacktrace_offset = - 620; + 628; static constexpr dart::compiler::target::word Thread_array_write_barrier_code_offset = 112; static constexpr dart::compiler::target::word - Thread_array_write_barrier_entry_point_offset = 192; + Thread_array_write_barrier_entry_point_offset = 196; static constexpr dart::compiler::target::word Thread_async_stack_trace_offset = 84; static constexpr dart::compiler::target::word - Thread_auto_scope_native_wrapper_entry_point_offset = 240; + Thread_auto_scope_native_wrapper_entry_point_offset = 248; static constexpr dart::compiler::target::word Thread_bool_false_offset = 104; static constexpr dart::compiler::target::word Thread_bool_true_offset = 100; static constexpr dart::compiler::target::word - Thread_call_to_runtime_entry_point_offset = 196; + Thread_call_to_runtime_entry_point_offset = 200; static constexpr dart::compiler::target::word Thread_call_to_runtime_stub_offset = 132; -static constexpr dart::compiler::target::word Thread_dart_stream_offset = 648; +static constexpr dart::compiler::target::word Thread_dart_stream_offset = 656; static constexpr dart::compiler::target::word Thread_optimize_entry_offset = - 224; + 228; static constexpr dart::compiler::target::word Thread_optimize_stub_offset = 156; static constexpr dart::compiler::target::word Thread_deoptimize_entry_offset = - 228; + 232; static constexpr dart::compiler::target::word Thread_deoptimize_stub_offset = 160; static constexpr dart::compiler::target::word Thread_double_abs_address_offset = - 260; + 268; static constexpr dart::compiler::target::word - Thread_double_negate_address_offset = 256; + Thread_double_negate_address_offset = 264; static constexpr dart::compiler::target::word Thread_end_offset = 60; static constexpr dart::compiler::target::word Thread_enter_safepoint_stub_offset = 180; static constexpr dart::compiler::target::word Thread_execution_state_offset = - 632; + 640; static constexpr dart::compiler::target::word Thread_exit_safepoint_stub_offset = 184; +static constexpr dart::compiler::target::word + Thread_call_native_through_safepoint_stub_offset = 188; +static constexpr dart::compiler::target::word + Thread_call_native_through_safepoint_entry_point_offset = 240; static constexpr dart::compiler::target::word Thread_fix_allocation_stub_code_offset = 120; static constexpr dart::compiler::target::word Thread_fix_callers_target_code_offset = 116; static constexpr dart::compiler::target::word - Thread_float_absolute_address_offset = 272; + Thread_float_absolute_address_offset = 280; static constexpr dart::compiler::target::word - Thread_float_negate_address_offset = 268; + Thread_float_negate_address_offset = 276; static constexpr dart::compiler::target::word Thread_float_not_address_offset = - 264; + 272; static constexpr dart::compiler::target::word - Thread_float_zerow_address_offset = 276; + Thread_float_zerow_address_offset = 284; static constexpr dart::compiler::target::word Thread_global_object_pool_offset = - 624; + 632; static constexpr dart::compiler::target::word - Thread_interpret_call_entry_point_offset = 244; + Thread_interpret_call_entry_point_offset = 252; static constexpr dart::compiler::target::word Thread_invoke_dart_code_from_bytecode_stub_offset = 128; static constexpr dart::compiler::target::word @@ -259,38 +263,38 @@ static constexpr dart::compiler::target::word static constexpr dart::compiler::target::word Thread_marking_stack_block_offset = 72; static constexpr dart::compiler::target::word - Thread_megamorphic_call_checked_entry_offset = 216; + Thread_megamorphic_call_checked_entry_offset = 220; static constexpr dart::compiler::target::word - Thread_monomorphic_miss_entry_offset = 220; + Thread_monomorphic_miss_entry_offset = 224; static constexpr dart::compiler::target::word Thread_monomorphic_miss_stub_offset = 152; static constexpr dart::compiler::target::word - Thread_no_scope_native_wrapper_entry_point_offset = 236; + Thread_no_scope_native_wrapper_entry_point_offset = 244; static constexpr dart::compiler::target::word - Thread_null_error_shared_with_fpu_regs_entry_point_offset = 204; + Thread_null_error_shared_with_fpu_regs_entry_point_offset = 208; static constexpr dart::compiler::target::word Thread_null_error_shared_with_fpu_regs_stub_offset = 140; static constexpr dart::compiler::target::word - Thread_null_error_shared_without_fpu_regs_entry_point_offset = 200; + Thread_null_error_shared_without_fpu_regs_entry_point_offset = 204; static constexpr dart::compiler::target::word Thread_null_error_shared_without_fpu_regs_stub_offset = 136; static constexpr dart::compiler::target::word Thread_object_null_offset = 96; static constexpr dart::compiler::target::word - Thread_predefined_symbols_address_offset = 248; -static constexpr dart::compiler::target::word Thread_resume_pc_offset = 628; + Thread_predefined_symbols_address_offset = 256; +static constexpr dart::compiler::target::word Thread_resume_pc_offset = 636; static constexpr dart::compiler::target::word Thread_safepoint_state_offset = - 636; + 644; static constexpr dart::compiler::target::word Thread_slow_type_test_stub_offset = 172; static constexpr dart::compiler::target::word Thread_stack_limit_offset = 36; static constexpr dart::compiler::target::word Thread_stack_overflow_flags_offset = 40; static constexpr dart::compiler::target::word - Thread_stack_overflow_shared_with_fpu_regs_entry_point_offset = 212; + Thread_stack_overflow_shared_with_fpu_regs_entry_point_offset = 216; static constexpr dart::compiler::target::word Thread_stack_overflow_shared_with_fpu_regs_stub_offset = 148; static constexpr dart::compiler::target::word - Thread_stack_overflow_shared_without_fpu_regs_entry_point_offset = 208; + Thread_stack_overflow_shared_without_fpu_regs_entry_point_offset = 212; static constexpr dart::compiler::target::word Thread_stack_overflow_shared_without_fpu_regs_stub_offset = 144; static constexpr dart::compiler::target::word Thread_store_buffer_block_offset = @@ -305,12 +309,12 @@ static constexpr dart::compiler::target::word Thread_vm_tag_offset = 80; static constexpr dart::compiler::target::word Thread_write_barrier_code_offset = 108; static constexpr dart::compiler::target::word - Thread_write_barrier_entry_point_offset = 188; + Thread_write_barrier_entry_point_offset = 192; static constexpr dart::compiler::target::word Thread_write_barrier_mask_offset = 44; static constexpr dart::compiler::target::word - Thread_verify_callback_entry_offset = 232; -static constexpr dart::compiler::target::word Thread_callback_code_offset = 640; + Thread_verify_callback_entry_offset = 236; +static constexpr dart::compiler::target::word Thread_callback_code_offset = 648; static constexpr dart::compiler::target::word TimelineStream_enabled_offset = 8; static constexpr dart::compiler::target::word TwoByteString_data_offset = 12; static constexpr dart::compiler::target::word Type_arguments_offset = 16; @@ -345,8 +349,8 @@ static constexpr dart::compiler::target::word Code_function_entry_point_offset[] = {4, 8}; static constexpr dart::compiler::target::word Thread_write_barrier_wrappers_thread_offset[] = { - 580, 584, 588, 592, 596, -1, 600, 604, - 608, 612, -1, -1, -1, -1, -1, -1}; + 588, 592, 596, 600, 604, -1, 608, 612, + 616, 620, -1, -1, -1, -1, -1, -1}; static constexpr dart::compiler::target::word Array_header_size = 12; static constexpr dart::compiler::target::word Context_header_size = 12; static constexpr dart::compiler::target::word Double_InstanceSize = 16; @@ -546,60 +550,64 @@ static constexpr dart::compiler::target::word String_hash_offset = 4; static constexpr dart::compiler::target::word String_length_offset = 8; static constexpr dart::compiler::target::word SubtypeTestCache_cache_offset = 8; static constexpr dart::compiler::target::word - Thread_AllocateArray_entry_point_offset = 552; + Thread_AllocateArray_entry_point_offset = 568; static constexpr dart::compiler::target::word Thread_active_exception_offset = - 1240; + 1256; static constexpr dart::compiler::target::word Thread_active_stacktrace_offset = - 1248; + 1264; static constexpr dart::compiler::target::word Thread_array_write_barrier_code_offset = 216; static constexpr dart::compiler::target::word - Thread_array_write_barrier_entry_point_offset = 376; + Thread_array_write_barrier_entry_point_offset = 384; static constexpr dart::compiler::target::word Thread_async_stack_trace_offset = 168; static constexpr dart::compiler::target::word - Thread_auto_scope_native_wrapper_entry_point_offset = 472; + Thread_auto_scope_native_wrapper_entry_point_offset = 488; static constexpr dart::compiler::target::word Thread_bool_false_offset = 200; static constexpr dart::compiler::target::word Thread_bool_true_offset = 192; static constexpr dart::compiler::target::word - Thread_call_to_runtime_entry_point_offset = 384; + Thread_call_to_runtime_entry_point_offset = 392; static constexpr dart::compiler::target::word Thread_call_to_runtime_stub_offset = 256; -static constexpr dart::compiler::target::word Thread_dart_stream_offset = 1304; +static constexpr dart::compiler::target::word Thread_dart_stream_offset = 1320; static constexpr dart::compiler::target::word Thread_optimize_entry_offset = - 440; + 448; static constexpr dart::compiler::target::word Thread_optimize_stub_offset = 304; static constexpr dart::compiler::target::word Thread_deoptimize_entry_offset = - 448; + 456; static constexpr dart::compiler::target::word Thread_deoptimize_stub_offset = 312; static constexpr dart::compiler::target::word Thread_double_abs_address_offset = - 512; + 528; static constexpr dart::compiler::target::word - Thread_double_negate_address_offset = 504; + Thread_double_negate_address_offset = 520; static constexpr dart::compiler::target::word Thread_end_offset = 120; static constexpr dart::compiler::target::word Thread_enter_safepoint_stub_offset = 352; static constexpr dart::compiler::target::word Thread_execution_state_offset = - 1272; + 1288; static constexpr dart::compiler::target::word Thread_exit_safepoint_stub_offset = 360; +static constexpr dart::compiler::target::word + Thread_call_native_through_safepoint_stub_offset = 368; +static constexpr dart::compiler::target::word + Thread_call_native_through_safepoint_entry_point_offset = 472; static constexpr dart::compiler::target::word Thread_fix_allocation_stub_code_offset = 232; static constexpr dart::compiler::target::word Thread_fix_callers_target_code_offset = 224; static constexpr dart::compiler::target::word - Thread_float_absolute_address_offset = 536; + Thread_float_absolute_address_offset = 552; static constexpr dart::compiler::target::word - Thread_float_negate_address_offset = 528; + Thread_float_negate_address_offset = 544; static constexpr dart::compiler::target::word Thread_float_not_address_offset = - 520; + 536; static constexpr dart::compiler::target::word - Thread_float_zerow_address_offset = 544; + Thread_float_zerow_address_offset = 560; static constexpr dart::compiler::target::word Thread_global_object_pool_offset = - 1256; + 1272; static constexpr dart::compiler::target::word - Thread_interpret_call_entry_point_offset = 480; + Thread_interpret_call_entry_point_offset = 496; static constexpr dart::compiler::target::word Thread_invoke_dart_code_from_bytecode_stub_offset = 248; static constexpr dart::compiler::target::word @@ -614,38 +622,38 @@ static constexpr dart::compiler::target::word static constexpr dart::compiler::target::word Thread_marking_stack_block_offset = 144; static constexpr dart::compiler::target::word - Thread_megamorphic_call_checked_entry_offset = 424; + Thread_megamorphic_call_checked_entry_offset = 432; static constexpr dart::compiler::target::word - Thread_monomorphic_miss_entry_offset = 432; + Thread_monomorphic_miss_entry_offset = 440; static constexpr dart::compiler::target::word Thread_monomorphic_miss_stub_offset = 296; static constexpr dart::compiler::target::word - Thread_no_scope_native_wrapper_entry_point_offset = 464; + Thread_no_scope_native_wrapper_entry_point_offset = 480; static constexpr dart::compiler::target::word - Thread_null_error_shared_with_fpu_regs_entry_point_offset = 400; + Thread_null_error_shared_with_fpu_regs_entry_point_offset = 408; static constexpr dart::compiler::target::word Thread_null_error_shared_with_fpu_regs_stub_offset = 272; static constexpr dart::compiler::target::word - Thread_null_error_shared_without_fpu_regs_entry_point_offset = 392; + Thread_null_error_shared_without_fpu_regs_entry_point_offset = 400; static constexpr dart::compiler::target::word Thread_null_error_shared_without_fpu_regs_stub_offset = 264; static constexpr dart::compiler::target::word Thread_object_null_offset = 184; static constexpr dart::compiler::target::word - Thread_predefined_symbols_address_offset = 488; -static constexpr dart::compiler::target::word Thread_resume_pc_offset = 1264; + Thread_predefined_symbols_address_offset = 504; +static constexpr dart::compiler::target::word Thread_resume_pc_offset = 1280; static constexpr dart::compiler::target::word Thread_safepoint_state_offset = - 1280; + 1296; static constexpr dart::compiler::target::word Thread_slow_type_test_stub_offset = 336; static constexpr dart::compiler::target::word Thread_stack_limit_offset = 72; static constexpr dart::compiler::target::word Thread_stack_overflow_flags_offset = 80; static constexpr dart::compiler::target::word - Thread_stack_overflow_shared_with_fpu_regs_entry_point_offset = 416; + Thread_stack_overflow_shared_with_fpu_regs_entry_point_offset = 424; static constexpr dart::compiler::target::word Thread_stack_overflow_shared_with_fpu_regs_stub_offset = 288; static constexpr dart::compiler::target::word - Thread_stack_overflow_shared_without_fpu_regs_entry_point_offset = 408; + Thread_stack_overflow_shared_without_fpu_regs_entry_point_offset = 416; static constexpr dart::compiler::target::word Thread_stack_overflow_shared_without_fpu_regs_stub_offset = 280; static constexpr dart::compiler::target::word Thread_store_buffer_block_offset = @@ -660,13 +668,13 @@ static constexpr dart::compiler::target::word Thread_vm_tag_offset = 160; static constexpr dart::compiler::target::word Thread_write_barrier_code_offset = 208; static constexpr dart::compiler::target::word - Thread_write_barrier_entry_point_offset = 368; + Thread_write_barrier_entry_point_offset = 376; static constexpr dart::compiler::target::word Thread_write_barrier_mask_offset = 88; static constexpr dart::compiler::target::word - Thread_verify_callback_entry_offset = 456; + Thread_verify_callback_entry_offset = 464; static constexpr dart::compiler::target::word Thread_callback_code_offset = - 1288; + 1304; static constexpr dart::compiler::target::word TimelineStream_enabled_offset = 16; static constexpr dart::compiler::target::word TwoByteString_data_offset = 16; @@ -702,8 +710,8 @@ static constexpr dart::compiler::target::word Code_function_entry_point_offset[] = {8, 16}; static constexpr dart::compiler::target::word Thread_write_barrier_wrappers_thread_offset[] = { - 1152, 1160, 1168, 1176, -1, -1, 1184, 1192, - 1200, 1208, 1216, -1, 1224, 1232, -1, -1}; + 1168, 1176, 1184, 1192, -1, -1, 1200, 1208, + 1216, 1224, 1232, -1, 1240, 1248, -1, -1}; static constexpr dart::compiler::target::word Array_header_size = 24; static constexpr dart::compiler::target::word Context_header_size = 24; static constexpr dart::compiler::target::word Double_InstanceSize = 16; @@ -901,60 +909,64 @@ static constexpr dart::compiler::target::word String_hash_offset = 8; static constexpr dart::compiler::target::word String_length_offset = 4; static constexpr dart::compiler::target::word SubtypeTestCache_cache_offset = 4; static constexpr dart::compiler::target::word - Thread_AllocateArray_entry_point_offset = 280; + Thread_AllocateArray_entry_point_offset = 288; static constexpr dart::compiler::target::word Thread_active_exception_offset = - 580; + 588; static constexpr dart::compiler::target::word Thread_active_stacktrace_offset = - 584; + 592; static constexpr dart::compiler::target::word Thread_array_write_barrier_code_offset = 112; static constexpr dart::compiler::target::word - Thread_array_write_barrier_entry_point_offset = 192; + Thread_array_write_barrier_entry_point_offset = 196; static constexpr dart::compiler::target::word Thread_async_stack_trace_offset = 84; static constexpr dart::compiler::target::word - Thread_auto_scope_native_wrapper_entry_point_offset = 240; + Thread_auto_scope_native_wrapper_entry_point_offset = 248; static constexpr dart::compiler::target::word Thread_bool_false_offset = 104; static constexpr dart::compiler::target::word Thread_bool_true_offset = 100; static constexpr dart::compiler::target::word - Thread_call_to_runtime_entry_point_offset = 196; + Thread_call_to_runtime_entry_point_offset = 200; static constexpr dart::compiler::target::word Thread_call_to_runtime_stub_offset = 132; -static constexpr dart::compiler::target::word Thread_dart_stream_offset = 612; +static constexpr dart::compiler::target::word Thread_dart_stream_offset = 620; static constexpr dart::compiler::target::word Thread_optimize_entry_offset = - 224; + 228; static constexpr dart::compiler::target::word Thread_optimize_stub_offset = 156; static constexpr dart::compiler::target::word Thread_deoptimize_entry_offset = - 228; + 232; static constexpr dart::compiler::target::word Thread_deoptimize_stub_offset = 160; static constexpr dart::compiler::target::word Thread_double_abs_address_offset = - 260; + 268; static constexpr dart::compiler::target::word - Thread_double_negate_address_offset = 256; + Thread_double_negate_address_offset = 264; static constexpr dart::compiler::target::word Thread_end_offset = 60; static constexpr dart::compiler::target::word Thread_enter_safepoint_stub_offset = 180; static constexpr dart::compiler::target::word Thread_execution_state_offset = - 596; + 604; static constexpr dart::compiler::target::word Thread_exit_safepoint_stub_offset = 184; +static constexpr dart::compiler::target::word + Thread_call_native_through_safepoint_stub_offset = 188; +static constexpr dart::compiler::target::word + Thread_call_native_through_safepoint_entry_point_offset = 240; static constexpr dart::compiler::target::word Thread_fix_allocation_stub_code_offset = 120; static constexpr dart::compiler::target::word Thread_fix_callers_target_code_offset = 116; static constexpr dart::compiler::target::word - Thread_float_absolute_address_offset = 272; + Thread_float_absolute_address_offset = 280; static constexpr dart::compiler::target::word - Thread_float_negate_address_offset = 268; + Thread_float_negate_address_offset = 276; static constexpr dart::compiler::target::word Thread_float_not_address_offset = - 264; + 272; static constexpr dart::compiler::target::word - Thread_float_zerow_address_offset = 276; + Thread_float_zerow_address_offset = 284; static constexpr dart::compiler::target::word Thread_global_object_pool_offset = - 588; + 596; static constexpr dart::compiler::target::word - Thread_interpret_call_entry_point_offset = 244; + Thread_interpret_call_entry_point_offset = 252; static constexpr dart::compiler::target::word Thread_invoke_dart_code_from_bytecode_stub_offset = 128; static constexpr dart::compiler::target::word @@ -969,38 +981,38 @@ static constexpr dart::compiler::target::word static constexpr dart::compiler::target::word Thread_marking_stack_block_offset = 72; static constexpr dart::compiler::target::word - Thread_megamorphic_call_checked_entry_offset = 216; + Thread_megamorphic_call_checked_entry_offset = 220; static constexpr dart::compiler::target::word - Thread_monomorphic_miss_entry_offset = 220; + Thread_monomorphic_miss_entry_offset = 224; static constexpr dart::compiler::target::word Thread_monomorphic_miss_stub_offset = 152; static constexpr dart::compiler::target::word - Thread_no_scope_native_wrapper_entry_point_offset = 236; + Thread_no_scope_native_wrapper_entry_point_offset = 244; static constexpr dart::compiler::target::word - Thread_null_error_shared_with_fpu_regs_entry_point_offset = 204; + Thread_null_error_shared_with_fpu_regs_entry_point_offset = 208; static constexpr dart::compiler::target::word Thread_null_error_shared_with_fpu_regs_stub_offset = 140; static constexpr dart::compiler::target::word - Thread_null_error_shared_without_fpu_regs_entry_point_offset = 200; + Thread_null_error_shared_without_fpu_regs_entry_point_offset = 204; static constexpr dart::compiler::target::word Thread_null_error_shared_without_fpu_regs_stub_offset = 136; static constexpr dart::compiler::target::word Thread_object_null_offset = 96; static constexpr dart::compiler::target::word - Thread_predefined_symbols_address_offset = 248; -static constexpr dart::compiler::target::word Thread_resume_pc_offset = 592; + Thread_predefined_symbols_address_offset = 256; +static constexpr dart::compiler::target::word Thread_resume_pc_offset = 600; static constexpr dart::compiler::target::word Thread_safepoint_state_offset = - 600; + 608; static constexpr dart::compiler::target::word Thread_slow_type_test_stub_offset = 172; static constexpr dart::compiler::target::word Thread_stack_limit_offset = 36; static constexpr dart::compiler::target::word Thread_stack_overflow_flags_offset = 40; static constexpr dart::compiler::target::word - Thread_stack_overflow_shared_with_fpu_regs_entry_point_offset = 212; + Thread_stack_overflow_shared_with_fpu_regs_entry_point_offset = 216; static constexpr dart::compiler::target::word Thread_stack_overflow_shared_with_fpu_regs_stub_offset = 148; static constexpr dart::compiler::target::word - Thread_stack_overflow_shared_without_fpu_regs_entry_point_offset = 208; + Thread_stack_overflow_shared_without_fpu_regs_entry_point_offset = 212; static constexpr dart::compiler::target::word Thread_stack_overflow_shared_without_fpu_regs_stub_offset = 144; static constexpr dart::compiler::target::word Thread_store_buffer_block_offset = @@ -1015,12 +1027,12 @@ static constexpr dart::compiler::target::word Thread_vm_tag_offset = 80; static constexpr dart::compiler::target::word Thread_write_barrier_code_offset = 108; static constexpr dart::compiler::target::word - Thread_write_barrier_entry_point_offset = 188; + Thread_write_barrier_entry_point_offset = 192; static constexpr dart::compiler::target::word Thread_write_barrier_mask_offset = 44; static constexpr dart::compiler::target::word - Thread_verify_callback_entry_offset = 232; -static constexpr dart::compiler::target::word Thread_callback_code_offset = 604; + Thread_verify_callback_entry_offset = 236; +static constexpr dart::compiler::target::word Thread_callback_code_offset = 612; static constexpr dart::compiler::target::word TimelineStream_enabled_offset = 8; static constexpr dart::compiler::target::word TwoByteString_data_offset = 12; static constexpr dart::compiler::target::word Type_arguments_offset = 16; @@ -1252,60 +1264,64 @@ static constexpr dart::compiler::target::word String_hash_offset = 4; static constexpr dart::compiler::target::word String_length_offset = 8; static constexpr dart::compiler::target::word SubtypeTestCache_cache_offset = 8; static constexpr dart::compiler::target::word - Thread_AllocateArray_entry_point_offset = 552; + Thread_AllocateArray_entry_point_offset = 568; static constexpr dart::compiler::target::word Thread_active_exception_offset = - 1328; + 1344; static constexpr dart::compiler::target::word Thread_active_stacktrace_offset = - 1336; + 1352; static constexpr dart::compiler::target::word Thread_array_write_barrier_code_offset = 216; static constexpr dart::compiler::target::word - Thread_array_write_barrier_entry_point_offset = 376; + Thread_array_write_barrier_entry_point_offset = 384; static constexpr dart::compiler::target::word Thread_async_stack_trace_offset = 168; static constexpr dart::compiler::target::word - Thread_auto_scope_native_wrapper_entry_point_offset = 472; + Thread_auto_scope_native_wrapper_entry_point_offset = 488; static constexpr dart::compiler::target::word Thread_bool_false_offset = 200; static constexpr dart::compiler::target::word Thread_bool_true_offset = 192; static constexpr dart::compiler::target::word - Thread_call_to_runtime_entry_point_offset = 384; + Thread_call_to_runtime_entry_point_offset = 392; static constexpr dart::compiler::target::word Thread_call_to_runtime_stub_offset = 256; -static constexpr dart::compiler::target::word Thread_dart_stream_offset = 1392; +static constexpr dart::compiler::target::word Thread_dart_stream_offset = 1408; static constexpr dart::compiler::target::word Thread_optimize_entry_offset = - 440; + 448; static constexpr dart::compiler::target::word Thread_optimize_stub_offset = 304; static constexpr dart::compiler::target::word Thread_deoptimize_entry_offset = - 448; + 456; static constexpr dart::compiler::target::word Thread_deoptimize_stub_offset = 312; static constexpr dart::compiler::target::word Thread_double_abs_address_offset = - 512; + 528; static constexpr dart::compiler::target::word - Thread_double_negate_address_offset = 504; + Thread_double_negate_address_offset = 520; static constexpr dart::compiler::target::word Thread_end_offset = 120; static constexpr dart::compiler::target::word Thread_enter_safepoint_stub_offset = 352; static constexpr dart::compiler::target::word Thread_execution_state_offset = - 1360; + 1376; static constexpr dart::compiler::target::word Thread_exit_safepoint_stub_offset = 360; +static constexpr dart::compiler::target::word + Thread_call_native_through_safepoint_stub_offset = 368; +static constexpr dart::compiler::target::word + Thread_call_native_through_safepoint_entry_point_offset = 472; static constexpr dart::compiler::target::word Thread_fix_allocation_stub_code_offset = 232; static constexpr dart::compiler::target::word Thread_fix_callers_target_code_offset = 224; static constexpr dart::compiler::target::word - Thread_float_absolute_address_offset = 536; + Thread_float_absolute_address_offset = 552; static constexpr dart::compiler::target::word - Thread_float_negate_address_offset = 528; + Thread_float_negate_address_offset = 544; static constexpr dart::compiler::target::word Thread_float_not_address_offset = - 520; + 536; static constexpr dart::compiler::target::word - Thread_float_zerow_address_offset = 544; + Thread_float_zerow_address_offset = 560; static constexpr dart::compiler::target::word Thread_global_object_pool_offset = - 1344; + 1360; static constexpr dart::compiler::target::word - Thread_interpret_call_entry_point_offset = 480; + Thread_interpret_call_entry_point_offset = 496; static constexpr dart::compiler::target::word Thread_invoke_dart_code_from_bytecode_stub_offset = 248; static constexpr dart::compiler::target::word @@ -1320,38 +1336,38 @@ static constexpr dart::compiler::target::word static constexpr dart::compiler::target::word Thread_marking_stack_block_offset = 144; static constexpr dart::compiler::target::word - Thread_megamorphic_call_checked_entry_offset = 424; + Thread_megamorphic_call_checked_entry_offset = 432; static constexpr dart::compiler::target::word - Thread_monomorphic_miss_entry_offset = 432; + Thread_monomorphic_miss_entry_offset = 440; static constexpr dart::compiler::target::word Thread_monomorphic_miss_stub_offset = 296; static constexpr dart::compiler::target::word - Thread_no_scope_native_wrapper_entry_point_offset = 464; + Thread_no_scope_native_wrapper_entry_point_offset = 480; static constexpr dart::compiler::target::word - Thread_null_error_shared_with_fpu_regs_entry_point_offset = 400; + Thread_null_error_shared_with_fpu_regs_entry_point_offset = 408; static constexpr dart::compiler::target::word Thread_null_error_shared_with_fpu_regs_stub_offset = 272; static constexpr dart::compiler::target::word - Thread_null_error_shared_without_fpu_regs_entry_point_offset = 392; + Thread_null_error_shared_without_fpu_regs_entry_point_offset = 400; static constexpr dart::compiler::target::word Thread_null_error_shared_without_fpu_regs_stub_offset = 264; static constexpr dart::compiler::target::word Thread_object_null_offset = 184; static constexpr dart::compiler::target::word - Thread_predefined_symbols_address_offset = 488; -static constexpr dart::compiler::target::word Thread_resume_pc_offset = 1352; + Thread_predefined_symbols_address_offset = 504; +static constexpr dart::compiler::target::word Thread_resume_pc_offset = 1368; static constexpr dart::compiler::target::word Thread_safepoint_state_offset = - 1368; + 1384; static constexpr dart::compiler::target::word Thread_slow_type_test_stub_offset = 336; static constexpr dart::compiler::target::word Thread_stack_limit_offset = 72; static constexpr dart::compiler::target::word Thread_stack_overflow_flags_offset = 80; static constexpr dart::compiler::target::word - Thread_stack_overflow_shared_with_fpu_regs_entry_point_offset = 416; + Thread_stack_overflow_shared_with_fpu_regs_entry_point_offset = 424; static constexpr dart::compiler::target::word Thread_stack_overflow_shared_with_fpu_regs_stub_offset = 288; static constexpr dart::compiler::target::word - Thread_stack_overflow_shared_without_fpu_regs_entry_point_offset = 408; + Thread_stack_overflow_shared_without_fpu_regs_entry_point_offset = 416; static constexpr dart::compiler::target::word Thread_stack_overflow_shared_without_fpu_regs_stub_offset = 280; static constexpr dart::compiler::target::word Thread_store_buffer_block_offset = @@ -1366,13 +1382,13 @@ static constexpr dart::compiler::target::word Thread_vm_tag_offset = 160; static constexpr dart::compiler::target::word Thread_write_barrier_code_offset = 208; static constexpr dart::compiler::target::word - Thread_write_barrier_entry_point_offset = 368; + Thread_write_barrier_entry_point_offset = 376; static constexpr dart::compiler::target::word Thread_write_barrier_mask_offset = 88; static constexpr dart::compiler::target::word - Thread_verify_callback_entry_offset = 456; + Thread_verify_callback_entry_offset = 464; static constexpr dart::compiler::target::word Thread_callback_code_offset = - 1376; + 1392; static constexpr dart::compiler::target::word TimelineStream_enabled_offset = 16; static constexpr dart::compiler::target::word TwoByteString_data_offset = 16; @@ -1408,9 +1424,9 @@ static constexpr dart::compiler::target::word Code_function_entry_point_offset[] = {8, 16}; static constexpr dart::compiler::target::word Thread_write_barrier_wrappers_thread_offset[] = { - 1152, 1160, 1168, 1176, 1184, 1192, 1200, 1208, 1216, 1224, 1232, - 1240, 1248, 1256, 1264, -1, -1, -1, -1, 1272, 1280, 1288, - 1296, 1304, 1312, 1320, -1, -1, -1, -1, -1, -1}; + 1168, 1176, 1184, 1192, 1200, 1208, 1216, 1224, 1232, 1240, 1248, + 1256, 1264, 1272, 1280, -1, -1, -1, -1, 1288, 1296, 1304, + 1312, 1320, 1328, 1336, -1, -1, -1, -1, -1, -1}; static constexpr dart::compiler::target::word Array_header_size = 24; static constexpr dart::compiler::target::word Context_header_size = 24; static constexpr dart::compiler::target::word Double_InstanceSize = 16; diff --git a/runtime/vm/compiler/runtime_offsets_list.h b/runtime/vm/compiler/runtime_offsets_list.h index 74264ca3f15..29588ec2d45 100644 --- a/runtime/vm/compiler/runtime_offsets_list.h +++ b/runtime/vm/compiler/runtime_offsets_list.h @@ -167,6 +167,8 @@ NOT_IN_DBC(FIELD(Thread, enter_safepoint_stub_offset)) \ FIELD(Thread, execution_state_offset) \ NOT_IN_DBC(FIELD(Thread, exit_safepoint_stub_offset)) \ + NOT_IN_DBC(FIELD(Thread, call_native_through_safepoint_stub_offset)) \ + NOT_IN_DBC(FIELD(Thread, call_native_through_safepoint_entry_point_offset)) \ NOT_IN_DBC(FIELD(Thread, fix_allocation_stub_code_offset)) \ NOT_IN_DBC(FIELD(Thread, fix_callers_target_code_offset)) \ FIELD(Thread, float_absolute_address_offset) \ diff --git a/runtime/vm/compiler/stub_code_compiler_arm.cc b/runtime/vm/compiler/stub_code_compiler_arm.cc index 6ee73bcd3ca..39974a27260 100644 --- a/runtime/vm/compiler/stub_code_compiler_arm.cc +++ b/runtime/vm/compiler/stub_code_compiler_arm.cc @@ -291,6 +291,13 @@ void StubCodeCompiler::GenerateExitSafepointStub(Assembler* assembler) { __ EnterFrame((1 << FP) | (1 << LR), 0); __ ReserveAlignedFrameSpace(0); + + // Set the execution state to VM while waiting for the safepoint to end. + // This isn't strictly necessary but enables tests to check that we're not + // in native code anymore. See tests/ffi/function_gc_test.dart for example. + __ LoadImmediate(R0, target::Thread::vm_execution_state()); + __ str(R0, Address(THR, target::Thread::execution_state_offset())); + __ ldr(R0, Address(THR, kExitSafepointRuntimeEntry.OffsetFromThread())); __ blx(R0); __ LeaveFrame((1 << FP) | (1 << LR), 0); @@ -299,6 +306,31 @@ void StubCodeCompiler::GenerateExitSafepointStub(Assembler* assembler) { __ Ret(); } +// Call a native function within a safepoint. +// +// On entry: +// Stack: set up for call, incl. alignment +// R8: target to call +// +// On exit: +// Stack: preserved +// NOTFP, R4: clobbered, although normally callee-saved +void StubCodeCompiler::GenerateCallNativeThroughSafepointStub( + Assembler* assembler) { + COMPILE_ASSERT((kAbiPreservedCpuRegs & (1 << R4)) != 0); + + // TransitionGeneratedToNative might clobber LR if it takes the slow path. + __ mov(R4, Operand(LR)); + + __ TransitionGeneratedToNative(R8, FPREG, R9 /*volatile*/, NOTFP); + + __ blx(R8); + + __ TransitionNativeToGenerated(R9 /*volatile*/, NOTFP); + + __ bx(R4); +} + void StubCodeCompiler::GenerateVerifyCallbackStub(Assembler* assembler) { __ EnterFrame(1 << FP | 1 << LR, 0); __ ReserveAlignedFrameSpace(0); @@ -3200,7 +3232,7 @@ void StubCodeCompiler::GenerateUnlinkedCallStub(Assembler* assembler) { __ CallRuntime(kUnlinkedCallRuntimeEntry, 3); __ Drop(2); __ Pop(CODE_REG); // result = stub - __ Pop(R9); // result = IC + __ Pop(R9); // result = IC __ Pop(R0); // Restore receiver. __ LeaveStubFrame(); @@ -3242,7 +3274,7 @@ void StubCodeCompiler::GenerateSingleTargetCallStub(Assembler* assembler) { __ CallRuntime(kSingleTargetMissRuntimeEntry, 2); __ Drop(1); __ Pop(CODE_REG); // result = stub - __ Pop(R9); // result = IC + __ Pop(R9); // result = IC __ Pop(R0); // Restore receiver. __ LeaveStubFrame(); @@ -3266,7 +3298,7 @@ void StubCodeCompiler::GenerateMonomorphicMissStub(Assembler* assembler) { __ CallRuntime(kMonomorphicMissRuntimeEntry, 2); __ Drop(1); __ Pop(CODE_REG); // result = stub - __ Pop(R9); // result = IC + __ Pop(R9); // result = IC __ Pop(R0); // Restore receiver. __ LeaveStubFrame(); diff --git a/runtime/vm/compiler/stub_code_compiler_arm64.cc b/runtime/vm/compiler/stub_code_compiler_arm64.cc index 4be246fb683..9961c181770 100644 --- a/runtime/vm/compiler/stub_code_compiler_arm64.cc +++ b/runtime/vm/compiler/stub_code_compiler_arm64.cc @@ -236,6 +236,13 @@ void StubCodeCompiler::GenerateExitSafepointStub(Assembler* assembler) { __ ReserveAlignedFrameSpace(0); __ mov(CSP, SP); + + // Set the execution state to VM while waiting for the safepoint to end. + // This isn't strictly necessary but enables tests to check that we're not + // in native code anymore. See tests/ffi/function_gc_test.dart for example. + __ LoadImmediate(R0, target::Thread::vm_execution_state()); + __ str(R0, Address(THR, target::Thread::execution_state_offset())); + __ ldr(R0, Address(THR, kExitSafepointRuntimeEntry.OffsetFromThread())); __ blr(R0); __ mov(SP, CALLEE_SAVED_TEMP); @@ -245,6 +252,40 @@ void StubCodeCompiler::GenerateExitSafepointStub(Assembler* assembler) { __ Ret(); } +// Calls native code within a safepoint. +// +// On entry: +// R8: target to call +// Stack: set up for native call (SP), aligned, CSP < SP +// +// On exit: +// R19: clobbered, although normally callee-saved +// Stack: preserved, CSP == SP +void StubCodeCompiler::GenerateCallNativeThroughSafepointStub( + Assembler* assembler) { + COMPILE_ASSERT((1 << R19) & kAbiPreservedCpuRegs); + + __ mov(R19, LR); + __ TransitionGeneratedToNative(R8, FPREG, R9 /*volatile*/); + __ mov(CSP, SP); + +#if defined(DEBUG) + // Check CSP alignment. + __ andi(R10 /*volatile*/, SP, + Immediate(~(OS::ActivationFrameAlignment() - 1))); + __ cmp(R10, Operand(SP)); + Label done; + __ b(&done, EQ); + __ Breakpoint(); + __ Bind(&done); +#endif + + __ blr(R8); + __ mov(SP, CSP); + __ TransitionNativeToGenerated(R9); + __ ret(R19); +} + void StubCodeCompiler::GenerateVerifyCallbackStub(Assembler* assembler) { __ EnterFrame(0); __ ReserveAlignedFrameSpace(0); @@ -3279,7 +3320,7 @@ void StubCodeCompiler::GenerateUnlinkedCallStub(Assembler* assembler) { __ CallRuntime(kUnlinkedCallRuntimeEntry, 3); __ Drop(2); __ Pop(CODE_REG); // result = stub - __ Pop(R5); // result = IC + __ Pop(R5); // result = IC __ Pop(R0); // Restore receiver. __ LeaveStubFrame(); @@ -3322,7 +3363,7 @@ void StubCodeCompiler::GenerateSingleTargetCallStub(Assembler* assembler) { __ CallRuntime(kSingleTargetMissRuntimeEntry, 2); __ Drop(1); __ Pop(CODE_REG); // result = stub - __ Pop(R5); // result = IC + __ Pop(R5); // result = IC __ Pop(R0); // Restore receiver. __ LeaveStubFrame(); @@ -3346,7 +3387,7 @@ void StubCodeCompiler::GenerateMonomorphicMissStub(Assembler* assembler) { __ CallRuntime(kMonomorphicMissRuntimeEntry, 2); __ Drop(1); __ Pop(CODE_REG); // result = stub - __ Pop(R5); // result = IC + __ Pop(R5); // result = IC __ Pop(R0); // Restore receiver. __ LeaveStubFrame(); diff --git a/runtime/vm/compiler/stub_code_compiler_ia32.cc b/runtime/vm/compiler/stub_code_compiler_ia32.cc index 2f638329d7f..b770097b29f 100644 --- a/runtime/vm/compiler/stub_code_compiler_ia32.cc +++ b/runtime/vm/compiler/stub_code_compiler_ia32.cc @@ -162,6 +162,13 @@ void StubCodeCompiler::GenerateExitSafepointStub(Assembler* assembler) { __ EnterFrame(0); __ ReserveAlignedFrameSpace(0); + + // Set the execution state to VM while waiting for the safepoint to end. + // This isn't strictly necessary but enables tests to check that we're not + // in native code anymore. See tests/ffi/function_gc_test.dart for example. + __ movl(Address(THR, target::Thread::execution_state_offset()), + Immediate(target::Thread::vm_execution_state())); + __ movl(EAX, Address(THR, kExitSafepointRuntimeEntry.OffsetFromThread())); __ call(EAX); __ LeaveFrame(); @@ -172,6 +179,26 @@ void StubCodeCompiler::GenerateExitSafepointStub(Assembler* assembler) { __ ret(); } +// Calls a native function inside a safepoint. +// +// On entry: +// Stack: set up for native call +// EAX: target to call +// +// On exit: +// Stack: preserved +// EBX: clobbered (even though it's normally callee-saved) +void StubCodeCompiler::GenerateCallNativeThroughSafepointStub( + Assembler* assembler) { + __ popl(EBX); + + __ TransitionGeneratedToNative(EAX, FPREG, ECX /*volatile*/); + __ call(EAX); + __ TransitionNativeToGenerated(ECX /*volatile*/); + + __ jmp(EBX); +} + void StubCodeCompiler::GenerateVerifyCallbackStub(Assembler* assembler) { __ EnterFrame(0); __ ReserveAlignedFrameSpace(0); diff --git a/runtime/vm/compiler/stub_code_compiler_x64.cc b/runtime/vm/compiler/stub_code_compiler_x64.cc index 55af42ec3ff..ae8191775b4 100644 --- a/runtime/vm/compiler/stub_code_compiler_x64.cc +++ b/runtime/vm/compiler/stub_code_compiler_x64.cc @@ -223,6 +223,13 @@ void StubCodeCompiler::GenerateExitSafepointStub(Assembler* assembler) { __ EnterFrame(0); __ ReserveAlignedFrameSpace(0); + + // Set the execution state to VM while waiting for the safepoint to end. + // This isn't strictly necessary but enables tests to check that we're not + // in native code anymore. See tests/ffi/function_gc_test.dart for example. + __ movq(Address(THR, target::Thread::execution_state_offset()), + Immediate(target::Thread::vm_execution_state())); + __ movq(RAX, Address(THR, kExitSafepointRuntimeEntry.OffsetFromThread())); __ CallCFunction(RAX); __ LeaveFrame(); @@ -245,6 +252,29 @@ void StubCodeCompiler::GenerateVerifyCallbackStub(Assembler* assembler) { __ ret(); } +// Calls native code within a safepoint. +// +// On entry: +// Stack: arguments set up and aligned for native call, excl. shadow space +// RBX = target address to call +// +// On exit: +// Stack pointer lowered by shadow space +// RBX, R12 clobbered +void StubCodeCompiler::GenerateCallNativeThroughSafepointStub( + Assembler* assembler) { + __ TransitionGeneratedToNative(RBX, FPREG); + + __ popq(R12); + __ CallCFunction(RBX); + + __ TransitionNativeToGenerated(); + + // Faster than jmp because it doesn't confuse the branch predictor. + __ pushq(R12); + __ ret(); +} + // RBX: The extracted method. // RDX: The type_arguments_field_offset (or 0) void StubCodeCompiler::GenerateBuildMethodExtractorStub( @@ -3284,7 +3314,7 @@ void StubCodeCompiler::GenerateUnlinkedCallStub(Assembler* assembler) { __ popq(RBX); __ popq(RBX); __ popq(CODE_REG); // result = stub - __ popq(RBX); // result = IC + __ popq(RBX); // result = IC __ popq(RDX); // Restore receiver. __ LeaveStubFrame(); @@ -3326,7 +3356,7 @@ void StubCodeCompiler::GenerateSingleTargetCallStub(Assembler* assembler) { __ CallRuntime(kSingleTargetMissRuntimeEntry, 2); __ popq(RBX); __ popq(CODE_REG); // result = stub - __ popq(RBX); // result = IC + __ popq(RBX); // result = IC __ popq(RDX); // Restore receiver. __ LeaveStubFrame(); @@ -3350,7 +3380,7 @@ void StubCodeCompiler::GenerateMonomorphicMissStub(Assembler* assembler) { __ CallRuntime(kMonomorphicMissRuntimeEntry, 2); __ popq(RBX); __ popq(CODE_REG); // result = stub - __ popq(RBX); // result = IC + __ popq(RBX); // result = IC __ popq(RDX); // Restore receiver. __ LeaveStubFrame(); diff --git a/runtime/vm/constants_arm.h b/runtime/vm/constants_arm.h index 48378f72ff8..15254c5991e 100644 --- a/runtime/vm/constants_arm.h +++ b/runtime/vm/constants_arm.h @@ -371,6 +371,7 @@ class CallingConventions { static constexpr Register kFirstNonArgumentRegister = R8; static constexpr Register kSecondNonArgumentRegister = R9; static constexpr Register kFirstCalleeSavedCpuReg = NOTFP; + static constexpr Register kSecondCalleeSavedCpuReg = R4; static constexpr Register kStackPointerRegister = SPREG; }; diff --git a/runtime/vm/native_api_impl.cc b/runtime/vm/native_api_impl.cc index ae475fb6698..d87bb5a8abd 100644 --- a/runtime/vm/native_api_impl.cc +++ b/runtime/vm/native_api_impl.cc @@ -2,6 +2,8 @@ // for details. 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/dart_native_api.h" #include "platform/assert.h" @@ -241,14 +243,47 @@ DART_EXPORT Dart_Handle Dart_FinalizeAllClasses() { #endif // defined(DART_PRECOMPILED_RUNTIME) } -DART_EXPORT void Dart_ExecuteInternalCommand(const char* command) { - if (!FLAG_enable_testing_pragmas) return; +struct RunInSafepointAndRWCodeArgs { + Isolate* isolate; + std::function* callback; +}; + +DART_EXPORT void* Dart_ExecuteInternalCommand(const char* command, void* arg) { + if (!FLAG_enable_testing_pragmas) return nullptr; - TransitionNativeToVM _(Thread::Current()); if (!strcmp(command, "gc-on-next-allocation")) { + TransitionNativeToVM _(Thread::Current()); Isolate::Current()->heap()->CollectOnNextAllocation(); + return nullptr; + } else if (!strcmp(command, "gc-now")) { - Isolate::Current()->heap()->CollectAllGarbage(Heap::kDebugging); + TransitionNativeToVM _(Thread::Current()); + Isolate::Current()->heap()->CollectAllGarbage(); + return nullptr; + + } else if (!strcmp(command, "is-mutator-in-native")) { + Isolate* const isolate = reinterpret_cast(arg); + if (isolate->mutator_thread()->execution_state() == + Thread::kThreadInNative) { + return arg; + } else { + return nullptr; + } + + } else if (!strcmp(command, "run-in-safepoint-and-rw-code")) { + const RunInSafepointAndRWCodeArgs* const args = + reinterpret_cast(arg); + Thread::EnterIsolateAsHelper(args->isolate, Thread::TaskKind::kUnknownTask); + Thread* const thread = Thread::Current(); + { + SafepointOperationScope scope(thread); + args->isolate->heap()->WriteProtectCode(/*read_only=*/false); + (*args->callback)(); + args->isolate->heap()->WriteProtectCode(/*read_only=*/true); + } + Thread::ExitIsolateAsHelper(); + return nullptr; + } else { UNREACHABLE(); } diff --git a/runtime/vm/runtime_entry.cc b/runtime/vm/runtime_entry.cc index 56669be6a97..9eee7a60279 100644 --- a/runtime/vm/runtime_entry.cc +++ b/runtime/vm/runtime_entry.cc @@ -3012,7 +3012,8 @@ extern "C" void DFLRT_ExitSafepoint(NativeArguments __unusable_) { CHECK_STACK_ALIGNMENT; Thread* thread = Thread::Current(); ASSERT(thread->top_exit_frame_info() != 0); - ASSERT(thread->execution_state() == Thread::kThreadInNative); + + ASSERT(thread->execution_state() == Thread::kThreadInVM); thread->ExitSafepoint(); } DEFINE_RAW_LEAF_RUNTIME_ENTRY(ExitSafepoint, 0, false, &DFLRT_ExitSafepoint); diff --git a/runtime/vm/simulator_dbc.cc b/runtime/vm/simulator_dbc.cc index 2a7176d76ae..619c72eff42 100644 --- a/runtime/vm/simulator_dbc.cc +++ b/runtime/vm/simulator_dbc.cc @@ -1853,8 +1853,16 @@ SwitchDispatch: Exit(thread, FP, SP, pc); { - TransitionGeneratedToNative transition(thread); + thread->set_execution_state(Thread::kThreadInNative); + thread->EnterSafepoint(); + FfiTrampolineCall(marshalled_args_data); + + // We set the execution state to kThreadInVM before waiting for the + // safepoint to end for the benefit of tests like ffi/function_gc_test. + thread->set_execution_state(Thread::kThreadInVM); + thread->ExitSafepoint(); + thread->set_execution_state(Thread::kThreadInGenerated); } const Representation result_rep = sig_desc.ResultRepresentation(); diff --git a/runtime/vm/stub_code_list.h b/runtime/vm/stub_code_list.h index a4d4a04df2b..bb35068c7eb 100644 --- a/runtime/vm/stub_code_list.h +++ b/runtime/vm/stub_code_list.h @@ -78,7 +78,8 @@ namespace dart { V(OneArgOptimizedCheckInlineCacheWithExactnessCheck) \ V(EnterSafepoint) \ V(ExitSafepoint) \ - V(VerifyCallback) + V(VerifyCallback) \ + V(CallNativeThroughSafepoint) #else #define VM_STUB_CODE_LIST(V) \ diff --git a/runtime/vm/thread.h b/runtime/vm/thread.h index e3d58d9ee63..1413ee11aa9 100644 --- a/runtime/vm/thread.h +++ b/runtime/vm/thread.h @@ -123,7 +123,9 @@ class Zone; V(RawCode*, lazy_specialize_type_test_stub_, \ StubCode::LazySpecializeTypeTest().raw(), NULL) \ V(RawCode*, enter_safepoint_stub_, StubCode::EnterSafepoint().raw(), NULL) \ - V(RawCode*, exit_safepoint_stub_, StubCode::ExitSafepoint().raw(), NULL) + V(RawCode*, exit_safepoint_stub_, StubCode::ExitSafepoint().raw(), NULL) \ + V(RawCode*, call_native_through_safepoint_stub_, \ + StubCode::CallNativeThroughSafepoint().raw(), NULL) #endif @@ -168,7 +170,9 @@ class Zone; 0) \ V(uword, optimize_entry_, StubCode::OptimizeFunction().EntryPoint(), 0) \ V(uword, deoptimize_entry_, StubCode::Deoptimize().EntryPoint(), 0) \ - V(uword, verify_callback_entry_, StubCode::VerifyCallback().EntryPoint(), 0) + V(uword, verify_callback_entry_, StubCode::VerifyCallback().EntryPoint(), 0) \ + V(uword, call_native_through_safepoint_entry_point_, \ + StubCode::CallNativeThroughSafepoint().EntryPoint(), 0) #endif #define CACHED_ADDRESSES_LIST(V) \ diff --git a/runtime/vm/virtual_memory.h b/runtime/vm/virtual_memory.h index a91f07a5ad0..2eb43728081 100644 --- a/runtime/vm/virtual_memory.h +++ b/runtime/vm/virtual_memory.h @@ -6,6 +6,7 @@ #define RUNTIME_VM_VIRTUAL_MEMORY_H_ #include "platform/utils.h" +#include "vm/flags.h" #include "vm/globals.h" #include "vm/memory_region.h" @@ -32,6 +33,9 @@ class VirtualMemory { static void Init(); + // Returns true if dual mapping is enabled. + static bool DualMappingEnabled(); + bool Contains(uword addr) const { return region_.Contains(addr); } bool ContainsAlias(uword addr) const { return (AliasOffset() != 0) && alias_.Contains(addr); diff --git a/runtime/vm/virtual_memory_fuchsia.cc b/runtime/vm/virtual_memory_fuchsia.cc index 91e9a78f3bc..99aabb1b8e6 100644 --- a/runtime/vm/virtual_memory_fuchsia.cc +++ b/runtime/vm/virtual_memory_fuchsia.cc @@ -57,6 +57,10 @@ static void Unmap(zx_handle_t vmar, uword start, uword end) { } } +bool VirtualMemory::DualMappingEnabled() { + return FLAG_dual_map_code; +} + VirtualMemory* VirtualMemory::AllocateAligned(intptr_t size, intptr_t alignment, bool is_executable, diff --git a/runtime/vm/virtual_memory_posix.cc b/runtime/vm/virtual_memory_posix.cc index bf9833f66a0..1441d951ea8 100644 --- a/runtime/vm/virtual_memory_posix.cc +++ b/runtime/vm/virtual_memory_posix.cc @@ -83,6 +83,10 @@ void VirtualMemory::Init() { #endif // defined(DUAL_MAPPING_SUPPORTED) } +bool VirtualMemory::DualMappingEnabled() { + return FLAG_dual_map_code; +} + static void unmap(uword start, uword end) { ASSERT(start <= end); uword size = end - start; diff --git a/runtime/vm/virtual_memory_win.cc b/runtime/vm/virtual_memory_win.cc index 3ec743f04e0..33d4edbc261 100644 --- a/runtime/vm/virtual_memory_win.cc +++ b/runtime/vm/virtual_memory_win.cc @@ -24,6 +24,10 @@ void VirtualMemory::Init() { page_size_ = info.dwPageSize; } +bool VirtualMemory::DualMappingEnabled() { + return false; +} + VirtualMemory* VirtualMemory::AllocateAligned(intptr_t size, intptr_t alignment, bool is_executable, diff --git a/tests/ffi/function_gc_test.dart b/tests/ffi/function_gc_test.dart index 8fa794daac5..d1db3acc2cf 100644 --- a/tests/ffi/function_gc_test.dart +++ b/tests/ffi/function_gc_test.dart @@ -4,6 +4,8 @@ // // VMOptions=--deterministic --optimization-counter-threshold=500 --enable-testing-pragmas // VMOptions=--deterministic --optimization-counter-threshold=-1 --enable-testing-pragmas +// VMOptions=--deterministic --optimization-counter-threshold=500 --enable-testing-pragmas --no-dual-map-code --write-protect-code +// VMOptions=--deterministic --optimization-counter-threshold=-1 --enable-testing-pragmas --no-dual-map-code --write-protect-code // // Dart test program for stress-testing boxing and GC in return paths from FFI // trampolines. @@ -25,6 +27,7 @@ main() async { testBoxPointer(); testAllocateInNative(); testRegress37069(); + testWriteProtection(); } typedef NativeNullaryOp64 = ffi.Int64 Function(); @@ -106,3 +109,11 @@ final regress37069 = ffiTestFunctions void testRegress37069() { regress37069(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11); } + +final unprotectCode = ffiTestFunctions + .lookupFunction("UnprotectCode"); + +void testWriteProtection() { + int result = unprotectCode(); + Expect.equals(result, 0); +} diff --git a/tests/ffi/function_test.dart b/tests/ffi/function_test.dart index f31a470b6e3..68529bdbef6 100644 --- a/tests/ffi/function_test.dart +++ b/tests/ffi/function_test.dart @@ -7,6 +7,8 @@ // VMOptions= // VMOptions=--deterministic --optimization-counter-threshold=10 // VMOptions=--use-slow-path +// VMOptions=--write-protect-code --no-dual-map-code +// VMOptions=--write-protect-code --no-dual-map-code --use-slow-path // SharedObjects=ffi_test_functions library FfiTest;