diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2020-03-12 18:55:16 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-03-12 18:55:16 -0400 |
| commit | f51bec321b1ef9196b143295d47a9ea919cdba28 (patch) | |
| tree | ec4871b071e28a1b4381399c166b5be7f77e1532 /src/ir.cpp | |
| parent | aa49f972d655eb61ca899f76ba37933254f780a2 (diff) | |
| parent | 71d776c3be91f6b4e982b45fbfe03e3696a397f5 (diff) | |
| download | zig-f51bec321b1ef9196b143295d47a9ea919cdba28.tar.gz zig-f51bec321b1ef9196b143295d47a9ea919cdba28.zip | |
Merge pull request #4707 from Vexu/small-atomics
Support atomic operations with bools and non power of two integers
Diffstat (limited to 'src/ir.cpp')
| -rw-r--r-- | src/ir.cpp | 206 |
1 files changed, 163 insertions, 43 deletions
diff --git a/src/ir.cpp b/src/ir.cpp index bb2dc75c64..b9875a7efe 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -25208,12 +25208,50 @@ static IrInstGen *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstSrcCmpxch return ira->codegen->invalid_inst_gen; } + ZigType *result_type = get_optional_type(ira->codegen, operand_type); + + // special case zero bit types + switch (type_has_one_possible_value(ira->codegen, operand_type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_inst_gen; + case OnePossibleValueYes: { + IrInstGen *result = ir_const(ira, &instruction->base.base, result_type); + set_optional_value_to_null(result->value); + return result; + } + case OnePossibleValueNo: + break; + } + if (instr_is_comptime(casted_ptr) && casted_ptr->value->data.x_ptr.mut != ConstPtrMutRuntimeVar && instr_is_comptime(casted_cmp_value) && instr_is_comptime(casted_new_value)) { - zig_panic("TODO compile-time execution of cmpxchg"); + ZigValue *ptr_val = ir_resolve_const(ira, casted_ptr, UndefBad); + if (ptr_val == nullptr) + return ira->codegen->invalid_inst_gen; + + ZigValue *stored_val = const_ptr_pointee(ira, ira->codegen, ptr_val, instruction->base.base.source_node); + if (stored_val == nullptr) + return ira->codegen->invalid_inst_gen; + + ZigValue *expected_val = ir_resolve_const(ira, casted_cmp_value, UndefBad); + if (expected_val == nullptr) + return ira->codegen->invalid_inst_gen; + + ZigValue *new_val = ir_resolve_const(ira, casted_new_value, UndefBad); + if (new_val == nullptr) + return ira->codegen->invalid_inst_gen; + + bool eql = const_values_equal(ira->codegen, stored_val, expected_val); + IrInstGen *result = ir_const(ira, &instruction->base.base, result_type); + if (eql) { + copy_const_val(ira->codegen, stored_val, new_val); + set_optional_value_to_null(result->value); + } else { + set_optional_payload(result->value, stored_val); + } + return result; } - ZigType *result_type = get_optional_type(ira->codegen, operand_type); IrInstGen *result_loc; if (handle_is_ptr(ira->codegen, result_type)) { result_loc = ir_resolve_result(ira, &instruction->base.base, instruction->result_loc, @@ -28324,43 +28362,20 @@ static ZigType *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstGen *op) { if (type_is_invalid(operand_type)) return ira->codegen->builtin_types.entry_invalid; - if (operand_type->id == ZigTypeIdInt) { - if (operand_type->data.integral.bit_count < 8) { - ir_add_error(ira, &op->base, - buf_sprintf("expected integer type 8 bits or larger, found %" PRIu32 "-bit integer type", - operand_type->data.integral.bit_count)); - return ira->codegen->builtin_types.entry_invalid; + if (operand_type->id == ZigTypeIdInt || operand_type->id == ZigTypeIdEnum) { + ZigType *int_type; + if (operand_type->id == ZigTypeIdEnum) { + int_type = operand_type->data.enumeration.tag_int_type; + } else { + int_type = operand_type; } + auto bit_count = int_type->data.integral.bit_count; uint32_t max_atomic_bits = target_arch_largest_atomic_bits(ira->codegen->zig_target->arch); - if (operand_type->data.integral.bit_count > max_atomic_bits) { + + if (bit_count > max_atomic_bits) { ir_add_error(ira, &op->base, buf_sprintf("expected %" PRIu32 "-bit integer type or smaller, found %" PRIu32 "-bit integer type", - max_atomic_bits, operand_type->data.integral.bit_count)); - return ira->codegen->builtin_types.entry_invalid; - } - if (!is_power_of_2(operand_type->data.integral.bit_count)) { - ir_add_error(ira, &op->base, - buf_sprintf("%" PRIu32 "-bit integer type is not a power of 2", operand_type->data.integral.bit_count)); - return ira->codegen->builtin_types.entry_invalid; - } - } else if (operand_type->id == ZigTypeIdEnum) { - ZigType *int_type = operand_type->data.enumeration.tag_int_type; - if (int_type->data.integral.bit_count < 8) { - ir_add_error(ira, &op->base, - buf_sprintf("expected enum tag type 8 bits or larger, found %" PRIu32 "-bit tag type", - int_type->data.integral.bit_count)); - return ira->codegen->builtin_types.entry_invalid; - } - uint32_t max_atomic_bits = target_arch_largest_atomic_bits(ira->codegen->zig_target->arch); - if (int_type->data.integral.bit_count > max_atomic_bits) { - ir_add_error(ira, &op->base, - buf_sprintf("expected %" PRIu32 "-bit enum tag type or smaller, found %" PRIu32 "-bit tag type", - max_atomic_bits, int_type->data.integral.bit_count)); - return ira->codegen->builtin_types.entry_invalid; - } - if (!is_power_of_2(int_type->data.integral.bit_count)) { - ir_add_error(ira, &op->base, - buf_sprintf("%" PRIu32 "-bit enum tag type is not a power of 2", int_type->data.integral.bit_count)); + max_atomic_bits, bit_count)); return ira->codegen->builtin_types.entry_invalid; } } else if (operand_type->id == ZigTypeIdFloat) { @@ -28371,6 +28386,8 @@ static ZigType *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstGen *op) { max_atomic_bits, (uint32_t) operand_type->data.floating.bit_count)); return ira->codegen->builtin_types.entry_invalid; } + } else if (operand_type->id == ZigTypeIdBool) { + // will be treated as u8 } else { Error err; ZigType *operand_ptr_type; @@ -28409,11 +28426,15 @@ static IrInstGen *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstSrcAto if (operand_type->id == ZigTypeIdEnum && op != AtomicRmwOp_xchg) { ir_add_error(ira, &instruction->op->base, - buf_sprintf("@atomicRmw on enum only works with .Xchg")); + buf_sprintf("@atomicRmw with enum only allowed with .Xchg")); + return ira->codegen->invalid_inst_gen; + } else if (operand_type->id == ZigTypeIdBool && op != AtomicRmwOp_xchg) { + ir_add_error(ira, &instruction->op->base, + buf_sprintf("@atomicRmw with bool only allowed with .Xchg")); return ira->codegen->invalid_inst_gen; } else if (operand_type->id == ZigTypeIdFloat && op > AtomicRmwOp_sub) { ir_add_error(ira, &instruction->op->base, - buf_sprintf("@atomicRmw with float only works with .Xchg, .Add and .Sub")); + buf_sprintf("@atomicRmw with float only allowed with .Xchg, .Add and .Sub")); return ira->codegen->invalid_inst_gen; } @@ -28434,14 +28455,103 @@ static IrInstGen *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstSrcAto return ira->codegen->invalid_inst_gen; } - if (instr_is_comptime(casted_operand) && instr_is_comptime(casted_ptr) && casted_ptr->value->data.x_ptr.mut == ConstPtrMutComptimeVar) - { - ir_add_error(ira, &instruction->base.base, - buf_sprintf("compiler bug: TODO compile-time execution of @atomicRmw")); - return ira->codegen->invalid_inst_gen; + // special case zero bit types + switch (type_has_one_possible_value(ira->codegen, operand_type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_inst_gen; + case OnePossibleValueYes: + return ir_const_move(ira, &instruction->base.base, get_the_one_possible_value(ira->codegen, operand_type)); + case OnePossibleValueNo: + break; + } + + IrInst *source_inst = &instruction->base.base; + if (instr_is_comptime(casted_operand) && instr_is_comptime(casted_ptr) && casted_ptr->value->data.x_ptr.mut == ConstPtrMutComptimeVar) { + ZigValue *ptr_val = ir_resolve_const(ira, casted_ptr, UndefBad); + if (ptr_val == nullptr) + return ira->codegen->invalid_inst_gen; + + ZigValue *op1_val = const_ptr_pointee(ira, ira->codegen, ptr_val, instruction->base.base.source_node); + if (op1_val == nullptr) + return ira->codegen->invalid_inst_gen; + + ZigValue *op2_val = ir_resolve_const(ira, casted_operand, UndefBad); + if (op2_val == nullptr) + return ira->codegen->invalid_inst_gen; + + IrInstGen *result = ir_const(ira, source_inst, operand_type); + copy_const_val(ira->codegen, result->value, op1_val); + if (op == AtomicRmwOp_xchg) { + copy_const_val(ira->codegen, op1_val, op2_val); + return result; + } + + if (operand_type->id == ZigTypeIdPointer || operand_type->id == ZigTypeIdOptional) { + ir_add_error(ira, &instruction->ordering->base, + buf_sprintf("TODO comptime @atomicRmw with pointers other than .Xchg")); + return ira->codegen->invalid_inst_gen; + } + + ErrorMsg *msg; + if (op == AtomicRmwOp_min || op == AtomicRmwOp_max) { + IrBinOp bin_op; + if (op == AtomicRmwOp_min) + // store op2 if op2 < op1 + bin_op = IrBinOpCmpGreaterThan; + else + // store op2 if op2 > op1 + bin_op = IrBinOpCmpLessThan; + + IrInstGen *dummy_value = ir_const(ira, source_inst, operand_type); + msg = ir_eval_bin_op_cmp_scalar(ira, source_inst, op1_val, bin_op, op2_val, dummy_value->value); + if (msg != nullptr) { + return ira->codegen->invalid_inst_gen; + } + if (dummy_value->value->data.x_bool) + copy_const_val(ira->codegen, op1_val, op2_val); + } else { + IrBinOp bin_op; + switch (op) { + case AtomicRmwOp_xchg: + case AtomicRmwOp_max: + case AtomicRmwOp_min: + zig_unreachable(); + case AtomicRmwOp_add: + if (operand_type->id == ZigTypeIdFloat) + bin_op = IrBinOpAdd; + else + bin_op = IrBinOpAddWrap; + break; + case AtomicRmwOp_sub: + if (operand_type->id == ZigTypeIdFloat) + bin_op = IrBinOpSub; + else + bin_op = IrBinOpSubWrap; + break; + case AtomicRmwOp_and: + case AtomicRmwOp_nand: + bin_op = IrBinOpBinAnd; + break; + case AtomicRmwOp_or: + bin_op = IrBinOpBinOr; + break; + case AtomicRmwOp_xor: + bin_op = IrBinOpBinXor; + break; + } + msg = ir_eval_math_op_scalar(ira, source_inst, operand_type, op1_val, bin_op, op2_val, op1_val); + if (msg != nullptr) { + return ira->codegen->invalid_inst_gen; + } + if (op == AtomicRmwOp_nand) { + bigint_not(&op1_val->data.x_bigint, &op1_val->data.x_bigint, + operand_type->data.integral.bit_count, operand_type->data.integral.is_signed); + } + } + return result; } - return ir_build_atomic_rmw_gen(ira, &instruction->base.base, casted_ptr, casted_operand, op, + return ir_build_atomic_rmw_gen(ira, source_inst, casted_ptr, casted_operand, op, ordering, operand_type); } @@ -28513,6 +28623,16 @@ static IrInstGen *ir_analyze_instruction_atomic_store(IrAnalyze *ira, IrInstSrcA return ira->codegen->invalid_inst_gen; } + // special case zero bit types + switch (type_has_one_possible_value(ira->codegen, operand_type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_inst_gen; + case OnePossibleValueYes: + return ir_const_void(ira, &instruction->base.base); + case OnePossibleValueNo: + break; + } + if (instr_is_comptime(casted_value) && instr_is_comptime(casted_ptr)) { IrInstGen *result = ir_analyze_store_ptr(ira, &instruction->base.base, casted_ptr, value, false); result->value->type = ira->codegen->builtin_types.entry_void; |
