| author | |
| committer | |
| log | 710b05b15302f05f98a31635af6d654858215f34 |
| tree | 9714ad94ff090312e7df8d0ccc4d1e516e4b0668 |
| parent | 9262f065f50db3ea72454b6c1b9b66fd911aa6ba |
| signature |
3 files changed, 114 insertions(+), 15 deletions(-)
src/ir.cpp+76-6| ... | @@ -28436,14 +28436,84 @@ static IrInstGen *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstSrcAto | ... | @@ -28436,14 +28436,84 @@ static IrInstGen *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstSrcAto |
| 28436 | return ir_const_move(ira, &instruction->base.base, get_the_one_possible_value(ira->codegen, operand_type)); | 28436 | return ir_const_move(ira, &instruction->base.base, get_the_one_possible_value(ira->codegen, operand_type)); |
| 28437 | } | 28437 | } |
| 28438 | 28438 | ||
| 28439 | if (instr_is_comptime(casted_operand) && instr_is_comptime(casted_ptr) && casted_ptr->value->data.x_ptr.mut == ConstPtrMutComptimeVar) | 28439 | IrInst *source_inst = &instruction->base.base; |
| 28440 | { | 28440 | if (instr_is_comptime(casted_operand) && instr_is_comptime(casted_ptr) && casted_ptr->value->data.x_ptr.mut == ConstPtrMutComptimeVar) { |
| 28441 | ir_add_error(ira, &instruction->base.base, | 28441 | ZigValue *ptr_val = ir_resolve_const(ira, casted_ptr, UndefBad); |
| 28442 | buf_sprintf("compiler bug: TODO compile-time execution of @atomicRmw")); | 28442 | if (ptr_val == nullptr) |
| 28443 | return ira->codegen->invalid_inst_gen; | 28443 | return ira->codegen->invalid_inst_gen; |
| 28444 | |||
| 28445 | ZigValue *op1_val = const_ptr_pointee(ira, ira->codegen, ptr_val, instruction->base.base.source_node); | ||
| 28446 | if (op1_val == nullptr) | ||
| 28447 | return ira->codegen->invalid_inst_gen; | ||
| 28448 | |||
| 28449 | ZigValue *op2_val = ir_resolve_const(ira, casted_operand, UndefBad); | ||
| 28450 | if (op2_val == nullptr) | ||
| 28451 | return ira->codegen->invalid_inst_gen; | ||
| 28452 | |||
| 28453 | if (op == AtomicRmwOp_xchg) { | ||
| 28454 | ir_analyze_store_ptr(ira, source_inst, casted_ptr, casted_operand, false); | ||
| 28455 | return ir_const_move(ira, source_inst, op1_val); | ||
| 28456 | } | ||
| 28457 | |||
| 28458 | if (operand_type->id == ZigTypeIdPointer || operand_type->id == ZigTypeIdOptional) { | ||
| 28459 | ir_add_error(ira, &instruction->ordering->base, | ||
| 28460 | buf_sprintf("TODO comptime @atomicRmw with pointers other than .Xchg")); | ||
| 28461 | return ira->codegen->invalid_inst_gen; | ||
| 28462 | } | ||
| 28463 | |||
| 28464 | if (op == AtomicRmwOp_min || op == AtomicRmwOp_max) { | ||
| 28465 | IrBinOp bin_op; | ||
| 28466 | if (op == AtomicRmwOp_min) | ||
| 28467 | // store op2 if op2 < op1 | ||
| 28468 | bin_op = IrBinOpCmpGreaterThan; | ||
| 28469 | else | ||
| 28470 | // store op2 if op2 > op1 | ||
| 28471 | bin_op = IrBinOpCmpLessThan; | ||
| 28472 | |||
| 28473 | IrInstGen *dummy_value = ir_const(ira, source_inst, operand_type); | ||
| 28474 | ir_eval_bin_op_cmp_scalar(ira, source_inst, op1_val, bin_op, op2_val, dummy_value->value); | ||
| 28475 | if (dummy_value->value->data.x_bool) | ||
| 28476 | ir_analyze_store_ptr(ira, source_inst, casted_ptr, casted_operand, false); | ||
| 28477 | } else { | ||
| 28478 | IrBinOp bin_op; | ||
| 28479 | switch (op) { | ||
| 28480 | case AtomicRmwOp_xchg: | ||
| 28481 | case AtomicRmwOp_max: | ||
| 28482 | case AtomicRmwOp_min: | ||
| 28483 | zig_unreachable(); | ||
| 28484 | case AtomicRmwOp_add: | ||
| 28485 | if (operand_type->id == ZigTypeIdFloat) | ||
| 28486 | bin_op = IrBinOpAdd; | ||
| 28487 | else | ||
| 28488 | bin_op = IrBinOpAddWrap; | ||
| 28489 | break; | ||
| 28490 | case AtomicRmwOp_sub: | ||
| 28491 | if (operand_type->id == ZigTypeIdFloat) | ||
| 28492 | bin_op = IrBinOpSub; | ||
| 28493 | else | ||
| 28494 | bin_op = IrBinOpSubWrap; | ||
| 28495 | break; | ||
| 28496 | case AtomicRmwOp_and: | ||
| 28497 | case AtomicRmwOp_nand: | ||
| 28498 | bin_op = IrBinOpBinAnd; | ||
| 28499 | break; | ||
| 28500 | case AtomicRmwOp_or: | ||
| 28501 | bin_op = IrBinOpBinOr; | ||
| 28502 | break; | ||
| 28503 | case AtomicRmwOp_xor: | ||
| 28504 | bin_op = IrBinOpBinXor; | ||
| 28505 | break; | ||
| 28506 | } | ||
| 28507 | ir_eval_math_op_scalar(ira, source_inst, operand_type, op1_val, bin_op, op2_val, op1_val); | ||
| 28508 | if (op == AtomicRmwOp_nand) { | ||
| 28509 | bigint_not(&op1_val->data.x_bigint, &op1_val->data.x_bigint, | ||
| 28510 | operand_type->data.integral.bit_count, operand_type->data.integral.is_signed); | ||
| 28511 | } | ||
| 28512 | } | ||
| 28513 | return ir_const_move(ira, source_inst, op1_val); | ||
| 28444 | } | 28514 | } |
| 28445 | 28515 | ||
| 28446 | return ir_build_atomic_rmw_gen(ira, &instruction->base.base, casted_ptr, casted_operand, op, | 28516 | return ir_build_atomic_rmw_gen(ira, source_inst, casted_ptr, casted_operand, op, |
| 28447 | ordering, operand_type); | 28517 | ordering, operand_type); |
| 28448 | } | 28518 | } |
| 28449 | 28519 |
test/compile_errors.zig+9-9| ... | @@ -2,15 +2,6 @@ const tests = @import("tests.zig"); | ... | @@ -2,15 +2,6 @@ const tests = @import("tests.zig"); |
| 2 | const std = @import("std"); | 2 | const std = @import("std"); |
| 3 | 3 | ||
| 4 | pub fn addCases(cases: *tests.CompileErrorContext) void { | 4 | pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5 | cases.add("atomicrmw with bool op not .Xchg", | ||
| 6 | \\export fn entry() void { | ||
| 7 | \\ var x = false; | ||
| 8 | \\ _ = @atomicRmw(bool, &x, .Add, true, .SeqCst); | ||
| 9 | \\} | ||
| 10 | , &[_][]const u8{ | ||
| 11 | "tmp.zig:3:30: error: @atomicRmw with bool only allowed with .Xchg", | ||
| 12 | }); | ||
| 13 | |||
| 14 | cases.addTest("combination of noasync and async", | 5 | cases.addTest("combination of noasync and async", |
| 15 | \\export fn entry() void { | 6 | \\export fn entry() void { |
| 16 | \\ noasync { | 7 | \\ noasync { |
| ... | @@ -26,6 +17,15 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { | ... | @@ -26,6 +17,15 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 26 | "tmp.zig:5:9: error: resume in noasync scope", | 17 | "tmp.zig:5:9: error: resume in noasync scope", |
| 27 | }); | 18 | }); |
| 28 | 19 | ||
| 20 | cases.add("atomicrmw with bool op not .Xchg", | ||
| 21 | \\export fn entry() void { | ||
| 22 | \\ var x = false; | ||
| 23 | \\ _ = @atomicRmw(bool, &x, .Add, true, .SeqCst); | ||
| 24 | \\} | ||
| 25 | , &[_][]const u8{ | ||
| 26 | "tmp.zig:3:30: error: @atomicRmw with bool only allowed with .Xchg", | ||
| 27 | }); | ||
| 28 | |||
| 29 | cases.addTest("@TypeOf with no arguments", | 29 | cases.addTest("@TypeOf with no arguments", |
| 30 | \\export fn entry() void { | 30 | \\export fn entry() void { |
| 31 | \\ _ = @TypeOf(); | 31 | \\ _ = @TypeOf(); |
test/stage1/behavior/atomics.zig+29| ... | @@ -149,6 +149,7 @@ fn testAtomicStore() void { | ... | @@ -149,6 +149,7 @@ fn testAtomicStore() void { |
| 149 | } | 149 | } |
| 150 | 150 | ||
| 151 | test "atomicrmw with floats" { | 151 | test "atomicrmw with floats" { |
| 152 | comptime testAtomicRmwFloat(); | ||
| 152 | if (builtin.arch == .aarch64 or builtin.arch == .arm or builtin.arch == .riscv64) | 153 | if (builtin.arch == .aarch64 or builtin.arch == .arm or builtin.arch == .riscv64) |
| 153 | return error.SkipZigTest; | 154 | return error.SkipZigTest; |
| 154 | testAtomicRmwFloat(); | 155 | testAtomicRmwFloat(); |
| ... | @@ -165,6 +166,34 @@ fn testAtomicRmwFloat() void { | ... | @@ -165,6 +166,34 @@ fn testAtomicRmwFloat() void { |
| 165 | expect(x == 4); | 166 | expect(x == 4); |
| 166 | } | 167 | } |
| 167 | 168 | ||
| 169 | test "atomicrmw with ints" { | ||
| 170 | testAtomicRmwFloat(); | ||
| 171 | comptime testAtomicRmwFloat(); | ||
| 172 | } | ||
| 173 | |||
| 174 | fn testAtomicRmwInt() void { | ||
| 175 | var x: u8 = 1; | ||
| 176 | _ = @atomicRmw(u8, &x, .Xchg, 3, .SeqCst); | ||
| 177 | expect(x == 3); | ||
| 178 | _ = @atomicRmw(u8, &x, .Add, 3, .SeqCst); | ||
| 179 | expect(x == 6); | ||
| 180 | _ = @atomicRmw(u8, &x, .Sub, 1, .SeqCst); | ||
| 181 | expect(x == 5); | ||
| 182 | _ = @atomicRmw(u8, &x, .And, 4, .SeqCst); | ||
| 183 | expect(x == 4); | ||
| 184 | _ = @atomicRmw(u8, &x, .Nand, 4, .SeqCst); | ||
| 185 | expect(x == 0xfb); | ||
| 186 | _ = @atomicRmw(u8, &x, .Or, 6, .SeqCst); | ||
| 187 | expect(x == 0xff); | ||
| 188 | _ = @atomicRmw(u8, &x, .Xor, 2, .SeqCst); | ||
| 189 | expect(x == 0xfd); | ||
| 190 | _ = @atomicRmw(u8, &x, .Max, 1, .SeqCst); | ||
| 191 | expect(x == 0xfd); | ||
| 192 | _ = @atomicRmw(u8, &x, .Min, 1, .SeqCst); | ||
| 193 | expect(x == 1); | ||
| 194 | } | ||
| 195 | |||
| 196 | |||
| 168 | test "atomics with different types" { | 197 | test "atomics with different types" { |
| 169 | testAtomicsWithType(bool, true, false); | 198 | testAtomicsWithType(bool, true, false); |
| 170 | inline for (.{ u1, i5, u15 }) |T| { | 199 | inline for (.{ u1, i5, u15 }) |T| { |