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;
1010const maxInt = std.math.maxInt;
1111const minInt = std.math.minInt;
1212const assert = std.debug.assert;
13const Endian = std.builtin.Endian;
14const Signedness = std.builtin.Signedness;
1315
1416const debug_safety = false;
1517
......@@ -328,7 +330,7 @@ pub const Mutable = struct {
328330 pub fn setTwosCompIntLimit(
329331 r: *Mutable,
330332 limit: TwosCompIntLimit,
331 signedness: std.builtin.Signedness,
333 signedness: Signedness,
332334 bit_count: usize,
333335 ) void {
334336 // Handle zero-bit types.
......@@ -457,7 +459,7 @@ pub const Mutable = struct {
457459 ///
458460 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
459461 /// 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 {
461463 const req_limbs = calcTwosCompLimbCount(bit_count);
462464
463465 // 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 {
493495 ///
494496 /// Assets the result fits in `r`. Upper bound on the number of limbs needed by
495497 /// 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 {
497499 const req_limbs = calcTwosCompLimbCount(bit_count);
498500
499501 // 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 {
595597 /// r, a and b may be aliases
596598 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
597599 /// 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 {
599601 r.addWrap(a, b.negate(), signedness, bit_count);
600602 }
601603
......@@ -604,7 +606,7 @@ pub const Mutable = struct {
604606 ///
605607 /// Assets the result fits in `r`. Upper bound on the number of limbs needed by
606608 /// 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 {
608610 r.addSat(a, b.negate(), signedness, bit_count);
609611 }
610612
......@@ -680,7 +682,7 @@ pub const Mutable = struct {
680682 rma: *Mutable,
681683 a: Const,
682684 b: Const,
683 signedness: std.builtin.Signedness,
685 signedness: Signedness,
684686 bit_count: usize,
685687 limbs_buffer: []Limb,
686688 allocator: ?*Allocator,
......@@ -721,7 +723,7 @@ pub const Mutable = struct {
721723 rma: *Mutable,
722724 a: Const,
723725 b: Const,
724 signedness: std.builtin.Signedness,
726 signedness: Signedness,
725727 bit_count: usize,
726728 allocator: ?*Allocator,
727729 ) void {
......@@ -1284,7 +1286,7 @@ pub const Mutable = struct {
12841286 ///
12851287 /// Asserts `r` has enough storage to store the result.
12861288 /// 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 {
12881290 const req_limbs = calcTwosCompLimbCount(bit_count);
12891291
12901292 // Handle 0-bit integers.
......@@ -1369,12 +1371,47 @@ pub const Mutable = struct {
13691371 ///
13701372 /// Asserts `r` has enough storage to store the result.
13711373 /// 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 {
13731375 if (!a.fitsInTwosComp(signedness, bit_count)) {
13741376 r.setTwosCompIntLimit(if (r.positive) .max else .min, signedness, bit_count);
13751377 }
13761378 }
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
13781415 /// Normalize a possible sequence of leading zeros.
13791416 ///
13801417 /// [1, 2, 3, 4, 0] -> [1, 2, 3, 4]
......@@ -1485,7 +1522,7 @@ pub const Const = struct {
14851522 return bits;
14861523 }
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 {
14891526 if (self.eqZero()) {
14901527 return true;
14911528 }
......@@ -1731,6 +1768,29 @@ pub const Const = struct {
17311768 return s.len;
17321769 }
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
17341794 /// Returns `math.Order.lt`, `math.Order.eq`, `math.Order.gt` if
17351795 /// `|a| < |b|`, `|a| == |b|`, or `|a| > |b|` respectively.
17361796 pub fn orderAbs(a: Const, b: Const) math.Order {
......@@ -1992,7 +2052,7 @@ pub const Managed = struct {
19922052 return self.toConst().bitCountTwosComp();
19932053 }
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 {
19962056 return self.toConst().fitsInTwosComp(signedness, bit_count);
19972057 }
19982058
......@@ -2051,7 +2111,7 @@ pub const Managed = struct {
20512111 pub fn setTwosCompIntLimit(
20522112 r: *Managed,
20532113 limit: TwosCompIntLimit,
2054 signedness: std.builtin.Signedness,
2114 signedness: Signedness,
20552115 bit_count: usize,
20562116 ) !void {
20572117 try r.ensureCapacity(calcTwosCompLimbCount(bit_count));
......@@ -2164,7 +2224,7 @@ pub const Managed = struct {
21642224 /// `r.ensureTwosCompCapacity` prior to calling `add`.
21652225 ///
21662226 /// 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 {
21682228 try r.ensureTwosCompCapacity(bit_count);
21692229 var m = r.toMutable();
21702230 m.addWrap(a, b, signedness, bit_count);
......@@ -2177,7 +2237,7 @@ pub const Managed = struct {
21772237 /// `r.ensureTwosCompCapacity` prior to calling `add`.
21782238 ///
21792239 /// 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 {
21812241 try r.ensureTwosCompCapacity(bit_count);
21822242 var m = r.toMutable();
21832243 m.addSat(a, b, signedness, bit_count);
......@@ -2202,7 +2262,7 @@ pub const Managed = struct {
22022262 /// `r.ensureTwosCompCapacity` prior to calling `add`.
22032263 ///
22042264 /// 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 {
22062266 try r.ensureTwosCompCapacity(bit_count);
22072267 var m = r.toMutable();
22082268 m.subWrap(a, b, signedness, bit_count);
......@@ -2215,7 +2275,7 @@ pub const Managed = struct {
22152275 /// `r.ensureTwosCompCapacity` prior to calling `add`.
22162276 ///
22172277 /// 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 {
22192279 try r.ensureTwosCompCapacity(bit_count);
22202280 var m = r.toMutable();
22212281 m.subSat(a, b, signedness, bit_count);
......@@ -2259,7 +2319,7 @@ pub const Managed = struct {
22592319 /// Returns an error if memory could not be allocated.
22602320 ///
22612321 /// 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 {
22632323 var alias_count: usize = 0;
22642324 if (rma.limbs.ptr == a.limbs.ptr)
22652325 alias_count += 1;
......@@ -2445,7 +2505,7 @@ pub const Managed = struct {
24452505 }
24462506
24472507 /// 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 {
24492509 try r.ensureCapacity(calcTwosCompLimbCount(bit_count));
24502510 var m = r.toMutable();
24512511 m.truncate(a, signedness, bit_count);
......@@ -2453,7 +2513,7 @@ pub const Managed = struct {
24532513 }
24542514
24552515 /// 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 {
24572517 try r.ensureCapacity(calcTwosCompLimbCount(bit_count));
24582518 var m = r.toMutable();
24592519 m.saturate(a, signedness, bit_count);
lib/std/special/compiler_rt.zig+49-53
......@@ -28,6 +28,52 @@ comptime {
2828 const __extendhftf2 = @import("compiler_rt/extendXfYf2.zig").__extendhftf2;
2929 @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
3177 if (!builtin.zig_is_stage2) {
3278 switch (arch) {
3379 .i386,
......@@ -46,59 +92,6 @@ comptime {
4692 // __clear_cache manages its own logic about whether to be exported or not.
4793 _ = @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
10295 const __unordsf2 = @import("compiler_rt/compareXf2.zig").__unordsf2;
10396 @export(__unordsf2, .{ .name = "__unordsf2", .linkage = linkage });
10497 const __unorddf2 = @import("compiler_rt/compareXf2.zig").__unorddf2;
......@@ -189,6 +182,9 @@ comptime {
189182
190183 const __truncsfhf2 = @import("compiler_rt/truncXfYf2.zig").__truncsfhf2;
191184 @export(__truncsfhf2, .{ .name = "__truncsfhf2", .linkage = linkage });
185 if (!is_test) {
186 @export(__truncsfhf2, .{ .name = "__gnu_f2h_ieee", .linkage = linkage });
187 }
192188 const __truncdfhf2 = @import("compiler_rt/truncXfYf2.zig").__truncdfhf2;
193189 @export(__truncdfhf2, .{ .name = "__truncdfhf2", .linkage = linkage });
194190 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) {
2121 const Unordered: GE = .Less;
2222};
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 {
2525 @setRuntimeSafety(builtin.is_test);
2626
2727 const bits = @typeInfo(T).Float.bits;
......@@ -32,7 +32,8 @@ pub fn cmp(comptime T: type, comptime RT: type, a: T, b: T) RT {
3232 const exponentBits = std.math.floatExponentBits(T);
3333 const signBit = (@as(rep_t, 1) << (significandBits + exponentBits));
3434 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
3738 const aInt = @bitCast(srep_t, a);
3839 const bInt = @bitCast(srep_t, b);
......@@ -46,20 +47,18 @@ pub fn cmp(comptime T: type, comptime RT: type, a: T, b: T) RT {
4647 if ((aAbs | bAbs) == 0) return .Equal;
4748
4849 // 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.
5051 if ((aInt & bInt) >= 0) {
5152 if (aInt < bInt) {
5253 return .Less;
5354 } else if (aInt == bInt) {
5455 return .Equal;
5556 } else return .Greater;
56 }
57
58 // Otherwise, both are negative, so we need to flip the sense of the
59 // comparison to get the correct result. (This assumes a twos- or ones-
60 // complement integer representation; if integers are represented in a
61 // sign-magnitude representation, then this flip is incorrect).
62 else {
57 } else {
58 // Otherwise, both are negative, so we need to flip the sense of the
59 // comparison to get the correct result. (This assumes a twos- or ones-
60 // complement integer representation; if integers are represented in a
61 // sign-magnitude representation, then this flip is incorrect).
6362 if (aInt > bInt) {
6463 return .Less;
6564 } else if (aInt == bInt) {
......@@ -68,7 +67,7 @@ pub fn cmp(comptime T: type, comptime RT: type, a: T, b: T) RT {
6867 }
6968}
7069
71pub fn unordcmp(comptime T: type, a: T, b: T) i32 {
70pub inline fn unordcmp(comptime T: type, a: T, b: T) i32 {
7271 @setRuntimeSafety(builtin.is_test);
7372
7473 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 {
8988
9089pub fn __lesf2(a: f32, b: f32) callconv(.C) i32 {
9190 @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);
9393}
9494
9595pub fn __gesf2(a: f32, b: f32) callconv(.C) i32 {
9696 @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);
9899}
99100
100101pub fn __eqsf2(a: f32, b: f32) callconv(.C) i32 {
......@@ -117,12 +118,14 @@ pub fn __gtsf2(a: f32, b: f32) callconv(.C) i32 {
117118
118119pub fn __ledf2(a: f64, b: f64) callconv(.C) i32 {
119120 @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);
121123}
122124
123125pub fn __gedf2(a: f64, b: f64) callconv(.C) i32 {
124126 @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);
126129}
127130
128131pub fn __eqdf2(a: f64, b: f64) callconv(.C) i32 {
......@@ -145,12 +148,14 @@ pub fn __gtdf2(a: f64, b: f64) callconv(.C) i32 {
145148
146149pub fn __letf2(a: f128, b: f128) callconv(.C) i32 {
147150 @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);
149153}
150154
151155pub fn __getf2(a: f128, b: f128) callconv(.C) i32 {
152156 @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);
154159}
155160
156161pub fn __eqtf2(a: f128, b: f128) callconv(.C) i32 {
......@@ -173,17 +178,17 @@ pub fn __gttf2(a: f128, b: f128) callconv(.C) i32 {
173178
174179pub fn __unordsf2(a: f32, b: f32) callconv(.C) i32 {
175180 @setRuntimeSafety(builtin.is_test);
176 return @call(.{ .modifier = .always_inline }, unordcmp, .{ f32, a, b });
181 return unordcmp(f32, a, b);
177182}
178183
179184pub fn __unorddf2(a: f64, b: f64) callconv(.C) i32 {
180185 @setRuntimeSafety(builtin.is_test);
181 return @call(.{ .modifier = .always_inline }, unordcmp, .{ f64, a, b });
186 return unordcmp(f64, a, b);
182187}
183188
184189pub fn __unordtf2(a: f128, b: f128) callconv(.C) i32 {
185190 @setRuntimeSafety(builtin.is_test);
186 return @call(.{ .modifier = .always_inline }, unordcmp, .{ f128, a, b });
191 return unordcmp(f128, a, b);
187192}
188193
189194// ARM EABI intrinsics
src/Sema.zig+10-8
......@@ -5064,7 +5064,7 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
50645064
50655065 const dest_type = try sema.resolveType(block, dest_ty_src, extra.lhs);
50665066 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);
50685068}
50695069
50705070fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -11016,7 +11016,7 @@ fn coerce(
1101611016
1101711017 const in_memory_result = coerceInMemoryAllowed(dest_type, inst_ty, false, target);
1101811018 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);
1102011020 }
1102111021
1102211022 // undefined to anything
......@@ -11439,18 +11439,20 @@ fn storePtrVal(
1143911439 }
1144011440}
1144111441
11442fn bitcast(
11442fn bitCast(
1144311443 sema: *Sema,
1144411444 block: *Block,
1144511445 dest_type: Type,
1144611446 inst: Air.Inst.Ref,
1144711447 inst_src: LazySrcLoc,
1144811448) CompileError!Air.Inst.Ref {
11449 // TODO validate the type size and other compile errors
1144911450 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {
11450 // Keep the comptime Value representation; take the new type.
11451 return sema.addConstant(dest_type, val);
11451 const target = sema.mod.getTarget();
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);
1145211455 }
11453 // TODO validate the type size and other compile errors
1145411456 try sema.requireRuntimeBlock(block, inst_src);
1145511457 return block.addTyOp(.bitcast, dest_type, inst);
1145611458}
......@@ -11482,7 +11484,7 @@ fn coerceArrayPtrToMany(
1148211484 return sema.addConstant(dest_type, val);
1148311485 }
1148411486 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);
1148611488}
1148711489
1148811490fn analyzeDeclVal(
......@@ -11571,7 +11573,7 @@ fn analyzeRef(
1157111573 try sema.storePtr(block, src, alloc, operand);
1157211574
1157311575 // 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);
1157511577}
1157611578
1157711579fn analyzeLoad(
src/codegen/llvm.zig+13-2
......@@ -1,4 +1,5 @@
11const std = @import("std");
2const builtin = @import("builtin");
23const assert = std.debug.assert;
34const Allocator = std.mem.Allocator;
45const Compilation = @import("../Compilation.zig");
......@@ -6,6 +7,7 @@ const llvm = @import("llvm/bindings.zig");
67const link = @import("../link.zig");
78const log = std.log.scoped(.codegen);
89const math = std.math;
10const native_endian = builtin.cpu.arch.endian();
911
1012const build_options = @import("build_options");
1113const Module = @import("../Module.zig");
......@@ -958,11 +960,20 @@ pub const DeclGen = struct {
958960 return llvm_int;
959961 },
960962 .Float => {
963 const llvm_ty = try self.llvmType(tv.ty);
961964 if (tv.ty.floatBits(self.module.getTarget()) <= 64) {
962 const llvm_ty = try self.llvmType(tv.ty);
963965 return llvm_ty.constReal(tv.val.toFloat(f64));
964966 }
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);
966977 },
967978 .Pointer => switch (tv.val.tag()) {
968979 .decl_ref => {
src/value.zig+112-10
......@@ -938,30 +938,132 @@ pub const Value = extern union {
938938
939939 pub fn toBool(self: Value) bool {
940940 return switch (self.tag()) {
941 .bool_true => true,
941 .bool_true, .one => true,
942942 .bool_false, .zero => false,
943943 else => unreachable,
944944 };
945945 }
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
9471029 /// Asserts that the value is a float or an integer.
948 pub fn toFloat(self: Value, comptime T: type) T {
949 return switch (self.tag()) {
950 .float_16 => @floatCast(T, self.castTag(.float_16).?.data),
951 .float_32 => @floatCast(T, self.castTag(.float_32).?.data),
952 .float_64 => @floatCast(T, self.castTag(.float_64).?.data),
953 .float_128 => @floatCast(T, self.castTag(.float_128).?.data),
1030 pub fn toFloat(val: Value, comptime T: type) T {
1031 return switch (val.tag()) {
1032 .float_16 => @floatCast(T, val.castTag(.float_16).?.data),
1033 .float_32 => @floatCast(T, val.castTag(.float_32).?.data),
1034 .float_64 => @floatCast(T, val.castTag(.float_64).?.data),
1035 .float_128 => @floatCast(T, val.castTag(.float_128).?.data),
9541036
9551037 .zero => 0,
9561038 .one => 1,
957 .int_u64 => @intToFloat(T, self.castTag(.int_u64).?.data),
958 .int_i64 => @intToFloat(T, self.castTag(.int_i64).?.data),
1039 .int_u64 => @intToFloat(T, val.castTag(.int_u64).?.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)),
9611044 else => unreachable,
9621045 };
9631046 }
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
9651067 pub fn clz(val: Value, ty: Type, target: Target) u64 {
9661068 const ty_bits = ty.intInfo(target).bits;
9671069 switch (val.tag()) {
test/behavior.zig+2-1
......@@ -5,6 +5,7 @@ test {
55 _ = @import("behavior/array.zig");
66 _ = @import("behavior/atomics.zig");
77 _ = @import("behavior/basic.zig");
8 _ = @import("behavior/bitcast.zig");
89 _ = @import("behavior/bool.zig");
910 _ = @import("behavior/bugs/655.zig");
1011 _ = @import("behavior/bugs/1277.zig");
......@@ -50,7 +51,7 @@ test {
5051 }
5152 _ = @import("behavior/await_struct.zig");
5253 _ = @import("behavior/bit_shifting.zig");
53 _ = @import("behavior/bitcast.zig");
54 _ = @import("behavior/bitcast_stage1.zig");
5455 _ = @import("behavior/bitreverse.zig");
5556 _ = @import("behavior/bugs/394.zig");
5657 _ = @import("behavior/bugs/421.zig");
test/behavior/bitcast.zig+1-154
......@@ -22,133 +22,6 @@ fn conv2(x: u32) i32 {
2222 return @bitCast(i32, x);
2323}
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
15225test "bitcast result to _" {
15326 _ = @bitCast(u8, @as(i8, 1));
15427}
......@@ -156,7 +29,7 @@ test "bitcast result to _" {
15629test "nested bitcast" {
15730 const S = struct {
15831 fn moo(x: isize) !void {
159 try @import("std").testing.expectEqual(@intCast(isize, 42), x);
32 try expect(@intCast(isize, 42) == x);
16033 }
16134
16235 fn foo(x: isize) !void {
......@@ -169,29 +42,3 @@ test "nested bitcast" {
16942 try S.foo(42);
17043 comptime try S.foo(42);
17144}
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}