authorgravatar for topolarity@tapscott.meCody Tapscott <topolarity@tapscott.me> 2022-10-18 11:37:43-07:00
committergravatar for topolarity@tapscott.meCody Tapscott <topolarity@tapscott.me> 2022-10-28 08:41:04-07:00
log3295fee9116789f144e6406493116c451aee7c57
tree71f10d7a5b987b956d0811d925424fea57fddd09
parentc639c225444c9252515949786e139494fb728861

stage2: Use mem.readPackedInt etc. for packed bitcasts

Packed memory has a well-defined layout that doesn't require conversion from an integer to read from. Let's use it :-) This change means that for bitcasting to/from a packed value that is N layers deep, we no longer have to create N temporary big-ints and perform N copies. Other miscellaneous improvements: - Adds support for casting to packed enums and vectors - Fixes bitcasting to/from vectors outside of a packed struct - Adds a fast path for bitcasting <= u/i64 - Fixes bug when bitcasting f80 which would clear following fields This also changes the bitcast memory layout of exotic integers on big-endian systems to match what's empirically observed on our targets. Technically, this layout is not guaranteed by LLVM so we should probably ban bitcasts that reveal these padding bits, but for now this is an improvement.

6 files changed, 461 insertions(+), 422 deletions(-)

lib/std/math/big/int.zig+69-92
...@@ -1762,16 +1762,32 @@ pub const Mutable = struct {...@@ -1762,16 +1762,32 @@ pub const Mutable = struct {
1762 }1762 }
17631763
1764 /// Read the value of `x` from `buffer`1764 /// Read the value of `x` from `buffer`
1765 /// Asserts that `buffer`, `abi_size`, and `bit_count` are large enough to store the value.1765 /// Asserts that `buffer` is large enough to contain a value of bit-size `bit_count`.
1766 ///1766 ///
1767 /// The contents of `buffer` are interpreted as if they were the contents of1767 /// The contents of `buffer` are interpreted as if they were the contents of
1768 /// @ptrCast(*[abi_size]const u8, &x). Byte ordering is determined by `endian`1768 /// @ptrCast(*[buffer.len]const u8, &x). Byte ordering is determined by `endian`
1769 /// and any required padding bits are expected on the MSB end.1769 /// and any required padding bits are expected on the MSB end.
1770 pub fn readTwosComplement(1770 pub fn readTwosComplement(
1771 x: *Mutable,1771 x: *Mutable,
1772 buffer: []const u8,1772 buffer: []const u8,
1773 bit_count: usize,1773 bit_count: usize,
1774 abi_size: usize,1774 endian: Endian,
1775 signedness: Signedness,
1776 ) void {
1777 return readPackedTwosComplement(x, buffer, 0, bit_count, endian, signedness);
1778 }
1779
1780 /// Read the value of `x` from a packed memory `buffer`.
1781 /// Asserts that `buffer` is large enough to contain a value of bit-size `bit_count`
1782 /// at offset `bit_offset`.
1783 ///
1784 /// This is equivalent to loading the value of an integer with `bit_count` bits as
1785 /// if it were a field in packed memory at the provided bit offset.
1786 pub fn readPackedTwosComplement(
1787 x: *Mutable,
1788 bytes: []const u8,
1789 bit_offset: usize,
1790 bit_count: usize,
1775 endian: Endian,1791 endian: Endian,
1776 signedness: Signedness,1792 signedness: Signedness,
1777 ) void {1793 ) void {
...@@ -1782,75 +1798,54 @@ pub const Mutable = struct {...@@ -1782,75 +1798,54 @@ pub const Mutable = struct {
1782 return;1798 return;
1783 }1799 }
17841800
1785 // byte_count is our total read size: it cannot exceed abi_size,
1786 // but may be less as long as it includes the required bits
1787 const limb_count = calcTwosCompLimbCount(bit_count);
1788 const byte_count = std.math.min(abi_size, @sizeOf(Limb) * limb_count);
1789 assert(8 * byte_count >= bit_count);
1790
1791 // Check whether the input is negative1801 // Check whether the input is negative
1792 var positive = true;1802 var positive = true;
1793 if (signedness == .signed) {1803 if (signedness == .signed) {
1804 const total_bits = bit_offset + bit_count;
1794 var last_byte = switch (endian) {1805 var last_byte = switch (endian) {
1795 .Little => ((bit_count + 7) / 8) - 1,1806 .Little => ((total_bits + 7) / 8) - 1,
1796 .Big => abi_size - ((bit_count + 7) / 8),1807 .Big => bytes.len - ((total_bits + 7) / 8),
1797 };1808 };
17981809
1799 const sign_bit = @as(u8, 1) << @intCast(u3, (bit_count - 1) % 8);1810 const sign_bit = @as(u8, 1) << @intCast(u3, (total_bits - 1) % 8);
1800 positive = ((buffer[last_byte] & sign_bit) == 0);1811 positive = ((bytes[last_byte] & sign_bit) == 0);
1801 }1812 }
18021813
1803 // Copy all complete limbs1814 // Copy all complete limbs
1804 var carry: u1 = if (positive) 0 else 1;1815 var carry: u1 = 1;
1805 var limb_index: usize = 0;1816 var limb_index: usize = 0;
1817 var bit_index: usize = 0;
1806 while (limb_index < bit_count / @bitSizeOf(Limb)) : (limb_index += 1) {1818 while (limb_index < bit_count / @bitSizeOf(Limb)) : (limb_index += 1) {
1807 var buf_index = switch (endian) {1819 // Read one Limb of bits
1808 .Little => @sizeOf(Limb) * limb_index,1820 var limb = mem.readPackedInt(Limb, bytes, bit_index + bit_offset, endian);
1809 .Big => abi_size - (limb_index + 1) * @sizeOf(Limb),1821 bit_index += @bitSizeOf(Limb);
1810 };
1811
1812 const limb_buf = @ptrCast(*const [@sizeOf(Limb)]u8, buffer[buf_index..]);
1813 var limb = mem.readInt(Limb, limb_buf, endian);
18141822
1815 // 2's complement (bitwise not, then add carry bit)1823 // 2's complement (bitwise not, then add carry bit)
1816 if (!positive) carry = @boolToInt(@addWithOverflow(Limb, ~limb, carry, &limb));1824 if (!positive) carry = @boolToInt(@addWithOverflow(Limb, ~limb, carry, &limb));
1817 x.limbs[limb_index] = limb;1825 x.limbs[limb_index] = limb;
1818 }1826 }
18191827
1820 // Copy the remaining N bytes (N <= @sizeOf(Limb))1828 // Copy the remaining bits
1821 var bytes_read = limb_index * @sizeOf(Limb);1829 if (bit_count != bit_index) {
1822 if (bytes_read != byte_count) {1830 // Read all remaining bits
1823 var limb: Limb = 0;1831 var limb = switch (signedness) {
18241832 .unsigned => mem.readVarPackedInt(Limb, bytes, bit_index + bit_offset, bit_count - bit_index, endian, .unsigned),
1825 while (bytes_read != byte_count) {1833 .signed => b: {
1826 const read_size = std.math.floorPowerOfTwo(usize, byte_count - bytes_read);1834 const SLimb = std.meta.Int(.signed, @bitSizeOf(Limb));
1827 var int_buffer = switch (endian) {1835 const limb = mem.readVarPackedInt(SLimb, bytes, bit_index + bit_offset, bit_count - bit_index, endian, .signed);
1828 .Little => buffer[bytes_read..],1836 break :b @bitCast(Limb, limb);
1829 .Big => buffer[(abi_size - bytes_read - read_size)..],1837 },
1830 };1838 };
1831 limb |= @intCast(Limb, switch (read_size) {
1832 1 => mem.readInt(u8, int_buffer[0..1], endian),
1833 2 => mem.readInt(u16, int_buffer[0..2], endian),
1834 4 => mem.readInt(u32, int_buffer[0..4], endian),
1835 8 => mem.readInt(u64, int_buffer[0..8], endian),
1836 16 => mem.readInt(u128, int_buffer[0..16], endian),
1837 else => unreachable,
1838 }) << @intCast(Log2Limb, 8 * (bytes_read % @sizeOf(Limb)));
1839 bytes_read += read_size;
1840 }
18411839
1842 // 2's complement (bitwise not, then add carry bit)1840 // 2's complement (bitwise not, then add carry bit)
1843 if (!positive) _ = @addWithOverflow(Limb, ~limb, carry, &limb);1841 if (!positive) assert(!@addWithOverflow(Limb, ~limb, carry, &limb));
18441842 x.limbs[limb_index] = limb;
1845 // Mask off any unused bits
1846 const valid_bits = @intCast(Log2Limb, bit_count % @bitSizeOf(Limb));
1847 const mask = (@as(Limb, 1) << valid_bits) -% 1; // 0b0..01..1 with (valid_bits_in_limb) trailing ones
1848 limb &= mask;
18491843
1850 x.limbs[limb_count - 1] = limb;1844 limb_index += 1;
1851 }1845 }
1846
1852 x.positive = positive;1847 x.positive = positive;
1853 x.len = limb_count;1848 x.len = limb_index;
1854 x.normalize(x.len);1849 x.normalize(x.len);
1855 }1850 }
18561851
...@@ -2212,66 +2207,48 @@ pub const Const = struct {...@@ -2212,66 +2207,48 @@ pub const Const = struct {
2212 }2207 }
22132208
2214 /// Write the value of `x` into `buffer`2209 /// Write the value of `x` into `buffer`
2215 /// Asserts that `buffer`, `abi_size`, and `bit_count` are large enough to store the value.2210 /// Asserts that `buffer` is large enough to store the value.
2216 ///2211 ///
2217 /// `buffer` is filled so that its contents match what would be observed via2212 /// `buffer` is filled so that its contents match what would be observed via
2218 /// @ptrCast(*[abi_size]const u8, &x). Byte ordering is determined by `endian`,2213 /// @ptrCast(*[buffer.len]const u8, &x). Byte ordering is determined by `endian`,
2219 /// and any required padding bits are added on the MSB end.2214 /// and any required padding bits are added on the MSB end.
2220 pub fn writeTwosComplement(x: Const, buffer: []u8, bit_count: usize, abi_size: usize, endian: Endian) void {2215 pub fn writeTwosComplement(x: Const, buffer: []u8, endian: Endian) void {
2216 return writePackedTwosComplement(x, buffer, 0, 8 * buffer.len, endian);
2217 }
22212218
2222 // byte_count is our total write size2219 /// Write the value of `x` to a packed memory `buffer`.
2223 const byte_count = abi_size;2220 /// Asserts that `buffer` is large enough to contain a value of bit-size `bit_count`
2224 assert(8 * byte_count >= bit_count);2221 /// at offset `bit_offset`.
2225 assert(buffer.len >= byte_count);2222 ///
2223 /// This is equivalent to storing the value of an integer with `bit_count` bits as
2224 /// if it were a field in packed memory at the provided bit offset.
2225 pub fn writePackedTwosComplement(x: Const, bytes: []u8, bit_offset: usize, bit_count: usize, endian: Endian) void {
2226 assert(x.fitsInTwosComp(if (x.positive) .unsigned else .signed, bit_count));2226 assert(x.fitsInTwosComp(if (x.positive) .unsigned else .signed, bit_count));
22272227
2228 // Copy all complete limbs2228 // Copy all complete limbs
2229 var carry: u1 = if (x.positive) 0 else 1;2229 var carry: u1 = 1;
2230 var limb_index: usize = 0;2230 var limb_index: usize = 0;
2231 while (limb_index < byte_count / @sizeOf(Limb)) : (limb_index += 1) {2231 var bit_index: usize = 0;
2232 var buf_index = switch (endian) {2232 while (limb_index < bit_count / @bitSizeOf(Limb)) : (limb_index += 1) {
2233 .Little => @sizeOf(Limb) * limb_index,
2234 .Big => abi_size - (limb_index + 1) * @sizeOf(Limb),
2235 };
2236
2237 var limb: Limb = if (limb_index < x.limbs.len) x.limbs[limb_index] else 0;2233 var limb: Limb = if (limb_index < x.limbs.len) x.limbs[limb_index] else 0;
2234
2238 // 2's complement (bitwise not, then add carry bit)2235 // 2's complement (bitwise not, then add carry bit)
2239 if (!x.positive) carry = @boolToInt(@addWithOverflow(Limb, ~limb, carry, &limb));2236 if (!x.positive) carry = @boolToInt(@addWithOverflow(Limb, ~limb, carry, &limb));
22402237
2241 var limb_buf = @ptrCast(*[@sizeOf(Limb)]u8, buffer[buf_index..]);2238 // Write one Limb of bits
2242 mem.writeInt(Limb, limb_buf, limb, endian);2239 mem.writePackedInt(Limb, bytes, bit_index + bit_offset, limb, endian);
2240 bit_index += @bitSizeOf(Limb);
2243 }2241 }
22442242
2245 // Copy the remaining N bytes (N < @sizeOf(Limb))2243 // Copy the remaining bits
2246 var bytes_written = limb_index * @sizeOf(Limb);2244 if (bit_count != bit_index) {
2247 if (bytes_written != byte_count) {
2248 var limb: Limb = if (limb_index < x.limbs.len) x.limbs[limb_index] else 0;2245 var limb: Limb = if (limb_index < x.limbs.len) x.limbs[limb_index] else 0;
2246
2249 // 2's complement (bitwise not, then add carry bit)2247 // 2's complement (bitwise not, then add carry bit)
2250 if (!x.positive) _ = @addWithOverflow(Limb, ~limb, carry, &limb);2248 if (!x.positive) _ = @addWithOverflow(Limb, ~limb, carry, &limb);
22512249
2252 while (bytes_written != byte_count) {2250 // Write all remaining bits
2253 const write_size = std.math.floorPowerOfTwo(usize, byte_count - bytes_written);2251 mem.writeVarPackedInt(bytes, bit_index + bit_offset, bit_count - bit_index, limb, endian);
2254 var int_buffer = switch (endian) {
2255 .Little => buffer[bytes_written..],
2256 .Big => buffer[(abi_size - bytes_written - write_size)..],
2257 };
2258
2259 if (write_size == 1) {
2260 mem.writeInt(u8, int_buffer[0..1], @truncate(u8, limb), endian);
2261 } else if (@sizeOf(Limb) >= 2 and write_size == 2) {
2262 mem.writeInt(u16, int_buffer[0..2], @truncate(u16, limb), endian);
2263 } else if (@sizeOf(Limb) >= 4 and write_size == 4) {
2264 mem.writeInt(u32, int_buffer[0..4], @truncate(u32, limb), endian);
2265 } else if (@sizeOf(Limb) >= 8 and write_size == 8) {
2266 mem.writeInt(u64, int_buffer[0..8], @truncate(u64, limb), endian);
2267 } else if (@sizeOf(Limb) >= 16 and write_size == 16) {
2268 mem.writeInt(u128, int_buffer[0..16], @truncate(u128, limb), endian);
2269 } else if (@sizeOf(Limb) >= 32) {
2270 @compileError("@sizeOf(Limb) exceeded supported range");
2271 } else unreachable;
2272 limb >>= @intCast(Log2Limb, 8 * write_size);
2273 bytes_written += write_size;
2274 }
2275 }2252 }
2276 }2253 }
22772254
lib/std/math/big/int_test.zig+60-42
...@@ -2603,13 +2603,13 @@ test "big int conversion read/write twos complement" {...@@ -2603,13 +2603,13 @@ test "big int conversion read/write twos complement" {
26032603
2604 for (endians) |endian| {2604 for (endians) |endian| {
2605 // Writing to buffer and back should not change anything2605 // Writing to buffer and back should not change anything
2606 a.toConst().writeTwosComplement(buffer1, 493, abi_size, endian);2606 a.toConst().writeTwosComplement(buffer1[0..abi_size], endian);
2607 m.readTwosComplement(buffer1, 493, abi_size, endian, .unsigned);2607 m.readTwosComplement(buffer1[0..abi_size], 493, endian, .unsigned);
2608 try testing.expect(m.toConst().order(a.toConst()) == .eq);2608 try testing.expect(m.toConst().order(a.toConst()) == .eq);
26092609
2610 // Equivalent to @bitCast(i493, @as(u493, intMax(u493))2610 // Equivalent to @bitCast(i493, @as(u493, intMax(u493))
2611 a.toConst().writeTwosComplement(buffer1, 493, abi_size, endian);2611 a.toConst().writeTwosComplement(buffer1[0..abi_size], endian);
2612 m.readTwosComplement(buffer1, 493, abi_size, endian, .signed);2612 m.readTwosComplement(buffer1[0..abi_size], 493, endian, .signed);
2613 try testing.expect(m.toConst().orderAgainstScalar(-1) == .eq);2613 try testing.expect(m.toConst().orderAgainstScalar(-1) == .eq);
2614 }2614 }
2615}2615}
...@@ -2628,26 +2628,26 @@ test "big int conversion read twos complement with padding" {...@@ -2628,26 +2628,26 @@ test "big int conversion read twos complement with padding" {
2628 // (3) should sign-extend any bits from bit_count to 8 * abi_size2628 // (3) should sign-extend any bits from bit_count to 8 * abi_size
26292629
2630 var bit_count: usize = 12 * 8 + 1;2630 var bit_count: usize = 12 * 8 + 1;
2631 a.toConst().writeTwosComplement(buffer1, bit_count, 13, .Little);2631 a.toConst().writeTwosComplement(buffer1[0..13], .Little);
2632 try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0xaa, 0xaa, 0xaa }));2632 try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0xaa, 0xaa, 0xaa }));
2633 a.toConst().writeTwosComplement(buffer1, bit_count, 13, .Big);2633 a.toConst().writeTwosComplement(buffer1[0..13], .Big);
2634 try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xaa, 0xaa, 0xaa }));2634 try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xaa, 0xaa, 0xaa }));
2635 a.toConst().writeTwosComplement(buffer1, bit_count, 16, .Little);2635 a.toConst().writeTwosComplement(buffer1[0..16], .Little);
2636 try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0, 0x0, 0x0 }));2636 try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0, 0x0, 0x0 }));
2637 a.toConst().writeTwosComplement(buffer1, bit_count, 16, .Big);2637 a.toConst().writeTwosComplement(buffer1[0..16], .Big);
2638 try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0x0, 0x0, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd }));2638 try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0x0, 0x0, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd }));
26392639
2640 @memset(buffer1.ptr, 0xaa, buffer1.len);2640 @memset(buffer1.ptr, 0xaa, buffer1.len);
2641 try a.set(-0x01_02030405_06070809_0a0b0c0d);2641 try a.set(-0x01_02030405_06070809_0a0b0c0d);
2642 bit_count = 12 * 8 + 2;2642 bit_count = 12 * 8 + 2;
26432643
2644 a.toConst().writeTwosComplement(buffer1, bit_count, 13, .Little);2644 a.toConst().writeTwosComplement(buffer1[0..13], .Little);
2645 try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0xf3, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xaa, 0xaa, 0xaa }));2645 try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0xf3, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xaa, 0xaa, 0xaa }));
2646 a.toConst().writeTwosComplement(buffer1, bit_count, 13, .Big);2646 a.toConst().writeTwosComplement(buffer1[0..13], .Big);
2647 try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf3, 0xaa, 0xaa, 0xaa }));2647 try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf3, 0xaa, 0xaa, 0xaa }));
2648 a.toConst().writeTwosComplement(buffer1, bit_count, 16, .Little);2648 a.toConst().writeTwosComplement(buffer1[0..16], .Little);
2649 try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0xf3, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0xff, 0xff }));2649 try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0xf3, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0xff, 0xff }));
2650 a.toConst().writeTwosComplement(buffer1, bit_count, 16, .Big);2650 a.toConst().writeTwosComplement(buffer1[0..16], .Big);
2651 try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0xff, 0xff, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf3 }));2651 try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0xff, 0xff, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf3 }));
2652}2652}
26532653
...@@ -2660,17 +2660,15 @@ test "big int write twos complement +/- zero" {...@@ -2660,17 +2660,15 @@ test "big int write twos complement +/- zero" {
2660 defer testing.allocator.free(buffer1);2660 defer testing.allocator.free(buffer1);
2661 @memset(buffer1.ptr, 0xaa, buffer1.len);2661 @memset(buffer1.ptr, 0xaa, buffer1.len);
26622662
2663 var bit_count: usize = 0;
2664
2665 // Test zero2663 // Test zero
26662664
2667 m.toConst().writeTwosComplement(buffer1, bit_count, 13, .Little);2665 m.toConst().writeTwosComplement(buffer1[0..13], .Little);
2668 try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 13) ++ ([_]u8{0xaa} ** 3))));2666 try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 13) ++ ([_]u8{0xaa} ** 3))));
2669 m.toConst().writeTwosComplement(buffer1, bit_count, 13, .Big);2667 m.toConst().writeTwosComplement(buffer1[0..13], .Big);
2670 try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 13) ++ ([_]u8{0xaa} ** 3))));2668 try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 13) ++ ([_]u8{0xaa} ** 3))));
2671 m.toConst().writeTwosComplement(buffer1, bit_count, 16, .Little);2669 m.toConst().writeTwosComplement(buffer1[0..16], .Little);
2672 try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 16))));2670 try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 16))));
2673 m.toConst().writeTwosComplement(buffer1, bit_count, 16, .Big);2671 m.toConst().writeTwosComplement(buffer1[0..16], .Big);
2674 try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 16))));2672 try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 16))));
26752673
2676 @memset(buffer1.ptr, 0xaa, buffer1.len);2674 @memset(buffer1.ptr, 0xaa, buffer1.len);
...@@ -2678,13 +2676,13 @@ test "big int write twos complement +/- zero" {...@@ -2678,13 +2676,13 @@ test "big int write twos complement +/- zero" {
26782676
2679 // Test negative zero2677 // Test negative zero
26802678
2681 m.toConst().writeTwosComplement(buffer1, bit_count, 13, .Little);2679 m.toConst().writeTwosComplement(buffer1[0..13], .Little);
2682 try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 13) ++ ([_]u8{0xaa} ** 3))));2680 try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 13) ++ ([_]u8{0xaa} ** 3))));
2683 m.toConst().writeTwosComplement(buffer1, bit_count, 13, .Big);2681 m.toConst().writeTwosComplement(buffer1[0..13], .Big);
2684 try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 13) ++ ([_]u8{0xaa} ** 3))));2682 try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 13) ++ ([_]u8{0xaa} ** 3))));
2685 m.toConst().writeTwosComplement(buffer1, bit_count, 16, .Little);2683 m.toConst().writeTwosComplement(buffer1[0..16], .Little);
2686 try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 16))));2684 try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 16))));
2687 m.toConst().writeTwosComplement(buffer1, bit_count, 16, .Big);2685 m.toConst().writeTwosComplement(buffer1[0..16], .Big);
2688 try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 16))));2686 try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 16))));
2689}2687}
26902688
...@@ -2705,62 +2703,82 @@ test "big int conversion write twos complement with padding" {...@@ -2705,62 +2703,82 @@ test "big int conversion write twos complement with padding" {
2705 // Test 0x01_02030405_06070809_0a0b0c0d2703 // Test 0x01_02030405_06070809_0a0b0c0d
27062704
2707 buffer = &[_]u8{ 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0xb };2705 buffer = &[_]u8{ 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0xb };
2708 m.readTwosComplement(buffer, bit_count, 13, .Little, .unsigned);2706 m.readTwosComplement(buffer[0..13], bit_count, .Little, .unsigned);
2709 try testing.expect(m.toConst().orderAgainstScalar(0x01_02030405_06070809_0a0b0c0d) == .eq);2707 try testing.expect(m.toConst().orderAgainstScalar(0x01_02030405_06070809_0a0b0c0d) == .eq);
27102708
2711 buffer = &[_]u8{ 0xb, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd };2709 buffer = &[_]u8{ 0xb, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd };
2712 m.readTwosComplement(buffer, bit_count, 13, .Big, .unsigned);2710 m.readTwosComplement(buffer[0..13], bit_count, .Big, .unsigned);
2713 try testing.expect(m.toConst().orderAgainstScalar(0x01_02030405_06070809_0a0b0c0d) == .eq);2711 try testing.expect(m.toConst().orderAgainstScalar(0x01_02030405_06070809_0a0b0c0d) == .eq);
27142712
2715 buffer = &[_]u8{ 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0xab, 0xaa, 0xaa, 0xaa };2713 buffer = &[_]u8{ 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0xab, 0xaa, 0xaa, 0xaa };
2716 m.readTwosComplement(buffer, bit_count, 16, .Little, .unsigned);2714 m.readTwosComplement(buffer[0..16], bit_count, .Little, .unsigned);
2717 try testing.expect(m.toConst().orderAgainstScalar(0x01_02030405_06070809_0a0b0c0d) == .eq);2715 try testing.expect(m.toConst().orderAgainstScalar(0x01_02030405_06070809_0a0b0c0d) == .eq);
27182716
2719 buffer = &[_]u8{ 0xaa, 0xaa, 0xaa, 0xab, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd };2717 buffer = &[_]u8{ 0xaa, 0xaa, 0xaa, 0xab, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd };
2720 m.readTwosComplement(buffer, bit_count, 16, .Big, .unsigned);2718 m.readTwosComplement(buffer[0..16], bit_count, .Big, .unsigned);
2721 try testing.expect(m.toConst().orderAgainstScalar(0x01_02030405_06070809_0a0b0c0d) == .eq);2719 try testing.expect(m.toConst().orderAgainstScalar(0x01_02030405_06070809_0a0b0c0d) == .eq);
27222720
2721 bit_count = @sizeOf(Limb) * 8;
2722
2723 // Test 0x0a0a0a0a_02030405_06070809_0a0b0c0d
2724
2725 buffer = &[_]u8{ 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0xaa };
2726 m.readTwosComplement(buffer[0..13], bit_count, .Little, .unsigned);
2727 try testing.expect(m.toConst().orderAgainstScalar(@truncate(Limb, 0xaa_02030405_06070809_0a0b0c0d)) == .eq);
2728
2729 buffer = &[_]u8{ 0xaa, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd };
2730 m.readTwosComplement(buffer[0..13], bit_count, .Big, .unsigned);
2731 try testing.expect(m.toConst().orderAgainstScalar(@truncate(Limb, 0xaa_02030405_06070809_0a0b0c0d)) == .eq);
2732
2733 buffer = &[_]u8{ 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0xaa, 0xaa, 0xaa, 0xaa };
2734 m.readTwosComplement(buffer[0..16], bit_count, .Little, .unsigned);
2735 try testing.expect(m.toConst().orderAgainstScalar(@truncate(Limb, 0xaaaaaaaa_02030405_06070809_0a0b0c0d)) == .eq);
2736
2737 buffer = &[_]u8{ 0xaa, 0xaa, 0xaa, 0xaa, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd };
2738 m.readTwosComplement(buffer[0..16], bit_count, .Big, .unsigned);
2739 try testing.expect(m.toConst().orderAgainstScalar(@truncate(Limb, 0xaaaaaaaa_02030405_06070809_0a0b0c0d)) == .eq);
2740
2723 bit_count = 12 * 8 + 2;2741 bit_count = 12 * 8 + 2;
27242742
2725 // Test -0x01_02030405_06070809_0a0b0c0d2743 // Test -0x01_02030405_06070809_0a0b0c0d
27262744
2727 buffer = &[_]u8{ 0xf3, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0x02 };2745 buffer = &[_]u8{ 0xf3, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0x02 };
2728 m.readTwosComplement(buffer, bit_count, 13, .Little, .signed);2746 m.readTwosComplement(buffer[0..13], bit_count, .Little, .signed);
2729 try testing.expect(m.toConst().orderAgainstScalar(-0x01_02030405_06070809_0a0b0c0d) == .eq);2747 try testing.expect(m.toConst().orderAgainstScalar(-0x01_02030405_06070809_0a0b0c0d) == .eq);
27302748
2731 buffer = &[_]u8{ 0x02, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf3 };2749 buffer = &[_]u8{ 0x02, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf3 };
2732 m.readTwosComplement(buffer, bit_count, 13, .Big, .signed);2750 m.readTwosComplement(buffer[0..13], bit_count, .Big, .signed);
2733 try testing.expect(m.toConst().orderAgainstScalar(-0x01_02030405_06070809_0a0b0c0d) == .eq);2751 try testing.expect(m.toConst().orderAgainstScalar(-0x01_02030405_06070809_0a0b0c0d) == .eq);
27342752
2735 buffer = &[_]u8{ 0xf3, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0x02, 0xaa, 0xaa, 0xaa };2753 buffer = &[_]u8{ 0xf3, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0x02, 0xaa, 0xaa, 0xaa };
2736 m.readTwosComplement(buffer, bit_count, 16, .Little, .signed);2754 m.readTwosComplement(buffer[0..16], bit_count, .Little, .signed);
2737 try testing.expect(m.toConst().orderAgainstScalar(-0x01_02030405_06070809_0a0b0c0d) == .eq);2755 try testing.expect(m.toConst().orderAgainstScalar(-0x01_02030405_06070809_0a0b0c0d) == .eq);
27382756
2739 buffer = &[_]u8{ 0xaa, 0xaa, 0xaa, 0x02, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf3 };2757 buffer = &[_]u8{ 0xaa, 0xaa, 0xaa, 0x02, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf3 };
2740 m.readTwosComplement(buffer, bit_count, 16, .Big, .signed);2758 m.readTwosComplement(buffer[0..16], bit_count, .Big, .signed);
2741 try testing.expect(m.toConst().orderAgainstScalar(-0x01_02030405_06070809_0a0b0c0d) == .eq);2759 try testing.expect(m.toConst().orderAgainstScalar(-0x01_02030405_06070809_0a0b0c0d) == .eq);
27422760
2743 // Test 02761 // Test 0
27442762
2745 buffer = &([_]u8{0} ** 16);2763 buffer = &([_]u8{0} ** 16);
2746 m.readTwosComplement(buffer, bit_count, 13, .Little, .unsigned);2764 m.readTwosComplement(buffer[0..13], bit_count, .Little, .unsigned);
2747 try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq);2765 try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq);
2748 m.readTwosComplement(buffer, bit_count, 13, .Big, .unsigned);2766 m.readTwosComplement(buffer[0..13], bit_count, .Big, .unsigned);
2749 try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq);2767 try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq);
2750 m.readTwosComplement(buffer, bit_count, 16, .Little, .unsigned);2768 m.readTwosComplement(buffer[0..16], bit_count, .Little, .unsigned);
2751 try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq);2769 try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq);
2752 m.readTwosComplement(buffer, bit_count, 16, .Big, .unsigned);2770 m.readTwosComplement(buffer[0..16], bit_count, .Big, .unsigned);
2753 try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq);2771 try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq);
27542772
2755 bit_count = 0;2773 bit_count = 0;
2756 buffer = &([_]u8{0xaa} ** 16);2774 buffer = &([_]u8{0xaa} ** 16);
2757 m.readTwosComplement(buffer, bit_count, 13, .Little, .unsigned);2775 m.readTwosComplement(buffer[0..13], bit_count, .Little, .unsigned);
2758 try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq);2776 try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq);
2759 m.readTwosComplement(buffer, bit_count, 13, .Big, .unsigned);2777 m.readTwosComplement(buffer[0..13], bit_count, .Big, .unsigned);
2760 try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq);2778 try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq);
2761 m.readTwosComplement(buffer, bit_count, 16, .Little, .unsigned);2779 m.readTwosComplement(buffer[0..16], bit_count, .Little, .unsigned);
2762 try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq);2780 try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq);
2763 m.readTwosComplement(buffer, bit_count, 16, .Big, .unsigned);2781 m.readTwosComplement(buffer[0..16], bit_count, .Big, .unsigned);
2764 try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq);2782 try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq);
2765}2783}
27662784
...@@ -2779,15 +2797,15 @@ test "big int conversion write twos complement zero" {...@@ -2779,15 +2797,15 @@ test "big int conversion write twos complement zero" {
2779 var buffer: []const u8 = undefined;2797 var buffer: []const u8 = undefined;
27802798
2781 buffer = &([_]u8{0} ** 13);2799 buffer = &([_]u8{0} ** 13);
2782 m.readTwosComplement(buffer, bit_count, 13, .Little, .unsigned);2800 m.readTwosComplement(buffer[0..13], bit_count, .Little, .unsigned);
2783 try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq);2801 try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq);
2784 m.readTwosComplement(buffer, bit_count, 13, .Big, .unsigned);2802 m.readTwosComplement(buffer[0..13], bit_count, .Big, .unsigned);
2785 try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq);2803 try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq);
27862804
2787 buffer = &([_]u8{0} ** 16);2805 buffer = &([_]u8{0} ** 16);
2788 m.readTwosComplement(buffer, bit_count, 16, .Little, .unsigned);2806 m.readTwosComplement(buffer[0..16], bit_count, .Little, .unsigned);
2789 try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq);2807 try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq);
2790 m.readTwosComplement(buffer, bit_count, 16, .Big, .unsigned);2808 m.readTwosComplement(buffer[0..16], bit_count, .Big, .unsigned);
2791 try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq);2809 try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq);
2792}2810}
27932811
src/Sema.zig-42
...@@ -26445,48 +26445,6 @@ fn bitCastVal(...@@ -26445,48 +26445,6 @@ fn bitCastVal(
26445 const target = sema.mod.getTarget();26445 const target = sema.mod.getTarget();
26446 if (old_ty.eql(new_ty, sema.mod)) return val;26446 if (old_ty.eql(new_ty, sema.mod)) return val;
2644726447
26448 // Some conversions have a bitwise definition that ignores in-memory layout,
26449 // such as converting between f80 and u80.
26450
26451 if (old_ty.eql(Type.f80, sema.mod) and new_ty.isAbiInt()) {
26452 const float = val.toFloat(f80);
26453 switch (new_ty.intInfo(target).signedness) {
26454 .signed => {
26455 const int = @bitCast(i80, float);
26456 const limbs = try sema.arena.alloc(std.math.big.Limb, 2);
26457 const big_int = std.math.big.int.Mutable.init(limbs, int);
26458 return Value.fromBigInt(sema.arena, big_int.toConst());
26459 },
26460 .unsigned => {
26461 const int = @bitCast(u80, float);
26462 const limbs = try sema.arena.alloc(std.math.big.Limb, 2);
26463 const big_int = std.math.big.int.Mutable.init(limbs, int);
26464 return Value.fromBigInt(sema.arena, big_int.toConst());
26465 },
26466 }
26467 }
26468
26469 if (new_ty.eql(Type.f80, sema.mod) and old_ty.isAbiInt()) {
26470 var bigint_space: Value.BigIntSpace = undefined;
26471 var bigint = try val.toBigIntAdvanced(&bigint_space, target, sema.kit(block, src));
26472 switch (old_ty.intInfo(target).signedness) {
26473 .signed => {
26474 // This conversion cannot fail because we already checked bit size before
26475 // calling bitCastVal.
26476 const int = bigint.to(i80) catch unreachable;
26477 const float = @bitCast(f80, int);
26478 return Value.Tag.float_80.create(sema.arena, float);
26479 },
26480 .unsigned => {
26481 // This conversion cannot fail because we already checked bit size before
26482 // calling bitCastVal.
26483 const int = bigint.to(u80) catch unreachable;
26484 const float = @bitCast(f80, int);
26485 return Value.Tag.float_80.create(sema.arena, float);
26486 },
26487 }
26488 }
26489
26490 // For types with well-defined memory layouts, we serialize them a byte buffer,26448 // For types with well-defined memory layouts, we serialize them a byte buffer,
26491 // then deserialize to the new type.26449 // then deserialize to the new type.
26492 const abi_size = try sema.usizeCast(block, src, old_ty.abiSize(target));26450 const abi_size = try sema.usizeCast(block, src, old_ty.abiSize(target));
src/codegen.zig+1-1
...@@ -470,7 +470,7 @@ pub fn generateSymbol(...@@ -470,7 +470,7 @@ pub fn generateSymbol(
470 const abi_size = math.cast(usize, typed_value.ty.abiSize(target)) orelse return error.Overflow;470 const abi_size = math.cast(usize, typed_value.ty.abiSize(target)) orelse return error.Overflow;
471 const start = code.items.len;471 const start = code.items.len;
472 try code.resize(start + abi_size);472 try code.resize(start + abi_size);
473 bigint.writeTwosComplement(code.items[start..][0..abi_size], info.bits, abi_size, endian);473 bigint.writeTwosComplement(code.items[start..][0..abi_size], endian);
474 return Result{ .appended = {} };474 return Result{ .appended = {} };
475 }475 }
476 switch (info.signedness) {476 switch (info.signedness) {
src/value.zig+245-244
...@@ -1206,8 +1206,13 @@ pub const Value = extern union {...@@ -1206,8 +1206,13 @@ pub const Value = extern union {
1206 };1206 };
1207 }1207 }
12081208
1209 /// Write a Value's contents to `buffer`.
1210 ///
1211 /// Asserts that buffer.len >= ty.abiSize(). The buffer is allowed to extend past
1212 /// the end of the value in memory.
1209 pub fn writeToMemory(val: Value, ty: Type, mod: *Module, buffer: []u8) void {1213 pub fn writeToMemory(val: Value, ty: Type, mod: *Module, buffer: []u8) void {
1210 const target = mod.getTarget();1214 const target = mod.getTarget();
1215 const endian = target.cpu.arch.endian();
1211 if (val.isUndef()) {1216 if (val.isUndef()) {
1212 const size = @intCast(usize, ty.abiSize(target));1217 const size = @intCast(usize, ty.abiSize(target));
1213 std.mem.set(u8, buffer[0..size], 0xaa);1218 std.mem.set(u8, buffer[0..size], 0xaa);
...@@ -1218,31 +1223,41 @@ pub const Value = extern union {...@@ -1218,31 +1223,41 @@ pub const Value = extern union {
1218 .Bool => {1223 .Bool => {
1219 buffer[0] = @boolToInt(val.toBool());1224 buffer[0] = @boolToInt(val.toBool());
1220 },1225 },
1221 .Int => {1226 .Int, .Enum => {
1222 var bigint_buffer: BigIntSpace = undefined;1227 const int_info = ty.intInfo(target);
1223 const bigint = val.toBigInt(&bigint_buffer, target);1228 const bits = int_info.bits;
1224 const bits = ty.intInfo(target).bits;1229 const byte_count = (bits + 7) / 8;
1225 const abi_size = @intCast(usize, ty.abiSize(target));1230
1226 bigint.writeTwosComplement(buffer, bits, abi_size, target.cpu.arch.endian());
1227 },
1228 .Enum => {
1229 var enum_buffer: Payload.U64 = undefined;1231 var enum_buffer: Payload.U64 = undefined;
1230 const int_val = val.enumToInt(ty, &enum_buffer);1232 const int_val = val.enumToInt(ty, &enum_buffer);
1231 var bigint_buffer: BigIntSpace = undefined;1233
1232 const bigint = int_val.toBigInt(&bigint_buffer, target);1234 if (byte_count <= @sizeOf(u64)) {
1233 const bits = ty.intInfo(target).bits;1235 const int: u64 = switch (int_val.tag()) {
1234 const abi_size = @intCast(usize, ty.abiSize(target));1236 .zero => 0,
1235 bigint.writeTwosComplement(buffer, bits, abi_size, target.cpu.arch.endian());1237 .one => 1,
1238 .int_u64 => int_val.castTag(.int_u64).?.data,
1239 .int_i64 => @bitCast(u64, int_val.castTag(.int_i64).?.data),
1240 else => unreachable,
1241 };
1242 for (buffer[0..byte_count]) |_, i| switch (endian) {
1243 .Little => buffer[i] = @truncate(u8, (int >> @intCast(u6, (8 * i)))),
1244 .Big => buffer[byte_count - i - 1] = @truncate(u8, (int >> @intCast(u6, (8 * i)))),
1245 };
1246 } else {
1247 var bigint_buffer: BigIntSpace = undefined;
1248 const bigint = int_val.toBigInt(&bigint_buffer, target);
1249 bigint.writeTwosComplement(buffer[0..byte_count], endian);
1250 }
1236 },1251 },
1237 .Float => switch (ty.floatBits(target)) {1252 .Float => switch (ty.floatBits(target)) {
1238 16 => return floatWriteToMemory(f16, val.toFloat(f16), target, buffer),1253 16 => std.mem.writeInt(u16, buffer[0..2], @bitCast(u16, val.toFloat(f16)), endian),
1239 32 => return floatWriteToMemory(f32, val.toFloat(f32), target, buffer),1254 32 => std.mem.writeInt(u32, buffer[0..4], @bitCast(u32, val.toFloat(f32)), endian),
1240 64 => return floatWriteToMemory(f64, val.toFloat(f64), target, buffer),1255 64 => std.mem.writeInt(u64, buffer[0..8], @bitCast(u64, val.toFloat(f64)), endian),
1241 80 => return floatWriteToMemory(f80, val.toFloat(f80), target, buffer),1256 80 => std.mem.writeInt(u80, buffer[0..10], @bitCast(u80, val.toFloat(f80)), endian),
1242 128 => return floatWriteToMemory(f128, val.toFloat(f128), target, buffer),1257 128 => std.mem.writeInt(u128, buffer[0..16], @bitCast(u128, val.toFloat(f128)), endian),
1243 else => unreachable,1258 else => unreachable,
1244 },1259 },
1245 .Array, .Vector => {1260 .Array => {
1246 const len = ty.arrayLen();1261 const len = ty.arrayLen();
1247 const elem_ty = ty.childType();1262 const elem_ty = ty.childType();
1248 const elem_size = @intCast(usize, elem_ty.abiSize(target));1263 const elem_size = @intCast(usize, elem_ty.abiSize(target));
...@@ -1251,10 +1266,16 @@ pub const Value = extern union {...@@ -1251,10 +1266,16 @@ pub const Value = extern union {
1251 var buf_off: usize = 0;1266 var buf_off: usize = 0;
1252 while (elem_i < len) : (elem_i += 1) {1267 while (elem_i < len) : (elem_i += 1) {
1253 const elem_val = val.elemValueBuffer(mod, elem_i, &elem_value_buf);1268 const elem_val = val.elemValueBuffer(mod, elem_i, &elem_value_buf);
1254 writeToMemory(elem_val, elem_ty, mod, buffer[buf_off..]);1269 elem_val.writeToMemory(elem_ty, mod, buffer[buf_off..]);
1255 buf_off += elem_size;1270 buf_off += elem_size;
1256 }1271 }
1257 },1272 },
1273 .Vector => {
1274 // We use byte_count instead of abi_size here, so that any padding bytes
1275 // follow the data bytes, on both big- and little-endian systems.
1276 const byte_count = (@intCast(usize, ty.bitSize(target)) + 7) / 8;
1277 writeToPackedMemory(val, ty, mod, buffer[0..byte_count], 0);
1278 },
1258 .Struct => switch (ty.containerLayout()) {1279 .Struct => switch (ty.containerLayout()) {
1259 .Auto => unreachable, // Sema is supposed to have emitted a compile error already1280 .Auto => unreachable, // Sema is supposed to have emitted a compile error already
1260 .Extern => {1281 .Extern => {
...@@ -1266,122 +1287,113 @@ pub const Value = extern union {...@@ -1266,122 +1287,113 @@ pub const Value = extern union {
1266 }1287 }
1267 },1288 },
1268 .Packed => {1289 .Packed => {
1269 // TODO allocate enough heap space instead of using this buffer1290 const byte_count = (@intCast(usize, ty.bitSize(target)) + 7) / 8;
1270 // on the stack.1291 writeToPackedMemory(val, ty, mod, buffer[0..byte_count], 0);
1271 var buf: [16]std.math.big.Limb = undefined;
1272 const host_int = packedStructToInt(val, ty, target, &buf);
1273 const abi_size = @intCast(usize, ty.abiSize(target));
1274 const bit_size = @intCast(usize, ty.bitSize(target));
1275 host_int.writeTwosComplement(buffer, bit_size, abi_size, target.cpu.arch.endian());
1276 },1292 },
1277 },1293 },
1278 .ErrorSet => {1294 .ErrorSet => {
1279 // TODO revisit this when we have the concept of the error tag type1295 // TODO revisit this when we have the concept of the error tag type
1280 const Int = u16;1296 const Int = u16;
1281 const int = mod.global_error_set.get(val.castTag(.@"error").?.data.name).?;1297 const int = mod.global_error_set.get(val.castTag(.@"error").?.data.name).?;
1282 std.mem.writeInt(Int, buffer[0..@sizeOf(Int)], @intCast(Int, int), target.cpu.arch.endian());1298 std.mem.writeInt(Int, buffer[0..@sizeOf(Int)], @intCast(Int, int), endian);
1283 },1299 },
1284 else => @panic("TODO implement writeToMemory for more types"),1300 else => @panic("TODO implement writeToMemory for more types"),
1285 }1301 }
1286 }1302 }
12871303
1288 fn packedStructToInt(val: Value, ty: Type, target: Target, buf: []std.math.big.Limb) BigIntConst {1304 /// Write a Value's contents to `buffer`.
1289 var bigint = BigIntMutable.init(buf, 0);1305 ///
1290 const fields = ty.structFields().values();1306 /// Both the start and the end of the provided buffer must be tight, since
1291 const field_vals = val.castTag(.aggregate).?.data;1307 /// big-endian packed memory layouts start at the end of the buffer.
1292 var bits: u16 = 0;1308 pub fn writeToPackedMemory(val: Value, ty: Type, mod: *Module, buffer: []u8, bit_offset: usize) void {
1293 // TODO allocate enough heap space instead of using this buffer1309 const target = mod.getTarget();
1294 // on the stack.
1295 var field_buf: [16]std.math.big.Limb = undefined;
1296 var field_space: BigIntSpace = undefined;
1297 var field_buf2: [16]std.math.big.Limb = undefined;
1298 for (fields) |field, i| {
1299 const field_val = field_vals[i];
1300 const field_bigint_const = switch (field.ty.zigTypeTag()) {
1301 .Void => continue,
1302 .Float => floatToBigInt(field_val, field.ty, target, &field_buf),
1303 .Int, .Bool => intOrBoolToBigInt(field_val, field.ty, target, &field_buf, &field_space),
1304 .Struct => switch (field.ty.containerLayout()) {
1305 .Auto, .Extern => unreachable, // Sema should have error'd before this.
1306 .Packed => packedStructToInt(field_val, field.ty, target, &field_buf),
1307 },
1308 .Vector => vectorToBigInt(field_val, field.ty, target, &field_buf),
1309 .Enum => enumToBigInt(field_val, field.ty, target, &field_space),
1310 .Union => unreachable, // TODO: packed structs support packed unions
1311 else => unreachable,
1312 };
1313 var field_bigint = BigIntMutable.init(&field_buf2, 0);
1314 field_bigint.shiftLeft(field_bigint_const, bits);
1315 bits += @intCast(u16, field.ty.bitSize(target));
1316 bigint.bitOr(bigint.toConst(), field_bigint.toConst());
1317 }
1318 return bigint.toConst();
1319 }
1320
1321 fn intOrBoolToBigInt(val: Value, ty: Type, target: Target, buf: []std.math.big.Limb, space: *BigIntSpace) BigIntConst {
1322 const big_int_const = val.toBigInt(space, target);
1323 if (big_int_const.positive) return big_int_const;
1324
1325 var big_int = BigIntMutable.init(buf, 0);
1326 big_int.bitNotWrap(big_int_const.negate(), .unsigned, @intCast(u32, ty.bitSize(target)));
1327 big_int.addScalar(big_int.toConst(), 1);
1328 return big_int.toConst();
1329 }
1330
1331 fn vectorToBigInt(val: Value, ty: Type, target: Target, buf: []std.math.big.Limb) BigIntConst {
1332 const endian = target.cpu.arch.endian();1310 const endian = target.cpu.arch.endian();
1333 var vec_bitint = BigIntMutable.init(buf, 0);1311 if (val.isUndef()) {
1334 const vec_len = @intCast(usize, ty.arrayLen());1312 const bit_size = @intCast(usize, ty.bitSize(target));
1335 const elem_ty = ty.childType();1313 std.mem.writeVarPackedInt(buffer, bit_offset, bit_size, @as(u1, 0), endian);
1336 const elem_size = @intCast(usize, elem_ty.bitSize(target));1314 return;
1337
1338 var elem_buf: [16]std.math.big.Limb = undefined;
1339 var elem_space: BigIntSpace = undefined;
1340 var elem_buf2: [16]std.math.big.Limb = undefined;
1341
1342 var elem_i: usize = 0;
1343 while (elem_i < vec_len) : (elem_i += 1) {
1344 const elem_i_target = if (endian == .Big) vec_len - elem_i - 1 else elem_i;
1345 const elem_val = val.indexVectorlike(elem_i_target);
1346 const elem_bigint_const = switch (elem_ty.zigTypeTag()) {
1347 .Int, .Bool => intOrBoolToBigInt(elem_val, elem_ty, target, &elem_buf, &elem_space),
1348 .Float => floatToBigInt(elem_val, elem_ty, target, &elem_buf),
1349 .Pointer => unreachable, // TODO
1350 else => unreachable, // Sema should not let this happen
1351 };
1352 var elem_bitint = BigIntMutable.init(&elem_buf2, 0);
1353 elem_bitint.shiftLeft(elem_bigint_const, elem_size * elem_i);
1354 vec_bitint.bitOr(vec_bitint.toConst(), elem_bitint.toConst());
1355 }1315 }
1356 return vec_bitint.toConst();1316 switch (ty.zigTypeTag()) {
1357 }1317 .Void => {},
1318 .Bool => {
1319 const byte_index = switch (endian) {
1320 .Little => bit_offset / 8,
1321 .Big => buffer.len - bit_offset / 8 - 1,
1322 };
1323 if (val.toBool()) {
1324 buffer[byte_index] |= (@as(u8, 1) << @intCast(u3, bit_offset % 8));
1325 } else {
1326 buffer[byte_index] &= ~(@as(u8, 1) << @intCast(u3, bit_offset % 8));
1327 }
1328 },
1329 .Int, .Enum => {
1330 const bits = ty.intInfo(target).bits;
1331 const abi_size = @intCast(usize, ty.abiSize(target));
13581332
1359 fn enumToBigInt(val: Value, ty: Type, target: Target, space: *BigIntSpace) BigIntConst {1333 var enum_buffer: Payload.U64 = undefined;
1360 var enum_buf: Payload.U64 = undefined;1334 const int_val = val.enumToInt(ty, &enum_buffer);
1361 const int_val = val.enumToInt(ty, &enum_buf);
1362 return int_val.toBigInt(space, target);
1363 }
13641335
1365 fn floatToBigInt(val: Value, ty: Type, target: Target, buf: []std.math.big.Limb) BigIntConst {1336 if (abi_size <= @sizeOf(u64)) {
1366 return switch (ty.floatBits(target)) {1337 const int: u64 = switch (int_val.tag()) {
1367 16 => bitcastFloatToBigInt(f16, val.toFloat(f16), buf),1338 .zero => 0,
1368 32 => bitcastFloatToBigInt(f32, val.toFloat(f32), buf),1339 .one => 1,
1369 64 => bitcastFloatToBigInt(f64, val.toFloat(f64), buf),1340 .int_u64 => int_val.castTag(.int_u64).?.data,
1370 80 => bitcastFloatToBigInt(f80, val.toFloat(f80), buf),1341 .int_i64 => @bitCast(u64, int_val.castTag(.int_i64).?.data),
1371 128 => bitcastFloatToBigInt(f128, val.toFloat(f128), buf),1342 else => unreachable,
1372 else => unreachable,1343 };
1373 };1344 std.mem.writeVarPackedInt(buffer, bit_offset, bits, int, endian);
1374 }1345 } else {
1346 var bigint_buffer: BigIntSpace = undefined;
1347 const bigint = int_val.toBigInt(&bigint_buffer, target);
1348 bigint.writePackedTwosComplement(buffer, bit_offset, bits, endian);
1349 }
1350 },
1351 .Float => switch (ty.floatBits(target)) {
1352 16 => std.mem.writePackedInt(u16, buffer, bit_offset, @bitCast(u16, val.toFloat(f16)), endian),
1353 32 => std.mem.writePackedInt(u32, buffer, bit_offset, @bitCast(u32, val.toFloat(f32)), endian),
1354 64 => std.mem.writePackedInt(u64, buffer, bit_offset, @bitCast(u64, val.toFloat(f64)), endian),
1355 80 => std.mem.writePackedInt(u80, buffer, bit_offset, @bitCast(u80, val.toFloat(f80)), endian),
1356 128 => std.mem.writePackedInt(u128, buffer, bit_offset, @bitCast(u128, val.toFloat(f128)), endian),
1357 else => unreachable,
1358 },
1359 .Vector => {
1360 const len = ty.arrayLen();
1361 const elem_ty = ty.childType();
1362 const elem_bit_size = @intCast(u16, elem_ty.bitSize(target));
13751363
1376 fn bitcastFloatToBigInt(comptime F: type, f: F, buf: []std.math.big.Limb) BigIntConst {1364 var bits: u16 = 0;
1377 const Int = @Type(.{ .Int = .{1365 var elem_i: usize = 0;
1378 .signedness = .unsigned,1366 var elem_value_buf: ElemValueBuffer = undefined;
1379 .bits = @typeInfo(F).Float.bits,1367 while (elem_i < len) : (elem_i += 1) {
1380 } });1368 // On big-endian systems, LLVM reverses the element order of vectors by default
1381 const int = @bitCast(Int, f);1369 const tgt_elem_i = if (endian == .Big) len - elem_i - 1 else elem_i;
1382 return BigIntMutable.init(buf, int).toConst();1370 const elem_val = val.elemValueBuffer(mod, tgt_elem_i, &elem_value_buf);
1371 elem_val.writeToPackedMemory(elem_ty, mod, buffer, bit_offset + bits);
1372 bits += elem_bit_size;
1373 }
1374 },
1375 .Struct => switch (ty.containerLayout()) {
1376 .Auto => unreachable, // Sema is supposed to have emitted a compile error already
1377 .Extern => unreachable, // Handled in non-packed writeToMemory
1378 .Packed => {
1379 var bits: u16 = 0;
1380 const fields = ty.structFields().values();
1381 const field_vals = val.castTag(.aggregate).?.data;
1382 for (fields) |field, i| {
1383 const field_bits = @intCast(u16, field.ty.bitSize(target));
1384 field_vals[i].writeToPackedMemory(field.ty, mod, buffer, bit_offset + bits);
1385 bits += field_bits;
1386 }
1387 },
1388 },
1389 else => @panic("TODO implement writeToPackedMemory for more types"),
1390 }
1383 }1391 }
13841392
1393 /// Load a Value from the contents of `buffer`.
1394 ///
1395 /// Asserts that buffer.len >= ty.abiSize(). The buffer is allowed to extend past
1396 /// the end of the value in memory.
1385 pub fn readFromMemory(1397 pub fn readFromMemory(
1386 ty: Type,1398 ty: Type,
1387 mod: *Module,1399 mod: *Module,
...@@ -1389,6 +1401,7 @@ pub const Value = extern union {...@@ -1389,6 +1401,7 @@ pub const Value = extern union {
1389 arena: Allocator,1401 arena: Allocator,
1390 ) Allocator.Error!Value {1402 ) Allocator.Error!Value {
1391 const target = mod.getTarget();1403 const target = mod.getTarget();
1404 const endian = target.cpu.arch.endian();
1392 switch (ty.zigTypeTag()) {1405 switch (ty.zigTypeTag()) {
1393 .Void => return Value.@"void",1406 .Void => return Value.@"void",
1394 .Bool => {1407 .Bool => {
...@@ -1398,27 +1411,40 @@ pub const Value = extern union {...@@ -1398,27 +1411,40 @@ pub const Value = extern union {
1398 return Value.@"true";1411 return Value.@"true";
1399 }1412 }
1400 },1413 },
1401 .Int => {1414 .Int, .Enum => {
1402 if (buffer.len == 0) return Value.zero;
1403 const int_info = ty.intInfo(target);1415 const int_info = ty.intInfo(target);
1404 const endian = target.cpu.arch.endian();1416 const bits = int_info.bits;
1405 const Limb = std.math.big.Limb;1417 const byte_count = (bits + 7) / 8;
1406 const limb_count = (buffer.len + @sizeOf(Limb) - 1) / @sizeOf(Limb);1418 if (bits == 0 or buffer.len == 0) return Value.zero;
1407 const limbs_buffer = try arena.alloc(Limb, limb_count);1419
1408 const abi_size = @intCast(usize, ty.abiSize(target));1420 if (bits <= 64) switch (int_info.signedness) { // Fast path for integers <= u64
1409 var bigint = BigIntMutable.init(limbs_buffer, 0);1421 .signed => {
1410 bigint.readTwosComplement(buffer, int_info.bits, abi_size, endian, int_info.signedness);1422 const val = std.mem.readVarInt(i64, buffer[0..byte_count], endian);
1411 return fromBigInt(arena, bigint.toConst());1423 return Value.Tag.int_i64.create(arena, (val << @intCast(u6, 64 - bits)) >> @intCast(u6, 64 - bits));
1424 },
1425 .unsigned => {
1426 const val = std.mem.readVarInt(u64, buffer[0..byte_count], endian);
1427 return Value.Tag.int_u64.create(arena, (val << @intCast(u6, 64 - bits)) >> @intCast(u6, 64 - bits));
1428 },
1429 } else { // Slow path, we have to construct a big-int
1430 const Limb = std.math.big.Limb;
1431 const limb_count = (byte_count + @sizeOf(Limb) - 1) / @sizeOf(Limb);
1432 const limbs_buffer = try arena.alloc(Limb, limb_count);
1433
1434 var bigint = BigIntMutable.init(limbs_buffer, 0);
1435 bigint.readTwosComplement(buffer[0..byte_count], bits, endian, int_info.signedness);
1436 return fromBigInt(arena, bigint.toConst());
1437 }
1412 },1438 },
1413 .Float => switch (ty.floatBits(target)) {1439 .Float => switch (ty.floatBits(target)) {
1414 16 => return Value.Tag.float_16.create(arena, floatReadFromMemory(f16, target, buffer)),1440 16 => return Value.Tag.float_16.create(arena, @bitCast(f16, std.mem.readInt(u16, buffer[0..2], endian))),
1415 32 => return Value.Tag.float_32.create(arena, floatReadFromMemory(f32, target, buffer)),1441 32 => return Value.Tag.float_32.create(arena, @bitCast(f32, std.mem.readInt(u32, buffer[0..4], endian))),
1416 64 => return Value.Tag.float_64.create(arena, floatReadFromMemory(f64, target, buffer)),1442 64 => return Value.Tag.float_64.create(arena, @bitCast(f64, std.mem.readInt(u64, buffer[0..8], endian))),
1417 80 => return Value.Tag.float_80.create(arena, floatReadFromMemory(f80, target, buffer)),1443 80 => return Value.Tag.float_80.create(arena, @bitCast(f80, std.mem.readInt(u80, buffer[0..10], endian))),
1418 128 => return Value.Tag.float_128.create(arena, floatReadFromMemory(f128, target, buffer)),1444 128 => return Value.Tag.float_128.create(arena, @bitCast(f128, std.mem.readInt(u128, buffer[0..16], endian))),
1419 else => unreachable,1445 else => unreachable,
1420 },1446 },
1421 .Array, .Vector => {1447 .Array => {
1422 const elem_ty = ty.childType();1448 const elem_ty = ty.childType();
1423 const elem_size = elem_ty.abiSize(target);1449 const elem_size = elem_ty.abiSize(target);
1424 const elems = try arena.alloc(Value, @intCast(usize, ty.arrayLen()));1450 const elems = try arena.alloc(Value, @intCast(usize, ty.arrayLen()));
...@@ -1429,6 +1455,12 @@ pub const Value = extern union {...@@ -1429,6 +1455,12 @@ pub const Value = extern union {
1429 }1455 }
1430 return Tag.aggregate.create(arena, elems);1456 return Tag.aggregate.create(arena, elems);
1431 },1457 },
1458 .Vector => {
1459 // We use byte_count instead of abi_size here, so that any padding bytes
1460 // follow the data bytes, on both big- and little-endian systems.
1461 const byte_count = (@intCast(usize, ty.bitSize(target)) + 7) / 8;
1462 return readFromPackedMemory(ty, mod, buffer[0..byte_count], 0, arena);
1463 },
1432 .Struct => switch (ty.containerLayout()) {1464 .Struct => switch (ty.containerLayout()) {
1433 .Auto => unreachable, // Sema is supposed to have emitted a compile error already1465 .Auto => unreachable, // Sema is supposed to have emitted a compile error already
1434 .Extern => {1466 .Extern => {
...@@ -1436,26 +1468,20 @@ pub const Value = extern union {...@@ -1436,26 +1468,20 @@ pub const Value = extern union {
1436 const field_vals = try arena.alloc(Value, fields.len);1468 const field_vals = try arena.alloc(Value, fields.len);
1437 for (fields) |field, i| {1469 for (fields) |field, i| {
1438 const off = @intCast(usize, ty.structFieldOffset(i, target));1470 const off = @intCast(usize, ty.structFieldOffset(i, target));
1439 field_vals[i] = try readFromMemory(field.ty, mod, buffer[off..], arena);1471 const sz = @intCast(usize, ty.structFieldType(i).abiSize(target));
1472 field_vals[i] = try readFromMemory(field.ty, mod, buffer[off..(off + sz)], arena);
1440 }1473 }
1441 return Tag.aggregate.create(arena, field_vals);1474 return Tag.aggregate.create(arena, field_vals);
1442 },1475 },
1443 .Packed => {1476 .Packed => {
1444 const endian = target.cpu.arch.endian();1477 const byte_count = (@intCast(usize, ty.bitSize(target)) + 7) / 8;
1445 const Limb = std.math.big.Limb;1478 return readFromPackedMemory(ty, mod, buffer[0..byte_count], 0, arena);
1446 const abi_size = @intCast(usize, ty.abiSize(target));
1447 const bit_size = @intCast(usize, ty.bitSize(target));
1448 const limb_count = (buffer.len + @sizeOf(Limb) - 1) / @sizeOf(Limb);
1449 const limbs_buffer = try arena.alloc(Limb, limb_count);
1450 var bigint = BigIntMutable.init(limbs_buffer, 0);
1451 bigint.readTwosComplement(buffer, bit_size, abi_size, endian, .unsigned);
1452 return intToPackedStruct(ty, target, bigint.toConst(), arena);
1453 },1479 },
1454 },1480 },
1455 .ErrorSet => {1481 .ErrorSet => {
1456 // TODO revisit this when we have the concept of the error tag type1482 // TODO revisit this when we have the concept of the error tag type
1457 const Int = u16;1483 const Int = u16;
1458 const int = std.mem.readInt(Int, buffer[0..@sizeOf(Int)], target.cpu.arch.endian());1484 const int = std.mem.readInt(Int, buffer[0..@sizeOf(Int)], endian);
14591485
1460 const payload = try arena.create(Value.Payload.Error);1486 const payload = try arena.create(Value.Payload.Error);
1461 payload.* = .{1487 payload.* = .{
...@@ -1468,115 +1494,90 @@ pub const Value = extern union {...@@ -1468,115 +1494,90 @@ pub const Value = extern union {
1468 }1494 }
1469 }1495 }
14701496
1471 fn intToPackedStruct(1497 /// Load a Value from the contents of `buffer`.
1498 ///
1499 /// Both the start and the end of the provided buffer must be tight, since
1500 /// big-endian packed memory layouts start at the end of the buffer.
1501 pub fn readFromPackedMemory(
1472 ty: Type,1502 ty: Type,
1473 target: Target,1503 mod: *Module,
1474 bigint: BigIntConst,1504 buffer: []const u8,
1505 bit_offset: usize,
1475 arena: Allocator,1506 arena: Allocator,
1476 ) Allocator.Error!Value {1507 ) Allocator.Error!Value {
1477 const limbs_buffer = try arena.alloc(std.math.big.Limb, bigint.limbs.len);1508 const target = mod.getTarget();
1478 var bigint_mut = bigint.toMutable(limbs_buffer);
1479 const fields = ty.structFields().values();
1480 const field_vals = try arena.alloc(Value, fields.len);
1481 var bits: u16 = 0;
1482 for (fields) |field, i| {
1483 const field_bits = @intCast(u16, field.ty.bitSize(target));
1484 bigint_mut.shiftRight(bigint, bits);
1485 bigint_mut.truncate(bigint_mut.toConst(), .unsigned, field_bits);
1486 bits += field_bits;
1487 const field_bigint = bigint_mut.toConst();
1488
1489 field_vals[i] = switch (field.ty.zigTypeTag()) {
1490 .Float => switch (field.ty.floatBits(target)) {
1491 16 => try bitCastBigIntToFloat(f16, .float_16, field_bigint, arena),
1492 32 => try bitCastBigIntToFloat(f32, .float_32, field_bigint, arena),
1493 64 => try bitCastBigIntToFloat(f64, .float_64, field_bigint, arena),
1494 80 => try bitCastBigIntToFloat(f80, .float_80, field_bigint, arena),
1495 128 => try bitCastBigIntToFloat(f128, .float_128, field_bigint, arena),
1496 else => unreachable,
1497 },
1498 .Bool => makeBool(!field_bigint.eqZero()),
1499 .Int => try Tag.int_big_positive.create(
1500 arena,
1501 try arena.dupe(std.math.big.Limb, field_bigint.limbs),
1502 ),
1503 .Struct => try intToPackedStruct(field.ty, target, field_bigint, arena),
1504 else => unreachable,
1505 };
1506 }
1507 return Tag.aggregate.create(arena, field_vals);
1508 }
1509
1510 fn bitCastBigIntToFloat(
1511 comptime F: type,
1512 comptime float_tag: Tag,
1513 bigint: BigIntConst,
1514 arena: Allocator,
1515 ) !Value {
1516 const Int = @Type(.{ .Int = .{
1517 .signedness = .unsigned,
1518 .bits = @typeInfo(F).Float.bits,
1519 } });
1520 const int = bigint.to(Int) catch |err| switch (err) {
1521 error.NegativeIntoUnsigned => unreachable,
1522 error.TargetTooSmall => unreachable,
1523 };
1524 const f = @bitCast(F, int);
1525 return float_tag.create(arena, f);
1526 }
1527
1528 fn floatWriteToMemory(comptime F: type, f: F, target: Target, buffer: []u8) void {
1529 const endian = target.cpu.arch.endian();1509 const endian = target.cpu.arch.endian();
1530 if (F == f80) {1510 switch (ty.zigTypeTag()) {
1531 const repr = std.math.break_f80(f);1511 .Void => return Value.@"void",
1532 std.mem.writeInt(u64, buffer[0..8], repr.fraction, endian);1512 .Bool => {
1533 std.mem.writeInt(u16, buffer[8..10], repr.exp, endian);1513 const byte = switch (endian) {
1534 std.mem.set(u8, buffer[10..], 0);1514 .Big => buffer[buffer.len - bit_offset / 8 - 1],
1535 return;1515 .Little => buffer[bit_offset / 8],
1536 }1516 };
1537 const Int = @Type(.{ .Int = .{1517 if (((byte >> @intCast(u3, bit_offset % 8)) & 1) == 0) {
1538 .signedness = .unsigned,1518 return Value.@"false";
1539 .bits = @typeInfo(F).Float.bits,1519 } else {
1540 } });1520 return Value.@"true";
1541 const int = @bitCast(Int, f);1521 }
1542 std.mem.writeInt(Int, buffer[0..@sizeOf(Int)], int, endian);1522 },
1543 }1523 .Int, .Enum => {
1524 if (buffer.len == 0) return Value.zero;
1525 const int_info = ty.intInfo(target);
1526 const abi_size = @intCast(usize, ty.abiSize(target));
15441527
1545 fn floatReadFromMemory(comptime F: type, target: Target, buffer: []const u8) F {1528 const bits = int_info.bits;
1546 const endian = target.cpu.arch.endian();1529 if (bits <= 64) switch (int_info.signedness) { // Fast path for integers <= u64
1547 if (F == f80) {1530 .signed => return Value.Tag.int_i64.create(arena, std.mem.readVarPackedInt(i64, buffer, bit_offset, bits, endian, .signed)),
1548 return std.math.make_f80(.{1531 .unsigned => return Value.Tag.int_u64.create(arena, std.mem.readVarPackedInt(u64, buffer, bit_offset, bits, endian, .unsigned)),
1549 .fraction = readInt(u64, buffer[0..8], endian),1532 } else { // Slow path, we have to construct a big-int
1550 .exp = readInt(u16, buffer[8..10], endian),1533 const Limb = std.math.big.Limb;
1551 });1534 const limb_count = (abi_size + @sizeOf(Limb) - 1) / @sizeOf(Limb);
1552 }1535 const limbs_buffer = try arena.alloc(Limb, limb_count);
1553 const Int = @Type(.{ .Int = .{1536
1554 .signedness = .unsigned,1537 var bigint = BigIntMutable.init(limbs_buffer, 0);
1555 .bits = @typeInfo(F).Float.bits,1538 bigint.readPackedTwosComplement(buffer, bit_offset, bits, endian, int_info.signedness);
1556 } });1539 return fromBigInt(arena, bigint.toConst());
1557 const int = readInt(Int, buffer[0..@sizeOf(Int)], endian);
1558 return @bitCast(F, int);
1559 }
1560
1561 fn readInt(comptime Int: type, buffer: *const [@sizeOf(Int)]u8, endian: std.builtin.Endian) Int {
1562 var result: Int = 0;
1563 switch (endian) {
1564 .Big => {
1565 for (buffer) |byte| {
1566 result <<= 8;
1567 result |= byte;
1568 }1540 }
1569 },1541 },
1570 .Little => {1542 .Float => switch (ty.floatBits(target)) {
1571 var i: usize = buffer.len;1543 16 => return Value.Tag.float_16.create(arena, @bitCast(f16, std.mem.readPackedInt(u16, buffer, bit_offset, endian))),
1572 while (i != 0) {1544 32 => return Value.Tag.float_32.create(arena, @bitCast(f32, std.mem.readPackedInt(u32, buffer, bit_offset, endian))),
1573 i -= 1;1545 64 => return Value.Tag.float_64.create(arena, @bitCast(f64, std.mem.readPackedInt(u64, buffer, bit_offset, endian))),
1574 result <<= 8;1546 80 => return Value.Tag.float_80.create(arena, @bitCast(f80, std.mem.readPackedInt(u80, buffer, bit_offset, endian))),
1575 result |= buffer[i];1547 128 => return Value.Tag.float_128.create(arena, @bitCast(f128, std.mem.readPackedInt(u128, buffer, bit_offset, endian))),
1548 else => unreachable,
1549 },
1550 .Vector => {
1551 const elem_ty = ty.childType();
1552 const elems = try arena.alloc(Value, @intCast(usize, ty.arrayLen()));
1553
1554 var bits: u16 = 0;
1555 const elem_bit_size = @intCast(u16, elem_ty.bitSize(target));
1556 for (elems) |_, i| {
1557 // On big-endian systems, LLVM reverses the element order of vectors by default
1558 const tgt_elem_i = if (endian == .Big) elems.len - i - 1 else i;
1559 elems[tgt_elem_i] = try readFromPackedMemory(elem_ty, mod, buffer, bit_offset + bits, arena);
1560 bits += elem_bit_size;
1576 }1561 }
1562 return Tag.aggregate.create(arena, elems);
1577 },1563 },
1564 .Struct => switch (ty.containerLayout()) {
1565 .Auto => unreachable, // Sema is supposed to have emitted a compile error already
1566 .Extern => unreachable, // Handled by non-packed readFromMemory
1567 .Packed => {
1568 var bits: u16 = 0;
1569 const fields = ty.structFields().values();
1570 const field_vals = try arena.alloc(Value, fields.len);
1571 for (fields) |field, i| {
1572 const field_bits = @intCast(u16, field.ty.bitSize(target));
1573 field_vals[i] = try readFromPackedMemory(field.ty, mod, buffer, bit_offset + bits, arena);
1574 bits += field_bits;
1575 }
1576 return Tag.aggregate.create(arena, field_vals);
1577 },
1578 },
1579 else => @panic("TODO implement readFromPackedMemory for more types"),
1578 }1580 }
1579 return result;
1580 }1581 }
15811582
1582 /// Asserts that the value is a float or an integer.1583 /// Asserts that the value is a float or an integer.
test/behavior/bitcast.zig+86-1
...@@ -63,6 +63,10 @@ fn testBitCast(comptime N: usize) !void {...@@ -63,6 +63,10 @@ fn testBitCast(comptime N: usize) !void {
63 try expect(conv_iN(N, 0) == 0);63 try expect(conv_iN(N, 0) == 0);
6464
65 try expect(conv_iN(N, -0) == 0);65 try expect(conv_iN(N, -0) == 0);
66
67 if (N > 24) {
68 try expect(conv_uN(N, 0xf23456) == 0xf23456);
69 }
66}70}
6771
68fn conv_iN(comptime N: usize, x: std.meta.Int(.signed, N)) std.meta.Int(.unsigned, N) {72fn conv_iN(comptime N: usize, x: std.meta.Int(.signed, N)) std.meta.Int(.unsigned, N) {
...@@ -73,6 +77,55 @@ fn conv_uN(comptime N: usize, x: std.meta.Int(.unsigned, N)) std.meta.Int(.signe...@@ -73,6 +77,55 @@ fn conv_uN(comptime N: usize, x: std.meta.Int(.unsigned, N)) std.meta.Int(.signe
73 return @bitCast(std.meta.Int(.signed, N), x);77 return @bitCast(std.meta.Int(.signed, N), x);
74}78}
7579
80test "bitcast uX to bytes" {
81 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
82 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
83 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
84 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
85 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
86
87 const bit_values = [_]usize{ 1, 48, 27, 512, 493, 293, 125, 204, 112 };
88 inline for (bit_values) |bits| {
89 try testBitCast(bits);
90 comptime try testBitCast(bits);
91 }
92}
93
94fn testBitCastuXToBytes(comptime N: usize) !void {
95
96 // The location of padding bits in these layouts are technically not defined
97 // by LLVM, but we currently allow exotic integers to be cast (at comptime)
98 // to types that expose their padding bits anyway.
99 //
100 // This test at least makes sure those bits are matched by the runtime behavior
101 // on the platforms we target. If the above behavior is restricted after all,
102 // this test should be deleted.
103
104 const T = std.meta.Int(.unsigned, N);
105 for ([_]T{ 0, ~@as(T, 0) }) |init_value| {
106 var x: T = init_value;
107 const bytes = std.mem.asBytes(&x);
108
109 const byte_count = (N + 7) / 8;
110 switch (builtin.cpu.arch.endian()) {
111 .Little => {
112 var byte_i = 0;
113 while (byte_i < (byte_count - 1)) : (byte_i += 1) {
114 try expect(bytes[byte_i] == 0xff);
115 }
116 try expect(((bytes[byte_i] ^ 0xff) << -%@truncate(u3, N)) == 0);
117 },
118 .Big => {
119 var byte_i = byte_count - 1;
120 while (byte_i > 0) : (byte_i -= 1) {
121 try expect(bytes[byte_i] == 0xff);
122 }
123 try expect(((bytes[byte_i] ^ 0xff) << -%@truncate(u3, N)) == 0);
124 },
125 }
126 }
127}
128
76test "nested bitcast" {129test "nested bitcast" {
77 const S = struct {130 const S = struct {
78 fn moo(x: isize) !void {131 fn moo(x: isize) !void {
...@@ -283,7 +336,8 @@ test "@bitCast packed struct of floats" {...@@ -283,7 +336,8 @@ test "@bitCast packed struct of floats" {
283 comptime try S.doTheTest();336 comptime try S.doTheTest();
284}337}
285338
286test "comptime @bitCast packed struct to int" {339test "comptime @bitCast packed struct to int and back" {
340 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
287 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;341 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
288 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;342 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
289 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;343 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
...@@ -304,6 +358,37 @@ test "comptime @bitCast packed struct to int" {...@@ -304,6 +358,37 @@ test "comptime @bitCast packed struct to int" {
304 vectorf: @Vector(2, f16) = .{ 3.14, 2.71 },358 vectorf: @Vector(2, f16) = .{ 3.14, 2.71 },
305 };359 };
306 const Int = @typeInfo(S).Struct.backing_integer.?;360 const Int = @typeInfo(S).Struct.backing_integer.?;
361
362 // S -> Int
307 var s: S = .{};363 var s: S = .{};
308 try expectEqual(@bitCast(Int, s), comptime @bitCast(Int, S{}));364 try expectEqual(@bitCast(Int, s), comptime @bitCast(Int, S{}));
365
366 // Int -> S
367 var i: Int = 0;
368 const rt_cast = @bitCast(S, i);
369 const ct_cast = comptime @bitCast(S, @as(Int, 0));
370 inline for (@typeInfo(S).Struct.fields) |field| {
371 if (@typeInfo(field.field_type) == .Vector)
372 continue; //TODO: https://github.com/ziglang/zig/issues/13201
373
374 try expectEqual(@field(rt_cast, field.name), @field(ct_cast, field.name));
375 }
376}
377
378test "comptime bitcast with fields following a float" {
379 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO: https://github.com/ziglang/zig/issues/13214
380
381 const FloatT = extern struct { f: f80, x: u128 };
382 var x: FloatT = .{ .f = 0.5, .x = 123 };
383 try expect(@bitCast(u256, x) == comptime @bitCast(u256, @as(FloatT, .{ .f = 0.5, .x = 123 })));
384}
385
386test "bitcast vector to integer and back" {
387 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO: https://github.com/ziglang/zig/issues/13220
388 if (builtin.zig_backend == .stage1) return error.SkipZigTest; // stage1 gets the comptime cast wrong
389
390 const arr: [16]bool = [_]bool{ true, false } ++ [_]bool{true} ** 14;
391 var x = @splat(16, true);
392 x[1] = false;
393 try expect(@bitCast(u16, x) == comptime @bitCast(u16, @as(@Vector(16, bool), arr)));
309}394}