Commit 6a1063c8 authored by Mike Stanton's avatar Mike Stanton Committed by V8 LUCI CQ

[compiler] TSAN data race on HeapNumber::value_as_bits()

TurboFan reads the value in HeapNumber, and TSAN detects a data
race between this read and sets on the main thread elsewhere.
We mark this as relaxed atomic (meaning, correct value of the read
is not guaranteed). The compiler uses the dependency mechanism
to re-read the value safely on the main thread later, and aborts
compilation if a change is detected.

Bug: chromium:1224277, v8:7790
Change-Id: I8931d8989812550c0c57b6bd27aa796f6f5e779d
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2996201Reviewed-by: 's avatarSantiago Aboy Solanes <solanes@chromium.org>
Reviewed-by: 's avatarLeszek Swirski <leszeks@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Commit-Queue: Michael Stanton <mvstanton@chromium.org>
Cr-Commit-Position: refs/heads/master@{#75586}
parent a2b76fa7
...@@ -342,7 +342,8 @@ MaybeHandle<Object> AsmJs::InstantiateAsmWasm(Isolate* isolate, ...@@ -342,7 +342,8 @@ MaybeHandle<Object> AsmJs::InstantiateAsmWasm(Isolate* isolate,
// Check that all used stdlib members are valid. // Check that all used stdlib members are valid.
bool stdlib_use_of_typed_array_present = false; bool stdlib_use_of_typed_array_present = false;
wasm::AsmJsParser::StdlibSet stdlib_uses = wasm::AsmJsParser::StdlibSet stdlib_uses =
wasm::AsmJsParser::StdlibSet::FromIntegral(uses_bitset->value_as_bits()); wasm::AsmJsParser::StdlibSet::FromIntegral(
uses_bitset->value_as_bits(kRelaxedLoad));
if (!stdlib_uses.empty()) { // No checking needed if no uses. if (!stdlib_uses.empty()) { // No checking needed if no uses.
if (stdlib.is_null()) { if (stdlib.is_null()) {
ReportInstantiationFailure(script, position, "Requires standard library"); ReportInstantiationFailure(script, position, "Requires standard library");
......
...@@ -259,8 +259,8 @@ class OwnConstantDataPropertyDependency final : public CompilationDependency { ...@@ -259,8 +259,8 @@ class OwnConstantDataPropertyDependency final : public CompilationDependency {
if (representation_.IsDouble()) { if (representation_.IsDouble()) {
// Compare doubles by bit pattern. // Compare doubles by bit pattern.
if (!current_value.IsHeapNumber() || !used_value.IsHeapNumber() || if (!current_value.IsHeapNumber() || !used_value.IsHeapNumber() ||
HeapNumber::cast(current_value).value_as_bits() != HeapNumber::cast(current_value).value_as_bits(kRelaxedLoad) !=
HeapNumber::cast(used_value).value_as_bits()) { HeapNumber::cast(used_value).value_as_bits(kRelaxedLoad)) {
TRACE_BROKER_MISSING(broker_, TRACE_BROKER_MISSING(broker_,
"Constant Double property value changed in " "Constant Double property value changed in "
<< holder_.object() << " at FieldIndex " << holder_.object() << " at FieldIndex "
......
...@@ -880,7 +880,7 @@ class HeapNumberData : public HeapObjectData { ...@@ -880,7 +880,7 @@ class HeapNumberData : public HeapObjectData {
ObjectDataKind kind = ObjectDataKind::kSerializedHeapObject) ObjectDataKind kind = ObjectDataKind::kSerializedHeapObject)
: HeapObjectData(broker, storage, object, kind), : HeapObjectData(broker, storage, object, kind),
value_(object->value()), value_(object->value()),
value_as_bits_(object->value_as_bits()) {} value_as_bits_(object->value_as_bits(kRelaxedLoad)) {}
double value() const { return value_; } double value() const { return value_; }
uint64_t value_as_bits() const { return value_as_bits_; } uint64_t value_as_bits() const { return value_as_bits_; }
...@@ -3135,7 +3135,14 @@ BIMODAL_ACCESSOR_C(FeedbackVector, double, invocation_count) ...@@ -3135,7 +3135,14 @@ BIMODAL_ACCESSOR_C(FeedbackVector, double, invocation_count)
BIMODAL_ACCESSOR(HeapObject, Map, map) BIMODAL_ACCESSOR(HeapObject, Map, map)
BIMODAL_ACCESSOR_C(HeapNumber, double, value) BIMODAL_ACCESSOR_C(HeapNumber, double, value)
BIMODAL_ACCESSOR_C(HeapNumber, uint64_t, value_as_bits)
uint64_t HeapNumberRef::value_as_bits() const {
if (data_->should_access_heap()) {
return object()->value_as_bits(kRelaxedLoad);
}
return ObjectRef::data()->AsHeapNumber()->value_as_bits();
}
// Immutable after initialization. // Immutable after initialization.
BIMODAL_ACCESSOR_WITH_FLAG(JSBoundFunction, JSReceiver, bound_target_function) BIMODAL_ACCESSOR_WITH_FLAG(JSBoundFunction, JSReceiver, bound_target_function)
......
...@@ -76,7 +76,7 @@ template <typename Impl> ...@@ -76,7 +76,7 @@ template <typename Impl>
template <AllocationType allocation> template <AllocationType allocation>
Handle<HeapNumber> FactoryBase<Impl>::NewHeapNumber(double value) { Handle<HeapNumber> FactoryBase<Impl>::NewHeapNumber(double value) {
Handle<HeapNumber> heap_number = NewHeapNumber<allocation>(); Handle<HeapNumber> heap_number = NewHeapNumber<allocation>();
heap_number->set_value(value); heap_number->set_value(value, kRelaxedStore);
return heap_number; return heap_number;
} }
...@@ -84,7 +84,7 @@ template <typename Impl> ...@@ -84,7 +84,7 @@ template <typename Impl>
template <AllocationType allocation> template <AllocationType allocation>
Handle<HeapNumber> FactoryBase<Impl>::NewHeapNumberFromBits(uint64_t bits) { Handle<HeapNumber> FactoryBase<Impl>::NewHeapNumberFromBits(uint64_t bits) {
Handle<HeapNumber> heap_number = NewHeapNumber<allocation>(); Handle<HeapNumber> heap_number = NewHeapNumber<allocation>();
heap_number->set_value_as_bits(bits); heap_number->set_value_as_bits(bits, kRelaxedStore);
return heap_number; return heap_number;
} }
......
...@@ -599,7 +599,7 @@ Handle<Object> JsonParser<Char>::BuildJsonObject( ...@@ -599,7 +599,7 @@ Handle<Object> JsonParser<Char>::BuildJsonObject(
HeapObject hn = HeapObject::FromAddress(mutable_double_address); HeapObject hn = HeapObject::FromAddress(mutable_double_address);
hn.set_map_after_allocation(*factory()->heap_number_map()); hn.set_map_after_allocation(*factory()->heap_number_map());
HeapNumber::cast(hn).set_value_as_bits(bits); HeapNumber::cast(hn).set_value_as_bits(bits, kRelaxedStore);
value = hn; value = hn;
mutable_double_address += kMutableDoubleSize; mutable_double_address += kMutableDoubleSize;
} else { } else {
......
...@@ -20,13 +20,20 @@ namespace internal { ...@@ -20,13 +20,20 @@ namespace internal {
TQ_OBJECT_CONSTRUCTORS_IMPL(HeapNumber) TQ_OBJECT_CONSTRUCTORS_IMPL(HeapNumber)
uint64_t HeapNumber::value_as_bits() const { uint64_t HeapNumber::value_as_bits(RelaxedLoadTag) const {
uint64_t value;
base::Relaxed_Memcpy(
reinterpret_cast<base::Atomic8*>(&value),
reinterpret_cast<base::Atomic8*>(field_address(kValueOffset)),
sizeof(uint64_t));
// Bug(v8:8875): HeapNumber's double may be unaligned. // Bug(v8:8875): HeapNumber's double may be unaligned.
return base::ReadUnalignedValue<uint64_t>(field_address(kValueOffset)); return value;
} }
void HeapNumber::set_value_as_bits(uint64_t bits) { void HeapNumber::set_value_as_bits(uint64_t bits, RelaxedStoreTag) {
base::WriteUnalignedValue<uint64_t>(field_address(kValueOffset), bits); base::Relaxed_Memcpy(
reinterpret_cast<base::Atomic8*>(field_address(kValueOffset)),
reinterpret_cast<base::Atomic8*>(&bits), sizeof(uint64_t));
} }
int HeapNumber::get_exponent() { int HeapNumber::get_exponent() {
......
...@@ -20,8 +20,11 @@ namespace internal { ...@@ -20,8 +20,11 @@ namespace internal {
class HeapNumber class HeapNumber
: public TorqueGeneratedHeapNumber<HeapNumber, PrimitiveHeapObject> { : public TorqueGeneratedHeapNumber<HeapNumber, PrimitiveHeapObject> {
public: public:
inline uint64_t value_as_bits() const; // Since the value is read from the compiler, and it can't be done
inline void set_value_as_bits(uint64_t bits); // atomically, we signal both to TSAN and ourselves that this is a
// relaxed load and store.
inline uint64_t value_as_bits(RelaxedLoadTag) const;
inline void set_value_as_bits(uint64_t bits, RelaxedStoreTag);
inline int get_exponent(); inline int get_exponent();
inline int get_sign(); inline int get_sign();
......
...@@ -4,7 +4,9 @@ ...@@ -4,7 +4,9 @@
@generateCppClass @generateCppClass
extern class HeapNumber extends PrimitiveHeapObject { extern class HeapNumber extends PrimitiveHeapObject {
value: float64; // Marked as a relaxed store because of a race with reading on the
// compiler thread.
@cppRelaxedStore value: float64;
} }
// The HeapNumber value NaN // The HeapNumber value NaN
......
...@@ -416,10 +416,10 @@ void JSObject::WriteToField(InternalIndex descriptor, PropertyDetails details, ...@@ -416,10 +416,10 @@ void JSObject::WriteToField(InternalIndex descriptor, PropertyDetails details,
bits = kHoleNanInt64; bits = kHoleNanInt64;
} else { } else {
DCHECK(value.IsHeapNumber()); DCHECK(value.IsHeapNumber());
bits = HeapNumber::cast(value).value_as_bits(); bits = HeapNumber::cast(value).value_as_bits(kRelaxedLoad);
} }
auto box = HeapNumber::cast(RawFastPropertyAt(index)); auto box = HeapNumber::cast(RawFastPropertyAt(index));
box.set_value_as_bits(bits); box.set_value_as_bits(bits, kRelaxedStore);
} else { } else {
FastPropertyAtPut(index, value); FastPropertyAtPut(index, value);
} }
......
...@@ -948,7 +948,7 @@ bool LookupIterator::IsConstFieldValueEqualTo(Object value) const { ...@@ -948,7 +948,7 @@ bool LookupIterator::IsConstFieldValueEqualTo(Object value) const {
uint64_t bits; uint64_t bits;
Object current_value = holder->RawFastPropertyAt(isolate_, field_index); Object current_value = holder->RawFastPropertyAt(isolate_, field_index);
DCHECK(current_value.IsHeapNumber(isolate_)); DCHECK(current_value.IsHeapNumber(isolate_));
bits = HeapNumber::cast(current_value).value_as_bits(); bits = HeapNumber::cast(current_value).value_as_bits(kRelaxedLoad);
// Use bit representation of double to check for hole double, since // Use bit representation of double to check for hole double, since
// manipulating the signaling NaN used for the hole in C++, e.g. with // manipulating the signaling NaN used for the hole in C++, e.g. with
// bit_cast or value(), will change its value on ia32 (the x87 stack is // bit_cast or value(), will change its value on ia32 (the x87 stack is
......
...@@ -221,12 +221,13 @@ Handle<Object> Object::NewStorageFor(Isolate* isolate, Handle<Object> object, ...@@ -221,12 +221,13 @@ Handle<Object> Object::NewStorageFor(Isolate* isolate, Handle<Object> object,
if (!representation.IsDouble()) return object; if (!representation.IsDouble()) return object;
auto result = isolate->factory()->NewHeapNumberWithHoleNaN(); auto result = isolate->factory()->NewHeapNumberWithHoleNaN();
if (object->IsUninitialized(isolate)) { if (object->IsUninitialized(isolate)) {
result->set_value_as_bits(kHoleNanInt64); result->set_value_as_bits(kHoleNanInt64, kRelaxedStore);
} else if (object->IsHeapNumber()) { } else if (object->IsHeapNumber()) {
// Ensure that all bits of the double value are preserved. // Ensure that all bits of the double value are preserved.
result->set_value_as_bits(HeapNumber::cast(*object).value_as_bits()); result->set_value_as_bits(
HeapNumber::cast(*object).value_as_bits(kRelaxedLoad), kRelaxedStore);
} else { } else {
result->set_value(object->Number()); result->set_value(object->Number(), kRelaxedStore);
} }
return result; return result;
} }
...@@ -240,7 +241,7 @@ Handle<Object> Object::WrapForRead(IsolateT* isolate, Handle<Object> object, ...@@ -240,7 +241,7 @@ Handle<Object> Object::WrapForRead(IsolateT* isolate, Handle<Object> object,
return object; return object;
} }
return isolate->factory()->template NewHeapNumberFromBits<allocation_type>( return isolate->factory()->template NewHeapNumberFromBits<allocation_type>(
HeapNumber::cast(*object).value_as_bits()); HeapNumber::cast(*object).value_as_bits(kRelaxedLoad));
} }
template Handle<Object> Object::WrapForRead<AllocationType::kYoung>( template Handle<Object> Object::WrapForRead<AllocationType::kYoung>(
...@@ -4715,7 +4716,7 @@ void Oddball::Initialize(Isolate* isolate, Handle<Oddball> oddball, ...@@ -4715,7 +4716,7 @@ void Oddball::Initialize(Isolate* isolate, Handle<Oddball> oddball,
isolate->factory()->InternalizeUtf8String(type_of); isolate->factory()->InternalizeUtf8String(type_of);
if (to_number->IsHeapNumber()) { if (to_number->IsHeapNumber()) {
oddball->set_to_number_raw_as_bits( oddball->set_to_number_raw_as_bits(
Handle<HeapNumber>::cast(to_number)->value_as_bits()); Handle<HeapNumber>::cast(to_number)->value_as_bits(kRelaxedLoad));
} else { } else {
oddball->set_to_number_raw(to_number->Number()); oddball->set_to_number_raw(to_number->Number());
} }
......
...@@ -128,7 +128,8 @@ MaybeHandle<JSObject> JSObjectWalkVisitor<ContextObject>::StructureWalk( ...@@ -128,7 +128,8 @@ MaybeHandle<JSObject> JSObjectWalkVisitor<ContextObject>::StructureWalk(
isolate, value, VisitElementOrProperty(copy, value), JSObject); isolate, value, VisitElementOrProperty(copy, value), JSObject);
if (copying) copy->FastPropertyAtPut(index, *value); if (copying) copy->FastPropertyAtPut(index, *value);
} else if (copying && details.representation().IsDouble()) { } else if (copying && details.representation().IsDouble()) {
uint64_t double_value = HeapNumber::cast(raw).value_as_bits(); uint64_t double_value =
HeapNumber::cast(raw).value_as_bits(kRelaxedLoad);
auto value = isolate->factory()->NewHeapNumberFromBits(double_value); auto value = isolate->factory()->NewHeapNumberFromBits(double_value);
copy->FastPropertyAtPut(index, *value); copy->FastPropertyAtPut(index, *value);
} }
......
...@@ -2773,13 +2773,13 @@ TEST(HoleyHeapNumber) { ...@@ -2773,13 +2773,13 @@ TEST(HoleyHeapNumber) {
Isolate* isolate = CcTest::i_isolate(); Isolate* isolate = CcTest::i_isolate();
auto mhn = isolate->factory()->NewHeapNumberWithHoleNaN(); auto mhn = isolate->factory()->NewHeapNumberWithHoleNaN();
CHECK_EQ(kHoleNanInt64, mhn->value_as_bits()); CHECK_EQ(kHoleNanInt64, mhn->value_as_bits(kRelaxedLoad));
mhn = isolate->factory()->NewHeapNumber(0.0); mhn = isolate->factory()->NewHeapNumber(0.0);
CHECK_EQ(uint64_t{0}, mhn->value_as_bits()); CHECK_EQ(uint64_t{0}, mhn->value_as_bits(kRelaxedLoad));
mhn->set_value_as_bits(kHoleNanInt64); mhn->set_value_as_bits(kHoleNanInt64, kRelaxedStore);
CHECK_EQ(kHoleNanInt64, mhn->value_as_bits()); CHECK_EQ(kHoleNanInt64, mhn->value_as_bits(kRelaxedLoad));
// Ensure that new storage for uninitialized value or mutable heap number // Ensure that new storage for uninitialized value or mutable heap number
// with uninitialized sentinel (kHoleNanInt64) is a mutable heap number // with uninitialized sentinel (kHoleNanInt64) is a mutable heap number
...@@ -2788,11 +2788,11 @@ TEST(HoleyHeapNumber) { ...@@ -2788,11 +2788,11 @@ TEST(HoleyHeapNumber) {
Object::NewStorageFor(isolate, isolate->factory()->uninitialized_value(), Object::NewStorageFor(isolate, isolate->factory()->uninitialized_value(),
Representation::Double()); Representation::Double());
CHECK(obj->IsHeapNumber()); CHECK(obj->IsHeapNumber());
CHECK_EQ(kHoleNanInt64, HeapNumber::cast(*obj).value_as_bits()); CHECK_EQ(kHoleNanInt64, HeapNumber::cast(*obj).value_as_bits(kRelaxedLoad));
obj = Object::NewStorageFor(isolate, mhn, Representation::Double()); obj = Object::NewStorageFor(isolate, mhn, Representation::Double());
CHECK(obj->IsHeapNumber()); CHECK(obj->IsHeapNumber());
CHECK_EQ(kHoleNanInt64, HeapNumber::cast(*obj).value_as_bits()); CHECK_EQ(kHoleNanInt64, HeapNumber::cast(*obj).value_as_bits(kRelaxedLoad));
} }
namespace { namespace {
......
// Copyright 2021 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax --stress-concurrent-inlining
var __v_13 = {};
function __f_7() {
return !__v_13.bar++;
}
%PrepareFunctionForOptimization(__f_7);
__f_7();
%OptimizeFunctionOnNextCall(__f_7, "concurrent");
__f_7();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment