diff --git a/lib/std/atomic.c3 b/lib/std/atomic.c3 index c26cf793e..bbf9534aa 100644 --- a/lib/std/atomic.c3 +++ b/lib/std/atomic.c3 @@ -76,7 +76,7 @@ macro Type Atomic.div(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTEN macro Type Atomic.max(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT) { Type* data = &self.data; - return @atomic_exec(atomic::fetch_div, data, value, ordering); + return @atomic_exec(atomic::fetch_max, data, value, ordering); } macro Type Atomic.min(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT) @@ -85,36 +85,49 @@ macro Type Atomic.min(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTEN return @atomic_exec(atomic::fetch_min, data, value, ordering); } -macro Type Atomic.or(&self, uint value, AtomicOrdering ordering = SEQ_CONSISTENT) @if(!types::is_float(Type)) +macro Type Atomic.or(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT) { Type* data = &self.data; return @atomic_exec(atomic::fetch_or, data, value, ordering); } -fn Type Atomic.xor(&self, uint value, AtomicOrdering ordering = SEQ_CONSISTENT) @if(!types::is_float(Type)) +fn Type Atomic.xor(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT) { Type* data = &self.data; return @atomic_exec(atomic::fetch_xor, data, value, ordering); } -macro Type Atomic.and(&self, uint value, AtomicOrdering ordering = SEQ_CONSISTENT) @if(!types::is_float(Type)) +macro Type Atomic.and(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT) { Type* data = &self.data; return @atomic_exec(atomic::fetch_and, data, value, ordering); } -macro Type Atomic.shift_right(&self, uint amount, AtomicOrdering ordering = SEQ_CONSISTENT) @if(!types::is_float(Type)) +macro Type Atomic.shr(&self, Type amount, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT) { Type* data = &self.data; return @atomic_exec(atomic::fetch_shift_right, data, amount, ordering); } -macro Type Atomic.shift_left(&self, uint amount, AtomicOrdering ordering = SEQ_CONSISTENT) @if(!types::is_float(Type)) +macro Type Atomic.shl(&self, Type amount, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT) { Type* data = &self.data; return @atomic_exec(atomic::fetch_shift_left, data, amount, ordering); } +macro Type Atomic.set(&self, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) == BOOL) +{ + Type* data = &self.data; + return @atomic_exec_no_arg(atomic::flag_set, data, ordering); +} + +macro Type Atomic.clear(&self, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) == BOOL) +{ + Type* data = &self.data; + + return @atomic_exec_no_arg(atomic::flag_clear, data, ordering); +} + macro @atomic_exec(#func, data, value, ordering) @local { switch(ordering) @@ -128,17 +141,57 @@ macro @atomic_exec(#func, data, value, ordering) @local } } +macro @atomic_exec_no_arg(#func, data, ordering) @local +{ + switch(ordering) + { + case RELAXED: return #func(data, RELAXED); + case ACQUIRE: return #func(data, ACQUIRE); + case RELEASE: return #func(data, RELEASE); + case ACQUIRE_RELEASE: return #func(data, ACQUIRE_RELEASE); + case SEQ_CONSISTENT: return #func(data, SEQ_CONSISTENT); + default: unreachable("Ordering may not be non-atomic or unordered."); + } +} + module std::atomic; import std::math; +macro bool @is_native_atomic_value(#value) @private +{ + return is_native_atomic_type($typeof(#value)); +} + +macro bool is_native_atomic_type($Type) +{ + $if $Type.sizeof > void*.sizeof: + return false; + $else + $switch ($Type.kindof) + $case SIGNED_INT: + $case UNSIGNED_INT: + $case POINTER: + $case FLOAT: + $case BOOL: + return true; + $case DISTINCT: + return is_native_atomic_type($typefrom($Type.inner)); + $default: + return false; + $endswitch + $endif +} + <* @param [&in] ptr "the variable or dereferenced pointer to the data." @param [in] y "the value to be added to ptr." @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT" @return "returns the old value of ptr" - @require !$alignment || math::is_power_of_2($alignment) "Alignment must be a power of two." - @require types::is_int($typeof(*ptr)) || types::is_float($typeof(*ptr)) "Only integer/float pointers may be used." + @require !$alignment || math::is_power_of_2($alignment) : "Alignment must be a power of two." + @require $defined(*ptr) : "Expected a pointer" + @require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used." + @require $defined(*ptr + y) : "+ must be defined between the values." @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid." *> macro fetch_add(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0) @@ -151,12 +204,14 @@ macro fetch_add(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatil <* @param [&in] ptr "the variable or dereferenced pointer to the data." - @param [in] y "the value to be added to ptr." + @param [in] y "the value to be subtracted from ptr." @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT" @return "returns the old value of ptr" @require !$alignment || math::is_power_of_2($alignment) "Alignment must be a power of two." - @require types::is_int($typeof(*ptr)) || types::is_float($typeof(*ptr)) "Only integer/float pointers may be used." + @require $defined(*ptr) : "Expected a pointer" + @require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used." + @require $defined(*ptr - y) : "- must be defined between the values." @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid." *> macro fetch_sub(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0) @@ -169,11 +224,13 @@ macro fetch_sub(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatil <* @param [&in] ptr "the variable or dereferenced pointer to the data." - @param [in] y "the value to be added to ptr." + @param [in] y "the value to be multiplied with ptr." @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT" @return "returns the old value of ptr" - @require types::is_int($typeof(*ptr)) || types::is_float($typeof(*ptr)) "Only integer/float pointers may be used." + @require $defined(*ptr) : "Expected a pointer" + @require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used." + @require $defined(*ptr * y) : "* must be defined between the values." @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid." *> macro fetch_mul(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT) @@ -193,24 +250,27 @@ macro fetch_mul(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT) $StorageType storage_old_value; $StorageType storage_new_value; - - do { + do + { storage_old_value = $$atomic_load(storage_ptr, false, $load_ordering.ordinal); old_value = bitcast(storage_old_value, $typeof(*ptr)); new_value = old_value * y; storage_new_value = bitcast(new_value, $StorageType); - } while (mem::compare_exchange(storage_ptr, storage_old_value, storage_new_value, $ordering, $load_ordering) != storage_old_value); + } + while (mem::compare_exchange(storage_ptr, storage_old_value, storage_new_value, $ordering, $load_ordering) != storage_old_value); return old_value; } <* @param [&in] ptr "the variable or dereferenced pointer to the data." - @param [in] y "the value to be added to ptr." + @param [in] y "the value to divide ptr by." @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT" @return "returns the old value of ptr" - @require types::is_int($typeof(*ptr)) || types::is_float($typeof(*ptr)) "Only integer/float pointers may be used." + @require $defined(*ptr) : "Expected a pointer" + @require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used." + @require $defined(*ptr * y) : "/ must be defined between the values." @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid." *> macro fetch_div(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT) @@ -230,153 +290,79 @@ macro fetch_div(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT) $StorageType storage_old_value; $StorageType storage_new_value; - do { + do + { storage_old_value = $$atomic_load(storage_ptr, false, $load_ordering.ordinal); old_value = bitcast(storage_old_value, $typeof(*ptr)); new_value = old_value / y; storage_new_value = bitcast(new_value, $StorageType); - } while (mem::compare_exchange(storage_ptr, storage_old_value, storage_new_value, $ordering, $load_ordering) != storage_old_value); + } + while (mem::compare_exchange(storage_ptr, storage_old_value, storage_new_value, $ordering, $load_ordering) != storage_old_value); return old_value; } <* @param [&in] ptr "the variable or dereferenced pointer to the data." - @param [in] y "the value to be added to ptr." + @param [in] y "the value to perform a bitwise or with." @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT" @return "returns the old value of ptr" @require !$alignment || math::is_power_of_2($alignment) "Alignment must be a power of two." - @require types::is_int($typeof(*ptr)) "Only integer pointers may be used." - @require types::is_int($typeof(y)) "The value for or must be an int" + @require $defined(*ptr) : "Expected a pointer" + @require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used." + @require $defined(*ptr | y) : "| must be defined between the values." @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid." *> macro fetch_or(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0) { - $if types::is_int($typeof(*ptr)): - return $$atomic_fetch_or(ptr, y, $volatile, $ordering.ordinal, $alignment); - $endif - - var $load_ordering = $ordering; - $if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE: - $load_ordering = AtomicOrdering.SEQ_CONSISTENT; - $endif - - var $StorageType = $typefrom(types::lower_to_atomic_compatible_type($typeof(*ptr))); - - $StorageType* storage_ptr = ($StorageType*)ptr; - - $typeof(*ptr) old_value; - $typeof(*ptr) new_value; - - $StorageType storage_old_value; - $StorageType storage_new_value; - $StorageType storage_y = ($StorageType)y; - - do { - storage_old_value = $$atomic_load(storage_ptr, false, $load_ordering.ordinal); - old_value = bitcast(storage_old_value, $typeof(*ptr)); - new_value = storage_old_value | storage_y; - storage_new_value = bitcast(new_value, $StorageType); - } while (mem::compare_exchange(storage_ptr, storage_old_value, storage_new_value, $ordering, $load_ordering) != storage_old_value); - - return old_value; + return $$atomic_fetch_or(ptr, y, $volatile, $ordering.ordinal, $alignment); } <* @param [&in] ptr "the variable or dereferenced pointer to the data." - @param [in] y "the value to be added to ptr." + @param [in] y "the value to perform a bitwise xor with." @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT" @return "returns the old value of ptr" @require !$alignment || math::is_power_of_2($alignment) "Alignment must be a power of two." - @require types::is_int($typeof(*ptr)) "Only integer pointers may be used." - @require types::is_int($typeof(y)) "The value for or must be an int" + @require $defined(*ptr) : "Expected a pointer" + @require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used." + @require $defined(*ptr ^ y) : "^ must be defined between the values." @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid." *> macro fetch_xor(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0) { - $if types::is_int($typeof(*ptr)): - return $$atomic_fetch_xor(ptr, y, $volatile, $ordering.ordinal, $alignment); - $endif - - var $load_ordering = $ordering; - $if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE: - $load_ordering = AtomicOrdering.SEQ_CONSISTENT; - $endif - - var $StorageType = $typefrom(types::lower_to_atomic_compatible_type($typeof(*ptr))); - - $StorageType* storage_ptr = ($StorageType*)ptr; - - $typeof(*ptr) old_value; - $typeof(*ptr) new_value; - - $StorageType storage_old_value; - $StorageType storage_new_value; - $StorageType storage_y = ($StorageType)y; - - do { - storage_old_value = $$atomic_load(storage_ptr, false, $load_ordering.ordinal); - old_value = bitcast(storage_old_value, $typeof(*ptr)); - new_value = storage_old_value ^ storage_y; - storage_new_value = bitcast(new_value, $StorageType); - } while (mem::compare_exchange(storage_ptr, storage_old_value, storage_new_value, $ordering, $load_ordering) != storage_old_value); - - return old_value; + return $$atomic_fetch_xor(ptr, y, $volatile, $ordering.ordinal, $alignment); } <* @param [&in] ptr "the variable or dereferenced pointer to the data." - @param [in] y "the value to be added to ptr." + @param [in] y "the value to perform a bitwise and with." @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT" @return "returns the old value of ptr" @require !$alignment || math::is_power_of_2($alignment) "Alignment must be a power of two." - @require types::is_int($typeof(*ptr)) "Only integer pointers may be used." - @require types::is_int($typeof(y)) "The value for or must be an int" + @require $defined(*ptr) : "Expected a pointer" + @require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used." + @require $defined(*ptr ^ y) : "& must be defined between the values." @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid." *> macro fetch_and(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0) { - $if types::is_int($typeof(*ptr)): - return $$atomic_fetch_and(ptr, y, $volatile, $ordering.ordinal, $alignment); - $endif - - var $load_ordering = $ordering; - $if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE: - $load_ordering = AtomicOrdering.SEQ_CONSISTENT; - $endif - - var $StorageType = $typefrom(types::lower_to_atomic_compatible_type($typeof(*ptr))); - - $StorageType* storage_ptr = ($StorageType*)ptr; - - $typeof(*ptr) old_value; - $typeof(*ptr) new_value; - - $StorageType storage_old_value; - $StorageType storage_new_value; - $StorageType storage_y = ($StorageType)y; - - do { - storage_old_value = $$atomic_load(storage_ptr, false, $load_ordering.ordinal); - old_value = bitcast(storage_old_value, $typeof(*ptr)); - new_value = storage_old_value & storage_y; - storage_new_value = bitcast(new_value, $StorageType); - } while (mem::compare_exchange(storage_ptr, storage_old_value, storage_new_value, $ordering, $load_ordering) != storage_old_value); - - return old_value; + return $$atomic_fetch_and(ptr, y, $volatile, $ordering.ordinal, $alignment); } <* @param [&in] ptr "the variable or dereferenced pointer to the data." - @param [in] y "the value to be added to ptr." + @param [in] y "the value to shift ptr by." @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT" @return "returns the old value of ptr" + @require $defined(*ptr) : "Expected a pointer" + @require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used." @require types::is_int($typeof(*ptr)) "Only integer pointers may be used." - @require types::is_int($typeof(y)) "The value for or must be an int" + @require types::is_int($typeof(y)) "The value for shift right must be an integer" @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid." *> macro fetch_shift_right(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT) @@ -397,24 +383,28 @@ macro fetch_shift_right(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT) $StorageType storage_new_value; $StorageType storage_y = ($StorageType)y; - do { + do + { storage_old_value = $$atomic_load(storage_ptr, false, $load_ordering.ordinal); old_value = bitcast(storage_old_value, $typeof(*ptr)); new_value = storage_old_value >> storage_y; storage_new_value = bitcast(new_value, $StorageType); - } while (mem::compare_exchange(storage_ptr, storage_old_value, storage_new_value, $ordering, $load_ordering) != storage_old_value); + } + while (mem::compare_exchange(storage_ptr, storage_old_value, storage_new_value, $ordering, $load_ordering) != storage_old_value); return old_value; } <* @param [&in] ptr "the variable or dereferenced pointer to the data." - @param [in] y "the value to be added to ptr." + @param [in] y "the value to shift ptr by." @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT" @return "returns the old value of ptr" + @require $defined(*ptr) : "Expected a pointer" + @require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used." @require types::is_int($typeof(*ptr)) "Only integer pointers may be used." - @require types::is_int($typeof(y)) "The value for or must be an int" + @require types::is_int($typeof(y)) "The value for shift left must be an integer" @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid." *> macro fetch_shift_left(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT) @@ -435,12 +425,14 @@ macro fetch_shift_left(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT) $StorageType storage_new_value; $StorageType storage_y = ($StorageType)y; - do { + do + { storage_old_value = $$atomic_load(storage_ptr, false, $load_ordering.ordinal); old_value = bitcast(storage_old_value, $typeof(*ptr)); new_value = storage_old_value << storage_y; storage_new_value = bitcast(new_value, $StorageType); - } while (mem::compare_exchange(storage_ptr, storage_old_value, storage_new_value, $ordering, $load_ordering) != storage_old_value); + } + while (mem::compare_exchange(storage_ptr, storage_old_value, storage_new_value, $ordering, $load_ordering) != storage_old_value); return old_value; } @@ -450,18 +442,26 @@ macro fetch_shift_left(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT) @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT" @return "returns the old value of ptr" - @require types::is_int($typeof(*ptr)) "Only integer pointers may be used." + @require $defined(*ptr) : "Expected a pointer" + @require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used." + @require types::flat_kind($typeof(*ptr)) == BOOL "Only bool pointers may be used." + @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid." *> macro flag_set(ptr, AtomicOrdering $ordering = SEQ_CONSISTENT) { $typeof(*ptr) old_value; $typeof(*ptr) new_value = true; + var $load_ordering = $ordering; + $if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE: + $load_ordering = AtomicOrdering.SEQ_CONSISTENT; + $endif + do + { + old_value = $$atomic_load(ptr, false, $load_ordering.ordinal); + } + while (mem::compare_exchange(ptr, old_value, new_value, $ordering, $load_ordering) != old_value); - do { - old_value = $$atomic_load(ptr, false, $ordering.ordinal); - } while (mem::compare_exchange(ptr, old_value, new_value, $ordering, $load_ordering) != old_value); - return old_value; } @@ -470,28 +470,37 @@ macro flag_set(ptr, AtomicOrdering $ordering = SEQ_CONSISTENT) @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT" @return "returns the old value of ptr" - @require types::is_int($typeof(*ptr)) "Only integer pointers may be used." - @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid." + @require $defined(*ptr) : "Expected a pointer" + @require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used." + @require types::flat_kind($typeof(*ptr)) == BOOL : "Only bool pointers may be used." + @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid." *> macro flag_clear(ptr, AtomicOrdering $ordering = SEQ_CONSISTENT) { $typeof(*ptr) old_value; $typeof(*ptr) new_value = false; - - do { - old_value = $$atomic_load(ptr, false, $ordering.ordinal); - } while (mem::compare_exchange(ptr, old_value, new_value, $ordering, $load_ordering) != old_value); + var $load_ordering = $ordering; + $if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE: + $load_ordering = AtomicOrdering.SEQ_CONSISTENT; + $endif + do + { + old_value = $$atomic_load(ptr, false, $load_ordering.ordinal); + } + while (mem::compare_exchange(ptr, old_value, new_value, $ordering, $load_ordering) != old_value); return old_value; } <* @param [&in] ptr "the variable or dereferenced pointer to the data." - @param [in] y "the value to be added to ptr." + @param [in] y "the value to be compared to ptr." @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT" @return "returns the old value of ptr" - @require types::is_int($typeof(*ptr)) || types::is_float($typeof(*ptr)) "Only integer/float pointers may be used." + @require $defined(*ptr) : "Expected a pointer" + @require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used." + @require $defined(*ptr > y) : "Only values that are comparable with > may be used" @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid." *> macro fetch_max(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0) @@ -504,11 +513,13 @@ macro fetch_max(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatil <* @param [&in] ptr "the variable or dereferenced pointer to the data." - @param [in] y "the value to be added to ptr." + @param [in] y "the value to be compared to ptr." @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT" @return "returns the old value of ptr" - @require types::is_int($typeof(*ptr)) || types::is_float($typeof(*ptr)) "Only integer/float pointers may be used." + @require $defined(*ptr) : "Expected a pointer" + @require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used." + @require $defined(*ptr > y) : "Only values that are comparable with > may be used" @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid." *> macro fetch_min(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0) diff --git a/lib/std/core/types.c3 b/lib/std/core/types.c3 index f42fc8105..41c76dedc 100644 --- a/lib/std/core/types.c3 +++ b/lib/std/core/types.c3 @@ -160,6 +160,24 @@ macro bool is_unsigned($Type) @const $endswitch } +macro typeid flat_type($Type) @const +{ + $if $Type.kindof == DISTINCT: + return flat_type($typefrom($Type.inner)); + $else + return $Type.typeid; + $endif +} + +macro TypeKind flat_kind($Type) @const +{ + $if $Type.kindof == DISTINCT: + return flat_type($typefrom($Type.inner)); + $else + return $Type.kindof; + $endif +} + macro bool is_indexable($Type) @const { return $defined(($Type){}[0]); @@ -267,14 +285,15 @@ macro bool may_load_atomic($Type) @const macro lower_to_atomic_compatible_type($Type) @const { $switch ($Type.kindof) - $case SIGNED_INT: - $case UNSIGNED_INT: - return $Type.typeid; + $case BOOL: + $case SIGNED_INT: + $case UNSIGNED_INT: + return $Type.typeid; $case DISTINCT: return lower_to_atomic_compatible_type($Type.inner); $case FLOAT: $switch ($Type) - $case float16: + $case float16: return ushort.typeid; $case float: return uint.typeid; diff --git a/releasenotes.md b/releasenotes.md index b20109d69..9e7a10831 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -24,6 +24,7 @@ - Post and pre-decrement operators switched places for vector elements #2010. - Aliases were incorrectly considered compile time constants. - FreeBSD libc stat definitions were incorrect. +- Atomic max was incorrect. ### Stdlib changes - `new_*` functions in general moved to version without `new_` prefix. @@ -37,6 +38,7 @@ - Remove Vec2 and other aliases from std::math. Replace `.length_sq()` with `sq_magnitude()` - Change all hash functions to have a common `hash` function. - `@wstring`, `@wstring32`, `@char32` and `@char16` compile time macros added. +- Updates to `Atomic` to handle distinct types and booleans. ## 0.6.8 Change list diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 6c102ca35..cfe685714 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -2763,6 +2763,7 @@ INLINE bool type_is_atomic(Type *type_flat) case TYPE_FAULTTYPE: case TYPE_ANYFAULT: case TYPE_TYPEID: + case TYPE_BOOL: break; default: return false; diff --git a/src/compiler/llvm_codegen_builtins.c b/src/compiler/llvm_codegen_builtins.c index 206b435ec..6ce420224 100644 --- a/src/compiler/llvm_codegen_builtins.c +++ b/src/compiler/llvm_codegen_builtins.c @@ -96,8 +96,7 @@ INLINE void llvm_emit_compare_exchange(GenContext *c, BEValue *result_value, Exp for (int i = 0; i < 3; i++) { llvm_emit_expr(c, &value, args[i]); - llvm_value_rvalue(c, &value); - normal_args[i] = value.value; + normal_args[i] = llvm_load_value_store(c, &value); } Type *type = value.type; bool is_volatile = args[3]->const_expr.b; diff --git a/src/compiler/sema_builtins.c b/src/compiler/sema_builtins.c index da8363d2a..9cb2b3a18 100644 --- a/src/compiler/sema_builtins.c +++ b/src/compiler/sema_builtins.c @@ -232,7 +232,6 @@ static bool sema_expr_analyse_compare_exchange(SemaContext *context, Expr *expr) { RETURN_SEMA_ERROR(arg, "%s may not be used with atomics.", type_quoted_error_string(arg->type)); } - optional = optional || IS_OPTIONAL(args[i]); } for (int i = 3; i < 5; i++) @@ -1047,7 +1046,7 @@ bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *expr) case BUILTIN_ATOMIC_FETCH_XOR: { ASSERT(arg_count == 5); - if (!sema_check_builtin_args(context, args, (BuiltinArg[]) {BA_POINTER, BA_INTEGER}, 2)) return false; + if (!sema_check_builtin_args(context, args, (BuiltinArg[]) {BA_POINTER, BA_BOOLINT}, 2)) return false; Type *original = type_flatten(args[0]->type); if (original != type_voidptr) { diff --git a/test/unit/stdlib/atomic_types.c3 b/test/unit/stdlib/atomic_types.c3 index 009ef8138..207bbe1de 100644 --- a/test/unit/stdlib/atomic_types.c3 +++ b/test/unit/stdlib/atomic_types.c3 @@ -118,6 +118,37 @@ fn void fadd() @test assert(fa.load() == ts.len * 10 * 0.5, "Threads returned %f, expected %f", fa.load(), ts.len * 10 * 0.5); } +fn void fops() @test +{ + fa.store(100); + fa.mul(2); + fa.div(3.4); +} + +fn void iops() @test +{ + a.store(100); + a.mul(3); + a.div(5); + a.shr(1); + a.shl(3); + a.xor(4); + a.or(5); + a.and(6); +} + +fn void bops() @test +{ + Atomic {bool} ab; + ab.store(true); + ab.store(false); + ab.xor(true); + ab.or(false); + ab.store(false); + test::eq(false, ab.set()); + test::eq(true, ab.clear()); + test::eq(false, ab.clear()); +} fn void fsub() @test { Thread[100] ts;