diff --git a/src/Sema.zig b/src/Sema.zig index fbf25be288..2cc9b82410 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -758,8 +758,8 @@ fn analyzeBodyInner( .is_non_null => try sema.zirIsNonNull(block, inst), .is_non_null_ptr => try sema.zirIsNonNullPtr(block, inst), .merge_error_sets => try sema.zirMergeErrorSets(block, inst), - .negate => try sema.zirNegate(block, inst, .sub), - .negate_wrap => try sema.zirNegate(block, inst, .subwrap), + .negate => try sema.zirNegate(block, inst), + .negate_wrap => try sema.zirNegateWrap(block, inst), .optional_payload_safe => try sema.zirOptionalPayload(block, inst, true), .optional_payload_safe_ptr => try sema.zirOptionalPayloadPtr(block, inst, true), .optional_payload_unsafe => try sema.zirOptionalPayload(block, inst, false), @@ -9328,15 +9328,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai return sema.fail(block, lhs_src, "TODO runtime array_mul", .{}); } -fn zirNegate( - sema: *Sema, - block: *Block, - inst: Zir.Inst.Index, - tag_override: Zir.Inst.Tag, -) CompileError!Air.Inst.Ref { - const tracy = trace(@src()); - defer tracy.end(); - +fn zirNegate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const lhs_src = src; @@ -9346,16 +9338,42 @@ fn zirNegate( const rhs_ty = sema.typeOf(rhs); const rhs_scalar_ty = rhs_ty.scalarType(); - if (tag_override == .sub and rhs_scalar_ty.isUnsignedInt()) { + if (rhs_scalar_ty.isUnsignedInt()) { return sema.fail(block, src, "negation of type '{}'", .{rhs_ty.fmt(sema.mod)}); } + if (rhs_scalar_ty.isAnyFloat()) { + // We handle comptime negation here to ensure negative zero is represented in the bits. + if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| { + if (rhs_val.isUndef()) return sema.addConstUndef(rhs_ty); + const target = sema.mod.getTarget(); + return sema.addConstant(rhs_ty, try rhs_val.floatNeg(rhs_ty, sema.arena, target)); + } + } + const lhs = if (rhs_ty.zigTypeTag() == .Vector) try sema.addConstant(rhs_ty, try Value.Tag.repeated.create(sema.arena, Value.zero)) else sema.resolveInst(.zero); - return sema.analyzeArithmetic(block, tag_override, lhs, rhs, src, lhs_src, rhs_src); + return sema.analyzeArithmetic(block, .sub, lhs, rhs, src, lhs_src, rhs_src); +} + +fn zirNegateWrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const inst_data = sema.code.instructions.items(.data)[inst].un_node; + const src = inst_data.src(); + const lhs_src = src; + const rhs_src = src; // TODO better source location + + const rhs = sema.resolveInst(inst_data.operand); + const rhs_ty = sema.typeOf(rhs); + + const lhs = if (rhs_ty.zigTypeTag() == .Vector) + try sema.addConstant(rhs_ty, try Value.Tag.repeated.create(sema.arena, Value.zero)) + else + sema.resolveInst(.zero); + + return sema.analyzeArithmetic(block, .subwrap, lhs, rhs, src, lhs_src, rhs_src); } fn zirArithmetic( diff --git a/src/value.zig b/src/value.zig index e8ccada8ed..1e70ad0c54 100644 --- a/src/value.zig +++ b/src/value.zig @@ -4155,6 +4155,38 @@ pub const Value = extern union { } } + pub fn floatNeg( + val: Value, + float_type: Type, + arena: Allocator, + target: Target, + ) !Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try floatNegScalar(val.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return floatNegScalar(val, float_type, arena, target); + } + + pub fn floatNegScalar( + val: Value, + float_type: Type, + arena: Allocator, + target: Target, + ) !Value { + switch (float_type.floatBits(target)) { + 16 => return Value.Tag.float_16.create(arena, -val.toFloat(f16)), + 32 => return Value.Tag.float_32.create(arena, -val.toFloat(f32)), + 64 => return Value.Tag.float_64.create(arena, -val.toFloat(f64)), + 80 => return Value.Tag.float_80.create(arena, -val.toFloat(f80)), + 128 => return Value.Tag.float_128.create(arena, -val.toFloat(f128)), + else => unreachable, + } + } + pub fn floatDiv( lhs: Value, rhs: Value, diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 42f2635afd..eb07ffd7a5 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -1593,17 +1593,30 @@ test "compare undefined literal with comptime_int" { } test "signed zeros are represented properly" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { - inline for ([_]type{ f16, f32, f64, f128 }) |T| { - const ST = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); - var as_fp_val = -@as(T, 0.0); - var as_uint_val = @bitCast(ST, as_fp_val); - // Ensure the sign bit is set. - try expect(as_uint_val >> (@typeInfo(T).Float.bits - 1) == 1); - } + try testOne(f16); + try testOne(f32); + try testOne(f64); + // TODO enable this + //try testOne(f80); + try testOne(f128); + // TODO enable this + //try testOne(c_longdouble); + } + + fn testOne(comptime T: type) !void { + const ST = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); + var as_fp_val = -@as(T, 0.0); + var as_uint_val = @bitCast(ST, as_fp_val); + // Ensure the sign bit is set. + try expect(as_uint_val >> (@typeInfo(T).Float.bits - 1) == 1); } };