commit
73e535e112
|
@ -6699,7 +6699,7 @@ async fn func(y: *i32) void {
|
||||||
This builtin function atomically dereferences a pointer and returns the value.
|
This builtin function atomically dereferences a pointer and returns the value.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
{#syntax#}T{#endsyntax#} must be a pointer type, a {#syntax#}bool{#endsyntax#}
|
{#syntax#}T{#endsyntax#} must be a pointer type, a {#syntax#}bool{#endsyntax#}, a float,
|
||||||
an integer whose bit count meets these requirements:
|
an integer whose bit count meets these requirements:
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -6734,7 +6734,7 @@ async fn func(y: *i32) void {
|
||||||
Supported operations:
|
Supported operations:
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>{#syntax#}.Xchg{#endsyntax#} - stores the operand unmodified.</li>
|
<li>{#syntax#}.Xchg{#endsyntax#} - stores the operand unmodified. Supports enums, integers and floats.</li>
|
||||||
<li>{#syntax#}.Add{#endsyntax#} - for integers, twos complement wraparound addition.
|
<li>{#syntax#}.Add{#endsyntax#} - for integers, twos complement wraparound addition.
|
||||||
Also supports {#link|Floats#}.</li>
|
Also supports {#link|Floats#}.</li>
|
||||||
<li>{#syntax#}.Sub{#endsyntax#} - for integers, twos complement wraparound subtraction.
|
<li>{#syntax#}.Sub{#endsyntax#} - for integers, twos complement wraparound subtraction.
|
||||||
|
@ -6753,7 +6753,7 @@ async fn func(y: *i32) void {
|
||||||
This builtin function atomically stores a value.
|
This builtin function atomically stores a value.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
{#syntax#}T{#endsyntax#} must be a pointer type, a {#syntax#}bool{#endsyntax#}
|
{#syntax#}T{#endsyntax#} must be a pointer type, a {#syntax#}bool{#endsyntax#}, a float,
|
||||||
an integer whose bit count meets these requirements:
|
an integer whose bit count meets these requirements:
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
|
|
|
@ -5132,19 +5132,21 @@ static LLVMAtomicOrdering to_LLVMAtomicOrdering(AtomicOrder atomic_order) {
|
||||||
zig_unreachable();
|
zig_unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
static LLVMAtomicRMWBinOp to_LLVMAtomicRMWBinOp(AtomicRmwOp op, bool is_signed) {
|
static enum ZigLLVM_AtomicRMWBinOp to_ZigLLVMAtomicRMWBinOp(AtomicRmwOp op, bool is_signed, bool is_float) {
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case AtomicRmwOp_xchg: return LLVMAtomicRMWBinOpXchg;
|
case AtomicRmwOp_xchg: return ZigLLVMAtomicRMWBinOpXchg;
|
||||||
case AtomicRmwOp_add: return LLVMAtomicRMWBinOpAdd;
|
case AtomicRmwOp_add:
|
||||||
case AtomicRmwOp_sub: return LLVMAtomicRMWBinOpSub;
|
return is_float ? ZigLLVMAtomicRMWBinOpFAdd : ZigLLVMAtomicRMWBinOpAdd;
|
||||||
case AtomicRmwOp_and: return LLVMAtomicRMWBinOpAnd;
|
case AtomicRmwOp_sub:
|
||||||
case AtomicRmwOp_nand: return LLVMAtomicRMWBinOpNand;
|
return is_float ? ZigLLVMAtomicRMWBinOpFSub : ZigLLVMAtomicRMWBinOpSub;
|
||||||
case AtomicRmwOp_or: return LLVMAtomicRMWBinOpOr;
|
case AtomicRmwOp_and: return ZigLLVMAtomicRMWBinOpAnd;
|
||||||
case AtomicRmwOp_xor: return LLVMAtomicRMWBinOpXor;
|
case AtomicRmwOp_nand: return ZigLLVMAtomicRMWBinOpNand;
|
||||||
|
case AtomicRmwOp_or: return ZigLLVMAtomicRMWBinOpOr;
|
||||||
|
case AtomicRmwOp_xor: return ZigLLVMAtomicRMWBinOpXor;
|
||||||
case AtomicRmwOp_max:
|
case AtomicRmwOp_max:
|
||||||
return is_signed ? LLVMAtomicRMWBinOpMax : LLVMAtomicRMWBinOpUMax;
|
return is_signed ? ZigLLVMAtomicRMWBinOpMax : ZigLLVMAtomicRMWBinOpUMax;
|
||||||
case AtomicRmwOp_min:
|
case AtomicRmwOp_min:
|
||||||
return is_signed ? LLVMAtomicRMWBinOpMin : LLVMAtomicRMWBinOpUMin;
|
return is_signed ? ZigLLVMAtomicRMWBinOpMin : ZigLLVMAtomicRMWBinOpUMin;
|
||||||
}
|
}
|
||||||
zig_unreachable();
|
zig_unreachable();
|
||||||
}
|
}
|
||||||
|
@ -5738,25 +5740,26 @@ static LLVMValueRef ir_render_atomic_rmw(CodeGen *g, IrExecutable *executable,
|
||||||
{
|
{
|
||||||
bool is_signed;
|
bool is_signed;
|
||||||
ZigType *operand_type = instruction->operand->value->type;
|
ZigType *operand_type = instruction->operand->value->type;
|
||||||
|
bool is_float = operand_type->id == ZigTypeIdFloat;
|
||||||
if (operand_type->id == ZigTypeIdInt) {
|
if (operand_type->id == ZigTypeIdInt) {
|
||||||
is_signed = operand_type->data.integral.is_signed;
|
is_signed = operand_type->data.integral.is_signed;
|
||||||
} else {
|
} else {
|
||||||
is_signed = false;
|
is_signed = false;
|
||||||
}
|
}
|
||||||
LLVMAtomicRMWBinOp op = to_LLVMAtomicRMWBinOp(instruction->resolved_op, is_signed);
|
enum ZigLLVM_AtomicRMWBinOp op = to_ZigLLVMAtomicRMWBinOp(instruction->resolved_op, is_signed, is_float);
|
||||||
LLVMAtomicOrdering ordering = to_LLVMAtomicOrdering(instruction->resolved_ordering);
|
LLVMAtomicOrdering ordering = to_LLVMAtomicOrdering(instruction->resolved_ordering);
|
||||||
LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr);
|
LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr);
|
||||||
LLVMValueRef operand = ir_llvm_value(g, instruction->operand);
|
LLVMValueRef operand = ir_llvm_value(g, instruction->operand);
|
||||||
|
|
||||||
if (get_codegen_ptr_type(operand_type) == nullptr) {
|
if (get_codegen_ptr_type(operand_type) == nullptr) {
|
||||||
return LLVMBuildAtomicRMW(g->builder, op, ptr, operand, ordering, g->is_single_threaded);
|
return ZigLLVMBuildAtomicRMW(g->builder, op, ptr, operand, ordering, g->is_single_threaded);
|
||||||
}
|
}
|
||||||
|
|
||||||
// it's a pointer but we need to treat it as an int
|
// it's a pointer but we need to treat it as an int
|
||||||
LLVMValueRef casted_ptr = LLVMBuildBitCast(g->builder, ptr,
|
LLVMValueRef casted_ptr = LLVMBuildBitCast(g->builder, ptr,
|
||||||
LLVMPointerType(g->builtin_types.entry_usize->llvm_type, 0), "");
|
LLVMPointerType(g->builtin_types.entry_usize->llvm_type, 0), "");
|
||||||
LLVMValueRef casted_operand = LLVMBuildPtrToInt(g->builder, operand, g->builtin_types.entry_usize->llvm_type, "");
|
LLVMValueRef casted_operand = LLVMBuildPtrToInt(g->builder, operand, g->builtin_types.entry_usize->llvm_type, "");
|
||||||
LLVMValueRef uncasted_result = LLVMBuildAtomicRMW(g->builder, op, casted_ptr, casted_operand, ordering,
|
LLVMValueRef uncasted_result = ZigLLVMBuildAtomicRMW(g->builder, op, casted_ptr, casted_operand, ordering,
|
||||||
g->is_single_threaded);
|
g->is_single_threaded);
|
||||||
return LLVMBuildIntToPtr(g->builder, uncasted_result, get_llvm_type(g, operand_type), "");
|
return LLVMBuildIntToPtr(g->builder, uncasted_result, get_llvm_type(g, operand_type), "");
|
||||||
}
|
}
|
||||||
|
|
20
src/ir.cpp
20
src/ir.cpp
|
@ -23959,6 +23959,12 @@ static IrInstruction *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructi
|
||||||
if (type_is_invalid(operand_type))
|
if (type_is_invalid(operand_type))
|
||||||
return ira->codegen->invalid_instruction;
|
return ira->codegen->invalid_instruction;
|
||||||
|
|
||||||
|
if (operand_type->id == ZigTypeIdFloat) {
|
||||||
|
ir_add_error(ira, instruction->type_value->child,
|
||||||
|
buf_sprintf("expected integer, enum or pointer type, found '%s'", buf_ptr(&operand_type->name)));
|
||||||
|
return ira->codegen->invalid_instruction;
|
||||||
|
}
|
||||||
|
|
||||||
IrInstruction *ptr = instruction->ptr->child;
|
IrInstruction *ptr = instruction->ptr->child;
|
||||||
if (type_is_invalid(ptr->value->type))
|
if (type_is_invalid(ptr->value->type))
|
||||||
return ira->codegen->invalid_instruction;
|
return ira->codegen->invalid_instruction;
|
||||||
|
@ -27440,9 +27446,17 @@ static ZigType *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op
|
||||||
buf_sprintf("%" PRIu32 "-bit enum tag type is not a power of 2", int_type->data.integral.bit_count));
|
buf_sprintf("%" PRIu32 "-bit enum tag type is not a power of 2", int_type->data.integral.bit_count));
|
||||||
return ira->codegen->builtin_types.entry_invalid;
|
return ira->codegen->builtin_types.entry_invalid;
|
||||||
}
|
}
|
||||||
|
} else if (operand_type->id == ZigTypeIdFloat) {
|
||||||
|
uint32_t max_atomic_bits = target_arch_largest_atomic_bits(ira->codegen->zig_target->arch);
|
||||||
|
if (operand_type->data.floating.bit_count > max_atomic_bits) {
|
||||||
|
ir_add_error(ira, op,
|
||||||
|
buf_sprintf("expected %" PRIu32 "-bit float or smaller, found %" PRIu32 "-bit float",
|
||||||
|
max_atomic_bits, (uint32_t) operand_type->data.floating.bit_count));
|
||||||
|
return ira->codegen->builtin_types.entry_invalid;
|
||||||
|
}
|
||||||
} else if (get_codegen_ptr_type(operand_type) == nullptr) {
|
} else if (get_codegen_ptr_type(operand_type) == nullptr) {
|
||||||
ir_add_error(ira, op,
|
ir_add_error(ira, op,
|
||||||
buf_sprintf("expected integer, enum or pointer type, found '%s'", buf_ptr(&operand_type->name)));
|
buf_sprintf("expected integer, float, enum or pointer type, found '%s'", buf_ptr(&operand_type->name)));
|
||||||
return ira->codegen->builtin_types.entry_invalid;
|
return ira->codegen->builtin_types.entry_invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27477,6 +27491,10 @@ static IrInstruction *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstru
|
||||||
ir_add_error(ira, instruction->op,
|
ir_add_error(ira, instruction->op,
|
||||||
buf_sprintf("@atomicRmw on enum only works with .Xchg"));
|
buf_sprintf("@atomicRmw on enum only works with .Xchg"));
|
||||||
return ira->codegen->invalid_instruction;
|
return ira->codegen->invalid_instruction;
|
||||||
|
} else if (operand_type->id == ZigTypeIdFloat && op > AtomicRmwOp_sub) {
|
||||||
|
ir_add_error(ira, instruction->op,
|
||||||
|
buf_sprintf("@atomicRmw with float only works with .Xchg, .Add and .Sub"));
|
||||||
|
return ira->codegen->invalid_instruction;
|
||||||
}
|
}
|
||||||
|
|
||||||
IrInstruction *operand = instruction->operand->child;
|
IrInstruction *operand = instruction->operand->child;
|
||||||
|
|
|
@ -1096,6 +1096,56 @@ bool ZigLLDLink(ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static AtomicRMWInst::BinOp toLLVMRMWBinOp(enum ZigLLVM_AtomicRMWBinOp BinOp) {
|
||||||
|
switch (BinOp) {
|
||||||
|
default:
|
||||||
|
case ZigLLVMAtomicRMWBinOpXchg: return AtomicRMWInst::Xchg;
|
||||||
|
case ZigLLVMAtomicRMWBinOpAdd: return AtomicRMWInst::Add;
|
||||||
|
case ZigLLVMAtomicRMWBinOpSub: return AtomicRMWInst::Sub;
|
||||||
|
case ZigLLVMAtomicRMWBinOpAnd: return AtomicRMWInst::And;
|
||||||
|
case ZigLLVMAtomicRMWBinOpNand: return AtomicRMWInst::Nand;
|
||||||
|
case ZigLLVMAtomicRMWBinOpOr: return AtomicRMWInst::Or;
|
||||||
|
case ZigLLVMAtomicRMWBinOpXor: return AtomicRMWInst::Xor;
|
||||||
|
case ZigLLVMAtomicRMWBinOpMax: return AtomicRMWInst::Max;
|
||||||
|
case ZigLLVMAtomicRMWBinOpMin: return AtomicRMWInst::Min;
|
||||||
|
case ZigLLVMAtomicRMWBinOpUMax: return AtomicRMWInst::UMax;
|
||||||
|
case ZigLLVMAtomicRMWBinOpUMin: return AtomicRMWInst::UMin;
|
||||||
|
case ZigLLVMAtomicRMWBinOpFAdd: return AtomicRMWInst::FAdd;
|
||||||
|
case ZigLLVMAtomicRMWBinOpFSub: return AtomicRMWInst::FSub;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static AtomicOrdering toLLVMOrdering(LLVMAtomicOrdering Ordering) {
|
||||||
|
switch (Ordering) {
|
||||||
|
default:
|
||||||
|
case LLVMAtomicOrderingNotAtomic: return AtomicOrdering::NotAtomic;
|
||||||
|
case LLVMAtomicOrderingUnordered: return AtomicOrdering::Unordered;
|
||||||
|
case LLVMAtomicOrderingMonotonic: return AtomicOrdering::Monotonic;
|
||||||
|
case LLVMAtomicOrderingAcquire: return AtomicOrdering::Acquire;
|
||||||
|
case LLVMAtomicOrderingRelease: return AtomicOrdering::Release;
|
||||||
|
case LLVMAtomicOrderingAcquireRelease: return AtomicOrdering::AcquireRelease;
|
||||||
|
case LLVMAtomicOrderingSequentiallyConsistent: return AtomicOrdering::SequentiallyConsistent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline LLVMAttributeRef wrap(Attribute Attr) {
|
||||||
|
return reinterpret_cast<LLVMAttributeRef>(Attr.getRawPointer());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Attribute unwrap(LLVMAttributeRef Attr) {
|
||||||
|
return Attribute::fromRawPointer(Attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
LLVMValueRef ZigLLVMBuildAtomicRMW(LLVMBuilderRef B, enum ZigLLVM_AtomicRMWBinOp op,
|
||||||
|
LLVMValueRef PTR, LLVMValueRef Val,
|
||||||
|
LLVMAtomicOrdering ordering, LLVMBool singleThread)
|
||||||
|
{
|
||||||
|
AtomicRMWInst::BinOp intop = toLLVMRMWBinOp(op);
|
||||||
|
return wrap(unwrap(B)->CreateAtomicRMW(intop, unwrap(PTR),
|
||||||
|
unwrap(Val), toLLVMOrdering(ordering),
|
||||||
|
singleThread ? SyncScope::SingleThread : SyncScope::System));
|
||||||
|
}
|
||||||
|
|
||||||
static_assert((Triple::ArchType)ZigLLVM_UnknownArch == Triple::UnknownArch, "");
|
static_assert((Triple::ArchType)ZigLLVM_UnknownArch == Triple::UnknownArch, "");
|
||||||
static_assert((Triple::ArchType)ZigLLVM_arm == Triple::arm, "");
|
static_assert((Triple::ArchType)ZigLLVM_arm == Triple::arm, "");
|
||||||
static_assert((Triple::ArchType)ZigLLVM_armeb == Triple::armeb, "");
|
static_assert((Triple::ArchType)ZigLLVM_armeb == Triple::armeb, "");
|
||||||
|
|
|
@ -422,6 +422,26 @@ enum ZigLLVM_ObjectFormatType {
|
||||||
ZigLLVM_XCOFF,
|
ZigLLVM_XCOFF,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum ZigLLVM_AtomicRMWBinOp {
|
||||||
|
ZigLLVMAtomicRMWBinOpXchg,
|
||||||
|
ZigLLVMAtomicRMWBinOpAdd,
|
||||||
|
ZigLLVMAtomicRMWBinOpSub,
|
||||||
|
ZigLLVMAtomicRMWBinOpAnd,
|
||||||
|
ZigLLVMAtomicRMWBinOpNand,
|
||||||
|
ZigLLVMAtomicRMWBinOpOr,
|
||||||
|
ZigLLVMAtomicRMWBinOpXor,
|
||||||
|
ZigLLVMAtomicRMWBinOpMax,
|
||||||
|
ZigLLVMAtomicRMWBinOpMin,
|
||||||
|
ZigLLVMAtomicRMWBinOpUMax,
|
||||||
|
ZigLLVMAtomicRMWBinOpUMin,
|
||||||
|
ZigLLVMAtomicRMWBinOpFAdd,
|
||||||
|
ZigLLVMAtomicRMWBinOpFSub,
|
||||||
|
};
|
||||||
|
|
||||||
|
LLVMValueRef ZigLLVMBuildAtomicRMW(LLVMBuilderRef B, enum ZigLLVM_AtomicRMWBinOp op,
|
||||||
|
LLVMValueRef PTR, LLVMValueRef Val,
|
||||||
|
LLVMAtomicOrdering ordering, LLVMBool singleThread);
|
||||||
|
|
||||||
#define ZigLLVM_DIFlags_Zero 0U
|
#define ZigLLVM_DIFlags_Zero 0U
|
||||||
#define ZigLLVM_DIFlags_Private 1U
|
#define ZigLLVM_DIFlags_Private 1U
|
||||||
#define ZigLLVM_DIFlags_Protected 2U
|
#define ZigLLVM_DIFlags_Protected 2U
|
||||||
|
|
|
@ -31,6 +31,26 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||||
"tmp.zig:3:12: note: destination pointer requires a terminating '0' sentinel",
|
"tmp.zig:3:12: note: destination pointer requires a terminating '0' sentinel",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
cases.add(
|
||||||
|
"cmpxchg with float",
|
||||||
|
\\export fn entry() void {
|
||||||
|
\\ var x: f32 = 0;
|
||||||
|
\\ _ = @cmpxchgWeak(f32, &x, 1, 2, .SeqCst, .SeqCst);
|
||||||
|
\\}
|
||||||
|
, &[_][]const u8{
|
||||||
|
"tmp.zig:3:22: error: expected integer, enum or pointer type, found 'f32'",
|
||||||
|
});
|
||||||
|
|
||||||
|
cases.add(
|
||||||
|
"atomicrmw with float op not .Xchg, .Add or .Sub",
|
||||||
|
\\export fn entry() void {
|
||||||
|
\\ var x: f32 = 0;
|
||||||
|
\\ _ = @atomicRmw(f32, &x, .And, 2, .SeqCst);
|
||||||
|
\\}
|
||||||
|
, &[_][]const u8{
|
||||||
|
"tmp.zig:3:29: error: @atomicRmw with float only works with .Xchg, .Add and .Sub",
|
||||||
|
});
|
||||||
|
|
||||||
cases.add("intToPtr with misaligned address",
|
cases.add("intToPtr with misaligned address",
|
||||||
\\pub fn main() void {
|
\\pub fn main() void {
|
||||||
\\ var y = @intToPtr([*]align(4) u8, 5);
|
\\ var y = @intToPtr([*]align(4) u8, 5);
|
||||||
|
|
|
@ -144,3 +144,20 @@ fn testAtomicStore() void {
|
||||||
@atomicStore(u32, &x, 12345678, .SeqCst);
|
@atomicStore(u32, &x, 12345678, .SeqCst);
|
||||||
expect(@atomicLoad(u32, &x, .SeqCst) == 12345678);
|
expect(@atomicLoad(u32, &x, .SeqCst) == 12345678);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "atomicrmw with floats" {
|
||||||
|
if (builtin.arch == .aarch64 or builtin.arch == .arm)
|
||||||
|
return;
|
||||||
|
testAtomicRmwFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn testAtomicRmwFloat() void {
|
||||||
|
var x: f32 = 0;
|
||||||
|
expect(x == 0);
|
||||||
|
_ = @atomicRmw(f32, &x, .Xchg, 1, .SeqCst);
|
||||||
|
expect(x == 1);
|
||||||
|
_ = @atomicRmw(f32, &x, .Add, 5, .SeqCst);
|
||||||
|
expect(x == 6);
|
||||||
|
_ = @atomicRmw(f32, &x, .Sub, 2, .SeqCst);
|
||||||
|
expect(x == 4);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue