diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2022-07-13 13:14:37 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2022-07-13 13:14:37 -0700 |
| commit | 92bc3cbe27792be0300fb5f104c011a11f3cf40f (patch) | |
| tree | 8d9e7f2df29a824d104cfa01d2eb832d456554aa /src | |
| parent | 35e70111248f795fbdcefd5ae0d6fc494d1b0683 (diff) | |
| download | zig-92bc3cbe27792be0300fb5f104c011a11f3cf40f.tar.gz zig-92bc3cbe27792be0300fb5f104c011a11f3cf40f.zip | |
stage2: fix comptime bitcast involving f80
* Sema: implement comptime bitcast of f80 with integer-like types
bitwise rather than taking a round trip through memory layout.
* Type: introduce `isAbiInt`.
* Value: comptime memory write of f80 writes 0 bytes for padding
instead of leaving the memory uninitialized.
* Value: floatReadFromMemory has a more general implementation, checking
the endianness rather than checking for specific architectures.
This fixes behavior test failures occurring on MIPS.
Diffstat (limited to 'src')
| -rw-r--r-- | src/Sema.zig | 42 | ||||
| -rw-r--r-- | src/type.zig | 10 | ||||
| -rw-r--r-- | src/value.zig | 17 |
3 files changed, 59 insertions, 10 deletions
diff --git a/src/Sema.zig b/src/Sema.zig index b139c3f89e..29840820d0 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -22571,6 +22571,48 @@ fn bitCastVal( const target = sema.mod.getTarget(); if (old_ty.eql(new_ty, sema.mod)) return val; + // Some conversions have a bitwise definition that ignores in-memory layout, + // such as converting between f80 and u80. + + if (old_ty.eql(Type.f80, sema.mod) and new_ty.isAbiInt()) { + const float = val.toFloat(f80); + switch (new_ty.intInfo(target).signedness) { + .signed => { + const int = @bitCast(i80, float); + const limbs = try sema.arena.alloc(std.math.big.Limb, 2); + const big_int = std.math.big.int.Mutable.init(limbs, int); + return Value.fromBigInt(sema.arena, big_int.toConst()); + }, + .unsigned => { + const int = @bitCast(u80, float); + const limbs = try sema.arena.alloc(std.math.big.Limb, 2); + const big_int = std.math.big.int.Mutable.init(limbs, int); + return Value.fromBigInt(sema.arena, big_int.toConst()); + }, + } + } + + if (new_ty.eql(Type.f80, sema.mod) and old_ty.isAbiInt()) { + var bigint_space: Value.BigIntSpace = undefined; + var bigint = try val.toBigIntAdvanced(&bigint_space, target, sema.kit(block, src)); + switch (old_ty.intInfo(target).signedness) { + .signed => { + // This conversion cannot fail because we already checked bit size before + // calling bitCastVal. + const int = bigint.to(i80) catch unreachable; + const float = @bitCast(f80, int); + return Value.Tag.float_80.create(sema.arena, float); + }, + .unsigned => { + // This conversion cannot fail because we already checked bit size before + // calling bitCastVal. + const int = bigint.to(u80) catch unreachable; + const float = @bitCast(f80, int); + return Value.Tag.float_80.create(sema.arena, float); + }, + } + } + // For types with well-defined memory layouts, we serialize them a byte buffer, // then deserialize to the new type. const abi_size = try sema.usizeCast(block, src, old_ty.abiSize(target)); diff --git a/src/type.zig b/src/type.zig index 765f1da18c..0744a50579 100644 --- a/src/type.zig +++ b/src/type.zig @@ -4439,6 +4439,16 @@ pub const Type = extern union { }; } + /// Returns true for integers, enums, error sets, and packed structs. + /// If this function returns true, then intInfo() can be called on the type. + pub fn isAbiInt(ty: Type) bool { + return switch (ty.zigTypeTag()) { + .Int, .Enum, .ErrorSet => true, + .Struct => ty.containerLayout() == .Packed, + else => false, + }; + } + /// Asserts the type is an integer, enum, error set, or vector of one of them. pub fn intInfo(self: Type, target: Target) struct { signedness: std.builtin.Signedness, bits: u16 } { var ty = self; diff --git a/src/value.zig b/src/value.zig index 04999c778a..b52e67e31c 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1468,8 +1468,7 @@ pub const Value = extern union { const repr = std.math.break_f80(f); std.mem.writeInt(u64, buffer[0..8], repr.fraction, endian); std.mem.writeInt(u16, buffer[8..10], repr.exp, endian); - // TODO set the rest of the bytes to undefined. should we use 0xaa - // or is there a different way? + std.mem.set(u8, buffer[10..], 0); return; } const Int = @Type(.{ .Int = .{ @@ -1481,20 +1480,18 @@ pub const Value = extern union { } fn floatReadFromMemory(comptime F: type, target: Target, buffer: []const u8) F { + const endian = target.cpu.arch.endian(); if (F == f80) { - switch (target.cpu.arch) { - .i386, .x86_64 => return std.math.make_f80(.{ - .fraction = std.mem.readIntLittle(u64, buffer[0..8]), - .exp = std.mem.readIntLittle(u16, buffer[8..10]), - }), - else => {}, - } + return std.math.make_f80(.{ + .fraction = readInt(u64, buffer[0..8], endian), + .exp = readInt(u16, buffer[8..10], endian), + }); } const Int = @Type(.{ .Int = .{ .signedness = .unsigned, .bits = @typeInfo(F).Float.bits, } }); - const int = readInt(Int, buffer[0..@sizeOf(Int)], target.cpu.arch.endian()); + const int = readInt(Int, buffer[0..@sizeOf(Int)], endian); return @bitCast(F, int); } |
