Skip to content

Commit b936064

Browse files
committed
add @atomicLoad builtin
See #174
1 parent 859b10d commit b936064

File tree

7 files changed

+183
-11
lines changed

7 files changed

+183
-11
lines changed

doc/langref.html.in

+20-1
Original file line numberDiff line numberDiff line change
@@ -3880,6 +3880,25 @@ pub fn main() void {
38803880
{#header_open|@ArgType#}
38813881
<p>TODO</p>
38823882
{#header_close#}
3883+
{#header_open|@atomicLoad#}
3884+
<pre><code class="zig">@atomicLoad(comptime T: type, ptr: &amp;const T, comptime ordering: builtin.AtomicOrder) -&gt; T</code></pre>
3885+
<p>
3886+
This builtin function atomically dereferences a pointer and returns the value.
3887+
</p>
3888+
<p>
3889+
<code>T</code> must be a pointer type, a <code>bool</code>,
3890+
or an integer whose bit count meets these requirements:
3891+
</p>
3892+
<ul>
3893+
<li>At least 8</li>
3894+
<li>At most the same as usize</li>
3895+
<li>Power of 2</li>
3896+
</ul>
3897+
<p>
3898+
TODO right now bool is not accepted. Also I think we could make non powers of 2 work fine, maybe
3899+
we can remove this restriction
3900+
</p>
3901+
{#header_close#}
38833902
{#header_open|@atomicRmw#}
38843903
<pre><code class="zig">@atomicRmw(comptime T: type, ptr: &amp;T, comptime op: builtin.AtomicRmwOp, operand: T, comptime ordering: builtin.AtomicOrder) -&gt; T</code></pre>
38853904
<p>
@@ -6001,7 +6020,7 @@ hljs.registerLanguage("zig", function(t) {
60016020
a = t.IR + "\\s*\\(",
60026021
c = {
60036022
keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong",
6004-
built_in: "breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchg fence divExact truncate atomicRmw sqrt",
6023+
built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchg fence divExact truncate atomicRmw sqrt",
60056024
literal: "true false null undefined"
60066025
},
60076026
n = [e, t.CLCM, t.CBCM, s, r];

src/all_types.hpp

+11
Original file line numberDiff line numberDiff line change
@@ -1347,6 +1347,7 @@ enum BuiltinFnId {
13471347
BuiltinFnIdExport,
13481348
BuiltinFnIdErrorReturnTrace,
13491349
BuiltinFnIdAtomicRmw,
1350+
BuiltinFnIdAtomicLoad,
13501351
};
13511352

13521353
struct BuiltinFnEntry {
@@ -2043,6 +2044,7 @@ enum IrInstructionId {
20432044
IrInstructionIdCoroPromise,
20442045
IrInstructionIdCoroAllocHelper,
20452046
IrInstructionIdAtomicRmw,
2047+
IrInstructionIdAtomicLoad,
20462048
IrInstructionIdPromiseResultType,
20472049
IrInstructionIdAwaitBookkeeping,
20482050
IrInstructionIdSaveErrRetAddr,
@@ -3003,6 +3005,15 @@ struct IrInstructionAtomicRmw {
30033005
AtomicOrder resolved_ordering;
30043006
};
30053007

3008+
struct IrInstructionAtomicLoad {
3009+
IrInstruction base;
3010+
3011+
IrInstruction *operand_type;
3012+
IrInstruction *ptr;
3013+
IrInstruction *ordering;
3014+
AtomicOrder resolved_ordering;
3015+
};
3016+
30063017
struct IrInstructionPromiseResultType {
30073018
IrInstruction base;
30083019

src/codegen.cpp

+13
Original file line numberDiff line numberDiff line change
@@ -4385,6 +4385,16 @@ static LLVMValueRef ir_render_atomic_rmw(CodeGen *g, IrExecutable *executable,
43854385
return LLVMBuildIntToPtr(g->builder, uncasted_result, operand_type->type_ref, "");
43864386
}
43874387

4388+
static LLVMValueRef ir_render_atomic_load(CodeGen *g, IrExecutable *executable,
4389+
IrInstructionAtomicLoad *instruction)
4390+
{
4391+
LLVMAtomicOrdering ordering = to_LLVMAtomicOrdering(instruction->resolved_ordering);
4392+
LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr);
4393+
LLVMValueRef load_inst = gen_load(g, ptr, instruction->ptr->value.type, "");
4394+
LLVMSetOrdering(load_inst, ordering);
4395+
return load_inst;
4396+
}
4397+
43884398
static LLVMValueRef ir_render_merge_err_ret_traces(CodeGen *g, IrExecutable *executable,
43894399
IrInstructionMergeErrRetTraces *instruction)
43904400
{
@@ -4628,6 +4638,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
46284638
return ir_render_coro_alloc_helper(g, executable, (IrInstructionCoroAllocHelper *)instruction);
46294639
case IrInstructionIdAtomicRmw:
46304640
return ir_render_atomic_rmw(g, executable, (IrInstructionAtomicRmw *)instruction);
4641+
case IrInstructionIdAtomicLoad:
4642+
return ir_render_atomic_load(g, executable, (IrInstructionAtomicLoad *)instruction);
46314643
case IrInstructionIdSaveErrRetAddr:
46324644
return ir_render_save_err_ret_addr(g, executable, (IrInstructionSaveErrRetAddr *)instruction);
46334645
case IrInstructionIdMergeErrRetTraces:
@@ -6136,6 +6148,7 @@ static void define_builtin_fns(CodeGen *g) {
61366148
create_builtin_fn(g, BuiltinFnIdExport, "export", 3);
61376149
create_builtin_fn(g, BuiltinFnIdErrorReturnTrace, "errorReturnTrace", 0);
61386150
create_builtin_fn(g, BuiltinFnIdAtomicRmw, "atomicRmw", 5);
6151+
create_builtin_fn(g, BuiltinFnIdAtomicLoad, "atomicLoad", 3);
61396152
}
61406153

61416154
static const char *bool_to_str(bool b) {

src/ir.cpp

+104-8
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionAtomicRmw *) {
709709
return IrInstructionIdAtomicRmw;
710710
}
711711

712+
static constexpr IrInstructionId ir_instruction_id(IrInstructionAtomicLoad *) {
713+
return IrInstructionIdAtomicLoad;
714+
}
715+
712716
static constexpr IrInstructionId ir_instruction_id(IrInstructionPromiseResultType *) {
713717
return IrInstructionIdPromiseResultType;
714718
}
@@ -2673,6 +2677,23 @@ static IrInstruction *ir_build_atomic_rmw(IrBuilder *irb, Scope *scope, AstNode
26732677
return &instruction->base;
26742678
}
26752679

2680+
static IrInstruction *ir_build_atomic_load(IrBuilder *irb, Scope *scope, AstNode *source_node,
2681+
IrInstruction *operand_type, IrInstruction *ptr,
2682+
IrInstruction *ordering, AtomicOrder resolved_ordering)
2683+
{
2684+
IrInstructionAtomicLoad *instruction = ir_build_instruction<IrInstructionAtomicLoad>(irb, scope, source_node);
2685+
instruction->operand_type = operand_type;
2686+
instruction->ptr = ptr;
2687+
instruction->ordering = ordering;
2688+
instruction->resolved_ordering = resolved_ordering;
2689+
2690+
if (operand_type != nullptr) ir_ref_instruction(operand_type, irb->current_basic_block);
2691+
ir_ref_instruction(ptr, irb->current_basic_block);
2692+
if (ordering != nullptr) ir_ref_instruction(ordering, irb->current_basic_block);
2693+
2694+
return &instruction->base;
2695+
}
2696+
26762697
static IrInstruction *ir_build_promise_result_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
26772698
IrInstruction *promise_type)
26782699
{
@@ -4303,6 +4324,27 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
43034324
// these 2 values don't mean anything since we passed non-null values for other args
43044325
AtomicRmwOp_xchg, AtomicOrderMonotonic);
43054326
}
4327+
case BuiltinFnIdAtomicLoad:
4328+
{
4329+
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
4330+
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
4331+
if (arg0_value == irb->codegen->invalid_instruction)
4332+
return arg0_value;
4333+
4334+
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
4335+
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
4336+
if (arg1_value == irb->codegen->invalid_instruction)
4337+
return arg1_value;
4338+
4339+
AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
4340+
IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope);
4341+
if (arg2_value == irb->codegen->invalid_instruction)
4342+
return arg2_value;
4343+
4344+
return ir_build_atomic_load(irb, scope, node, arg0_value, arg1_value, arg2_value,
4345+
// this value does not mean anything since we passed non-null values for other arg
4346+
AtomicOrderMonotonic);
4347+
}
43064348
}
43074349
zig_unreachable();
43084350
}
@@ -17898,35 +17940,43 @@ static TypeTableEntry *ir_analyze_instruction_coro_alloc_helper(IrAnalyze *ira,
1789817940
return result->value.type;
1789917941
}
1790017942

17901-
static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstructionAtomicRmw *instruction) {
17902-
TypeTableEntry *operand_type = ir_resolve_type(ira, instruction->operand_type->other);
17903-
if (type_is_invalid(operand_type)) {
17943+
static TypeTableEntry *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op) {
17944+
TypeTableEntry *operand_type = ir_resolve_type(ira, op);
17945+
if (type_is_invalid(operand_type))
1790417946
return ira->codegen->builtin_types.entry_invalid;
17905-
}
17947+
1790617948
if (operand_type->id == TypeTableEntryIdInt) {
1790717949
if (operand_type->data.integral.bit_count < 8) {
17908-
ir_add_error(ira, &instruction->base,
17950+
ir_add_error(ira, op,
1790917951
buf_sprintf("expected integer type 8 bits or larger, found %" PRIu32 "-bit integer type",
1791017952
operand_type->data.integral.bit_count));
1791117953
return ira->codegen->builtin_types.entry_invalid;
1791217954
}
1791317955
if (operand_type->data.integral.bit_count > ira->codegen->pointer_size_bytes * 8) {
17914-
ir_add_error(ira, &instruction->base,
17956+
ir_add_error(ira, op,
1791517957
buf_sprintf("expected integer type pointer size or smaller, found %" PRIu32 "-bit integer type",
1791617958
operand_type->data.integral.bit_count));
1791717959
return ira->codegen->builtin_types.entry_invalid;
1791817960
}
1791917961
if (!is_power_of_2(operand_type->data.integral.bit_count)) {
17920-
ir_add_error(ira, &instruction->base,
17962+
ir_add_error(ira, op,
1792117963
buf_sprintf("%" PRIu32 "-bit integer type is not a power of 2", operand_type->data.integral.bit_count));
1792217964
return ira->codegen->builtin_types.entry_invalid;
1792317965
}
1792417966
} else if (get_codegen_ptr_type(operand_type) == nullptr) {
17925-
ir_add_error(ira, &instruction->base,
17967+
ir_add_error(ira, op,
1792617968
buf_sprintf("expected integer or pointer type, found '%s'", buf_ptr(&operand_type->name)));
1792717969
return ira->codegen->builtin_types.entry_invalid;
1792817970
}
1792917971

17972+
return operand_type;
17973+
}
17974+
17975+
static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstructionAtomicRmw *instruction) {
17976+
TypeTableEntry *operand_type = ir_resolve_atomic_operand_type(ira, instruction->operand_type->other);
17977+
if (type_is_invalid(operand_type))
17978+
return ira->codegen->builtin_types.entry_invalid;
17979+
1793017980
IrInstruction *ptr_inst = instruction->ptr->other;
1793117981
if (type_is_invalid(ptr_inst->value.type))
1793217982
return ira->codegen->builtin_types.entry_invalid;
@@ -17974,6 +18024,49 @@ static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstr
1797418024
return result->value.type;
1797518025
}
1797618026

18027+
static TypeTableEntry *ir_analyze_instruction_atomic_load(IrAnalyze *ira, IrInstructionAtomicLoad *instruction) {
18028+
TypeTableEntry *operand_type = ir_resolve_atomic_operand_type(ira, instruction->operand_type->other);
18029+
if (type_is_invalid(operand_type))
18030+
return ira->codegen->builtin_types.entry_invalid;
18031+
18032+
IrInstruction *ptr_inst = instruction->ptr->other;
18033+
if (type_is_invalid(ptr_inst->value.type))
18034+
return ira->codegen->builtin_types.entry_invalid;
18035+
18036+
TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, operand_type, true);
18037+
IrInstruction *casted_ptr = ir_implicit_cast(ira, ptr_inst, ptr_type);
18038+
if (type_is_invalid(casted_ptr->value.type))
18039+
return ira->codegen->builtin_types.entry_invalid;
18040+
18041+
AtomicOrder ordering;
18042+
if (instruction->ordering == nullptr) {
18043+
ordering = instruction->resolved_ordering;
18044+
} else {
18045+
if (!ir_resolve_atomic_order(ira, instruction->ordering->other, &ordering))
18046+
return ira->codegen->builtin_types.entry_invalid;
18047+
}
18048+
18049+
if (ordering == AtomicOrderRelease || ordering == AtomicOrderAcqRel) {
18050+
assert(instruction->ordering != nullptr);
18051+
ir_add_error(ira, instruction->ordering,
18052+
buf_sprintf("@atomicLoad atomic ordering must not be Release or AcqRel"));
18053+
return ira->codegen->builtin_types.entry_invalid;
18054+
}
18055+
18056+
if (instr_is_comptime(casted_ptr)) {
18057+
IrInstruction *result = ir_get_deref(ira, &instruction->base, casted_ptr);
18058+
ir_link_new_instruction(result, &instruction->base);
18059+
assert(result->value.type != nullptr);
18060+
return result->value.type;
18061+
}
18062+
18063+
IrInstruction *result = ir_build_atomic_load(&ira->new_irb, instruction->base.scope,
18064+
instruction->base.source_node, nullptr, casted_ptr, nullptr, ordering);
18065+
ir_link_new_instruction(result, &instruction->base);
18066+
result->value.type = operand_type;
18067+
return result->value.type;
18068+
}
18069+
1797718070
static TypeTableEntry *ir_analyze_instruction_promise_result_type(IrAnalyze *ira, IrInstructionPromiseResultType *instruction) {
1797818071
TypeTableEntry *promise_type = ir_resolve_type(ira, instruction->promise_type->other);
1797918072
if (type_is_invalid(promise_type))
@@ -18357,6 +18450,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
1835718450
return ir_analyze_instruction_coro_alloc_helper(ira, (IrInstructionCoroAllocHelper *)instruction);
1835818451
case IrInstructionIdAtomicRmw:
1835918452
return ir_analyze_instruction_atomic_rmw(ira, (IrInstructionAtomicRmw *)instruction);
18453+
case IrInstructionIdAtomicLoad:
18454+
return ir_analyze_instruction_atomic_load(ira, (IrInstructionAtomicLoad *)instruction);
1836018455
case IrInstructionIdPromiseResultType:
1836118456
return ir_analyze_instruction_promise_result_type(ira, (IrInstructionPromiseResultType *)instruction);
1836218457
case IrInstructionIdAwaitBookkeeping:
@@ -18584,6 +18679,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
1858418679
case IrInstructionIdCoroPromise:
1858518680
case IrInstructionIdPromiseResultType:
1858618681
case IrInstructionIdSqrt:
18682+
case IrInstructionIdAtomicLoad:
1858718683
return false;
1858818684

1858918685
case IrInstructionIdAsm:

src/ir_print.cpp

+21
Original file line numberDiff line numberDiff line change
@@ -1172,6 +1172,24 @@ static void ir_print_atomic_rmw(IrPrint *irp, IrInstructionAtomicRmw *instructio
11721172
fprintf(irp->f, ")");
11731173
}
11741174

1175+
static void ir_print_atomic_load(IrPrint *irp, IrInstructionAtomicLoad *instruction) {
1176+
fprintf(irp->f, "@atomicLoad(");
1177+
if (instruction->operand_type != nullptr) {
1178+
ir_print_other_instruction(irp, instruction->operand_type);
1179+
} else {
1180+
fprintf(irp->f, "[TODO print]");
1181+
}
1182+
fprintf(irp->f, ",");
1183+
ir_print_other_instruction(irp, instruction->ptr);
1184+
fprintf(irp->f, ",");
1185+
if (instruction->ordering != nullptr) {
1186+
ir_print_other_instruction(irp, instruction->ordering);
1187+
} else {
1188+
fprintf(irp->f, "[TODO print]");
1189+
}
1190+
fprintf(irp->f, ")");
1191+
}
1192+
11751193
static void ir_print_await_bookkeeping(IrPrint *irp, IrInstructionAwaitBookkeeping *instruction) {
11761194
fprintf(irp->f, "@awaitBookkeeping(");
11771195
ir_print_other_instruction(irp, instruction->promise_result_type);
@@ -1605,6 +1623,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
16051623
case IrInstructionIdSqrt:
16061624
ir_print_sqrt(irp, (IrInstructionSqrt *)instruction);
16071625
break;
1626+
case IrInstructionIdAtomicLoad:
1627+
ir_print_atomic_load(irp, (IrInstructionAtomicLoad *)instruction);
1628+
break;
16081629
}
16091630
fprintf(irp->f, "\n");
16101631
}

std/os/index.zig

+1-1
Original file line numberDiff line numberDiff line change
@@ -2392,7 +2392,7 @@ pub const Thread = struct {
23922392

23932393
pub fn wait(self: &const Thread) void {
23942394
while (true) {
2395-
const pid_value = self.pid; // TODO atomic load
2395+
const pid_value = @atomicLoad(i32, &self.pid, builtin.AtomicOrder.SeqCst);
23962396
if (pid_value == 0) break;
23972397
const rc = linux.futex_wait(@ptrToInt(&self.pid), linux.FUTEX_WAIT, pid_value, null);
23982398
switch (linux.getErrno(rc)) {

test/cases/atomics.zig

+13-1
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,25 @@ test "fence" {
1515
x = 5678;
1616
}
1717

18-
test "atomicrmw" {
18+
test "atomicrmw and atomicload" {
1919
var data: u8 = 200;
2020
testAtomicRmw(&data);
2121
assert(data == 42);
22+
testAtomicLoad(&data);
2223
}
2324

2425
fn testAtomicRmw(ptr: &u8) void {
2526
const prev_value = @atomicRmw(u8, ptr, AtomicRmwOp.Xchg, 42, AtomicOrder.SeqCst);
2627
assert(prev_value == 200);
28+
comptime {
29+
var x: i32 = 1234;
30+
const y: i32 = 12345;
31+
assert(@atomicLoad(i32, &x, AtomicOrder.SeqCst) == 1234);
32+
assert(@atomicLoad(i32, &y, AtomicOrder.SeqCst) == 12345);
33+
}
34+
}
35+
36+
fn testAtomicLoad(ptr: &u8) void {
37+
const x = @atomicLoad(u8, ptr, AtomicOrder.SeqCst);
38+
assert(x == 42);
2739
}

0 commit comments

Comments
 (0)