[vm] C++20 updates.

- std::atomic_ref
  - std::bit_cast
  - std::rotl,rotr
  - designated initializers

TEST=ci
Bug: https://github.com/dart-lang/sdk/issues/42074
Change-Id: I7be17147723db6f7620a147e75d38ebe46393f8c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/484700
Reviewed-by: Alexander Aprelev <aam@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
This commit is contained in:
Ryan Macnak
2026-03-04 12:40:19 -08:00
committed by Commit Queue
parent e8d02a14da
commit 4bb6609724
13 changed files with 108 additions and 196 deletions
+2 -35
View File
@@ -90,6 +90,7 @@
#include <string.h>
#include <sys/types.h>
#include <bit>
#include <cassert> // For assert() in constant expressions.
#if defined(_WIN32)
@@ -644,41 +645,7 @@ constexpr double MicrosecondsToMilliseconds(int64_t micros) {
template <typename T>
static inline void USE(T&&) {}
// The type-based aliasing rule allows the compiler to assume that
// pointers of different types (for some definition of different)
// never alias each other. Thus the following code does not work:
//
// float f = foo();
// int fbits = *(int*)(&f);
//
// The compiler 'knows' that the int pointer can't refer to f since
// the types don't match, so the compiler may cache f in a register,
// leaving random data in fbits. Using C++ style casts makes no
// difference, however a pointer to char data is assumed to alias any
// other pointer. This is the 'memcpy exception'.
//
// The bit_cast function uses the memcpy exception to move the bits
// from a variable of one type to a variable of another type. Of
// course the end result is likely to be implementation dependent.
// Most compilers (gcc-4.2 and MSVC 2005) will completely optimize
// bit_cast away.
//
// There is an additional use for bit_cast. Recent gccs will warn when
// they see casts that may result in breakage due to the type-based
// aliasing rule. If you have checked that there is no breakage you
// can use bit_cast to cast one pointer type to another. This confuses
// gcc enough that it can no longer see that you have cast one pointer
// type to another thus avoiding the warning.
template <class D, class S>
DART_FORCE_INLINE D bit_cast(const S& source) {
static_assert(sizeof(D) == sizeof(S),
"Source and destination must have the same size");
D destination;
// This use of memcpy is safe: source and destination cannot overlap.
memcpy(&destination, &source, sizeof(destination));
return destination;
}
using std::bit_cast;
// Similar to bit_cast, but allows copying from types of unrelated
// sizes. This method was introduced to enable the strict aliasing
+2 -4
View File
@@ -379,8 +379,7 @@ class Utils {
ASSERT(0 <= rotate);
ASSERT(rotate <= width);
using Unsigned = typename std::make_unsigned<T>::type;
return (static_cast<Unsigned>(value) << rotate) |
(static_cast<T>(value) >> ((width - rotate) & (width - 1)));
return std::rotl(static_cast<Unsigned>(value), rotate);
}
template <typename T>
static inline T RotateRight(T value, uint8_t rotate) {
@@ -388,8 +387,7 @@ class Utils {
ASSERT(0 <= rotate);
ASSERT(rotate <= width);
using Unsigned = typename std::make_unsigned<T>::type;
return (static_cast<T>(value) >> rotate) |
(static_cast<Unsigned>(value) << ((width - rotate) & (width - 1)));
return std::rotr(static_cast<Unsigned>(value), rotate);
}
NO_SANITIZE_UNDEFINED("float-divide-by-zero")
+4 -5
View File
@@ -12,17 +12,16 @@ namespace dart {
void BSS::InitializeBSSEntry(BSS::Relocation relocation,
uword new_value,
uword* bss_start) {
std::atomic<uword>* slot = reinterpret_cast<std::atomic<uword>*>(
&bss_start[BSS::RelocationIndex(relocation)]);
uword old_value = slot->load(std::memory_order_relaxed);
auto slot = std::atomic_ref(bss_start[BSS::RelocationIndex(relocation)]);
uword old_value = slot.load(std::memory_order_relaxed);
// FullSnapshotReader::ReadProgramSnapshot, and thus BSS::Initialize, can
// get called multiple times for the same isolate in different threads, though
// the initialized value will be consistent and thus change only once. Avoid
// calling compare_exchange_strong unless we actually need to change the
// value, to avoid spurious read/write races by TSAN.
if (old_value == new_value) return;
if (!slot->compare_exchange_strong(old_value, new_value,
std::memory_order_relaxed)) {
if (!slot.compare_exchange_strong(old_value, new_value,
std::memory_order_relaxed)) {
RELEASE_ASSERT(old_value == new_value);
}
}
+1 -1
View File
@@ -117,7 +117,7 @@ void FieldTable::Grow(intptr_t new_capacity) {
old_tables_->Add(old_table);
// Ensure that new_table_ is populated before it is published
// via store to table_.
reinterpret_cast<AcqRelAtomic<ObjectPtr*>*>(&table_)->store(new_table);
std::atomic_ref(table_).store(new_table, std::memory_order_release);
if (isolate_group_ != nullptr) {
isolate_group_->ForEachIsolate(
[&](Isolate* isolate) {
+4 -3
View File
@@ -63,8 +63,8 @@ class FieldTable {
ASSERT(IsValidIndex(index));
if (concurrent_use) {
ObjectPtr* table =
reinterpret_cast<const AcqRelAtomic<ObjectPtr*>*>(&table_)->load();
return reinterpret_cast<AcqRelAtomic<ObjectPtr>*>(&table[index])->load();
std::atomic_ref(table_).load(std::memory_order::acquire);
return std::atomic_ref(table[index]).load(std::memory_order_acquire);
} else {
// There is no concurrent access expected for this field, so we avoid
// using atomics. This will allow us to detect via TSAN if there are
@@ -106,7 +106,8 @@ class FieldTable {
// element, last element contains -1.
intptr_t free_head_;
ObjectPtr* table_;
// Mutable for atomic_ref in At.
mutable ObjectPtr* table_;
// When table_ grows and have to reallocated, keep the old one here
// so it will get freed when its are no longer in use.
MallocGrowableArray<ObjectPtr*>* old_tables_;
+7 -6
View File
@@ -117,14 +117,14 @@ static void objcpy(void* dst, const void* src, size_t size) {
DART_FORCE_INLINE
static uword ReadHeaderRelaxed(ObjectPtr obj) {
return reinterpret_cast<std::atomic<uword>*>(UntaggedObject::ToAddr(obj))
->load(std::memory_order_relaxed);
return std::atomic_ref(*reinterpret_cast<uword*>(UntaggedObject::ToAddr(obj)))
.load(std::memory_order_relaxed);
}
DART_FORCE_INLINE
static void WriteHeaderRelaxed(ObjectPtr obj, uword header) {
reinterpret_cast<std::atomic<uword>*>(UntaggedObject::ToAddr(obj))
->store(header, std::memory_order_relaxed);
std::atomic_ref(*reinterpret_cast<uword*>(UntaggedObject::ToAddr(obj)))
.store(header, std::memory_order_relaxed);
}
class ScavengerVisitor : public ObjectPointerVisitor,
@@ -524,8 +524,9 @@ class ScavengerVisitor : public ObjectPointerVisitor,
bool InstallForwardingPointer(uword addr,
uword* old_header,
uword new_header) {
return reinterpret_cast<std::atomic<uword>*>(addr)->compare_exchange_strong(
*old_header, new_header, std::memory_order_relaxed);
return std::atomic_ref(*reinterpret_cast<uword*>(addr))
.compare_exchange_strong(*old_header, new_header,
std::memory_order_relaxed);
}
DART_FORCE_INLINE
+3 -3
View File
@@ -126,9 +126,9 @@ class InterruptChecker : public ThreadPool::Task {
// Busy wait for interrupts.
uword limit = 0;
do {
limit = reinterpret_cast<RelaxedAtomic<uword>*>(
thread_->stack_limit_address())
->load();
limit = std::atomic_ref(
*reinterpret_cast<uword*>(thread_->stack_limit_address()))
.load(std::memory_order_relaxed);
} while (
(limit == thread_->saved_stack_limit_) ||
(((limit & Thread::kInterruptsMask) & Thread::kVMInterrupt) == 0));
+19 -40
View File
@@ -26,40 +26,19 @@
namespace dart {
static Dart_CObject cobj_sentinel = {Dart_CObject_kUnsupported, {false}};
static Dart_CObject cobj_dynamic_type = {Dart_CObject_kUnsupported, {false}};
static Dart_CObject cobj_void_type = {Dart_CObject_kUnsupported, {false}};
static Dart_CObject cobj_empty_type_arguments = {Dart_CObject_kUnsupported,
{false}};
static Dart_CObject cobj_true = {Dart_CObject_kBool, {true}};
static Dart_CObject cobj_false = {Dart_CObject_kBool, {false}};
// Workaround for lack of designated initializers until we adopt c++20
class PredefinedCObjects {
public:
static PredefinedCObjects& getInstance() {
static PredefinedCObjects instance;
return instance;
}
static Dart_CObject* cobj_null() { return &getInstance().cobj_null_; }
static Dart_CObject* cobj_empty_array() {
return &getInstance().cobj_empty_array_;
}
private:
PredefinedCObjects() {
cobj_null_.type = Dart_CObject_kNull;
cobj_null_.value.as_int64 = 0;
cobj_empty_array_.type = Dart_CObject_kArray;
cobj_empty_array_.value.as_array = {0, nullptr};
}
Dart_CObject cobj_null_;
Dart_CObject cobj_empty_array_;
DISALLOW_COPY_AND_ASSIGN(PredefinedCObjects);
};
static Dart_CObject cobj_sentinel = {.type = Dart_CObject_kUnsupported};
static Dart_CObject cobj_dynamic_type = {.type = Dart_CObject_kUnsupported};
static Dart_CObject cobj_void_type = {.type = Dart_CObject_kUnsupported};
static Dart_CObject cobj_empty_type_arguments = {.type =
Dart_CObject_kUnsupported};
static Dart_CObject cobj_true = {.type = Dart_CObject_kBool,
.value = {.as_bool = true}};
static Dart_CObject cobj_false = {.type = Dart_CObject_kBool,
.value = {.as_bool = false}};
static Dart_CObject cobj_null = {.type = Dart_CObject_kNull,
.value = {.as_int64 = 0}};
static Dart_CObject cobj_empty_array = {.type = Dart_CObject_kArray,
.value = {.as_array = {0, nullptr}}};
enum class MessagePhase {
kBeforeTypes = 0,
@@ -2374,7 +2353,7 @@ class ArrayMessageSerializationCluster : public MessageSerializationCluster {
for (intptr_t i = 0; i < count; i++) {
Dart_CObject* array = reinterpret_cast<Dart_CObject*>(objects_[i]);
intptr_t length = array->value.as_array.length;
s->WriteRef(PredefinedCObjects::cobj_null()); // TypeArguments
s->WriteRef(&cobj_null); // TypeArguments
for (intptr_t j = 0; j < length; j++) {
s->WriteRef(array->value.as_array.values[j]);
}
@@ -2819,7 +2798,7 @@ bool ApiMessageSerializer::Trace(Dart_CObject* object) {
intptr_t cid;
switch (object->type) {
case Dart_CObject_kNull:
ForwardRef(object, PredefinedCObjects::cobj_null());
ForwardRef(object, &cobj_null);
return true;
case Dart_CObject_kBool:
ForwardRef(object, object->value.as_bool ? &cobj_true : &cobj_false);
@@ -3234,9 +3213,9 @@ void MessageDeserializer::AddBaseObjects() {
}
void ApiMessageSerializer::AddBaseObjects() {
AddBaseObject(PredefinedCObjects::cobj_null());
AddBaseObject(&cobj_null);
AddBaseObject(&cobj_sentinel);
AddBaseObject(PredefinedCObjects::cobj_empty_array());
AddBaseObject(&cobj_empty_array);
AddBaseObject(&cobj_dynamic_type);
AddBaseObject(&cobj_void_type);
AddBaseObject(&cobj_empty_type_arguments);
@@ -3245,9 +3224,9 @@ void ApiMessageSerializer::AddBaseObjects() {
}
void ApiMessageDeserializer::AddBaseObjects() {
AddBaseObject(PredefinedCObjects::cobj_null());
AddBaseObject(&cobj_null);
AddBaseObject(&cobj_sentinel);
AddBaseObject(PredefinedCObjects::cobj_empty_array());
AddBaseObject(&cobj_empty_array);
AddBaseObject(&cobj_dynamic_type);
AddBaseObject(&cobj_void_type);
AddBaseObject(&cobj_empty_type_arguments);
+4 -5
View File
@@ -896,9 +896,8 @@ class Object {
template <typename FieldType, std::memory_order order>
FieldType LoadNonPointer(const FieldType* addr) const {
return reinterpret_cast<std::atomic<FieldType>*>(
const_cast<FieldType*>(addr))
->load(order);
return std::atomic_ref<FieldType>(*const_cast<FieldType*>(addr))
.load(order);
}
// Needs two template arguments to allow assigning enums to fixed-size ints.
@@ -913,8 +912,8 @@ class Object {
void StoreNonPointer(const FieldType* addr, ValueType value) const {
// Can't use Contains, as it uses tags_, which is set through this method.
ASSERT(reinterpret_cast<uword>(addr) >= UntaggedObject::ToAddr(ptr()));
reinterpret_cast<std::atomic<FieldType>*>(const_cast<FieldType*>(addr))
->store(value, order);
std::atomic_ref<FieldType>(*const_cast<FieldType*>(addr))
.store(value, order);
}
// Provides non-const access to non-pointer fields within the object. Such
+22 -32
View File
@@ -602,8 +602,7 @@ class UntaggedObject {
}
template <typename type, std::memory_order order>
type LoadPointer(type const* addr) const {
return reinterpret_cast<std::atomic<type>*>(const_cast<type*>(addr))
->load(order);
return std::atomic_ref<type>(*const_cast<type*>(addr)).load(order);
}
template <typename type, typename compressed_type>
type LoadCompressedPointer(compressed_type const* addr) const {
@@ -612,9 +611,9 @@ class UntaggedObject {
}
template <typename type, typename compressed_type, std::memory_order order>
type LoadCompressedPointer(compressed_type const* addr) const {
compressed_type v = reinterpret_cast<std::atomic<compressed_type>*>(
const_cast<compressed_type*>(addr))
->load(order);
compressed_type v =
std::atomic_ref<compressed_type>(*const_cast<compressed_type*>(addr))
.load(order);
return static_cast<type>(v.Decompress(heap_base()));
}
template <typename type, typename compressed_type>
@@ -638,8 +637,7 @@ class UntaggedObject {
}
template <typename type, std::memory_order order>
void StorePointer(type const* addr, type value) {
reinterpret_cast<std::atomic<type>*>(const_cast<type*>(addr))
->store(value, order);
std::atomic_ref<type>(*const_cast<type*>(addr)).store(value, order);
if (value.IsHeapObject()) {
CheckHeapPointerStore(value, Thread::Current());
}
@@ -654,9 +652,8 @@ class UntaggedObject {
}
template <typename type, typename compressed_type, std::memory_order order>
void StoreCompressedPointer(compressed_type const* addr, type value) {
reinterpret_cast<std::atomic<compressed_type>*>(
const_cast<compressed_type*>(addr))
->store(static_cast<compressed_type>(value), order);
std::atomic_ref<compressed_type>(*const_cast<compressed_type*>(addr))
.store(static_cast<compressed_type>(value), order);
if (value.IsHeapObject()) {
CheckHeapPointerStore(value, Thread::Current());
}
@@ -698,8 +695,7 @@ class UntaggedObject {
}
template <typename type, std::memory_order order, typename value_type = type>
void StoreArrayPointer(type const* addr, value_type value) {
reinterpret_cast<std::atomic<type>*>(const_cast<type*>(addr))
->store(type(value), order);
std::atomic_ref<type>(*const_cast<type*>(addr)).store(type(value), order);
if (value->IsHeapObject()) {
CheckArrayPointerStore(addr, value, Thread::Current());
}
@@ -722,9 +718,8 @@ class UntaggedObject {
}
template <typename type, typename compressed_type, std::memory_order order>
void StoreCompressedArrayPointer(compressed_type const* addr, type value) {
reinterpret_cast<std::atomic<compressed_type>*>(
const_cast<compressed_type*>(addr))
->store(static_cast<compressed_type>(value), order);
std::atomic_ref<compressed_type>(*const_cast<compressed_type*>(addr))
.store(static_cast<compressed_type>(value), order);
if (value->IsHeapObject()) {
CheckArrayPointerStore(addr, value, Thread::Current());
}
@@ -734,9 +729,8 @@ class UntaggedObject {
void StoreCompressedArrayPointer(compressed_type const* addr,
type value,
Thread* thread) {
reinterpret_cast<std::atomic<compressed_type>*>(
const_cast<compressed_type*>(addr))
->store(static_cast<compressed_type>(value), order);
std::atomic_ref<compressed_type>(*const_cast<compressed_type*>(addr))
.store(static_cast<compressed_type>(value), order);
if (value->IsHeapObject()) {
CheckArrayPointerStore(addr, value, thread);
}
@@ -757,9 +751,8 @@ class UntaggedObject {
std::memory_order order = std::memory_order_relaxed>
type ExchangeCompressedPointer(compressed_type const* addr, type value) {
compressed_type previous_value =
reinterpret_cast<std::atomic<compressed_type>*>(
const_cast<compressed_type*>(addr))
->exchange(static_cast<compressed_type>(value), order);
std::atomic_ref<compressed_type>(*const_cast<compressed_type*>(addr))
.exchange(static_cast<compressed_type>(value), order);
if (value.IsHeapObject()) {
CheckHeapPointerStore(value, Thread::Current());
}
@@ -771,8 +764,7 @@ class UntaggedObject {
}
template <std::memory_order order>
SmiPtr LoadSmi(SmiPtr const* addr) const {
return reinterpret_cast<std::atomic<SmiPtr>*>(const_cast<SmiPtr*>(addr))
->load(order);
return std::atomic_ref<SmiPtr>(*const_cast<SmiPtr*>(addr)).load(order);
}
SmiPtr LoadCompressedSmi(CompressedSmiPtr const* addr) const {
return static_cast<SmiPtr>(
@@ -780,10 +772,10 @@ class UntaggedObject {
}
template <std::memory_order order>
SmiPtr LoadCompressedSmi(CompressedSmiPtr const* addr) const {
return static_cast<SmiPtr>(reinterpret_cast<std::atomic<CompressedSmiPtr>*>(
const_cast<CompressedSmiPtr*>(addr))
->load(order)
.DecompressSmi());
return static_cast<SmiPtr>(
std::atomic_ref<CompressedSmiPtr>(*const_cast<CompressedSmiPtr*>(addr))
.load(order)
.DecompressSmi());
}
// Use for storing into an explicitly Smi-typed field of an object
@@ -794,17 +786,15 @@ class UntaggedObject {
}
template <typename type, std::memory_order order>
void StoreSmi(type const* addr, type value) {
reinterpret_cast<std::atomic<type>*>(const_cast<type*>(addr))
->store(value, order);
std::atomic_ref<type>(*const_cast<type*>(addr)).store(value, order);
}
void StoreCompressedSmi(CompressedSmiPtr const* addr, SmiPtr value) {
*const_cast<CompressedSmiPtr*>(addr) = value;
}
template <std::memory_order order>
void StoreCompressedSmi(CompressedSmiPtr const* addr, SmiPtr value) {
reinterpret_cast<std::atomic<CompressedSmiPtr>*>(
const_cast<CompressedSmiPtr*>(addr))
->store(static_cast<CompressedSmiPtr>(value), order);
std::atomic_ref<CompressedSmiPtr>(*const_cast<CompressedSmiPtr*>(addr))
.store(static_cast<CompressedSmiPtr>(value), order);
}
private:
+8 -8
View File
@@ -2560,15 +2560,15 @@ void Simulator::DecodeAtomicMemory(Instr* instr) {
if (size == 3) {
uint64_t in = get_register(rs, R31IsZR);
auto addr =
reinterpret_cast<std::atomic<uint64_t>*>(get_register(rn, R31IsSP));
auto addr = std::atomic_ref(
*reinterpret_cast<uint64_t*>(get_register(rn, R31IsSP)));
uint64_t out;
switch (opc) {
case 1:
out = addr->fetch_and(~in, order);
out = addr.fetch_and(~in, order);
break;
case 3:
out = addr->fetch_or(in, order);
out = addr.fetch_or(in, order);
break;
default:
UNIMPLEMENTED();
@@ -2577,15 +2577,15 @@ void Simulator::DecodeAtomicMemory(Instr* instr) {
} else if (size == 2) {
ASSERT(size == 2);
uint32_t in = get_wregister(rs, R31IsZR);
auto addr =
reinterpret_cast<std::atomic<uint32_t>*>(get_register(rn, R31IsSP));
auto addr = std::atomic_ref(
*reinterpret_cast<uint32_t*>(get_register(rn, R31IsSP)));
uint32_t out;
switch (opc) {
case 1:
out = addr->fetch_and(~in, order);
out = addr.fetch_and(~in, order);
break;
case 3:
out = addr->fetch_or(in, order);
out = addr.fetch_or(in, order);
break;
default:
UNIMPLEMENTED();
+8 -32
View File
@@ -26,20 +26,12 @@ class DirectSimulatorMemory {
template <typename T>
T Load(uword addr, std::memory_order order) {
// TODO(42074): Once we switch to C++20 we should change this to use use
// `std::atomic_ref<T>` which supports performing atomic operations on
// non-atomic data.
static_assert(sizeof(std::atomic<T>) == sizeof(T));
return reinterpret_cast<std::atomic<T>*>(addr)->load(order);
return std::atomic_ref(*reinterpret_cast<T*>(addr)).load(order);
}
template <typename T>
void Store(uword addr, T value, std::memory_order order) {
// TODO(42074): Once we switch to C++20 we should change this to use use
// `std::atomic_ref<T>` which supports performing atomic operations on
// non-atomic data.
static_assert(sizeof(std::atomic<T>) == sizeof(T));
reinterpret_cast<std::atomic<T>*>(addr)->store(value, order);
std::atomic_ref(*reinterpret_cast<T*>(addr)).store(value, order);
}
template <typename T>
@@ -47,12 +39,8 @@ class DirectSimulatorMemory {
T& old_value,
T value,
std::memory_order order) {
// TODO(42074): Once we switch to C++20 we should change this to use use
// `std::atomic_ref<T>` which supports performing atomic operations on
// non-atomic data.
static_assert(sizeof(std::atomic<T>) == sizeof(T));
return reinterpret_cast<std::atomic<T>*>(addr)->compare_exchange_weak(
old_value, value, order);
return std::atomic_ref(*reinterpret_cast<T*>(addr))
.compare_exchange_weak(old_value, value, order);
}
void FlushAddress(uword addr) {}
@@ -112,21 +100,13 @@ class BufferedSimulatorMemory {
template <typename T>
T Load(uword addr, std::memory_order order) {
FlushAddress(addr);
// TODO(42074): Once we switch to C++20 we should change this to use use
// `std::atomic_ref<T>` which supports performing atomic operations on
// non-atomic data.
static_assert(sizeof(std::atomic<T>) == sizeof(T));
return reinterpret_cast<std::atomic<T>*>(addr)->load(order);
return std::atomic_ref(*reinterpret_cast<T*>(addr)).load(order);
}
template <typename T>
void Store(uword addr, T value, std::memory_order order) {
FlushAddress(addr);
// TODO(42074): Once we switch to C++20 we should change this to use use
// `std::atomic_ref<T>` which supports performing atomic operations on
// non-atomic data.
static_assert(sizeof(std::atomic<T>) == sizeof(T));
reinterpret_cast<std::atomic<T>*>(addr)->store(value, order);
std::atomic_ref(*reinterpret_cast<T*>(addr)).store(value, order);
}
template <typename T>
@@ -135,12 +115,8 @@ class BufferedSimulatorMemory {
T value,
std::memory_order order) {
FlushAddress(addr);
// TODO(42074): Once we switch to C++20 we should change this to use use
// `std::atomic_ref<T>` which supports performing atomic operations on
// non-atomic data.
static_assert(sizeof(std::atomic<T>) == sizeof(T));
return reinterpret_cast<std::atomic<T>*>(addr)->compare_exchange_weak(
old_value, value, order);
return std::atomic_ref<T>(*reinterpret_cast<T*>(addr))
.compare_exchange_weak(old_value, value, order);
}
void FlushAddress(uword addr) {
+24 -22
View File
@@ -2398,9 +2398,9 @@ void Simulator::InterpretAMOSWAP(Instr instr) {
Fault("Misaligned atomic memory operation");
}
memory_.FlushAddress(addr);
std::atomic<type>* atomic = reinterpret_cast<std::atomic<type>*>(addr);
std::atomic_ref<type> atomic(*reinterpret_cast<type*>(addr));
type desired = get_xreg(instr.rs2());
type result = atomic->exchange(desired, instr.memory_order());
type result = atomic.exchange(desired, instr.memory_order());
set_xreg(instr.rd(), sign_extend(result));
}
@@ -2411,9 +2411,9 @@ void Simulator::InterpretAMOADD(Instr instr) {
Fault("Misaligned atomic memory operation");
}
memory_.FlushAddress(addr);
std::atomic<type>* atomic = reinterpret_cast<std::atomic<type>*>(addr);
std::atomic_ref<type> atomic(*reinterpret_cast<type*>(addr));
type arg = get_xreg(instr.rs2());
type result = atomic->fetch_add(arg, instr.memory_order());
type result = atomic.fetch_add(arg, instr.memory_order());
set_xreg(instr.rd(), sign_extend(result));
}
@@ -2424,9 +2424,9 @@ void Simulator::InterpretAMOXOR(Instr instr) {
Fault("Misaligned atomic memory operation");
}
memory_.FlushAddress(addr);
std::atomic<type>* atomic = reinterpret_cast<std::atomic<type>*>(addr);
std::atomic_ref<type> atomic(*reinterpret_cast<type*>(addr));
type arg = get_xreg(instr.rs2());
type result = atomic->fetch_xor(arg, instr.memory_order());
type result = atomic.fetch_xor(arg, instr.memory_order());
set_xreg(instr.rd(), sign_extend(result));
}
@@ -2437,9 +2437,9 @@ void Simulator::InterpretAMOAND(Instr instr) {
Fault("Misaligned atomic memory operation");
}
memory_.FlushAddress(addr);
std::atomic<type>* atomic = reinterpret_cast<std::atomic<type>*>(addr);
std::atomic_ref<type> atomic(*reinterpret_cast<type*>(addr));
type arg = get_xreg(instr.rs2());
type result = atomic->fetch_and(arg, instr.memory_order());
type result = atomic.fetch_and(arg, instr.memory_order());
set_xreg(instr.rd(), sign_extend(result));
}
@@ -2450,9 +2450,9 @@ void Simulator::InterpretAMOOR(Instr instr) {
Fault("Misaligned atomic memory operation");
}
memory_.FlushAddress(addr);
std::atomic<type>* atomic = reinterpret_cast<std::atomic<type>*>(addr);
std::atomic_ref<type> atomic(*reinterpret_cast<type*>(addr));
type arg = get_xreg(instr.rs2());
type result = atomic->fetch_or(arg, instr.memory_order());
type result = atomic.fetch_or(arg, instr.memory_order());
set_xreg(instr.rd(), sign_extend(result));
}
@@ -2463,14 +2463,15 @@ void Simulator::InterpretAMOMIN(Instr instr) {
Fault("Misaligned atomic memory operation");
}
memory_.FlushAddress(addr);
std::atomic<type>* atomic = reinterpret_cast<std::atomic<type>*>(addr);
type expected = atomic->load(std::memory_order_relaxed);
// TODO(c++26): fetch_max
std::atomic_ref<type> atomic(*reinterpret_cast<type*>(addr));
type expected = atomic.load(std::memory_order_relaxed);
type compare = get_xreg(instr.rs2());
type desired;
do {
desired = expected < compare ? expected : compare;
} while (
!atomic->compare_exchange_weak(expected, desired, instr.memory_order()));
!atomic.compare_exchange_weak(expected, desired, instr.memory_order()));
set_xreg(instr.rd(), sign_extend(expected));
}
@@ -2481,14 +2482,15 @@ void Simulator::InterpretAMOMAX(Instr instr) {
Fault("Misaligned atomic memory operation");
}
memory_.FlushAddress(addr);
std::atomic<type>* atomic = reinterpret_cast<std::atomic<type>*>(addr);
type expected = atomic->load(std::memory_order_relaxed);
// TODO(c++26): fetch_max
std::atomic_ref<type> atomic(*reinterpret_cast<type*>(addr));
type expected = atomic.load(std::memory_order_relaxed);
type compare = get_xreg(instr.rs2());
type desired;
do {
desired = expected > compare ? expected : compare;
} while (
!atomic->compare_exchange_weak(expected, desired, instr.memory_order()));
!atomic.compare_exchange_weak(expected, desired, instr.memory_order()));
set_xreg(instr.rd(), sign_extend(expected));
}
@@ -2499,8 +2501,8 @@ void Simulator::InterpretLOADORDERED(Instr instr) {
Fault("Misaligned atomic memory operation");
}
memory_.FlushAddress(addr);
std::atomic<type>* atomic = reinterpret_cast<std::atomic<type>*>(addr);
type value = atomic->load(instr.memory_order());
std::atomic_ref<type> atomic(*reinterpret_cast<type*>(addr));
type value = atomic.load(instr.memory_order());
set_xreg(instr.rd(), sign_extend(value));
}
@@ -2512,8 +2514,8 @@ void Simulator::InterpretSTOREORDERED(Instr instr) {
}
memory_.FlushAddress(addr);
type value = get_xreg(instr.rs2());
std::atomic<type>* atomic = reinterpret_cast<std::atomic<type>*>(addr);
atomic->store(value, instr.memory_order());
std::atomic_ref<type> atomic(*reinterpret_cast<type*>(addr));
atomic.store(value, instr.memory_order());
}
template <typename type>
@@ -2524,8 +2526,8 @@ void Simulator::InterpretAMOCAS(Instr instr) {
}
type expected = get_xreg(instr.rd());
type desired = get_xreg(instr.rs2());
std::atomic<type>* atomic = reinterpret_cast<std::atomic<type>*>(addr);
atomic->compare_exchange_weak(expected, desired, instr.memory_order());
std::atomic_ref<type> atomic(*reinterpret_cast<type*>(addr));
atomic.compare_exchange_weak(expected, desired, instr.memory_order());
set_xreg(instr.rd(), expected);
}