authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-04 23:30:04-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-04 23:30:04-07:00
log78902db68bbd400f6d84b65280c31d417105f2a8
tree593851ca76e95033abe7afee171cccc659b76f53
parent598db831f3ea1267d469162db1a54c2d62ff3e87

stage2: fix comptime `@bitCast`

Before, Sema for comptime `@bitCast` would return the same Value but change the Type. This gave invalid results because, for example, an integer Value when the Type is a float would be interpreted numerically, but `@bitCast` needs it to reinterpret how they would be stored in memory. This requires a mechanism to serialize a Value to a byte buffer and deserialize a Value from a byte buffer. Not done yet, but needs to happen: comptime dereferencing a pointer to a Decl needs to perform a comptime bitcast on the loaded value. Currently the value is silently wrong in the same way that `@bitCast` was silently wrong before this commit. The logic in Value for handling readFromMemory for large integers is only correct for small integers. It needs to be fleshed out for proper big integers. As part of this change: * std.math.big.Int: initial implementations of readTwosComplement and writeTwosComplement. They only support bit_count <= 128 so far and panic otherwise. * compiler-rt: move the compareXf2 exports over to the stage2 section. Even with the improvements in this commit, I'm still seeing test failures in the widening behavior tests; more investigation is needed.

9 files changed, 450 insertions(+), 267 deletions(-)

