authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-09-20 14:13:33-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-09-20 14:24:43-07:00
logb9d3527e0ed53c4796ab64b4df7daf0909739807
tree6cb29ce60e606fc4cfd30dad8b6f88c07a6bf8a7
parent5dc251747b4ea65b1c8d3f2b5af62ca83c6d1196

stage2: implement comptime `@atomicRmw`

* introduce float_to_int and int_to_float AIR instructionts and implement for the LLVM backend and C backend. * Sema: implement `zirIntToFloat`. * Sema: implement `@atomicRmw` comptime evaluation - introduce `storePtrVal` for when one needs to store a Value to a pointer which is a Value, and assert it happens at comptime. * Value: introduce new functionality: - intToFloat - numberAddWrap - numberSubWrap - numberMax - numberMin - bitwiseAnd - bitwiseNand (not implemented yet) - bitwiseOr - bitwiseXor * Sema: hook up `zirBitwise` to the new Value bitwise implementations * Type: rename `isFloat` to `isRuntimeFloat` because it returns `false` for `comptime_float`.

12 files changed, 573 insertions(+), 128 deletions(-)

src/Air.zig+8
......@@ -311,6 +311,12 @@ pub const Inst = struct {
311311 /// Given a pointer to an array, return a slice.
312312 /// Uses the `ty_op` field.
313313 array_to_slice,
314 /// Given a float operand, return the integer with the closest mathematical meaning.
315 /// Uses the `ty_op` field.
316 float_to_int,
317 /// Given an integer operand, return the float with the closest mathematical meaning.
318 /// Uses the `ty_op` field.
319 int_to_float,
314320
315321 /// Uses the `ty_pl` field with payload `Cmpxchg`.
316322 cmpxchg_weak,
......@@ -598,6 +604,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
598604 .struct_field_ptr_index_2,
599605 .struct_field_ptr_index_3,
600606 .array_to_slice,
607 .float_to_int,
608 .int_to_float,
601609 => return air.getRefType(datas[inst].ty_op.ty),
602610
603611 .loop,
src/Liveness.zig+2
......@@ -293,6 +293,8 @@ fn analyzeInst(
293293 .struct_field_ptr_index_2,
294294 .struct_field_ptr_index_3,
295295 .array_to_slice,
296 .float_to_int,
297 .int_to_float,
296298 => {
297299 const o = inst_datas[inst].ty_op;
298300 return trackOperands(a, new_set, inst, main_tomb, .{ o.operand, .none, .none });
src/Sema.zig+161-79
......@@ -4830,8 +4830,8 @@ fn analyzeSwitch(
48304830 var arena = std.heap.ArenaAllocator.init(gpa);
48314831 defer arena.deinit();
48324832
4833 const min_int = try operand_ty.minInt(&arena, mod.getTarget());
4834 const max_int = try operand_ty.maxInt(&arena, mod.getTarget());
4833 const min_int = try operand_ty.minInt(&arena.allocator, mod.getTarget());
4834 const max_int = try operand_ty.maxInt(&arena.allocator, mod.getTarget());
48354835 if (try range_set.spans(min_int, max_int, operand_ty)) {
48364836 if (special_prong == .@"else") {
48374837 return mod.fail(
......@@ -5671,10 +5671,13 @@ fn zirBitwise(
56715671
56725672 if (try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs)) |lhs_val| {
56735673 if (try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs)) |rhs_val| {
5674 if (lhs_val.isUndef() or rhs_val.isUndef()) {
5675 return sema.addConstUndef(resolved_type);
5676 }
5677 return sema.mod.fail(&block.base, src, "TODO implement comptime bitwise operations", .{});
5674 const result_val = switch (air_tag) {
5675 .bit_and => try lhs_val.bitwiseAnd(rhs_val, sema.arena),
5676 .bit_or => try lhs_val.bitwiseOr(rhs_val, sema.arena),
5677 .xor => try lhs_val.bitwiseXor(rhs_val, sema.arena),
5678 else => unreachable,
5679 };
5680 return sema.addConstant(scalar_type, result_val);
56785681 }
56795682 }
56805683
......@@ -6028,8 +6031,8 @@ fn analyzeArithmetic(
60286031 }
60296032
60306033 if (zir_tag == .mod_rem) {
6031 const dirty_lhs = lhs_ty.isSignedInt() or lhs_ty.isFloat();
6032 const dirty_rhs = rhs_ty.isSignedInt() or rhs_ty.isFloat();
6034 const dirty_lhs = lhs_ty.isSignedInt() or lhs_ty.isRuntimeFloat();
6035 const dirty_rhs = rhs_ty.isSignedInt() or rhs_ty.isRuntimeFloat();
60336036 if (dirty_lhs or dirty_rhs) {
60346037 return sema.mod.fail(&block.base, src, "remainder division with '{}' and '{}': signed integers and floats must use @rem or @mod", .{ lhs_ty, rhs_ty });
60356038 }
......@@ -7298,13 +7301,30 @@ fn zirFrameSize(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileE
72987301fn zirFloatToInt(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
72997302 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
73007303 const src = inst_data.src();
7304 // TODO don't forget the safety check!
73017305 return sema.mod.fail(&block.base, src, "TODO: Sema.zirFloatToInt", .{});
73027306}
73037307
73047308fn zirIntToFloat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
73057309 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
7306 const src = inst_data.src();
7307 return sema.mod.fail(&block.base, src, "TODO: Sema.zirIntToFloat", .{});
7310 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
7311 const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
7312 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
7313 const dest_ty = try sema.resolveType(block, ty_src, extra.lhs);
7314 const operand = sema.resolveInst(extra.rhs);
7315 const operand_ty = sema.typeOf(operand);
7316
7317 try sema.checkIntType(block, ty_src, dest_ty);
7318 try sema.checkFloatType(block, operand_src, operand_ty);
7319
7320 if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
7321 const target = sema.mod.getTarget();
7322 const result_val = try val.intToFloat(sema.arena, dest_ty, target);
7323 return sema.addConstant(dest_ty, result_val);
7324 }
7325
7326 try sema.requireRuntimeBlock(block, operand_src);
7327 return block.addTyOp(.int_to_float, dest_ty, operand);
73087328}
73097329
73107330fn zirIntToPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -7542,6 +7562,34 @@ fn zirOffsetOf(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
75427562 return sema.mod.fail(&block.base, src, "TODO: Sema.zirOffsetOf", .{});
75437563}
75447564
7565fn checkIntType(
7566 sema: *Sema,
7567 block: *Scope.Block,
7568 ty_src: LazySrcLoc,
7569 ty: Type,
7570) CompileError!void {
7571 switch (ty.zigTypeTag()) {
7572 .ComptimeInt, .Int => {},
7573 else => return sema.mod.fail(&block.base, ty_src, "expected integer type, found '{}'", .{
7574 ty,
7575 }),
7576 }
7577}
7578
7579fn checkFloatType(
7580 sema: *Sema,
7581 block: *Scope.Block,
7582 ty_src: LazySrcLoc,
7583 ty: Type,
7584) CompileError!void {
7585 switch (ty.zigTypeTag()) {
7586 .ComptimeFloat, .Float => {},
7587 else => return sema.mod.fail(&block.base, ty_src, "expected float type, found '{}'", .{
7588 ty,
7589 }),
7590 }
7591}
7592
75457593fn checkAtomicOperandType(
75467594 sema: *Sema,
75477595 block: *Scope.Block,
......@@ -7815,9 +7863,23 @@ fn zirAtomicRmw(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileE
78157863
78167864 const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: {
78177865 if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |operand_val| {
7818 _ = ptr_val;
7819 _ = operand_val;
7820 return mod.fail(&block.base, src, "TODO implement Sema for @atomicRmw at comptime", .{});
7866 const target = sema.mod.getTarget();
7867 const stored_val = (try ptr_val.pointerDeref(sema.arena)) orelse break :rs ptr_src;
7868 const new_val = switch (op) {
7869 // zig fmt: off
7870 .Xchg => operand_val,
7871 .Add => try stored_val.numberAddWrap(operand_val, operand_ty, sema.arena, target),
7872 .Sub => try stored_val.numberSubWrap(operand_val, operand_ty, sema.arena, target),
7873 .And => try stored_val.bitwiseAnd (operand_val, sema.arena),
7874 .Nand => try stored_val.bitwiseNand (operand_val, operand_ty, sema.arena),
7875 .Or => try stored_val.bitwiseOr (operand_val, sema.arena),
7876 .Xor => try stored_val.bitwiseXor (operand_val, sema.arena),
7877 .Max => try stored_val.numberMax (operand_val, sema.arena),
7878 .Min => try stored_val.numberMin (operand_val, sema.arena),
7879 // zig fmt: on
7880 };
7881 try sema.storePtrVal(block, src, ptr_val, new_val, operand_ty);
7882 return sema.addConstant(operand_ty, stored_val);
78217883 } else break :rs operand_src;
78227884 } else ptr_src;
78237885
......@@ -9298,33 +9360,38 @@ fn coerceNum(
92989360
92999361 const target = sema.mod.getTarget();
93009362
9301 if (dst_zig_tag == .ComptimeInt or dst_zig_tag == .Int) {
9302 if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
9303 if (val.floatHasFraction()) {
9304 return sema.mod.fail(&block.base, inst_src, "fractional component prevents float value {} from being casted to type '{}'", .{ val, inst_ty });
9363 switch (dst_zig_tag) {
9364 .ComptimeInt, .Int => {
9365 if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
9366 if (val.floatHasFraction()) {
9367 return sema.mod.fail(&block.base, inst_src, "fractional component prevents float value {} from being casted to type '{}'", .{ val, inst_ty });
9368 }
9369 return sema.mod.fail(&block.base, inst_src, "TODO float to int", .{});
9370 } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
9371 if (!val.intFitsInType(dest_type, target)) {
9372 return sema.mod.fail(&block.base, inst_src, "type {} cannot represent integer value {}", .{ dest_type, val });
9373 }
9374 return try sema.addConstant(dest_type, val);
93059375 }
9306 return sema.mod.fail(&block.base, inst_src, "TODO float to int", .{});
9307 } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
9308 if (!val.intFitsInType(dest_type, target)) {
9309 return sema.mod.fail(&block.base, inst_src, "type {} cannot represent integer value {}", .{ dest_type, val });
9376 },
9377 .ComptimeFloat, .Float => {
9378 if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
9379 const res = val.floatCast(sema.arena, dest_type) catch |err| switch (err) {
9380 error.Overflow => return sema.mod.fail(
9381 &block.base,
9382 inst_src,
9383 "cast of value {} to type '{}' loses information",
9384 .{ val, dest_type },
9385 ),
9386 error.OutOfMemory => return error.OutOfMemory,
9387 };
9388 return try sema.addConstant(dest_type, res);
9389 } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
9390 const result_val = try val.intToFloat(sema.arena, dest_type, target);
9391 return try sema.addConstant(dest_type, result_val);
93109392 }
9311 return try sema.addConstant(dest_type, val);
9312 }
9313 } else if (dst_zig_tag == .ComptimeFloat or dst_zig_tag == .Float) {
9314 if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
9315 const res = val.floatCast(sema.arena, dest_type) catch |err| switch (err) {
9316 error.Overflow => return sema.mod.fail(
9317 &block.base,
9318 inst_src,
9319 "cast of value {} to type '{}' loses information",
9320 .{ val, dest_type },
9321 ),
9322 error.OutOfMemory => return error.OutOfMemory,
9323 };
9324 return try sema.addConstant(dest_type, res);
9325 } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
9326 return sema.mod.fail(&block.base, inst_src, "TODO int to float", .{});
9327 }
9393 },
9394 else => {},
93289395 }
93299396 return null;
93309397}
......@@ -9375,42 +9442,10 @@ fn storePtr2(
93759442 return;
93769443
93779444 const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: {
9378 if (ptr_val.castTag(.decl_ref_mut)) |decl_ref_mut| {
9379 const const_val = (try sema.resolveMaybeUndefVal(block, operand_src, operand)) orelse
9380 return sema.mod.fail(&block.base, src, "cannot store runtime value in compile time variable", .{});
9381
9382 if (decl_ref_mut.data.runtime_index < block.runtime_index) {
9383 if (block.runtime_cond) |cond_src| {
9384 const msg = msg: {
9385 const msg = try sema.mod.errMsg(&block.base, src, "store to comptime variable depends on runtime condition", .{});
9386 errdefer msg.destroy(sema.gpa);
9387 try sema.mod.errNote(&block.base, cond_src, msg, "runtime condition here", .{});
9388 break :msg msg;
9389 };
9390 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
9391 }
9392 if (block.runtime_loop) |loop_src| {
9393 const msg = msg: {
9394 const msg = try sema.mod.errMsg(&block.base, src, "cannot store to comptime variable in non-inline loop", .{});
9395 errdefer msg.destroy(sema.gpa);
9396 try sema.mod.errNote(&block.base, loop_src, msg, "non-inline loop here", .{});
9397 break :msg msg;
9398 };
9399 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
9400 }
9401 unreachable;
9402 }
9403 var new_arena = std.heap.ArenaAllocator.init(sema.gpa);
9404 errdefer new_arena.deinit();
9405 const new_ty = try elem_ty.copy(&new_arena.allocator);
9406 const new_val = try const_val.copy(&new_arena.allocator);
9407 const decl = decl_ref_mut.data.decl;
9408 var old_arena = decl.value_arena.?.promote(sema.gpa);
9409 decl.value_arena = null;
9410 try decl.finalizeNewArena(&new_arena);
9411 decl.ty = new_ty;
9412 decl.val = new_val;
9413 old_arena.deinit();
9445 const operand_val = (try sema.resolveMaybeUndefVal(block, operand_src, operand)) orelse
9446 return sema.mod.fail(&block.base, src, "cannot store runtime value in compile time variable", .{});
9447 if (ptr_val.tag() == .decl_ref_mut) {
9448 try sema.storePtrVal(block, src, ptr_val, operand_val, elem_ty);
94149449 return;
94159450 }
94169451 break :rs operand_src;
......@@ -9422,6 +9457,53 @@ fn storePtr2(
94229457 _ = try block.addBinOp(air_tag, ptr, operand);
94239458}
94249459
9460/// Call when you have Value objects rather than Air instructions, and you want to
9461/// assert the store must be done at comptime.
9462fn storePtrVal(
9463 sema: *Sema,
9464 block: *Scope.Block,
9465 src: LazySrcLoc,
9466 ptr_val: Value,
9467 operand_val: Value,
9468 operand_ty: Type,
9469) !void {
9470 if (ptr_val.castTag(.decl_ref_mut)) |decl_ref_mut| {
9471 if (decl_ref_mut.data.runtime_index < block.runtime_index) {
9472 if (block.runtime_cond) |cond_src| {
9473 const msg = msg: {
9474 const msg = try sema.mod.errMsg(&block.base, src, "store to comptime variable depends on runtime condition", .{});
9475 errdefer msg.destroy(sema.gpa);
9476 try sema.mod.errNote(&block.base, cond_src, msg, "runtime condition here", .{});
9477 break :msg msg;
9478 };
9479 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
9480 }
9481 if (block.runtime_loop) |loop_src| {
9482 const msg = msg: {
9483 const msg = try sema.mod.errMsg(&block.base, src, "cannot store to comptime variable in non-inline loop", .{});
9484 errdefer msg.destroy(sema.gpa);
9485 try sema.mod.errNote(&block.base, loop_src, msg, "non-inline loop here", .{});
9486 break :msg msg;
9487 };
9488 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
9489 }
9490 unreachable;
9491 }
9492 var new_arena = std.heap.ArenaAllocator.init(sema.gpa);
9493 errdefer new_arena.deinit();
9494 const new_ty = try operand_ty.copy(&new_arena.allocator);
9495 const new_val = try operand_val.copy(&new_arena.allocator);
9496 const decl = decl_ref_mut.data.decl;
9497 var old_arena = decl.value_arena.?.promote(sema.gpa);
9498 decl.value_arena = null;
9499 try decl.finalizeNewArena(&new_arena);
9500 decl.ty = new_ty;
9501 decl.val = new_val;
9502 old_arena.deinit();
9503 return;
9504 }
9505}
9506
94259507fn bitcast(
94269508 sema: *Sema,
94279509 block: *Scope.Block,
......@@ -9801,11 +9883,11 @@ fn cmpNumeric(
98019883 const lhs_is_signed = if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val|
98029884 lhs_val.compareWithZero(.lt)
98039885 else
9804 (lhs_ty.isFloat() or lhs_ty.isSignedInt());
9886 (lhs_ty.isRuntimeFloat() or lhs_ty.isSignedInt());
98059887 const rhs_is_signed = if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val|
98069888 rhs_val.compareWithZero(.lt)
98079889 else
9808 (rhs_ty.isFloat() or rhs_ty.isSignedInt());
9890 (rhs_ty.isRuntimeFloat() or rhs_ty.isSignedInt());
98099891 const dest_int_is_signed = lhs_is_signed or rhs_is_signed;
98109892
98119893 var dest_float_type: ?Type = null;
......@@ -10031,7 +10113,7 @@ fn resolvePeerTypes(
1003110113 }
1003210114 continue;
1003310115 }
10034 if (chosen_ty.isFloat() and candidate_ty.isFloat()) {
10116 if (chosen_ty.isRuntimeFloat() and candidate_ty.isRuntimeFloat()) {
1003510117 if (chosen_ty.floatBits(target) < candidate_ty.floatBits(target)) {
1003610118 chosen = candidate;
1003710119 chosen_i = candidate_i + 1;
......@@ -10049,13 +10131,13 @@ fn resolvePeerTypes(
1004910131 continue;
1005010132 }
1005110133
10052 if (chosen_ty.zigTypeTag() == .ComptimeFloat and candidate_ty.isFloat()) {
10134 if (chosen_ty.zigTypeTag() == .ComptimeFloat and candidate_ty.isRuntimeFloat()) {
1005310135 chosen = candidate;
1005410136 chosen_i = candidate_i + 1;
1005510137 continue;
1005610138 }
1005710139
10058 if (chosen_ty.isFloat() and candidate_ty.zigTypeTag() == .ComptimeFloat) {
10140 if (chosen_ty.isRuntimeFloat() and candidate_ty.zigTypeTag() == .ComptimeFloat) {
1005910141 continue;
1006010142 }
1006110143
src/codegen.zig+22
......@@ -858,6 +858,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
858858 .struct_field_ptr=> try self.airStructFieldPtr(inst),
859859 .struct_field_val=> try self.airStructFieldVal(inst),
860860 .array_to_slice => try self.airArrayToSlice(inst),
861 .int_to_float => try self.airIntToFloat(inst),
862 .float_to_int => try self.airFloatToInt(inst),
861863 .cmpxchg_strong => try self.airCmpxchg(inst),
862864 .cmpxchg_weak => try self.airCmpxchg(inst),
863865 .atomic_rmw => try self.airAtomicRmw(inst),
......@@ -4769,6 +4771,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
47694771 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
47704772 }
47714773
4774 fn airIntToFloat(self: *Self, inst: Air.Inst.Index) !void {
4775 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4776 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
4777 else => return self.fail("TODO implement airIntToFloat for {}", .{
4778 self.target.cpu.arch,
4779 }),
4780 };
4781 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
4782 }
4783
4784 fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void {
4785 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4786 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
4787 else => return self.fail("TODO implement airFloatToInt for {}", .{
4788 self.target.cpu.arch,
4789 }),
4790 };
4791 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
4792 }
4793
47724794 fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void {
47734795 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
47744796 const extra = self.air.extraData(Air.Block, ty_pl.payload);
src/codegen/c.zig+20
......@@ -917,6 +917,8 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM
917917 .atomic_rmw => try airAtomicRmw(o, inst),
918918 .atomic_load => try airAtomicLoad(o, inst),
919919
920 .int_to_float, .float_to_int => try airSimpleCast(o, inst),
921
920922 .atomic_store_unordered => try airAtomicStore(o, inst, toMemoryOrder(.Unordered)),
921923 .atomic_store_monotonic => try airAtomicStore(o, inst, toMemoryOrder(.Monotonic)),
922924 .atomic_store_release => try airAtomicStore(o, inst, toMemoryOrder(.Release)),
......@@ -1899,6 +1901,24 @@ fn airArrayToSlice(o: *Object, inst: Air.Inst.Index) !CValue {
18991901 return local;
19001902}
19011903
1904/// Emits a local variable with the result type and initializes it
1905/// with the operand.
1906fn airSimpleCast(o: *Object, inst: Air.Inst.Index) !CValue {
1907 if (o.liveness.isUnused(inst))
1908 return CValue.none;
1909
1910 const inst_ty = o.air.typeOfIndex(inst);
1911 const local = try o.allocLocal(inst_ty, .Const);
1912 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
1913 const writer = o.writer();
1914 const operand = try o.resolveInst(ty_op.operand);
1915
1916 try writer.writeAll(" = ");
1917 try o.writeCValue(writer, operand);
1918 try writer.writeAll(";\n");
1919 return local;
1920}
1921
19021922fn airCmpxchg(o: *Object, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue {
19031923 const ty_pl = o.air.instructions.items(.data)[inst].ty_pl;
19041924 const extra = o.air.extraData(Air.Cmpxchg, ty_pl.payload).data;
src/codegen/llvm.zig+42-6
......@@ -1122,6 +1122,8 @@ pub const FuncGen = struct {
11221122 .slice_ptr => try self.airSliceField(inst, 0),
11231123 .slice_len => try self.airSliceField(inst, 1),
11241124 .array_to_slice => try self.airArrayToSlice(inst),
1125 .float_to_int => try self.airFloatToInt(inst),
1126 .int_to_float => try self.airIntToFloat(inst),
11251127 .cmpxchg_weak => try self.airCmpxchg(inst, true),
11261128 .cmpxchg_strong => try self.airCmpxchg(inst, false),
11271129 .fence => try self.airFence(inst),
......@@ -1372,6 +1374,40 @@ pub const FuncGen = struct {
13721374 return self.builder.buildInsertValue(partial, len, 1, "");
13731375 }
13741376
1377 fn airIntToFloat(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1378 if (self.liveness.isUnused(inst))
1379 return null;
1380
1381 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1382 const operand = try self.resolveInst(ty_op.operand);
1383 const dest_ty = self.air.typeOfIndex(inst);
1384 const dest_llvm_ty = try self.dg.llvmType(dest_ty);
1385
1386 if (dest_ty.isSignedInt()) {
1387 return self.builder.buildSIToFP(operand, dest_llvm_ty, "");
1388 } else {
1389 return self.builder.buildUIToFP(operand, dest_llvm_ty, "");
1390 }
1391 }
1392
1393 fn airFloatToInt(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1394 if (self.liveness.isUnused(inst))
1395 return null;
1396
1397 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1398 const operand = try self.resolveInst(ty_op.operand);
1399 const dest_ty = self.air.typeOfIndex(inst);
1400 const dest_llvm_ty = try self.dg.llvmType(dest_ty);
1401
1402 // TODO set fast math flag
1403
1404 if (dest_ty.isSignedInt()) {
1405 return self.builder.buildFPToSI(operand, dest_llvm_ty, "");
1406 } else {
1407 return self.builder.buildFPToUI(operand, dest_llvm_ty, "");
1408 }
1409 }
1410
13751411 fn airSliceField(self: *FuncGen, inst: Air.Inst.Index, index: c_uint) !?*const llvm.Value {
13761412 if (self.liveness.isUnused(inst))
13771413 return null;
......@@ -1818,7 +1854,7 @@ pub const FuncGen = struct {
18181854 const rhs = try self.resolveInst(bin_op.rhs);
18191855 const inst_ty = self.air.typeOfIndex(inst);
18201856
1821 if (inst_ty.isFloat()) return self.builder.buildFAdd(lhs, rhs, "");
1857 if (inst_ty.isRuntimeFloat()) return self.builder.buildFAdd(lhs, rhs, "");
18221858 if (wrap) return self.builder.buildAdd(lhs, rhs, "");
18231859 if (inst_ty.isSignedInt()) return self.builder.buildNSWAdd(lhs, rhs, "");
18241860 return self.builder.buildNUWAdd(lhs, rhs, "");
......@@ -1833,7 +1869,7 @@ pub const FuncGen = struct {
18331869 const rhs = try self.resolveInst(bin_op.rhs);
18341870 const inst_ty = self.air.typeOfIndex(inst);
18351871
1836 if (inst_ty.isFloat()) return self.builder.buildFSub(lhs, rhs, "");
1872 if (inst_ty.isRuntimeFloat()) return self.builder.buildFSub(lhs, rhs, "");
18371873 if (wrap) return self.builder.buildSub(lhs, rhs, "");
18381874 if (inst_ty.isSignedInt()) return self.builder.buildNSWSub(lhs, rhs, "");
18391875 return self.builder.buildNUWSub(lhs, rhs, "");
......@@ -1848,7 +1884,7 @@ pub const FuncGen = struct {
18481884 const rhs = try self.resolveInst(bin_op.rhs);
18491885 const inst_ty = self.air.typeOfIndex(inst);
18501886
1851 if (inst_ty.isFloat()) return self.builder.buildFMul(lhs, rhs, "");
1887 if (inst_ty.isRuntimeFloat()) return self.builder.buildFMul(lhs, rhs, "");
18521888 if (wrap) return self.builder.buildMul(lhs, rhs, "");
18531889 if (inst_ty.isSignedInt()) return self.builder.buildNSWMul(lhs, rhs, "");
18541890 return self.builder.buildNUWMul(lhs, rhs, "");
......@@ -1863,7 +1899,7 @@ pub const FuncGen = struct {
18631899 const rhs = try self.resolveInst(bin_op.rhs);
18641900 const inst_ty = self.air.typeOfIndex(inst);
18651901
1866 if (inst_ty.isFloat()) return self.builder.buildFDiv(lhs, rhs, "");
1902 if (inst_ty.isRuntimeFloat()) return self.builder.buildFDiv(lhs, rhs, "");
18671903 if (inst_ty.isSignedInt()) return self.builder.buildSDiv(lhs, rhs, "");
18681904 return self.builder.buildUDiv(lhs, rhs, "");
18691905 }
......@@ -1876,7 +1912,7 @@ pub const FuncGen = struct {
18761912 const rhs = try self.resolveInst(bin_op.rhs);
18771913 const inst_ty = self.air.typeOfIndex(inst);
18781914
1879 if (inst_ty.isFloat()) return self.builder.buildFRem(lhs, rhs, "");
1915 if (inst_ty.isRuntimeFloat()) return self.builder.buildFRem(lhs, rhs, "");
18801916 if (inst_ty.isSignedInt()) return self.builder.buildSRem(lhs, rhs, "");
18811917 return self.builder.buildURem(lhs, rhs, "");
18821918 }
......@@ -2165,7 +2201,7 @@ pub const FuncGen = struct {
21652201 const operand_ty = ptr_ty.elemType();
21662202 const operand = try self.resolveInst(extra.operand);
21672203 const is_signed_int = operand_ty.isSignedInt();
2168 const is_float = operand_ty.isFloat();
2204 const is_float = operand_ty.isRuntimeFloat();
21692205 const op = toLlvmAtomicRmwBinOp(extra.op(), is_signed_int, is_float);
21702206 const ordering = toLlvmAtomicOrdering(extra.ordering());
21712207 const single_threaded = llvm.Bool.fromBool(self.single_threaded);
src/codegen/llvm/bindings.zig+32
......@@ -563,6 +563,38 @@ pub const Builder = opaque {
563563 ordering: AtomicOrdering,
564564 singleThread: Bool,
565565 ) *const Value;
566
567 pub const buildFPToUI = LLVMBuildFPToUI;
568 extern fn LLVMBuildFPToUI(
569 *const Builder,
570 Val: *const Value,
571 DestTy: *const Type,
572 Name: [*:0]const u8,
573 ) *const Value;
574
575 pub const buildFPToSI = LLVMBuildFPToSI;
576 extern fn LLVMBuildFPToSI(
577 *const Builder,
578 Val: *const Value,
579 DestTy: *const Type,
580 Name: [*:0]const u8,
581 ) *const Value;
582
583 pub const buildUIToFP = LLVMBuildUIToFP;
584 extern fn LLVMBuildUIToFP(
585 *const Builder,
586 Val: *const Value,
587 DestTy: *const Type,
588 Name: [*:0]const u8,
589 ) *const Value;
590
591 pub const buildSIToFP = LLVMBuildSIToFP;
592 extern fn LLVMBuildSIToFP(
593 *const Builder,
594 Val: *const Value,
595 DestTy: *const Type,
596 Name: [*:0]const u8,
597 ) *const Value;
566598};
567599
568600pub const IntPredicate = enum(c_uint) {
src/print_air.zig+2
......@@ -175,6 +175,8 @@ const Writer = struct {
175175 .struct_field_ptr_index_2,
176176 .struct_field_ptr_index_3,
177177 .array_to_slice,
178 .int_to_float,
179 .float_to_int,
178180 => try w.writeTyOp(s, inst),
179181
180182 .block,
src/type.zig+31-14
......@@ -2523,7 +2523,8 @@ pub const Type = extern union {
25232523 };
25242524 }
25252525
2526 pub fn isFloat(self: Type) bool {
2526 /// Returns `false` for `comptime_float`.
2527 pub fn isRuntimeFloat(self: Type) bool {
25272528 return switch (self.tag()) {
25282529 .f16,
25292530 .f32,
......@@ -2536,13 +2537,29 @@ pub const Type = extern union {
25362537 };
25372538 }
25382539
2539 /// Asserts the type is a fixed-size float.
2540 /// Returns `true` for `comptime_float`.
2541 pub fn isAnyFloat(self: Type) bool {
2542 return switch (self.tag()) {
2543 .f16,
2544 .f32,
2545 .f64,
2546 .f128,
2547 .c_longdouble,
2548 .comptime_float,
2549 => true,
2550
2551 else => false,
2552 };
2553 }
2554
2555 /// Asserts the type is a fixed-size float or comptime_float.
2556 /// Returns 128 for comptime_float types.
25402557 pub fn floatBits(self: Type, target: Target) u16 {
25412558 return switch (self.tag()) {
25422559 .f16 => 16,
25432560 .f32 => 32,
25442561 .f64 => 64,
2545 .f128 => 128,
2562 .f128, .comptime_float => 128,
25462563 .c_longdouble => CType.longdouble.sizeInBits(target),
25472564
25482565 else => unreachable,
......@@ -2879,7 +2896,7 @@ pub const Type = extern union {
28792896 }
28802897
28812898 /// Asserts that self.zigTypeTag() == .Int.
2882 pub fn minInt(self: Type, arena: *std.heap.ArenaAllocator, target: Target) !Value {
2899 pub fn minInt(self: Type, arena: *Allocator, target: Target) !Value {
28832900 assert(self.zigTypeTag() == .Int);
28842901 const info = self.intInfo(target);
28852902
......@@ -2889,35 +2906,35 @@ pub const Type = extern union {
28892906
28902907 if ((info.bits - 1) <= std.math.maxInt(u6)) {
28912908 const n: i64 = -(@as(i64, 1) << @truncate(u6, info.bits - 1));
2892 return Value.Tag.int_i64.create(&arena.allocator, n);
2909 return Value.Tag.int_i64.create(arena, n);
28932910 }
28942911
2895 var res = try std.math.big.int.Managed.initSet(&arena.allocator, 1);
2912 var res = try std.math.big.int.Managed.initSet(arena, 1);
28962913 try res.shiftLeft(res, info.bits - 1);
28972914 res.negate();
28982915
28992916 const res_const = res.toConst();
29002917 if (res_const.positive) {
2901 return Value.Tag.int_big_positive.create(&arena.allocator, res_const.limbs);
2918 return Value.Tag.int_big_positive.create(arena, res_const.limbs);
29022919 } else {
2903 return Value.Tag.int_big_negative.create(&arena.allocator, res_const.limbs);
2920 return Value.Tag.int_big_negative.create(arena, res_const.limbs);
29042921 }
29052922 }
29062923
29072924 /// Asserts that self.zigTypeTag() == .Int.
2908 pub fn maxInt(self: Type, arena: *std.heap.ArenaAllocator, target: Target) !Value {
2925 pub fn maxInt(self: Type, arena: *Allocator, target: Target) !Value {
29092926 assert(self.zigTypeTag() == .Int);
29102927 const info = self.intInfo(target);
29112928
29122929 if (info.signedness == .signed and (info.bits - 1) <= std.math.maxInt(u6)) {
29132930 const n: i64 = (@as(i64, 1) << @truncate(u6, info.bits - 1)) - 1;
2914 return Value.Tag.int_i64.create(&arena.allocator, n);
2931 return Value.Tag.int_i64.create(arena, n);
29152932 } else if (info.signedness == .signed and info.bits <= std.math.maxInt(u6)) {
29162933 const n: u64 = (@as(u64, 1) << @truncate(u6, info.bits)) - 1;
2917 return Value.Tag.int_u64.create(&arena.allocator, n);
2934 return Value.Tag.int_u64.create(arena, n);
29182935 }
29192936
2920 var res = try std.math.big.int.Managed.initSet(&arena.allocator, 1);
2937 var res = try std.math.big.int.Managed.initSet(arena, 1);
29212938 try res.shiftLeft(res, info.bits - @boolToInt(info.signedness == .signed));
29222939 const one = std.math.big.int.Const{
29232940 .limbs = &[_]std.math.big.Limb{1},
......@@ -2927,9 +2944,9 @@ pub const Type = extern union {
29272944
29282945 const res_const = res.toConst();
29292946 if (res_const.positive) {
2930 return Value.Tag.int_big_positive.create(&arena.allocator, res_const.limbs);
2947 return Value.Tag.int_big_positive.create(arena, res_const.limbs);
29312948 } else {
2932 return Value.Tag.int_big_negative.create(&arena.allocator, res_const.limbs);
2949 return Value.Tag.int_big_negative.create(arena, res_const.limbs);
29332950 }
29342951 }
29352952
src/value.zig+224
......@@ -1524,6 +1524,230 @@ pub const Value = extern union {
15241524 };
15251525 }
15261526
1527 pub fn intToFloat(val: Value, allocator: *Allocator, dest_ty: Type, target: Target) !Value {
1528 switch (val.tag()) {
1529 .undef, .zero, .one => return val,
1530 .int_u64 => {
1531 return intToFloatInner(val.castTag(.int_u64).?.data, allocator, dest_ty, target);
1532 },
1533 .int_i64 => {
1534 return intToFloatInner(val.castTag(.int_i64).?.data, allocator, dest_ty, target);
1535 },
1536 .int_big_positive, .int_big_negative => @panic("big int to float"),
1537 else => unreachable,
1538 }
1539 }
1540
1541 fn intToFloatInner(x: anytype, arena: *Allocator, dest_ty: Type, target: Target) !Value {
1542 switch (dest_ty.floatBits(target)) {
1543 16 => return Value.Tag.float_16.create(arena, @intToFloat(f16, x)),
1544 32 => return Value.Tag.float_32.create(arena, @intToFloat(f32, x)),
1545 64 => return Value.Tag.float_64.create(arena, @intToFloat(f64, x)),
1546 128 => return Value.Tag.float_128.create(arena, @intToFloat(f128, x)),
1547 else => unreachable,
1548 }
1549 }
1550
1551 /// Supports both floats and ints; handles undefined.
1552 pub fn numberAddWrap(
1553 lhs: Value,
1554 rhs: Value,
1555 ty: Type,
1556 arena: *Allocator,
1557 target: Target,
1558 ) !Value {
1559 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
1560
1561 if (ty.isAnyFloat()) {
1562 return floatAdd(lhs, rhs, ty, arena);
1563 }
1564 const result = try intAdd(lhs, rhs, arena);
1565
1566 const max = try ty.maxInt(arena, target);
1567 if (compare(result, .gt, max, ty)) {
1568 @panic("TODO comptime wrapping integer addition");
1569 }
1570
1571 const min = try ty.minInt(arena, target);
1572 if (compare(result, .lt, min, ty)) {
1573 @panic("TODO comptime wrapping integer addition");
1574 }
1575
1576 return result;
1577 }
1578
1579 /// Supports both floats and ints; handles undefined.
1580 pub fn numberSubWrap(
1581 lhs: Value,
1582 rhs: Value,
1583 ty: Type,
1584 arena: *Allocator,
1585 target: Target,
1586 ) !Value {
1587 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
1588
1589 if (ty.isAnyFloat()) {
1590 return floatSub(lhs, rhs, ty, arena);
1591 }
1592 const result = try intSub(lhs, rhs, arena);
1593
1594 const max = try ty.maxInt(arena, target);
1595 if (compare(result, .gt, max, ty)) {
1596 @panic("TODO comptime wrapping integer subtraction");
1597 }
1598
1599 const min = try ty.minInt(arena, target);
1600 if (compare(result, .lt, min, ty)) {
1601 @panic("TODO comptime wrapping integer subtraction");
1602 }
1603
1604 return result;
1605 }
1606
1607 /// Supports both floats and ints; handles undefined.
1608 pub fn numberMax(lhs: Value, rhs: Value, arena: *Allocator) !Value {
1609 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
1610
1611 // TODO is this a performance issue? maybe we should try the operation without
1612 // resorting to BigInt first.
1613 var lhs_space: Value.BigIntSpace = undefined;
1614 var rhs_space: Value.BigIntSpace = undefined;
1615 const lhs_bigint = lhs.toBigInt(&lhs_space);
1616 const rhs_bigint = rhs.toBigInt(&rhs_space);
1617 const limbs = try arena.alloc(
1618 std.math.big.Limb,
1619 std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
1620 );
1621 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1622
1623 switch (lhs_bigint.order(rhs_bigint)) {
1624 .lt => result_bigint.copy(rhs_bigint),
1625 .gt, .eq => result_bigint.copy(lhs_bigint),
1626 }
1627
1628 const result_limbs = result_bigint.limbs[0..result_bigint.len];
1629
1630 if (result_bigint.positive) {
1631 return Value.Tag.int_big_positive.create(arena, result_limbs);
1632 } else {
1633 return Value.Tag.int_big_negative.create(arena, result_limbs);
1634 }
1635 }
1636
1637 /// Supports both floats and ints; handles undefined.
1638 pub fn numberMin(lhs: Value, rhs: Value, arena: *Allocator) !Value {
1639 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
1640
1641 // TODO is this a performance issue? maybe we should try the operation without
1642 // resorting to BigInt first.
1643 var lhs_space: Value.BigIntSpace = undefined;
1644 var rhs_space: Value.BigIntSpace = undefined;
1645 const lhs_bigint = lhs.toBigInt(&lhs_space);
1646 const rhs_bigint = rhs.toBigInt(&rhs_space);
1647 const limbs = try arena.alloc(
1648 std.math.big.Limb,
1649 std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
1650 );
1651 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1652
1653 switch (lhs_bigint.order(rhs_bigint)) {
1654 .lt => result_bigint.copy(lhs_bigint),
1655 .gt, .eq => result_bigint.copy(rhs_bigint),
1656 }
1657
1658 const result_limbs = result_bigint.limbs[0..result_bigint.len];
1659
1660 if (result_bigint.positive) {
1661 return Value.Tag.int_big_positive.create(arena, result_limbs);
1662 } else {
1663 return Value.Tag.int_big_negative.create(arena, result_limbs);
1664 }
1665 }
1666
1667 /// operands must be integers; handles undefined.
1668 pub fn bitwiseAnd(lhs: Value, rhs: Value, arena: *Allocator) !Value {
1669 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
1670
1671 // TODO is this a performance issue? maybe we should try the operation without
1672 // resorting to BigInt first.
1673 var lhs_space: Value.BigIntSpace = undefined;
1674 var rhs_space: Value.BigIntSpace = undefined;
1675 const lhs_bigint = lhs.toBigInt(&lhs_space);
1676 const rhs_bigint = rhs.toBigInt(&rhs_space);
1677 const limbs = try arena.alloc(
1678 std.math.big.Limb,
1679 std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
1680 );
1681 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1682 result_bigint.bitAnd(lhs_bigint, rhs_bigint);
1683 const result_limbs = result_bigint.limbs[0..result_bigint.len];
1684
1685 if (result_bigint.positive) {
1686 return Value.Tag.int_big_positive.create(arena, result_limbs);
1687 } else {
1688 return Value.Tag.int_big_negative.create(arena, result_limbs);
1689 }
1690 }
1691
1692 /// operands must be integers; handles undefined.
1693 pub fn bitwiseNand(lhs: Value, rhs: Value, ty: Type, arena: *Allocator) !Value {
1694 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
1695
1696 _ = ty;
1697 _ = arena;
1698 @panic("TODO comptime bitwise NAND");
1699 }
1700
1701 /// operands must be integers; handles undefined.
1702 pub fn bitwiseOr(lhs: Value, rhs: Value, arena: *Allocator) !Value {
1703 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
1704
1705 // TODO is this a performance issue? maybe we should try the operation without
1706 // resorting to BigInt first.
1707 var lhs_space: Value.BigIntSpace = undefined;
1708 var rhs_space: Value.BigIntSpace = undefined;
1709 const lhs_bigint = lhs.toBigInt(&lhs_space);
1710 const rhs_bigint = rhs.toBigInt(&rhs_space);
1711 const limbs = try arena.alloc(
1712 std.math.big.Limb,
1713 std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
1714 );
1715 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1716 result_bigint.bitOr(lhs_bigint, rhs_bigint);
1717 const result_limbs = result_bigint.limbs[0..result_bigint.len];
1718
1719 if (result_bigint.positive) {
1720 return Value.Tag.int_big_positive.create(arena, result_limbs);
1721 } else {
1722 return Value.Tag.int_big_negative.create(arena, result_limbs);
1723 }
1724 }
1725
1726 /// operands must be integers; handles undefined.
1727 pub fn bitwiseXor(lhs: Value, rhs: Value, arena: *Allocator) !Value {
1728 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
1729
1730 // TODO is this a performance issue? maybe we should try the operation without
1731 // resorting to BigInt first.
1732 var lhs_space: Value.BigIntSpace = undefined;
1733 var rhs_space: Value.BigIntSpace = undefined;
1734 const lhs_bigint = lhs.toBigInt(&lhs_space);
1735 const rhs_bigint = rhs.toBigInt(&rhs_space);
1736 const limbs = try arena.alloc(
1737 std.math.big.Limb,
1738 std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
1739 );
1740 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1741 result_bigint.bitXor(lhs_bigint, rhs_bigint);
1742 const result_limbs = result_bigint.limbs[0..result_bigint.len];
1743
1744 if (result_bigint.positive) {
1745 return Value.Tag.int_big_positive.create(arena, result_limbs);
1746 } else {
1747 return Value.Tag.int_big_negative.create(arena, result_limbs);
1748 }
1749 }
1750
15271751 pub fn intAdd(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
15281752 // TODO is this a performance issue? maybe we should try the operation without
15291753 // resorting to BigInt first.
test/behavior/atomics.zig+29
......@@ -138,3 +138,32 @@ test "atomic store" {
138138 @atomicStore(u32, &x, 12345678, .SeqCst);
139139 try expect(@atomicLoad(u32, &x, .SeqCst) == 12345678);
140140}
141
142test "atomic store comptime" {
143 comptime try testAtomicStore();
144 try testAtomicStore();
145}
146
147fn testAtomicStore() !void {
148 var x: u32 = 0;
149 @atomicStore(u32, &x, 1, .SeqCst);
150 try expect(@atomicLoad(u32, &x, .SeqCst) == 1);
151 @atomicStore(u32, &x, 12345678, .SeqCst);
152 try expect(@atomicLoad(u32, &x, .SeqCst) == 12345678);
153}
154
155test "atomicrmw with floats" {
156 try testAtomicRmwFloat();
157 comptime try testAtomicRmwFloat();
158}
159
160fn testAtomicRmwFloat() !void {
161 var x: f32 = 0;
162 try expect(x == 0);
163 _ = @atomicRmw(f32, &x, .Xchg, 1, .SeqCst);
164 try expect(x == 1);
165 _ = @atomicRmw(f32, &x, .Add, 5, .SeqCst);
166 try expect(x == 6);
167 _ = @atomicRmw(f32, &x, .Sub, 2, .SeqCst);
168 try expect(x == 4);
169}
test/behavior/atomics_stage1.zig-29
......@@ -3,35 +3,6 @@ const expect = std.testing.expect;
33const expectEqual = std.testing.expectEqual;
44const builtin = @import("builtin");
55
6test "atomic store comptime" {
7 comptime try testAtomicStore();
8 try testAtomicStore();
9}
10
11fn testAtomicStore() !void {
12 var x: u32 = 0;
13 @atomicStore(u32, &x, 1, .SeqCst);
14 try expect(@atomicLoad(u32, &x, .SeqCst) == 1);
15 @atomicStore(u32, &x, 12345678, .SeqCst);
16 try expect(@atomicLoad(u32, &x, .SeqCst) == 12345678);
17}
18
19test "atomicrmw with floats" {
20 try testAtomicRmwFloat();
21 comptime try testAtomicRmwFloat();
22}
23
24fn testAtomicRmwFloat() !void {
25 var x: f32 = 0;
26 try expect(x == 0);
27 _ = @atomicRmw(f32, &x, .Xchg, 1, .SeqCst);
28 try expect(x == 1);
29 _ = @atomicRmw(f32, &x, .Add, 5, .SeqCst);
30 try expect(x == 6);
31 _ = @atomicRmw(f32, &x, .Sub, 2, .SeqCst);
32 try expect(x == 4);
33}
34
356test "atomicrmw with ints" {
367 try testAtomicRmwInt();
378 comptime try testAtomicRmwInt();