[vm] Record stack for JIT TSAN.
The main interesting thing is that deopt can expand one optimized frame to multiple unoptimized frames, so deopt needs to call __tsan_func_entry enough times to rebalance the stack. Also fixes stack balance for throw error slow paths for AOT. TEST=tsan Bug: https://github.com/dart-lang/sdk/issues/61352 Change-Id: I3fdc8481bc8db7a3aec0fa1938ac3e0e96ac13a5 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/452943 Commit-Queue: Ryan Macnak <rmacnak@google.com> Reviewed-by: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
committed by
Commit Queue
parent
487e68e163
commit
0a1678c06c
@@ -71,4 +71,6 @@ extern "C" void __tsan_func_exit();
|
||||
#define DO_IF_NOT_TSAN(CODE) CODE
|
||||
#endif
|
||||
|
||||
constexpr uintptr_t kExternalPCBit = 1ULL << 60;
|
||||
|
||||
#endif // RUNTIME_PLATFORM_THREAD_SANITIZER_H_
|
||||
|
||||
@@ -18,7 +18,7 @@ noopt() {}
|
||||
@pragma("vm:never-inline")
|
||||
dataRaceFromMain() {
|
||||
final localBox = box;
|
||||
for (var i = 0; i < 1000000; i++) {
|
||||
for (var i = 0; i < 10000000; i++) {
|
||||
localBox[0] += 1;
|
||||
noopt();
|
||||
}
|
||||
@@ -27,7 +27,7 @@ dataRaceFromMain() {
|
||||
@pragma("vm:never-inline")
|
||||
dataRaceFromChild() {
|
||||
final localBox = box;
|
||||
for (var i = 0; i < 1000000; i++) {
|
||||
for (var i = 0; i < 10000000; i++) {
|
||||
localBox[0] += 1;
|
||||
noopt();
|
||||
}
|
||||
|
||||
@@ -175,6 +175,9 @@ dart/unobfuscated_static_symbols_test: Pass, Slow # Test includes creating sever
|
||||
dart/use_strip_flag_test: Pass, Slow # This test can take a longer time to complete.
|
||||
dart/v8_snapshot_profile_writer_test: SkipSlow
|
||||
|
||||
[ $sanitizer != tsan ]
|
||||
dart/tsan/*: SkipByDesign
|
||||
|
||||
[ $system == android ]
|
||||
dart/isolates/dart_api_create_lightweight_isolate_test: SkipByDesign # On android this test does not work due to not being able to identify library uri.
|
||||
dart/sdk_hash_test: SkipByDesign # The test doesn't know location of cross-platform gen_snapshot
|
||||
@@ -403,9 +406,6 @@ dart/finalizer/finalizer_isolate_groups_run_gc_test: SkipByDesign # uses spawnUr
|
||||
dart/isolates/send_object_to_spawn_uri_isolate_test: SkipByDesign # uses spawnUri
|
||||
dart/issue32950_test: SkipByDesign # uses spawnUri.
|
||||
|
||||
[ $runtime != dart_precompiled || $sanitizer != tsan ]
|
||||
dart/tsan/*: SkipByDesign
|
||||
|
||||
[ $runtime != dart_precompiled || $sanitizer != msan && $sanitizer != tsan ]
|
||||
dart/sanitizer_compatibility_test: SkipByDesign
|
||||
|
||||
|
||||
@@ -354,7 +354,6 @@ void Assembler::TsanStoreRelease(Register src,
|
||||
}
|
||||
|
||||
void Assembler::TsanFuncEntry(bool preserve_registers) {
|
||||
Comment("TsanFuncEntry");
|
||||
LeafRuntimeScope rt(this, /*frame_size=*/0, preserve_registers);
|
||||
ldr(R0, Address(FP, target::frame_layout.saved_caller_fp_from_fp *
|
||||
target::kWordSize));
|
||||
@@ -364,7 +363,6 @@ void Assembler::TsanFuncEntry(bool preserve_registers) {
|
||||
}
|
||||
|
||||
void Assembler::TsanFuncExit(bool preserve_registers) {
|
||||
Comment("TsanFuncExit");
|
||||
LeafRuntimeScope rt(this, /*frame_size=*/0, preserve_registers);
|
||||
rt.Call(kTsanFuncExitRuntimeEntry, /*argument_count=*/0);
|
||||
}
|
||||
@@ -1801,16 +1799,14 @@ void Assembler::CallRuntime(const RuntimeEntry& entry,
|
||||
ASSERT(!entry.is_leaf());
|
||||
// Argument count is not checked here, but in the runtime entry for a more
|
||||
// informative error message.
|
||||
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode &&
|
||||
tsan_enter_exit) {
|
||||
if (FLAG_target_thread_sanitizer && tsan_enter_exit) {
|
||||
TsanFuncEntry(/*preserve_registers=*/false);
|
||||
}
|
||||
ldr(R5, compiler::Address(THR, entry.OffsetFromThread()));
|
||||
LoadImmediate(R4, argument_count);
|
||||
Comment("Runtime call: %s", entry.name());
|
||||
Call(Address(THR, target::Thread::call_to_runtime_entry_point_offset()));
|
||||
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode &&
|
||||
tsan_enter_exit) {
|
||||
if (FLAG_target_thread_sanitizer && tsan_enter_exit) {
|
||||
TsanFuncExit(/*preserve_registers=*/false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3173,7 +3173,6 @@ void Assembler::TsanStoreRelease(Register src,
|
||||
}
|
||||
|
||||
void Assembler::TsanFuncEntry(bool preserve_registers) {
|
||||
Comment("TsanFuncEntry");
|
||||
LeafRuntimeScope rt(this, /*frame_size=*/0, preserve_registers);
|
||||
lx(A0, Address(FP, target::frame_layout.saved_caller_fp_from_fp *
|
||||
target::kWordSize));
|
||||
@@ -3183,7 +3182,6 @@ void Assembler::TsanFuncEntry(bool preserve_registers) {
|
||||
}
|
||||
|
||||
void Assembler::TsanFuncExit(bool preserve_registers) {
|
||||
Comment("TsanFuncExit");
|
||||
LeafRuntimeScope rt(this, /*frame_size=*/0, preserve_registers);
|
||||
rt.Call(kTsanFuncExitRuntimeEntry, /*argument_count=*/0);
|
||||
}
|
||||
@@ -4825,16 +4823,14 @@ void Assembler::CallRuntime(const RuntimeEntry& entry,
|
||||
ASSERT(!entry.is_leaf());
|
||||
// Argument count is not checked here, but in the runtime entry for a more
|
||||
// informative error message.
|
||||
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode &&
|
||||
tsan_enter_exit) {
|
||||
if (FLAG_target_thread_sanitizer && tsan_enter_exit) {
|
||||
TsanFuncEntry(/*preserve_registers=*/false);
|
||||
}
|
||||
lx(T5, compiler::Address(THR, entry.OffsetFromThread()));
|
||||
li(T4, argument_count);
|
||||
Comment("Runtime call: %s", entry.name());
|
||||
Call(Address(THR, target::Thread::call_to_runtime_entry_point_offset()));
|
||||
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode &&
|
||||
tsan_enter_exit) {
|
||||
if (FLAG_target_thread_sanitizer && tsan_enter_exit) {
|
||||
TsanFuncExit(/*preserve_registers=*/false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2063,16 +2063,14 @@ void Assembler::CallRuntime(const RuntimeEntry& entry,
|
||||
ASSERT(!entry.is_leaf());
|
||||
// Argument count is not checked here, but in the runtime entry for a more
|
||||
// informative error message.
|
||||
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode &&
|
||||
tsan_enter_exit) {
|
||||
if (FLAG_target_thread_sanitizer && tsan_enter_exit) {
|
||||
TsanFuncEntry(/*preserve_registers=*/false);
|
||||
}
|
||||
movq(RBX, compiler::Address(THR, entry.OffsetFromThread()));
|
||||
LoadImmediate(R10, compiler::Immediate(argument_count));
|
||||
Comment("Runtime call: %s", entry.name());
|
||||
call(Address(THR, target::Thread::call_to_runtime_entry_point_offset()));
|
||||
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode &&
|
||||
tsan_enter_exit) {
|
||||
if (FLAG_target_thread_sanitizer && tsan_enter_exit) {
|
||||
TsanFuncExit(/*preserve_registers=*/false);
|
||||
}
|
||||
}
|
||||
@@ -2208,7 +2206,6 @@ void Assembler::TsanStoreRelease(Register src, Address addr, OperandSize size) {
|
||||
}
|
||||
|
||||
void Assembler::TsanFuncEntry(bool preserve_registers) {
|
||||
Comment("TsanFuncEntry");
|
||||
LeafRuntimeScope rt(this, /*frame_size=*/0, preserve_registers);
|
||||
movq(CallingConventions::kArg1Reg,
|
||||
Address(RBP, target::frame_layout.saved_caller_fp_from_fp *
|
||||
@@ -2221,7 +2218,6 @@ void Assembler::TsanFuncEntry(bool preserve_registers) {
|
||||
}
|
||||
|
||||
void Assembler::TsanFuncExit(bool preserve_registers) {
|
||||
Comment("TsanFuncExit");
|
||||
LeafRuntimeScope rt(this, /*frame_size=*/0, preserve_registers);
|
||||
rt.Call(kTsanFuncExitRuntimeEntry, /*argument_count=*/0);
|
||||
}
|
||||
|
||||
@@ -2716,16 +2716,19 @@ void FlowGraph::AddTsanInstrumentation() {
|
||||
store->index_scale(), store->class_id(), store->source());
|
||||
InsertBefore(store, tsan_write, /*env=*/nullptr, kEffect);
|
||||
needs_entry_exit = true;
|
||||
} else if (current->CanCallDart() || current->MayThrow()) {
|
||||
} else if (current->CanCallDart() || current->MayThrow() ||
|
||||
current->IsBox() || current->IsBoxLanes()) {
|
||||
// Box instructions enter the runtime on allocation slow path, and might
|
||||
// throw OutOfMemory.
|
||||
needs_entry_exit = true;
|
||||
} else if (current->CanDeoptimize() && !FLAG_precompiled_mode) {
|
||||
// TSAN stack rebalancing at deopt after DLRT_DeoptimizeFillFrame
|
||||
// assumes the optimized code has a frame.
|
||||
needs_entry_exit = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TSAN can't symbolize JIT functions anyway, so don't bother with this
|
||||
// overhead.
|
||||
if (!FLAG_precompiled_mode) return;
|
||||
|
||||
if (!needs_entry_exit) return;
|
||||
|
||||
for (BlockIterator block_it = reverse_postorder_iterator(); !block_it.Done();
|
||||
@@ -2736,16 +2739,14 @@ void FlowGraph::AddTsanInstrumentation() {
|
||||
TsanFuncEntryExitInstr::kEntry, block->source());
|
||||
InsertAfter(block, tsan_entry, /*env=*/nullptr, kEffect);
|
||||
}
|
||||
for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) {
|
||||
Instruction* current = it.Current();
|
||||
if ((current->IsDartReturn() &&
|
||||
!parsed_function_.function().IsAsyncFunction() &&
|
||||
!parsed_function_.function().IsAsyncGenerator()) ||
|
||||
current->IsTailCall()) {
|
||||
auto* tsan_exit = new (Z) TsanFuncEntryExitInstr(
|
||||
TsanFuncEntryExitInstr::kExit, current->source());
|
||||
InsertBefore(current, tsan_exit, /*env=*/nullptr, kEffect);
|
||||
}
|
||||
Instruction* last = block->last_instruction();
|
||||
if ((last->IsDartReturn() &&
|
||||
!parsed_function_.function().IsAsyncFunction() &&
|
||||
!parsed_function_.function().IsAsyncGenerator()) ||
|
||||
last->IsTailCall() || last->IsNativeReturn()) {
|
||||
auto* tsan_exit = new (Z)
|
||||
TsanFuncEntryExitInstr(TsanFuncEntryExitInstr::kExit, last->source());
|
||||
InsertBefore(last, tsan_exit, /*env=*/nullptr, kEffect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3087,7 +3087,6 @@ void ThrowErrorSlowPathCode::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
EmitCodeAtSlowPathEntry(compiler);
|
||||
LocationSummary* locs = instruction()->locs();
|
||||
const bool has_frame = compiler->flow_graph().graph_entry()->NeedsFrame();
|
||||
bool need_tsan_exit = false;
|
||||
if (use_shared_stub) {
|
||||
if (!has_frame) {
|
||||
#if !defined(TARGET_ARCH_IA32)
|
||||
@@ -3095,7 +3094,7 @@ void ThrowErrorSlowPathCode::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
__ set_constant_pool_allowed(false);
|
||||
#endif
|
||||
__ EnterDartFrame(0);
|
||||
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
|
||||
if (FLAG_target_thread_sanitizer) {
|
||||
__ TsanFuncEntry();
|
||||
}
|
||||
}
|
||||
@@ -3111,14 +3110,9 @@ void ThrowErrorSlowPathCode::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
// Save registers as they are needed for lazy deopt / exception handling.
|
||||
compiler->SaveLiveRegisters(locs);
|
||||
PushArgumentsForRuntimeCall(compiler);
|
||||
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
|
||||
need_tsan_exit = true;
|
||||
__ TsanFuncEntry(/*preserve_registers=*/false);
|
||||
}
|
||||
// We need to emit TsanFuncEntry/Exit separately so the pc descriptors, etc
|
||||
// are recordered for the call's return address.
|
||||
bool tsan_enter_exit = false;
|
||||
__ CallRuntime(runtime_entry_, num_args, tsan_enter_exit);
|
||||
__ CallRuntime(runtime_entry_, num_args, /*tsan_enter_exit=*/false);
|
||||
}
|
||||
const intptr_t deopt_id = instruction()->deopt_id();
|
||||
compiler->AddCurrentDescriptor(UntaggedPcDescriptors::kOther, deopt_id,
|
||||
@@ -3145,8 +3139,6 @@ void ThrowErrorSlowPathCode::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
}
|
||||
if (!use_shared_stub) {
|
||||
__ Breakpoint();
|
||||
} else if (need_tsan_exit) {
|
||||
__ TsanFuncExit(/*preserve_registers=*/false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -258,6 +258,16 @@ void FlowGraphCompiler::EmitFrameEntry() {
|
||||
} else if (FLAG_precompiled_mode) {
|
||||
assembler()->set_constant_pool_allowed(true);
|
||||
}
|
||||
if (FLAG_target_thread_sanitizer && !is_optimizing()) {
|
||||
bool uses_args_desc = parsed_function().has_arg_desc_var();
|
||||
if (uses_args_desc) {
|
||||
__ MoveRegister(CALLEE_SAVED_TEMP, ARGS_DESC_REG);
|
||||
}
|
||||
__ TsanFuncEntry(/*preserve_registers=*/false);
|
||||
if (uses_args_desc) {
|
||||
__ MoveRegister(ARGS_DESC_REG, CALLEE_SAVED_TEMP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const InstructionSource& PrologueSource() {
|
||||
@@ -336,6 +346,9 @@ void FlowGraphCompiler::EmitTailCallToStub(const Code& stub) {
|
||||
ASSERT(!stub.IsNull());
|
||||
if (CanPcRelativeCall(stub)) {
|
||||
if (flow_graph().graph_entry()->NeedsFrame()) {
|
||||
if (FLAG_target_thread_sanitizer && !is_optimizing()) {
|
||||
__ TsanFuncExit();
|
||||
}
|
||||
__ LeaveDartFrame();
|
||||
}
|
||||
__ GenerateUnRelocatedPcRelativeTailCall();
|
||||
@@ -346,6 +359,9 @@ void FlowGraphCompiler::EmitTailCallToStub(const Code& stub) {
|
||||
} else {
|
||||
__ LoadObject(CODE_REG, stub);
|
||||
if (flow_graph().graph_entry()->NeedsFrame()) {
|
||||
if (FLAG_target_thread_sanitizer && !is_optimizing()) {
|
||||
__ TsanFuncExit();
|
||||
}
|
||||
__ LeaveDartFrame();
|
||||
}
|
||||
__ ldr(TMP, compiler::FieldAddress(
|
||||
|
||||
@@ -248,6 +248,16 @@ void FlowGraphCompiler::EmitFrameEntry() {
|
||||
} else if (FLAG_precompiled_mode) {
|
||||
assembler()->set_constant_pool_allowed(true);
|
||||
}
|
||||
if (FLAG_target_thread_sanitizer && !is_optimizing()) {
|
||||
bool uses_args_desc = parsed_function().has_arg_desc_var();
|
||||
if (uses_args_desc) {
|
||||
__ MoveRegister(CALLEE_SAVED_TEMP, ARGS_DESC_REG);
|
||||
}
|
||||
__ TsanFuncEntry(/*preserve_registers=*/false);
|
||||
if (uses_args_desc) {
|
||||
__ MoveRegister(ARGS_DESC_REG, CALLEE_SAVED_TEMP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const InstructionSource& PrologueSource() {
|
||||
@@ -332,6 +342,9 @@ void FlowGraphCompiler::EmitTailCallToStub(const Code& stub) {
|
||||
ASSERT(!stub.IsNull());
|
||||
if (CanPcRelativeCall(stub)) {
|
||||
if (flow_graph().graph_entry()->NeedsFrame()) {
|
||||
if (FLAG_target_thread_sanitizer && !is_optimizing()) {
|
||||
__ TsanFuncExit();
|
||||
}
|
||||
__ LeaveDartFrame();
|
||||
}
|
||||
__ GenerateUnRelocatedPcRelativeTailCall();
|
||||
@@ -342,6 +355,9 @@ void FlowGraphCompiler::EmitTailCallToStub(const Code& stub) {
|
||||
} else {
|
||||
__ LoadObject(CODE_REG, stub);
|
||||
if (flow_graph().graph_entry()->NeedsFrame()) {
|
||||
if (FLAG_target_thread_sanitizer && !is_optimizing()) {
|
||||
__ TsanFuncExit();
|
||||
}
|
||||
__ LeaveDartFrame();
|
||||
}
|
||||
__ lx(TMP, compiler::FieldAddress(
|
||||
|
||||
@@ -261,6 +261,16 @@ void FlowGraphCompiler::EmitFrameEntry() {
|
||||
__ Comment("Enter frame");
|
||||
__ EnterDartFrame(StackSize() * kWordSize);
|
||||
}
|
||||
if (FLAG_target_thread_sanitizer && !is_optimizing()) {
|
||||
bool uses_args_desc = parsed_function().has_arg_desc_var();
|
||||
if (uses_args_desc) {
|
||||
__ MoveRegister(CALLEE_SAVED_TEMP, ARGS_DESC_REG);
|
||||
}
|
||||
__ TsanFuncEntry(/*preserve_registers=*/false);
|
||||
if (uses_args_desc) {
|
||||
__ MoveRegister(ARGS_DESC_REG, CALLEE_SAVED_TEMP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const InstructionSource& PrologueSource() {
|
||||
@@ -340,6 +350,9 @@ void FlowGraphCompiler::EmitTailCallToStub(const Code& stub) {
|
||||
ASSERT(!stub.IsNull());
|
||||
if (CanPcRelativeCall(stub)) {
|
||||
if (flow_graph().graph_entry()->NeedsFrame()) {
|
||||
if (FLAG_target_thread_sanitizer && !is_optimizing()) {
|
||||
__ TsanFuncExit();
|
||||
}
|
||||
__ LeaveDartFrame();
|
||||
}
|
||||
__ GenerateUnRelocatedPcRelativeTailCall();
|
||||
@@ -350,6 +363,9 @@ void FlowGraphCompiler::EmitTailCallToStub(const Code& stub) {
|
||||
} else {
|
||||
__ LoadObject(CODE_REG, stub);
|
||||
if (flow_graph().graph_entry()->NeedsFrame()) {
|
||||
if (FLAG_target_thread_sanitizer && !is_optimizing()) {
|
||||
__ TsanFuncExit();
|
||||
}
|
||||
__ LeaveDartFrame();
|
||||
}
|
||||
__ jmp(compiler::FieldAddress(
|
||||
|
||||
@@ -540,6 +540,12 @@ void DartReturnInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (FLAG_target_thread_sanitizer && !compiler->is_optimizing()) {
|
||||
RELEASE_ASSERT(locs()->in(0).IsRegister());
|
||||
__ MoveRegister(CALLEE_SAVED_TEMP, locs()->in(0).reg());
|
||||
__ TsanFuncExit(/*preserve_registers=*/false);
|
||||
__ MoveRegister(locs()->in(0).reg(), CALLEE_SAVED_TEMP);
|
||||
}
|
||||
#if defined(DEBUG)
|
||||
compiler::Label stack_ok;
|
||||
__ Comment("Stack Check");
|
||||
@@ -2393,7 +2399,7 @@ void GuardFieldClassInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
|
||||
__ PushPair(value_reg, field_reg);
|
||||
ASSERT(!compiler->is_optimizing()); // No deopt info needed.
|
||||
__ CallRuntime(kUpdateFieldCidRuntimeEntry, 2);
|
||||
__ CallRuntime(kUpdateFieldCidRuntimeEntry, 2, /*tsan_enter_exit=*/false);
|
||||
__ Drop(2); // Drop the field and the value.
|
||||
} else {
|
||||
__ b(fail);
|
||||
@@ -2498,7 +2504,7 @@ void GuardFieldLengthInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
|
||||
__ PushPair(value_reg, field_reg);
|
||||
ASSERT(!compiler->is_optimizing()); // No deopt info needed.
|
||||
__ CallRuntime(kUpdateFieldCidRuntimeEntry, 2);
|
||||
__ CallRuntime(kUpdateFieldCidRuntimeEntry, 2, /*tsan_enter_exit=*/false);
|
||||
__ Drop(2); // Drop the field and the value.
|
||||
} else {
|
||||
__ b(deopt, NE);
|
||||
@@ -2928,7 +2934,7 @@ class CheckStackOverflowSlowPath
|
||||
ASSERT(__ constant_pool_allowed());
|
||||
__ set_constant_pool_allowed(false);
|
||||
__ EnterDartFrame(0);
|
||||
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
|
||||
if (FLAG_target_thread_sanitizer) {
|
||||
__ TsanFuncEntry();
|
||||
}
|
||||
}
|
||||
@@ -2955,7 +2961,7 @@ class CheckStackOverflowSlowPath
|
||||
instruction()->deopt_id(),
|
||||
instruction()->source());
|
||||
if (!has_frame) {
|
||||
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
|
||||
if (FLAG_target_thread_sanitizer) {
|
||||
__ TsanFuncExit();
|
||||
}
|
||||
__ LeaveDartFrame();
|
||||
@@ -3788,7 +3794,7 @@ void BoxInt64Instr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
ASSERT(__ constant_pool_allowed());
|
||||
__ set_constant_pool_allowed(false);
|
||||
__ EnterDartFrame(0);
|
||||
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
|
||||
if (FLAG_target_thread_sanitizer) {
|
||||
__ TsanFuncEntry();
|
||||
}
|
||||
}
|
||||
@@ -3805,7 +3811,7 @@ void BoxInt64Instr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
compiler->GenerateStubCall(source(), stub, UntaggedPcDescriptors::kOther,
|
||||
locs(), DeoptId::kNone, extended_env);
|
||||
if (!has_frame) {
|
||||
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
|
||||
if (FLAG_target_thread_sanitizer) {
|
||||
__ TsanFuncExit();
|
||||
}
|
||||
__ LeaveDartFrame();
|
||||
|
||||
@@ -601,6 +601,12 @@ void DartReturnInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (FLAG_target_thread_sanitizer && !compiler->is_optimizing()) {
|
||||
RELEASE_ASSERT(locs()->in(0).IsRegister());
|
||||
__ MoveRegister(CALLEE_SAVED_TEMP, locs()->in(0).reg());
|
||||
__ TsanFuncExit(/*preserve_registers=*/false);
|
||||
__ MoveRegister(locs()->in(0).reg(), CALLEE_SAVED_TEMP);
|
||||
}
|
||||
const intptr_t fp_sp_dist =
|
||||
(compiler::target::frame_layout.first_local_from_fp + 1 -
|
||||
compiler->StackSize()) *
|
||||
@@ -2592,7 +2598,7 @@ void GuardFieldClassInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
|
||||
__ PushRegisterPair(value_reg, field_reg);
|
||||
ASSERT(!compiler->is_optimizing()); // No deopt info needed.
|
||||
__ CallRuntime(kUpdateFieldCidRuntimeEntry, 2);
|
||||
__ CallRuntime(kUpdateFieldCidRuntimeEntry, 2, /*tsan_enter_exit=*/false);
|
||||
__ Drop(2); // Drop the field and the value.
|
||||
} else {
|
||||
__ j(fail);
|
||||
@@ -2696,7 +2702,7 @@ void GuardFieldLengthInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
|
||||
__ PushRegisterPair(value_reg, field_reg);
|
||||
ASSERT(!compiler->is_optimizing()); // No deopt info needed.
|
||||
__ CallRuntime(kUpdateFieldCidRuntimeEntry, 2);
|
||||
__ CallRuntime(kUpdateFieldCidRuntimeEntry, 2, /*tsan_enter_exit=*/false);
|
||||
__ Drop(2); // Drop the field and the value.
|
||||
} else {
|
||||
__ BranchIf(NE, deopt);
|
||||
@@ -3124,7 +3130,7 @@ class CheckStackOverflowSlowPath
|
||||
ASSERT(__ constant_pool_allowed());
|
||||
__ set_constant_pool_allowed(false);
|
||||
__ EnterDartFrame(0);
|
||||
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
|
||||
if (FLAG_target_thread_sanitizer) {
|
||||
__ TsanFuncEntry();
|
||||
}
|
||||
}
|
||||
@@ -3151,7 +3157,7 @@ class CheckStackOverflowSlowPath
|
||||
instruction()->deopt_id(),
|
||||
instruction()->source());
|
||||
if (!has_frame) {
|
||||
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
|
||||
if (FLAG_target_thread_sanitizer) {
|
||||
__ TsanFuncExit();
|
||||
}
|
||||
__ LeaveDartFrame();
|
||||
@@ -4155,7 +4161,7 @@ void BoxInt64Instr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
ASSERT(__ constant_pool_allowed());
|
||||
__ set_constant_pool_allowed(false);
|
||||
__ EnterDartFrame(0);
|
||||
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
|
||||
if (FLAG_target_thread_sanitizer) {
|
||||
__ TsanFuncEntry();
|
||||
}
|
||||
}
|
||||
@@ -4172,7 +4178,7 @@ void BoxInt64Instr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
compiler->GenerateStubCall(source(), stub, UntaggedPcDescriptors::kOther,
|
||||
locs(), DeoptId::kNone, extended_env);
|
||||
if (!has_frame) {
|
||||
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
|
||||
if (FLAG_target_thread_sanitizer) {
|
||||
__ TsanFuncExit();
|
||||
}
|
||||
__ LeaveDartFrame();
|
||||
|
||||
@@ -442,6 +442,12 @@ void DartReturnInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (FLAG_target_thread_sanitizer && !compiler->is_optimizing()) {
|
||||
RELEASE_ASSERT(locs()->in(0).IsRegister());
|
||||
__ MoveRegister(CALLEE_SAVED_TEMP, locs()->in(0).reg());
|
||||
__ TsanFuncExit(/*preserve_registers=*/false);
|
||||
__ MoveRegister(locs()->in(0).reg(), CALLEE_SAVED_TEMP);
|
||||
}
|
||||
#if defined(DEBUG)
|
||||
__ Comment("Stack Check");
|
||||
compiler::Label done;
|
||||
@@ -2267,7 +2273,7 @@ void GuardFieldClassInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
__ pushq(field_reg);
|
||||
__ pushq(value_reg);
|
||||
ASSERT(!compiler->is_optimizing()); // No deopt info needed.
|
||||
__ CallRuntime(kUpdateFieldCidRuntimeEntry, 2);
|
||||
__ CallRuntime(kUpdateFieldCidRuntimeEntry, 2, /*tsan_enter_exit=*/false);
|
||||
__ Drop(2); // Drop the field and the value.
|
||||
} else {
|
||||
__ jmp(fail);
|
||||
@@ -2373,7 +2379,7 @@ void GuardFieldLengthInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
__ pushq(field_reg);
|
||||
__ pushq(value_reg);
|
||||
ASSERT(!compiler->is_optimizing()); // No deopt info needed.
|
||||
__ CallRuntime(kUpdateFieldCidRuntimeEntry, 2);
|
||||
__ CallRuntime(kUpdateFieldCidRuntimeEntry, 2, /*tsan_enter_exit=*/false);
|
||||
__ Drop(2); // Drop the field and the value.
|
||||
} else {
|
||||
__ j(NOT_EQUAL, deopt);
|
||||
@@ -2471,7 +2477,7 @@ void GuardFieldTypeInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
__ PushObject(original);
|
||||
__ pushq(value_reg);
|
||||
ASSERT(!compiler->is_optimizing()); // No deopt info needed.
|
||||
__ CallRuntime(kUpdateFieldCidRuntimeEntry, 2);
|
||||
__ CallRuntime(kUpdateFieldCidRuntimeEntry, 2, /*tsan_enter_exit=*/false);
|
||||
__ Drop(2);
|
||||
}
|
||||
|
||||
@@ -2893,7 +2899,7 @@ class CheckStackOverflowSlowPath
|
||||
ASSERT(__ constant_pool_allowed());
|
||||
__ set_constant_pool_allowed(false);
|
||||
__ EnterDartFrame(0);
|
||||
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
|
||||
if (FLAG_target_thread_sanitizer) {
|
||||
__ TsanFuncEntry();
|
||||
}
|
||||
}
|
||||
@@ -2907,7 +2913,7 @@ class CheckStackOverflowSlowPath
|
||||
instruction()->deopt_id(),
|
||||
instruction()->source());
|
||||
if (!has_frame) {
|
||||
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
|
||||
if (FLAG_target_thread_sanitizer) {
|
||||
__ TsanFuncExit();
|
||||
}
|
||||
__ LeaveDartFrame();
|
||||
@@ -4039,7 +4045,7 @@ void BoxInt64Instr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
ASSERT(__ constant_pool_allowed());
|
||||
__ set_constant_pool_allowed(false);
|
||||
__ EnterDartFrame(0);
|
||||
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
|
||||
if (FLAG_target_thread_sanitizer) {
|
||||
__ TsanFuncEntry();
|
||||
}
|
||||
}
|
||||
@@ -4056,7 +4062,7 @@ void BoxInt64Instr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
compiler->GenerateStubCall(source(), stub, UntaggedPcDescriptors::kOther,
|
||||
locs(), DeoptId::kNone, extended_env);
|
||||
if (!has_frame) {
|
||||
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
|
||||
if (FLAG_target_thread_sanitizer) {
|
||||
__ TsanFuncExit();
|
||||
}
|
||||
__ LeaveDartFrame();
|
||||
|
||||
@@ -123,11 +123,11 @@ void StubCodeCompiler::GenerateInitLateStaticFieldStub(bool is_final,
|
||||
// Load a GC-safe value for the arguments descriptor (unused but tagged).
|
||||
__ LoadImmediate(ARGS_DESC_REG, 0);
|
||||
}
|
||||
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
|
||||
if (FLAG_target_thread_sanitizer) {
|
||||
__ TsanFuncEntry();
|
||||
}
|
||||
__ Call(FieldAddress(FUNCTION_REG, target::Function::entry_point_offset()));
|
||||
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
|
||||
if (FLAG_target_thread_sanitizer) {
|
||||
__ TsanFuncExit();
|
||||
}
|
||||
__ MoveRegister(kResultReg, CallingConventions::kReturnReg);
|
||||
@@ -239,11 +239,11 @@ void StubCodeCompiler::GenerateInitLateInstanceFieldStub(bool is_final) {
|
||||
__ LoadImmediate(ARGS_DESC_REG, 0);
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
}
|
||||
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
|
||||
if (FLAG_target_thread_sanitizer) {
|
||||
__ TsanFuncEntry();
|
||||
}
|
||||
__ Call(FieldAddress(FUNCTION_REG, target::Function::entry_point_offset()));
|
||||
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
|
||||
if (FLAG_target_thread_sanitizer) {
|
||||
__ TsanFuncExit();
|
||||
}
|
||||
__ Drop(1); // Drop argument.
|
||||
@@ -1880,14 +1880,11 @@ static void CallDartCoreLibraryFunction(
|
||||
intptr_t entry_point_offset_in_thread,
|
||||
intptr_t function_offset_in_object_store,
|
||||
bool uses_args_desc = false) {
|
||||
if (FLAG_target_thread_sanitizer) {
|
||||
__ TsanFuncEntry();
|
||||
}
|
||||
if (FLAG_precompiled_mode) {
|
||||
if (FLAG_target_thread_sanitizer) {
|
||||
__ TsanFuncEntry();
|
||||
}
|
||||
__ Call(Address(THR, entry_point_offset_in_thread));
|
||||
if (FLAG_target_thread_sanitizer) {
|
||||
__ TsanFuncExit();
|
||||
}
|
||||
} else {
|
||||
__ LoadIsolateGroup(FUNCTION_REG);
|
||||
__ LoadFromOffset(FUNCTION_REG, FUNCTION_REG,
|
||||
@@ -1902,6 +1899,9 @@ static void CallDartCoreLibraryFunction(
|
||||
}
|
||||
__ Call(FieldAddress(FUNCTION_REG, target::Function::entry_point_offset()));
|
||||
}
|
||||
if (FLAG_target_thread_sanitizer) {
|
||||
__ TsanFuncExit();
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to generate allocation of _SuspendState instance.
|
||||
@@ -2161,7 +2161,7 @@ void StubCodeCompiler::GenerateSuspendStub(
|
||||
|
||||
__ LeaveStubFrame();
|
||||
|
||||
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
|
||||
if (FLAG_target_thread_sanitizer) {
|
||||
__ TsanFuncExit();
|
||||
}
|
||||
#if !defined(TARGET_ARCH_X64) && !defined(TARGET_ARCH_IA32)
|
||||
@@ -2339,7 +2339,7 @@ void StubCodeCompiler::GenerateResumeStub() {
|
||||
// ... [SuspendState] [value] [exception] [stackTrace] [ReturnAddress]
|
||||
|
||||
__ EnterDartFrame(0);
|
||||
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
|
||||
if (FLAG_target_thread_sanitizer) {
|
||||
__ TsanFuncEntry();
|
||||
}
|
||||
|
||||
@@ -2554,7 +2554,7 @@ void StubCodeCompiler::GenerateReturnStub(
|
||||
__ Bind(&okay);
|
||||
}
|
||||
#endif
|
||||
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
|
||||
if (FLAG_target_thread_sanitizer) {
|
||||
__ TsanFuncExit();
|
||||
}
|
||||
__ LeaveDartFrame();
|
||||
@@ -2609,7 +2609,7 @@ void StubCodeCompiler::GenerateAsyncExceptionHandlerStub() {
|
||||
__ CompareObject(kSuspendState, NullObject());
|
||||
__ BranchIf(EQUAL, &rethrow_exception);
|
||||
|
||||
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
|
||||
if (FLAG_target_thread_sanitizer) {
|
||||
__ TsanFuncExit();
|
||||
}
|
||||
__ LeaveDartFrame();
|
||||
@@ -2633,7 +2633,7 @@ void StubCodeCompiler::GenerateAsyncExceptionHandlerStub() {
|
||||
#endif
|
||||
__ Comment("Rethrow exception");
|
||||
__ Bind(&rethrow_exception);
|
||||
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
|
||||
if (FLAG_target_thread_sanitizer) {
|
||||
__ TsanFuncExit();
|
||||
}
|
||||
__ LeaveDartFrame();
|
||||
|
||||
@@ -1194,6 +1194,16 @@ static void GenerateDeoptimizationSequence(Assembler* assembler,
|
||||
/*preserve_registers=*/false);
|
||||
rt.Call(kDeoptimizeFillFrameRuntimeEntry, 1);
|
||||
}
|
||||
if (FLAG_target_thread_sanitizer) {
|
||||
__ mov(CALLEE_SAVED_TEMP, R0); // Frames created
|
||||
__ TsanFuncExit(/*preserve_registers=*/false); // Optimized frame.
|
||||
Label loop;
|
||||
__ Bind(&loop);
|
||||
__ LoadImmediate(R0, 42);
|
||||
__ TsanFuncEntry(/*preserve_registers=*/false); // Unoptimized frames.
|
||||
__ sub(CALLEE_SAVED_TEMP, CALLEE_SAVED_TEMP, Operand(1));
|
||||
__ cbnz(&loop, CALLEE_SAVED_TEMP);
|
||||
}
|
||||
if (kind == kLazyDeoptFromReturn) {
|
||||
// Restore result into R1.
|
||||
__ LoadFromOffset(
|
||||
@@ -3464,7 +3474,7 @@ void StubCodeCompiler::GenerateJumpToFrameStub() {
|
||||
ASSERT(kStackTraceObjectReg == R1);
|
||||
__ set_lr_state(compiler::LRState::Clobbered());
|
||||
__ mov(THR, R3);
|
||||
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
|
||||
if (FLAG_target_thread_sanitizer) {
|
||||
Label again, done;
|
||||
__ mov(SP, CSP);
|
||||
__ ldr(CALLEE_SAVED_TEMP,
|
||||
|
||||
@@ -1038,6 +1038,16 @@ static void GenerateDeoptimizationSequence(Assembler* assembler,
|
||||
/*preserve_registers=*/false);
|
||||
rt.Call(kDeoptimizeFillFrameRuntimeEntry, 1);
|
||||
}
|
||||
if (FLAG_target_thread_sanitizer) {
|
||||
__ mv(CALLEE_SAVED_TEMP, A0); // Frames created
|
||||
__ TsanFuncExit(/*preserve_registers=*/false); // Optimized frame.
|
||||
Label loop;
|
||||
__ Bind(&loop);
|
||||
__ li(A0, 42);
|
||||
__ TsanFuncEntry(/*preserve_registers=*/false); // Unoptimized frames.
|
||||
__ subi(CALLEE_SAVED_TEMP, CALLEE_SAVED_TEMP, 1);
|
||||
__ bnez(CALLEE_SAVED_TEMP, &loop);
|
||||
}
|
||||
if (kind == kLazyDeoptFromReturn) {
|
||||
// Restore result into T1.
|
||||
__ LoadFromOffset(
|
||||
@@ -2962,7 +2972,7 @@ void StubCodeCompiler::GenerateJumpToFrameStub() {
|
||||
ASSERT(kExceptionObjectReg == A0);
|
||||
ASSERT(kStackTraceObjectReg == A1);
|
||||
__ mv(THR, A3);
|
||||
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
|
||||
if (FLAG_target_thread_sanitizer) {
|
||||
Label again, done;
|
||||
__ lx(CALLEE_SAVED_TEMP,
|
||||
Address(THR, target::Thread::top_exit_frame_info_offset()));
|
||||
|
||||
@@ -1190,6 +1190,16 @@ static void GenerateDeoptimizationSequence(Assembler* assembler,
|
||||
/*preserve_registers=*/false);
|
||||
rt.Call(kDeoptimizeFillFrameRuntimeEntry, 1);
|
||||
}
|
||||
if (FLAG_target_thread_sanitizer) {
|
||||
__ movq(CALLEE_SAVED_TEMP, RAX); // Frames created
|
||||
__ TsanFuncExit(/*preserve_registers=*/false); // Optimized frame.
|
||||
Label loop;
|
||||
__ Bind(&loop);
|
||||
__ movq(CallingConventions::kArg1Reg, Immediate(42));
|
||||
__ TsanFuncEntry(/*preserve_registers=*/false); // Unoptimized frames.
|
||||
__ subq(CALLEE_SAVED_TEMP, Immediate(1));
|
||||
__ j(NOT_ZERO, &loop);
|
||||
}
|
||||
if (kind == kLazyDeoptFromReturn) {
|
||||
// Restore result into RBX.
|
||||
__ movq(RBX, Address(RBP, target::frame_layout.first_local_from_fp *
|
||||
@@ -3443,7 +3453,7 @@ void StubCodeCompiler::GenerateGetCStackPointerStub() {
|
||||
// No Result.
|
||||
void StubCodeCompiler::GenerateJumpToFrameStub() {
|
||||
__ movq(THR, CallingConventions::kArg4Reg);
|
||||
if (FLAG_target_thread_sanitizer && FLAG_precompiled_mode) {
|
||||
if (FLAG_target_thread_sanitizer) {
|
||||
Label again, done;
|
||||
__ movq(CALLEE_SAVED_TEMP,
|
||||
Address(THR, target::Thread::top_exit_frame_info_offset()));
|
||||
|
||||
@@ -255,7 +255,7 @@ static bool IsObjectInstruction(DeoptInstr::Kind kind) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void DeoptContext::FillDestFrame() {
|
||||
intptr_t DeoptContext::FillDestFrame() {
|
||||
const Code& code = Code::Handle(code_);
|
||||
const TypedData& deopt_info = TypedData::Handle(deopt_info_);
|
||||
|
||||
@@ -294,6 +294,7 @@ void DeoptContext::FillDestFrame() {
|
||||
}
|
||||
|
||||
// Populate stack frames.
|
||||
intptr_t frame_count = 0;
|
||||
for (intptr_t to_index = frame_size - 1, from_index = len - 1; to_index >= 0;
|
||||
to_index--, from_index--) {
|
||||
intptr_t* to_addr = GetDestFrameAddressAt(to_index);
|
||||
@@ -303,6 +304,9 @@ void DeoptContext::FillDestFrame() {
|
||||
} else {
|
||||
*reinterpret_cast<ObjectPtr*>(to_addr) = Object::null();
|
||||
}
|
||||
if (instr->kind() == DeoptInstr::kRetAddress) {
|
||||
frame_count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (FLAG_trace_deoptimization_verbose) {
|
||||
@@ -312,6 +316,8 @@ void DeoptContext::FillDestFrame() {
|
||||
deopt_instructions[i + (len - frame_size)]->ToCString());
|
||||
}
|
||||
}
|
||||
|
||||
return frame_count;
|
||||
}
|
||||
|
||||
static void FillDeferredSlots(DeoptContext* deopt_context,
|
||||
|
||||
@@ -144,8 +144,8 @@ class DeoptContext : public MallocAllocated {
|
||||
TypedDataPtr deopt_info() const { return deopt_info_; }
|
||||
|
||||
// Fills the destination frame but defers materialization of
|
||||
// objects.
|
||||
void FillDestFrame();
|
||||
// objects. Returns the number of unoptimized frames.
|
||||
intptr_t FillDestFrame();
|
||||
|
||||
// Materializes all deferred objects. Returns the total number of
|
||||
// artificial arguments used during deoptimization.
|
||||
|
||||
@@ -7975,6 +7975,9 @@ FutureOr<T?> bar<T>() { return null; }
|
||||
|
||||
static void GenerateInvokeInstantiateTAVStub(compiler::Assembler* assembler) {
|
||||
__ EnterDartFrame(0);
|
||||
if (FLAG_target_thread_sanitizer) {
|
||||
__ TsanFuncEntry();
|
||||
}
|
||||
|
||||
// Load the arguments into the right stub calling convention registers.
|
||||
const intptr_t uninstantiated_offset =
|
||||
@@ -7996,6 +7999,9 @@ static void GenerateInvokeInstantiateTAVStub(compiler::Assembler* assembler) {
|
||||
// Set the return from the stub.
|
||||
__ MoveRegister(CallingConventions::kReturnReg,
|
||||
InstantiationABI::kResultTypeArgumentsReg);
|
||||
if (FLAG_target_thread_sanitizer) {
|
||||
__ TsanFuncExit();
|
||||
}
|
||||
__ LeaveDartFrame();
|
||||
__ Ret();
|
||||
}
|
||||
|
||||
+27
-16
@@ -4474,7 +4474,7 @@ DEFINE_LEAF_RUNTIME_ENTRY(DeoptimizeCopyFrame, 2, DLRT_DeoptimizeCopyFrame);
|
||||
|
||||
// The stack has been adjusted to fit all values for unoptimized frame.
|
||||
// Fill the unoptimized frame.
|
||||
extern "C" void DLRT_DeoptimizeFillFrame(uword last_fp) {
|
||||
extern "C" intptr_t DLRT_DeoptimizeFillFrame(uword last_fp) {
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
Thread* thread = Thread::Current();
|
||||
StackZone zone(thread);
|
||||
@@ -4504,10 +4504,15 @@ extern "C" void DLRT_DeoptimizeFillFrame(uword last_fp) {
|
||||
#endif
|
||||
|
||||
deopt_context->set_dest_frame(caller_frame);
|
||||
deopt_context->FillDestFrame();
|
||||
|
||||
intptr_t frame_count = deopt_context->FillDestFrame();
|
||||
ASSERT(frame_count > 0);
|
||||
if (FLAG_trace_deoptimization) {
|
||||
THR_Print("Deopt created %" Pd " frames\n", frame_count);
|
||||
}
|
||||
return frame_count;
|
||||
#else
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
#endif // !DART_PRECOMPILED_RUNTIME
|
||||
}
|
||||
DEFINE_LEAF_RUNTIME_ENTRY(DeoptimizeFillFrame, 1, DLRT_DeoptimizeFillFrame);
|
||||
@@ -5301,11 +5306,11 @@ extern "C" void __tsan_func_exit() {
|
||||
#else
|
||||
#define CASE(x) \
|
||||
extern "C" NO_SANITIZE_THREAD DISABLE_SANITIZER_INSTRUMENTATION void \
|
||||
jit_tsan_##x(void* addr) { \
|
||||
dart_tsan_##x(void* addr) { \
|
||||
__tsan_##x##_pc( \
|
||||
addr, reinterpret_cast<void*>( \
|
||||
reinterpret_cast<uintptr_t>(__builtin_return_address(0)) | \
|
||||
(1ULL << 60))); \
|
||||
kExternalPCBit)); \
|
||||
}
|
||||
|
||||
CASE(read1)
|
||||
@@ -5319,6 +5324,11 @@ CASE(write4)
|
||||
CASE(write8)
|
||||
CASE(write16)
|
||||
#undef CASE
|
||||
extern "C" NO_SANITIZE_THREAD DISABLE_SANITIZER_INSTRUMENTATION void
|
||||
dart_tsan_func_entry(void* pc) {
|
||||
__tsan_func_entry(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(pc) |
|
||||
kExternalPCBit));
|
||||
}
|
||||
#endif
|
||||
|
||||
// These runtime entries are defined even when not using MSAN / TSAN to keep
|
||||
@@ -5330,16 +5340,17 @@ DEFINE_LEAF_RUNTIME_ENTRY(TsanAtomic32Store, 3, __tsan_atomic32_store);
|
||||
DEFINE_LEAF_RUNTIME_ENTRY(TsanAtomic64Load, 2, __tsan_atomic64_load);
|
||||
DEFINE_LEAF_RUNTIME_ENTRY(TsanAtomic64Store, 3, __tsan_atomic64_store);
|
||||
#if defined(USING_THREAD_SANITIZER) && !defined(DART_PRECOMPILED_RUNTIME)
|
||||
DEFINE_LEAF_RUNTIME_ENTRY(TsanRead1, 1, jit_tsan_read1);
|
||||
DEFINE_LEAF_RUNTIME_ENTRY(TsanRead2, 1, jit_tsan_read2);
|
||||
DEFINE_LEAF_RUNTIME_ENTRY(TsanRead4, 1, jit_tsan_read4);
|
||||
DEFINE_LEAF_RUNTIME_ENTRY(TsanRead8, 1, jit_tsan_read8);
|
||||
DEFINE_LEAF_RUNTIME_ENTRY(TsanRead16, 1, jit_tsan_read16);
|
||||
DEFINE_LEAF_RUNTIME_ENTRY(TsanWrite1, 1, jit_tsan_write1);
|
||||
DEFINE_LEAF_RUNTIME_ENTRY(TsanWrite2, 1, jit_tsan_write2);
|
||||
DEFINE_LEAF_RUNTIME_ENTRY(TsanWrite4, 1, jit_tsan_write4);
|
||||
DEFINE_LEAF_RUNTIME_ENTRY(TsanWrite8, 1, jit_tsan_write8);
|
||||
DEFINE_LEAF_RUNTIME_ENTRY(TsanWrite16, 1, jit_tsan_write16);
|
||||
DEFINE_LEAF_RUNTIME_ENTRY(TsanRead1, 1, dart_tsan_read1);
|
||||
DEFINE_LEAF_RUNTIME_ENTRY(TsanRead2, 1, dart_tsan_read2);
|
||||
DEFINE_LEAF_RUNTIME_ENTRY(TsanRead4, 1, dart_tsan_read4);
|
||||
DEFINE_LEAF_RUNTIME_ENTRY(TsanRead8, 1, dart_tsan_read8);
|
||||
DEFINE_LEAF_RUNTIME_ENTRY(TsanRead16, 1, dart_tsan_read16);
|
||||
DEFINE_LEAF_RUNTIME_ENTRY(TsanWrite1, 1, dart_tsan_write1);
|
||||
DEFINE_LEAF_RUNTIME_ENTRY(TsanWrite2, 1, dart_tsan_write2);
|
||||
DEFINE_LEAF_RUNTIME_ENTRY(TsanWrite4, 1, dart_tsan_write4);
|
||||
DEFINE_LEAF_RUNTIME_ENTRY(TsanWrite8, 1, dart_tsan_write8);
|
||||
DEFINE_LEAF_RUNTIME_ENTRY(TsanWrite16, 1, dart_tsan_write16);
|
||||
DEFINE_LEAF_RUNTIME_ENTRY(TsanFuncEntry, 1, dart_tsan_func_entry);
|
||||
#else
|
||||
DEFINE_LEAF_RUNTIME_ENTRY(TsanRead1, 1, __tsan_read1);
|
||||
DEFINE_LEAF_RUNTIME_ENTRY(TsanRead2, 1, __tsan_read2);
|
||||
@@ -5351,8 +5362,8 @@ DEFINE_LEAF_RUNTIME_ENTRY(TsanWrite2, 1, __tsan_write2);
|
||||
DEFINE_LEAF_RUNTIME_ENTRY(TsanWrite4, 1, __tsan_write4);
|
||||
DEFINE_LEAF_RUNTIME_ENTRY(TsanWrite8, 1, __tsan_write8);
|
||||
DEFINE_LEAF_RUNTIME_ENTRY(TsanWrite16, 1, __tsan_write16);
|
||||
#endif
|
||||
DEFINE_LEAF_RUNTIME_ENTRY(TsanFuncEntry, 1, __tsan_func_entry);
|
||||
#endif
|
||||
DEFINE_LEAF_RUNTIME_ENTRY(TsanFuncExit, 0, __tsan_func_exit);
|
||||
|
||||
} // namespace dart
|
||||
|
||||
@@ -95,7 +95,7 @@ namespace dart {
|
||||
|
||||
#define LEAF_RUNTIME_ENTRY_LIST(V) \
|
||||
V(intptr_t, DeoptimizeCopyFrame, uword, uword) \
|
||||
V(void, DeoptimizeFillFrame, uword) \
|
||||
V(intptr_t, DeoptimizeFillFrame, uword) \
|
||||
V(void, StoreBufferBlockProcess, Thread*) \
|
||||
V(void, OldMarkingStackBlockProcess, Thread*) \
|
||||
V(void, NewMarkingStackBlockProcess, Thread*) \
|
||||
|
||||
@@ -1925,6 +1925,9 @@ void Simulator::InterpretMISCMEM(Instr instr) {
|
||||
switch (instr.funct3()) {
|
||||
case FENCE:
|
||||
memory_.FlushAll();
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
#pragma GCC diagnostic ignored "-Wtsan"
|
||||
#endif
|
||||
std::atomic_thread_fence(std::memory_order_acq_rel);
|
||||
break;
|
||||
case FENCEI:
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "vm/tsan_symbolize.h"
|
||||
|
||||
#include "include/dart_api.h"
|
||||
#include "platform/atomic.h"
|
||||
#include "platform/thread_sanitizer.h"
|
||||
#include "vm/code_descriptors.h"
|
||||
@@ -36,86 +37,95 @@ static void WriteString(GrowableArray<uint8_t>& out, const char* str) {
|
||||
}
|
||||
|
||||
void RegisterTsanSymbolize(const Code& code) {
|
||||
if (!code.IsFunctionCode()) {
|
||||
// TODO(rmacnak): Do something for stubs?
|
||||
return;
|
||||
}
|
||||
|
||||
const CodeSourceMap& map = CodeSourceMap::Handle(code.code_source_map());
|
||||
RELEASE_ASSERT(!map.IsNull());
|
||||
const Array& functions = Array::Handle(code.inlined_id_to_function());
|
||||
Function& function = Function::Handle(code.function());
|
||||
Script& script = Script::Handle(function.script());
|
||||
String& url = String::Handle(script.url());
|
||||
GrowableArray<Function*> function_stack(8);
|
||||
GrowableArray<TokenPosition> token_positions(8);
|
||||
function_stack.Add(&Function::Handle(function.ptr()));
|
||||
token_positions.Add(CodeSourceMapBuilder::kInitialPosition);
|
||||
|
||||
GrowableArray<uint8_t> out(256);
|
||||
out.Add(OP_PUSH_FUNCTION);
|
||||
WriteString(out, function.QualifiedUserVisibleNameCString());
|
||||
WriteString(out, url.ToCString());
|
||||
|
||||
NoSafepointScope no_safepoint;
|
||||
ReadStream stream(map.Data(), map.Length());
|
||||
while (stream.PendingBytes() > 0) {
|
||||
int32_t arg;
|
||||
switch (CodeSourceMapOps::Read(&stream, &arg)) {
|
||||
case CodeSourceMapOps::kChangePosition: {
|
||||
const TokenPosition& old_token = token_positions.Last();
|
||||
token_positions.Last() = TokenPosition::Deserialize(
|
||||
Utils::AddWithWrapAround(arg, old_token.Serialize()));
|
||||
if (code.IsFunctionCode()) {
|
||||
const CodeSourceMap& map = CodeSourceMap::Handle(code.code_source_map());
|
||||
RELEASE_ASSERT(!map.IsNull());
|
||||
const Array& functions = Array::Handle(code.inlined_id_to_function());
|
||||
Function& function = Function::Handle(code.function());
|
||||
Script& script = Script::Handle(function.script());
|
||||
String& url = String::Handle(script.url());
|
||||
GrowableArray<Function*> function_stack(8);
|
||||
GrowableArray<TokenPosition> token_positions(8);
|
||||
function_stack.Add(&Function::Handle(function.ptr()));
|
||||
token_positions.Add(CodeSourceMapBuilder::kInitialPosition);
|
||||
|
||||
TokenPosition pos = token_positions.Last();
|
||||
if (pos.IsNoSource()) {
|
||||
pos = function_stack.Last()->token_pos();
|
||||
out.Add(OP_PUSH_FUNCTION);
|
||||
WriteString(out, function.QualifiedUserVisibleNameCString());
|
||||
WriteString(out, url.ToCString());
|
||||
|
||||
NoSafepointScope no_safepoint;
|
||||
ReadStream stream(map.Data(), map.Length());
|
||||
while (stream.PendingBytes() > 0) {
|
||||
int32_t arg;
|
||||
switch (CodeSourceMapOps::Read(&stream, &arg)) {
|
||||
case CodeSourceMapOps::kChangePosition: {
|
||||
const TokenPosition& old_token = token_positions.Last();
|
||||
token_positions.Last() = TokenPosition::Deserialize(
|
||||
Utils::AddWithWrapAround(arg, old_token.Serialize()));
|
||||
|
||||
TokenPosition pos = token_positions.Last();
|
||||
if (pos.IsNoSource()) {
|
||||
pos = function_stack.Last()->token_pos();
|
||||
}
|
||||
|
||||
intptr_t line = -1;
|
||||
intptr_t column = -1;
|
||||
script = function_stack.Last()->script();
|
||||
script.GetTokenLocation(pos, &line, &column);
|
||||
|
||||
out.Add(OP_CHANGE_POSITION);
|
||||
out.Add((line >> 0) & 0xFF);
|
||||
out.Add((line >> 8) & 0xFF);
|
||||
out.Add((column >> 0) & 0xFF);
|
||||
out.Add((column >> 8) & 0xFF);
|
||||
break;
|
||||
}
|
||||
case CodeSourceMapOps::kAdvancePC: {
|
||||
out.Add(OP_ADVANCE_PC);
|
||||
out.Add((arg >> 0) & 0xFF);
|
||||
out.Add((arg >> 8) & 0xFF);
|
||||
out.Add((arg >> 16) & 0xFF);
|
||||
out.Add((arg >> 24) & 0xFF);
|
||||
break;
|
||||
}
|
||||
case CodeSourceMapOps::kPushFunction: {
|
||||
function ^= functions.At(arg);
|
||||
function_stack.Add(&Function::Handle(function.ptr()));
|
||||
token_positions.Add(CodeSourceMapBuilder::kInitialPosition);
|
||||
|
||||
intptr_t line = -1;
|
||||
intptr_t column = -1;
|
||||
script = function_stack.Last()->script();
|
||||
script.GetTokenLocation(pos, &line, &column);
|
||||
|
||||
out.Add(OP_CHANGE_POSITION);
|
||||
out.Add((line >> 0) & 0xFF);
|
||||
out.Add((line >> 8) & 0xFF);
|
||||
out.Add((column >> 0) & 0xFF);
|
||||
out.Add((column >> 8) & 0xFF);
|
||||
break;
|
||||
out.Add(OP_PUSH_FUNCTION);
|
||||
WriteString(out, function.QualifiedUserVisibleNameCString());
|
||||
script = function_stack.Last()->script();
|
||||
url = script.url();
|
||||
WriteString(out, url.ToCString());
|
||||
break;
|
||||
}
|
||||
case CodeSourceMapOps::kPopFunction: {
|
||||
function_stack.RemoveLast();
|
||||
token_positions.RemoveLast();
|
||||
out.Add(OP_POP_FUNCTION);
|
||||
break;
|
||||
}
|
||||
case CodeSourceMapOps::kNullCheck: {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
case CodeSourceMapOps::kAdvancePC: {
|
||||
out.Add(OP_ADVANCE_PC);
|
||||
out.Add((arg >> 0) & 0xFF);
|
||||
out.Add((arg >> 8) & 0xFF);
|
||||
out.Add((arg >> 16) & 0xFF);
|
||||
out.Add((arg >> 24) & 0xFF);
|
||||
break;
|
||||
}
|
||||
case CodeSourceMapOps::kPushFunction: {
|
||||
function ^= functions.At(arg);
|
||||
function_stack.Add(&Function::Handle(function.ptr()));
|
||||
token_positions.Add(CodeSourceMapBuilder::kInitialPosition);
|
||||
|
||||
out.Add(OP_PUSH_FUNCTION);
|
||||
WriteString(out, function.QualifiedUserVisibleNameCString());
|
||||
script = function_stack.Last()->script();
|
||||
url = script.url();
|
||||
WriteString(out, url.ToCString());
|
||||
break;
|
||||
}
|
||||
case CodeSourceMapOps::kPopFunction: {
|
||||
function_stack.RemoveLast();
|
||||
token_positions.RemoveLast();
|
||||
out.Add(OP_POP_FUNCTION);
|
||||
break;
|
||||
}
|
||||
case CodeSourceMapOps::kNullCheck: {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
} else {
|
||||
out.Add(OP_PUSH_FUNCTION);
|
||||
WriteString(out, code.Name());
|
||||
WriteString(out, "stub");
|
||||
|
||||
int32_t arg = code.Size();
|
||||
out.Add(OP_ADVANCE_PC);
|
||||
out.Add((arg >> 0) & 0xFF);
|
||||
out.Add((arg >> 8) & 0xFF);
|
||||
out.Add((arg >> 16) & 0xFF);
|
||||
out.Add((arg >> 24) & 0xFF);
|
||||
}
|
||||
out.Add(OP_STOP);
|
||||
|
||||
@@ -143,9 +153,8 @@ typedef void (*AddFrame)(void* ctxt,
|
||||
// then symbolize using our normal PC descriptors, etc, but this function must
|
||||
// not call any function that has been instrumented by TSAN or it might deadlock
|
||||
// during __tsan_func_entry.
|
||||
extern "C" NO_SANITIZE_THREAD DISABLE_SANITIZER_INSTRUMENTATION void
|
||||
DART_EXPORT NO_SANITIZE_THREAD DISABLE_SANITIZER_INSTRUMENTATION void
|
||||
__tsan_symbolize_external_ex(uintptr_t pc, AddFrame add_frame, void* ctxt) {
|
||||
constexpr uintptr_t kExternalPCBit = 1ULL << 60;
|
||||
const uword lookup_pc = pc & ~kExternalPCBit;
|
||||
|
||||
for (TsanLineNumberProgram* lnp = head.load(std::memory_order_acquire);
|
||||
@@ -203,19 +212,6 @@ __tsan_symbolize_external_ex(uintptr_t pc, AddFrame add_frame, void* ctxt) {
|
||||
uint32_t disp = a | (b << 8) | (c << 16) | (d << 24);
|
||||
lnp_pc += disp;
|
||||
if (lookup_pc <= lnp_pc) {
|
||||
#if 0
|
||||
char s[17];
|
||||
s[16] = 0;
|
||||
for (intptr_t k = 0; k < 16; k++) {
|
||||
s[k] = "0123456789abcdef"[lookup_pc >> ((15 - k) << 2)) & 0xFF];
|
||||
}
|
||||
add_frame(ctxt, "lookup_pc", s, 0, 0);
|
||||
for (intptr_t k = 0; k < 16; k++) {
|
||||
s[k] = "0123456789abcdef"[lnp_pc >> ((15 - k) << 2)) & 0xFF];
|
||||
}
|
||||
add_frame(ctxt, "lnp_pc", s, 0, 0);
|
||||
#endif
|
||||
|
||||
for (intptr_t i = depth - 1; i >= 0; i--) {
|
||||
add_frame(ctxt, names[i], files[i], lines[i], columns[i]);
|
||||
}
|
||||
@@ -233,7 +229,20 @@ __tsan_symbolize_external_ex(uintptr_t pc, AddFrame add_frame, void* ctxt) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
add_frame(ctxt, "dart-code-lookup-failed", nullptr, 0, 0);
|
||||
|
||||
// TSAN doesn't fall back to reporting the PC if we provide no frame or a
|
||||
// frame with a null location.
|
||||
char pc_string[17];
|
||||
for (intptr_t i = 0; i < 16; i++) {
|
||||
pc_string[i] = "0123456789abcdef"[(lookup_pc >> ((15 - i) << 2)) & 0xF];
|
||||
}
|
||||
pc_string[16] = 0;
|
||||
|
||||
if (lookup_pc == 42) {
|
||||
add_frame(ctxt, "dart-deoptimized-code", pc_string, 0, 0);
|
||||
} else {
|
||||
add_frame(ctxt, "dart-code-lookup-failed", pc_string, 0, 0);
|
||||
}
|
||||
}
|
||||
#else
|
||||
void RegisterTsanSymbolize(const Code& code) {}
|
||||
|
||||
@@ -72,6 +72,9 @@ static void GenerateInvokeTTSStub(compiler::Assembler* assembler) {
|
||||
};
|
||||
|
||||
__ EnterDartFrame(0);
|
||||
if (FLAG_target_thread_sanitizer) {
|
||||
__ TsanFuncEntry();
|
||||
}
|
||||
|
||||
for (intptr_t i = 0; i < kNumberOfCpuRegisters; ++i) {
|
||||
if (((1 << i) & kDartAvailableCpuRegs) == 0) continue;
|
||||
@@ -181,6 +184,9 @@ static void GenerateInvokeTTSStub(compiler::Assembler* assembler) {
|
||||
|
||||
// Set the return from the stub to be null.
|
||||
__ LoadObject(CallingConventions::kReturnReg, Object::null_object());
|
||||
if (FLAG_target_thread_sanitizer) {
|
||||
__ TsanFuncExit();
|
||||
}
|
||||
__ LeaveDartFrame();
|
||||
__ Ret();
|
||||
}
|
||||
|
||||
@@ -1519,7 +1519,8 @@
|
||||
"arguments": [
|
||||
"-nvm-${sanitizer}-${system}-${mode}-${arch}",
|
||||
"ffi",
|
||||
"standalone"
|
||||
"standalone",
|
||||
"vm/dart/tsan"
|
||||
],
|
||||
"fileset": "vm",
|
||||
"shards": 2
|
||||
@@ -1529,7 +1530,8 @@
|
||||
"arguments": [
|
||||
"-nvm-aot-${sanitizer}-${system}-${mode}-${arch}",
|
||||
"ffi",
|
||||
"standalone"
|
||||
"standalone",
|
||||
"vm/dart/tsan"
|
||||
],
|
||||
"fileset": "vm",
|
||||
"shards": 3
|
||||
|
||||
Reference in New Issue
Block a user