| author | |
| committer | |
| log | b33c8b0b06dd6a24d89efb278eeb833db93b2f9b |
| tree | 6b5c91beeae7563c581279f8d08d553e08c40d3f |
| parent | f5edf78eea403c14635a05e1729672cfb50aca89 |
When handling the `negate` ZIR instruction, Zig now checks for a
comptime operand and handles it as a special case rather than lowering
it as `0 - x` so that the expression `-x` where `x` is a floating point
value known at compile-time, will get the negative zero bitwise
representation.3 files changed, 84 insertions(+), 21 deletions(-)
src/Sema.zig+31-13| ... | ... | @@ -758,8 +758,8 @@ fn analyzeBodyInner( |
| 758 | 758 | .is_non_null => try sema.zirIsNonNull(block, inst), |
| 759 | 759 | .is_non_null_ptr => try sema.zirIsNonNullPtr(block, inst), |
| 760 | 760 | .merge_error_sets => try sema.zirMergeErrorSets(block, inst), |
| 761 | .negate => try sema.zirNegate(block, inst, .sub), | |
| 762 | .negate_wrap => try sema.zirNegate(block, inst, .subwrap), | |
| 761 | .negate => try sema.zirNegate(block, inst), | |
| 762 | .negate_wrap => try sema.zirNegateWrap(block, inst), | |
| 763 | 763 | .optional_payload_safe => try sema.zirOptionalPayload(block, inst, true), |
| 764 | 764 | .optional_payload_safe_ptr => try sema.zirOptionalPayloadPtr(block, inst, true), |
| 765 | 765 | .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 |
| 9328 | 9328 | return sema.fail(block, lhs_src, "TODO runtime array_mul", .{}); |
| 9329 | 9329 | } |
| 9330 | 9330 | |
| 9331 | fn zirNegate( | |
| 9332 | sema: *Sema, | |
| 9333 | block: *Block, | |
| 9334 | inst: Zir.Inst.Index, | |
| 9335 | tag_override: Zir.Inst.Tag, | |
| 9336 | ) CompileError!Air.Inst.Ref { | |
| 9337 | const tracy = trace(@src()); | |
| 9338 | defer tracy.end(); | |
| 9339 | ||
| 9331 | fn zirNegate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | |
| 9340 | 9332 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 9341 | 9333 | const src = inst_data.src(); |
| 9342 | 9334 | const lhs_src = src; |
| ... | ... | @@ -9346,16 +9338,42 @@ fn zirNegate( |
| 9346 | 9338 | const rhs_ty = sema.typeOf(rhs); |
| 9347 | 9339 | const rhs_scalar_ty = rhs_ty.scalarType(); |
| 9348 | 9340 | |
| 9349 | if (tag_override == .sub and rhs_scalar_ty.isUnsignedInt()) { | |
| 9341 | if (rhs_scalar_ty.isUnsignedInt()) { | |
| 9350 | 9342 | return sema.fail(block, src, "negation of type '{}'", .{rhs_ty.fmt(sema.mod)}); |
| 9351 | 9343 | } |
| 9352 | 9344 | |
| 9345 | if (rhs_scalar_ty.isAnyFloat()) { | |
| 9346 | // We handle comptime negation here to ensure negative zero is represented in the bits. | |
| 9347 | if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| { | |
| 9348 | if (rhs_val.isUndef()) return sema.addConstUndef(rhs_ty); | |
| 9349 | const target = sema.mod.getTarget(); | |
| 9350 | return sema.addConstant(rhs_ty, try rhs_val.floatNeg(rhs_ty, sema.arena, target)); | |
| 9351 | } | |
| 9352 | } | |
| 9353 | ||
| 9354 | const lhs = if (rhs_ty.zigTypeTag() == .Vector) | |
| 9355 | try sema.addConstant(rhs_ty, try Value.Tag.repeated.create(sema.arena, Value.zero)) | |
| 9356 | else | |
| 9357 | sema.resolveInst(.zero); | |
| 9358 | ||
| 9359 | return sema.analyzeArithmetic(block, .sub, lhs, rhs, src, lhs_src, rhs_src); | |
| 9360 | } | |
| 9361 | ||
| 9362 | fn zirNegateWrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | |
| 9363 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | |
| 9364 | const src = inst_data.src(); | |
| 9365 | const lhs_src = src; | |
| 9366 | const rhs_src = src; // TODO better source location | |
| 9367 | ||
| 9368 | const rhs = sema.resolveInst(inst_data.operand); | |
| 9369 | const rhs_ty = sema.typeOf(rhs); | |
| 9370 | ||
| 9353 | 9371 | const lhs = if (rhs_ty.zigTypeTag() == .Vector) |
| 9354 | 9372 | try sema.addConstant(rhs_ty, try Value.Tag.repeated.create(sema.arena, Value.zero)) |
| 9355 | 9373 | else |
| 9356 | 9374 | sema.resolveInst(.zero); |
| 9357 | 9375 | |
| 9358 | return sema.analyzeArithmetic(block, tag_override, lhs, rhs, src, lhs_src, rhs_src); | |
| 9376 | return sema.analyzeArithmetic(block, .subwrap, lhs, rhs, src, lhs_src, rhs_src); | |
| 9359 | 9377 | } |
| 9360 | 9378 | |
| 9361 | 9379 | fn zirArithmetic( |
src/value.zig+32| ... | ... | @@ -4155,6 +4155,38 @@ pub const Value = extern union { |
| 4155 | 4155 | } |
| 4156 | 4156 | } |
| 4157 | 4157 | |
| 4158 | pub fn floatNeg( | |
| 4159 | val: Value, | |
| 4160 | float_type: Type, | |
| 4161 | arena: Allocator, | |
| 4162 | target: Target, | |
| 4163 | ) !Value { | |
| 4164 | if (float_type.zigTypeTag() == .Vector) { | |
| 4165 | const result_data = try arena.alloc(Value, float_type.vectorLen()); | |
| 4166 | for (result_data) |*scalar, i| { | |
| 4167 | scalar.* = try floatNegScalar(val.indexVectorlike(i), float_type.scalarType(), arena, target); | |
| 4168 | } | |
| 4169 | return Value.Tag.aggregate.create(arena, result_data); | |
| 4170 | } | |
| 4171 | return floatNegScalar(val, float_type, arena, target); | |
| 4172 | } | |
| 4173 | ||
| 4174 | pub fn floatNegScalar( | |
| 4175 | val: Value, | |
| 4176 | float_type: Type, | |
| 4177 | arena: Allocator, | |
| 4178 | target: Target, | |
| 4179 | ) !Value { | |
| 4180 | switch (float_type.floatBits(target)) { | |
| 4181 | 16 => return Value.Tag.float_16.create(arena, -val.toFloat(f16)), | |
| 4182 | 32 => return Value.Tag.float_32.create(arena, -val.toFloat(f32)), | |
| 4183 | 64 => return Value.Tag.float_64.create(arena, -val.toFloat(f64)), | |
| 4184 | 80 => return Value.Tag.float_80.create(arena, -val.toFloat(f80)), | |
| 4185 | 128 => return Value.Tag.float_128.create(arena, -val.toFloat(f128)), | |
| 4186 | else => unreachable, | |
| 4187 | } | |
| 4188 | } | |
| 4189 | ||
| 4158 | 4190 | pub fn floatDiv( |
| 4159 | 4191 | lhs: Value, |
| 4160 | 4192 | rhs: Value, |
test/behavior/math.zig+21-8| ... | ... | @@ -1593,17 +1593,30 @@ test "compare undefined literal with comptime_int" { |
| 1593 | 1593 | } |
| 1594 | 1594 | |
| 1595 | 1595 | test "signed zeros are represented properly" { |
| 1596 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | |
| 1596 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 1597 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | |
| 1598 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 1599 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | |
| 1600 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | |
| 1597 | 1601 | |
| 1598 | 1602 | const S = struct { |
| 1599 | 1603 | fn doTheTest() !void { |
| 1600 | inline for ([_]type{ f16, f32, f64, f128 }) |T| { | |
| 1601 | const ST = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); | |
| 1602 | var as_fp_val = -@as(T, 0.0); | |
| 1603 | var as_uint_val = @bitCast(ST, as_fp_val); | |
| 1604 | // Ensure the sign bit is set. | |
| 1605 | try expect(as_uint_val >> (@typeInfo(T).Float.bits - 1) == 1); | |
| 1606 | } | |
| 1604 | try testOne(f16); | |
| 1605 | try testOne(f32); | |
| 1606 | try testOne(f64); | |
| 1607 | // TODO enable this | |
| 1608 | //try testOne(f80); | |
| 1609 | try testOne(f128); | |
| 1610 | // TODO enable this | |
| 1611 | //try testOne(c_longdouble); | |
| 1612 | } | |
| 1613 | ||
| 1614 | fn testOne(comptime T: type) !void { | |
| 1615 | const ST = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); | |
| 1616 | var as_fp_val = -@as(T, 0.0); | |
| 1617 | var as_uint_val = @bitCast(ST, as_fp_val); | |
| 1618 | // Ensure the sign bit is set. | |
| 1619 | try expect(as_uint_val >> (@typeInfo(T).Float.bits - 1) == 1); | |
| 1607 | 1620 | } |
| 1608 | 1621 | }; |
| 1609 | 1622 |