From 287ff4ab58f8af70383b6e334c7c862c8b8fbeec Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 30 Jan 2022 17:23:14 -0700 Subject: [PATCH 1/6] stage2: add more float arithmetic and f80 support AstGen: Fixed bug where f80 types in source were triggering illegal behavior. Value: handle f80 in floating point arithmetic functions. Value: implement floatRem and floatMod This commit introduces dependencies on compiler-rt that are not implemented. Those are a prerequisite to merging this branch. --- src/AstGen.zig | 6 +- src/Sema.zig | 24 ++--- src/value.zig | 215 +++++++++++++++++++++++++++++++---------- test/behavior/math.zig | 2 - 4 files changed, 178 insertions(+), 69 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index 228937fffa..4133d3d364 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -8409,10 +8409,11 @@ fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.In .c_ushort_type, .comptime_float_type, .comptime_int_type, - .f128_type, .f16_type, .f32_type, .f64_type, + .f80_type, + .f128_type, .i16_type, .i32_type, .i64_type, @@ -8648,10 +8649,11 @@ fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool { .c_ulong_type, .c_ulonglong_type, .c_ushort_type, - .f128_type, .f16_type, .f32_type, .f64_type, + .f80_type, + .f128_type, .i16_type, .i32_type, .i64_type, diff --git a/src/Sema.zig b/src/Sema.zig index 934fa4064b..75492e2ae7 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -8187,7 +8187,7 @@ fn analyzeArithmetic( } else { return sema.addConstant( scalar_type, - try lhs_val.floatAdd(rhs_val, scalar_type, sema.arena), + try lhs_val.floatAdd(rhs_val, scalar_type, sema.arena, target), ); } } else break :rs .{ .src = rhs_src, .air_tag = .add }; @@ -8280,7 +8280,7 @@ fn analyzeArithmetic( } else { return sema.addConstant( scalar_type, - try lhs_val.floatSub(rhs_val, scalar_type, sema.arena), + try lhs_val.floatSub(rhs_val, scalar_type, sema.arena, target), ); } } else break :rs .{ .src = rhs_src, .air_tag = .sub }; @@ -8396,7 +8396,7 @@ fn analyzeArithmetic( } else { return sema.addConstant( scalar_type, - try lhs_val.floatDiv(rhs_val, scalar_type, sema.arena), + try lhs_val.floatDiv(rhs_val, scalar_type, sema.arena, target), ); } } else { @@ -8471,7 +8471,7 @@ fn analyzeArithmetic( } else { return sema.addConstant( scalar_type, - try lhs_val.floatDivTrunc(rhs_val, scalar_type, sema.arena), + try lhs_val.floatDivTrunc(rhs_val, scalar_type, sema.arena, target), ); } } else break :rs .{ .src = rhs_src, .air_tag = .div_trunc }; @@ -8534,7 +8534,7 @@ fn analyzeArithmetic( } else { return sema.addConstant( scalar_type, - try lhs_val.floatDivFloor(rhs_val, scalar_type, sema.arena), + try lhs_val.floatDivFloor(rhs_val, scalar_type, sema.arena, target), ); } } else break :rs .{ .src = rhs_src, .air_tag = .div_floor }; @@ -8586,7 +8586,7 @@ fn analyzeArithmetic( // TODO: emit compile error if there is a remainder return sema.addConstant( scalar_type, - try lhs_val.floatDiv(rhs_val, scalar_type, sema.arena), + try lhs_val.floatDiv(rhs_val, scalar_type, sema.arena, target), ); } } else break :rs .{ .src = rhs_src, .air_tag = .div_exact }; @@ -8641,7 +8641,7 @@ fn analyzeArithmetic( } else { return sema.addConstant( scalar_type, - try lhs_val.floatMul(rhs_val, scalar_type, sema.arena), + try lhs_val.floatMul(rhs_val, scalar_type, sema.arena, target), ); } } else break :rs .{ .src = lhs_src, .air_tag = .mul }; @@ -8797,7 +8797,7 @@ fn analyzeArithmetic( } return sema.addConstant( scalar_type, - try lhs_val.floatRem(rhs_val, sema.arena), + try lhs_val.floatRem(rhs_val, scalar_type, sema.arena, target), ); } else { return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty); @@ -8858,7 +8858,7 @@ fn analyzeArithmetic( if (maybe_rhs_val) |rhs_val| { return sema.addConstant( scalar_type, - try lhs_val.floatRem(rhs_val, sema.arena), + try lhs_val.floatRem(rhs_val, scalar_type, sema.arena, target), ); } else break :rs .{ .src = rhs_src, .air_tag = .rem }; } else break :rs .{ .src = lhs_src, .air_tag = .rem }; @@ -8915,7 +8915,7 @@ fn analyzeArithmetic( if (maybe_rhs_val) |rhs_val| { return sema.addConstant( scalar_type, - try lhs_val.floatMod(rhs_val, sema.arena), + try lhs_val.floatMod(rhs_val, scalar_type, sema.arena, target), ); } else break :rs .{ .src = rhs_src, .air_tag = .mod }; } else break :rs .{ .src = lhs_src, .air_tag = .mod }; @@ -14195,12 +14195,12 @@ fn coerce( .Float, .ComptimeFloat => switch (inst_ty.zigTypeTag()) { .ComptimeFloat => { const val = try sema.resolveConstValue(block, inst_src, inst); - const result_val = try val.floatCast(sema.arena, dest_ty); + const result_val = try val.floatCast(sema.arena, dest_ty, target); return try sema.addConstant(dest_ty, result_val); }, .Float => { if (try sema.resolveDefinedValue(block, inst_src, inst)) |val| { - const result_val = try val.floatCast(sema.arena, dest_ty); + const result_val = try val.floatCast(sema.arena, dest_ty, target); if (!val.eql(result_val, dest_ty)) { return sema.fail( block, diff --git a/src/value.zig b/src/value.zig index e444e2daf1..18c8357b6b 100644 --- a/src/value.zig +++ b/src/value.zig @@ -138,6 +138,7 @@ pub const Value = extern union { float_16, float_32, float_64, + float_80, float_128, enum_literal, /// A specific enum tag, indicated by the field index (declaration order). @@ -295,6 +296,7 @@ pub const Value = extern union { .float_16 => Payload.Float_16, .float_32 => Payload.Float_32, .float_64 => Payload.Float_64, + .float_80 => Payload.Float_80, .float_128 => Payload.Float_128, .@"error" => Payload.Error, .inferred_alloc => Payload.InferredAlloc, @@ -546,6 +548,7 @@ pub const Value = extern union { .float_16 => return self.copyPayloadShallow(arena, Payload.Float_16), .float_32 => return self.copyPayloadShallow(arena, Payload.Float_32), .float_64 => return self.copyPayloadShallow(arena, Payload.Float_64), + .float_80 => return self.copyPayloadShallow(arena, Payload.Float_80), .float_128 => return self.copyPayloadShallow(arena, Payload.Float_128), .enum_literal => { const payload = self.castTag(.enum_literal).?; @@ -733,6 +736,7 @@ pub const Value = extern union { .float_16 => return out_stream.print("{}", .{val.castTag(.float_16).?.data}), .float_32 => return out_stream.print("{}", .{val.castTag(.float_32).?.data}), .float_64 => return out_stream.print("{}", .{val.castTag(.float_64).?.data}), + .float_80 => return out_stream.print("{}", .{val.castTag(.float_80).?.data}), .float_128 => return out_stream.print("{}", .{val.castTag(.float_128).?.data}), .@"error" => return out_stream.print("error.{s}", .{val.castTag(.@"error").?.data.name}), // TODO to print this it should be error{ Set, Items }!T(val), but we need the type for that @@ -1083,6 +1087,7 @@ pub const Value = extern union { 16 => return Value.Tag.float_16.create(arena, floatReadFromMemory(f16, target, buffer)), 32 => return Value.Tag.float_32.create(arena, floatReadFromMemory(f32, target, buffer)), 64 => return Value.Tag.float_64.create(arena, floatReadFromMemory(f64, target, buffer)), + 80 => return Value.Tag.float_80.create(arena, floatReadFromMemory(f80, target, buffer)), 128 => return Value.Tag.float_128.create(arena, floatReadFromMemory(f128, target, buffer)), else => unreachable, }, @@ -1100,6 +1105,12 @@ pub const Value = extern union { } fn floatReadFromMemory(comptime F: type, target: Target, buffer: []const u8) F { + if (F == f80) { + // TODO: use std.math.F80Repr + const big_int = std.mem.readInt(u128, buffer[0..16], target.cpu.arch.endian()); + const int = @truncate(u80, big_int); + return @bitCast(F, int); + } const Int = @Type(.{ .Int = .{ .signedness = .unsigned, .bits = @typeInfo(F).Float.bits, @@ -1114,6 +1125,7 @@ pub const Value = extern union { .float_16 => @floatCast(T, val.castTag(.float_16).?.data), .float_32 => @floatCast(T, val.castTag(.float_32).?.data), .float_64 => @floatCast(T, val.castTag(.float_64).?.data), + .float_80 => @floatCast(T, val.castTag(.float_80).?.data), .float_128 => @floatCast(T, val.castTag(.float_128).?.data), .zero => 0, @@ -1367,14 +1379,13 @@ pub const Value = extern union { /// Converts an integer or a float to a float. May result in a loss of information. /// Caller can find out by equality checking the result against the operand. - pub fn floatCast(self: Value, arena: Allocator, dest_ty: Type) !Value { - switch (dest_ty.tag()) { - .f16 => return Value.Tag.float_16.create(arena, self.toFloat(f16)), - .f32 => return Value.Tag.float_32.create(arena, self.toFloat(f32)), - .f64 => return Value.Tag.float_64.create(arena, self.toFloat(f64)), - .f128, .comptime_float, .c_longdouble => { - return Value.Tag.float_128.create(arena, self.toFloat(f128)); - }, + pub fn floatCast(self: Value, arena: Allocator, dest_ty: Type, target: Target) !Value { + switch (dest_ty.floatBits(target)) { + 16 => return Value.Tag.float_16.create(arena, self.toFloat(f16)), + 32 => return Value.Tag.float_32.create(arena, self.toFloat(f32)), + 64 => return Value.Tag.float_64.create(arena, self.toFloat(f64)), + 80 => return Value.Tag.float_80.create(arena, self.toFloat(f80)), + 128 => return Value.Tag.float_128.create(arena, self.toFloat(f128)), else => unreachable, } } @@ -1389,8 +1400,8 @@ pub const Value = extern union { .float_16 => @rem(self.castTag(.float_16).?.data, 1) != 0, .float_32 => @rem(self.castTag(.float_32).?.data, 1) != 0, .float_64 => @rem(self.castTag(.float_64).?.data, 1) != 0, - // .float_128 => @rem(self.castTag(.float_128).?.data, 1) != 0, - .float_128 => @panic("TODO lld: error: undefined symbol: fmodl"), + .float_80 => @rem(self.castTag(.float_80).?.data, 1) != 0, + .float_128 => @rem(self.castTag(.float_128).?.data, 1) != 0, else => unreachable, }; @@ -1408,6 +1419,7 @@ pub const Value = extern union { .float_16 => self.castTag(.float_16).?.data == 0, .float_32 => self.castTag(.float_32).?.data == 0, .float_64 => self.castTag(.float_64).?.data == 0, + .float_80 => self.castTag(.float_80).?.data == 0, .float_128 => self.castTag(.float_128).?.data == 0, .int_big_positive => self.castTag(.int_big_positive).?.asBigInt().eqZero(), @@ -1440,6 +1452,7 @@ pub const Value = extern union { .float_16 => std.math.order(lhs.castTag(.float_16).?.data, 0), .float_32 => std.math.order(lhs.castTag(.float_32).?.data, 0), .float_64 => std.math.order(lhs.castTag(.float_64).?.data, 0), + .float_80 => std.math.order(lhs.castTag(.float_80).?.data, 0), .float_128 => std.math.order(lhs.castTag(.float_128).?.data, 0), else => unreachable, @@ -1471,6 +1484,7 @@ pub const Value = extern union { .float_16 => return std.math.order(lhs.castTag(.float_16).?.data, rhs.castTag(.float_16).?.data), .float_32 => return std.math.order(lhs.castTag(.float_32).?.data, rhs.castTag(.float_32).?.data), .float_64 => return std.math.order(lhs.castTag(.float_64).?.data, rhs.castTag(.float_64).?.data), + .float_80 => return std.math.order(lhs.castTag(.float_80).?.data, rhs.castTag(.float_80).?.data), .float_128 => return std.math.order(lhs.castTag(.float_128).?.data, rhs.castTag(.float_128).?.data), else => unreachable, }; @@ -2139,6 +2153,7 @@ pub const Value = extern union { .float_16, .float_32, .float_64, + .float_80, .float_128, => true, else => false, @@ -2174,6 +2189,7 @@ pub const Value = extern union { 16 => return Value.Tag.float_16.create(arena, @intToFloat(f16, x)), 32 => return Value.Tag.float_32.create(arena, @intToFloat(f32, x)), 64 => return Value.Tag.float_64.create(arena, @intToFloat(f64, x)), + 80 => return Value.Tag.float_80.create(arena, @intToFloat(f80, x)), 128 => return Value.Tag.float_128.create(arena, @intToFloat(f128, x)), else => unreachable, } @@ -2184,6 +2200,7 @@ pub const Value = extern union { 16 => return Value.Tag.float_16.create(arena, @floatCast(f16, float)), 32 => return Value.Tag.float_32.create(arena, @floatCast(f32, float)), 64 => return Value.Tag.float_64.create(arena, @floatCast(f64, float)), + 80 => return Value.Tag.float_80.create(arena, @floatCast(f80, float)), 128 => return Value.Tag.float_128.create(arena, float), else => unreachable, } @@ -2281,7 +2298,7 @@ pub const Value = extern union { } if (ty.isAnyFloat()) { - return floatAdd(lhs, rhs, ty, arena); + return floatAdd(lhs, rhs, ty, arena, target); } const overflow_result = try intAddWithOverflow(lhs, rhs, ty, arena, target); @@ -2371,7 +2388,7 @@ pub const Value = extern union { } if (ty.isAnyFloat()) { - return floatSub(lhs, rhs, ty, arena); + return floatSub(lhs, rhs, ty, arena, target); } const overflow_result = try intSubWithOverflow(lhs, rhs, ty, arena, target); @@ -2454,7 +2471,7 @@ pub const Value = extern union { } if (ty.isAnyFloat()) { - return floatMul(lhs, rhs, ty, arena); + return floatMul(lhs, rhs, ty, arena, target); } const overflow_result = try intMulWithOverflow(lhs, rhs, ty, arena, target); @@ -2753,23 +2770,72 @@ pub const Value = extern union { .float_16 => std.math.isNan(val.castTag(.float_16).?.data), .float_32 => std.math.isNan(val.castTag(.float_32).?.data), .float_64 => std.math.isNan(val.castTag(.float_64).?.data), + .float_80 => std.math.isNan(val.castTag(.float_80).?.data), .float_128 => std.math.isNan(val.castTag(.float_128).?.data), else => false, }; } - pub fn floatRem(lhs: Value, rhs: Value, allocator: Allocator) !Value { - _ = lhs; - _ = rhs; - _ = allocator; - @panic("TODO implement Value.floatRem"); + pub fn floatRem(lhs: Value, rhs: Value, float_type: Type, arena: Allocator, target: Target) !Value { + switch (float_type.floatBits(target)) { + 16 => { + const lhs_val = lhs.toFloat(f16); + const rhs_val = rhs.toFloat(f16); + return Value.Tag.float_16.create(arena, @rem(lhs_val, rhs_val)); + }, + 32 => { + const lhs_val = lhs.toFloat(f32); + const rhs_val = rhs.toFloat(f32); + return Value.Tag.float_32.create(arena, @rem(lhs_val, rhs_val)); + }, + 64 => { + const lhs_val = lhs.toFloat(f64); + const rhs_val = rhs.toFloat(f64); + return Value.Tag.float_64.create(arena, @rem(lhs_val, rhs_val)); + }, + 80 => { + const lhs_val = lhs.toFloat(f80); + const rhs_val = rhs.toFloat(f80); + return Value.Tag.float_80.create(arena, @rem(lhs_val, rhs_val)); + }, + 128 => { + const lhs_val = lhs.toFloat(f128); + const rhs_val = rhs.toFloat(f128); + return Value.Tag.float_128.create(arena, @rem(lhs_val, rhs_val)); + }, + else => unreachable, + } } - pub fn floatMod(lhs: Value, rhs: Value, allocator: Allocator) !Value { - _ = lhs; - _ = rhs; - _ = allocator; - @panic("TODO implement Value.floatMod"); + pub fn floatMod(lhs: Value, rhs: Value, float_type: Type, arena: Allocator, target: Target) !Value { + switch (float_type.floatBits(target)) { + 16 => { + const lhs_val = lhs.toFloat(f16); + const rhs_val = rhs.toFloat(f16); + return Value.Tag.float_16.create(arena, @mod(lhs_val, rhs_val)); + }, + 32 => { + const lhs_val = lhs.toFloat(f32); + const rhs_val = rhs.toFloat(f32); + return Value.Tag.float_32.create(arena, @mod(lhs_val, rhs_val)); + }, + 64 => { + const lhs_val = lhs.toFloat(f64); + const rhs_val = rhs.toFloat(f64); + return Value.Tag.float_64.create(arena, @mod(lhs_val, rhs_val)); + }, + 80 => { + const lhs_val = lhs.toFloat(f80); + const rhs_val = rhs.toFloat(f80); + return Value.Tag.float_80.create(arena, @mod(lhs_val, rhs_val)); + }, + 128 => { + const lhs_val = lhs.toFloat(f128); + const rhs_val = rhs.toFloat(f128); + return Value.Tag.float_128.create(arena, @mod(lhs_val, rhs_val)); + }, + else => unreachable, + } } pub fn intMul(lhs: Value, rhs: Value, allocator: Allocator) !Value { @@ -2929,24 +2995,30 @@ pub const Value = extern union { rhs: Value, float_type: Type, arena: Allocator, + target: Target, ) !Value { - switch (float_type.tag()) { - .f16 => { + switch (float_type.floatBits(target)) { + 16 => { const lhs_val = lhs.toFloat(f16); const rhs_val = rhs.toFloat(f16); return Value.Tag.float_16.create(arena, lhs_val + rhs_val); }, - .f32 => { + 32 => { const lhs_val = lhs.toFloat(f32); const rhs_val = rhs.toFloat(f32); return Value.Tag.float_32.create(arena, lhs_val + rhs_val); }, - .f64 => { + 64 => { const lhs_val = lhs.toFloat(f64); const rhs_val = rhs.toFloat(f64); return Value.Tag.float_64.create(arena, lhs_val + rhs_val); }, - .f128, .comptime_float, .c_longdouble => { + 80 => { + const lhs_val = lhs.toFloat(f80); + const rhs_val = rhs.toFloat(f80); + return Value.Tag.float_80.create(arena, lhs_val + rhs_val); + }, + 128 => { const lhs_val = lhs.toFloat(f128); const rhs_val = rhs.toFloat(f128); return Value.Tag.float_128.create(arena, lhs_val + rhs_val); @@ -2960,24 +3032,30 @@ pub const Value = extern union { rhs: Value, float_type: Type, arena: Allocator, + target: Target, ) !Value { - switch (float_type.tag()) { - .f16 => { + switch (float_type.floatBits(target)) { + 16 => { const lhs_val = lhs.toFloat(f16); const rhs_val = rhs.toFloat(f16); return Value.Tag.float_16.create(arena, lhs_val - rhs_val); }, - .f32 => { + 32 => { const lhs_val = lhs.toFloat(f32); const rhs_val = rhs.toFloat(f32); return Value.Tag.float_32.create(arena, lhs_val - rhs_val); }, - .f64 => { + 64 => { const lhs_val = lhs.toFloat(f64); const rhs_val = rhs.toFloat(f64); return Value.Tag.float_64.create(arena, lhs_val - rhs_val); }, - .f128, .comptime_float, .c_longdouble => { + 80 => { + const lhs_val = lhs.toFloat(f80); + const rhs_val = rhs.toFloat(f80); + return Value.Tag.float_80.create(arena, lhs_val - rhs_val); + }, + 128 => { const lhs_val = lhs.toFloat(f128); const rhs_val = rhs.toFloat(f128); return Value.Tag.float_128.create(arena, lhs_val - rhs_val); @@ -2991,24 +3069,30 @@ pub const Value = extern union { rhs: Value, float_type: Type, arena: Allocator, + target: Target, ) !Value { - switch (float_type.tag()) { - .f16 => { + switch (float_type.floatBits(target)) { + 16 => { const lhs_val = lhs.toFloat(f16); const rhs_val = rhs.toFloat(f16); return Value.Tag.float_16.create(arena, lhs_val / rhs_val); }, - .f32 => { + 32 => { const lhs_val = lhs.toFloat(f32); const rhs_val = rhs.toFloat(f32); return Value.Tag.float_32.create(arena, lhs_val / rhs_val); }, - .f64 => { + 64 => { const lhs_val = lhs.toFloat(f64); const rhs_val = rhs.toFloat(f64); return Value.Tag.float_64.create(arena, lhs_val / rhs_val); }, - .f128, .comptime_float, .c_longdouble => { + 80 => { + const lhs_val = lhs.toFloat(f80); + const rhs_val = rhs.toFloat(f80); + return Value.Tag.float_80.create(arena, lhs_val / rhs_val); + }, + 128 => { const lhs_val = lhs.toFloat(f128); const rhs_val = rhs.toFloat(f128); return Value.Tag.float_128.create(arena, lhs_val / rhs_val); @@ -3022,24 +3106,30 @@ pub const Value = extern union { rhs: Value, float_type: Type, arena: Allocator, + target: Target, ) !Value { - switch (float_type.tag()) { - .f16 => { + switch (float_type.floatBits(target)) { + 16 => { const lhs_val = lhs.toFloat(f16); const rhs_val = rhs.toFloat(f16); return Value.Tag.float_16.create(arena, @divFloor(lhs_val, rhs_val)); }, - .f32 => { + 32 => { const lhs_val = lhs.toFloat(f32); const rhs_val = rhs.toFloat(f32); return Value.Tag.float_32.create(arena, @divFloor(lhs_val, rhs_val)); }, - .f64 => { + 64 => { const lhs_val = lhs.toFloat(f64); const rhs_val = rhs.toFloat(f64); return Value.Tag.float_64.create(arena, @divFloor(lhs_val, rhs_val)); }, - .f128, .comptime_float, .c_longdouble => { + 80 => { + const lhs_val = lhs.toFloat(f80); + const rhs_val = rhs.toFloat(f80); + return Value.Tag.float_80.create(arena, @divFloor(lhs_val, rhs_val)); + }, + 128 => { const lhs_val = lhs.toFloat(f128); const rhs_val = rhs.toFloat(f128); return Value.Tag.float_128.create(arena, @divFloor(lhs_val, rhs_val)); @@ -3053,24 +3143,30 @@ pub const Value = extern union { rhs: Value, float_type: Type, arena: Allocator, + target: Target, ) !Value { - switch (float_type.tag()) { - .f16 => { + switch (float_type.floatBits(target)) { + 16 => { const lhs_val = lhs.toFloat(f16); const rhs_val = rhs.toFloat(f16); return Value.Tag.float_16.create(arena, @divTrunc(lhs_val, rhs_val)); }, - .f32 => { + 32 => { const lhs_val = lhs.toFloat(f32); const rhs_val = rhs.toFloat(f32); return Value.Tag.float_32.create(arena, @divTrunc(lhs_val, rhs_val)); }, - .f64 => { + 64 => { const lhs_val = lhs.toFloat(f64); const rhs_val = rhs.toFloat(f64); return Value.Tag.float_64.create(arena, @divTrunc(lhs_val, rhs_val)); }, - .f128, .comptime_float, .c_longdouble => { + 80 => { + const lhs_val = lhs.toFloat(f80); + const rhs_val = rhs.toFloat(f80); + return Value.Tag.float_80.create(arena, @divTrunc(lhs_val, rhs_val)); + }, + 128 => { const lhs_val = lhs.toFloat(f128); const rhs_val = rhs.toFloat(f128); return Value.Tag.float_128.create(arena, @divTrunc(lhs_val, rhs_val)); @@ -3084,24 +3180,30 @@ pub const Value = extern union { rhs: Value, float_type: Type, arena: Allocator, + target: Target, ) !Value { - switch (float_type.tag()) { - .f16 => { + switch (float_type.floatBits(target)) { + 16 => { const lhs_val = lhs.toFloat(f16); const rhs_val = rhs.toFloat(f16); return Value.Tag.float_16.create(arena, lhs_val * rhs_val); }, - .f32 => { + 32 => { const lhs_val = lhs.toFloat(f32); const rhs_val = rhs.toFloat(f32); return Value.Tag.float_32.create(arena, lhs_val * rhs_val); }, - .f64 => { + 64 => { const lhs_val = lhs.toFloat(f64); const rhs_val = rhs.toFloat(f64); return Value.Tag.float_64.create(arena, lhs_val * rhs_val); }, - .f128, .comptime_float, .c_longdouble => { + 80 => { + const lhs_val = lhs.toFloat(f80); + const rhs_val = rhs.toFloat(f80); + return Value.Tag.float_80.create(arena, lhs_val * rhs_val); + }, + 128 => { const lhs_val = lhs.toFloat(f128); const rhs_val = rhs.toFloat(f128); return Value.Tag.float_128.create(arena, lhs_val * rhs_val); @@ -3250,6 +3352,13 @@ pub const Value = extern union { data: f64, }; + pub const Float_80 = struct { + pub const base_tag = Tag.float_80; + + base: Payload = .{ .tag = base_tag }, + data: f80, + }; + pub const Float_128 = struct { pub const base_tag = Tag.float_128; diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 8f947e2829..a1243eb7c1 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -768,8 +768,6 @@ test "shift left/right on u0 operand" { } test "comptime float rem int" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - comptime { var x = @as(f32, 1) % 2; try expect(x == 1.0); From 495fd4ee3e81bd9d26768cdb8fc639afac79d9dc Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 6 Feb 2022 19:45:49 -0700 Subject: [PATCH 2/6] AstGen: refactor redundant expressions This is a non-functional change. --- src/AstGen.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index 4133d3d364..5f6d05b7f5 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -2601,8 +2601,8 @@ fn varDecl( var resolve_inferred_alloc: Zir.Inst.Ref = .none; var opt_type_inst: Zir.Inst.Ref = .none; - if (var_decl.ast.type_node != 0) { - const type_inst = try typeExpr(gz, &init_scope.base, var_decl.ast.type_node); + if (type_node != 0) { + const type_inst = try typeExpr(gz, &init_scope.base, type_node); opt_type_inst = type_inst; if (align_inst == .none) { init_scope.instructions_top = gz.instructions.items.len; @@ -2683,7 +2683,7 @@ fn varDecl( const src_inst = gz.instructions.items[src]; if (zir_tags[src_inst] == .store_to_block_ptr) { if (zir_datas[src_inst].bin.lhs == init_scope.rl_ptr) { - if (var_decl.ast.type_node != 0) { + if (type_node != 0) { zir_tags[src_inst] = .store; } else { zir_tags[src_inst] = .store_to_inferred_ptr; From d4805472c3c98c488c17bce0ee28b6c44e93793c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 6 Feb 2022 20:06:00 -0700 Subject: [PATCH 3/6] compiler_rt: addXf3: add coercion to `@clz` We're going to remove the first parameter from this function in the future. Stage2 already ignores the first parameter. So we put an `@as` in here to make it work for both. --- lib/std/special/compiler_rt/addXf3.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/special/compiler_rt/addXf3.zig b/lib/std/special/compiler_rt/addXf3.zig index 41ff00e95d..1339cc340d 100644 --- a/lib/std/special/compiler_rt/addXf3.zig +++ b/lib/std/special/compiler_rt/addXf3.zig @@ -339,7 +339,7 @@ pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 { // If partial cancellation occurred, we need to left-shift the result // and adjust the exponent: if (a_int < int_bit << 3) { - const shift = @intCast(i32, @clz(u80, a_int)) - @intCast(i32, @clz(u80, int_bit << 3)); + const shift = @intCast(i32, @clz(u80, a_int)) - @intCast(i32, @clz(u80, @as(u80, int_bit) << 3)); a_int <<= @intCast(u7, shift); a_exp -= shift; } From 3bcce5f6d1f48e20dd177a7e440ddea1c451e779 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 6 Feb 2022 20:07:43 -0700 Subject: [PATCH 4/6] Sema: implement writing structs to memory at comptime --- src/value.zig | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/value.zig b/src/value.zig index 18c8357b6b..ac0344bf34 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1033,6 +1033,11 @@ pub const Value = extern union { } pub fn writeToMemory(val: Value, ty: Type, target: Target, buffer: []u8) void { + if (val.isUndef()) { + const size = @intCast(usize, ty.abiSize(target)); + std.mem.set(u8, buffer[0..size], 0xaa); + return; + } switch (ty.zigTypeTag()) { .Int => { var bigint_buffer: BigIntSpace = undefined; @@ -1068,6 +1073,14 @@ pub const Value = extern union { buf_off += elem_size; } }, + .Struct => { + const fields = ty.structFields().values(); + const field_vals = val.castTag(.@"struct").?.data; + for (fields) |field, i| { + const off = @intCast(usize, ty.structFieldOffset(i, target)); + writeToMemory(field_vals[i], field.ty, target, buffer[off..]); + } + }, else => @panic("TODO implement writeToMemory for more types"), } } @@ -1106,7 +1119,7 @@ pub const Value = extern union { fn floatReadFromMemory(comptime F: type, target: Target, buffer: []const u8) F { if (F == f80) { - // TODO: use std.math.F80Repr + // TODO: use std.math.F80Repr? const big_int = std.mem.readInt(u128, buffer[0..16], target.cpu.arch.endian()); const int = @truncate(u80, big_int); return @bitCast(F, int); From 65b6faa0485253b284f7a63601dc7d0f5858515a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 6 Feb 2022 20:23:40 -0700 Subject: [PATCH 5/6] Sema: avoid `@intToFloat` for f80 which breaks on non-x86 targets Currently Zig lowers `@intToFloat` for f80 incorrectly on non-x86 targets: ``` broken LLVM module found: UIToFP result must be FP or FP vector %62 = uitofp i64 %61 to i128 SIToFP result must be FP or FP vector %66 = sitofp i64 %65 to i128 ``` This happens because on such targets, we use i128 instead of x86_fp80 in order to avoid "LLVM ERROR: Cannot select". `@intToFloat` must be lowered differently to account for this difference as well. --- src/stage1/codegen.cpp | 7 +++++-- src/value.zig | 22 +++++++++++++++++----- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index ec1454ce4f..02f84beeab 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -9432,11 +9432,14 @@ static void define_builtin_types(CodeGen *g) { if (target_has_f80(g->zig_target)) { entry->llvm_type = LLVMX86FP80Type(); } else { + // We use i128 here instead of x86_fp80 because on targets such as arm, + // LLVM will give "ERROR: Cannot select" for any instructions involving + // the x86_fp80 type. entry->llvm_type = get_int_type(g, false, 128)->llvm_type; } entry->size_in_bits = 8 * 16; - entry->abi_size = 16; - entry->abi_align = 16; + entry->abi_size = 16; // matches LLVMABISizeOfType(LLVMX86FP80Type()) + entry->abi_align = 16; // matches LLVMABIAlignmentOfType(LLVMX86FP80Type()) buf_init_from_str(&entry->name, "f80"); entry->data.floating.bit_count = 80; diff --git a/src/value.zig b/src/value.zig index ac0344bf34..6d551d9eba 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1120,8 +1120,8 @@ pub const Value = extern union { fn floatReadFromMemory(comptime F: type, target: Target, buffer: []const u8) F { if (F == f80) { // TODO: use std.math.F80Repr? - const big_int = std.mem.readInt(u128, buffer[0..16], target.cpu.arch.endian()); - const int = @truncate(u80, big_int); + const int = std.mem.readInt(u128, buffer[0..16], target.cpu.arch.endian()); + // TODO shouldn't this be a bitcast from u80 to f80 instead of u128 to f80? return @bitCast(F, int); } const Int = @Type(.{ .Int = .{ @@ -1143,8 +1143,18 @@ pub const Value = extern union { .zero => 0, .one => 1, - .int_u64 => @intToFloat(T, val.castTag(.int_u64).?.data), - .int_i64 => @intToFloat(T, val.castTag(.int_i64).?.data), + .int_u64 => { + if (T == f80) { + @panic("TODO we can't lower this properly on non-x86 llvm backend yet"); + } + return @intToFloat(T, val.castTag(.int_u64).?.data); + }, + .int_i64 => { + if (T == f80) { + @panic("TODO we can't lower this properly on non-x86 llvm backend yet"); + } + return @intToFloat(T, val.castTag(.int_i64).?.data); + }, .int_big_positive => @floatCast(T, bigIntToFloat(val.castTag(.int_big_positive).?.data, true)), .int_big_negative => @floatCast(T, bigIntToFloat(val.castTag(.int_big_negative).?.data, false)), @@ -2202,7 +2212,9 @@ pub const Value = extern union { 16 => return Value.Tag.float_16.create(arena, @intToFloat(f16, x)), 32 => return Value.Tag.float_32.create(arena, @intToFloat(f32, x)), 64 => return Value.Tag.float_64.create(arena, @intToFloat(f64, x)), - 80 => return Value.Tag.float_80.create(arena, @intToFloat(f80, x)), + // We can't lower this properly on non-x86 llvm backends yet + //80 => return Value.Tag.float_80.create(arena, @intToFloat(f80, x)), + 80 => @panic("TODO f80 intToFloat"), 128 => return Value.Tag.float_128.create(arena, @intToFloat(f128, x)), else => unreachable, } From eb82fdf96c3392ec9eabacbaa98c976f9ccd264e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 6 Feb 2022 20:38:57 -0700 Subject: [PATCH 6/6] Sema: panic instead of lowering to unavailable compiler-rt functions Once the relevant compiler_rt functions are implemented, these panics can be removed. --- src/value.zig | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/value.zig b/src/value.zig index 6d551d9eba..1a7f51ecd5 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1423,8 +1423,10 @@ pub const Value = extern union { .float_16 => @rem(self.castTag(.float_16).?.data, 1) != 0, .float_32 => @rem(self.castTag(.float_32).?.data, 1) != 0, .float_64 => @rem(self.castTag(.float_64).?.data, 1) != 0, - .float_80 => @rem(self.castTag(.float_80).?.data, 1) != 0, - .float_128 => @rem(self.castTag(.float_128).?.data, 1) != 0, + //.float_80 => @rem(self.castTag(.float_80).?.data, 1) != 0, + .float_80 => @panic("TODO implement __remx in compiler-rt"), + //.float_128 => @rem(self.castTag(.float_128).?.data, 1) != 0, + .float_128 => @panic("TODO implement fmodl in compiler-rt"), else => unreachable, }; @@ -2819,11 +2821,17 @@ pub const Value = extern union { return Value.Tag.float_64.create(arena, @rem(lhs_val, rhs_val)); }, 80 => { + if (true) { + @panic("TODO implement compiler_rt __remx"); + } const lhs_val = lhs.toFloat(f80); const rhs_val = rhs.toFloat(f80); return Value.Tag.float_80.create(arena, @rem(lhs_val, rhs_val)); }, 128 => { + if (true) { + @panic("TODO implement compiler_rt fmodl"); + } const lhs_val = lhs.toFloat(f128); const rhs_val = rhs.toFloat(f128); return Value.Tag.float_128.create(arena, @rem(lhs_val, rhs_val)); @@ -2850,11 +2858,17 @@ pub const Value = extern union { return Value.Tag.float_64.create(arena, @mod(lhs_val, rhs_val)); }, 80 => { + if (true) { + @panic("TODO implement compiler_rt __modx"); + } const lhs_val = lhs.toFloat(f80); const rhs_val = rhs.toFloat(f80); return Value.Tag.float_80.create(arena, @mod(lhs_val, rhs_val)); }, 128 => { + if (true) { + @panic("TODO implement compiler_rt fmodl"); + } const lhs_val = lhs.toFloat(f128); const rhs_val = rhs.toFloat(f128); return Value.Tag.float_128.create(arena, @mod(lhs_val, rhs_val)); @@ -3113,6 +3127,9 @@ pub const Value = extern union { return Value.Tag.float_64.create(arena, lhs_val / rhs_val); }, 80 => { + if (true) { + @panic("TODO implement compiler_rt __divxf3"); + } const lhs_val = lhs.toFloat(f80); const rhs_val = rhs.toFloat(f80); return Value.Tag.float_80.create(arena, lhs_val / rhs_val); @@ -3150,6 +3167,9 @@ pub const Value = extern union { return Value.Tag.float_64.create(arena, @divFloor(lhs_val, rhs_val)); }, 80 => { + if (true) { + @panic("TODO implement compiler_rt __floorx"); + } const lhs_val = lhs.toFloat(f80); const rhs_val = rhs.toFloat(f80); return Value.Tag.float_80.create(arena, @divFloor(lhs_val, rhs_val)); @@ -3187,6 +3207,9 @@ pub const Value = extern union { return Value.Tag.float_64.create(arena, @divTrunc(lhs_val, rhs_val)); }, 80 => { + if (true) { + @panic("TODO implement compiler_rt __truncx"); + } const lhs_val = lhs.toFloat(f80); const rhs_val = rhs.toFloat(f80); return Value.Tag.float_80.create(arena, @divTrunc(lhs_val, rhs_val)); @@ -3224,6 +3247,9 @@ pub const Value = extern union { return Value.Tag.float_64.create(arena, lhs_val * rhs_val); }, 80 => { + if (true) { + @panic("TODO implement compiler_rt __mulxf3"); + } const lhs_val = lhs.toFloat(f80); const rhs_val = rhs.toFloat(f80); return Value.Tag.float_80.create(arena, lhs_val * rhs_val);