authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-04-15 18:12:00-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-04-15 18:12:00-04:00
logb9360640cefd1aa30dedf71a0c6b7bddc51a6ae3
tree89c751d93d98bee02e77fd7ac015845f398d8afd
parent859b10d8bfcca3c4a30798b4522fd88ec6c66de6

add @atomicLoad builtin

See #174

7 files changed, 183 insertions(+), 11 deletions(-)

doc/langref.html.in+20-1
......@@ -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
......@@ -1347,6 +1347,7 @@ enum BuiltinFnId {
13471347 BuiltinFnIdExport,
13481348 BuiltinFnIdErrorReturnTrace,
13491349 BuiltinFnIdAtomicRmw,
1350 BuiltinFnIdAtomicLoad,
13501351};
13511352
13521353struct 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
3008struct IrInstructionAtomicLoad {
3009 IrInstruction base;
3010
3011 IrInstruction *operand_type;
3012 IrInstruction *ptr;
3013 IrInstruction *ordering;
3014 AtomicOrder resolved_ordering;
3015};
3016
30063017struct IrInstructionPromiseResultType {
30073018 IrInstruction base;
30083019
src/codegen.cpp+13
......@@ -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
4388static 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
43884398static 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
61416154static const char *bool_to_str(bool b) {
src/ir.cpp+104-8
......@@ -709,6 +709,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionAtomicRmw *) {
709709 return IrInstructionIdAtomicRmw;
710710}
711711
712static constexpr IrInstructionId ir_instruction_id(IrInstructionAtomicLoad *) {
713 return IrInstructionIdAtomicLoad;
714}
715
712716static 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
2680static 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
26762697static 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
17901static 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)) {
17943static 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
17975static 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
18027static 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
1797718070static 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
......@@ -1172,6 +1172,24 @@ static void ir_print_atomic_rmw(IrPrint *irp, IrInstructionAtomicRmw *instructio
11721172 fprintf(irp->f, ")");
11731173}
11741174
1175static 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
11751193static 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
......@@ -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
......@@ -15,13 +15,25 @@ test "fence" {
1515 x = 5678;
1616}
1717
18test "atomicrmw" {
18test "atomicrmw and atomicload" {
1919 var data: u8 = 200;
2020 testAtomicRmw(&data);
2121 assert(data == 42);
22 testAtomicLoad(&data);
2223}
2324
2425fn 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
36fn testAtomicLoad(ptr: &u8) void {
37 const x = @atomicLoad(u8, ptr, AtomicOrder.SeqCst);
38 assert(x == 42);
2739}