lib/std/math/big/int.zig+79-19
...@@ -10,6 +10,8 @@ const mem = std.mem;...@@ -10,6 +10,8 @@ const mem = std.mem;
10const maxInt = std.math.maxInt;10const maxInt = std.math.maxInt;
11const minInt = std.math.minInt;11const minInt = std.math.minInt;
12const assert = std.debug.assert;12const assert = std.debug.assert;
13const Endian = std.builtin.Endian;
14const Signedness = std.builtin.Signedness;
1315
14const debug_safety = false;16const debug_safety = false;
1517
...@@ -328,7 +330,7 @@ pub const Mutable = struct {...@@ -328,7 +330,7 @@ pub const Mutable = struct {
328 pub fn setTwosCompIntLimit(330 pub fn setTwosCompIntLimit(
329 r: *Mutable,331 r: *Mutable,
330 limit: TwosCompIntLimit,332 limit: TwosCompIntLimit,
331 signedness: std.builtin.Signedness,333 signedness: Signedness,
332 bit_count: usize,334 bit_count: usize,
333 ) void {335 ) void {
334 // Handle zero-bit types.336 // Handle zero-bit types.
...@@ -457,7 +459,7 @@ pub const Mutable = struct {...@@ -457,7 +459,7 @@ pub const Mutable = struct {
457 ///459 ///
458 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by460 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
459 /// r is `calcTwosCompLimbCount(bit_count)`.461 /// r is `calcTwosCompLimbCount(bit_count)`.
460 pub fn addWrap(r: *Mutable, a: Const, b: Const, signedness: std.builtin.Signedness, bit_count: usize) void {462 pub fn addWrap(r: *Mutable, a: Const, b: Const, signedness: Signedness, bit_count: usize) void {
461 const req_limbs = calcTwosCompLimbCount(bit_count);463 const req_limbs = calcTwosCompLimbCount(bit_count);
462464
463 // Slice of the upper bits if they exist, these will be ignored and allows us to use addCarry to determine465 // Slice of the upper bits if they exist, these will be ignored and allows us to use addCarry to determine
...@@ -493,7 +495,7 @@ pub const Mutable = struct {...@@ -493,7 +495,7 @@ pub const Mutable = struct {
493 ///495 ///
494 /// Assets the result fits in `r`. Upper bound on the number of limbs needed by496 /// Assets the result fits in `r`. Upper bound on the number of limbs needed by
495 /// r is `calcTwosCompLimbCount(bit_count)`.497 /// r is `calcTwosCompLimbCount(bit_count)`.
496 pub fn addSat(r: *Mutable, a: Const, b: Const, signedness: std.builtin.Signedness, bit_count: usize) void {498 pub fn addSat(r: *Mutable, a: Const, b: Const, signedness: Signedness, bit_count: usize) void {
497 const req_limbs = calcTwosCompLimbCount(bit_count);499 const req_limbs = calcTwosCompLimbCount(bit_count);
498500
499 // Slice of the upper bits if they exist, these will be ignored and allows us to use addCarry to determine501 // Slice of the upper bits if they exist, these will be ignored and allows us to use addCarry to determine
...@@ -595,7 +597,7 @@ pub const Mutable = struct {...@@ -595,7 +597,7 @@ pub const Mutable = struct {
595 /// r, a and b may be aliases597 /// r, a and b may be aliases
596 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by598 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
597 /// r is `calcTwosCompLimbCount(bit_count)`.599 /// r is `calcTwosCompLimbCount(bit_count)`.
598 pub fn subWrap(r: *Mutable, a: Const, b: Const, signedness: std.builtin.Signedness, bit_count: usize) void {600 pub fn subWrap(r: *Mutable, a: Const, b: Const, signedness: Signedness, bit_count: usize) void {
599 r.addWrap(a, b.negate(), signedness, bit_count);601 r.addWrap(a, b.negate(), signedness, bit_count);
600 }602 }
601603
...@@ -604,7 +606,7 @@ pub const Mutable = struct {...@@ -604,7 +606,7 @@ pub const Mutable = struct {
604 ///606 ///
605 /// Assets the result fits in `r`. Upper bound on the number of limbs needed by607 /// Assets the result fits in `r`. Upper bound on the number of limbs needed by
606 /// r is `calcTwosCompLimbCount(bit_count)`.608 /// r is `calcTwosCompLimbCount(bit_count)`.
607 pub fn subSat(r: *Mutable, a: Const, b: Const, signedness: std.builtin.Signedness, bit_count: usize) void {609 pub fn subSat(r: *Mutable, a: Const, b: Const, signedness: Signedness, bit_count: usize) void {
608 r.addSat(a, b.negate(), signedness, bit_count);610 r.addSat(a, b.negate(), signedness, bit_count);
609 }611 }
610612
...@@ -680,7 +682,7 @@ pub const Mutable = struct {...@@ -680,7 +682,7 @@ pub const Mutable = struct {
680 rma: *Mutable,682 rma: *Mutable,
681 a: Const,683 a: Const,
682 b: Const,684 b: Const,
683 signedness: std.builtin.Signedness,685 signedness: Signedness,
684 bit_count: usize,686 bit_count: usize,
685 limbs_buffer: []Limb,687 limbs_buffer: []Limb,
686 allocator: ?*Allocator,688 allocator: ?*Allocator,
...@@ -721,7 +723,7 @@ pub const Mutable = struct {...@@ -721,7 +723,7 @@ pub const Mutable = struct {
721 rma: *Mutable,723 rma: *Mutable,
722 a: Const,724 a: Const,
723 b: Const,725 b: Const,
724 signedness: std.builtin.Signedness,726 signedness: Signedness,
725 bit_count: usize,727 bit_count: usize,
726 allocator: ?*Allocator,728 allocator: ?*Allocator,
727 ) void {729 ) void {
...@@ -1284,7 +1286,7 @@ pub const Mutable = struct {...@@ -1284,7 +1286,7 @@ pub const Mutable = struct {
1284 ///1286 ///
1285 /// Asserts `r` has enough storage to store the result.1287 /// Asserts `r` has enough storage to store the result.
1286 /// The upper bound is `calcTwosCompLimbCount(a.len)`.1288 /// The upper bound is `calcTwosCompLimbCount(a.len)`.
1287 pub fn truncate(r: *Mutable, a: Const, signedness: std.builtin.Signedness, bit_count: usize) void {1289 pub fn truncate(r: *Mutable, a: Const, signedness: Signedness, bit_count: usize) void {
1288 const req_limbs = calcTwosCompLimbCount(bit_count);1290 const req_limbs = calcTwosCompLimbCount(bit_count);
12891291
1290 // Handle 0-bit integers.1292 // Handle 0-bit integers.
...@@ -1369,12 +1371,47 @@ pub const Mutable = struct {...@@ -1369,12 +1371,47 @@ pub const Mutable = struct {
1369 ///1371 ///
1370 /// Asserts `r` has enough storage to store the result.1372 /// Asserts `r` has enough storage to store the result.
1371 /// The upper bound is `calcTwosCompLimbCount(a.len)`.1373 /// The upper bound is `calcTwosCompLimbCount(a.len)`.
1372 pub fn saturate(r: *Mutable, a: Const, signedness: std.builtin.Signedness, bit_count: usize) void {1374 pub fn saturate(r: *Mutable, a: Const, signedness: Signedness, bit_count: usize) void {
1373 if (!a.fitsInTwosComp(signedness, bit_count)) {1375 if (!a.fitsInTwosComp(signedness, bit_count)) {
1374 r.setTwosCompIntLimit(if (r.positive) .max else .min, signedness, bit_count);1376 r.setTwosCompIntLimit(if (r.positive) .max else .min, signedness, bit_count);
1375 }1377 }
1376 }1378 }
13771379
1380 pub fn readTwosComplement(
1381 x: *Mutable,
1382 buffer: []const u8,
1383 bit_count: usize,
1384 endian: Endian,
1385 signedness: Signedness,
1386 ) void {
1387 if (bit_count == 0) {
1388 x.limbs[0] = 0;
1389 x.len = 1;
1390 x.positive = true;
1391 return;
1392 }
1393 // zig fmt: off
1394 switch (signedness) {
1395 .signed => {
1396 if (bit_count <= 8) return x.set(mem.readInt( i8, buffer[0.. 1], endian));
1397 if (bit_count <= 16) return x.set(mem.readInt( i16, buffer[0.. 2], endian));
1398 if (bit_count <= 32) return x.set(mem.readInt( i32, buffer[0.. 4], endian));
1399 if (bit_count <= 64) return x.set(mem.readInt( i64, buffer[0.. 8], endian));
1400 if (bit_count <= 128) return x.set(mem.readInt(i128, buffer[0..16], endian));
1401 },
1402 .unsigned => {
1403 if (bit_count <= 8) return x.set(mem.readInt( u8, buffer[0.. 1], endian));
1404 if (bit_count <= 16) return x.set(mem.readInt( u16, buffer[0.. 2], endian));
1405 if (bit_count <= 32) return x.set(mem.readInt( u32, buffer[0.. 4], endian));
1406 if (bit_count <= 64) return x.set(mem.readInt( u64, buffer[0.. 8], endian));
1407 if (bit_count <= 128) return x.set(mem.readInt(u128, buffer[0..16], endian));
1408 },
1409 }
1410 // zig fmt: on
1411
1412 @panic("TODO implement std lib big int readTwosComplement");
1413 }
1414
1378 /// Normalize a possible sequence of leading zeros.1415 /// Normalize a possible sequence of leading zeros.
1379 ///1416 ///
1380 /// [1, 2, 3, 4, 0] -> [1, 2, 3, 4]1417 /// [1, 2, 3, 4, 0] -> [1, 2, 3, 4]
...@@ -1485,7 +1522,7 @@ pub const Const = struct {...@@ -1485,7 +1522,7 @@ pub const Const = struct {
1485 return bits;1522 return bits;
1486 }1523 }
14871524
1488 pub fn fitsInTwosComp(self: Const, signedness: std.builtin.Signedness, bit_count: usize) bool {1525 pub fn fitsInTwosComp(self: Const, signedness: Signedness, bit_count: usize) bool {
1489 if (self.eqZero()) {1526 if (self.eqZero()) {
1490 return true;1527 return true;
1491 }1528 }
...@@ -1731,6 +1768,29 @@ pub const Const = struct {...@@ -1731,6 +1768,29 @@ pub const Const = struct {
1731 return s.len;1768 return s.len;
1732 }1769 }
17331770
1771 /// Asserts that `buffer` and `bit_count` are large enough to store the value.
1772 pub fn writeTwosComplement(x: Const, buffer: []u8, bit_count: usize, endian: Endian) void {
1773 if (bit_count == 0) return;
1774
1775 // zig fmt: off
1776 if (x.positive) {
1777 if (bit_count <= 8) return mem.writeInt( u8, buffer[0.. 1], x.to( u8) catch unreachable, endian);
1778 if (bit_count <= 16) return mem.writeInt( u16, buffer[0.. 2], x.to( u16) catch unreachable, endian);
1779 if (bit_count <= 32) return mem.writeInt( u32, buffer[0.. 4], x.to( u32) catch unreachable, endian);
1780 if (bit_count <= 64) return mem.writeInt( u64, buffer[0.. 8], x.to( u64) catch unreachable, endian);
1781 if (bit_count <= 128) return mem.writeInt(u128, buffer[0..16], x.to(u128) catch unreachable, endian);
1782 } else {
1783 if (bit_count <= 8) return mem.writeInt( i8, buffer[0.. 1], x.to( i8) catch unreachable, endian);
1784 if (bit_count <= 16) return mem.writeInt( i16, buffer[0.. 2], x.to( i16) catch unreachable, endian);
1785 if (bit_count <= 32) return mem.writeInt( i32, buffer[0.. 4], x.to( i32) catch unreachable, endian);
1786 if (bit_count <= 64) return mem.writeInt( i64, buffer[0.. 8], x.to( i64) catch unreachable, endian);
1787 if (bit_count <= 128) return mem.writeInt(i128, buffer[0..16], x.to(i128) catch unreachable, endian);
1788 }
1789 // zig fmt: on
1790
1791 @panic("TODO implement std lib big int writeTwosComplement for larger than 128 bits");
1792 }
1793
1734 /// Returns `math.Order.lt`, `math.Order.eq`, `math.Order.gt` if1794 /// Returns `math.Order.lt`, `math.Order.eq`, `math.Order.gt` if
1735 /// `|a| < |b|`, `|a| == |b|`, or `|a| > |b|` respectively.1795 /// `|a| < |b|`, `|a| == |b|`, or `|a| > |b|` respectively.
1736 pub fn orderAbs(a: Const, b: Const) math.Order {1796 pub fn orderAbs(a: Const, b: Const) math.Order {
...@@ -1992,7 +2052,7 @@ pub const Managed = struct {...@@ -1992,7 +2052,7 @@ pub const Managed = struct {
1992 return self.toConst().bitCountTwosComp();2052 return self.toConst().bitCountTwosComp();
1993 }2053 }
19942054
1995 pub fn fitsInTwosComp(self: Managed, signedness: std.builtin.Signedness, bit_count: usize) bool {2055 pub fn fitsInTwosComp(self: Managed, signedness: Signedness, bit_count: usize) bool {
1996 return self.toConst().fitsInTwosComp(signedness, bit_count);2056 return self.toConst().fitsInTwosComp(signedness, bit_count);
1997 }2057 }
19982058
...@@ -2051,7 +2111,7 @@ pub const Managed = struct {...@@ -2051,7 +2111,7 @@ pub const Managed = struct {
2051 pub fn setTwosCompIntLimit(2111 pub fn setTwosCompIntLimit(
2052 r: *Managed,2112 r: *Managed,
2053 limit: TwosCompIntLimit,2113 limit: TwosCompIntLimit,
2054 signedness: std.builtin.Signedness,2114 signedness: Signedness,
2055 bit_count: usize,2115 bit_count: usize,
2056 ) !void {2116 ) !void {
2057 try r.ensureCapacity(calcTwosCompLimbCount(bit_count));2117 try r.ensureCapacity(calcTwosCompLimbCount(bit_count));
...@@ -2164,7 +2224,7 @@ pub const Managed = struct {...@@ -2164,7 +2224,7 @@ pub const Managed = struct {
2164 /// `r.ensureTwosCompCapacity` prior to calling `add`.2224 /// `r.ensureTwosCompCapacity` prior to calling `add`.
2165 ///2225 ///
2166 /// Returns an error if memory could not be allocated.2226 /// Returns an error if memory could not be allocated.
2167 pub fn addWrap(r: *Managed, a: Const, b: Const, signedness: std.builtin.Signedness, bit_count: usize) Allocator.Error!void {2227 pub fn addWrap(r: *Managed, a: Const, b: Const, signedness: Signedness, bit_count: usize) Allocator.Error!void {
2168 try r.ensureTwosCompCapacity(bit_count);2228 try r.ensureTwosCompCapacity(bit_count);
2169 var m = r.toMutable();2229 var m = r.toMutable();
2170 m.addWrap(a, b, signedness, bit_count);2230 m.addWrap(a, b, signedness, bit_count);
...@@ -2177,7 +2237,7 @@ pub const Managed = struct {...@@ -2177,7 +2237,7 @@ pub const Managed = struct {
2177 /// `r.ensureTwosCompCapacity` prior to calling `add`.2237 /// `r.ensureTwosCompCapacity` prior to calling `add`.
2178 ///2238 ///
2179 /// Returns an error if memory could not be allocated.2239 /// Returns an error if memory could not be allocated.
2180 pub fn addSat(r: *Managed, a: Const, b: Const, signedness: std.builtin.Signedness, bit_count: usize) Allocator.Error!void {2240 pub fn addSat(r: *Managed, a: Const, b: Const, signedness: Signedness, bit_count: usize) Allocator.Error!void {
2181 try r.ensureTwosCompCapacity(bit_count);2241 try r.ensureTwosCompCapacity(bit_count);
2182 var m = r.toMutable();2242 var m = r.toMutable();
2183 m.addSat(a, b, signedness, bit_count);2243 m.addSat(a, b, signedness, bit_count);
...@@ -2202,7 +2262,7 @@ pub const Managed = struct {...@@ -2202,7 +2262,7 @@ pub const Managed = struct {
2202 /// `r.ensureTwosCompCapacity` prior to calling `add`.2262 /// `r.ensureTwosCompCapacity` prior to calling `add`.
2203 ///2263 ///
2204 /// Returns an error if memory could not be allocated.2264 /// Returns an error if memory could not be allocated.
2205 pub fn subWrap(r: *Managed, a: Const, b: Const, signedness: std.builtin.Signedness, bit_count: usize) Allocator.Error!void {2265 pub fn subWrap(r: *Managed, a: Const, b: Const, signedness: Signedness, bit_count: usize) Allocator.Error!void {
2206 try r.ensureTwosCompCapacity(bit_count);2266 try r.ensureTwosCompCapacity(bit_count);
2207 var m = r.toMutable();2267 var m = r.toMutable();
2208 m.subWrap(a, b, signedness, bit_count);2268 m.subWrap(a, b, signedness, bit_count);
...@@ -2215,7 +2275,7 @@ pub const Managed = struct {...@@ -2215,7 +2275,7 @@ pub const Managed = struct {
2215 /// `r.ensureTwosCompCapacity` prior to calling `add`.2275 /// `r.ensureTwosCompCapacity` prior to calling `add`.
2216 ///2276 ///
2217 /// Returns an error if memory could not be allocated.2277 /// Returns an error if memory could not be allocated.
2218 pub fn subSat(r: *Managed, a: Const, b: Const, signedness: std.builtin.Signedness, bit_count: usize) Allocator.Error!void {2278 pub fn subSat(r: *Managed, a: Const, b: Const, signedness: Signedness, bit_count: usize) Allocator.Error!void {
2219 try r.ensureTwosCompCapacity(bit_count);2279 try r.ensureTwosCompCapacity(bit_count);
2220 var m = r.toMutable();2280 var m = r.toMutable();
2221 m.subSat(a, b, signedness, bit_count);2281 m.subSat(a, b, signedness, bit_count);
...@@ -2259,7 +2319,7 @@ pub const Managed = struct {...@@ -2259,7 +2319,7 @@ pub const Managed = struct {
2259 /// Returns an error if memory could not be allocated.2319 /// Returns an error if memory could not be allocated.
2260 ///2320 ///
2261 /// rma's allocator is used for temporary storage to speed up the multiplication.2321 /// rma's allocator is used for temporary storage to speed up the multiplication.
2262 pub fn mulWrap(rma: *Managed, a: Const, b: Const, signedness: std.builtin.Signedness, bit_count: usize) !void {2322 pub fn mulWrap(rma: *Managed, a: Const, b: Const, signedness: Signedness, bit_count: usize) !void {
2263 var alias_count: usize = 0;2323 var alias_count: usize = 0;
2264 if (rma.limbs.ptr == a.limbs.ptr)2324 if (rma.limbs.ptr == a.limbs.ptr)
2265 alias_count += 1;2325 alias_count += 1;
...@@ -2445,7 +2505,7 @@ pub const Managed = struct {...@@ -2445,7 +2505,7 @@ pub const Managed = struct {
2445 }2505 }
24462506
2447 /// r = truncate(Int(signedness, bit_count), a)2507 /// r = truncate(Int(signedness, bit_count), a)
2448 pub fn truncate(r: *Managed, a: Const, signedness: std.builtin.Signedness, bit_count: usize) !void {2508 pub fn truncate(r: *Managed, a: Const, signedness: Signedness, bit_count: usize) !void {
2449 try r.ensureCapacity(calcTwosCompLimbCount(bit_count));2509 try r.ensureCapacity(calcTwosCompLimbCount(bit_count));
2450 var m = r.toMutable();2510 var m = r.toMutable();
2451 m.truncate(a, signedness, bit_count);2511 m.truncate(a, signedness, bit_count);
...@@ -2453,7 +2513,7 @@ pub const Managed = struct {...@@ -2453,7 +2513,7 @@ pub const Managed = struct {
2453 }2513 }
24542514
2455 /// r = saturate(Int(signedness, bit_count), a)2515 /// r = saturate(Int(signedness, bit_count), a)
2456 pub fn saturate(r: *Managed, a: Const, signedness: std.builtin.Signedness, bit_count: usize) !void {2516 pub fn saturate(r: *Managed, a: Const, signedness: Signedness, bit_count: usize) !void {
2457 try r.ensureCapacity(calcTwosCompLimbCount(bit_count));2517 try r.ensureCapacity(calcTwosCompLimbCount(bit_count));
2458 var m = r.toMutable();2518 var m = r.toMutable();
2459 m.saturate(a, signedness, bit_count);2519 m.saturate(a, signedness, bit_count);
lib/std/special/compiler_rt.zig+49-53
...@@ -28,6 +28,52 @@ comptime {...@@ -28,6 +28,52 @@ comptime {
28 const __extendhftf2 = @import("compiler_rt/extendXfYf2.zig").__extendhftf2;28 const __extendhftf2 = @import("compiler_rt/extendXfYf2.zig").__extendhftf2;
29 @export(__extendhftf2, .{ .name = "__extendhftf2", .linkage = linkage });29 @export(__extendhftf2, .{ .name = "__extendhftf2", .linkage = linkage });
3030
31 const __lesf2 = @import("compiler_rt/compareXf2.zig").__lesf2;
32 @export(__lesf2, .{ .name = "__lesf2", .linkage = linkage });
33 const __ledf2 = @import("compiler_rt/compareXf2.zig").__ledf2;
34 @export(__ledf2, .{ .name = "__ledf2", .linkage = linkage });
35 const __letf2 = @import("compiler_rt/compareXf2.zig").__letf2;
36 @export(__letf2, .{ .name = "__letf2", .linkage = linkage });
37
38 const __gesf2 = @import("compiler_rt/compareXf2.zig").__gesf2;
39 @export(__gesf2, .{ .name = "__gesf2", .linkage = linkage });
40 const __gedf2 = @import("compiler_rt/compareXf2.zig").__gedf2;
41 @export(__gedf2, .{ .name = "__gedf2", .linkage = linkage });
42 const __getf2 = @import("compiler_rt/compareXf2.zig").__getf2;
43 @export(__getf2, .{ .name = "__getf2", .linkage = linkage });
44
45 if (!is_test) {
46 @export(__lesf2, .{ .name = "__cmpsf2", .linkage = linkage });
47 @export(__ledf2, .{ .name = "__cmpdf2", .linkage = linkage });
48 @export(__letf2, .{ .name = "__cmptf2", .linkage = linkage });
49
50 const __eqsf2 = @import("compiler_rt/compareXf2.zig").__eqsf2;
51 @export(__eqsf2, .{ .name = "__eqsf2", .linkage = linkage });
52 const __eqdf2 = @import("compiler_rt/compareXf2.zig").__eqdf2;
53 @export(__eqdf2, .{ .name = "__eqdf2", .linkage = linkage });
54 @export(__letf2, .{ .name = "__eqtf2", .linkage = linkage });
55
56 const __ltsf2 = @import("compiler_rt/compareXf2.zig").__ltsf2;
57 @export(__ltsf2, .{ .name = "__ltsf2", .linkage = linkage });
58 const __ltdf2 = @import("compiler_rt/compareXf2.zig").__ltdf2;
59 @export(__ltdf2, .{ .name = "__ltdf2", .linkage = linkage });
60 @export(__letf2, .{ .name = "__lttf2", .linkage = linkage });
61
62 const __nesf2 = @import("compiler_rt/compareXf2.zig").__nesf2;
63 @export(__nesf2, .{ .name = "__nesf2", .linkage = linkage });
64 const __nedf2 = @import("compiler_rt/compareXf2.zig").__nedf2;
65 @export(__nedf2, .{ .name = "__nedf2", .linkage = linkage });
66 @export(__letf2, .{ .name = "__netf2", .linkage = linkage });
67
68 const __gtsf2 = @import("compiler_rt/compareXf2.zig").__gtsf2;
69 @export(__gtsf2, .{ .name = "__gtsf2", .linkage = linkage });
70 const __gtdf2 = @import("compiler_rt/compareXf2.zig").__gtdf2;
71 @export(__gtdf2, .{ .name = "__gtdf2", .linkage = linkage });
72 @export(__getf2, .{ .name = "__gttf2", .linkage = linkage });
73
74 @export(__extendhfsf2, .{ .name = "__gnu_h2f_ieee", .linkage = linkage });
75 }
76
31 if (!builtin.zig_is_stage2) {77 if (!builtin.zig_is_stage2) {
32 switch (arch) {78 switch (arch) {
33 .i386,79 .i386,
...@@ -46,59 +92,6 @@ comptime {...@@ -46,59 +92,6 @@ comptime {
46 // __clear_cache manages its own logic about whether to be exported or not.92 // __clear_cache manages its own logic about whether to be exported or not.
47 _ = @import("compiler_rt/clear_cache.zig").clear_cache;93 _ = @import("compiler_rt/clear_cache.zig").clear_cache;
4894
49 const __lesf2 = @import("compiler_rt/compareXf2.zig").__lesf2;
50 @export(__lesf2, .{ .name = "__lesf2", .linkage = linkage });
51 const __ledf2 = @import("compiler_rt/compareXf2.zig").__ledf2;
52 @export(__ledf2, .{ .name = "__ledf2", .linkage = linkage });
53 const __letf2 = @import("compiler_rt/compareXf2.zig").__letf2;
54 @export(__letf2, .{ .name = "__letf2", .linkage = linkage });
55
56 const __gesf2 = @import("compiler_rt/compareXf2.zig").__gesf2;
57 @export(__gesf2, .{ .name = "__gesf2", .linkage = linkage });
58 const __gedf2 = @import("compiler_rt/compareXf2.zig").__gedf2;
59 @export(__gedf2, .{ .name = "__gedf2", .linkage = linkage });
60 const __getf2 = @import("compiler_rt/compareXf2.zig").__getf2;
61 @export(__getf2, .{ .name = "__getf2", .linkage = linkage });
62
63 if (!is_test) {
64 @export(__lesf2, .{ .name = "__cmpsf2", .linkage = linkage });
65 @export(__ledf2, .{ .name = "__cmpdf2", .linkage = linkage });
66 @export(__letf2, .{ .name = "__cmptf2", .linkage = linkage });
67
68 const __eqsf2 = @import("compiler_rt/compareXf2.zig").__eqsf2;
69 @export(__eqsf2, .{ .name = "__eqsf2", .linkage = linkage });
70 const __eqdf2 = @import("compiler_rt/compareXf2.zig").__eqdf2;
71 @export(__eqdf2, .{ .name = "__eqdf2", .linkage = linkage });
72 @export(__letf2, .{ .name = "__eqtf2", .linkage = linkage });
73
74 const __ltsf2 = @import("compiler_rt/compareXf2.zig").__ltsf2;
75 @export(__ltsf2, .{ .name = "__ltsf2", .linkage = linkage });
76 const __ltdf2 = @import("compiler_rt/compareXf2.zig").__ltdf2;
77 @export(__ltdf2, .{ .name = "__ltdf2", .linkage = linkage });
78 @export(__letf2, .{ .name = "__lttf2", .linkage = linkage });
79
80 const __nesf2 = @import("compiler_rt/compareXf2.zig").__nesf2;
81 @export(__nesf2, .{ .name = "__nesf2", .linkage = linkage });
82 const __nedf2 = @import("compiler_rt/compareXf2.zig").__nedf2;
83 @export(__nedf2, .{ .name = "__nedf2", .linkage = linkage });
84 @export(__letf2, .{ .name = "__netf2", .linkage = linkage });
85
86 const __gtsf2 = @import("compiler_rt/compareXf2.zig").__gtsf2;
87 @export(__gtsf2, .{ .name = "__gtsf2", .linkage = linkage });
88 const __gtdf2 = @import("compiler_rt/compareXf2.zig").__gtdf2;
89 @export(__gtdf2, .{ .name = "__gtdf2", .linkage = linkage });
90 @export(__getf2, .{ .name = "__gttf2", .linkage = linkage });
91
92 @export(@import("compiler_rt/extendXfYf2.zig").__extendhfsf2, .{
93 .name = "__gnu_h2f_ieee",
94 .linkage = linkage,
95 });
96 @export(@import("compiler_rt/truncXfYf2.zig").__truncsfhf2, .{
97 .name = "__gnu_f2h_ieee",
98 .linkage = linkage,
99 });
100 }
101
102 const __unordsf2 = @import("compiler_rt/compareXf2.zig").__unordsf2;95 const __unordsf2 = @import("compiler_rt/compareXf2.zig").__unordsf2;
103 @export(__unordsf2, .{ .name = "__unordsf2", .linkage = linkage });96 @export(__unordsf2, .{ .name = "__unordsf2", .linkage = linkage });
104 const __unorddf2 = @import("compiler_rt/compareXf2.zig").__unorddf2;97 const __unorddf2 = @import("compiler_rt/compareXf2.zig").__unorddf2;
...@@ -189,6 +182,9 @@ comptime {...@@ -189,6 +182,9 @@ comptime {
189182
190 const __truncsfhf2 = @import("compiler_rt/truncXfYf2.zig").__truncsfhf2;183 const __truncsfhf2 = @import("compiler_rt/truncXfYf2.zig").__truncsfhf2;
191 @export(__truncsfhf2, .{ .name = "__truncsfhf2", .linkage = linkage });184 @export(__truncsfhf2, .{ .name = "__truncsfhf2", .linkage = linkage });
185 if (!is_test) {
186 @export(__truncsfhf2, .{ .name = "__gnu_f2h_ieee", .linkage = linkage });
187 }
192 const __truncdfhf2 = @import("compiler_rt/truncXfYf2.zig").__truncdfhf2;188 const __truncdfhf2 = @import("compiler_rt/truncXfYf2.zig").__truncdfhf2;
193 @export(__truncdfhf2, .{ .name = "__truncdfhf2", .linkage = linkage });189 @export(__truncdfhf2, .{ .name = "__truncdfhf2", .linkage = linkage });
194 const __trunctfhf2 = @import("compiler_rt/truncXfYf2.zig").__trunctfhf2;190 const __trunctfhf2 = @import("compiler_rt/truncXfYf2.zig").__trunctfhf2;
lib/std/special/compiler_rt/compareXf2.zig+25-20
...@@ -21,7 +21,7 @@ const GE = enum(i32) {...@@ -21,7 +21,7 @@ const GE = enum(i32) {
21 const Unordered: GE = .Less;21 const Unordered: GE = .Less;
22};22};
2323
24pub fn cmp(comptime T: type, comptime RT: type, a: T, b: T) RT {24pub inline fn cmp(comptime T: type, comptime RT: type, a: T, b: T) RT {
25 @setRuntimeSafety(builtin.is_test);25 @setRuntimeSafety(builtin.is_test);
2626
27 const bits = @typeInfo(T).Float.bits;27 const bits = @typeInfo(T).Float.bits;
...@@ -32,7 +32,8 @@ pub fn cmp(comptime T: type, comptime RT: type, a: T, b: T) RT {...@@ -32,7 +32,8 @@ pub fn cmp(comptime T: type, comptime RT: type, a: T, b: T) RT {
32 const exponentBits = std.math.floatExponentBits(T);32 const exponentBits = std.math.floatExponentBits(T);
33 const signBit = (@as(rep_t, 1) << (significandBits + exponentBits));33 const signBit = (@as(rep_t, 1) << (significandBits + exponentBits));
34 const absMask = signBit - 1;34 const absMask = signBit - 1;
35 const infRep = @bitCast(rep_t, std.math.inf(T));35 const infT = std.math.inf(T);
36 const infRep = @bitCast(rep_t, infT);
3637
37 const aInt = @bitCast(srep_t, a);38 const aInt = @bitCast(srep_t, a);
38 const bInt = @bitCast(srep_t, b);39 const bInt = @bitCast(srep_t, b);
...@@ -46,20 +47,18 @@ pub fn cmp(comptime T: type, comptime RT: type, a: T, b: T) RT {...@@ -46,20 +47,18 @@ pub fn cmp(comptime T: type, comptime RT: type, a: T, b: T) RT {
46 if ((aAbs | bAbs) == 0) return .Equal;47 if ((aAbs | bAbs) == 0) return .Equal;
4748
48 // If at least one of a and b is positive, we get the same result comparing49 // If at least one of a and b is positive, we get the same result comparing
49 // a and b as signed integers as we would with a fp_ting-point compare.50 // a and b as signed integers as we would with a floating-point compare.
50 if ((aInt & bInt) >= 0) {51 if ((aInt & bInt) >= 0) {
51 if (aInt < bInt) {52 if (aInt < bInt) {
52 return .Less;53 return .Less;
53 } else if (aInt == bInt) {54 } else if (aInt == bInt) {
54 return .Equal;55 return .Equal;
55 } else return .Greater;56 } else return .Greater;
56 }57 } else {
5758 // Otherwise, both are negative, so we need to flip the sense of the
58 // Otherwise, both are negative, so we need to flip the sense of the59 // comparison to get the correct result. (This assumes a twos- or ones-
59 // comparison to get the correct result. (This assumes a twos- or ones-60 // complement integer representation; if integers are represented in a
60 // complement integer representation; if integers are represented in a61 // sign-magnitude representation, then this flip is incorrect).
61 // sign-magnitude representation, then this flip is incorrect).
62 else {
63 if (aInt > bInt) {62 if (aInt > bInt) {
64 return .Less;63 return .Less;
65 } else if (aInt == bInt) {64 } else if (aInt == bInt) {
...@@ -68,7 +67,7 @@ pub fn cmp(comptime T: type, comptime RT: type, a: T, b: T) RT {...@@ -68,7 +67,7 @@ pub fn cmp(comptime T: type, comptime RT: type, a: T, b: T) RT {
68 }67 }
69}68}
7069
71pub fn unordcmp(comptime T: type, a: T, b: T) i32 {70pub inline fn unordcmp(comptime T: type, a: T, b: T) i32 {
72 @setRuntimeSafety(builtin.is_test);71 @setRuntimeSafety(builtin.is_test);
7372
74 const rep_t = std.meta.Int(.unsigned, @typeInfo(T).Float.bits);73 const rep_t = std.meta.Int(.unsigned, @typeInfo(T).Float.bits);
...@@ -89,12 +88,14 @@ pub fn unordcmp(comptime T: type, a: T, b: T) i32 {...@@ -89,12 +88,14 @@ pub fn unordcmp(comptime T: type, a: T, b: T) i32 {
8988
90pub fn __lesf2(a: f32, b: f32) callconv(.C) i32 {89pub fn __lesf2(a: f32, b: f32) callconv(.C) i32 {
91 @setRuntimeSafety(builtin.is_test);90 @setRuntimeSafety(builtin.is_test);
92 return @bitCast(i32, @call(.{ .modifier = .always_inline }, cmp, .{ f32, LE, a, b }));91 const float = cmp(f32, LE, a, b);
92 return @bitCast(i32, float);
93}93}
9494
95pub fn __gesf2(a: f32, b: f32) callconv(.C) i32 {95pub fn __gesf2(a: f32, b: f32) callconv(.C) i32 {
96 @setRuntimeSafety(builtin.is_test);96 @setRuntimeSafety(builtin.is_test);
97 return @bitCast(i32, @call(.{ .modifier = .always_inline }, cmp, .{ f32, GE, a, b }));97 const float = cmp(f32, GE, a, b);
98 return @bitCast(i32, float);
98}99}
99100
100pub fn __eqsf2(a: f32, b: f32) callconv(.C) i32 {101pub fn __eqsf2(a: f32, b: f32) callconv(.C) i32 {
...@@ -117,12 +118,14 @@ pub fn __gtsf2(a: f32, b: f32) callconv(.C) i32 {...@@ -117,12 +118,14 @@ pub fn __gtsf2(a: f32, b: f32) callconv(.C) i32 {
117118
118pub fn __ledf2(a: f64, b: f64) callconv(.C) i32 {119pub fn __ledf2(a: f64, b: f64) callconv(.C) i32 {
119 @setRuntimeSafety(builtin.is_test);120 @setRuntimeSafety(builtin.is_test);
120 return @bitCast(i32, @call(.{ .modifier = .always_inline }, cmp, .{ f64, LE, a, b }));121 const float = cmp(f64, LE, a, b);
122 return @bitCast(i32, float);
121}123}
122124
123pub fn __gedf2(a: f64, b: f64) callconv(.C) i32 {125pub fn __gedf2(a: f64, b: f64) callconv(.C) i32 {
124 @setRuntimeSafety(builtin.is_test);126 @setRuntimeSafety(builtin.is_test);
125 return @bitCast(i32, @call(.{ .modifier = .always_inline }, cmp, .{ f64, GE, a, b }));127 const float = cmp(f64, GE, a, b);
128 return @bitCast(i32, float);
126}129}
127130
128pub fn __eqdf2(a: f64, b: f64) callconv(.C) i32 {131pub fn __eqdf2(a: f64, b: f64) callconv(.C) i32 {
...@@ -145,12 +148,14 @@ pub fn __gtdf2(a: f64, b: f64) callconv(.C) i32 {...@@ -145,12 +148,14 @@ pub fn __gtdf2(a: f64, b: f64) callconv(.C) i32 {
145148
146pub fn __letf2(a: f128, b: f128) callconv(.C) i32 {149pub fn __letf2(a: f128, b: f128) callconv(.C) i32 {
147 @setRuntimeSafety(builtin.is_test);150 @setRuntimeSafety(builtin.is_test);
148 return @bitCast(i32, @call(.{ .modifier = .always_inline }, cmp, .{ f128, LE, a, b }));151 const float = cmp(f128, LE, a, b);
152 return @bitCast(i32, float);
149}153}
150154
151pub fn __getf2(a: f128, b: f128) callconv(.C) i32 {155pub fn __getf2(a: f128, b: f128) callconv(.C) i32 {
152 @setRuntimeSafety(builtin.is_test);156 @setRuntimeSafety(builtin.is_test);
153 return @bitCast(i32, @call(.{ .modifier = .always_inline }, cmp, .{ f128, GE, a, b }));157 const float = cmp(f128, GE, a, b);
158 return @bitCast(i32, float);
154}159}
155160
156pub fn __eqtf2(a: f128, b: f128) callconv(.C) i32 {161pub fn __eqtf2(a: f128, b: f128) callconv(.C) i32 {
...@@ -173,17 +178,17 @@ pub fn __gttf2(a: f128, b: f128) callconv(.C) i32 {...@@ -173,17 +178,17 @@ pub fn __gttf2(a: f128, b: f128) callconv(.C) i32 {
173178
174pub fn __unordsf2(a: f32, b: f32) callconv(.C) i32 {179pub fn __unordsf2(a: f32, b: f32) callconv(.C) i32 {
175 @setRuntimeSafety(builtin.is_test);180 @setRuntimeSafety(builtin.is_test);
176 return @call(.{ .modifier = .always_inline }, unordcmp, .{ f32, a, b });181 return unordcmp(f32, a, b);
177}182}
178183
179pub fn __unorddf2(a: f64, b: f64) callconv(.C) i32 {184pub fn __unorddf2(a: f64, b: f64) callconv(.C) i32 {
180 @setRuntimeSafety(builtin.is_test);185 @setRuntimeSafety(builtin.is_test);
181 return @call(.{ .modifier = .always_inline }, unordcmp, .{ f64, a, b });186 return unordcmp(f64, a, b);
182}187}
183188
184pub fn __unordtf2(a: f128, b: f128) callconv(.C) i32 {189pub fn __unordtf2(a: f128, b: f128) callconv(.C) i32 {
185 @setRuntimeSafety(builtin.is_test);190 @setRuntimeSafety(builtin.is_test);
186 return @call(.{ .modifier = .always_inline }, unordcmp, .{ f128, a, b });191 return unordcmp(f128, a, b);
187}192}
188193
189// ARM EABI intrinsics194// ARM EABI intrinsics
src/Sema.zig+10-8
...@@ -5064,7 +5064,7 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -5064,7 +5064,7 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
50645064
5065 const dest_type = try sema.resolveType(block, dest_ty_src, extra.lhs);5065 const dest_type = try sema.resolveType(block, dest_ty_src, extra.lhs);
5066 const operand = sema.resolveInst(extra.rhs);5066 const operand = sema.resolveInst(extra.rhs);
5067 return sema.bitcast(block, dest_type, operand, operand_src);5067 return sema.bitCast(block, dest_type, operand, operand_src);
5068}5068}
50695069
5070fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {5070fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
...@@ -11016,7 +11016,7 @@ fn coerce(...@@ -11016,7 +11016,7 @@ fn coerce(
1101611016
11017 const in_memory_result = coerceInMemoryAllowed(dest_type, inst_ty, false, target);11017 const in_memory_result = coerceInMemoryAllowed(dest_type, inst_ty, false, target);
11018 if (in_memory_result == .ok) {11018 if (in_memory_result == .ok) {
11019 return sema.bitcast(block, dest_type, inst, inst_src);11019 return sema.bitCast(block, dest_type, inst, inst_src);
11020 }11020 }
1102111021
11022 // undefined to anything11022 // undefined to anything
...@@ -11439,18 +11439,20 @@ fn storePtrVal(...@@ -11439,18 +11439,20 @@ fn storePtrVal(
11439 }11439 }
11440}11440}
1144111441
11442fn bitcast(11442fn bitCast(
11443 sema: *Sema,11443 sema: *Sema,
11444 block: *Block,11444 block: *Block,
11445 dest_type: Type,11445 dest_type: Type,
11446 inst: Air.Inst.Ref,11446 inst: Air.Inst.Ref,
11447 inst_src: LazySrcLoc,11447 inst_src: LazySrcLoc,
11448) CompileError!Air.Inst.Ref {11448) CompileError!Air.Inst.Ref {
11449 // TODO validate the type size and other compile errors
11449 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {11450 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {
11450 // Keep the comptime Value representation; take the new type.11451 const target = sema.mod.getTarget();
11451 return sema.addConstant(dest_type, val);11452 const old_ty = sema.typeOf(inst);
11453 const result_val = try val.bitCast(old_ty, dest_type, target, sema.gpa, sema.arena);
11454 return sema.addConstant(dest_type, result_val);
11452 }11455 }
11453 // TODO validate the type size and other compile errors
11454 try sema.requireRuntimeBlock(block, inst_src);11456 try sema.requireRuntimeBlock(block, inst_src);
11455 return block.addTyOp(.bitcast, dest_type, inst);11457 return block.addTyOp(.bitcast, dest_type, inst);
11456}11458}
...@@ -11482,7 +11484,7 @@ fn coerceArrayPtrToMany(...@@ -11482,7 +11484,7 @@ fn coerceArrayPtrToMany(
11482 return sema.addConstant(dest_type, val);11484 return sema.addConstant(dest_type, val);
11483 }11485 }
11484 try sema.requireRuntimeBlock(block, inst_src);11486 try sema.requireRuntimeBlock(block, inst_src);
11485 return sema.bitcast(block, dest_type, inst, inst_src);11487 return sema.bitCast(block, dest_type, inst, inst_src);
11486}11488}
1148711489
11488fn analyzeDeclVal(11490fn analyzeDeclVal(
...@@ -11571,7 +11573,7 @@ fn analyzeRef(...@@ -11571,7 +11573,7 @@ fn analyzeRef(
11571 try sema.storePtr(block, src, alloc, operand);11573 try sema.storePtr(block, src, alloc, operand);
1157211574
11573 // TODO: Replace with sema.coerce when that supports adding pointer constness.11575 // TODO: Replace with sema.coerce when that supports adding pointer constness.
11574 return sema.bitcast(block, ptr_type, alloc, src);11576 return sema.bitCast(block, ptr_type, alloc, src);
11575}11577}
1157611578
11577fn analyzeLoad(11579fn analyzeLoad(
src/codegen/llvm.zig+13-2
...@@ -1,4 +1,5 @@...@@ -1,4 +1,5 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");
2const assert = std.debug.assert;3const assert = std.debug.assert;
3const Allocator = std.mem.Allocator;4const Allocator = std.mem.Allocator;
4const Compilation = @import("../Compilation.zig");5const Compilation = @import("../Compilation.zig");
...@@ -6,6 +7,7 @@ const llvm = @import("llvm/bindings.zig");...@@ -6,6 +7,7 @@ const llvm = @import("llvm/bindings.zig");
6const link = @import("../link.zig");7const link = @import("../link.zig");
7const log = std.log.scoped(.codegen);8const log = std.log.scoped(.codegen);
8const math = std.math;9const math = std.math;
10const native_endian = builtin.cpu.arch.endian();
911
10const build_options = @import("build_options");12const build_options = @import("build_options");
11const Module = @import("../Module.zig");13const Module = @import("../Module.zig");
...@@ -958,11 +960,20 @@ pub const DeclGen = struct {...@@ -958,11 +960,20 @@ pub const DeclGen = struct {
958 return llvm_int;960 return llvm_int;
959 },961 },
960 .Float => {962 .Float => {
963 const llvm_ty = try self.llvmType(tv.ty);
961 if (tv.ty.floatBits(self.module.getTarget()) <= 64) {964 if (tv.ty.floatBits(self.module.getTarget()) <= 64) {
962 const llvm_ty = try self.llvmType(tv.ty);
963 return llvm_ty.constReal(tv.val.toFloat(f64));965 return llvm_ty.constReal(tv.val.toFloat(f64));
964 }966 }
965 return self.todo("bitcast to f128 from an integer", .{});967
968 var buf: [2]u64 = @bitCast([2]u64, tv.val.toFloat(f128));
969 // LLVM seems to require that the lower half of the f128 be placed first
970 // in the buffer.
971 if (native_endian == .Big) {
972 std.mem.swap(u64, &buf[0], &buf[1]);
973 }
974
975 const int = self.context.intType(128).constIntOfArbitraryPrecision(buf.len, &buf);
976 return int.constBitCast(llvm_ty);
966 },977 },
967 .Pointer => switch (tv.val.tag()) {978 .Pointer => switch (tv.val.tag()) {
968 .decl_ref => {979 .decl_ref => {
src/value.zig+112-10
...@@ -938,30 +938,132 @@ pub const Value = extern union {...@@ -938,30 +938,132 @@ pub const Value = extern union {
938938
939 pub fn toBool(self: Value) bool {939 pub fn toBool(self: Value) bool {
940 return switch (self.tag()) {940 return switch (self.tag()) {
941 .bool_true => true,941 .bool_true, .one => true,
942 .bool_false, .zero => false,942 .bool_false, .zero => false,
943 else => unreachable,943 else => unreachable,
944 };944 };
945 }945 }
946946
947 pub fn bitCast(
948 val: Value,
949 old_ty: Type,
950 new_ty: Type,
951 target: Target,
952 gpa: *Allocator,
953 arena: *Allocator,
954 ) !Value {
955 // For types with well-defined memory layouts, we serialize them a byte buffer,
956 // then deserialize to the new type.
957 const buffer = try gpa.alloc(u8, old_ty.abiSize(target));
958 defer gpa.free(buffer);
959 val.writeToMemory(old_ty, target, buffer);
960 return Value.readFromMemory(new_ty, target, buffer, arena);
961 }
962
963 pub fn writeToMemory(val: Value, ty: Type, target: Target, buffer: []u8) void {
964 switch (ty.zigTypeTag()) {
965 .Int => {
966 var bigint_buffer: BigIntSpace = undefined;
967 const bigint = val.toBigInt(&bigint_buffer);
968 const bits = ty.intInfo(target).bits;
969 bigint.writeTwosComplement(buffer, bits, target.cpu.arch.endian());
970 },
971 .Float => switch (ty.floatBits(target)) {
972 16 => return floatWriteToMemory(f16, val.toFloat(f16), target, buffer),
973 32 => return floatWriteToMemory(f32, val.toFloat(f32), target, buffer),
974 64 => return floatWriteToMemory(f64, val.toFloat(f64), target, buffer),
975 128 => return floatWriteToMemory(f128, val.toFloat(f128), target, buffer),
976 else => unreachable,
977 },
978 else => @panic("TODO implement writeToMemory for more types"),
979 }
980 }
981
982 pub fn readFromMemory(ty: Type, target: Target, buffer: []const u8, arena: *Allocator) !Value {
983 switch (ty.zigTypeTag()) {
984 .Int => {
985 const int_info = ty.intInfo(target);
986 const endian = target.cpu.arch.endian();
987 // TODO use a correct amount of limbs
988 const limbs_buffer = try arena.alloc(std.math.big.Limb, 2);
989 var bigint = BigIntMutable.init(limbs_buffer, 0);
990 bigint.readTwosComplement(buffer, int_info.bits, endian, int_info.signedness);
991 // TODO if it fits in 64 bits then use one of those tags
992
993 const result_limbs = bigint.limbs[0..bigint.len];
994 if (bigint.positive) {
995 return Value.Tag.int_big_positive.create(arena, result_limbs);
996 } else {
997 return Value.Tag.int_big_negative.create(arena, result_limbs);
998 }
999 },
1000 .Float => switch (ty.floatBits(target)) {
1001 16 => return Value.Tag.float_16.create(arena, floatReadFromMemory(f16, target, buffer)),
1002 32 => return Value.Tag.float_32.create(arena, floatReadFromMemory(f32, target, buffer)),
1003 64 => return Value.Tag.float_64.create(arena, floatReadFromMemory(f64, target, buffer)),
1004 128 => return Value.Tag.float_128.create(arena, floatReadFromMemory(f128, target, buffer)),
1005 else => unreachable,
1006 },
1007 else => @panic("TODO implement readFromMemory for more types"),
1008 }
1009 }
1010
1011 fn floatWriteToMemory(comptime F: type, f: F, target: Target, buffer: []u8) void {
1012 const Int = @Type(.{ .Int = .{
1013 .signedness = .unsigned,
1014 .bits = @typeInfo(F).Float.bits,
1015 } });
1016 const int = @bitCast(Int, f);
1017 std.mem.writeInt(Int, buffer[0..@sizeOf(Int)], int, target.cpu.arch.endian());
1018 }
1019
1020 fn floatReadFromMemory(comptime F: type, target: Target, buffer: []const u8) F {
1021 const Int = @Type(.{ .Int = .{
1022 .signedness = .unsigned,
1023 .bits = @typeInfo(F).Float.bits,
1024 } });
1025 const int = std.mem.readInt(Int, buffer[0..@sizeOf(Int)], target.cpu.arch.endian());
1026 return @bitCast(F, int);
1027 }
1028
947 /// Asserts that the value is a float or an integer.1029 /// Asserts that the value is a float or an integer.
948 pub fn toFloat(self: Value, comptime T: type) T {1030 pub fn toFloat(val: Value, comptime T: type) T {
949 return switch (self.tag()) {1031 return switch (val.tag()) {
950 .float_16 => @floatCast(T, self.castTag(.float_16).?.data),1032 .float_16 => @floatCast(T, val.castTag(.float_16).?.data),
951 .float_32 => @floatCast(T, self.castTag(.float_32).?.data),1033 .float_32 => @floatCast(T, val.castTag(.float_32).?.data),
952 .float_64 => @floatCast(T, self.castTag(.float_64).?.data),1034 .float_64 => @floatCast(T, val.castTag(.float_64).?.data),
953 .float_128 => @floatCast(T, self.castTag(.float_128).?.data),1035 .float_128 => @floatCast(T, val.castTag(.float_128).?.data),
9541036
955 .zero => 0,1037 .zero => 0,
956 .one => 1,1038 .one => 1,
957 .int_u64 => @intToFloat(T, self.castTag(.int_u64).?.data),1039 .int_u64 => @intToFloat(T, val.castTag(.int_u64).?.data),
958 .int_i64 => @intToFloat(T, self.castTag(.int_i64).?.data),1040 .int_i64 => @intToFloat(T, val.castTag(.int_i64).?.data),
9591041
960 .int_big_positive, .int_big_negative => @panic("big int to f128"),1042 .int_big_positive => @floatCast(T, bigIntToFloat(val.castTag(.int_big_positive).?.data, true)),
1043 .int_big_negative => @floatCast(T, bigIntToFloat(val.castTag(.int_big_negative).?.data, false)),
961 else => unreachable,1044 else => unreachable,
962 };1045 };
963 }1046 }
9641047
1048 /// TODO move this to std lib big int code
1049 fn bigIntToFloat(limbs: []const std.math.big.Limb, positive: bool) f128 {
1050 if (limbs.len == 0) return 0;
1051
1052 const base = std.math.maxInt(std.math.big.Limb) + 1;
1053 var result: f128 = 0;
1054 var i: usize = limbs.len;
1055 while (i != 0) {
1056 i -= 1;
1057 const limb: f128 = @intToFloat(f128, limbs[i]);
1058 result = @mulAdd(f128, base, limb, result);
1059 }
1060 if (positive) {
1061 return result;
1062 } else {
1063 return -result;
1064 }
1065 }
1066
965 pub fn clz(val: Value, ty: Type, target: Target) u64 {1067 pub fn clz(val: Value, ty: Type, target: Target) u64 {
966 const ty_bits = ty.intInfo(target).bits;1068 const ty_bits = ty.intInfo(target).bits;
967 switch (val.tag()) {1069 switch (val.tag()) {
test/behavior.zig+2-1
...@@ -5,6 +5,7 @@ test {...@@ -5,6 +5,7 @@ test {
5 _ = @import("behavior/array.zig");5 _ = @import("behavior/array.zig");
6 _ = @import("behavior/atomics.zig");6 _ = @import("behavior/atomics.zig");
7 _ = @import("behavior/basic.zig");7 _ = @import("behavior/basic.zig");
8 _ = @import("behavior/bitcast.zig");
8 _ = @import("behavior/bool.zig");9 _ = @import("behavior/bool.zig");
9 _ = @import("behavior/bugs/655.zig");10 _ = @import("behavior/bugs/655.zig");
10 _ = @import("behavior/bugs/1277.zig");11 _ = @import("behavior/bugs/1277.zig");
...@@ -50,7 +51,7 @@ test {...@@ -50,7 +51,7 @@ test {
50 }51 }
51 _ = @import("behavior/await_struct.zig");52 _ = @import("behavior/await_struct.zig");
52 _ = @import("behavior/bit_shifting.zig");53 _ = @import("behavior/bit_shifting.zig");
53 _ = @import("behavior/bitcast.zig");54 _ = @import("behavior/bitcast_stage1.zig");
54 _ = @import("behavior/bitreverse.zig");55 _ = @import("behavior/bitreverse.zig");
55 _ = @import("behavior/bugs/394.zig");56 _ = @import("behavior/bugs/394.zig");
56 _ = @import("behavior/bugs/421.zig");57 _ = @import("behavior/bugs/421.zig");
test/behavior/bitcast.zig+1-154
...@@ -22,133 +22,6 @@ fn conv2(x: u32) i32 {...@@ -22,133 +22,6 @@ fn conv2(x: u32) i32 {
22 return @bitCast(i32, x);22 return @bitCast(i32, x);
23}23}
2424
25test "@bitCast enum to its integer type" {
26 const SOCK = enum(c_int) {
27 A,
28 B,
29
30 fn testBitCastExternEnum() !void {
31 var SOCK_DGRAM = @This().B;
32 var sock_dgram = @bitCast(c_int, SOCK_DGRAM);
33 try expect(sock_dgram == 1);
34 }
35 };
36
37 try SOCK.testBitCastExternEnum();
38 comptime try SOCK.testBitCastExternEnum();
39}
40
41test "@bitCast packed structs at runtime and comptime" {
42 const Full = packed struct {
43 number: u16,
44 };
45 const Divided = packed struct {
46 half1: u8,
47 quarter3: u4,
48 quarter4: u4,
49 };
50 const S = struct {
51 fn doTheTest() !void {
52 var full = Full{ .number = 0x1234 };
53 var two_halves = @bitCast(Divided, full);
54 switch (native_endian) {
55 .Big => {
56 try expect(two_halves.half1 == 0x12);
57 try expect(two_halves.quarter3 == 0x3);
58 try expect(two_halves.quarter4 == 0x4);
59 },
60 .Little => {
61 try expect(two_halves.half1 == 0x34);
62 try expect(two_halves.quarter3 == 0x2);
63 try expect(two_halves.quarter4 == 0x1);
64 },
65 }
66 }
67 };
68 try S.doTheTest();
69 comptime try S.doTheTest();
70}
71
72test "@bitCast extern structs at runtime and comptime" {
73 const Full = extern struct {
74 number: u16,
75 };
76 const TwoHalves = extern struct {
77 half1: u8,
78 half2: u8,
79 };
80 const S = struct {
81 fn doTheTest() !void {
82 var full = Full{ .number = 0x1234 };
83 var two_halves = @bitCast(TwoHalves, full);
84 switch (native_endian) {
85 .Big => {
86 try expect(two_halves.half1 == 0x12);
87 try expect(two_halves.half2 == 0x34);
88 },
89 .Little => {
90 try expect(two_halves.half1 == 0x34);
91 try expect(two_halves.half2 == 0x12);
92 },
93 }
94 }
95 };
96 try S.doTheTest();
97 comptime try S.doTheTest();
98}
99
100test "bitcast packed struct to integer and back" {
101 const LevelUpMove = packed struct {
102 move_id: u9,
103 level: u7,
104 };
105 const S = struct {
106 fn doTheTest() !void {
107 var move = LevelUpMove{ .move_id = 1, .level = 2 };
108 var v = @bitCast(u16, move);
109 var back_to_a_move = @bitCast(LevelUpMove, v);
110 try expect(back_to_a_move.move_id == 1);
111 try expect(back_to_a_move.level == 2);
112 }
113 };
114 try S.doTheTest();
115 comptime try S.doTheTest();
116}
117
118test "implicit cast to error union by returning" {
119 const S = struct {
120 fn entry() !void {
121 try expect((func(-1) catch unreachable) == maxInt(u64));
122 }
123 pub fn func(sz: i64) anyerror!u64 {
124 return @bitCast(u64, sz);
125 }
126 };
127 try S.entry();
128 comptime try S.entry();
129}
130
131// issue #3010: compiler segfault
132test "bitcast literal [4]u8 param to u32" {
133 const ip = @bitCast(u32, [_]u8{ 255, 255, 255, 255 });
134 try expect(ip == maxInt(u32));
135}
136
137test "bitcast packed struct literal to byte" {
138 const Foo = packed struct {
139 value: u8,
140 };
141 const casted = @bitCast(u8, Foo{ .value = 0xF });
142 try expect(casted == 0xf);
143}
144
145test "comptime bitcast used in expression has the correct type" {
146 const Foo = packed struct {
147 value: u8,
148 };
149 try expect(@bitCast(u8, Foo{ .value = 0xF }) == 0xf);
150}
151
152test "bitcast result to _" {25test "bitcast result to _" {
153 _ = @bitCast(u8, @as(i8, 1));26 _ = @bitCast(u8, @as(i8, 1));
154}27}
...@@ -156,7 +29,7 @@ test "bitcast result to _" {...@@ -156,7 +29,7 @@ test "bitcast result to _" {
156test "nested bitcast" {29test "nested bitcast" {
157 const S = struct {30 const S = struct {
158 fn moo(x: isize) !void {31 fn moo(x: isize) !void {
159 try @import("std").testing.expectEqual(@intCast(isize, 42), x);32 try expect(@intCast(isize, 42) == x);
160 }33 }
16134
162 fn foo(x: isize) !void {35 fn foo(x: isize) !void {
...@@ -169,29 +42,3 @@ test "nested bitcast" {...@@ -169,29 +42,3 @@ test "nested bitcast" {
169 try S.foo(42);42 try S.foo(42);
170 comptime try S.foo(42);43 comptime try S.foo(42);
171}44}
172
173test "bitcast passed as tuple element" {
174 const S = struct {
175 fn foo(args: anytype) !void {
176 comptime try expect(@TypeOf(args[0]) == f32);
177 try expect(args[0] == 12.34);
178 }
179 };
180 try S.foo(.{@bitCast(f32, @as(u32, 0x414570A4))});
181}
182
183test "triple level result location with bitcast sandwich passed as tuple element" {
184 const S = struct {
185 fn foo(args: anytype) !void {
186 comptime try expect(@TypeOf(args[0]) == f64);
187 try expect(args[0] > 12.33 and args[0] < 12.35);
188 }
189 };
190 try S.foo(.{@as(f64, @bitCast(f32, @as(u32, 0x414570A4)))});
191}
192
193test "bitcast generates a temporary value" {
194 var y = @as(u16, 0x55AA);
195 const x = @bitCast(u16, @bitCast([2]u8, y));
196 try expectEqual(y, x);
197}
test/behavior/bitcast_stage1.zig created+159
...@@ -0,0 +1,159 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const expect = std.testing.expect;
4const expectEqual = std.testing.expectEqual;
5const maxInt = std.math.maxInt;
6const native_endian = builtin.target.cpu.arch.endian();
7
8test "@bitCast enum to its integer type" {
9 const SOCK = enum(c_int) {
10 A,
11 B,
12
13 fn testBitCastExternEnum() !void {
14 var SOCK_DGRAM = @This().B;
15 var sock_dgram = @bitCast(c_int, SOCK_DGRAM);
16 try expect(sock_dgram == 1);
17 }
18 };
19
20 try SOCK.testBitCastExternEnum();
21 comptime try SOCK.testBitCastExternEnum();
22}
23
24test "@bitCast packed structs at runtime and comptime" {
25 const Full = packed struct {
26 number: u16,
27 };
28 const Divided = packed struct {
29 half1: u8,
30 quarter3: u4,
31 quarter4: u4,
32 };
33 const S = struct {
34 fn doTheTest() !void {
35 var full = Full{ .number = 0x1234 };
36 var two_halves = @bitCast(Divided, full);
37 switch (native_endian) {
38 .Big => {
39 try expect(two_halves.half1 == 0x12);
40 try expect(two_halves.quarter3 == 0x3);
41 try expect(two_halves.quarter4 == 0x4);
42 },
43 .Little => {
44 try expect(two_halves.half1 == 0x34);
45 try expect(two_halves.quarter3 == 0x2);
46 try expect(two_halves.quarter4 == 0x1);
47 },
48 }
49 }
50 };
51 try S.doTheTest();
52 comptime try S.doTheTest();
53}
54
55test "@bitCast extern structs at runtime and comptime" {
56 const Full = extern struct {
57 number: u16,
58 };
59 const TwoHalves = extern struct {
60 half1: u8,
61 half2: u8,
62 };
63 const S = struct {
64 fn doTheTest() !void {
65 var full = Full{ .number = 0x1234 };
66 var two_halves = @bitCast(TwoHalves, full);
67 switch (native_endian) {
68 .Big => {
69 try expect(two_halves.half1 == 0x12);
70 try expect(two_halves.half2 == 0x34);
71 },
72 .Little => {
73 try expect(two_halves.half1 == 0x34);
74 try expect(two_halves.half2 == 0x12);
75 },
76 }
77 }
78 };
79 try S.doTheTest();
80 comptime try S.doTheTest();
81}
82
83test "bitcast packed struct to integer and back" {
84 const LevelUpMove = packed struct {
85 move_id: u9,
86 level: u7,
87 };
88 const S = struct {
89 fn doTheTest() !void {
90 var move = LevelUpMove{ .move_id = 1, .level = 2 };
91 var v = @bitCast(u16, move);
92 var back_to_a_move = @bitCast(LevelUpMove, v);
93 try expect(back_to_a_move.move_id == 1);
94 try expect(back_to_a_move.level == 2);
95 }
96 };
97 try S.doTheTest();
98 comptime try S.doTheTest();
99}
100
101test "implicit cast to error union by returning" {
102 const S = struct {
103 fn entry() !void {
104 try expect((func(-1) catch unreachable) == maxInt(u64));
105 }
106 pub fn func(sz: i64) anyerror!u64 {
107 return @bitCast(u64, sz);
108 }
109 };
110 try S.entry();
111 comptime try S.entry();
112}
113
114// issue #3010: compiler segfault
115test "bitcast literal [4]u8 param to u32" {
116 const ip = @bitCast(u32, [_]u8{ 255, 255, 255, 255 });
117 try expect(ip == maxInt(u32));
118}
119
120test "bitcast packed struct literal to byte" {
121 const Foo = packed struct {
122 value: u8,
123 };
124 const casted = @bitCast(u8, Foo{ .value = 0xF });
125 try expect(casted == 0xf);
126}
127
128test "comptime bitcast used in expression has the correct type" {
129 const Foo = packed struct {
130 value: u8,
131 };
132 try expect(@bitCast(u8, Foo{ .value = 0xF }) == 0xf);
133}
134
135test "bitcast passed as tuple element" {
136 const S = struct {
137 fn foo(args: anytype) !void {
138 comptime try expect(@TypeOf(args[0]) == f32);
139 try expect(args[0] == 12.34);
140 }
141 };
142 try S.foo(.{@bitCast(f32, @as(u32, 0x414570A4))});
143}
144
145test "triple level result location with bitcast sandwich passed as tuple element" {
146 const S = struct {
147 fn foo(args: anytype) !void {
148 comptime try expect(@TypeOf(args[0]) == f64);
149 try expect(args[0] > 12.33 and args[0] < 12.35);
150 }
151 };
152 try S.foo(.{@as(f64, @bitCast(f32, @as(u32, 0x414570A4)))});
153}
154
155test "bitcast generates a temporary value" {
156 var y = @as(u16, 0x55AA);
157 const x = @bitCast(u16, @bitCast([2]u8, y));
158 try expectEqual(y, x);
159}