[vm, compiler] Don't do safepoint transitions in generated code under TSAN.

Go to the runtime so TSAN sees the instrumented access to Thread::safepoint_state_, avoiding false data race reports.

TEST=tsan
Bug: https://github.com/dart-lang/sdk/issues/52024
Change-Id: Ic686bac2221d8ac6b9865ce9c82a4c36711037a7
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/295740
Reviewed-by: Alexander Aprelev <aam@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
This commit is contained in:
Ryan Macnak
2023-04-17 22:37:57 +00:00
committed by Commit Queue
parent 39fc15a0b4
commit c4795a7550
4 changed files with 26 additions and 12 deletions
+2
View File
@@ -17,8 +17,10 @@
#define NO_SANITIZE_THREAD __attribute__((no_sanitize("thread")))
extern "C" void __tsan_acquire(void* addr);
extern "C" void __tsan_release(void* addr);
constexpr bool kUsingThreadSanitizer = true;
#else
#define NO_SANITIZE_THREAD
constexpr bool kUsingThreadSanitizer = false;
#endif
#if defined(USING_THREAD_SANITIZER)
@@ -1643,12 +1643,14 @@ void Assembler::LeaveDartFrame() {
void Assembler::EnterFullSafepoint(Register state) {
// We generate the same number of instructions whether or not the slow-path is
// forced. This simplifies GenerateJitCallbackTrampolines.
// For TSAN, we always go to the runtime so TSAN is aware of the release
// semantics of entering the safepoint.
Register addr = TMP2;
ASSERT(addr != state);
Label slow_path, done, retry;
if (FLAG_use_slow_path) {
if (FLAG_use_slow_path || kUsingThreadSanitizer) {
b(&slow_path);
}
@@ -1663,7 +1665,7 @@ void Assembler::EnterFullSafepoint(Register state) {
stxr(TMP, state, addr);
cbz(&done, TMP); // 0 means stxr was successful.
if (!FLAG_use_slow_path) {
if (!FLAG_use_slow_path && !kUsingThreadSanitizer) {
b(&retry);
}
@@ -1701,11 +1703,13 @@ void Assembler::ExitFullSafepoint(Register state,
bool ignore_unwind_in_progress) {
// We generate the same number of instructions whether or not the slow-path is
// forced, for consistency with EnterFullSafepoint.
// For TSAN, we always go to the runtime so TSAN is aware of the acquire
// semantics of leaving the safepoint.
Register addr = TMP2;
ASSERT(addr != state);
Label slow_path, done, retry;
if (FLAG_use_slow_path) {
if (FLAG_use_slow_path || kUsingThreadSanitizer) {
b(&slow_path);
}
@@ -1720,7 +1724,7 @@ void Assembler::ExitFullSafepoint(Register state,
stxr(TMP, state, addr);
cbz(&done, TMP); // 0 means stxr was successful.
if (!FLAG_use_slow_path) {
if (!FLAG_use_slow_path && !kUsingThreadSanitizer) {
b(&retry);
}
@@ -3863,12 +3863,14 @@ void Assembler::TransitionNativeToGenerated(Register state,
void Assembler::EnterFullSafepoint(Register state) {
// We generate the same number of instructions whether or not the slow-path is
// forced. This simplifies GenerateJitCallbackTrampolines.
// For TSAN, we always go to the runtime so TSAN is aware of the release
// semantics of entering the safepoint.
Register addr = RA;
ASSERT(addr != state);
Label slow_path, done, retry;
if (FLAG_use_slow_path) {
if (FLAG_use_slow_path || kUsingThreadSanitizer) {
j(&slow_path, Assembler::kNearJump);
}
@@ -3882,7 +3884,7 @@ void Assembler::EnterFullSafepoint(Register state) {
sc(state, state, Address(addr, 0));
beqz(state, &done, Assembler::kNearJump); // 0 means sc was successful.
if (!FLAG_use_slow_path) {
if (!FLAG_use_slow_path && !kUsingThreadSanitizer) {
j(&retry, Assembler::kNearJump);
}
@@ -3898,11 +3900,13 @@ void Assembler::ExitFullSafepoint(Register state,
bool ignore_unwind_in_progress) {
// We generate the same number of instructions whether or not the slow-path is
// forced, for consistency with EnterFullSafepoint.
// For TSAN, we always go to the runtime so TSAN is aware of the acquire
// semantics of leaving the safepoint.
Register addr = RA;
ASSERT(addr != state);
Label slow_path, done, retry;
if (FLAG_use_slow_path) {
if (FLAG_use_slow_path || kUsingThreadSanitizer) {
j(&slow_path, Assembler::kNearJump);
}
@@ -3916,7 +3920,7 @@ void Assembler::ExitFullSafepoint(Register state,
sc(state, state, Address(addr, 0));
beqz(state, &done, Assembler::kNearJump); // 0 means sc was successful.
if (!FLAG_use_slow_path) {
if (!FLAG_use_slow_path && !kUsingThreadSanitizer) {
j(&retry, Assembler::kNearJump);
}
@@ -129,8 +129,10 @@ void Assembler::setcc(Condition condition, ByteRegister dst) {
void Assembler::EnterFullSafepoint() {
// We generate the same number of instructions whether or not the slow-path is
// forced, to simplify GenerateJitCallbackTrampolines.
// For TSAN, we always go to the runtime so TSAN is aware of the release
// semantics of entering the safepoint.
Label done, slow_path;
if (FLAG_use_slow_path) {
if (FLAG_use_slow_path || kUsingThreadSanitizer) {
jmp(&slow_path);
}
@@ -144,7 +146,7 @@ void Assembler::EnterFullSafepoint() {
popq(RAX);
cmpq(TMP, Immediate(target::Thread::full_safepoint_state_unacquired()));
if (!FLAG_use_slow_path) {
if (!FLAG_use_slow_path && !kUsingThreadSanitizer) {
j(EQUAL, &done);
}
@@ -184,8 +186,10 @@ void Assembler::TransitionGeneratedToNative(Register destination_address,
void Assembler::ExitFullSafepoint(bool ignore_unwind_in_progress) {
// We generate the same number of instructions whether or not the slow-path is
// forced, for consistency with EnterFullSafepoint.
// For TSAN, we always go to the runtime so TSAN is aware of the acquire
// semantics of leaving the safepoint.
Label done, slow_path;
if (FLAG_use_slow_path) {
if (FLAG_use_slow_path || kUsingThreadSanitizer) {
jmp(&slow_path);
}
@@ -201,7 +205,7 @@ void Assembler::ExitFullSafepoint(bool ignore_unwind_in_progress) {
popq(RAX);
cmpq(TMP, Immediate(target::Thread::full_safepoint_state_acquired()));
if (!FLAG_use_slow_path) {
if (!FLAG_use_slow_path && !kUsingThreadSanitizer) {
j(EQUAL, &done);
}