authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-01 17:35:52-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-05-01 17:35:52-04:00
log3386bb896d071eef4ff571fac399e18b2270a382
treec3e597506a6f5a41269acdd386fd87bd473cdaa9
parent94b0d0e80242563f4ad7ad41e3c0f5193a60b70c
parentec6ef86219578822fd32bbe2e5eb83b24ddfdca6
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #5192 from ziglang/stage2-tests

add ZIR compare output test case to test suite

27 files changed, 5679 insertions(+), 3480 deletions(-)

build.zig+1-2
......@@ -44,7 +44,7 @@ pub fn build(b: *Builder) !void {
4444 try findAndReadConfigH(b);
4545
4646 var test_stage2 = b.addTest("src-self-hosted/test.zig");
47 test_stage2.setBuildMode(builtin.Mode.Debug);
47 test_stage2.setBuildMode(.Debug); // note this is only the mode of the test harness
4848 test_stage2.addPackagePath("stage2_tests", "test/stage2/test.zig");
4949
5050 const fmt_build_zig = b.addFmt(&[_][]const u8{"build.zig"});
......@@ -68,7 +68,6 @@ pub fn build(b: *Builder) !void {
6868 var ctx = parseConfigH(b, config_h_text);
6969 ctx.llvm = try findLLVM(b, ctx.llvm_config_exe);
7070
71 try configureStage2(b, test_stage2, ctx);
7271 try configureStage2(b, exe, ctx);
7372
7473 b.default_step.dependOn(&exe.step);
lib/std/child_process.zig+11-1
......@@ -46,6 +46,12 @@ pub const ChildProcess = struct {
4646
4747 /// Set to change the current working directory when spawning the child process.
4848 cwd: ?[]const u8,
49 /// Set to change the current working directory when spawning the child process.
50 /// This is not yet implemented for Windows. See https://github.com/ziglang/zig/issues/5190
51 /// Once that is done, `cwd` will be deprecated in favor of this field.
52 /// The directory handle must be opened with the ability to be passed
53 /// to a child process (no `O_CLOEXEC` flag on POSIX).
54 cwd_dir: ?fs.Dir = null,
4955
5056 err_pipe: if (builtin.os.tag == .windows) void else [2]os.fd_t,
5157
......@@ -183,6 +189,7 @@ pub const ChildProcess = struct {
183189 allocator: *mem.Allocator,
184190 argv: []const []const u8,
185191 cwd: ?[]const u8 = null,
192 cwd_dir: ?fs.Dir = null,
186193 env_map: ?*const BufMap = null,
187194 max_output_bytes: usize = 50 * 1024,
188195 expand_arg0: Arg0Expand = .no_expand,
......@@ -194,6 +201,7 @@ pub const ChildProcess = struct {
194201 child.stdout_behavior = .Pipe;
195202 child.stderr_behavior = .Pipe;
196203 child.cwd = args.cwd;
204 child.cwd_dir = args.cwd_dir;
197205 child.env_map = args.env_map;
198206 child.expand_arg0 = args.expand_arg0;
199207
......@@ -414,7 +422,9 @@ pub const ChildProcess = struct {
414422 os.close(stderr_pipe[1]);
415423 }
416424
417 if (self.cwd) |cwd| {
425 if (self.cwd_dir) |cwd| {
426 os.fchdir(cwd.fd) catch |err| forkChildErrReport(err_pipe[1], err);
427 } else if (self.cwd) |cwd| {
418428 os.chdir(cwd) catch |err| forkChildErrReport(err_pipe[1], err);
419429 }
420430
lib/std/fmt.zig+1-1
......@@ -1058,7 +1058,7 @@ pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) {
10581058 return value;
10591059}
10601060
1061fn digitToChar(digit: u8, uppercase: bool) u8 {
1061pub fn digitToChar(digit: u8, uppercase: bool) u8 {
10621062 return switch (digit) {
10631063 0...9 => digit + '0',
10641064 10...35 => digit + ((if (uppercase) @as(u8, 'A') else @as(u8, 'a')) - 10),
lib/std/fs.zig+22-4
......@@ -606,7 +606,8 @@ pub const Dir = struct {
606606 } else 0;
607607
608608 const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0;
609 const os_flags = lock_flag | O_LARGEFILE | os.O_CLOEXEC | if (flags.write and flags.read)
609 const O_CLOEXEC: u32 = if (flags.share_with_child_process) 0 else os.O_CLOEXEC;
610 const os_flags = lock_flag | O_LARGEFILE | O_CLOEXEC | if (flags.write and flags.read)
610611 @as(u32, os.O_RDWR)
611612 else if (flags.write)
612613 @as(u32, os.O_WRONLY)
......@@ -689,7 +690,8 @@ pub const Dir = struct {
689690 } else 0;
690691
691692 const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0;
692 const os_flags = lock_flag | O_LARGEFILE | os.O_CREAT | os.O_CLOEXEC |
693 const O_CLOEXEC: u32 = if (flags.share_with_child_process) 0 else os.O_CLOEXEC;
694 const os_flags = lock_flag | O_LARGEFILE | os.O_CREAT | O_CLOEXEC |
693695 (if (flags.truncate) @as(u32, os.O_TRUNC) else 0) |
694696 (if (flags.read) @as(u32, os.O_RDWR) else os.O_WRONLY) |
695697 (if (flags.exclusive) @as(u32, os.O_EXCL) else 0);
......@@ -787,6 +789,15 @@ pub const Dir = struct {
787789 }
788790 }
789791
792 /// This function performs `makePath`, followed by `openDir`.
793 /// If supported by the OS, this operation is atomic. It is not atomic on
794 /// all operating systems.
795 pub fn makeOpenPath(self: Dir, sub_path: []const u8, open_dir_options: OpenDirOptions) !Dir {
796 // TODO improve this implementation on Windows; we can avoid 1 call to NtClose
797 try self.makePath(sub_path);
798 return self.openDir(sub_path, open_dir_options);
799 }
800
790801 /// Changes the current working directory to the open directory handle.
791802 /// This modifies global state and can have surprising effects in multi-
792803 /// threaded applications. Most applications and especially libraries should
......@@ -807,6 +818,11 @@ pub const Dir = struct {
807818 /// `true` means the opened directory can be scanned for the files and sub-directories
808819 /// of the result. It means the `iterate` function can be called.
809820 iterate: bool = false,
821
822 /// `true` means the opened directory can be passed to a child process.
823 /// `false` means the directory handle is considered to be closed when a child
824 /// process is spawned. This corresponds to the inverse of `O_CLOEXEC` on POSIX.
825 share_with_child_process: bool = false,
810826 };
811827
812828 /// Opens a directory at the given path. The directory is a system resource that remains
......@@ -832,9 +848,11 @@ pub const Dir = struct {
832848 return self.openDirW(&sub_path_w, args);
833849 } else if (!args.iterate) {
834850 const O_PATH = if (@hasDecl(os, "O_PATH")) os.O_PATH else 0;
835 return self.openDirFlagsZ(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | os.O_CLOEXEC | O_PATH);
851 const O_CLOEXEC: u32 = if (args.share_with_child_process) 0 else os.O_CLOEXEC;
852 return self.openDirFlagsZ(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | O_CLOEXEC | O_PATH);
836853 } else {
837 return self.openDirFlagsZ(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | os.O_CLOEXEC);
854 const O_CLOEXEC: u32 = if (args.share_with_child_process) 0 else os.O_CLOEXEC;
855 return self.openDirFlagsZ(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | O_CLOEXEC);
838856 }
839857 }
840858
lib/std/fs/file.zig+10
......@@ -69,6 +69,11 @@ pub const File = struct {
6969 /// It allows the use of `noasync` when calling functions related to opening
7070 /// the file, reading, and writing.
7171 always_blocking: bool = false,
72
73 /// `true` means the opened directory can be passed to a child process.
74 /// `false` means the directory handle is considered to be closed when a child
75 /// process is spawned. This corresponds to the inverse of `O_CLOEXEC` on POSIX.
76 share_with_child_process: bool = false,
7277 };
7378
7479 /// TODO https://github.com/ziglang/zig/issues/3802
......@@ -107,6 +112,11 @@ pub const File = struct {
107112 /// For POSIX systems this is the file system mode the file will
108113 /// be created with.
109114 mode: Mode = default_mode,
115
116 /// `true` means the opened directory can be passed to a child process.
117 /// `false` means the directory handle is considered to be closed when a child
118 /// process is spawned. This corresponds to the inverse of `O_CLOEXEC` on POSIX.
119 share_with_child_process: bool = false,
110120 };
111121
112122 /// Upon success, the stream is in an uninitialized state. To continue using it,
lib/std/math.zig+37
......@@ -986,6 +986,43 @@ pub const Order = enum {
986986
987987 /// Greater than (`>`)
988988 gt,
989
990 pub fn invert(self: Order) Order {
991 return switch (self) {
992 .lt => .gt,
993 .eq => .eq,
994 .gt => .gt,
995 };
996 }
997
998 pub fn compare(self: Order, op: CompareOperator) bool {
999 return switch (self) {
1000 .lt => switch (op) {
1001 .lt => true,
1002 .lte => true,
1003 .eq => false,
1004 .gte => false,
1005 .gt => false,
1006 .neq => true,
1007 },
1008 .eq => switch (op) {
1009 .lt => false,
1010 .lte => true,
1011 .eq => true,
1012 .gte => true,
1013 .gt => false,
1014 .neq => false,
1015 },
1016 .gt => switch (op) {
1017 .lt => false,
1018 .lte => false,
1019 .eq => false,
1020 .gte => true,
1021 .gt => true,
1022 .neq => true,
1023 },
1024 };
1025 }
9891026};
9901027
9911028/// Given two numbers, this function returns the order they are with respect to each other.
lib/std/math/big.zig+22-5
......@@ -1,7 +1,24 @@
1pub usingnamespace @import("big/int.zig");
2pub usingnamespace @import("big/rational.zig");
1const std = @import("../std.zig");
2const assert = std.debug.assert;
33
4test "math.big" {
5 _ = @import("big/int.zig");
6 _ = @import("big/rational.zig");
4pub const Rational = @import("big/rational.zig").Rational;
5pub const int = @import("big/int.zig");
6pub const Limb = usize;
7pub const DoubleLimb = std.meta.IntType(false, 2 * Limb.bit_count);
8pub const SignedDoubleLimb = std.meta.IntType(true, DoubleLimb.bit_count);
9pub const Log2Limb = std.math.Log2Int(Limb);
10
11comptime {
12 assert(std.math.floorPowerOfTwo(usize, Limb.bit_count) == Limb.bit_count);
13 assert(Limb.bit_count <= 64); // u128 set is unsupported
14 assert(Limb.is_signed == false);
15}
16
17test "" {
18 _ = int;
19 _ = Rational;
20 _ = Limb;
21 _ = DoubleLimb;
22 _ = SignedDoubleLimb;
23 _ = Log2Limb;
724}
lib/std/math/big/int.zig+1681-2497
......@@ -1,293 +1,196 @@
11const std = @import("../../std.zig");
2const debug = std.debug;
3const testing = std.testing;
42const math = std.math;
3const Limb = std.math.big.Limb;
4const DoubleLimb = std.math.big.DoubleLimb;
5const SignedDoubleLimb = std.math.big.SignedDoubleLimb;
6const Log2Limb = std.math.big.Log2Limb;
7const Allocator = std.mem.Allocator;
58const mem = std.mem;
6const Allocator = mem.Allocator;
7const ArrayList = std.ArrayList;
89const maxInt = std.math.maxInt;
910const minInt = std.math.minInt;
11const assert = std.debug.assert;
1012
11pub const Limb = usize;
12pub const DoubleLimb = std.meta.Int(false, 2 * Limb.bit_count);
13pub const SignedDoubleLimb = std.meta.Int(true, DoubleLimb.bit_count);
14pub const Log2Limb = math.Log2Int(Limb);
13/// Returns the number of limbs needed to store `scalar`, which must be a
14/// primitive integer value.
15pub fn calcLimbLen(scalar: var) usize {
16 const T = @TypeOf(scalar);
17 switch (@typeInfo(T)) {
18 .Int => |info| {
19 const UT = if (info.is_signed) std.meta.Int(false, info.bits - 1) else T;
20 return @sizeOf(UT) / @sizeOf(Limb);
21 },
22 .ComptimeInt => {
23 const w_value = if (scalar < 0) -scalar else scalar;
24 return @divFloor(math.log2(w_value), Limb.bit_count) + 1;
25 },
26 else => @compileError("parameter must be a primitive integer type"),
27 }
28}
1529
16comptime {
17 debug.assert(math.floorPowerOfTwo(usize, Limb.bit_count) == Limb.bit_count);
18 debug.assert(Limb.bit_count <= 64); // u128 set is unsupported
19 debug.assert(Limb.is_signed == false);
30pub fn calcToStringLimbsBufferLen(a_len: usize, base: u8) usize {
31 if (math.isPowerOfTwo(base))
32 return 0;
33 return a_len + 2 + a_len + calcDivLimbsBufferLen(a_len, 1);
2034}
2135
22/// An arbitrary-precision big integer.
23///
24/// Memory is allocated by an Int as needed to ensure operations never overflow. The range of an
25/// Int is bounded only by available memory.
26pub const Int = struct {
27 const sign_bit: usize = 1 << (usize.bit_count - 1);
36pub fn calcDivLimbsBufferLen(a_len: usize, b_len: usize) usize {
37 return calcMulLimbsBufferLen(a_len, b_len, 2) * 4;
38}
2839
29 /// Default number of limbs to allocate on creation of an Int.
30 pub const default_capacity = 4;
40pub fn calcMulLimbsBufferLen(a_len: usize, b_len: usize, aliases: usize) usize {
41 return aliases * math.max(a_len, b_len);
42}
43
44pub fn calcSetStringLimbsBufferLen(base: u8, string_len: usize) usize {
45 const limb_count = calcSetStringLimbCount(base, string_len);
46 return calcMulLimbsBufferLen(limb_count, limb_count, 2);
47}
3148
32 /// Allocator used by the Int when requesting memory.
33 allocator: ?*Allocator,
49pub fn calcSetStringLimbCount(base: u8, string_len: usize) usize {
50 return (string_len + (Limb.bit_count / base - 1)) / (Limb.bit_count / base);
51}
52
53/// a + b * c + *carry, sets carry to the overflow bits
54pub fn addMulLimbWithCarry(a: Limb, b: Limb, c: Limb, carry: *Limb) Limb {
55 @setRuntimeSafety(false);
56 var r1: Limb = undefined;
57
58 // r1 = a + *carry
59 const c1: Limb = @boolToInt(@addWithOverflow(Limb, a, carry.*, &r1));
60
61 // r2 = b * c
62 const bc = @as(DoubleLimb, math.mulWide(Limb, b, c));
63 const r2 = @truncate(Limb, bc);
64 const c2 = @truncate(Limb, bc >> Limb.bit_count);
65
66 // r1 = r1 + r2
67 const c3: Limb = @boolToInt(@addWithOverflow(Limb, r1, r2, &r1));
68
69 // This never overflows, c1, c3 are either 0 or 1 and if both are 1 then
70 // c2 is at least <= maxInt(Limb) - 2.
71 carry.* = c1 + c2 + c3;
72
73 return r1;
74}
3475
76/// A arbitrary-precision big integer, with a fixed set of mutable limbs.
77pub const Mutable = struct {
3578 /// Raw digits. These are:
3679 ///
3780 /// * Little-endian ordered
3881 /// * limbs.len >= 1
39 /// * Zero is represent as Int.len() == 1 with limbs[0] == 0.
82 /// * Zero is represented as limbs.len == 1 with limbs[0] == 0.
4083 ///
4184 /// Accessing limbs directly should be avoided.
85 /// These are allocated limbs; the `len` field tells the valid range.
4286 limbs: []Limb,
87 len: usize,
88 positive: bool,
4389
44 /// High bit is the sign bit. If set, Int is negative, else Int is positive.
45 /// The remaining bits represent the number of limbs used by Int.
46 metadata: usize,
47
48 /// Creates a new Int. default_capacity limbs will be allocated immediately.
49 /// Int will be zeroed.
50 pub fn init(allocator: *Allocator) !Int {
51 return try Int.initCapacity(allocator, default_capacity);
52 }
53
54 /// Creates a new Int. Int will be set to `value`.
55 ///
56 /// This is identical to an `init`, followed by a `set`.
57 pub fn initSet(allocator: *Allocator, value: var) !Int {
58 var s = try Int.init(allocator);
59 try s.set(value);
60 return s;
90 pub fn toConst(self: Mutable) Const {
91 return .{
92 .limbs = self.limbs[0..self.len],
93 .positive = self.positive,
94 };
6195 }
6296
63 /// Creates a new Int with a specific capacity. If capacity < default_capacity then the
64 /// default capacity will be used instead.
65 pub fn initCapacity(allocator: *Allocator, capacity: usize) !Int {
66 return Int{
97 /// Asserts that the allocator owns the limbs memory. If this is not the case,
98 /// use `toConst().toManaged()`.
99 pub fn toManaged(self: Mutable, allocator: *Allocator) Managed {
100 return .{
67101 .allocator = allocator,
68 .metadata = 1,
69 .limbs = block: {
70 var limbs = try allocator.alloc(Limb, math.max(default_capacity, capacity));
71 limbs[0] = 0;
72 break :block limbs;
73 },
102 .limbs = limbs,
103 .metadata = if (self.positive)
104 self.len & ~Managed.sign_bit
105 else
106 self.len | Managed.sign_bit,
74107 };
75108 }
76109
77 /// Returns the number of limbs currently in use.
78 pub fn len(self: Int) usize {
79 return self.metadata & ~sign_bit;
80 }
81
82 /// Returns whether an Int is positive.
83 pub fn isPositive(self: Int) bool {
84 return self.metadata & sign_bit == 0;
85 }
86
87 /// Sets the sign of an Int.
88 pub fn setSign(self: *Int, positive: bool) void {
89 if (positive) {
90 self.metadata &= ~sign_bit;
91 } else {
92 self.metadata |= sign_bit;
93 }
94 }
95
96 /// Sets the length of an Int.
97 ///
98 /// If setLen is used, then the Int must be normalized to suit.
99 pub fn setLen(self: *Int, new_len: usize) void {
100 self.metadata &= sign_bit;
101 self.metadata |= new_len;
102 }
103
104 /// Returns an Int backed by a fixed set of limb values.
105 /// This is read-only and cannot be used as a result argument. If the Int tries to allocate
106 /// memory a runtime panic will occur.
107 pub fn initFixed(limbs: []const Limb) Int {
108 var self = Int{
109 .allocator = null,
110 .metadata = limbs.len,
111 // Cast away the const, invalid use to pass as a pointer argument.
112 .limbs = @intToPtr([*]Limb, @ptrToInt(limbs.ptr))[0..limbs.len],
110 /// `value` is a primitive integer type.
111 /// Asserts the value fits within the provided `limbs_buffer`.
112 /// Note: `calcLimbLen` can be used to figure out how big an array to allocate for `limbs_buffer`.
113 pub fn init(limbs_buffer: []Limb, value: var) Mutable {
114 limbs_buffer[0] = 0;
115 var self: Mutable = .{
116 .limbs = limbs_buffer,
117 .len = 1,
118 .positive = true,
113119 };
114
115 self.normalize(limbs.len);
120 self.set(value);
116121 return self;
117122 }
118123
119 /// Ensures an Int has enough space allocated for capacity limbs. If the Int does not have
120 /// sufficient capacity, the exact amount will be allocated. This occurs even if the requested
121 /// capacity is only greater than the current capacity by one limb.
122 pub fn ensureCapacity(self: *Int, capacity: usize) !void {
123 self.assertWritable();
124 if (capacity <= self.limbs.len) {
125 return;
126 }
127
128 self.limbs = try self.allocator.?.realloc(self.limbs, capacity);
129 }
130
131 fn assertWritable(self: Int) void {
132 if (self.allocator == null) {
133 @panic("provided Int value is read-only but must be writable");
134 }
135 }
136
137 /// Frees all memory associated with an Int.
138 pub fn deinit(self: Int) void {
139 self.assertWritable();
140 self.allocator.?.free(self.limbs);
141 }
142
143 /// Clones an Int and returns a new Int with the same value. The new Int is a deep copy and
144 /// can be modified separately from the original.
145 pub fn clone(other: Int) !Int {
146 return other.clone2(other.allocator.?);
147 }
148
149 pub fn clone2(other: Int, allocator: *Allocator) !Int {
150 return Int{
151 .allocator = allocator,
152 .metadata = other.metadata,
153 .limbs = block: {
154 var limbs = try allocator.alloc(Limb, other.len());
155 mem.copy(Limb, limbs[0..], other.limbs[0..other.len()]);
156 break :block limbs;
157 },
158 };
159 }
160
161 /// Copies the value of an Int to an existing Int so that they both have the same value.
162 /// Extra memory will be allocated if the receiver does not have enough capacity.
163 pub fn copy(self: *Int, other: Int) !void {
164 self.assertWritable();
165 if (self.limbs.ptr == other.limbs.ptr) {
166 return;
124 /// Copies the value of a Const to an existing Mutable so that they both have the same value.
125 /// Asserts the value fits in the limbs buffer.
126 pub fn copy(self: *Mutable, other: Const) void {
127 if (self.limbs.ptr != other.limbs.ptr) {
128 mem.copy(Limb, self.limbs[0..], other.limbs[0..other.limbs.len]);
167129 }
168
169 try self.ensureCapacity(other.len());
170 mem.copy(Limb, self.limbs[0..], other.limbs[0..other.len()]);
171 self.metadata = other.metadata;
130 self.positive = other.positive;
131 self.len = other.limbs.len;
172132 }
173133
174 /// Efficiently swap an Int with another. This swaps the limb pointers and a full copy is not
134 /// Efficiently swap an Mutable with another. This swaps the limb pointers and a full copy is not
175135 /// performed. The address of the limbs field will not be the same after this function.
176 pub fn swap(self: *Int, other: *Int) void {
177 self.assertWritable();
178 mem.swap(Int, self, other);
179 }
180
181 pub fn dump(self: Int) void {
182 for (self.limbs) |limb| {
183 debug.warn("{x} ", .{limb});
184 }
185 debug.warn("\n", .{});
186 }
187
188 /// Negate the sign of an Int.
189 pub fn negate(self: *Int) void {
190 self.metadata ^= sign_bit;
191 }
192
193 /// Make an Int positive.
194 pub fn abs(self: *Int) void {
195 self.metadata &= ~sign_bit;
196 }
197
198 /// Returns true if an Int is odd.
199 pub fn isOdd(self: Int) bool {
200 return self.limbs[0] & 1 != 0;
201 }
202
203 /// Returns true if an Int is even.
204 pub fn isEven(self: Int) bool {
205 return !self.isOdd();
206 }
207
208 /// Returns the number of bits required to represent the absolute value an Int.
209 fn bitCountAbs(self: Int) usize {
210 return (self.len() - 1) * Limb.bit_count + (Limb.bit_count - @clz(Limb, self.limbs[self.len() - 1]));
136 pub fn swap(self: *Mutable, other: *Mutable) void {
137 mem.swap(Mutable, self, other);
211138 }
212139
213 /// Returns the number of bits required to represent the integer in twos-complement form.
214 ///
215 /// If the integer is negative the value returned is the number of bits needed by a signed
216 /// integer to represent the value. If positive the value is the number of bits for an
217 /// unsigned integer. Any unsigned integer will fit in the signed integer with bitcount
218 /// one greater than the returned value.
219 ///
220 /// e.g. -127 returns 8 as it will fit in an i8. 127 returns 7 since it fits in a u7.
221 fn bitCountTwosComp(self: Int) usize {
222 var bits = self.bitCountAbs();
223
224 // If the entire value has only one bit set (e.g. 0b100000000) then the negation in twos
225 // complement requires one less bit.
226 if (!self.isPositive()) block: {
227 bits += 1;
228
229 if (@popCount(Limb, self.limbs[self.len() - 1]) == 1) {
230 for (self.limbs[0 .. self.len() - 1]) |limb| {
231 if (@popCount(Limb, limb) != 0) {
232 break :block;
233 }
234 }
235
236 bits -= 1;
237 }
140 pub fn dump(self: Mutable) void {
141 for (self.limbs[0..self.len]) |limb| {
142 std.debug.warn("{x} ", .{limb});
238143 }
239
240 return bits;
144 std.debug.warn("capacity={} positive={}\n", .{ self.limbs.len, self.positive });
241145 }
242146
243 pub fn fitsInTwosComp(self: Int, is_signed: bool, bit_count: usize) bool {
244 if (self.eqZero()) {
245 return true;
246 }
247 if (!is_signed and !self.isPositive()) {
248 return false;
249 }
250
251 const req_bits = self.bitCountTwosComp() + @boolToInt(self.isPositive() and is_signed);
252 return bit_count >= req_bits;
147 /// Clones an Mutable and returns a new Mutable with the same value. The new Mutable is a deep copy and
148 /// can be modified separately from the original.
149 /// Asserts that limbs is big enough to store the value.
150 pub fn clone(other: Mutable, limbs: []Limb) Mutable {
151 mem.copy(Limb, limbs, other.limbs[0..other.len]);
152 return .{
153 .limbs = limbs,
154 .len = other.len,
155 .positive = other.positive,
156 };
253157 }
254158
255 /// Returns whether self can fit into an integer of the requested type.
256 pub fn fits(self: Int, comptime T: type) bool {
257 return self.fitsInTwosComp(T.is_signed, T.bit_count);
159 pub fn negate(self: *Mutable) void {
160 self.positive = !self.positive;
258161 }
259162
260 /// Returns the approximate size of the integer in the given base. Negative values accommodate for
261 /// the minus sign. This is used for determining the number of characters needed to print the
262 /// value. It is inexact and may exceed the given value by ~1-2 bytes.
263 pub fn sizeInBase(self: Int, base: usize) usize {
264 const bit_count = @as(usize, @boolToInt(!self.isPositive())) + self.bitCountAbs();
265 return (bit_count / math.log2(base)) + 1;
163 /// Modify to become the absolute value
164 pub fn abs(self: *Mutable) void {
165 self.positive = true;
266166 }
267167
268 /// Sets an Int to value. Value must be an primitive integer type.
269 pub fn set(self: *Int, value: var) Allocator.Error!void {
270 self.assertWritable();
168 /// Sets the Mutable to value. Value must be an primitive integer type.
169 /// Asserts the value fits within the limbs buffer.
170 /// Note: `calcLimbLen` can be used to figure out how big the limbs buffer
171 /// needs to be to store a specific value.
172 pub fn set(self: *Mutable, value: var) void {
271173 const T = @TypeOf(value);
272174
273175 switch (@typeInfo(T)) {
274176 .Int => |info| {
275177 const UT = if (T.is_signed) std.meta.Int(false, T.bit_count - 1) else T;
276178
277 try self.ensureCapacity(@sizeOf(UT) / @sizeOf(Limb));
278 self.metadata = 0;
279 self.setSign(value >= 0);
179 const needed_limbs = @sizeOf(UT) / @sizeOf(Limb);
180 assert(needed_limbs <= self.limbs.len); // value too big
181 self.len = 0;
182 self.positive = value >= 0;
280183
281184 var w_value: UT = if (value < 0) @intCast(UT, -value) else @intCast(UT, value);
282185
283186 if (info.bits <= Limb.bit_count) {
284187 self.limbs[0] = @as(Limb, w_value);
285 self.metadata += 1;
188 self.len += 1;
286189 } else {
287190 var i: usize = 0;
288191 while (w_value != 0) : (i += 1) {
289192 self.limbs[i] = @truncate(Limb, w_value);
290 self.metadata += 1;
193 self.len += 1;
291194
292195 // TODO: shift == 64 at compile-time fails. Fails on u128 limbs.
293196 w_value >>= Limb.bit_count / 2;
......@@ -299,10 +202,10 @@ pub const Int = struct {
299202 comptime var w_value = if (value < 0) -value else value;
300203
301204 const req_limbs = @divFloor(math.log2(w_value), Limb.bit_count) + 1;
302 try self.ensureCapacity(req_limbs);
205 assert(req_limbs <= self.limbs.len); // value too big
303206
304 self.metadata = req_limbs;
305 self.setSign(value >= 0);
207 self.len = req_limbs;
208 self.positive = value >= 0;
306209
307210 if (w_value <= maxInt(Limb)) {
308211 self.limbs[0] = w_value;
......@@ -318,98 +221,35 @@ pub const Int = struct {
318221 }
319222 }
320223 },
321 else => {
322 @compileError("cannot set Int using type " ++ @typeName(T));
323 },
324 }
325 }
326
327 pub const ConvertError = error{
328 NegativeIntoUnsigned,
329 TargetTooSmall,
330 };
331
332 /// Convert self to type T.
333 ///
334 /// Returns an error if self cannot be narrowed into the requested type without truncation.
335 pub fn to(self: Int, comptime T: type) ConvertError!T {
336 switch (@typeInfo(T)) {
337 .Int => {
338 const UT = std.meta.Int(false, T.bit_count);
339
340 if (self.bitCountTwosComp() > T.bit_count) {
341 return error.TargetTooSmall;
342 }
343
344 var r: UT = 0;
345
346 if (@sizeOf(UT) <= @sizeOf(Limb)) {
347 r = @intCast(UT, self.limbs[0]);
348 } else {
349 for (self.limbs[0..self.len()]) |_, ri| {
350 const limb = self.limbs[self.len() - ri - 1];
351 r <<= Limb.bit_count;
352 r |= limb;
353 }
354 }
355
356 if (!T.is_signed) {
357 return if (self.isPositive()) @intCast(T, r) else error.NegativeIntoUnsigned;
358 } else {
359 if (self.isPositive()) {
360 return @intCast(T, r);
361 } else {
362 if (math.cast(T, r)) |ok| {
363 return -ok;
364 } else |_| {
365 return minInt(T);
366 }
367 }
368 }
369 },
370 else => {
371 @compileError("cannot convert Int to type " ++ @typeName(T));
372 },
224 else => @compileError("cannot set Mutable using type " ++ @typeName(T)),
373225 }
374226 }
375227
376 fn charToDigit(ch: u8, base: u8) !u8 {
377 const d = switch (ch) {
378 '0'...'9' => ch - '0',
379 'a'...'f' => (ch - 'a') + 0xa,
380 'A'...'F' => (ch - 'A') + 0xa,
381 else => return error.InvalidCharForDigit,
382 };
383
384 return if (d < base) d else return error.DigitTooLargeForBase;
385 }
386
387 fn digitToChar(d: u8, base: u8, uppercase: bool) !u8 {
388 if (d >= base) {
389 return error.DigitTooLargeForBase;
390 }
391
392 const a: u8 = if (uppercase) 'A' else 'a';
393 return switch (d) {
394 0...9 => '0' + d,
395 0xa...0xf => (a - 0xa) + d,
396 else => unreachable,
397 };
398 }
399
400228 /// Set self from the string representation `value`.
401229 ///
402230 /// `value` must contain only digits <= `base` and is case insensitive. Base prefixes are
403231 /// not allowed (e.g. 0x43 should simply be 43). Underscores in the input string are
404232 /// ignored and can be used as digit separators.
405233 ///
406 /// Returns an error if memory could not be allocated or `value` has invalid digits for the
407 /// requested base.
408 pub fn setString(self: *Int, base: u8, value: []const u8) !void {
409 self.assertWritable();
410 if (base < 2 or base > 16) {
411 return error.InvalidBase;
412 }
234 /// Asserts there is enough memory for the value in `self.limbs`. An upper bound on number of limbs can
235 /// be determined with `calcSetStringLimbCount`.
236 /// Asserts the base is in the range [2, 16].
237 ///
238 /// Returns an error if the value has invalid digits for the requested base.
239 ///
240 /// `limbs_buffer` is used for temporary storage. The size required can be found with
241 /// `calcSetStringLimbsBufferLen`.
242 ///
243 /// If `allocator` is provided, it will be used for temporary storage to improve
244 /// multiplication performance. `error.OutOfMemory` is handled with a fallback algorithm.
245 pub fn setString(
246 self: *Mutable,
247 base: u8,
248 value: []const u8,
249 limbs_buffer: []Limb,
250 allocator: ?*Allocator,
251 ) error{InvalidCharacter}!void {
252 assert(base >= 2 and base <= 16);
413253
414254 var i: usize = 0;
415255 var positive = true;
......@@ -418,753 +258,561 @@ pub const Int = struct {
418258 i += 1;
419259 }
420260
421 const ap_base = Int.initFixed(([_]Limb{base})[0..]);
422 try self.set(0);
261 const ap_base: Const = .{ .limbs = &[_]Limb{base}, .positive = true };
262 self.set(0);
423263
424264 for (value[i..]) |ch| {
425265 if (ch == '_') {
426266 continue;
427267 }
428 const d = try charToDigit(ch, base);
429
430 const ap_d = Int.initFixed(([_]Limb{d})[0..]);
268 const d = try std.fmt.charToDigit(ch, base);
269 const ap_d: Const = .{ .limbs = &[_]Limb{d}, .positive = true };
431270
432 try self.mul(self.*, ap_base);
433 try self.add(self.*, ap_d);
271 self.mul(self.toConst(), ap_base, limbs_buffer, allocator);
272 self.add(self.toConst(), ap_d);
434273 }
435 self.setSign(positive);
274 self.positive = positive;
436275 }
437276
438 /// Converts self to a string in the requested base. Memory is allocated from the provided
439 /// allocator and not the one present in self.
440 /// TODO make this call format instead of the other way around
441 pub fn toString(self: Int, allocator: *Allocator, base: u8, uppercase: bool) ![]const u8 {
442 if (base < 2 or base > 16) {
443 return error.InvalidBase;
444 }
445
446 var digits = ArrayList(u8).init(allocator);
447 try digits.ensureCapacity(self.sizeInBase(base) + 1);
448 defer digits.deinit();
277 /// r = a + scalar
278 ///
279 /// r and a may be aliases.
280 /// scalar is a primitive integer type.
281 ///
282 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
283 /// r is `math.max(a.limbs.len, calcLimbLen(scalar)) + 1`.
284 pub fn addScalar(r: *Mutable, a: Const, scalar: var) void {
285 var limbs: [calcLimbLen(scalar)]Limb = undefined;
286 const operand = init(&limbs, scalar).toConst();
287 return add(r, a, operand);
288 }
449289
450 if (self.eqZero()) {
451 try digits.append('0');
452 return digits.toOwnedSlice();
290 /// r = a + b
291 ///
292 /// r, a and b may be aliases.
293 ///
294 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
295 /// r is `math.max(a.limbs.len, b.limbs.len) + 1`.
296 pub fn add(r: *Mutable, a: Const, b: Const) void {
297 if (a.eqZero()) {
298 r.copy(b);
299 return;
300 } else if (b.eqZero()) {
301 r.copy(a);
302 return;
453303 }
454304
455 // Power of two: can do a single pass and use masks to extract digits.
456 if (math.isPowerOfTwo(base)) {
457 const base_shift = math.log2_int(Limb, base);
458
459 for (self.limbs[0..self.len()]) |limb| {
460 var shift: usize = 0;
461 while (shift < Limb.bit_count) : (shift += base_shift) {
462 const r = @intCast(u8, (limb >> @intCast(Log2Limb, shift)) & @as(Limb, base - 1));
463 const ch = try digitToChar(r, base, uppercase);
464 try digits.append(ch);
465 }
305 if (a.limbs.len == 1 and b.limbs.len == 1 and a.positive == b.positive) {
306 if (!@addWithOverflow(Limb, a.limbs[0], b.limbs[0], &r.limbs[0])) {
307 r.len = 1;
308 r.positive = a.positive;
309 return;
466310 }
311 }
467312
468 while (true) {
469 // always will have a non-zero digit somewhere
470 const c = digits.pop();
471 if (c != '0') {
472 digits.append(c) catch unreachable;
473 break;
474 }
313 if (a.positive != b.positive) {
314 if (a.positive) {
315 // (a) + (-b) => a - b
316 r.sub(a, b.abs());
317 } else {
318 // (-a) + (b) => b - a
319 r.sub(b, a.abs());
475320 }
476321 } else {
477 // Non power-of-two: batch divisions per word size.
478 const digits_per_limb = math.log(Limb, base, maxInt(Limb));
479 var limb_base: Limb = 1;
480 var j: usize = 0;
481 while (j < digits_per_limb) : (j += 1) {
482 limb_base *= base;
322 if (a.limbs.len >= b.limbs.len) {
323 lladd(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]);
324 r.normalize(a.limbs.len + 1);
325 } else {
326 lladd(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]);
327 r.normalize(b.limbs.len + 1);
483328 }
484329
485 var q = try self.clone2(allocator);
486 defer q.deinit();
487 q.abs();
488 var r = try Int.init(allocator);
489 defer r.deinit();
490 var b = try Int.initSet(allocator, limb_base);
491 defer b.deinit();
492
493 while (q.len() >= 2) {
494 try Int.divTrunc(&q, &r, q, b);
330 r.positive = a.positive;
331 }
332 }
495333
496 var r_word = r.limbs[0];
497 var i: usize = 0;
498 while (i < digits_per_limb) : (i += 1) {
499 const ch = try digitToChar(@intCast(u8, r_word % base), base, uppercase);
500 r_word /= base;
501 try digits.append(ch);
334 /// r = a - b
335 ///
336 /// r, a and b may be aliases.
337 ///
338 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
339 /// r is `math.max(a.limbs.len, b.limbs.len) + 1`. The +1 is not needed if both operands are positive.
340 pub fn sub(r: *Mutable, a: Const, b: Const) void {
341 if (a.positive != b.positive) {
342 if (a.positive) {
343 // (a) - (-b) => a + b
344 r.add(a, b.abs());
345 } else {
346 // (-a) - (b) => -(a + b)
347 r.add(a.abs(), b);
348 r.positive = false;
349 }
350 } else {
351 if (a.positive) {
352 // (a) - (b) => a - b
353 if (a.order(b) != .lt) {
354 llsub(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]);
355 r.normalize(a.limbs.len);
356 r.positive = true;
357 } else {
358 llsub(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]);
359 r.normalize(b.limbs.len);
360 r.positive = false;
361 }
362 } else {
363 // (-a) - (-b) => -(a - b)
364 if (a.order(b) == .lt) {
365 llsub(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]);
366 r.normalize(a.limbs.len);
367 r.positive = false;
368 } else {
369 llsub(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]);
370 r.normalize(b.limbs.len);
371 r.positive = true;
502372 }
503373 }
374 }
375 }
504376
505 {
506 debug.assert(q.len() == 1);
507
508 var r_word = q.limbs[0];
509 while (r_word != 0) {
510 const ch = try digitToChar(@intCast(u8, r_word % base), base, uppercase);
511 r_word /= base;
512 try digits.append(ch);
513 }
514 }
515 }
516
517 if (!self.isPositive()) {
518 try digits.append('-');
519 }
520
521 var s = digits.toOwnedSlice();
522 mem.reverse(u8, s);
523 return s;
524 }
377 /// rma = a * b
378 ///
379 /// `rma` may alias with `a` or `b`.
380 /// `a` and `b` may alias with each other.
381 ///
382 /// Asserts the result fits in `rma`. An upper bound on the number of limbs needed by
383 /// rma is given by `a.limbs.len + b.limbs.len + 1`.
384 ///
385 /// `limbs_buffer` is used for temporary storage. The amount required is given by `calcMulLimbsBufferLen`.
386 pub fn mul(rma: *Mutable, a: Const, b: Const, limbs_buffer: []Limb, allocator: ?*Allocator) void {
387 var buf_index: usize = 0;
525388
526 /// To allow `std.fmt.printf` to work with Int.
527 /// TODO make this non-allocating
528 /// TODO support read-only fixed integers
529 pub fn format(
530 self: Int,
531 comptime fmt: []const u8,
532 options: std.fmt.FormatOptions,
533 out_stream: var,
534 ) !void {
535 comptime var radix = 10;
536 comptime var uppercase = false;
389 const a_copy = if (rma.limbs.ptr == a.limbs.ptr) blk: {
390 const start = buf_index;
391 mem.copy(Limb, limbs_buffer[buf_index..], a.limbs);
392 buf_index += a.limbs.len;
393 break :blk a.toMutable(limbs_buffer[start..buf_index]).toConst();
394 } else a;
537395
538 if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "d")) {
539 radix = 10;
540 uppercase = false;
541 } else if (comptime std.mem.eql(u8, fmt, "b")) {
542 radix = 2;
543 uppercase = false;
544 } else if (comptime std.mem.eql(u8, fmt, "x")) {
545 radix = 16;
546 uppercase = false;
547 } else if (comptime std.mem.eql(u8, fmt, "X")) {
548 radix = 16;
549 uppercase = true;
550 } else {
551 @compileError("Unknown format string: '" ++ fmt ++ "'");
552 }
396 const b_copy = if (rma.limbs.ptr == b.limbs.ptr) blk: {
397 const start = buf_index;
398 mem.copy(Limb, limbs_buffer[buf_index..], b.limbs);
399 buf_index += b.limbs.len;
400 break :blk b.toMutable(limbs_buffer[start..buf_index]).toConst();
401 } else b;
553402
554 var buf: [4096]u8 = undefined;
555 var fba = std.heap.FixedBufferAllocator.init(&buf);
556 const str = self.toString(&fba.allocator, radix, uppercase) catch @panic("TODO make this non allocating");
557 return out_stream.writeAll(str);
403 return rma.mulNoAlias(a_copy, b_copy, allocator);
558404 }
559405
560 /// Returns math.Order.lt, math.Order.eq, math.Order.gt if |a| < |b|, |a| ==
561 /// |b| or |a| > |b| respectively.
562 pub fn cmpAbs(a: Int, b: Int) math.Order {
563 if (a.len() < b.len()) {
564 return .lt;
565 }
566 if (a.len() > b.len()) {
567 return .gt;
568 }
569
570 var i: usize = a.len() - 1;
571 while (i != 0) : (i -= 1) {
572 if (a.limbs[i] != b.limbs[i]) {
573 break;
406 /// rma = a * b
407 ///
408 /// `rma` may not alias with `a` or `b`.
409 /// `a` and `b` may alias with each other.
410 ///
411 /// Asserts the result fits in `rma`. An upper bound on the number of limbs needed by
412 /// rma is given by `a.limbs.len + b.limbs.len + 1`.
413 ///
414 /// If `allocator` is provided, it will be used for temporary storage to improve
415 /// multiplication performance. `error.OutOfMemory` is handled with a fallback algorithm.
416 pub fn mulNoAlias(rma: *Mutable, a: Const, b: Const, allocator: ?*Allocator) void {
417 assert(rma.limbs.ptr != a.limbs.ptr); // illegal aliasing
418 assert(rma.limbs.ptr != b.limbs.ptr); // illegal aliasing
419
420 if (a.limbs.len == 1 and b.limbs.len == 1) {
421 if (!@mulWithOverflow(Limb, a.limbs[0], b.limbs[0], &rma.limbs[0])) {
422 rma.len = 1;
423 rma.positive = (a.positive == b.positive);
424 return;
574425 }
575426 }
576427
577 if (a.limbs[i] < b.limbs[i]) {
578 return .lt;
579 } else if (a.limbs[i] > b.limbs[i]) {
580 return .gt;
581 } else {
582 return .eq;
583 }
584 }
585
586 /// Returns math.Order.lt, math.Order.eq, math.Order.gt if a < b, a == b or a
587 /// > b respectively.
588 pub fn cmp(a: Int, b: Int) math.Order {
589 if (a.isPositive() != b.isPositive()) {
590 return if (a.isPositive()) .gt else .lt;
591 } else {
592 const r = cmpAbs(a, b);
593 return if (a.isPositive()) r else switch (r) {
594 .lt => math.Order.gt,
595 .eq => math.Order.eq,
596 .gt => math.Order.lt,
597 };
598 }
599 }
600
601 /// Returns true if a == 0.
602 pub fn eqZero(a: Int) bool {
603 return a.len() == 1 and a.limbs[0] == 0;
604 }
428 mem.set(Limb, rma.limbs[0 .. a.limbs.len + b.limbs.len + 1], 0);
605429
606 /// Returns true if |a| == |b|.
607 pub fn eqAbs(a: Int, b: Int) bool {
608 return cmpAbs(a, b) == .eq;
609 }
430 llmulacc(allocator, rma.limbs, a.limbs, b.limbs);
610431
611 /// Returns true if a == b.
612 pub fn eq(a: Int, b: Int) bool {
613 return cmp(a, b) == .eq;
432 rma.normalize(a.limbs.len + b.limbs.len);
433 rma.positive = (a.positive == b.positive);
614434 }
615435
616 // Normalize a possible sequence of leading zeros.
617 //
618 // [1, 2, 3, 4, 0] -> [1, 2, 3, 4]
619 // [1, 2, 0, 0, 0] -> [1, 2]
620 // [0, 0, 0, 0, 0] -> [0]
621 fn normalize(r: *Int, length: usize) void {
622 debug.assert(length > 0);
623 debug.assert(length <= r.limbs.len);
436 /// q = a / b (rem r)
437 ///
438 /// a / b are floored (rounded towards 0).
439 /// q may alias with a or b.
440 ///
441 /// Asserts there is enough memory to store q and r.
442 /// The upper bound for r limb count is a.limbs.len.
443 /// The upper bound for q limb count is given by `a.limbs.len + b.limbs.len + 1`.
444 ///
445 /// If `allocator` is provided, it will be used for temporary storage to improve
446 /// multiplication performance. `error.OutOfMemory` is handled with a fallback algorithm.
447 ///
448 /// `limbs_buffer` is used for temporary storage. The amount required is given by `calcDivLimbsBufferLen`.
449 pub fn divFloor(
450 q: *Mutable,
451 r: *Mutable,
452 a: Const,
453 b: Const,
454 limbs_buffer: []Limb,
455 allocator: ?*Allocator,
456 ) void {
457 div(q, r, a, b, limbs_buffer, allocator);
624458
625 var j = length;
626 while (j > 0) : (j -= 1) {
627 if (r.limbs[j - 1] != 0) {
628 break;
629 }
459 // Trunc -> Floor.
460 if (!q.positive) {
461 const one: Const = .{ .limbs = &[_]Limb{1}, .positive = true };
462 q.sub(q.toConst(), one);
463 r.add(q.toConst(), one);
630464 }
631
632 // Handle zero
633 r.setLen(if (j != 0) j else 1);
465 r.positive = b.positive;
634466 }
635467
636 // Cannot be used as a result argument to any function.
637 fn readOnlyPositive(a: Int) Int {
638 return Int{
639 .allocator = null,
640 .metadata = a.len(),
641 .limbs = a.limbs,
642 };
468 /// q = a / b (rem r)
469 ///
470 /// a / b are truncated (rounded towards -inf).
471 /// q may alias with a or b.
472 ///
473 /// Asserts there is enough memory to store q and r.
474 /// The upper bound for r limb count is a.limbs.len.
475 /// The upper bound for q limb count is given by `calcQuotientLimbLen`. This accounts
476 /// for temporary space used by the division algorithm.
477 ///
478 /// If `allocator` is provided, it will be used for temporary storage to improve
479 /// multiplication performance. `error.OutOfMemory` is handled with a fallback algorithm.
480 ///
481 /// `limbs_buffer` is used for temporary storage. The amount required is given by `calcDivLimbsBufferLen`.
482 pub fn divTrunc(
483 q: *Mutable,
484 r: *Mutable,
485 a: Const,
486 b: Const,
487 limbs_buffer: []Limb,
488 allocator: ?*Allocator,
489 ) void {
490 div(q, r, a, b, limbs_buffer, allocator);
491 r.positive = a.positive;
643492 }
644493
645 /// r = a + b
494 /// r = a << shift, in other words, r = a * 2^shift
646495 ///
647 /// r, a and b may be aliases.
496 /// r and a may alias.
648497 ///
649 /// Returns an error if memory could not be allocated.
650 pub fn add(r: *Int, a: Int, b: Int) Allocator.Error!void {
651 r.assertWritable();
652 if (a.eqZero()) {
653 try r.copy(b);
654 return;
655 } else if (b.eqZero()) {
656 try r.copy(a);
657 return;
658 }
659
660 if (a.isPositive() != b.isPositive()) {
661 if (a.isPositive()) {
662 // (a) + (-b) => a - b
663 try r.sub(a, readOnlyPositive(b));
664 } else {
665 // (-a) + (b) => b - a
666 try r.sub(b, readOnlyPositive(a));
667 }
668 } else {
669 if (a.len() >= b.len()) {
670 try r.ensureCapacity(a.len() + 1);
671 lladd(r.limbs[0..], a.limbs[0..a.len()], b.limbs[0..b.len()]);
672 r.normalize(a.len() + 1);
673 } else {
674 try r.ensureCapacity(b.len() + 1);
675 lladd(r.limbs[0..], b.limbs[0..b.len()], a.limbs[0..a.len()]);
676 r.normalize(b.len() + 1);
677 }
678
679 r.setSign(a.isPositive());
680 }
498 /// Asserts there is enough memory to fit the result. The upper bound Limb count is
499 /// `a.limbs.len + (shift / (@sizeOf(Limb) * 8))`.
500 pub fn shiftLeft(r: *Mutable, a: Const, shift: usize) void {
501 llshl(r.limbs[0..], a.limbs[0..a.limbs.len], shift);
502 r.normalize(a.limbs.len + (shift / Limb.bit_count) + 1);
503 r.positive = a.positive;
681504 }
682505
683 // Knuth 4.3.1, Algorithm A.
684 fn lladd(r: []Limb, a: []const Limb, b: []const Limb) void {
685 @setRuntimeSafety(false);
686 debug.assert(a.len != 0 and b.len != 0);
687 debug.assert(a.len >= b.len);
688 debug.assert(r.len >= a.len + 1);
689
690 var i: usize = 0;
691 var carry: Limb = 0;
692
693 while (i < b.len) : (i += 1) {
694 var c: Limb = 0;
695 c += @boolToInt(@addWithOverflow(Limb, a[i], b[i], &r[i]));
696 c += @boolToInt(@addWithOverflow(Limb, r[i], carry, &r[i]));
697 carry = c;
698 }
699
700 while (i < a.len) : (i += 1) {
701 carry = @boolToInt(@addWithOverflow(Limb, a[i], carry, &r[i]));
506 /// r = a >> shift
507 /// r and a may alias.
508 ///
509 /// Asserts there is enough memory to fit the result. The upper bound Limb count is
510 /// `a.limbs.len - (shift / (@sizeOf(Limb) * 8))`.
511 pub fn shiftRight(r: *Mutable, a: Const, shift: usize) void {
512 if (a.limbs.len <= shift / Limb.bit_count) {
513 r.len = 1;
514 r.positive = true;
515 r.limbs[0] = 0;
516 return;
702517 }
703518
704 r[i] = carry;
519 const r_len = llshr(r.limbs[0..], a.limbs[0..a.limbs.len], shift);
520 r.len = a.limbs.len - (shift / Limb.bit_count);
521 r.positive = a.positive;
705522 }
706523
707 /// r = a - b
524 /// r = a | b
525 /// r may alias with a or b.
708526 ///
709 /// r, a and b may be aliases.
527 /// a and b are zero-extended to the longer of a or b.
710528 ///
711 /// Returns an error if memory could not be allocated.
712 pub fn sub(r: *Int, a: Int, b: Int) !void {
713 r.assertWritable();
714 if (a.isPositive() != b.isPositive()) {
715 if (a.isPositive()) {
716 // (a) - (-b) => a + b
717 try r.add(a, readOnlyPositive(b));
718 } else {
719 // (-a) - (b) => -(a + b)
720 try r.add(readOnlyPositive(a), b);
721 r.setSign(false);
722 }
529 /// Asserts that r has enough limbs to store the result. Upper bound is `math.max(a.limbs.len, b.limbs.len)`.
530 pub fn bitOr(r: *Mutable, a: Const, b: Const) void {
531 if (a.limbs.len > b.limbs.len) {
532 llor(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]);
533 r.len = a.limbs.len;
723534 } else {
724 if (a.isPositive()) {
725 // (a) - (b) => a - b
726 if (a.cmp(b) != .lt) {
727 try r.ensureCapacity(a.len() + 1);
728 llsub(r.limbs[0..], a.limbs[0..a.len()], b.limbs[0..b.len()]);
729 r.normalize(a.len());
730 r.setSign(true);
731 } else {
732 try r.ensureCapacity(b.len() + 1);
733 llsub(r.limbs[0..], b.limbs[0..b.len()], a.limbs[0..a.len()]);
734 r.normalize(b.len());
735 r.setSign(false);
736 }
737 } else {
738 // (-a) - (-b) => -(a - b)
739 if (a.cmp(b) == .lt) {
740 try r.ensureCapacity(a.len() + 1);
741 llsub(r.limbs[0..], a.limbs[0..a.len()], b.limbs[0..b.len()]);
742 r.normalize(a.len());
743 r.setSign(false);
744 } else {
745 try r.ensureCapacity(b.len() + 1);
746 llsub(r.limbs[0..], b.limbs[0..b.len()], a.limbs[0..a.len()]);
747 r.normalize(b.len());
748 r.setSign(true);
749 }
750 }
535 llor(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]);
536 r.len = b.limbs.len;
751537 }
752538 }
753539
754 // Knuth 4.3.1, Algorithm S.
755 fn llsub(r: []Limb, a: []const Limb, b: []const Limb) void {
756 @setRuntimeSafety(false);
757 debug.assert(a.len != 0 and b.len != 0);
758 debug.assert(a.len > b.len or (a.len == b.len and a[a.len - 1] >= b[b.len - 1]));
759 debug.assert(r.len >= a.len);
760
761 var i: usize = 0;
762 var borrow: Limb = 0;
763
764 while (i < b.len) : (i += 1) {
765 var c: Limb = 0;
766 c += @boolToInt(@subWithOverflow(Limb, a[i], b[i], &r[i]));
767 c += @boolToInt(@subWithOverflow(Limb, r[i], borrow, &r[i]));
768 borrow = c;
540 /// r = a & b
541 /// r may alias with a or b.
542 ///
543 /// Asserts that r has enough limbs to store the result. Upper bound is `math.min(a.limbs.len, b.limbs.len)`.
544 pub fn bitAnd(r: *Mutable, a: Const, b: Const) void {
545 if (a.limbs.len > b.limbs.len) {
546 lland(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]);
547 r.normalize(b.limbs.len);
548 } else {
549 lland(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]);
550 r.normalize(a.limbs.len);
769551 }
552 }
770553
771 while (i < a.len) : (i += 1) {
772 borrow = @boolToInt(@subWithOverflow(Limb, a[i], borrow, &r[i]));
554 /// r = a ^ b
555 /// r may alias with a or b.
556 ///
557 /// Asserts that r has enough limbs to store the result. Upper bound is `math.max(a.limbs.len, b.limbs.len)`.
558 pub fn bitXor(r: *Mutable, a: Const, b: Const) void {
559 if (a.limbs.len > b.limbs.len) {
560 llxor(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]);
561 r.normalize(a.limbs.len);
562 } else {
563 llxor(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]);
564 r.normalize(b.limbs.len);
773565 }
774
775 debug.assert(borrow == 0);
776566 }
777567
778 /// rma = a * b
568 /// rma may alias x or y.
569 /// x and y may alias each other.
570 /// Asserts that `rma` has enough limbs to store the result. Upper bound is
571 /// `math.min(x.limbs.len, y.limbs.len)`.
779572 ///
780 /// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b.
573 /// `limbs_buffer` is used for temporary storage during the operation. When this function returns,
574 /// it will have the same length as it had when the function was called.
575 pub fn gcd(rma: *Mutable, x: Const, y: Const, limbs_buffer: *std.ArrayList(Limb)) !void {
576 const prev_len = limbs_buffer.items.len;
577 defer limbs_buffer.shrink(prev_len);
578 const x_copy = if (rma.limbs.ptr == x.limbs.ptr) blk: {
579 const start = limbs_buffer.items.len;
580 try limbs_buffer.appendSlice(x.limbs);
581 break :blk x.toMutable(limbs_buffer.items[start..]).toConst();
582 } else x;
583 const y_copy = if (rma.limbs.ptr == y.limbs.ptr) blk: {
584 const start = limbs_buffer.items.len;
585 try limbs_buffer.appendSlice(y.limbs);
586 break :blk y.toMutable(limbs_buffer.items[start..]).toConst();
587 } else y;
588
589 return gcdLehmer(rma, x_copy, y_copy, limbs_buffer);
590 }
591
592 /// rma may not alias x or y.
593 /// x and y may alias each other.
594 /// Asserts that `rma` has enough limbs to store the result. Upper bound is given by `calcGcdNoAliasLimbLen`.
781595 ///
782 /// Returns an error if memory could not be allocated.
783 pub fn mul(rma: *Int, a: Int, b: Int) !void {
784 rma.assertWritable();
596 /// `limbs_buffer` is used for temporary storage during the operation.
597 pub fn gcdNoAlias(rma: *Mutable, x: Const, y: Const, limbs_buffer: *std.ArrayList(Limb)) !void {
598 assert(rma.limbs.ptr != x.limbs.ptr); // illegal aliasing
599 assert(rma.limbs.ptr != y.limbs.ptr); // illegal aliasing
600 return gcdLehmer(rma, x, y, allocator);
601 }
785602
786 var r = rma;
787 var aliased = rma.limbs.ptr == a.limbs.ptr or rma.limbs.ptr == b.limbs.ptr;
603 fn gcdLehmer(result: *Mutable, xa: Const, ya: Const, limbs_buffer: *std.ArrayList(Limb)) !void {
604 var x = try xa.toManaged(limbs_buffer.allocator);
605 defer x.deinit();
606 x.abs();
607
608 var y = try ya.toManaged(limbs_buffer.allocator);
609 defer y.deinit();
610 y.abs();
788611
789 var sr: Int = undefined;
790 if (aliased) {
791 sr = try Int.initCapacity(rma.allocator.?, a.len() + b.len());
792 r = &sr;
793 aliased = true;
612 if (x.toConst().order(y.toConst()) == .lt) {
613 x.swap(&y);
794614 }
795 defer if (aliased) {
796 rma.swap(r);
797 r.deinit();
798 };
799615
800 try r.ensureCapacity(a.len() + b.len() + 1);
616 var t_big = try Managed.init(limbs_buffer.allocator);
617 defer t_big.deinit();
801618
802 mem.set(Limb, r.limbs[0 .. a.len() + b.len() + 1], 0);
619 var r = try Managed.init(limbs_buffer.allocator);
620 defer r.deinit();
803621
804 try llmulacc(rma.allocator.?, r.limbs, a.limbs[0..a.len()], b.limbs[0..b.len()]);
622 while (y.len() > 1) {
623 assert(x.isPositive() and y.isPositive());
624 assert(x.len() >= y.len());
805625
806 r.normalize(a.len() + b.len());
807 r.setSign(a.isPositive() == b.isPositive());
808 }
626 var xh: SignedDoubleLimb = x.limbs[x.len() - 1];
627 var yh: SignedDoubleLimb = if (x.len() > y.len()) 0 else y.limbs[x.len() - 1];
809628
810 // a + b * c + *carry, sets carry to the overflow bits
811 pub fn addMulLimbWithCarry(a: Limb, b: Limb, c: Limb, carry: *Limb) Limb {
812 @setRuntimeSafety(false);
813 var r1: Limb = undefined;
629 var A: SignedDoubleLimb = 1;
630 var B: SignedDoubleLimb = 0;
631 var C: SignedDoubleLimb = 0;
632 var D: SignedDoubleLimb = 1;
814633
815 // r1 = a + *carry
816 const c1: Limb = @boolToInt(@addWithOverflow(Limb, a, carry.*, &r1));
634 while (yh + C != 0 and yh + D != 0) {
635 const q = @divFloor(xh + A, yh + C);
636 const qp = @divFloor(xh + B, yh + D);
637 if (q != qp) {
638 break;
639 }
817640
818 // r2 = b * c
819 const bc = @as(DoubleLimb, math.mulWide(Limb, b, c));
820 const r2 = @truncate(Limb, bc);
821 const c2 = @truncate(Limb, bc >> Limb.bit_count);
641 var t = A - q * C;
642 A = C;
643 C = t;
644 t = B - q * D;
645 B = D;
646 D = t;
822647
823 // r1 = r1 + r2
824 const c3: Limb = @boolToInt(@addWithOverflow(Limb, r1, r2, &r1));
648 t = xh - q * yh;
649 xh = yh;
650 yh = t;
651 }
825652
826 // This never overflows, c1, c3 are either 0 or 1 and if both are 1 then
827 // c2 is at least <= maxInt(Limb) - 2.
828 carry.* = c1 + c2 + c3;
653 if (B == 0) {
654 // t_big = x % y, r is unused
655 try r.divTrunc(&t_big, x.toConst(), y.toConst());
656 assert(t_big.isPositive());
829657
830 return r1;
831 }
658 x.swap(&y);
659 y.swap(&t_big);
660 } else {
661 var storage: [8]Limb = undefined;
662 const Ap = fixedIntFromSignedDoubleLimb(A, storage[0..2]).toConst();
663 const Bp = fixedIntFromSignedDoubleLimb(B, storage[2..4]).toConst();
664 const Cp = fixedIntFromSignedDoubleLimb(C, storage[4..6]).toConst();
665 const Dp = fixedIntFromSignedDoubleLimb(D, storage[6..8]).toConst();
832666
833 fn llmulDigit(acc: []Limb, y: []const Limb, xi: Limb) void {
834 @setRuntimeSafety(false);
835 if (xi == 0) {
836 return;
837 }
667 // t_big = Ax + By
668 try r.mul(x.toConst(), Ap);
669 try t_big.mul(y.toConst(), Bp);
670 try t_big.add(r.toConst(), t_big.toConst());
838671
839 var carry: usize = 0;
840 var a_lo = acc[0..y.len];
841 var a_hi = acc[y.len..];
672 // u = Cx + Dy, r as u
673 try x.mul(x.toConst(), Cp);
674 try r.mul(y.toConst(), Dp);
675 try r.add(x.toConst(), r.toConst());
842676
843 var j: usize = 0;
844 while (j < a_lo.len) : (j += 1) {
845 a_lo[j] = @call(.{ .modifier = .always_inline }, addMulLimbWithCarry, .{ a_lo[j], y[j], xi, &carry });
677 x.swap(&t_big);
678 y.swap(&r);
679 }
846680 }
847681
848 j = 0;
849 while ((carry != 0) and (j < a_hi.len)) : (j += 1) {
850 carry = @boolToInt(@addWithOverflow(Limb, a_hi[j], carry, &a_hi[j]));
682 // euclidean algorithm
683 assert(x.toConst().order(y.toConst()) != .lt);
684
685 while (!y.toConst().eqZero()) {
686 try t_big.divTrunc(&r, x.toConst(), y.toConst());
687 x.swap(&y);
688 y.swap(&r);
851689 }
690
691 result.copy(x.toConst());
852692 }
853693
854 // Knuth 4.3.1, Algorithm M.
855 //
856 // r MUST NOT alias any of a or b.
857 fn llmulacc(allocator: *Allocator, r: []Limb, a: []const Limb, b: []const Limb) error{OutOfMemory}!void {
858 @setRuntimeSafety(false);
694 /// Truncates by default.
695 fn div(quo: *Mutable, rem: *Mutable, a: Const, b: Const, limbs_buffer: []Limb, allocator: ?*Allocator) void {
696 assert(!b.eqZero()); // division by zero
697 assert(quo != rem); // illegal aliasing
859698
860 const a_norm = a[0..llnormalize(a)];
861 const b_norm = b[0..llnormalize(b)];
862 var x = a_norm;
863 var y = b_norm;
864 if (a_norm.len > b_norm.len) {
865 x = b_norm;
866 y = a_norm;
867 }
699 if (a.orderAbs(b) == .lt) {
700 // quo may alias a so handle rem first
701 rem.copy(a);
702 rem.positive = a.positive == b.positive;
868703
869 debug.assert(r.len >= x.len + y.len + 1);
704 quo.positive = true;
705 quo.len = 1;
706 quo.limbs[0] = 0;
707 return;
708 }
870709
871 // 48 is a pretty abitrary size chosen based on performance of a factorial program.
872 if (x.len <= 48) {
873 // Basecase multiplication
710 // Handle trailing zero-words of divisor/dividend. These are not handled in the following
711 // algorithms.
712 const a_zero_limb_count = blk: {
874713 var i: usize = 0;
875 while (i < x.len) : (i += 1) {
876 llmulDigit(r[i..], y, x[i]);
714 while (i < a.limbs.len) : (i += 1) {
715 if (a.limbs[i] != 0) break;
877716 }
878 } else {
879 // Karatsuba multiplication
880 const split = @divFloor(x.len, 2);
881 var x0 = x[0..split];
882 var x1 = x[split..x.len];
883 var y0 = y[0..split];
884 var y1 = y[split..y.len];
885
886 var tmp = try allocator.alloc(Limb, x1.len + y1.len + 1);
887 defer allocator.free(tmp);
888 mem.set(Limb, tmp, 0);
889
890 try llmulacc(allocator, tmp, x1, y1);
717 break :blk i;
718 };
719 const b_zero_limb_count = blk: {
720 var i: usize = 0;
721 while (i < b.limbs.len) : (i += 1) {
722 if (b.limbs[i] != 0) break;
723 }
724 break :blk i;
725 };
891726
892 var length = llnormalize(tmp);
893 _ = llaccum(r[split..], tmp[0..length]);
894 _ = llaccum(r[split * 2 ..], tmp[0..length]);
727 const ab_zero_limb_count = math.min(a_zero_limb_count, b_zero_limb_count);
895728
896 mem.set(Limb, tmp[0..length], 0);
729 if (b.limbs.len - ab_zero_limb_count == 1) {
730 lldiv1(quo.limbs[0..], &rem.limbs[0], a.limbs[ab_zero_limb_count..a.limbs.len], b.limbs[b.limbs.len - 1]);
731 quo.normalize(a.limbs.len - ab_zero_limb_count);
732 quo.positive = (a.positive == b.positive);
897733
898 try llmulacc(allocator, tmp, x0, y0);
734 rem.len = 1;
735 rem.positive = true;
736 } else {
737 // x and y are modified during division
738 const sep_len = calcMulLimbsBufferLen(a.limbs.len, b.limbs.len, 2);
739 const x_limbs = limbs_buffer[0 * sep_len ..][0..sep_len];
740 const y_limbs = limbs_buffer[1 * sep_len ..][0..sep_len];
741 const t_limbs = limbs_buffer[2 * sep_len ..][0..sep_len];
742 const mul_limbs_buf = limbs_buffer[3 * sep_len ..][0..sep_len];
743
744 var x: Mutable = .{
745 .limbs = x_limbs,
746 .positive = a.positive,
747 .len = a.limbs.len - ab_zero_limb_count,
748 };
749 var y: Mutable = .{
750 .limbs = y_limbs,
751 .positive = b.positive,
752 .len = b.limbs.len - ab_zero_limb_count,
753 };
899754
900 length = llnormalize(tmp);
901 _ = llaccum(r[0..], tmp[0..length]);
902 _ = llaccum(r[split..], tmp[0..length]);
755 // Shrink x, y such that the trailing zero limbs shared between are removed.
756 mem.copy(Limb, x.limbs, a.limbs[ab_zero_limb_count..a.limbs.len]);
757 mem.copy(Limb, y.limbs, b.limbs[ab_zero_limb_count..b.limbs.len]);
903758
904 const x_cmp = llcmp(x1, x0);
905 const y_cmp = llcmp(y1, y0);
906 if (x_cmp * y_cmp == 0) {
907 return;
908 }
909 const x0_len = llnormalize(x0);
910 const x1_len = llnormalize(x1);
911 var j0 = try allocator.alloc(Limb, math.max(x0_len, x1_len));
912 defer allocator.free(j0);
913 if (x_cmp == 1) {
914 llsub(j0, x1[0..x1_len], x0[0..x0_len]);
915 } else {
916 llsub(j0, x0[0..x0_len], x1[0..x1_len]);
917 }
759 divN(quo, rem, &x, &y, t_limbs, mul_limbs_buf, allocator);
760 quo.positive = (a.positive == b.positive);
761 }
918762
919 const y0_len = llnormalize(y0);
920 const y1_len = llnormalize(y1);
921 var j1 = try allocator.alloc(Limb, math.max(y0_len, y1_len));
922 defer allocator.free(j1);
923 if (y_cmp == 1) {
924 llsub(j1, y1[0..y1_len], y0[0..y0_len]);
925 } else {
926 llsub(j1, y0[0..y0_len], y1[0..y1_len]);
927 }
928 const j0_len = llnormalize(j0);
929 const j1_len = llnormalize(j1);
930 if (x_cmp == y_cmp) {
931 mem.set(Limb, tmp[0..length], 0);
932 try llmulacc(allocator, tmp, j0, j1);
933
934 length = Int.llnormalize(tmp);
935 llsub(r[split..], r[split..], tmp[0..length]);
936 } else {
937 try llmulacc(allocator, r[split..], j0, j1);
938 }
763 if (ab_zero_limb_count != 0) {
764 rem.shiftLeft(rem.toConst(), ab_zero_limb_count * Limb.bit_count);
939765 }
940766 }
941767
942 // r = r + a
943 fn llaccum(r: []Limb, a: []const Limb) Limb {
944 @setRuntimeSafety(false);
945 debug.assert(r.len != 0 and a.len != 0);
946 debug.assert(r.len >= a.len);
947
948 var i: usize = 0;
949 var carry: Limb = 0;
950
951 while (i < a.len) : (i += 1) {
952 var c: Limb = 0;
953 c += @boolToInt(@addWithOverflow(Limb, r[i], a[i], &r[i]));
954 c += @boolToInt(@addWithOverflow(Limb, r[i], carry, &r[i]));
955 carry = c;
956 }
768 /// Handbook of Applied Cryptography, 14.20
769 ///
770 /// x = qy + r where 0 <= r < y
771 fn divN(
772 q: *Mutable,
773 r: *Mutable,
774 x: *Mutable,
775 y: *Mutable,
776 tmp_limbs: []Limb,
777 mul_limb_buf: []Limb,
778 allocator: ?*Allocator,
779 ) void {
780 assert(y.len >= 2);
781 assert(x.len >= y.len);
782 assert(q.limbs.len >= x.len + y.len - 1);
783
784 // See 3.2
785 var backup_tmp_limbs: [3]Limb = undefined;
786 const t_limbs = if (tmp_limbs.len < 3) &backup_tmp_limbs else tmp_limbs;
787
788 var tmp: Mutable = .{
789 .limbs = t_limbs,
790 .len = 1,
791 .positive = true,
792 };
793 tmp.limbs[0] = 0;
957794
958 while ((carry != 0) and i < r.len) : (i += 1) {
959 carry = @boolToInt(@addWithOverflow(Limb, r[i], carry, &r[i]));
795 // Normalize so y > Limb.bit_count / 2 (i.e. leading bit is set) and even
796 var norm_shift = @clz(Limb, y.limbs[y.len - 1]);
797 if (norm_shift == 0 and y.toConst().isOdd()) {
798 norm_shift = Limb.bit_count;
960799 }
800 x.shiftLeft(x.toConst(), norm_shift);
801 y.shiftLeft(y.toConst(), norm_shift);
961802
962 return carry;
963 }
803 const n = x.len - 1;
804 const t = y.len - 1;
964805
965 /// Returns -1, 0, 1 if |a| < |b|, |a| == |b| or |a| > |b| respectively for limbs.
966 pub fn llcmp(a: []const Limb, b: []const Limb) i8 {
967 @setRuntimeSafety(false);
968 const a_len = llnormalize(a);
969 const b_len = llnormalize(b);
970 if (a_len < b_len) {
971 return -1;
972 }
973 if (a_len > b_len) {
974 return 1;
975 }
806 // 1.
807 q.len = n - t + 1;
808 q.positive = true;
809 mem.set(Limb, q.limbs[0..q.len], 0);
976810
977 var i: usize = a_len - 1;
978 while (i != 0) : (i -= 1) {
979 if (a[i] != b[i]) {
980 break;
981 }
982 }
983
984 if (a[i] < b[i]) {
985 return -1;
986 } else if (a[i] > b[i]) {
987 return 1;
988 } else {
989 return 0;
990 }
991 }
992
993 // returns the min length the limb could be.
994 fn llnormalize(a: []const Limb) usize {
995 @setRuntimeSafety(false);
996 var j = a.len;
997 while (j > 0) : (j -= 1) {
998 if (a[j - 1] != 0) {
999 break;
1000 }
1001 }
1002
1003 // Handle zero
1004 return if (j != 0) j else 1;
1005 }
1006
1007 /// q = a / b (rem r)
1008 ///
1009 /// a / b are floored (rounded towards 0).
1010 pub fn divFloor(q: *Int, r: *Int, a: Int, b: Int) !void {
1011 try div(q, r, a, b);
1012
1013 // Trunc -> Floor.
1014 if (!q.isPositive()) {
1015 const one = Int.initFixed(([_]Limb{1})[0..]);
1016 try q.sub(q.*, one);
1017 try r.add(q.*, one);
1018 }
1019 r.setSign(b.isPositive());
1020 }
1021
1022 /// q = a / b (rem r)
1023 ///
1024 /// a / b are truncated (rounded towards -inf).
1025 pub fn divTrunc(q: *Int, r: *Int, a: Int, b: Int) !void {
1026 try div(q, r, a, b);
1027 r.setSign(a.isPositive());
1028 }
1029
1030 // Truncates by default.
1031 fn div(quo: *Int, rem: *Int, a: Int, b: Int) !void {
1032 quo.assertWritable();
1033 rem.assertWritable();
1034
1035 if (b.eqZero()) {
1036 @panic("division by zero");
1037 }
1038 if (quo == rem) {
1039 @panic("quo and rem cannot be same variable");
1040 }
1041
1042 if (a.cmpAbs(b) == .lt) {
1043 // quo may alias a so handle rem first
1044 try rem.copy(a);
1045 rem.setSign(a.isPositive() == b.isPositive());
1046
1047 quo.metadata = 1;
1048 quo.limbs[0] = 0;
1049 return;
1050 }
1051
1052 // Handle trailing zero-words of divisor/dividend. These are not handled in the following
1053 // algorithms.
1054 const a_zero_limb_count = blk: {
1055 var i: usize = 0;
1056 while (i < a.len()) : (i += 1) {
1057 if (a.limbs[i] != 0) break;
1058 }
1059 break :blk i;
1060 };
1061 const b_zero_limb_count = blk: {
1062 var i: usize = 0;
1063 while (i < b.len()) : (i += 1) {
1064 if (b.limbs[i] != 0) break;
1065 }
1066 break :blk i;
1067 };
1068
1069 const ab_zero_limb_count = std.math.min(a_zero_limb_count, b_zero_limb_count);
1070
1071 if (b.len() - ab_zero_limb_count == 1) {
1072 try quo.ensureCapacity(a.len());
1073
1074 lldiv1(quo.limbs[0..], &rem.limbs[0], a.limbs[ab_zero_limb_count..a.len()], b.limbs[b.len() - 1]);
1075 quo.normalize(a.len() - ab_zero_limb_count);
1076 quo.setSign(a.isPositive() == b.isPositive());
1077
1078 rem.metadata = 1;
1079 } else {
1080 // x and y are modified during division
1081 var x = try Int.initCapacity(quo.allocator.?, a.len());
1082 defer x.deinit();
1083 try x.copy(a);
1084
1085 var y = try Int.initCapacity(quo.allocator.?, b.len());
1086 defer y.deinit();
1087 try y.copy(b);
1088
1089 // x may grow one limb during normalization
1090 try quo.ensureCapacity(a.len() + y.len());
1091
1092 // Shrink x, y such that the trailing zero limbs shared between are removed.
1093 if (ab_zero_limb_count != 0) {
1094 std.mem.copy(Limb, x.limbs[0..], x.limbs[ab_zero_limb_count..]);
1095 std.mem.copy(Limb, y.limbs[0..], y.limbs[ab_zero_limb_count..]);
1096 x.metadata -= ab_zero_limb_count;
1097 y.metadata -= ab_zero_limb_count;
1098 }
1099
1100 try divN(quo.allocator.?, quo, rem, &x, &y);
1101 quo.setSign(a.isPositive() == b.isPositive());
1102 }
1103
1104 if (ab_zero_limb_count != 0) {
1105 try rem.shiftLeft(rem.*, ab_zero_limb_count * Limb.bit_count);
1106 }
1107 }
1108
1109 // Knuth 4.3.1, Exercise 16.
1110 fn lldiv1(quo: []Limb, rem: *Limb, a: []const Limb, b: Limb) void {
1111 @setRuntimeSafety(false);
1112 debug.assert(a.len > 1 or a[0] >= b);
1113 debug.assert(quo.len >= a.len);
1114
1115 rem.* = 0;
1116 for (a) |_, ri| {
1117 const i = a.len - ri - 1;
1118 const pdiv = ((@as(DoubleLimb, rem.*) << Limb.bit_count) | a[i]);
1119
1120 if (pdiv == 0) {
1121 quo[i] = 0;
1122 rem.* = 0;
1123 } else if (pdiv < b) {
1124 quo[i] = 0;
1125 rem.* = @truncate(Limb, pdiv);
1126 } else if (pdiv == b) {
1127 quo[i] = 1;
1128 rem.* = 0;
1129 } else {
1130 quo[i] = @truncate(Limb, @divTrunc(pdiv, b));
1131 rem.* = @truncate(Limb, pdiv - (quo[i] *% b));
1132 }
1133 }
1134 }
1135
1136 // Handbook of Applied Cryptography, 14.20
1137 //
1138 // x = qy + r where 0 <= r < y
1139 fn divN(allocator: *Allocator, q: *Int, r: *Int, x: *Int, y: *Int) !void {
1140 debug.assert(y.len() >= 2);
1141 debug.assert(x.len() >= y.len());
1142 debug.assert(q.limbs.len >= x.len() + y.len() - 1);
1143 debug.assert(default_capacity >= 3); // see 3.2
1144
1145 var tmp = try Int.init(allocator);
1146 defer tmp.deinit();
1147
1148 // Normalize so y > Limb.bit_count / 2 (i.e. leading bit is set) and even
1149 var norm_shift = @clz(Limb, y.limbs[y.len() - 1]);
1150 if (norm_shift == 0 and y.isOdd()) {
1151 norm_shift = Limb.bit_count;
1152 }
1153 try x.shiftLeft(x.*, norm_shift);
1154 try y.shiftLeft(y.*, norm_shift);
1155
1156 const n = x.len() - 1;
1157 const t = y.len() - 1;
1158
1159 // 1.
1160 q.metadata = n - t + 1;
1161 mem.set(Limb, q.limbs[0..q.len()], 0);
1162
1163 // 2.
1164 try tmp.shiftLeft(y.*, Limb.bit_count * (n - t));
1165 while (x.cmp(tmp) != .lt) {
1166 q.limbs[n - t] += 1;
1167 try x.sub(x.*, tmp);
811 // 2.
812 tmp.shiftLeft(y.toConst(), Limb.bit_count * (n - t));
813 while (x.toConst().order(tmp.toConst()) != .lt) {
814 q.limbs[n - t] += 1;
815 x.sub(x.toConst(), tmp.toConst());
1168816 }
1169817
1170818 // 3.
......@@ -1193,7 +841,7 @@ pub const Int = struct {
1193841 r.limbs[2] = carry;
1194842 r.normalize(3);
1195843
1196 if (r.cmpAbs(tmp) != .gt) {
844 if (r.toConst().orderAbs(tmp.toConst()) != .gt) {
1197845 break;
1198846 }
1199847
......@@ -1201,1748 +849,1284 @@ pub const Int = struct {
1201849 }
1202850
1203851 // 3.3
1204 try tmp.set(q.limbs[i - t - 1]);
1205 try tmp.mul(tmp, y.*);
1206 try tmp.shiftLeft(tmp, Limb.bit_count * (i - t - 1));
1207 try x.sub(x.*, tmp);
1208
1209 if (!x.isPositive()) {
1210 try tmp.shiftLeft(y.*, Limb.bit_count * (i - t - 1));
1211 try x.add(x.*, tmp);
852 tmp.set(q.limbs[i - t - 1]);
853 tmp.mul(tmp.toConst(), y.toConst(), mul_limb_buf, allocator);
854 tmp.shiftLeft(tmp.toConst(), Limb.bit_count * (i - t - 1));
855 x.sub(x.toConst(), tmp.toConst());
856
857 if (!x.positive) {
858 tmp.shiftLeft(y.toConst(), Limb.bit_count * (i - t - 1));
859 x.add(x.toConst(), tmp.toConst());
1212860 q.limbs[i - t - 1] -= 1;
1213861 }
1214862 }
1215863
1216864 // Denormalize
1217 q.normalize(q.len());
865 q.normalize(q.len);
1218866
1219 try r.shiftRight(x.*, norm_shift);
1220 r.normalize(r.len());
867 r.shiftRight(x.toConst(), norm_shift);
868 r.normalize(r.len);
1221869 }
1222870
1223 /// r = a << shift, in other words, r = a * 2^shift
1224 pub fn shiftLeft(r: *Int, a: Int, shift: usize) !void {
1225 r.assertWritable();
1226
1227 try r.ensureCapacity(a.len() + (shift / Limb.bit_count) + 1);
1228 llshl(r.limbs[0..], a.limbs[0..a.len()], shift);
1229 r.normalize(a.len() + (shift / Limb.bit_count) + 1);
1230 r.setSign(a.isPositive());
871 /// Normalize a possible sequence of leading zeros.
872 ///
873 /// [1, 2, 3, 4, 0] -> [1, 2, 3, 4]
874 /// [1, 2, 0, 0, 0] -> [1, 2]
875 /// [0, 0, 0, 0, 0] -> [0]
876 fn normalize(r: *Mutable, length: usize) void {
877 r.len = llnormalize(r.limbs[0..length]);
1231878 }
879};
1232880
1233 fn llshl(r: []Limb, a: []const Limb, shift: usize) void {
1234 @setRuntimeSafety(false);
1235 debug.assert(a.len >= 1);
1236 debug.assert(r.len >= a.len + (shift / Limb.bit_count) + 1);
1237
1238 const limb_shift = shift / Limb.bit_count + 1;
1239 const interior_limb_shift = @intCast(Log2Limb, shift % Limb.bit_count);
1240
1241 var carry: Limb = 0;
1242 var i: usize = 0;
1243 while (i < a.len) : (i += 1) {
1244 const src_i = a.len - i - 1;
1245 const dst_i = src_i + limb_shift;
1246
1247 const src_digit = a[src_i];
1248 r[dst_i] = carry | @call(.{ .modifier = .always_inline }, math.shr, .{
1249 Limb,
1250 src_digit,
1251 Limb.bit_count - @intCast(Limb, interior_limb_shift),
1252 });
1253 carry = (src_digit << interior_limb_shift);
1254 }
1255
1256 r[limb_shift - 1] = carry;
1257 mem.set(Limb, r[0 .. limb_shift - 1], 0);
881/// A arbitrary-precision big integer, with a fixed set of immutable limbs.
882pub const Const = struct {
883 /// Raw digits. These are:
884 ///
885 /// * Little-endian ordered
886 /// * limbs.len >= 1
887 /// * Zero is represented as limbs.len == 1 with limbs[0] == 0.
888 ///
889 /// Accessing limbs directly should be avoided.
890 limbs: []const Limb,
891 positive: bool,
892
893 /// The result is an independent resource which is managed by the caller.
894 pub fn toManaged(self: Const, allocator: *Allocator) Allocator.Error!Managed {
895 const limbs = try allocator.alloc(Limb, math.max(Managed.default_capacity, self.limbs.len));
896 mem.copy(Limb, limbs, self.limbs);
897 return Managed{
898 .allocator = allocator,
899 .limbs = limbs,
900 .metadata = if (self.positive)
901 self.limbs.len & ~Managed.sign_bit
902 else
903 self.limbs.len | Managed.sign_bit,
904 };
1258905 }
1259906
1260 /// r = a >> shift
1261 pub fn shiftRight(r: *Int, a: Int, shift: usize) !void {
1262 r.assertWritable();
907 /// Asserts `limbs` is big enough to store the value.
908 pub fn toMutable(self: Const, limbs: []Limb) Mutable {
909 mem.copy(Limb, limbs, self.limbs[0..self.limbs.len]);
910 return .{
911 .limbs = limbs,
912 .positive = self.positive,
913 .len = self.limbs.len,
914 };
915 }
1263916
1264 if (a.len() <= shift / Limb.bit_count) {
1265 r.metadata = 1;
1266 r.limbs[0] = 0;
1267 return;
917 pub fn dump(self: Const) void {
918 for (self.limbs[0..self.limbs.len]) |limb| {
919 std.debug.warn("{x} ", .{limb});
1268920 }
921 std.debug.warn("positive={}\n", .{self.positive});
922 }
1269923
1270 try r.ensureCapacity(a.len() - (shift / Limb.bit_count));
1271 const r_len = llshr(r.limbs[0..], a.limbs[0..a.len()], shift);
1272 r.metadata = a.len() - (shift / Limb.bit_count);
1273 r.setSign(a.isPositive());
924 pub fn abs(self: Const) Const {
925 return .{
926 .limbs = self.limbs,
927 .positive = true,
928 };
1274929 }
1275930
1276 fn llshr(r: []Limb, a: []const Limb, shift: usize) void {
1277 @setRuntimeSafety(false);
1278 debug.assert(a.len >= 1);
1279 debug.assert(r.len >= a.len - (shift / Limb.bit_count));
931 pub fn isOdd(self: Const) bool {
932 return self.limbs[0] & 1 != 0;
933 }
1280934
1281 const limb_shift = shift / Limb.bit_count;
1282 const interior_limb_shift = @intCast(Log2Limb, shift % Limb.bit_count);
935 pub fn isEven(self: Const) bool {
936 return !self.isOdd();
937 }
1283938
1284 var carry: Limb = 0;
1285 var i: usize = 0;
1286 while (i < a.len - limb_shift) : (i += 1) {
1287 const src_i = a.len - i - 1;
1288 const dst_i = src_i - limb_shift;
1289
1290 const src_digit = a[src_i];
1291 r[dst_i] = carry | (src_digit >> interior_limb_shift);
1292 carry = @call(.{ .modifier = .always_inline }, math.shl, .{
1293 Limb,
1294 src_digit,
1295 Limb.bit_count - @intCast(Limb, interior_limb_shift),
1296 });
1297 }
939 /// Returns the number of bits required to represent the absolute value of an integer.
940 pub fn bitCountAbs(self: Const) usize {
941 return (self.limbs.len - 1) * Limb.bit_count + (Limb.bit_count - @clz(Limb, self.limbs[self.limbs.len - 1]));
1298942 }
1299943
1300 /// r = a | b
944 /// Returns the number of bits required to represent the integer in twos-complement form.
1301945 ///
1302 /// a and b are zero-extended to the longer of a or b.
1303 pub fn bitOr(r: *Int, a: Int, b: Int) !void {
1304 r.assertWritable();
946 /// If the integer is negative the value returned is the number of bits needed by a signed
947 /// integer to represent the value. If positive the value is the number of bits for an
948 /// unsigned integer. Any unsigned integer will fit in the signed integer with bitcount
949 /// one greater than the returned value.
950 ///
951 /// e.g. -127 returns 8 as it will fit in an i8. 127 returns 7 since it fits in a u7.
952 pub fn bitCountTwosComp(self: Const) usize {
953 var bits = self.bitCountAbs();
1305954
1306 if (a.len() > b.len()) {
1307 try r.ensureCapacity(a.len());
1308 llor(r.limbs[0..], a.limbs[0..a.len()], b.limbs[0..b.len()]);
1309 r.setLen(a.len());
1310 } else {
1311 try r.ensureCapacity(b.len());
1312 llor(r.limbs[0..], b.limbs[0..b.len()], a.limbs[0..a.len()]);
1313 r.setLen(b.len());
955 // If the entire value has only one bit set (e.g. 0b100000000) then the negation in twos
956 // complement requires one less bit.
957 if (!self.positive) block: {
958 bits += 1;
959
960 if (@popCount(Limb, self.limbs[self.limbs.len - 1]) == 1) {
961 for (self.limbs[0 .. self.limbs.len - 1]) |limb| {
962 if (@popCount(Limb, limb) != 0) {
963 break :block;
964 }
965 }
966
967 bits -= 1;
968 }
1314969 }
1315 }
1316970
1317 fn llor(r: []Limb, a: []const Limb, b: []const Limb) void {
1318 @setRuntimeSafety(false);
1319 debug.assert(r.len >= a.len);
1320 debug.assert(a.len >= b.len);
971 return bits;
972 }
1321973
1322 var i: usize = 0;
1323 while (i < b.len) : (i += 1) {
1324 r[i] = a[i] | b[i];
974 pub fn fitsInTwosComp(self: Const, is_signed: bool, bit_count: usize) bool {
975 if (self.eqZero()) {
976 return true;
1325977 }
1326 while (i < a.len) : (i += 1) {
1327 r[i] = a[i];
978 if (!is_signed and !self.positive) {
979 return false;
1328980 }
981
982 const req_bits = self.bitCountTwosComp() + @boolToInt(self.positive and is_signed);
983 return bit_count >= req_bits;
1329984 }
1330985
1331 /// r = a & b
1332 pub fn bitAnd(r: *Int, a: Int, b: Int) !void {
1333 r.assertWritable();
986 /// Returns whether self can fit into an integer of the requested type.
987 pub fn fits(self: Const, comptime T: type) bool {
988 const info = @typeInfo(T).Int;
989 return self.fitsInTwosComp(info.is_signed, info.bits);
990 }
1334991
1335 if (a.len() > b.len()) {
1336 try r.ensureCapacity(b.len());
1337 lland(r.limbs[0..], a.limbs[0..a.len()], b.limbs[0..b.len()]);
1338 r.normalize(b.len());
1339 } else {
1340 try r.ensureCapacity(a.len());
1341 lland(r.limbs[0..], b.limbs[0..b.len()], a.limbs[0..a.len()]);
1342 r.normalize(a.len());
1343 }
992 /// Returns the approximate size of the integer in the given base. Negative values accommodate for
993 /// the minus sign. This is used for determining the number of characters needed to print the
994 /// value. It is inexact and may exceed the given value by ~1-2 bytes.
995 /// TODO See if we can make this exact.
996 pub fn sizeInBaseUpperBound(self: Const, base: usize) usize {
997 const bit_count = @as(usize, @boolToInt(!self.positive)) + self.bitCountAbs();
998 return (bit_count / math.log2(base)) + 2;
1344999 }
13451000
1346 fn lland(r: []Limb, a: []const Limb, b: []const Limb) void {
1347 @setRuntimeSafety(false);
1348 debug.assert(r.len >= b.len);
1349 debug.assert(a.len >= b.len);
1001 pub const ConvertError = error{
1002 NegativeIntoUnsigned,
1003 TargetTooSmall,
1004 };
1005
1006 /// Convert self to type T.
1007 ///
1008 /// Returns an error if self cannot be narrowed into the requested type without truncation.
1009 pub fn to(self: Const, comptime T: type) ConvertError!T {
1010 switch (@typeInfo(T)) {
1011 .Int => {
1012 const UT = std.meta.Int(false, T.bit_count);
13501013
1351 var i: usize = 0;
1352 while (i < b.len) : (i += 1) {
1353 r[i] = a[i] & b[i];
1014 if (self.bitCountTwosComp() > T.bit_count) {
1015 return error.TargetTooSmall;
1016 }
1017
1018 var r: UT = 0;
1019
1020 if (@sizeOf(UT) <= @sizeOf(Limb)) {
1021 r = @intCast(UT, self.limbs[0]);
1022 } else {
1023 for (self.limbs[0..self.limbs.len]) |_, ri| {
1024 const limb = self.limbs[self.limbs.len - ri - 1];
1025 r <<= Limb.bit_count;
1026 r |= limb;
1027 }
1028 }
1029
1030 if (!T.is_signed) {
1031 return if (self.positive) @intCast(T, r) else error.NegativeIntoUnsigned;
1032 } else {
1033 if (self.positive) {
1034 return @intCast(T, r);
1035 } else {
1036 if (math.cast(T, r)) |ok| {
1037 return -ok;
1038 } else |_| {
1039 return minInt(T);
1040 }
1041 }
1042 }
1043 },
1044 else => @compileError("cannot convert Const to type " ++ @typeName(T)),
13541045 }
13551046 }
13561047
1357 /// r = a ^ b
1358 pub fn bitXor(r: *Int, a: Int, b: Int) !void {
1359 r.assertWritable();
1048 /// To allow `std.fmt.format` to work with this type.
1049 /// If the integer is larger than `pow(2, 64 * @sizeOf(usize) * 8), this function will fail
1050 /// to print the string, printing "(BigInt)" instead of a number.
1051 /// This is because the rendering algorithm requires reversing a string, which requires O(N) memory.
1052 /// See `toString` and `toStringAlloc` for a way to print big integers without failure.
1053 pub fn format(
1054 self: Const,
1055 comptime fmt: []const u8,
1056 options: std.fmt.FormatOptions,
1057 out_stream: var,
1058 ) !void {
1059 comptime var radix = 10;
1060 comptime var uppercase = false;
13601061
1361 if (a.len() > b.len()) {
1362 try r.ensureCapacity(a.len());
1363 llxor(r.limbs[0..], a.limbs[0..a.len()], b.limbs[0..b.len()]);
1364 r.normalize(a.len());
1062 if (fmt.len == 0 or comptime mem.eql(u8, fmt, "d")) {
1063 radix = 10;
1064 uppercase = false;
1065 } else if (comptime mem.eql(u8, fmt, "b")) {
1066 radix = 2;
1067 uppercase = false;
1068 } else if (comptime mem.eql(u8, fmt, "x")) {
1069 radix = 16;
1070 uppercase = false;
1071 } else if (comptime mem.eql(u8, fmt, "X")) {
1072 radix = 16;
1073 uppercase = true;
13651074 } else {
1366 try r.ensureCapacity(b.len());
1367 llxor(r.limbs[0..], b.limbs[0..b.len()], a.limbs[0..a.len()]);
1368 r.normalize(b.len());
1075 @compileError("Unknown format string: '" ++ fmt ++ "'");
13691076 }
1370 }
13711077
1372 fn llxor(r: []Limb, a: []const Limb, b: []const Limb) void {
1373 @setRuntimeSafety(false);
1374 debug.assert(r.len >= a.len);
1375 debug.assert(a.len >= b.len);
1078 var limbs: [128]Limb = undefined;
1079 const needed_limbs = calcDivLimbsBufferLen(self.limbs.len, 1);
1080 if (needed_limbs > limbs.len)
1081 return out_stream.writeAll("(BigInt)");
13761082
1377 var i: usize = 0;
1378 while (i < b.len) : (i += 1) {
1379 r[i] = a[i] ^ b[i];
1380 }
1381 while (i < a.len) : (i += 1) {
1382 r[i] = a[i];
1383 }
1083 // This is the inverse of calcDivLimbsBufferLen
1084 const available_len = (limbs.len / 3) - 2;
1085
1086 const biggest: Const = .{
1087 .limbs = &([1]Limb{math.maxInt(Limb)} ** available_len),
1088 .positive = false,
1089 };
1090 var buf: [biggest.sizeInBaseUpperBound(radix)]u8 = undefined;
1091 const len = self.toString(&buf, radix, uppercase, &limbs);
1092 return out_stream.writeAll(buf[0..len]);
13841093 }
13851094
1386 pub fn gcd(rma: *Int, x: Int, y: Int) !void {
1387 rma.assertWritable();
1388 var r = rma;
1389 var aliased = rma.limbs.ptr == x.limbs.ptr or rma.limbs.ptr == y.limbs.ptr;
1095 /// Converts self to a string in the requested base.
1096 /// Caller owns returned memory.
1097 /// Asserts that `base` is in the range [2, 16].
1098 /// See also `toString`, a lower level function than this.
1099 pub fn toStringAlloc(self: Const, allocator: *Allocator, base: u8, uppercase: bool) Allocator.Error![]u8 {
1100 assert(base >= 2);
1101 assert(base <= 16);
13901102
1391 var sr: Int = undefined;
1392 if (aliased) {
1393 sr = try Int.initCapacity(rma.allocator.?, math.max(x.len(), y.len()));
1394 r = &sr;
1395 aliased = true;
1103 if (self.eqZero()) {
1104 return mem.dupe(allocator, u8, "0");
13961105 }
1397 defer if (aliased) {
1398 rma.swap(r);
1399 r.deinit();
1400 };
1106 const string = try allocator.alloc(u8, self.sizeInBaseUpperBound(base));
1107 errdefer allocator.free(string);
14011108
1402 try gcdLehmer(r, x, y);
1403 }
1109 const limbs = try allocator.alloc(Limb, calcToStringLimbsBufferLen(self.limbs.len, base));
1110 defer allocator.free(limbs);
14041111
1405 fn gcdLehmer(r: *Int, xa: Int, ya: Int) !void {
1406 var x = try xa.clone();
1407 x.abs();
1408 defer x.deinit();
1112 return allocator.shrink(string, self.toString(string, base, uppercase, limbs));
1113 }
14091114
1410 var y = try ya.clone();
1411 y.abs();
1412 defer y.deinit();
1115 /// Converts self to a string in the requested base.
1116 /// Asserts that `base` is in the range [2, 16].
1117 /// `string` is a caller-provided slice of at least `sizeInBaseUpperBound` bytes,
1118 /// where the result is written to.
1119 /// Returns the length of the string.
1120 /// `limbs_buffer` is caller-provided memory for `toString` to use as a working area. It must have
1121 /// length of at least `calcToStringLimbsBufferLen`.
1122 /// In the case of power-of-two base, `limbs_buffer` is ignored.
1123 /// See also `toStringAlloc`, a higher level function than this.
1124 pub fn toString(self: Const, string: []u8, base: u8, uppercase: bool, limbs_buffer: []Limb) usize {
1125 assert(base >= 2);
1126 assert(base <= 16);
14131127
1414 if (x.cmp(y) == .lt) {
1415 x.swap(&y);
1128 if (self.eqZero()) {
1129 string[0] = '0';
1130 return 1;
14161131 }
14171132
1418 var T = try Int.init(r.allocator.?);
1419 defer T.deinit();
1420
1421 while (y.len() > 1) {
1422 debug.assert(x.isPositive() and y.isPositive());
1423 debug.assert(x.len() >= y.len());
1424
1425 var xh: SignedDoubleLimb = x.limbs[x.len() - 1];
1426 var yh: SignedDoubleLimb = if (x.len() > y.len()) 0 else y.limbs[x.len() - 1];
1133 var digits_len: usize = 0;
14271134
1428 var A: SignedDoubleLimb = 1;
1429 var B: SignedDoubleLimb = 0;
1430 var C: SignedDoubleLimb = 0;
1431 var D: SignedDoubleLimb = 1;
1135 // Power of two: can do a single pass and use masks to extract digits.
1136 if (math.isPowerOfTwo(base)) {
1137 const base_shift = math.log2_int(Limb, base);
14321138
1433 while (yh + C != 0 and yh + D != 0) {
1434 const q = @divFloor(xh + A, yh + C);
1435 const qp = @divFloor(xh + B, yh + D);
1436 if (q != qp) {
1437 break;
1139 outer: for (self.limbs[0..self.limbs.len]) |limb| {
1140 var shift: usize = 0;
1141 while (shift < Limb.bit_count) : (shift += base_shift) {
1142 const r = @intCast(u8, (limb >> @intCast(Log2Limb, shift)) & @as(Limb, base - 1));
1143 const ch = std.fmt.digitToChar(r, uppercase);
1144 string[digits_len] = ch;
1145 digits_len += 1;
1146 // If we hit the end, it must be all zeroes from here.
1147 if (digits_len == string.len) break :outer;
14381148 }
1149 }
14391150
1440 var t = A - q * C;
1441 A = C;
1442 C = t;
1443 t = B - q * D;
1444 B = D;
1445 D = t;
1446
1447 t = xh - q * yh;
1448 xh = yh;
1449 yh = t;
1151 // Always will have a non-zero digit somewhere.
1152 while (string[digits_len - 1] == '0') {
1153 digits_len -= 1;
1154 }
1155 } else {
1156 // Non power-of-two: batch divisions per word size.
1157 const digits_per_limb = math.log(Limb, base, maxInt(Limb));
1158 var limb_base: Limb = 1;
1159 var j: usize = 0;
1160 while (j < digits_per_limb) : (j += 1) {
1161 limb_base *= base;
14501162 }
1163 const b: Const = .{ .limbs = &[_]Limb{limb_base}, .positive = true };
14511164
1452 if (B == 0) {
1453 // T = x % y, r is unused
1454 try Int.divTrunc(r, &T, x, y);
1455 debug.assert(T.isPositive());
1165 var q: Mutable = .{
1166 .limbs = limbs_buffer[0 .. self.limbs.len + 2],
1167 .positive = true, // Make absolute by ignoring self.positive.
1168 .len = self.limbs.len,
1169 };
1170 mem.copy(Limb, q.limbs, self.limbs);
14561171
1457 x.swap(&y);
1458 y.swap(&T);
1459 } else {
1460 var storage: [8]Limb = undefined;
1461 const Ap = FixedIntFromSignedDoubleLimb(A, storage[0..2]);
1462 const Bp = FixedIntFromSignedDoubleLimb(B, storage[2..4]);
1463 const Cp = FixedIntFromSignedDoubleLimb(C, storage[4..6]);
1464 const Dp = FixedIntFromSignedDoubleLimb(D, storage[6..8]);
1172 var r: Mutable = .{
1173 .limbs = limbs_buffer[q.limbs.len..][0..self.limbs.len],
1174 .positive = true,
1175 .len = 1,
1176 };
1177 r.limbs[0] = 0;
14651178
1466 // T = Ax + By
1467 try r.mul(x, Ap);
1468 try T.mul(y, Bp);
1469 try T.add(r.*, T);
1179 const rest_of_the_limbs_buf = limbs_buffer[q.limbs.len + r.limbs.len ..];
14701180
1471 // u = Cx + Dy, r as u
1472 try x.mul(x, Cp);
1473 try r.mul(y, Dp);
1474 try r.add(x, r.*);
1181 while (q.len >= 2) {
1182 // Passing an allocator here would not be helpful since this division is destroying
1183 // information, not creating it. [TODO citation needed]
1184 q.divTrunc(&r, q.toConst(), b, rest_of_the_limbs_buf, null);
14751185
1476 x.swap(&T);
1477 y.swap(r);
1186 var r_word = r.limbs[0];
1187 var i: usize = 0;
1188 while (i < digits_per_limb) : (i += 1) {
1189 const ch = std.fmt.digitToChar(@intCast(u8, r_word % base), uppercase);
1190 r_word /= base;
1191 string[digits_len] = ch;
1192 digits_len += 1;
1193 }
14781194 }
1479 }
14801195
1481 // euclidean algorithm
1482 debug.assert(x.cmp(y) != .lt);
1196 {
1197 assert(q.len == 1);
14831198
1484 while (!y.eqZero()) {
1485 try Int.divTrunc(&T, r, x, y);
1486 x.swap(&y);
1487 y.swap(r);
1199 var r_word = q.limbs[0];
1200 while (r_word != 0) {
1201 const ch = std.fmt.digitToChar(@intCast(u8, r_word % base), uppercase);
1202 r_word /= base;
1203 string[digits_len] = ch;
1204 digits_len += 1;
1205 }
1206 }
14881207 }
14891208
1490 r.swap(&x);
1491 }
1492};
1493
1494// Storage must live for the lifetime of the returned value
1495fn FixedIntFromSignedDoubleLimb(A: SignedDoubleLimb, storage: []Limb) Int {
1496 std.debug.assert(storage.len >= 2);
1497
1498 var A_is_positive = A >= 0;
1499 const Au = @intCast(DoubleLimb, if (A < 0) -A else A);
1500 storage[0] = @truncate(Limb, Au);
1501 storage[1] = @truncate(Limb, Au >> Limb.bit_count);
1502 var Ap = Int.initFixed(storage[0..2]);
1503 Ap.setSign(A_is_positive);
1504 return Ap;
1505}
1506
1507// NOTE: All the following tests assume the max machine-word will be 64-bit.
1508//
1509// They will still run on larger than this and should pass, but the multi-limb code-paths
1510// may be untested in some cases.
1511
1512test "big.int comptime_int set" {
1513 comptime var s = 0xefffffff00000001eeeeeeefaaaaaaab;
1514 var a = try Int.initSet(testing.allocator, s);
1515 defer a.deinit();
1516
1517 const s_limb_count = 128 / Limb.bit_count;
1209 if (!self.positive) {
1210 string[digits_len] = '-';
1211 digits_len += 1;
1212 }
15181213
1519 comptime var i: usize = 0;
1520 inline while (i < s_limb_count) : (i += 1) {
1521 const result = @as(Limb, s & maxInt(Limb));
1522 s >>= Limb.bit_count / 2;
1523 s >>= Limb.bit_count / 2;
1524 testing.expect(a.limbs[i] == result);
1214 const s = string[0..digits_len];
1215 mem.reverse(u8, s);
1216 return s.len;
15251217 }
1526}
1527
1528test "big.int comptime_int set negative" {
1529 var a = try Int.initSet(testing.allocator, -10);
1530 defer a.deinit();
1531
1532 testing.expect(a.limbs[0] == 10);
1533 testing.expect(a.isPositive() == false);
1534}
1535
1536test "big.int int set unaligned small" {
1537 var a = try Int.initSet(testing.allocator, @as(u7, 45));
1538 defer a.deinit();
1539
1540 testing.expect(a.limbs[0] == 45);
1541 testing.expect(a.isPositive() == true);
1542}
1543
1544test "big.int comptime_int to" {
1545 const a = try Int.initSet(testing.allocator, 0xefffffff00000001eeeeeeefaaaaaaab);
1546 defer a.deinit();
1547
1548 testing.expect((try a.to(u128)) == 0xefffffff00000001eeeeeeefaaaaaaab);
1549}
1550
1551test "big.int sub-limb to" {
1552 const a = try Int.initSet(testing.allocator, 10);
1553 defer a.deinit();
1554
1555 testing.expect((try a.to(u8)) == 10);
1556}
1557
1558test "big.int to target too small error" {
1559 const a = try Int.initSet(testing.allocator, 0xffffffff);
1560 defer a.deinit();
1561
1562 testing.expectError(error.TargetTooSmall, a.to(u8));
1563}
1564
1565test "big.int normalize" {
1566 var a = try Int.init(testing.allocator);
1567 defer a.deinit();
1568 try a.ensureCapacity(8);
1569
1570 a.limbs[0] = 1;
1571 a.limbs[1] = 2;
1572 a.limbs[2] = 3;
1573 a.limbs[3] = 0;
1574 a.normalize(4);
1575 testing.expect(a.len() == 3);
1576
1577 a.limbs[0] = 1;
1578 a.limbs[1] = 2;
1579 a.limbs[2] = 3;
1580 a.normalize(3);
1581 testing.expect(a.len() == 3);
1582
1583 a.limbs[0] = 0;
1584 a.limbs[1] = 0;
1585 a.normalize(2);
1586 testing.expect(a.len() == 1);
1587
1588 a.limbs[0] = 0;
1589 a.normalize(1);
1590 testing.expect(a.len() == 1);
1591}
1592
1593test "big.int normalize multi" {
1594 var a = try Int.init(testing.allocator);
1595 defer a.deinit();
1596 try a.ensureCapacity(8);
1597
1598 a.limbs[0] = 1;
1599 a.limbs[1] = 2;
1600 a.limbs[2] = 0;
1601 a.limbs[3] = 0;
1602 a.normalize(4);
1603 testing.expect(a.len() == 2);
1604
1605 a.limbs[0] = 1;
1606 a.limbs[1] = 2;
1607 a.limbs[2] = 3;
1608 a.normalize(3);
1609 testing.expect(a.len() == 3);
1610
1611 a.limbs[0] = 0;
1612 a.limbs[1] = 0;
1613 a.limbs[2] = 0;
1614 a.limbs[3] = 0;
1615 a.normalize(4);
1616 testing.expect(a.len() == 1);
1617
1618 a.limbs[0] = 0;
1619 a.normalize(1);
1620 testing.expect(a.len() == 1);
1621}
1622
1623test "big.int parity" {
1624 var a = try Int.init(testing.allocator);
1625 defer a.deinit();
1626
1627 try a.set(0);
1628 testing.expect(a.isEven());
1629 testing.expect(!a.isOdd());
1630
1631 try a.set(7);
1632 testing.expect(!a.isEven());
1633 testing.expect(a.isOdd());
1634}
1635
1636test "big.int bitcount + sizeInBase" {
1637 var a = try Int.init(testing.allocator);
1638 defer a.deinit();
1639
1640 try a.set(0b100);
1641 testing.expect(a.bitCountAbs() == 3);
1642 testing.expect(a.sizeInBase(2) >= 3);
1643 testing.expect(a.sizeInBase(10) >= 1);
1644
1645 a.negate();
1646 testing.expect(a.bitCountAbs() == 3);
1647 testing.expect(a.sizeInBase(2) >= 4);
1648 testing.expect(a.sizeInBase(10) >= 2);
1649
1650 try a.set(0xffffffff);
1651 testing.expect(a.bitCountAbs() == 32);
1652 testing.expect(a.sizeInBase(2) >= 32);
1653 testing.expect(a.sizeInBase(10) >= 10);
1654
1655 try a.shiftLeft(a, 5000);
1656 testing.expect(a.bitCountAbs() == 5032);
1657 testing.expect(a.sizeInBase(2) >= 5032);
1658 a.setSign(false);
1659
1660 testing.expect(a.bitCountAbs() == 5032);
1661 testing.expect(a.sizeInBase(2) >= 5033);
1662}
1663
1664test "big.int bitcount/to" {
1665 var a = try Int.init(testing.allocator);
1666 defer a.deinit();
16671218
1668 try a.set(0);
1669 testing.expect(a.bitCountTwosComp() == 0);
1670
1671 testing.expect((try a.to(u0)) == 0);
1672 testing.expect((try a.to(i0)) == 0);
1673
1674 try a.set(-1);
1675 testing.expect(a.bitCountTwosComp() == 1);
1676 testing.expect((try a.to(i1)) == -1);
1677
1678 try a.set(-8);
1679 testing.expect(a.bitCountTwosComp() == 4);
1680 testing.expect((try a.to(i4)) == -8);
1681
1682 try a.set(127);
1683 testing.expect(a.bitCountTwosComp() == 7);
1684 testing.expect((try a.to(u7)) == 127);
1685
1686 try a.set(-128);
1687 testing.expect(a.bitCountTwosComp() == 8);
1688 testing.expect((try a.to(i8)) == -128);
1689
1690 try a.set(-129);
1691 testing.expect(a.bitCountTwosComp() == 9);
1692 testing.expect((try a.to(i9)) == -129);
1693}
1694
1695test "big.int fits" {
1696 var a = try Int.init(testing.allocator);
1697 defer a.deinit();
1698
1699 try a.set(0);
1700 testing.expect(a.fits(u0));
1701 testing.expect(a.fits(i0));
1702
1703 try a.set(255);
1704 testing.expect(!a.fits(u0));
1705 testing.expect(!a.fits(u1));
1706 testing.expect(!a.fits(i8));
1707 testing.expect(a.fits(u8));
1708 testing.expect(a.fits(u9));
1709 testing.expect(a.fits(i9));
1710
1711 try a.set(-128);
1712 testing.expect(!a.fits(i7));
1713 testing.expect(a.fits(i8));
1714 testing.expect(a.fits(i9));
1715 testing.expect(!a.fits(u9));
1716
1717 try a.set(0x1ffffffffeeeeeeee);
1718 testing.expect(!a.fits(u32));
1719 testing.expect(!a.fits(u64));
1720 testing.expect(a.fits(u65));
1721}
1722
1723test "big.int string set" {
1724 var a = try Int.init(testing.allocator);
1725 defer a.deinit();
1726
1727 try a.setString(10, "120317241209124781241290847124");
1728 testing.expect((try a.to(u128)) == 120317241209124781241290847124);
1729}
1730
1731test "big.int string negative" {
1732 var a = try Int.init(testing.allocator);
1733 defer a.deinit();
1734
1735 try a.setString(10, "-1023");
1736 testing.expect((try a.to(i32)) == -1023);
1737}
1738
1739test "big.int string set number with underscores" {
1740 var a = try Int.init(testing.allocator);
1741 defer a.deinit();
1742
1743 try a.setString(10, "__1_2_0_3_1_7_2_4_1_2_0_____9_1__2__4_7_8_1_2_4_1_2_9_0_8_4_7_1_2_4___");
1744 testing.expect((try a.to(u128)) == 120317241209124781241290847124);
1745}
1746
1747test "big.int string set case insensitive number" {
1748 var a = try Int.init(testing.allocator);
1749 defer a.deinit();
1750
1751 try a.setString(16, "aB_cD_eF");
1752 testing.expect((try a.to(u32)) == 0xabcdef);
1753}
1754
1755test "big.int string set bad char error" {
1756 var a = try Int.init(testing.allocator);
1757 defer a.deinit();
1758 testing.expectError(error.InvalidCharForDigit, a.setString(10, "x"));
1759}
1760
1761test "big.int string set bad base error" {
1762 var a = try Int.init(testing.allocator);
1763 defer a.deinit();
1764 testing.expectError(error.InvalidBase, a.setString(45, "10"));
1765}
1766
1767test "big.int string to" {
1768 const a = try Int.initSet(testing.allocator, 120317241209124781241290847124);
1769 defer a.deinit();
1770
1771 const as = try a.toString(testing.allocator, 10, false);
1772 defer testing.allocator.free(as);
1773 const es = "120317241209124781241290847124";
1774
1775 testing.expect(mem.eql(u8, as, es));
1776}
1777
1778test "big.int string to base base error" {
1779 const a = try Int.initSet(testing.allocator, 0xffffffff);
1780 defer a.deinit();
1781
1782 testing.expectError(error.InvalidBase, a.toString(testing.allocator, 45, false));
1783}
1784
1785test "big.int string to base 2" {
1786 const a = try Int.initSet(testing.allocator, -0b1011);
1787 defer a.deinit();
1788
1789 const as = try a.toString(testing.allocator, 2, false);
1790 defer testing.allocator.free(as);
1791 const es = "-1011";
1792
1793 testing.expect(mem.eql(u8, as, es));
1794}
1795
1796test "big.int string to base 16" {
1797 const a = try Int.initSet(testing.allocator, 0xefffffff00000001eeeeeeefaaaaaaab);
1798 defer a.deinit();
1799
1800 const as = try a.toString(testing.allocator, 16, false);
1801 defer testing.allocator.free(as);
1802 const es = "efffffff00000001eeeeeeefaaaaaaab";
1803
1804 testing.expect(mem.eql(u8, as, es));
1805}
1806
1807test "big.int neg string to" {
1808 const a = try Int.initSet(testing.allocator, -123907434);
1809 defer a.deinit();
1810
1811 const as = try a.toString(testing.allocator, 10, false);
1812 defer testing.allocator.free(as);
1813 const es = "-123907434";
1814
1815 testing.expect(mem.eql(u8, as, es));
1816}
1817
1818test "big.int zero string to" {
1819 const a = try Int.initSet(testing.allocator, 0);
1820 defer a.deinit();
1821
1822 const as = try a.toString(testing.allocator, 10, false);
1823 defer testing.allocator.free(as);
1824 const es = "0";
1825
1826 testing.expect(mem.eql(u8, as, es));
1827}
1828
1829test "big.int clone" {
1830 var a = try Int.initSet(testing.allocator, 1234);
1831 defer a.deinit();
1832 const b = try a.clone();
1833 defer b.deinit();
1834
1835 testing.expect((try a.to(u32)) == 1234);
1836 testing.expect((try b.to(u32)) == 1234);
1837
1838 try a.set(77);
1839 testing.expect((try a.to(u32)) == 77);
1840 testing.expect((try b.to(u32)) == 1234);
1841}
1842
1843test "big.int swap" {
1844 var a = try Int.initSet(testing.allocator, 1234);
1845 defer a.deinit();
1846 var b = try Int.initSet(testing.allocator, 5678);
1847 defer b.deinit();
1848
1849 testing.expect((try a.to(u32)) == 1234);
1850 testing.expect((try b.to(u32)) == 5678);
1851
1852 a.swap(&b);
1853
1854 testing.expect((try a.to(u32)) == 5678);
1855 testing.expect((try b.to(u32)) == 1234);
1856}
1857
1858test "big.int to negative" {
1859 var a = try Int.initSet(testing.allocator, -10);
1860 defer a.deinit();
1861
1862 testing.expect((try a.to(i32)) == -10);
1863}
1864
1865test "big.int compare" {
1866 var a = try Int.initSet(testing.allocator, -11);
1867 defer a.deinit();
1868 var b = try Int.initSet(testing.allocator, 10);
1869 defer b.deinit();
1870
1871 testing.expect(a.cmpAbs(b) == .gt);
1872 testing.expect(a.cmp(b) == .lt);
1873}
1874
1875test "big.int compare similar" {
1876 var a = try Int.initSet(testing.allocator, 0xffffffffeeeeeeeeffffffffeeeeeeee);
1877 defer a.deinit();
1878 var b = try Int.initSet(testing.allocator, 0xffffffffeeeeeeeeffffffffeeeeeeef);
1879 defer b.deinit();
1880
1881 testing.expect(a.cmpAbs(b) == .lt);
1882 testing.expect(b.cmpAbs(a) == .gt);
1883}
1884
1885test "big.int compare different limb size" {
1886 var a = try Int.initSet(testing.allocator, maxInt(Limb) + 1);
1887 defer a.deinit();
1888 var b = try Int.initSet(testing.allocator, 1);
1889 defer b.deinit();
1890
1891 testing.expect(a.cmpAbs(b) == .gt);
1892 testing.expect(b.cmpAbs(a) == .lt);
1893}
1894
1895test "big.int compare multi-limb" {
1896 var a = try Int.initSet(testing.allocator, -0x7777777799999999ffffeeeeffffeeeeffffeeeef);
1897 defer a.deinit();
1898 var b = try Int.initSet(testing.allocator, 0x7777777799999999ffffeeeeffffeeeeffffeeeee);
1899 defer b.deinit();
1900
1901 testing.expect(a.cmpAbs(b) == .gt);
1902 testing.expect(a.cmp(b) == .lt);
1903}
1904
1905test "big.int equality" {
1906 var a = try Int.initSet(testing.allocator, 0xffffffff1);
1907 defer a.deinit();
1908 var b = try Int.initSet(testing.allocator, -0xffffffff1);
1909 defer b.deinit();
1910
1911 testing.expect(a.eqAbs(b));
1912 testing.expect(!a.eq(b));
1913}
1914
1915test "big.int abs" {
1916 var a = try Int.initSet(testing.allocator, -5);
1917 defer a.deinit();
1918
1919 a.abs();
1920 testing.expect((try a.to(u32)) == 5);
1921
1922 a.abs();
1923 testing.expect((try a.to(u32)) == 5);
1924}
1925
1926test "big.int negate" {
1927 var a = try Int.initSet(testing.allocator, 5);
1928 defer a.deinit();
1929
1930 a.negate();
1931 testing.expect((try a.to(i32)) == -5);
1932
1933 a.negate();
1934 testing.expect((try a.to(i32)) == 5);
1935}
1936
1937test "big.int add single-single" {
1938 var a = try Int.initSet(testing.allocator, 50);
1939 defer a.deinit();
1940 var b = try Int.initSet(testing.allocator, 5);
1941 defer b.deinit();
1942
1943 var c = try Int.init(testing.allocator);
1944 defer c.deinit();
1945 try c.add(a, b);
1946
1947 testing.expect((try c.to(u32)) == 55);
1948}
1949
1950test "big.int add multi-single" {
1951 var a = try Int.initSet(testing.allocator, maxInt(Limb) + 1);
1952 defer a.deinit();
1953 var b = try Int.initSet(testing.allocator, 1);
1954 defer b.deinit();
1955
1956 var c = try Int.init(testing.allocator);
1957 defer c.deinit();
1958
1959 try c.add(a, b);
1960 testing.expect((try c.to(DoubleLimb)) == maxInt(Limb) + 2);
1961
1962 try c.add(b, a);
1963 testing.expect((try c.to(DoubleLimb)) == maxInt(Limb) + 2);
1964}
1965
1966test "big.int add multi-multi" {
1967 const op1 = 0xefefefef7f7f7f7f;
1968 const op2 = 0xfefefefe9f9f9f9f;
1969 var a = try Int.initSet(testing.allocator, op1);
1970 defer a.deinit();
1971 var b = try Int.initSet(testing.allocator, op2);
1972 defer b.deinit();
1973
1974 var c = try Int.init(testing.allocator);
1975 defer c.deinit();
1976 try c.add(a, b);
1977
1978 testing.expect((try c.to(u128)) == op1 + op2);
1979}
1980
1981test "big.int add zero-zero" {
1982 var a = try Int.initSet(testing.allocator, 0);
1983 defer a.deinit();
1984 var b = try Int.initSet(testing.allocator, 0);
1985 defer b.deinit();
1986
1987 var c = try Int.init(testing.allocator);
1988 defer c.deinit();
1989 try c.add(a, b);
1990
1991 testing.expect((try c.to(u32)) == 0);
1992}
1993
1994test "big.int add alias multi-limb nonzero-zero" {
1995 const op1 = 0xffffffff777777771;
1996 var a = try Int.initSet(testing.allocator, op1);
1997 defer a.deinit();
1998 var b = try Int.initSet(testing.allocator, 0);
1999 defer b.deinit();
2000
2001 try a.add(a, b);
2002
2003 testing.expect((try a.to(u128)) == op1);
2004}
2005
2006test "big.int add sign" {
2007 var a = try Int.init(testing.allocator);
2008 defer a.deinit();
2009
2010 const one = try Int.initSet(testing.allocator, 1);
2011 defer one.deinit();
2012 const two = try Int.initSet(testing.allocator, 2);
2013 defer two.deinit();
2014 const neg_one = try Int.initSet(testing.allocator, -1);
2015 defer neg_one.deinit();
2016 const neg_two = try Int.initSet(testing.allocator, -2);
2017 defer neg_two.deinit();
2018
2019 try a.add(one, two);
2020 testing.expect((try a.to(i32)) == 3);
2021
2022 try a.add(neg_one, two);
2023 testing.expect((try a.to(i32)) == 1);
2024
2025 try a.add(one, neg_two);
2026 testing.expect((try a.to(i32)) == -1);
2027
2028 try a.add(neg_one, neg_two);
2029 testing.expect((try a.to(i32)) == -3);
2030}
2031
2032test "big.int sub single-single" {
2033 var a = try Int.initSet(testing.allocator, 50);
2034 defer a.deinit();
2035 var b = try Int.initSet(testing.allocator, 5);
2036 defer b.deinit();
2037
2038 var c = try Int.init(testing.allocator);
2039 defer c.deinit();
2040 try c.sub(a, b);
2041
2042 testing.expect((try c.to(u32)) == 45);
2043}
2044
2045test "big.int sub multi-single" {
2046 var a = try Int.initSet(testing.allocator, maxInt(Limb) + 1);
2047 defer a.deinit();
2048 var b = try Int.initSet(testing.allocator, 1);
2049 defer b.deinit();
2050
2051 var c = try Int.init(testing.allocator);
2052 defer c.deinit();
2053 try c.sub(a, b);
2054
2055 testing.expect((try c.to(Limb)) == maxInt(Limb));
2056}
2057
2058test "big.int sub multi-multi" {
2059 const op1 = 0xefefefefefefefefefefefef;
2060 const op2 = 0xabababababababababababab;
2061
2062 var a = try Int.initSet(testing.allocator, op1);
2063 defer a.deinit();
2064 var b = try Int.initSet(testing.allocator, op2);
2065 defer b.deinit();
2066
2067 var c = try Int.init(testing.allocator);
2068 defer c.deinit();
2069 try c.sub(a, b);
2070
2071 testing.expect((try c.to(u128)) == op1 - op2);
2072}
2073
2074test "big.int sub equal" {
2075 var a = try Int.initSet(testing.allocator, 0x11efefefefefefefefefefefef);
2076 defer a.deinit();
2077 var b = try Int.initSet(testing.allocator, 0x11efefefefefefefefefefefef);
2078 defer b.deinit();
2079
2080 var c = try Int.init(testing.allocator);
2081 defer c.deinit();
2082 try c.sub(a, b);
2083
2084 testing.expect((try c.to(u32)) == 0);
2085}
2086
2087test "big.int sub sign" {
2088 var a = try Int.init(testing.allocator);
2089 defer a.deinit();
2090
2091 const one = try Int.initSet(testing.allocator, 1);
2092 defer one.deinit();
2093 const two = try Int.initSet(testing.allocator, 2);
2094 defer two.deinit();
2095 const neg_one = try Int.initSet(testing.allocator, -1);
2096 defer neg_one.deinit();
2097 const neg_two = try Int.initSet(testing.allocator, -2);
2098 defer neg_two.deinit();
2099
2100 try a.sub(one, two);
2101 testing.expect((try a.to(i32)) == -1);
2102
2103 try a.sub(neg_one, two);
2104 testing.expect((try a.to(i32)) == -3);
2105
2106 try a.sub(one, neg_two);
2107 testing.expect((try a.to(i32)) == 3);
2108
2109 try a.sub(neg_one, neg_two);
2110 testing.expect((try a.to(i32)) == 1);
2111
2112 try a.sub(neg_two, neg_one);
2113 testing.expect((try a.to(i32)) == -1);
2114}
2115
2116test "big.int mul single-single" {
2117 var a = try Int.initSet(testing.allocator, 50);
2118 defer a.deinit();
2119 var b = try Int.initSet(testing.allocator, 5);
2120 defer b.deinit();
2121
2122 var c = try Int.init(testing.allocator);
2123 defer c.deinit();
2124 try c.mul(a, b);
2125
2126 testing.expect((try c.to(u64)) == 250);
2127}
2128
2129test "big.int mul multi-single" {
2130 var a = try Int.initSet(testing.allocator, maxInt(Limb));
2131 defer a.deinit();
2132 var b = try Int.initSet(testing.allocator, 2);
2133 defer b.deinit();
2134
2135 var c = try Int.init(testing.allocator);
2136 defer c.deinit();
2137 try c.mul(a, b);
2138
2139 testing.expect((try c.to(DoubleLimb)) == 2 * maxInt(Limb));
2140}
2141
2142test "big.int mul multi-multi" {
2143 const op1 = 0x998888efefefefefefefef;
2144 const op2 = 0x333000abababababababab;
2145 var a = try Int.initSet(testing.allocator, op1);
2146 defer a.deinit();
2147 var b = try Int.initSet(testing.allocator, op2);
2148 defer b.deinit();
2149
2150 var c = try Int.init(testing.allocator);
2151 defer c.deinit();
2152 try c.mul(a, b);
2153
2154 testing.expect((try c.to(u256)) == op1 * op2);
2155}
2156
2157test "big.int mul alias r with a" {
2158 var a = try Int.initSet(testing.allocator, maxInt(Limb));
2159 defer a.deinit();
2160 var b = try Int.initSet(testing.allocator, 2);
2161 defer b.deinit();
2162
2163 try a.mul(a, b);
2164
2165 testing.expect((try a.to(DoubleLimb)) == 2 * maxInt(Limb));
2166}
2167
2168test "big.int mul alias r with b" {
2169 var a = try Int.initSet(testing.allocator, maxInt(Limb));
2170 defer a.deinit();
2171 var b = try Int.initSet(testing.allocator, 2);
2172 defer b.deinit();
2173
2174 try a.mul(b, a);
2175
2176 testing.expect((try a.to(DoubleLimb)) == 2 * maxInt(Limb));
2177}
2178
2179test "big.int mul alias r with a and b" {
2180 var a = try Int.initSet(testing.allocator, maxInt(Limb));
2181 defer a.deinit();
2182
2183 try a.mul(a, a);
2184
2185 testing.expect((try a.to(DoubleLimb)) == maxInt(Limb) * maxInt(Limb));
2186}
2187
2188test "big.int mul a*0" {
2189 var a = try Int.initSet(testing.allocator, 0xefefefefefefefef);
2190 defer a.deinit();
2191 var b = try Int.initSet(testing.allocator, 0);
2192 defer b.deinit();
2193
2194 var c = try Int.init(testing.allocator);
2195 defer c.deinit();
2196 try c.mul(a, b);
2197
2198 testing.expect((try c.to(u32)) == 0);
2199}
2200
2201test "big.int mul 0*0" {
2202 var a = try Int.initSet(testing.allocator, 0);
2203 defer a.deinit();
2204 var b = try Int.initSet(testing.allocator, 0);
2205 defer b.deinit();
2206
2207 var c = try Int.init(testing.allocator);
2208 defer c.deinit();
2209 try c.mul(a, b);
2210
2211 testing.expect((try c.to(u32)) == 0);
2212}
2213
2214test "big.int div single-single no rem" {
2215 var a = try Int.initSet(testing.allocator, 50);
2216 defer a.deinit();
2217 var b = try Int.initSet(testing.allocator, 5);
2218 defer b.deinit();
2219
2220 var q = try Int.init(testing.allocator);
2221 defer q.deinit();
2222 var r = try Int.init(testing.allocator);
2223 defer r.deinit();
2224 try Int.divTrunc(&q, &r, a, b);
2225
2226 testing.expect((try q.to(u32)) == 10);
2227 testing.expect((try r.to(u32)) == 0);
2228}
2229
2230test "big.int div single-single with rem" {
2231 var a = try Int.initSet(testing.allocator, 49);
2232 defer a.deinit();
2233 var b = try Int.initSet(testing.allocator, 5);
2234 defer b.deinit();
2235
2236 var q = try Int.init(testing.allocator);
2237 defer q.deinit();
2238 var r = try Int.init(testing.allocator);
2239 defer r.deinit();
2240 try Int.divTrunc(&q, &r, a, b);
2241
2242 testing.expect((try q.to(u32)) == 9);
2243 testing.expect((try r.to(u32)) == 4);
2244}
2245
2246test "big.int div multi-single no rem" {
2247 const op1 = 0xffffeeeeddddcccc;
2248 const op2 = 34;
2249
2250 var a = try Int.initSet(testing.allocator, op1);
2251 defer a.deinit();
2252 var b = try Int.initSet(testing.allocator, op2);
2253 defer b.deinit();
2254
2255 var q = try Int.init(testing.allocator);
2256 defer q.deinit();
2257 var r = try Int.init(testing.allocator);
2258 defer r.deinit();
2259 try Int.divTrunc(&q, &r, a, b);
2260
2261 testing.expect((try q.to(u64)) == op1 / op2);
2262 testing.expect((try r.to(u64)) == 0);
2263}
2264
2265test "big.int div multi-single with rem" {
2266 const op1 = 0xffffeeeeddddcccf;
2267 const op2 = 34;
2268
2269 var a = try Int.initSet(testing.allocator, op1);
2270 defer a.deinit();
2271 var b = try Int.initSet(testing.allocator, op2);
2272 defer b.deinit();
2273
2274 var q = try Int.init(testing.allocator);
2275 defer q.deinit();
2276 var r = try Int.init(testing.allocator);
2277 defer r.deinit();
2278 try Int.divTrunc(&q, &r, a, b);
1219 /// Returns `math.Order.lt`, `math.Order.eq`, `math.Order.gt` if
1220 /// `|a| < |b|`, `|a| == |b|`, or `|a| > |b|` respectively.
1221 pub fn orderAbs(a: Const, b: Const) math.Order {
1222 if (a.limbs.len < b.limbs.len) {
1223 return .lt;
1224 }
1225 if (a.limbs.len > b.limbs.len) {
1226 return .gt;
1227 }
22791228
2280 testing.expect((try q.to(u64)) == op1 / op2);
2281 testing.expect((try r.to(u64)) == 3);
2282}
1229 var i: usize = a.limbs.len - 1;
1230 while (i != 0) : (i -= 1) {
1231 if (a.limbs[i] != b.limbs[i]) {
1232 break;
1233 }
1234 }
22831235
2284test "big.int div multi>2-single" {
2285 const op1 = 0xfefefefefefefefefefefefefefefefe;
2286 const op2 = 0xefab8;
1236 if (a.limbs[i] < b.limbs[i]) {
1237 return .lt;
1238 } else if (a.limbs[i] > b.limbs[i]) {
1239 return .gt;
1240 } else {
1241 return .eq;
1242 }
1243 }
22871244
2288 var a = try Int.initSet(testing.allocator, op1);
2289 defer a.deinit();
2290 var b = try Int.initSet(testing.allocator, op2);
2291 defer b.deinit();
1245 /// Returns `math.Order.lt`, `math.Order.eq`, `math.Order.gt` if `a < b`, `a == b` or `a > b` respectively.
1246 pub fn order(a: Const, b: Const) math.Order {
1247 if (a.positive != b.positive) {
1248 return if (a.positive) .gt else .lt;
1249 } else {
1250 const r = orderAbs(a, b);
1251 return if (a.positive) r else switch (r) {
1252 .lt => math.Order.gt,
1253 .eq => math.Order.eq,
1254 .gt => math.Order.lt,
1255 };
1256 }
1257 }
22921258
2293 var q = try Int.init(testing.allocator);
2294 defer q.deinit();
2295 var r = try Int.init(testing.allocator);
2296 defer r.deinit();
2297 try Int.divTrunc(&q, &r, a, b);
1259 /// Same as `order` but the right-hand operand is a primitive integer.
1260 pub fn orderAgainstScalar(lhs: Const, scalar: var) math.Order {
1261 var limbs: [calcLimbLen(scalar)]Limb = undefined;
1262 const rhs = Mutable.init(&limbs, scalar);
1263 return order(lhs, rhs.toConst());
1264 }
22981265
2299 testing.expect((try q.to(u128)) == op1 / op2);
2300 testing.expect((try r.to(u32)) == 0x3e4e);
2301}
1266 /// Returns true if `a == 0`.
1267 pub fn eqZero(a: Const) bool {
1268 return a.limbs.len == 1 and a.limbs[0] == 0;
1269 }
23021270
2303test "big.int div single-single q < r" {
2304 var a = try Int.initSet(testing.allocator, 0x0078f432);
2305 defer a.deinit();
2306 var b = try Int.initSet(testing.allocator, 0x01000000);
2307 defer b.deinit();
1271 /// Returns true if `|a| == |b|`.
1272 pub fn eqAbs(a: Const, b: Const) bool {
1273 return orderAbs(a, b) == .eq;
1274 }
23081275
2309 var q = try Int.init(testing.allocator);
2310 defer q.deinit();
2311 var r = try Int.init(testing.allocator);
2312 defer r.deinit();
2313 try Int.divTrunc(&q, &r, a, b);
1276 /// Returns true if `a == b`.
1277 pub fn eq(a: Const, b: Const) bool {
1278 return order(a, b) == .eq;
1279 }
1280};
23141281
2315 testing.expect((try q.to(u64)) == 0);
2316 testing.expect((try r.to(u64)) == 0x0078f432);
2317}
1282/// An arbitrary-precision big integer along with an allocator which manages the memory.
1283///
1284/// Memory is allocated as needed to ensure operations never overflow. The range
1285/// is bounded only by available memory.
1286pub const Managed = struct {
1287 pub const sign_bit: usize = 1 << (usize.bit_count - 1);
23181288
2319test "big.int div single-single q == r" {
2320 var a = try Int.initSet(testing.allocator, 10);
2321 defer a.deinit();
2322 var b = try Int.initSet(testing.allocator, 10);
2323 defer b.deinit();
1289 /// Default number of limbs to allocate on creation of a `Managed`.
1290 pub const default_capacity = 4;
23241291
2325 var q = try Int.init(testing.allocator);
2326 defer q.deinit();
2327 var r = try Int.init(testing.allocator);
2328 defer r.deinit();
2329 try Int.divTrunc(&q, &r, a, b);
1292 /// Allocator used by the Managed when requesting memory.
1293 allocator: *Allocator,
23301294
2331 testing.expect((try q.to(u64)) == 1);
2332 testing.expect((try r.to(u64)) == 0);
2333}
1295 /// Raw digits. These are:
1296 ///
1297 /// * Little-endian ordered
1298 /// * limbs.len >= 1
1299 /// * Zero is represent as Managed.len() == 1 with limbs[0] == 0.
1300 ///
1301 /// Accessing limbs directly should be avoided.
1302 limbs: []Limb,
23341303
2335test "big.int div q=0 alias" {
2336 var a = try Int.initSet(testing.allocator, 3);
2337 defer a.deinit();
2338 var b = try Int.initSet(testing.allocator, 10);
2339 defer b.deinit();
1304 /// High bit is the sign bit. If set, Managed is negative, else Managed is positive.
1305 /// The remaining bits represent the number of limbs used by Managed.
1306 metadata: usize,
23401307
2341 try Int.divTrunc(&a, &b, a, b);
1308 /// Creates a new `Managed`. `default_capacity` limbs will be allocated immediately.
1309 /// The integer value after initializing is `0`.
1310 pub fn init(allocator: *Allocator) !Managed {
1311 return initCapacity(allocator, default_capacity);
1312 }
23421313
2343 testing.expect((try a.to(u64)) == 0);
2344 testing.expect((try b.to(u64)) == 3);
2345}
1314 pub fn toMutable(self: Managed) Mutable {
1315 return .{
1316 .limbs = self.limbs,
1317 .positive = self.isPositive(),
1318 .len = self.len(),
1319 };
1320 }
23461321
2347test "big.int div multi-multi q < r" {
2348 const op1 = 0x1ffffffff0078f432;
2349 const op2 = 0x1ffffffff01000000;
2350 var a = try Int.initSet(testing.allocator, op1);
2351 defer a.deinit();
2352 var b = try Int.initSet(testing.allocator, op2);
2353 defer b.deinit();
2354
2355 var q = try Int.init(testing.allocator);
2356 defer q.deinit();
2357 var r = try Int.init(testing.allocator);
2358 defer r.deinit();
2359 try Int.divTrunc(&q, &r, a, b);
2360
2361 testing.expect((try q.to(u128)) == 0);
2362 testing.expect((try r.to(u128)) == op1);
2363}
1322 pub fn toConst(self: Managed) Const {
1323 return .{
1324 .limbs = self.limbs[0..self.len()],
1325 .positive = self.isPositive(),
1326 };
1327 }
23641328
2365test "big.int div trunc single-single +/+" {
2366 const u: i32 = 5;
2367 const v: i32 = 3;
1329 /// Creates a new `Managed` with value `value`.
1330 ///
1331 /// This is identical to an `init`, followed by a `set`.
1332 pub fn initSet(allocator: *Allocator, value: var) !Managed {
1333 var s = try Managed.init(allocator);
1334 try s.set(value);
1335 return s;
1336 }
23681337
2369 var a = try Int.initSet(testing.allocator, u);
2370 defer a.deinit();
2371 var b = try Int.initSet(testing.allocator, v);
2372 defer b.deinit();
1338 /// Creates a new Managed with a specific capacity. If capacity < default_capacity then the
1339 /// default capacity will be used instead.
1340 /// The integer value after initializing is `0`.
1341 pub fn initCapacity(allocator: *Allocator, capacity: usize) !Managed {
1342 return Managed{
1343 .allocator = allocator,
1344 .metadata = 1,
1345 .limbs = block: {
1346 const limbs = try allocator.alloc(Limb, math.max(default_capacity, capacity));
1347 limbs[0] = 0;
1348 break :block limbs;
1349 },
1350 };
1351 }
23731352
2374 var q = try Int.init(testing.allocator);
2375 defer q.deinit();
2376 var r = try Int.init(testing.allocator);
2377 defer r.deinit();
2378 try Int.divTrunc(&q, &r, a, b);
1353 /// Returns the number of limbs currently in use.
1354 pub fn len(self: Managed) usize {
1355 return self.metadata & ~sign_bit;
1356 }
23791357
2380 // n = q * d + r
2381 // 5 = 1 * 3 + 2
2382 const eq = @divTrunc(u, v);
2383 const er = @mod(u, v);
1358 /// Returns whether an Managed is positive.
1359 pub fn isPositive(self: Managed) bool {
1360 return self.metadata & sign_bit == 0;
1361 }
23841362
2385 testing.expect((try q.to(i32)) == eq);
2386 testing.expect((try r.to(i32)) == er);
2387}
1363 /// Sets the sign of an Managed.
1364 pub fn setSign(self: *Managed, positive: bool) void {
1365 if (positive) {
1366 self.metadata &= ~sign_bit;
1367 } else {
1368 self.metadata |= sign_bit;
1369 }
1370 }
23881371
2389test "big.int div trunc single-single -/+" {
2390 const u: i32 = -5;
2391 const v: i32 = 3;
1372 /// Sets the length of an Managed.
1373 ///
1374 /// If setLen is used, then the Managed must be normalized to suit.
1375 pub fn setLen(self: *Managed, new_len: usize) void {
1376 self.metadata &= sign_bit;
1377 self.metadata |= new_len;
1378 }
23921379
2393 var a = try Int.initSet(testing.allocator, u);
2394 defer a.deinit();
2395 var b = try Int.initSet(testing.allocator, v);
2396 defer b.deinit();
1380 pub fn setMetadata(self: *Managed, positive: bool, length: usize) void {
1381 self.metadata = if (positive) length & ~sign_bit else length | sign_bit;
1382 }
23971383
2398 var q = try Int.init(testing.allocator);
2399 defer q.deinit();
2400 var r = try Int.init(testing.allocator);
2401 defer r.deinit();
2402 try Int.divTrunc(&q, &r, a, b);
1384 /// Ensures an Managed has enough space allocated for capacity limbs. If the Managed does not have
1385 /// sufficient capacity, the exact amount will be allocated. This occurs even if the requested
1386 /// capacity is only greater than the current capacity by one limb.
1387 pub fn ensureCapacity(self: *Managed, capacity: usize) !void {
1388 if (capacity <= self.limbs.len) {
1389 return;
1390 }
1391 self.limbs = try self.allocator.realloc(self.limbs, capacity);
1392 }
24031393
2404 // n = q * d + r
2405 // -5 = 1 * -3 - 2
2406 const eq = -1;
2407 const er = -2;
1394 /// Frees all associated memory.
1395 pub fn deinit(self: *Managed) void {
1396 self.allocator.free(self.limbs);
1397 self.* = undefined;
1398 }
24081399
2409 testing.expect((try q.to(i32)) == eq);
2410 testing.expect((try r.to(i32)) == er);
2411}
1400 /// Returns a `Managed` with the same value. The returned `Managed` is a deep copy and
1401 /// can be modified separately from the original, and its resources are managed
1402 /// separately from the original.
1403 pub fn clone(other: Managed) !Managed {
1404 return other.cloneWithDifferentAllocator(other.allocator);
1405 }
24121406
2413test "big.int div trunc single-single +/-" {
2414 const u: i32 = 5;
2415 const v: i32 = -3;
1407 pub fn cloneWithDifferentAllocator(other: Managed, allocator: *Allocator) !Managed {
1408 return Managed{
1409 .allocator = allocator,
1410 .metadata = other.metadata,
1411 .limbs = block: {
1412 var limbs = try allocator.alloc(Limb, other.len());
1413 mem.copy(Limb, limbs[0..], other.limbs[0..other.len()]);
1414 break :block limbs;
1415 },
1416 };
1417 }
24161418
2417 var a = try Int.initSet(testing.allocator, u);
2418 defer a.deinit();
2419 var b = try Int.initSet(testing.allocator, v);
2420 defer b.deinit();
1419 /// Copies the value of the integer to an existing `Managed` so that they both have the same value.
1420 /// Extra memory will be allocated if the receiver does not have enough capacity.
1421 pub fn copy(self: *Managed, other: Const) !void {
1422 if (self.limbs.ptr == other.limbs.ptr) return;
24211423
2422 var q = try Int.init(testing.allocator);
2423 defer q.deinit();
2424 var r = try Int.init(testing.allocator);
2425 defer r.deinit();
2426 try Int.divTrunc(&q, &r, a, b);
1424 try self.ensureCapacity(other.limbs.len);
1425 mem.copy(Limb, self.limbs[0..], other.limbs[0..other.limbs.len]);
1426 self.setMetadata(other.positive, other.limbs.len);
1427 }
24271428
2428 // n = q * d + r
2429 // 5 = -1 * -3 + 2
2430 const eq = -1;
2431 const er = 2;
1429 /// Efficiently swap a `Managed` with another. This swaps the limb pointers and a full copy is not
1430 /// performed. The address of the limbs field will not be the same after this function.
1431 pub fn swap(self: *Managed, other: *Managed) void {
1432 mem.swap(Managed, self, other);
1433 }
24321434
2433 testing.expect((try q.to(i32)) == eq);
2434 testing.expect((try r.to(i32)) == er);
2435}
1435 /// Debugging tool: prints the state to stderr.
1436 pub fn dump(self: Managed) void {
1437 for (self.limbs[0..self.len()]) |limb| {
1438 std.debug.warn("{x} ", .{limb});
1439 }
1440 std.debug.warn("capacity={} positive={}\n", .{ self.limbs.len, self.positive });
1441 }
24361442
2437test "big.int div trunc single-single -/-" {
2438 const u: i32 = -5;
2439 const v: i32 = -3;
1443 /// Negate the sign.
1444 pub fn negate(self: *Managed) void {
1445 self.metadata ^= sign_bit;
1446 }
24401447
2441 var a = try Int.initSet(testing.allocator, u);
2442 defer a.deinit();
2443 var b = try Int.initSet(testing.allocator, v);
2444 defer b.deinit();
1448 /// Make positive.
1449 pub fn abs(self: *Managed) void {
1450 self.metadata &= ~sign_bit;
1451 }
24451452
2446 var q = try Int.init(testing.allocator);
2447 defer q.deinit();
2448 var r = try Int.init(testing.allocator);
2449 defer r.deinit();
2450 try Int.divTrunc(&q, &r, a, b);
1453 pub fn isOdd(self: Managed) bool {
1454 return self.limbs[0] & 1 != 0;
1455 }
24511456
2452 // n = q * d + r
2453 // -5 = 1 * -3 - 2
2454 const eq = 1;
2455 const er = -2;
1457 pub fn isEven(self: Managed) bool {
1458 return !self.isOdd();
1459 }
24561460
2457 testing.expect((try q.to(i32)) == eq);
2458 testing.expect((try r.to(i32)) == er);
2459}
1461 /// Returns the number of bits required to represent the absolute value of an integer.
1462 pub fn bitCountAbs(self: Managed) usize {
1463 return self.toConst().bitCountAbs();
1464 }
24601465
2461test "big.int div floor single-single +/+" {
2462 const u: i32 = 5;
2463 const v: i32 = 3;
1466 /// Returns the number of bits required to represent the integer in twos-complement form.
1467 ///
1468 /// If the integer is negative the value returned is the number of bits needed by a signed
1469 /// integer to represent the value. If positive the value is the number of bits for an
1470 /// unsigned integer. Any unsigned integer will fit in the signed integer with bitcount
1471 /// one greater than the returned value.
1472 ///
1473 /// e.g. -127 returns 8 as it will fit in an i8. 127 returns 7 since it fits in a u7.
1474 pub fn bitCountTwosComp(self: Managed) usize {
1475 return self.toConst().bitCountTwosComp();
1476 }
24641477
2465 var a = try Int.initSet(testing.allocator, u);
2466 defer a.deinit();
2467 var b = try Int.initSet(testing.allocator, v);
2468 defer b.deinit();
1478 pub fn fitsInTwosComp(self: Managed, is_signed: bool, bit_count: usize) bool {
1479 return self.toConst().fitsInTwosComp(is_signed, bit_count);
1480 }
24691481
2470 var q = try Int.init(testing.allocator);
2471 defer q.deinit();
2472 var r = try Int.init(testing.allocator);
2473 defer r.deinit();
2474 try Int.divFloor(&q, &r, a, b);
1482 /// Returns whether self can fit into an integer of the requested type.
1483 pub fn fits(self: Managed, comptime T: type) bool {
1484 return self.toConst().fits(T);
1485 }
24751486
2476 // n = q * d + r
2477 // 5 = 1 * 3 + 2
2478 const eq = 1;
2479 const er = 2;
1487 /// Returns the approximate size of the integer in the given base. Negative values accommodate for
1488 /// the minus sign. This is used for determining the number of characters needed to print the
1489 /// value. It is inexact and may exceed the given value by ~1-2 bytes.
1490 pub fn sizeInBaseUpperBound(self: Managed, base: usize) usize {
1491 return self.toConst().sizeInBaseUpperBound(base);
1492 }
24801493
2481 testing.expect((try q.to(i32)) == eq);
2482 testing.expect((try r.to(i32)) == er);
2483}
1494 /// Sets an Managed to value. Value must be an primitive integer type.
1495 pub fn set(self: *Managed, value: var) Allocator.Error!void {
1496 try self.ensureCapacity(calcLimbLen(value));
1497 var m = self.toMutable();
1498 m.set(value);
1499 self.setMetadata(m.positive, m.len);
1500 }
24841501
2485test "big.int div floor single-single -/+" {
2486 const u: i32 = -5;
2487 const v: i32 = 3;
1502 pub const ConvertError = Const.ConvertError;
24881503
2489 var a = try Int.initSet(testing.allocator, u);
2490 defer a.deinit();
2491 var b = try Int.initSet(testing.allocator, v);
2492 defer b.deinit();
1504 /// Convert self to type T.
1505 ///
1506 /// Returns an error if self cannot be narrowed into the requested type without truncation.
1507 pub fn to(self: Managed, comptime T: type) ConvertError!T {
1508 return self.toConst().to(T);
1509 }
24931510
2494 var q = try Int.init(testing.allocator);
2495 defer q.deinit();
2496 var r = try Int.init(testing.allocator);
2497 defer r.deinit();
2498 try Int.divFloor(&q, &r, a, b);
1511 /// Set self from the string representation `value`.
1512 ///
1513 /// `value` must contain only digits <= `base` and is case insensitive. Base prefixes are
1514 /// not allowed (e.g. 0x43 should simply be 43). Underscores in the input string are
1515 /// ignored and can be used as digit separators.
1516 ///
1517 /// Returns an error if memory could not be allocated or `value` has invalid digits for the
1518 /// requested base.
1519 ///
1520 /// self's allocator is used for temporary storage to boost multiplication performance.
1521 pub fn setString(self: *Managed, base: u8, value: []const u8) !void {
1522 if (base < 2 or base > 16) return error.InvalidBase;
1523 const den = (@sizeOf(Limb) * 8 / base);
1524 try self.ensureCapacity((value.len + (den - 1)) / den);
1525 const limbs_buffer = try self.allocator.alloc(Limb, calcSetStringLimbsBufferLen(base, value.len));
1526 defer self.allocator.free(limbs_buffer);
1527 var m = self.toMutable();
1528 try m.setString(base, value, limbs_buffer, self.allocator);
1529 self.setMetadata(m.positive, m.len);
1530 }
24991531
2500 // n = q * d + r
2501 // -5 = -2 * 3 + 1
2502 const eq = -2;
2503 const er = 1;
1532 /// Converts self to a string in the requested base. Memory is allocated from the provided
1533 /// allocator and not the one present in self.
1534 pub fn toString(self: Managed, allocator: *Allocator, base: u8, uppercase: bool) ![]u8 {
1535 if (base < 2 or base > 16) return error.InvalidBase;
1536 return self.toConst().toStringAlloc(self.allocator, base, uppercase);
1537 }
25041538
2505 testing.expect((try q.to(i32)) == eq);
2506 testing.expect((try r.to(i32)) == er);
2507}
1539 /// To allow `std.fmt.format` to work with `Managed`.
1540 /// If the integer is larger than `pow(2, 64 * @sizeOf(usize) * 8), this function will fail
1541 /// to print the string, printing "(BigInt)" instead of a number.
1542 /// This is because the rendering algorithm requires reversing a string, which requires O(N) memory.
1543 /// See `toString` and `toStringAlloc` for a way to print big integers without failure.
1544 pub fn format(
1545 self: Managed,
1546 comptime fmt: []const u8,
1547 options: std.fmt.FormatOptions,
1548 out_stream: var,
1549 ) !void {
1550 return self.toConst().format(fmt, options, out_stream);
1551 }
25081552
2509test "big.int div floor single-single +/-" {
2510 const u: i32 = 5;
2511 const v: i32 = -3;
1553 /// Returns math.Order.lt, math.Order.eq, math.Order.gt if |a| < |b|, |a| ==
1554 /// |b| or |a| > |b| respectively.
1555 pub fn orderAbs(a: Managed, b: Managed) math.Order {
1556 return a.toConst().orderAbs(b.toConst());
1557 }
25121558
2513 var a = try Int.initSet(testing.allocator, u);
2514 defer a.deinit();
2515 var b = try Int.initSet(testing.allocator, v);
2516 defer b.deinit();
1559 /// Returns math.Order.lt, math.Order.eq, math.Order.gt if a < b, a == b or a
1560 /// > b respectively.
1561 pub fn order(a: Managed, b: Managed) math.Order {
1562 return a.toConst().order(b.toConst());
1563 }
25171564
2518 var q = try Int.init(testing.allocator);
2519 defer q.deinit();
2520 var r = try Int.init(testing.allocator);
2521 defer r.deinit();
2522 try Int.divFloor(&q, &r, a, b);
1565 /// Returns true if a == 0.
1566 pub fn eqZero(a: Managed) bool {
1567 return a.toConst().eqZero();
1568 }
25231569
2524 // n = q * d + r
2525 // 5 = -2 * -3 - 1
2526 const eq = -2;
2527 const er = -1;
1570 /// Returns true if |a| == |b|.
1571 pub fn eqAbs(a: Managed, b: Managed) bool {
1572 return a.toConst().eqAbs(b.toConst());
1573 }
25281574
2529 testing.expect((try q.to(i32)) == eq);
2530 testing.expect((try r.to(i32)) == er);
2531}
1575 /// Returns true if a == b.
1576 pub fn eq(a: Managed, b: Managed) bool {
1577 return a.toConst().eq(b.toConst());
1578 }
25321579
2533test "big.int div floor single-single -/-" {
2534 const u: i32 = -5;
2535 const v: i32 = -3;
1580 /// Normalize a possible sequence of leading zeros.
1581 ///
1582 /// [1, 2, 3, 4, 0] -> [1, 2, 3, 4]
1583 /// [1, 2, 0, 0, 0] -> [1, 2]
1584 /// [0, 0, 0, 0, 0] -> [0]
1585 pub fn normalize(r: *Managed, length: usize) void {
1586 assert(length > 0);
1587 assert(length <= r.limbs.len);
25361588
2537 var a = try Int.initSet(testing.allocator, u);
2538 defer a.deinit();
2539 var b = try Int.initSet(testing.allocator, v);
2540 defer b.deinit();
1589 var j = length;
1590 while (j > 0) : (j -= 1) {
1591 if (r.limbs[j - 1] != 0) {
1592 break;
1593 }
1594 }
25411595
2542 var q = try Int.init(testing.allocator);
2543 defer q.deinit();
2544 var r = try Int.init(testing.allocator);
2545 defer r.deinit();
2546 try Int.divFloor(&q, &r, a, b);
1596 // Handle zero
1597 r.setLen(if (j != 0) j else 1);
1598 }
25471599
2548 // n = q * d + r
2549 // -5 = 2 * -3 + 1
2550 const eq = 1;
2551 const er = -2;
1600 /// r = a + scalar
1601 ///
1602 /// r and a may be aliases.
1603 /// scalar is a primitive integer type.
1604 ///
1605 /// Returns an error if memory could not be allocated.
1606 pub fn addScalar(r: *Managed, a: Const, scalar: var) Allocator.Error!void {
1607 try r.ensureCapacity(math.max(a.limbs.len, calcLimbLen(scalar)) + 1);
1608 var m = r.toMutable();
1609 m.addScalar(a, scalar);
1610 r.setMetadata(m.positive, m.len);
1611 }
25521612
2553 testing.expect((try q.to(i32)) == eq);
2554 testing.expect((try r.to(i32)) == er);
2555}
1613 /// r = a + b
1614 ///
1615 /// r, a and b may be aliases.
1616 ///
1617 /// Returns an error if memory could not be allocated.
1618 pub fn add(r: *Managed, a: Const, b: Const) Allocator.Error!void {
1619 try r.ensureCapacity(math.max(a.limbs.len, b.limbs.len) + 1);
1620 var m = r.toMutable();
1621 m.add(a, b);
1622 r.setMetadata(m.positive, m.len);
1623 }
25561624
2557test "big.int div multi-multi with rem" {
2558 var a = try Int.initSet(testing.allocator, 0x8888999911110000ffffeeeeddddccccbbbbaaaa9999);
2559 defer a.deinit();
2560 var b = try Int.initSet(testing.allocator, 0x99990000111122223333);
2561 defer b.deinit();
1625 /// r = a - b
1626 ///
1627 /// r, a and b may be aliases.
1628 ///
1629 /// Returns an error if memory could not be allocated.
1630 pub fn sub(r: *Managed, a: Const, b: Const) !void {
1631 try r.ensureCapacity(math.max(a.limbs.len, b.limbs.len) + 1);
1632 var m = r.toMutable();
1633 m.sub(a, b);
1634 r.setMetadata(m.positive, m.len);
1635 }
25621636
2563 var q = try Int.init(testing.allocator);
2564 defer q.deinit();
2565 var r = try Int.init(testing.allocator);
2566 defer r.deinit();
2567 try Int.divTrunc(&q, &r, a, b);
1637 /// rma = a * b
1638 ///
1639 /// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b.
1640 ///
1641 /// Returns an error if memory could not be allocated.
1642 ///
1643 /// rma's allocator is used for temporary storage to speed up the multiplication.
1644 pub fn mul(rma: *Managed, a: Const, b: Const) !void {
1645 try rma.ensureCapacity(a.limbs.len + b.limbs.len + 1);
1646 var alias_count: usize = 0;
1647 if (rma.limbs.ptr == a.limbs.ptr)
1648 alias_count += 1;
1649 if (rma.limbs.ptr == b.limbs.ptr)
1650 alias_count += 1;
1651 var m = rma.toMutable();
1652 if (alias_count == 0) {
1653 m.mulNoAlias(a, b, rma.allocator);
1654 } else {
1655 const limb_count = calcMulLimbsBufferLen(a.limbs.len, b.limbs.len, alias_count);
1656 const limbs_buffer = try rma.allocator.alloc(Limb, limb_count);
1657 defer rma.allocator.free(limbs_buffer);
1658 m.mul(a, b, limbs_buffer, rma.allocator);
1659 }
1660 rma.setMetadata(m.positive, m.len);
1661 }
25681662
2569 testing.expect((try q.to(u128)) == 0xe38f38e39161aaabd03f0f1b);
2570 testing.expect((try r.to(u128)) == 0x28de0acacd806823638);
2571}
1663 /// q = a / b (rem r)
1664 ///
1665 /// a / b are floored (rounded towards 0).
1666 ///
1667 /// Returns an error if memory could not be allocated.
1668 ///
1669 /// q's allocator is used for temporary storage to speed up the multiplication.
1670 pub fn divFloor(q: *Managed, r: *Managed, a: Const, b: Const) !void {
1671 try q.ensureCapacity(a.limbs.len + b.limbs.len + 1);
1672 try r.ensureCapacity(a.limbs.len);
1673 var mq = q.toMutable();
1674 var mr = r.toMutable();
1675 const limbs_buffer = try q.allocator.alloc(Limb, calcDivLimbsBufferLen(a.limbs.len, b.limbs.len));
1676 defer q.allocator.free(limbs_buffer);
1677 mq.divFloor(&mr, a, b, limbs_buffer, q.allocator);
1678 q.setMetadata(mq.positive, mq.len);
1679 r.setMetadata(mr.positive, mr.len);
1680 }
25721681
2573test "big.int div multi-multi no rem" {
2574 var a = try Int.initSet(testing.allocator, 0x8888999911110000ffffeeeedb4fec200ee3a4286361);
2575 defer a.deinit();
2576 var b = try Int.initSet(testing.allocator, 0x99990000111122223333);
2577 defer b.deinit();
1682 /// q = a / b (rem r)
1683 ///
1684 /// a / b are truncated (rounded towards -inf).
1685 ///
1686 /// Returns an error if memory could not be allocated.
1687 ///
1688 /// q's allocator is used for temporary storage to speed up the multiplication.
1689 pub fn divTrunc(q: *Managed, r: *Managed, a: Const, b: Const) !void {
1690 try q.ensureCapacity(a.limbs.len + b.limbs.len + 1);
1691 try r.ensureCapacity(a.limbs.len);
1692 var mq = q.toMutable();
1693 var mr = r.toMutable();
1694 const limbs_buffer = try q.allocator.alloc(Limb, calcDivLimbsBufferLen(a.limbs.len, b.limbs.len));
1695 defer q.allocator.free(limbs_buffer);
1696 mq.divTrunc(&mr, a, b, limbs_buffer, q.allocator);
1697 q.setMetadata(mq.positive, mq.len);
1698 r.setMetadata(mr.positive, mr.len);
1699 }
25781700
2579 var q = try Int.init(testing.allocator);
2580 defer q.deinit();
2581 var r = try Int.init(testing.allocator);
2582 defer r.deinit();
2583 try Int.divTrunc(&q, &r, a, b);
1701 /// r = a << shift, in other words, r = a * 2^shift
1702 pub fn shiftLeft(r: *Managed, a: Managed, shift: usize) !void {
1703 try r.ensureCapacity(a.len() + (shift / Limb.bit_count) + 1);
1704 var m = r.toMutable();
1705 m.shiftLeft(a.toConst(), shift);
1706 r.setMetadata(m.positive, m.len);
1707 }
25841708
2585 testing.expect((try q.to(u128)) == 0xe38f38e39161aaabd03f0f1b);
2586 testing.expect((try r.to(u128)) == 0);
2587}
1709 /// r = a >> shift
1710 pub fn shiftRight(r: *Managed, a: Managed, shift: usize) !void {
1711 if (a.len() <= shift / Limb.bit_count) {
1712 r.metadata = 1;
1713 r.limbs[0] = 0;
1714 return;
1715 }
25881716
2589test "big.int div multi-multi (2 branch)" {
2590 var a = try Int.initSet(testing.allocator, 0x866666665555555588888887777777761111111111111111);
2591 defer a.deinit();
2592 var b = try Int.initSet(testing.allocator, 0x86666666555555554444444433333333);
2593 defer b.deinit();
1717 try r.ensureCapacity(a.len() - (shift / Limb.bit_count));
1718 var m = r.toMutable();
1719 m.shiftRight(a.toConst(), shift);
1720 r.setMetadata(m.positive, m.len);
1721 }
25941722
2595 var q = try Int.init(testing.allocator);
2596 defer q.deinit();
2597 var r = try Int.init(testing.allocator);
2598 defer r.deinit();
2599 try Int.divTrunc(&q, &r, a, b);
1723 /// r = a | b
1724 ///
1725 /// a and b are zero-extended to the longer of a or b.
1726 pub fn bitOr(r: *Managed, a: Managed, b: Managed) !void {
1727 try r.ensureCapacity(math.max(a.len(), b.len()));
1728 var m = r.toMutable();
1729 m.bitOr(a.toConst(), b.toConst());
1730 r.setMetadata(m.positive, m.len);
1731 }
26001732
2601 testing.expect((try q.to(u128)) == 0x10000000000000000);
2602 testing.expect((try r.to(u128)) == 0x44444443444444431111111111111111);
2603}
1733 /// r = a & b
1734 pub fn bitAnd(r: *Managed, a: Managed, b: Managed) !void {
1735 try r.ensureCapacity(math.min(a.len(), b.len()));
1736 var m = r.toMutable();
1737 m.bitAnd(a.toConst(), b.toConst());
1738 r.setMetadata(m.positive, m.len);
1739 }
26041740
2605test "big.int div multi-multi (3.1/3.3 branch)" {
2606 var a = try Int.initSet(testing.allocator, 0x11111111111111111111111111111111111111111111111111111111111111);
2607 defer a.deinit();
2608 var b = try Int.initSet(testing.allocator, 0x1111111111111111111111111111111111111111171);
2609 defer b.deinit();
1741 /// r = a ^ b
1742 pub fn bitXor(r: *Managed, a: Managed, b: Managed) !void {
1743 try r.ensureCapacity(math.max(a.len(), b.len()));
1744 var m = r.toMutable();
1745 m.bitXor(a.toConst(), b.toConst());
1746 r.setMetadata(m.positive, m.len);
1747 }
26101748
2611 var q = try Int.init(testing.allocator);
2612 defer q.deinit();
2613 var r = try Int.init(testing.allocator);
2614 defer r.deinit();
2615 try Int.divTrunc(&q, &r, a, b);
1749 /// rma may alias x or y.
1750 /// x and y may alias each other.
1751 ///
1752 /// rma's allocator is used for temporary storage to boost multiplication performance.
1753 pub fn gcd(rma: *Managed, x: Managed, y: Managed) !void {
1754 try rma.ensureCapacity(math.min(x.len(), y.len()));
1755 var m = rma.toMutable();
1756 var limbs_buffer = std.ArrayList(Limb).init(rma.allocator);
1757 defer limbs_buffer.deinit();
1758 try m.gcd(x.toConst(), y.toConst(), &limbs_buffer);
1759 rma.setMetadata(m.positive, m.len);
1760 }
1761};
26161762
2617 testing.expect((try q.to(u128)) == 0xfffffffffffffffffff);
2618 testing.expect((try r.to(u256)) == 0x1111111111111111111110b12222222222222222282);
2619}
1763/// Knuth 4.3.1, Algorithm M.
1764///
1765/// r MUST NOT alias any of a or b.
1766fn llmulacc(opt_allocator: ?*Allocator, r: []Limb, a: []const Limb, b: []const Limb) void {
1767 @setRuntimeSafety(false);
1768
1769 const a_norm = a[0..llnormalize(a)];
1770 const b_norm = b[0..llnormalize(b)];
1771 var x = a_norm;
1772 var y = b_norm;
1773 if (a_norm.len > b_norm.len) {
1774 x = b_norm;
1775 y = a_norm;
1776 }
1777
1778 assert(r.len >= x.len + y.len + 1);
1779
1780 // 48 is a pretty abitrary size chosen based on performance of a factorial program.
1781 if (x.len > 48) {
1782 if (opt_allocator) |allocator| {
1783 llmulacc_karatsuba(allocator, r, x, y) catch |err| switch (err) {
1784 error.OutOfMemory => {}, // handled below
1785 };
1786 }
1787 }
26201788
2621test "big.int div multi-single zero-limb trailing" {
2622 var a = try Int.initSet(testing.allocator, 0x60000000000000000000000000000000000000000000000000000000000000000);
2623 defer a.deinit();
2624 var b = try Int.initSet(testing.allocator, 0x10000000000000000);
2625 defer b.deinit();
2626
2627 var q = try Int.init(testing.allocator);
2628 defer q.deinit();
2629 var r = try Int.init(testing.allocator);
2630 defer r.deinit();
2631 try Int.divTrunc(&q, &r, a, b);
2632
2633 var expected = try Int.initSet(testing.allocator, 0x6000000000000000000000000000000000000000000000000);
2634 defer expected.deinit();
2635 testing.expect(q.eq(expected));
2636 testing.expect(r.eqZero());
1789 // Basecase multiplication
1790 var i: usize = 0;
1791 while (i < x.len) : (i += 1) {
1792 llmulDigit(r[i..], y, x[i]);
1793 }
26371794}
26381795
2639test "big.int div multi-multi zero-limb trailing (with rem)" {
2640 var a = try Int.initSet(testing.allocator, 0x86666666555555558888888777777776111111111111111100000000000000000000000000000000);
2641 defer a.deinit();
2642 var b = try Int.initSet(testing.allocator, 0x8666666655555555444444443333333300000000000000000000000000000000);
2643 defer b.deinit();
2644
2645 var q = try Int.init(testing.allocator);
2646 defer q.deinit();
2647 var r = try Int.init(testing.allocator);
2648 defer r.deinit();
2649 try Int.divTrunc(&q, &r, a, b);
2650
2651 testing.expect((try q.to(u128)) == 0x10000000000000000);
1796/// Knuth 4.3.1, Algorithm M.
1797///
1798/// r MUST NOT alias any of a or b.
1799fn llmulacc_karatsuba(allocator: *Allocator, r: []Limb, x: []const Limb, y: []const Limb) error{OutOfMemory}!void {
1800 @setRuntimeSafety(false);
26521801
2653 const rs = try r.toString(testing.allocator, 16, false);
2654 defer testing.allocator.free(rs);
2655 testing.expect(std.mem.eql(u8, rs, "4444444344444443111111111111111100000000000000000000000000000000"));
2656}
1802 assert(r.len >= x.len + y.len + 1);
26571803
2658test "big.int div multi-multi zero-limb trailing (with rem) and dividend zero-limb count > divisor zero-limb count" {
2659 var a = try Int.initSet(testing.allocator, 0x8666666655555555888888877777777611111111111111110000000000000000);
2660 defer a.deinit();
2661 var b = try Int.initSet(testing.allocator, 0x8666666655555555444444443333333300000000000000000000000000000000);
2662 defer b.deinit();
1804 const split = @divFloor(x.len, 2);
1805 var x0 = x[0..split];
1806 var x1 = x[split..x.len];
1807 var y0 = y[0..split];
1808 var y1 = y[split..y.len];
26631809
2664 var q = try Int.init(testing.allocator);
2665 defer q.deinit();
2666 var r = try Int.init(testing.allocator);
2667 defer r.deinit();
2668 try Int.divTrunc(&q, &r, a, b);
1810 var tmp = try allocator.alloc(Limb, x1.len + y1.len + 1);
1811 defer allocator.free(tmp);
1812 mem.set(Limb, tmp, 0);
26691813
2670 testing.expect((try q.to(u128)) == 0x1);
1814 llmulacc(allocator, tmp, x1, y1);
26711815
2672 const rs = try r.toString(testing.allocator, 16, false);
2673 defer testing.allocator.free(rs);
2674 testing.expect(std.mem.eql(u8, rs, "444444434444444311111111111111110000000000000000"));
2675}
1816 var length = llnormalize(tmp);
1817 _ = llaccum(r[split..], tmp[0..length]);
1818 _ = llaccum(r[split * 2 ..], tmp[0..length]);
26761819
2677test "big.int div multi-multi zero-limb trailing (with rem) and dividend zero-limb count < divisor zero-limb count" {
2678 var a = try Int.initSet(testing.allocator, 0x86666666555555558888888777777776111111111111111100000000000000000000000000000000);
2679 defer a.deinit();
2680 var b = try Int.initSet(testing.allocator, 0x866666665555555544444444333333330000000000000000);
2681 defer b.deinit();
2682
2683 var q = try Int.init(testing.allocator);
2684 defer q.deinit();
2685 var r = try Int.init(testing.allocator);
2686 defer r.deinit();
2687 try Int.divTrunc(&q, &r, a, b);
2688
2689 const qs = try q.toString(testing.allocator, 16, false);
2690 defer testing.allocator.free(qs);
2691 testing.expect(std.mem.eql(u8, qs, "10000000000000000820820803105186f"));
2692
2693 const rs = try r.toString(testing.allocator, 16, false);
2694 defer testing.allocator.free(rs);
2695 testing.expect(std.mem.eql(u8, rs, "4e11f2baa5896a321d463b543d0104e30000000000000000"));
2696}
1820 mem.set(Limb, tmp[0..length], 0);
26971821
2698test "big.int div multi-multi fuzz case #1" {
2699 var a = try Int.init(testing.allocator);
2700 defer a.deinit();
2701 var b = try Int.init(testing.allocator);
2702 defer b.deinit();
1822 llmulacc(allocator, tmp, x0, y0);
27031823
2704 try a.setString(16, "ffffffffffffffffffffffffffffc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
2705 try b.setString(16, "3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0000000000000000000000000000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffc000000000000000000000000000000007fffffffffff");
1824 length = llnormalize(tmp);
1825 _ = llaccum(r[0..], tmp[0..length]);
1826 _ = llaccum(r[split..], tmp[0..length]);
27061827
2707 var q = try Int.init(testing.allocator);
2708 defer q.deinit();
2709 var r = try Int.init(testing.allocator);
2710 defer r.deinit();
2711 try Int.divTrunc(&q, &r, a, b);
1828 const x_cmp = llcmp(x1, x0);
1829 const y_cmp = llcmp(y1, y0);
1830 if (x_cmp * y_cmp == 0) {
1831 return;
1832 }
1833 const x0_len = llnormalize(x0);
1834 const x1_len = llnormalize(x1);
1835 var j0 = try allocator.alloc(Limb, math.max(x0_len, x1_len));
1836 defer allocator.free(j0);
1837 if (x_cmp == 1) {
1838 llsub(j0, x1[0..x1_len], x0[0..x0_len]);
1839 } else {
1840 llsub(j0, x0[0..x0_len], x1[0..x1_len]);
1841 }
27121842
2713 const qs = try q.toString(testing.allocator, 16, false);
2714 defer testing.allocator.free(qs);
2715 testing.expect(std.mem.eql(u8, qs, "3ffffffffffffffffffffffffffff0000000000000000000000000000000000001ffffffffffffffffffffffffffff7fffffffe000000000000000000000000000180000000000000000000003fffffbfffffffdfffffffffffffeffff800000100101000000100000000020003fffffdfbfffffe3ffffffffffffeffff7fffc00800a100000017ffe000002000400007efbfff7fe9f00000037ffff3fff7fffa004006100000009ffe00000190038200bf7d2ff7fefe80400060000f7d7f8fbf9401fe38e0403ffc0bdffffa51102c300d7be5ef9df4e5060007b0127ad3fa69f97d0f820b6605ff617ddf7f32ad7a05c0d03f2e7bc78a6000e087a8bbcdc59e07a5a079128a7861f553ddebed7e8e56701756f9ead39b48cd1b0831889ea6ec1fddf643d0565b075ff07e6caea4e2854ec9227fd635ed60a2f5eef2893052ffd54718fa08604acbf6a15e78a467c4a3c53c0278af06c4416573f925491b195e8fd79302cb1aaf7caf4ecfc9aec1254cc969786363ac729f914c6ddcc26738d6b0facd54eba026580aba2eb6482a088b0d224a8852420b91ec1"));
1843 const y0_len = llnormalize(y0);
1844 const y1_len = llnormalize(y1);
1845 var j1 = try allocator.alloc(Limb, math.max(y0_len, y1_len));
1846 defer allocator.free(j1);
1847 if (y_cmp == 1) {
1848 llsub(j1, y1[0..y1_len], y0[0..y0_len]);
1849 } else {
1850 llsub(j1, y0[0..y0_len], y1[0..y1_len]);
1851 }
1852 const j0_len = llnormalize(j0);
1853 const j1_len = llnormalize(j1);
1854 if (x_cmp == y_cmp) {
1855 mem.set(Limb, tmp[0..length], 0);
1856 llmulacc(allocator, tmp, j0, j1);
27161857
2717 const rs = try r.toString(testing.allocator, 16, false);
2718 defer testing.allocator.free(rs);
2719 testing.expect(std.mem.eql(u8, rs, "310d1d4c414426b4836c2635bad1df3a424e50cbdd167ffccb4dfff57d36b4aae0d6ca0910698220171a0f3373c1060a046c2812f0027e321f72979daa5e7973214170d49e885de0c0ecc167837d44502430674a82522e5df6a0759548052420b91ec1"));
1858 length = llnormalize(tmp);
1859 llsub(r[split..], r[split..], tmp[0..length]);
1860 } else {
1861 llmulacc(allocator, r[split..], j0, j1);
1862 }
27201863}
27211864
2722test "big.int div multi-multi fuzz case #2" {
2723 var a = try Int.init(testing.allocator);
2724 defer a.deinit();
2725 var b = try Int.init(testing.allocator);
2726 defer b.deinit();
1865// r = r + a
1866fn llaccum(r: []Limb, a: []const Limb) Limb {
1867 @setRuntimeSafety(false);
1868 assert(r.len != 0 and a.len != 0);
1869 assert(r.len >= a.len);
27271870
2728 try a.setString(16, "3ffffffffe00000000000000000000000000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffe000000000000000000000000000000000000000000000000000000000000001fffffffffffffffff800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffc000000000000000000000000000000000000000000000000000000000000000");
2729 try b.setString(16, "ffc0000000000000000000000000000000000000000000000000");
1871 var i: usize = 0;
1872 var carry: Limb = 0;
27301873
2731 var q = try Int.init(testing.allocator);
2732 defer q.deinit();
2733 var r = try Int.init(testing.allocator);
2734 defer r.deinit();
2735 try Int.divTrunc(&q, &r, a, b);
1874 while (i < a.len) : (i += 1) {
1875 var c: Limb = 0;
1876 c += @boolToInt(@addWithOverflow(Limb, r[i], a[i], &r[i]));
1877 c += @boolToInt(@addWithOverflow(Limb, r[i], carry, &r[i]));
1878 carry = c;
1879 }
27361880
2737 const qs = try q.toString(testing.allocator, 16, false);
2738 defer testing.allocator.free(qs);
2739 testing.expect(std.mem.eql(u8, qs, "40100400fe3f8fe3f8fe3f8fe3f8fe3f8fe4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f91e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4992649926499264991e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4792e4b92e4b92e4b92e4b92a4a92a4a92a4"));
1881 while ((carry != 0) and i < r.len) : (i += 1) {
1882 carry = @boolToInt(@addWithOverflow(Limb, r[i], carry, &r[i]));
1883 }
27401884
2741 const rs = try r.toString(testing.allocator, 16, false);
2742 defer testing.allocator.free(rs);
2743 testing.expect(std.mem.eql(u8, rs, "a900000000000000000000000000000000000000000000000000"));
1885 return carry;
27441886}
27451887
2746test "big.int shift-right single" {
2747 var a = try Int.initSet(testing.allocator, 0xffff0000);
2748 defer a.deinit();
2749 try a.shiftRight(a, 16);
2750
2751 testing.expect((try a.to(u32)) == 0xffff);
2752}
1888/// Returns -1, 0, 1 if |a| < |b|, |a| == |b| or |a| > |b| respectively for limbs.
1889pub fn llcmp(a: []const Limb, b: []const Limb) i8 {
1890 @setRuntimeSafety(false);
1891 const a_len = llnormalize(a);
1892 const b_len = llnormalize(b);
1893 if (a_len < b_len) {
1894 return -1;
1895 }
1896 if (a_len > b_len) {
1897 return 1;
1898 }
27531899
2754test "big.int shift-right multi" {
2755 var a = try Int.initSet(testing.allocator, 0xffff0000eeee1111dddd2222cccc3333);
2756 defer a.deinit();
2757 try a.shiftRight(a, 67);
1900 var i: usize = a_len - 1;
1901 while (i != 0) : (i -= 1) {
1902 if (a[i] != b[i]) {
1903 break;
1904 }
1905 }
27581906
2759 testing.expect((try a.to(u64)) == 0x1fffe0001dddc222);
1907 if (a[i] < b[i]) {
1908 return -1;
1909 } else if (a[i] > b[i]) {
1910 return 1;
1911 } else {
1912 return 0;
1913 }
27601914}
27611915
2762test "big.int shift-left single" {
2763 var a = try Int.initSet(testing.allocator, 0xffff);
2764 defer a.deinit();
2765 try a.shiftLeft(a, 16);
1916fn llmulDigit(acc: []Limb, y: []const Limb, xi: Limb) void {
1917 @setRuntimeSafety(false);
1918 if (xi == 0) {
1919 return;
1920 }
27661921
2767 testing.expect((try a.to(u64)) == 0xffff0000);
2768}
1922 var carry: usize = 0;
1923 var a_lo = acc[0..y.len];
1924 var a_hi = acc[y.len..];
27691925
2770test "big.int shift-left multi" {
2771 var a = try Int.initSet(testing.allocator, 0x1fffe0001dddc222);
2772 defer a.deinit();
2773 try a.shiftLeft(a, 67);
1926 var j: usize = 0;
1927 while (j < a_lo.len) : (j += 1) {
1928 a_lo[j] = @call(.{ .modifier = .always_inline }, addMulLimbWithCarry, .{ a_lo[j], y[j], xi, &carry });
1929 }
27741930
2775 testing.expect((try a.to(u128)) == 0xffff0000eeee11100000000000000000);
1931 j = 0;
1932 while ((carry != 0) and (j < a_hi.len)) : (j += 1) {
1933 carry = @boolToInt(@addWithOverflow(Limb, a_hi[j], carry, &a_hi[j]));
1934 }
27761935}
27771936
2778test "big.int shift-right negative" {
2779 var a = try Int.init(testing.allocator);
2780 defer a.deinit();
2781
2782 try a.shiftRight(try Int.initSet(testing.allocator, -20), 2);
2783 defer a.deinit();
2784 testing.expect((try a.to(i32)) == -20 >> 2);
1937/// returns the min length the limb could be.
1938fn llnormalize(a: []const Limb) usize {
1939 @setRuntimeSafety(false);
1940 var j = a.len;
1941 while (j > 0) : (j -= 1) {
1942 if (a[j - 1] != 0) {
1943 break;
1944 }
1945 }
27851946
2786 try a.shiftRight(try Int.initSet(testing.allocator, -5), 10);
2787 defer a.deinit();
2788 testing.expect((try a.to(i32)) == -5 >> 10);
1947 // Handle zero
1948 return if (j != 0) j else 1;
27891949}
27901950
2791test "big.int shift-left negative" {
2792 var a = try Int.init(testing.allocator);
2793 defer a.deinit();
1951/// Knuth 4.3.1, Algorithm S.
1952fn llsub(r: []Limb, a: []const Limb, b: []const Limb) void {
1953 @setRuntimeSafety(false);
1954 assert(a.len != 0 and b.len != 0);
1955 assert(a.len > b.len or (a.len == b.len and a[a.len - 1] >= b[b.len - 1]));
1956 assert(r.len >= a.len);
27941957
2795 try a.shiftRight(try Int.initSet(testing.allocator, -10), 1232);
2796 defer a.deinit();
2797 testing.expect((try a.to(i32)) == -10 >> 1232);
2798}
1958 var i: usize = 0;
1959 var borrow: Limb = 0;
27991960
2800test "big.int bitwise and simple" {
2801 var a = try Int.initSet(testing.allocator, 0xffffffff11111111);
2802 defer a.deinit();
2803 var b = try Int.initSet(testing.allocator, 0xeeeeeeee22222222);
2804 defer b.deinit();
1961 while (i < b.len) : (i += 1) {
1962 var c: Limb = 0;
1963 c += @boolToInt(@subWithOverflow(Limb, a[i], b[i], &r[i]));
1964 c += @boolToInt(@subWithOverflow(Limb, r[i], borrow, &r[i]));
1965 borrow = c;
1966 }
28051967
2806 try a.bitAnd(a, b);
1968 while (i < a.len) : (i += 1) {
1969 borrow = @boolToInt(@subWithOverflow(Limb, a[i], borrow, &r[i]));
1970 }
28071971
2808 testing.expect((try a.to(u64)) == 0xeeeeeeee00000000);
1972 assert(borrow == 0);
28091973}
28101974
2811test "big.int bitwise and multi-limb" {
2812 var a = try Int.initSet(testing.allocator, maxInt(Limb) + 1);
2813 defer a.deinit();
2814 var b = try Int.initSet(testing.allocator, maxInt(Limb));
2815 defer b.deinit();
2816
2817 try a.bitAnd(a, b);
1975/// Knuth 4.3.1, Algorithm A.
1976fn lladd(r: []Limb, a: []const Limb, b: []const Limb) void {
1977 @setRuntimeSafety(false);
1978 assert(a.len != 0 and b.len != 0);
1979 assert(a.len >= b.len);
1980 assert(r.len >= a.len + 1);
28181981
2819 testing.expect((try a.to(u128)) == 0);
2820}
1982 var i: usize = 0;
1983 var carry: Limb = 0;
28211984
2822test "big.int bitwise xor simple" {
2823 var a = try Int.initSet(testing.allocator, 0xffffffff11111111);
2824 defer a.deinit();
2825 var b = try Int.initSet(testing.allocator, 0xeeeeeeee22222222);
2826 defer b.deinit();
1985 while (i < b.len) : (i += 1) {
1986 var c: Limb = 0;
1987 c += @boolToInt(@addWithOverflow(Limb, a[i], b[i], &r[i]));
1988 c += @boolToInt(@addWithOverflow(Limb, r[i], carry, &r[i]));
1989 carry = c;
1990 }
28271991
2828 try a.bitXor(a, b);
1992 while (i < a.len) : (i += 1) {
1993 carry = @boolToInt(@addWithOverflow(Limb, a[i], carry, &r[i]));
1994 }
28291995
2830 testing.expect((try a.to(u64)) == 0x1111111133333333);
1996 r[i] = carry;
28311997}
28321998
2833test "big.int bitwise xor multi-limb" {
2834 var a = try Int.initSet(testing.allocator, maxInt(Limb) + 1);
2835 defer a.deinit();
2836 var b = try Int.initSet(testing.allocator, maxInt(Limb));
2837 defer b.deinit();
1999/// Knuth 4.3.1, Exercise 16.
2000fn lldiv1(quo: []Limb, rem: *Limb, a: []const Limb, b: Limb) void {
2001 @setRuntimeSafety(false);
2002 assert(a.len > 1 or a[0] >= b);
2003 assert(quo.len >= a.len);
28382004
2839 try a.bitXor(a, b);
2005 rem.* = 0;
2006 for (a) |_, ri| {
2007 const i = a.len - ri - 1;
2008 const pdiv = ((@as(DoubleLimb, rem.*) << Limb.bit_count) | a[i]);
28402009
2841 testing.expect((try a.to(DoubleLimb)) == (maxInt(Limb) + 1) ^ maxInt(Limb));
2010 if (pdiv == 0) {
2011 quo[i] = 0;
2012 rem.* = 0;
2013 } else if (pdiv < b) {
2014 quo[i] = 0;
2015 rem.* = @truncate(Limb, pdiv);
2016 } else if (pdiv == b) {
2017 quo[i] = 1;
2018 rem.* = 0;
2019 } else {
2020 quo[i] = @truncate(Limb, @divTrunc(pdiv, b));
2021 rem.* = @truncate(Limb, pdiv - (quo[i] *% b));
2022 }
2023 }
28422024}
28432025
2844test "big.int bitwise or simple" {
2845 var a = try Int.initSet(testing.allocator, 0xffffffff11111111);
2846 defer a.deinit();
2847 var b = try Int.initSet(testing.allocator, 0xeeeeeeee22222222);
2848 defer b.deinit();
2026fn llshl(r: []Limb, a: []const Limb, shift: usize) void {
2027 @setRuntimeSafety(false);
2028 assert(a.len >= 1);
2029 assert(r.len >= a.len + (shift / Limb.bit_count) + 1);
28492030
2850 try a.bitOr(a, b);
2031 const limb_shift = shift / Limb.bit_count + 1;
2032 const interior_limb_shift = @intCast(Log2Limb, shift % Limb.bit_count);
28512033
2852 testing.expect((try a.to(u64)) == 0xffffffff33333333);
2853}
2854
2855test "big.int bitwise or multi-limb" {
2856 var a = try Int.initSet(testing.allocator, maxInt(Limb) + 1);
2857 defer a.deinit();
2858 var b = try Int.initSet(testing.allocator, maxInt(Limb));
2859 defer b.deinit();
2034 var carry: Limb = 0;
2035 var i: usize = 0;
2036 while (i < a.len) : (i += 1) {
2037 const src_i = a.len - i - 1;
2038 const dst_i = src_i + limb_shift;
28602039
2861 try a.bitOr(a, b);
2040 const src_digit = a[src_i];
2041 r[dst_i] = carry | @call(.{ .modifier = .always_inline }, math.shr, .{
2042 Limb,
2043 src_digit,
2044 Limb.bit_count - @intCast(Limb, interior_limb_shift),
2045 });
2046 carry = (src_digit << interior_limb_shift);
2047 }
28622048
2863 // TODO: big.int.cpp or is wrong on multi-limb.
2864 testing.expect((try a.to(DoubleLimb)) == (maxInt(Limb) + 1) + maxInt(Limb));
2049 r[limb_shift - 1] = carry;
2050 mem.set(Limb, r[0 .. limb_shift - 1], 0);
28652051}
28662052
2867test "big.int var args" {
2868 var a = try Int.initSet(testing.allocator, 5);
2869 defer a.deinit();
2053fn llshr(r: []Limb, a: []const Limb, shift: usize) void {
2054 @setRuntimeSafety(false);
2055 assert(a.len >= 1);
2056 assert(r.len >= a.len - (shift / Limb.bit_count));
28702057
2871 const b = try Int.initSet(testing.allocator, 6);
2872 defer b.deinit();
2873 try a.add(a, b);
2874 testing.expect((try a.to(u64)) == 11);
2058 const limb_shift = shift / Limb.bit_count;
2059 const interior_limb_shift = @intCast(Log2Limb, shift % Limb.bit_count);
28752060
2876 const c = try Int.initSet(testing.allocator, 11);
2877 defer c.deinit();
2878 testing.expect(a.cmp(c) == .eq);
2061 var carry: Limb = 0;
2062 var i: usize = 0;
2063 while (i < a.len - limb_shift) : (i += 1) {
2064 const src_i = a.len - i - 1;
2065 const dst_i = src_i - limb_shift;
28792066
2880 const d = try Int.initSet(testing.allocator, 14);
2881 defer d.deinit();
2882 testing.expect(a.cmp(d) != .gt);
2067 const src_digit = a[src_i];
2068 r[dst_i] = carry | (src_digit >> interior_limb_shift);
2069 carry = @call(.{ .modifier = .always_inline }, math.shl, .{
2070 Limb,
2071 src_digit,
2072 Limb.bit_count - @intCast(Limb, interior_limb_shift),
2073 });
2074 }
28832075}
28842076
2885test "big.int gcd non-one small" {
2886 var a = try Int.initSet(testing.allocator, 17);
2887 defer a.deinit();
2888 var b = try Int.initSet(testing.allocator, 97);
2889 defer b.deinit();
2890 var r = try Int.init(testing.allocator);
2891 defer r.deinit();
2077fn llor(r: []Limb, a: []const Limb, b: []const Limb) void {
2078 @setRuntimeSafety(false);
2079 assert(r.len >= a.len);
2080 assert(a.len >= b.len);
28922081
2893 try r.gcd(a, b);
2894
2895 testing.expect((try r.to(u32)) == 1);
2082 var i: usize = 0;
2083 while (i < b.len) : (i += 1) {
2084 r[i] = a[i] | b[i];
2085 }
2086 while (i < a.len) : (i += 1) {
2087 r[i] = a[i];
2088 }
28962089}
28972090
2898test "big.int gcd non-one small" {
2899 var a = try Int.initSet(testing.allocator, 4864);
2900 defer a.deinit();
2901 var b = try Int.initSet(testing.allocator, 3458);
2902 defer b.deinit();
2903 var r = try Int.init(testing.allocator);
2904 defer r.deinit();
2905
2906 try r.gcd(a, b);
2091fn lland(r: []Limb, a: []const Limb, b: []const Limb) void {
2092 @setRuntimeSafety(false);
2093 assert(r.len >= b.len);
2094 assert(a.len >= b.len);
29072095
2908 testing.expect((try r.to(u32)) == 38);
2096 var i: usize = 0;
2097 while (i < b.len) : (i += 1) {
2098 r[i] = a[i] & b[i];
2099 }
29092100}
29102101
2911test "big.int gcd non-one large" {
2912 var a = try Int.initSet(testing.allocator, 0xffffffffffffffff);
2913 defer a.deinit();
2914 var b = try Int.initSet(testing.allocator, 0xffffffffffffffff7777);
2915 defer b.deinit();
2916 var r = try Int.init(testing.allocator);
2917 defer r.deinit();
2918
2919 try r.gcd(a, b);
2102fn llxor(r: []Limb, a: []const Limb, b: []const Limb) void {
2103 assert(r.len >= a.len);
2104 assert(a.len >= b.len);
29202105
2921 testing.expect((try r.to(u32)) == 4369);
2106 var i: usize = 0;
2107 while (i < b.len) : (i += 1) {
2108 r[i] = a[i] ^ b[i];
2109 }
2110 while (i < a.len) : (i += 1) {
2111 r[i] = a[i];
2112 }
29222113}
29232114
2924test "big.int gcd large multi-limb result" {
2925 var a = try Int.initSet(testing.allocator, 0x12345678123456781234567812345678123456781234567812345678);
2926 defer a.deinit();
2927 var b = try Int.initSet(testing.allocator, 0x12345671234567123456712345671234567123456712345671234567);
2928 defer b.deinit();
2929 var r = try Int.init(testing.allocator);
2930 defer r.deinit();
2931
2932 try r.gcd(a, b);
2115// Storage must live for the lifetime of the returned value
2116fn fixedIntFromSignedDoubleLimb(A: SignedDoubleLimb, storage: []Limb) Mutable {
2117 assert(storage.len >= 2);
29332118
2934 testing.expect((try r.to(u256)) == 0xf000000ff00000fff0000ffff000fffff00ffffff1);
2119 const A_is_positive = A >= 0;
2120 const Au = @intCast(DoubleLimb, if (A < 0) -A else A);
2121 storage[0] = @truncate(Limb, Au);
2122 storage[1] = @truncate(Limb, Au >> Limb.bit_count);
2123 return .{
2124 .limbs = storage[0..2],
2125 .positive = A_is_positive,
2126 .len = 2,
2127 };
29352128}
29362129
2937test "big.int gcd one large" {
2938 var a = try Int.initSet(testing.allocator, 1897056385327307);
2939 defer a.deinit();
2940 var b = try Int.initSet(testing.allocator, 2251799813685248);
2941 defer b.deinit();
2942 var r = try Int.init(testing.allocator);
2943 defer r.deinit();
2944
2945 try r.gcd(a, b);
2946
2947 testing.expect((try r.to(u64)) == 1);
2130test "" {
2131 _ = @import("int_test.zig");
29482132}
lib/std/math/big/int_test.zig created+1455
......@@ -0,0 +1,1455 @@
1const std = @import("../../std.zig");
2const mem = std.mem;
3const testing = std.testing;
4const Managed = std.math.big.int.Managed;
5const Limb = std.math.big.Limb;
6const DoubleLimb = std.math.big.DoubleLimb;
7const maxInt = std.math.maxInt;
8const minInt = std.math.minInt;
9
10// NOTE: All the following tests assume the max machine-word will be 64-bit.
11//
12// They will still run on larger than this and should pass, but the multi-limb code-paths
13// may be untested in some cases.
14
15test "big.int comptime_int set" {
16 comptime var s = 0xefffffff00000001eeeeeeefaaaaaaab;
17 var a = try Managed.initSet(testing.allocator, s);
18 defer a.deinit();
19
20 const s_limb_count = 128 / Limb.bit_count;
21
22 comptime var i: usize = 0;
23 inline while (i < s_limb_count) : (i += 1) {
24 const result = @as(Limb, s & maxInt(Limb));
25 s >>= Limb.bit_count / 2;
26 s >>= Limb.bit_count / 2;
27 testing.expect(a.limbs[i] == result);
28 }
29}
30
31test "big.int comptime_int set negative" {
32 var a = try Managed.initSet(testing.allocator, -10);
33 defer a.deinit();
34
35 testing.expect(a.limbs[0] == 10);
36 testing.expect(a.isPositive() == false);
37}
38
39test "big.int int set unaligned small" {
40 var a = try Managed.initSet(testing.allocator, @as(u7, 45));
41 defer a.deinit();
42
43 testing.expect(a.limbs[0] == 45);
44 testing.expect(a.isPositive() == true);
45}
46
47test "big.int comptime_int to" {
48 var a = try Managed.initSet(testing.allocator, 0xefffffff00000001eeeeeeefaaaaaaab);
49 defer a.deinit();
50
51 testing.expect((try a.to(u128)) == 0xefffffff00000001eeeeeeefaaaaaaab);
52}
53
54test "big.int sub-limb to" {
55 var a = try Managed.initSet(testing.allocator, 10);
56 defer a.deinit();
57
58 testing.expect((try a.to(u8)) == 10);
59}
60
61test "big.int to target too small error" {
62 var a = try Managed.initSet(testing.allocator, 0xffffffff);
63 defer a.deinit();
64
65 testing.expectError(error.TargetTooSmall, a.to(u8));
66}
67
68test "big.int normalize" {
69 var a = try Managed.init(testing.allocator);
70 defer a.deinit();
71 try a.ensureCapacity(8);
72
73 a.limbs[0] = 1;
74 a.limbs[1] = 2;
75 a.limbs[2] = 3;
76 a.limbs[3] = 0;
77 a.normalize(4);
78 testing.expect(a.len() == 3);
79
80 a.limbs[0] = 1;
81 a.limbs[1] = 2;
82 a.limbs[2] = 3;
83 a.normalize(3);
84 testing.expect(a.len() == 3);
85
86 a.limbs[0] = 0;
87 a.limbs[1] = 0;
88 a.normalize(2);
89 testing.expect(a.len() == 1);
90
91 a.limbs[0] = 0;
92 a.normalize(1);
93 testing.expect(a.len() == 1);
94}
95
96test "big.int normalize multi" {
97 var a = try Managed.init(testing.allocator);
98 defer a.deinit();
99 try a.ensureCapacity(8);
100
101 a.limbs[0] = 1;
102 a.limbs[1] = 2;
103 a.limbs[2] = 0;
104 a.limbs[3] = 0;
105 a.normalize(4);
106 testing.expect(a.len() == 2);
107
108 a.limbs[0] = 1;
109 a.limbs[1] = 2;
110 a.limbs[2] = 3;
111 a.normalize(3);
112 testing.expect(a.len() == 3);
113
114 a.limbs[0] = 0;
115 a.limbs[1] = 0;
116 a.limbs[2] = 0;
117 a.limbs[3] = 0;
118 a.normalize(4);
119 testing.expect(a.len() == 1);
120
121 a.limbs[0] = 0;
122 a.normalize(1);
123 testing.expect(a.len() == 1);
124}
125
126test "big.int parity" {
127 var a = try Managed.init(testing.allocator);
128 defer a.deinit();
129
130 try a.set(0);
131 testing.expect(a.isEven());
132 testing.expect(!a.isOdd());
133
134 try a.set(7);
135 testing.expect(!a.isEven());
136 testing.expect(a.isOdd());
137}
138
139test "big.int bitcount + sizeInBaseUpperBound" {
140 var a = try Managed.init(testing.allocator);
141 defer a.deinit();
142
143 try a.set(0b100);
144 testing.expect(a.bitCountAbs() == 3);
145 testing.expect(a.sizeInBaseUpperBound(2) >= 3);
146 testing.expect(a.sizeInBaseUpperBound(10) >= 1);
147
148 a.negate();
149 testing.expect(a.bitCountAbs() == 3);
150 testing.expect(a.sizeInBaseUpperBound(2) >= 4);
151 testing.expect(a.sizeInBaseUpperBound(10) >= 2);
152
153 try a.set(0xffffffff);
154 testing.expect(a.bitCountAbs() == 32);
155 testing.expect(a.sizeInBaseUpperBound(2) >= 32);
156 testing.expect(a.sizeInBaseUpperBound(10) >= 10);
157
158 try a.shiftLeft(a, 5000);
159 testing.expect(a.bitCountAbs() == 5032);
160 testing.expect(a.sizeInBaseUpperBound(2) >= 5032);
161 a.setSign(false);
162
163 testing.expect(a.bitCountAbs() == 5032);
164 testing.expect(a.sizeInBaseUpperBound(2) >= 5033);
165}
166
167test "big.int bitcount/to" {
168 var a = try Managed.init(testing.allocator);
169 defer a.deinit();
170
171 try a.set(0);
172 testing.expect(a.bitCountTwosComp() == 0);
173
174 testing.expect((try a.to(u0)) == 0);
175 testing.expect((try a.to(i0)) == 0);
176
177 try a.set(-1);
178 testing.expect(a.bitCountTwosComp() == 1);
179 testing.expect((try a.to(i1)) == -1);
180
181 try a.set(-8);
182 testing.expect(a.bitCountTwosComp() == 4);
183 testing.expect((try a.to(i4)) == -8);
184
185 try a.set(127);
186 testing.expect(a.bitCountTwosComp() == 7);
187 testing.expect((try a.to(u7)) == 127);
188
189 try a.set(-128);
190 testing.expect(a.bitCountTwosComp() == 8);
191 testing.expect((try a.to(i8)) == -128);
192
193 try a.set(-129);
194 testing.expect(a.bitCountTwosComp() == 9);
195 testing.expect((try a.to(i9)) == -129);
196}
197
198test "big.int fits" {
199 var a = try Managed.init(testing.allocator);
200 defer a.deinit();
201
202 try a.set(0);
203 testing.expect(a.fits(u0));
204 testing.expect(a.fits(i0));
205
206 try a.set(255);
207 testing.expect(!a.fits(u0));
208 testing.expect(!a.fits(u1));
209 testing.expect(!a.fits(i8));
210 testing.expect(a.fits(u8));
211 testing.expect(a.fits(u9));
212 testing.expect(a.fits(i9));
213
214 try a.set(-128);
215 testing.expect(!a.fits(i7));
216 testing.expect(a.fits(i8));
217 testing.expect(a.fits(i9));
218 testing.expect(!a.fits(u9));
219
220 try a.set(0x1ffffffffeeeeeeee);
221 testing.expect(!a.fits(u32));
222 testing.expect(!a.fits(u64));
223 testing.expect(a.fits(u65));
224}
225
226test "big.int string set" {
227 var a = try Managed.init(testing.allocator);
228 defer a.deinit();
229
230 try a.setString(10, "120317241209124781241290847124");
231 testing.expect((try a.to(u128)) == 120317241209124781241290847124);
232}
233
234test "big.int string negative" {
235 var a = try Managed.init(testing.allocator);
236 defer a.deinit();
237
238 try a.setString(10, "-1023");
239 testing.expect((try a.to(i32)) == -1023);
240}
241
242test "big.int string set number with underscores" {
243 var a = try Managed.init(testing.allocator);
244 defer a.deinit();
245
246 try a.setString(10, "__1_2_0_3_1_7_2_4_1_2_0_____9_1__2__4_7_8_1_2_4_1_2_9_0_8_4_7_1_2_4___");
247 testing.expect((try a.to(u128)) == 120317241209124781241290847124);
248}
249
250test "big.int string set case insensitive number" {
251 var a = try Managed.init(testing.allocator);
252 defer a.deinit();
253
254 try a.setString(16, "aB_cD_eF");
255 testing.expect((try a.to(u32)) == 0xabcdef);
256}
257
258test "big.int string set bad char error" {
259 var a = try Managed.init(testing.allocator);
260 defer a.deinit();
261 testing.expectError(error.InvalidCharacter, a.setString(10, "x"));
262}
263
264test "big.int string set bad base error" {
265 var a = try Managed.init(testing.allocator);
266 defer a.deinit();
267 testing.expectError(error.InvalidBase, a.setString(45, "10"));
268}
269
270test "big.int string to" {
271 var a = try Managed.initSet(testing.allocator, 120317241209124781241290847124);
272 defer a.deinit();
273
274 const as = try a.toString(testing.allocator, 10, false);
275 defer testing.allocator.free(as);
276 const es = "120317241209124781241290847124";
277
278 testing.expect(mem.eql(u8, as, es));
279}
280
281test "big.int string to base base error" {
282 var a = try Managed.initSet(testing.allocator, 0xffffffff);
283 defer a.deinit();
284
285 testing.expectError(error.InvalidBase, a.toString(testing.allocator, 45, false));
286}
287
288test "big.int string to base 2" {
289 var a = try Managed.initSet(testing.allocator, -0b1011);
290 defer a.deinit();
291
292 const as = try a.toString(testing.allocator, 2, false);
293 defer testing.allocator.free(as);
294 const es = "-1011";
295
296 testing.expect(mem.eql(u8, as, es));
297}
298
299test "big.int string to base 16" {
300 var a = try Managed.initSet(testing.allocator, 0xefffffff00000001eeeeeeefaaaaaaab);
301 defer a.deinit();
302
303 const as = try a.toString(testing.allocator, 16, false);
304 defer testing.allocator.free(as);
305 const es = "efffffff00000001eeeeeeefaaaaaaab";
306
307 testing.expect(mem.eql(u8, as, es));
308}
309
310test "big.int neg string to" {
311 var a = try Managed.initSet(testing.allocator, -123907434);
312 defer a.deinit();
313
314 const as = try a.toString(testing.allocator, 10, false);
315 defer testing.allocator.free(as);
316 const es = "-123907434";
317
318 testing.expect(mem.eql(u8, as, es));
319}
320
321test "big.int zero string to" {
322 var a = try Managed.initSet(testing.allocator, 0);
323 defer a.deinit();
324
325 const as = try a.toString(testing.allocator, 10, false);
326 defer testing.allocator.free(as);
327 const es = "0";
328
329 testing.expect(mem.eql(u8, as, es));
330}
331
332test "big.int clone" {
333 var a = try Managed.initSet(testing.allocator, 1234);
334 defer a.deinit();
335 var b = try a.clone();
336 defer b.deinit();
337
338 testing.expect((try a.to(u32)) == 1234);
339 testing.expect((try b.to(u32)) == 1234);
340
341 try a.set(77);
342 testing.expect((try a.to(u32)) == 77);
343 testing.expect((try b.to(u32)) == 1234);
344}
345
346test "big.int swap" {
347 var a = try Managed.initSet(testing.allocator, 1234);
348 defer a.deinit();
349 var b = try Managed.initSet(testing.allocator, 5678);
350 defer b.deinit();
351
352 testing.expect((try a.to(u32)) == 1234);
353 testing.expect((try b.to(u32)) == 5678);
354
355 a.swap(&b);
356
357 testing.expect((try a.to(u32)) == 5678);
358 testing.expect((try b.to(u32)) == 1234);
359}
360
361test "big.int to negative" {
362 var a = try Managed.initSet(testing.allocator, -10);
363 defer a.deinit();
364
365 testing.expect((try a.to(i32)) == -10);
366}
367
368test "big.int compare" {
369 var a = try Managed.initSet(testing.allocator, -11);
370 defer a.deinit();
371 var b = try Managed.initSet(testing.allocator, 10);
372 defer b.deinit();
373
374 testing.expect(a.orderAbs(b) == .gt);
375 testing.expect(a.order(b) == .lt);
376}
377
378test "big.int compare similar" {
379 var a = try Managed.initSet(testing.allocator, 0xffffffffeeeeeeeeffffffffeeeeeeee);
380 defer a.deinit();
381 var b = try Managed.initSet(testing.allocator, 0xffffffffeeeeeeeeffffffffeeeeeeef);
382 defer b.deinit();
383
384 testing.expect(a.orderAbs(b) == .lt);
385 testing.expect(b.orderAbs(a) == .gt);
386}
387
388test "big.int compare different limb size" {
389 var a = try Managed.initSet(testing.allocator, maxInt(Limb) + 1);
390 defer a.deinit();
391 var b = try Managed.initSet(testing.allocator, 1);
392 defer b.deinit();
393
394 testing.expect(a.orderAbs(b) == .gt);
395 testing.expect(b.orderAbs(a) == .lt);
396}
397
398test "big.int compare multi-limb" {
399 var a = try Managed.initSet(testing.allocator, -0x7777777799999999ffffeeeeffffeeeeffffeeeef);
400 defer a.deinit();
401 var b = try Managed.initSet(testing.allocator, 0x7777777799999999ffffeeeeffffeeeeffffeeeee);
402 defer b.deinit();
403
404 testing.expect(a.orderAbs(b) == .gt);
405 testing.expect(a.order(b) == .lt);
406}
407
408test "big.int equality" {
409 var a = try Managed.initSet(testing.allocator, 0xffffffff1);
410 defer a.deinit();
411 var b = try Managed.initSet(testing.allocator, -0xffffffff1);
412 defer b.deinit();
413
414 testing.expect(a.eqAbs(b));
415 testing.expect(!a.eq(b));
416}
417
418test "big.int abs" {
419 var a = try Managed.initSet(testing.allocator, -5);
420 defer a.deinit();
421
422 a.abs();
423 testing.expect((try a.to(u32)) == 5);
424
425 a.abs();
426 testing.expect((try a.to(u32)) == 5);
427}
428
429test "big.int negate" {
430 var a = try Managed.initSet(testing.allocator, 5);
431 defer a.deinit();
432
433 a.negate();
434 testing.expect((try a.to(i32)) == -5);
435
436 a.negate();
437 testing.expect((try a.to(i32)) == 5);
438}
439
440test "big.int add single-single" {
441 var a = try Managed.initSet(testing.allocator, 50);
442 defer a.deinit();
443 var b = try Managed.initSet(testing.allocator, 5);
444 defer b.deinit();
445
446 var c = try Managed.init(testing.allocator);
447 defer c.deinit();
448 try c.add(a.toConst(), b.toConst());
449
450 testing.expect((try c.to(u32)) == 55);
451}
452
453test "big.int add multi-single" {
454 var a = try Managed.initSet(testing.allocator, maxInt(Limb) + 1);
455 defer a.deinit();
456 var b = try Managed.initSet(testing.allocator, 1);
457 defer b.deinit();
458
459 var c = try Managed.init(testing.allocator);
460 defer c.deinit();
461
462 try c.add(a.toConst(), b.toConst());
463 testing.expect((try c.to(DoubleLimb)) == maxInt(Limb) + 2);
464
465 try c.add(b.toConst(), a.toConst());
466 testing.expect((try c.to(DoubleLimb)) == maxInt(Limb) + 2);
467}
468
469test "big.int add multi-multi" {
470 const op1 = 0xefefefef7f7f7f7f;
471 const op2 = 0xfefefefe9f9f9f9f;
472 var a = try Managed.initSet(testing.allocator, op1);
473 defer a.deinit();
474 var b = try Managed.initSet(testing.allocator, op2);
475 defer b.deinit();
476
477 var c = try Managed.init(testing.allocator);
478 defer c.deinit();
479 try c.add(a.toConst(), b.toConst());
480
481 testing.expect((try c.to(u128)) == op1 + op2);
482}
483
484test "big.int add zero-zero" {
485 var a = try Managed.initSet(testing.allocator, 0);
486 defer a.deinit();
487 var b = try Managed.initSet(testing.allocator, 0);
488 defer b.deinit();
489
490 var c = try Managed.init(testing.allocator);
491 defer c.deinit();
492 try c.add(a.toConst(), b.toConst());
493
494 testing.expect((try c.to(u32)) == 0);
495}
496
497test "big.int add alias multi-limb nonzero-zero" {
498 const op1 = 0xffffffff777777771;
499 var a = try Managed.initSet(testing.allocator, op1);
500 defer a.deinit();
501 var b = try Managed.initSet(testing.allocator, 0);
502 defer b.deinit();
503
504 try a.add(a.toConst(), b.toConst());
505
506 testing.expect((try a.to(u128)) == op1);
507}
508
509test "big.int add sign" {
510 var a = try Managed.init(testing.allocator);
511 defer a.deinit();
512
513 var one = try Managed.initSet(testing.allocator, 1);
514 defer one.deinit();
515 var two = try Managed.initSet(testing.allocator, 2);
516 defer two.deinit();
517 var neg_one = try Managed.initSet(testing.allocator, -1);
518 defer neg_one.deinit();
519 var neg_two = try Managed.initSet(testing.allocator, -2);
520 defer neg_two.deinit();
521
522 try a.add(one.toConst(), two.toConst());
523 testing.expect((try a.to(i32)) == 3);
524
525 try a.add(neg_one.toConst(), two.toConst());
526 testing.expect((try a.to(i32)) == 1);
527
528 try a.add(one.toConst(), neg_two.toConst());
529 testing.expect((try a.to(i32)) == -1);
530
531 try a.add(neg_one.toConst(), neg_two.toConst());
532 testing.expect((try a.to(i32)) == -3);
533}
534
535test "big.int sub single-single" {
536 var a = try Managed.initSet(testing.allocator, 50);
537 defer a.deinit();
538 var b = try Managed.initSet(testing.allocator, 5);
539 defer b.deinit();
540
541 var c = try Managed.init(testing.allocator);
542 defer c.deinit();
543 try c.sub(a.toConst(), b.toConst());
544
545 testing.expect((try c.to(u32)) == 45);
546}
547
548test "big.int sub multi-single" {
549 var a = try Managed.initSet(testing.allocator, maxInt(Limb) + 1);
550 defer a.deinit();
551 var b = try Managed.initSet(testing.allocator, 1);
552 defer b.deinit();
553
554 var c = try Managed.init(testing.allocator);
555 defer c.deinit();
556 try c.sub(a.toConst(), b.toConst());
557
558 testing.expect((try c.to(Limb)) == maxInt(Limb));
559}
560
561test "big.int sub multi-multi" {
562 const op1 = 0xefefefefefefefefefefefef;
563 const op2 = 0xabababababababababababab;
564
565 var a = try Managed.initSet(testing.allocator, op1);
566 defer a.deinit();
567 var b = try Managed.initSet(testing.allocator, op2);
568 defer b.deinit();
569
570 var c = try Managed.init(testing.allocator);
571 defer c.deinit();
572 try c.sub(a.toConst(), b.toConst());
573
574 testing.expect((try c.to(u128)) == op1 - op2);
575}
576
577test "big.int sub equal" {
578 var a = try Managed.initSet(testing.allocator, 0x11efefefefefefefefefefefef);
579 defer a.deinit();
580 var b = try Managed.initSet(testing.allocator, 0x11efefefefefefefefefefefef);
581 defer b.deinit();
582
583 var c = try Managed.init(testing.allocator);
584 defer c.deinit();
585 try c.sub(a.toConst(), b.toConst());
586
587 testing.expect((try c.to(u32)) == 0);
588}
589
590test "big.int sub sign" {
591 var a = try Managed.init(testing.allocator);
592 defer a.deinit();
593
594 var one = try Managed.initSet(testing.allocator, 1);
595 defer one.deinit();
596 var two = try Managed.initSet(testing.allocator, 2);
597 defer two.deinit();
598 var neg_one = try Managed.initSet(testing.allocator, -1);
599 defer neg_one.deinit();
600 var neg_two = try Managed.initSet(testing.allocator, -2);
601 defer neg_two.deinit();
602
603 try a.sub(one.toConst(), two.toConst());
604 testing.expect((try a.to(i32)) == -1);
605
606 try a.sub(neg_one.toConst(), two.toConst());
607 testing.expect((try a.to(i32)) == -3);
608
609 try a.sub(one.toConst(), neg_two.toConst());
610 testing.expect((try a.to(i32)) == 3);
611
612 try a.sub(neg_one.toConst(), neg_two.toConst());
613 testing.expect((try a.to(i32)) == 1);
614
615 try a.sub(neg_two.toConst(), neg_one.toConst());
616 testing.expect((try a.to(i32)) == -1);
617}
618
619test "big.int mul single-single" {
620 var a = try Managed.initSet(testing.allocator, 50);
621 defer a.deinit();
622 var b = try Managed.initSet(testing.allocator, 5);
623 defer b.deinit();
624
625 var c = try Managed.init(testing.allocator);
626 defer c.deinit();
627 try c.mul(a.toConst(), b.toConst());
628
629 testing.expect((try c.to(u64)) == 250);
630}
631
632test "big.int mul multi-single" {
633 var a = try Managed.initSet(testing.allocator, maxInt(Limb));
634 defer a.deinit();
635 var b = try Managed.initSet(testing.allocator, 2);
636 defer b.deinit();
637
638 var c = try Managed.init(testing.allocator);
639 defer c.deinit();
640 try c.mul(a.toConst(), b.toConst());
641
642 testing.expect((try c.to(DoubleLimb)) == 2 * maxInt(Limb));
643}
644
645test "big.int mul multi-multi" {
646 const op1 = 0x998888efefefefefefefef;
647 const op2 = 0x333000abababababababab;
648 var a = try Managed.initSet(testing.allocator, op1);
649 defer a.deinit();
650 var b = try Managed.initSet(testing.allocator, op2);
651 defer b.deinit();
652
653 var c = try Managed.init(testing.allocator);
654 defer c.deinit();
655 try c.mul(a.toConst(), b.toConst());
656
657 testing.expect((try c.to(u256)) == op1 * op2);
658}
659
660test "big.int mul alias r with a" {
661 var a = try Managed.initSet(testing.allocator, maxInt(Limb));
662 defer a.deinit();
663 var b = try Managed.initSet(testing.allocator, 2);
664 defer b.deinit();
665
666 try a.mul(a.toConst(), b.toConst());
667
668 testing.expect((try a.to(DoubleLimb)) == 2 * maxInt(Limb));
669}
670
671test "big.int mul alias r with b" {
672 var a = try Managed.initSet(testing.allocator, maxInt(Limb));
673 defer a.deinit();
674 var b = try Managed.initSet(testing.allocator, 2);
675 defer b.deinit();
676
677 try a.mul(b.toConst(), a.toConst());
678
679 testing.expect((try a.to(DoubleLimb)) == 2 * maxInt(Limb));
680}
681
682test "big.int mul alias r with a and b" {
683 var a = try Managed.initSet(testing.allocator, maxInt(Limb));
684 defer a.deinit();
685
686 try a.mul(a.toConst(), a.toConst());
687
688 testing.expect((try a.to(DoubleLimb)) == maxInt(Limb) * maxInt(Limb));
689}
690
691test "big.int mul a*0" {
692 var a = try Managed.initSet(testing.allocator, 0xefefefefefefefef);
693 defer a.deinit();
694 var b = try Managed.initSet(testing.allocator, 0);
695 defer b.deinit();
696
697 var c = try Managed.init(testing.allocator);
698 defer c.deinit();
699 try c.mul(a.toConst(), b.toConst());
700
701 testing.expect((try c.to(u32)) == 0);
702}
703
704test "big.int mul 0*0" {
705 var a = try Managed.initSet(testing.allocator, 0);
706 defer a.deinit();
707 var b = try Managed.initSet(testing.allocator, 0);
708 defer b.deinit();
709
710 var c = try Managed.init(testing.allocator);
711 defer c.deinit();
712 try c.mul(a.toConst(), b.toConst());
713
714 testing.expect((try c.to(u32)) == 0);
715}
716
717test "big.int div single-single no rem" {
718 var a = try Managed.initSet(testing.allocator, 50);
719 defer a.deinit();
720 var b = try Managed.initSet(testing.allocator, 5);
721 defer b.deinit();
722
723 var q = try Managed.init(testing.allocator);
724 defer q.deinit();
725 var r = try Managed.init(testing.allocator);
726 defer r.deinit();
727 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
728
729 testing.expect((try q.to(u32)) == 10);
730 testing.expect((try r.to(u32)) == 0);
731}
732
733test "big.int div single-single with rem" {
734 var a = try Managed.initSet(testing.allocator, 49);
735 defer a.deinit();
736 var b = try Managed.initSet(testing.allocator, 5);
737 defer b.deinit();
738
739 var q = try Managed.init(testing.allocator);
740 defer q.deinit();
741 var r = try Managed.init(testing.allocator);
742 defer r.deinit();
743 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
744
745 testing.expect((try q.to(u32)) == 9);
746 testing.expect((try r.to(u32)) == 4);
747}
748
749test "big.int div multi-single no rem" {
750 const op1 = 0xffffeeeeddddcccc;
751 const op2 = 34;
752
753 var a = try Managed.initSet(testing.allocator, op1);
754 defer a.deinit();
755 var b = try Managed.initSet(testing.allocator, op2);
756 defer b.deinit();
757
758 var q = try Managed.init(testing.allocator);
759 defer q.deinit();
760 var r = try Managed.init(testing.allocator);
761 defer r.deinit();
762 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
763
764 testing.expect((try q.to(u64)) == op1 / op2);
765 testing.expect((try r.to(u64)) == 0);
766}
767
768test "big.int div multi-single with rem" {
769 const op1 = 0xffffeeeeddddcccf;
770 const op2 = 34;
771
772 var a = try Managed.initSet(testing.allocator, op1);
773 defer a.deinit();
774 var b = try Managed.initSet(testing.allocator, op2);
775 defer b.deinit();
776
777 var q = try Managed.init(testing.allocator);
778 defer q.deinit();
779 var r = try Managed.init(testing.allocator);
780 defer r.deinit();
781 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
782
783 testing.expect((try q.to(u64)) == op1 / op2);
784 testing.expect((try r.to(u64)) == 3);
785}
786
787test "big.int div multi>2-single" {
788 const op1 = 0xfefefefefefefefefefefefefefefefe;
789 const op2 = 0xefab8;
790
791 var a = try Managed.initSet(testing.allocator, op1);
792 defer a.deinit();
793 var b = try Managed.initSet(testing.allocator, op2);
794 defer b.deinit();
795
796 var q = try Managed.init(testing.allocator);
797 defer q.deinit();
798 var r = try Managed.init(testing.allocator);
799 defer r.deinit();
800 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
801
802 testing.expect((try q.to(u128)) == op1 / op2);
803 testing.expect((try r.to(u32)) == 0x3e4e);
804}
805
806test "big.int div single-single q < r" {
807 var a = try Managed.initSet(testing.allocator, 0x0078f432);
808 defer a.deinit();
809 var b = try Managed.initSet(testing.allocator, 0x01000000);
810 defer b.deinit();
811
812 var q = try Managed.init(testing.allocator);
813 defer q.deinit();
814 var r = try Managed.init(testing.allocator);
815 defer r.deinit();
816 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
817
818 testing.expect((try q.to(u64)) == 0);
819 testing.expect((try r.to(u64)) == 0x0078f432);
820}
821
822test "big.int div single-single q == r" {
823 var a = try Managed.initSet(testing.allocator, 10);
824 defer a.deinit();
825 var b = try Managed.initSet(testing.allocator, 10);
826 defer b.deinit();
827
828 var q = try Managed.init(testing.allocator);
829 defer q.deinit();
830 var r = try Managed.init(testing.allocator);
831 defer r.deinit();
832 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
833
834 testing.expect((try q.to(u64)) == 1);
835 testing.expect((try r.to(u64)) == 0);
836}
837
838test "big.int div q=0 alias" {
839 var a = try Managed.initSet(testing.allocator, 3);
840 defer a.deinit();
841 var b = try Managed.initSet(testing.allocator, 10);
842 defer b.deinit();
843
844 try Managed.divTrunc(&a, &b, a.toConst(), b.toConst());
845
846 testing.expect((try a.to(u64)) == 0);
847 testing.expect((try b.to(u64)) == 3);
848}
849
850test "big.int div multi-multi q < r" {
851 const op1 = 0x1ffffffff0078f432;
852 const op2 = 0x1ffffffff01000000;
853 var a = try Managed.initSet(testing.allocator, op1);
854 defer a.deinit();
855 var b = try Managed.initSet(testing.allocator, op2);
856 defer b.deinit();
857
858 var q = try Managed.init(testing.allocator);
859 defer q.deinit();
860 var r = try Managed.init(testing.allocator);
861 defer r.deinit();
862 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
863
864 testing.expect((try q.to(u128)) == 0);
865 testing.expect((try r.to(u128)) == op1);
866}
867
868test "big.int div trunc single-single +/+" {
869 const u: i32 = 5;
870 const v: i32 = 3;
871
872 var a = try Managed.initSet(testing.allocator, u);
873 defer a.deinit();
874 var b = try Managed.initSet(testing.allocator, v);
875 defer b.deinit();
876
877 var q = try Managed.init(testing.allocator);
878 defer q.deinit();
879 var r = try Managed.init(testing.allocator);
880 defer r.deinit();
881 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
882
883 // n = q * d + r
884 // 5 = 1 * 3 + 2
885 const eq = @divTrunc(u, v);
886 const er = @mod(u, v);
887
888 testing.expect((try q.to(i32)) == eq);
889 testing.expect((try r.to(i32)) == er);
890}
891
892test "big.int div trunc single-single -/+" {
893 const u: i32 = -5;
894 const v: i32 = 3;
895
896 var a = try Managed.initSet(testing.allocator, u);
897 defer a.deinit();
898 var b = try Managed.initSet(testing.allocator, v);
899 defer b.deinit();
900
901 var q = try Managed.init(testing.allocator);
902 defer q.deinit();
903 var r = try Managed.init(testing.allocator);
904 defer r.deinit();
905 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
906
907 // n = q * d + r
908 // -5 = 1 * -3 - 2
909 const eq = -1;
910 const er = -2;
911
912 testing.expect((try q.to(i32)) == eq);
913 testing.expect((try r.to(i32)) == er);
914}
915
916test "big.int div trunc single-single +/-" {
917 const u: i32 = 5;
918 const v: i32 = -3;
919
920 var a = try Managed.initSet(testing.allocator, u);
921 defer a.deinit();
922 var b = try Managed.initSet(testing.allocator, v);
923 defer b.deinit();
924
925 var q = try Managed.init(testing.allocator);
926 defer q.deinit();
927 var r = try Managed.init(testing.allocator);
928 defer r.deinit();
929 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
930
931 // n = q * d + r
932 // 5 = -1 * -3 + 2
933 const eq = -1;
934 const er = 2;
935
936 testing.expect((try q.to(i32)) == eq);
937 testing.expect((try r.to(i32)) == er);
938}
939
940test "big.int div trunc single-single -/-" {
941 const u: i32 = -5;
942 const v: i32 = -3;
943
944 var a = try Managed.initSet(testing.allocator, u);
945 defer a.deinit();
946 var b = try Managed.initSet(testing.allocator, v);
947 defer b.deinit();
948
949 var q = try Managed.init(testing.allocator);
950 defer q.deinit();
951 var r = try Managed.init(testing.allocator);
952 defer r.deinit();
953 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
954
955 // n = q * d + r
956 // -5 = 1 * -3 - 2
957 const eq = 1;
958 const er = -2;
959
960 testing.expect((try q.to(i32)) == eq);
961 testing.expect((try r.to(i32)) == er);
962}
963
964test "big.int div floor single-single +/+" {
965 const u: i32 = 5;
966 const v: i32 = 3;
967
968 var a = try Managed.initSet(testing.allocator, u);
969 defer a.deinit();
970 var b = try Managed.initSet(testing.allocator, v);
971 defer b.deinit();
972
973 var q = try Managed.init(testing.allocator);
974 defer q.deinit();
975 var r = try Managed.init(testing.allocator);
976 defer r.deinit();
977 try Managed.divFloor(&q, &r, a.toConst(), b.toConst());
978
979 // n = q * d + r
980 // 5 = 1 * 3 + 2
981 const eq = 1;
982 const er = 2;
983
984 testing.expect((try q.to(i32)) == eq);
985 testing.expect((try r.to(i32)) == er);
986}
987
988test "big.int div floor single-single -/+" {
989 const u: i32 = -5;
990 const v: i32 = 3;
991
992 var a = try Managed.initSet(testing.allocator, u);
993 defer a.deinit();
994 var b = try Managed.initSet(testing.allocator, v);
995 defer b.deinit();
996
997 var q = try Managed.init(testing.allocator);
998 defer q.deinit();
999 var r = try Managed.init(testing.allocator);
1000 defer r.deinit();
1001 try Managed.divFloor(&q, &r, a.toConst(), b.toConst());
1002
1003 // n = q * d + r
1004 // -5 = -2 * 3 + 1
1005 const eq = -2;
1006 const er = 1;
1007
1008 testing.expect((try q.to(i32)) == eq);
1009 testing.expect((try r.to(i32)) == er);
1010}
1011
1012test "big.int div floor single-single +/-" {
1013 const u: i32 = 5;
1014 const v: i32 = -3;
1015
1016 var a = try Managed.initSet(testing.allocator, u);
1017 defer a.deinit();
1018 var b = try Managed.initSet(testing.allocator, v);
1019 defer b.deinit();
1020
1021 var q = try Managed.init(testing.allocator);
1022 defer q.deinit();
1023 var r = try Managed.init(testing.allocator);
1024 defer r.deinit();
1025 try Managed.divFloor(&q, &r, a.toConst(), b.toConst());
1026
1027 // n = q * d + r
1028 // 5 = -2 * -3 - 1
1029 const eq = -2;
1030 const er = -1;
1031
1032 testing.expect((try q.to(i32)) == eq);
1033 testing.expect((try r.to(i32)) == er);
1034}
1035
1036test "big.int div floor single-single -/-" {
1037 const u: i32 = -5;
1038 const v: i32 = -3;
1039
1040 var a = try Managed.initSet(testing.allocator, u);
1041 defer a.deinit();
1042 var b = try Managed.initSet(testing.allocator, v);
1043 defer b.deinit();
1044
1045 var q = try Managed.init(testing.allocator);
1046 defer q.deinit();
1047 var r = try Managed.init(testing.allocator);
1048 defer r.deinit();
1049 try Managed.divFloor(&q, &r, a.toConst(), b.toConst());
1050
1051 // n = q * d + r
1052 // -5 = 2 * -3 + 1
1053 const eq = 1;
1054 const er = -2;
1055
1056 testing.expect((try q.to(i32)) == eq);
1057 testing.expect((try r.to(i32)) == er);
1058}
1059
1060test "big.int div multi-multi with rem" {
1061 var a = try Managed.initSet(testing.allocator, 0x8888999911110000ffffeeeeddddccccbbbbaaaa9999);
1062 defer a.deinit();
1063 var b = try Managed.initSet(testing.allocator, 0x99990000111122223333);
1064 defer b.deinit();
1065
1066 var q = try Managed.init(testing.allocator);
1067 defer q.deinit();
1068 var r = try Managed.init(testing.allocator);
1069 defer r.deinit();
1070 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
1071
1072 testing.expect((try q.to(u128)) == 0xe38f38e39161aaabd03f0f1b);
1073 testing.expect((try r.to(u128)) == 0x28de0acacd806823638);
1074}
1075
1076test "big.int div multi-multi no rem" {
1077 var a = try Managed.initSet(testing.allocator, 0x8888999911110000ffffeeeedb4fec200ee3a4286361);
1078 defer a.deinit();
1079 var b = try Managed.initSet(testing.allocator, 0x99990000111122223333);
1080 defer b.deinit();
1081
1082 var q = try Managed.init(testing.allocator);
1083 defer q.deinit();
1084 var r = try Managed.init(testing.allocator);
1085 defer r.deinit();
1086 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
1087
1088 testing.expect((try q.to(u128)) == 0xe38f38e39161aaabd03f0f1b);
1089 testing.expect((try r.to(u128)) == 0);
1090}
1091
1092test "big.int div multi-multi (2 branch)" {
1093 var a = try Managed.initSet(testing.allocator, 0x866666665555555588888887777777761111111111111111);
1094 defer a.deinit();
1095 var b = try Managed.initSet(testing.allocator, 0x86666666555555554444444433333333);
1096 defer b.deinit();
1097
1098 var q = try Managed.init(testing.allocator);
1099 defer q.deinit();
1100 var r = try Managed.init(testing.allocator);
1101 defer r.deinit();
1102 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
1103
1104 testing.expect((try q.to(u128)) == 0x10000000000000000);
1105 testing.expect((try r.to(u128)) == 0x44444443444444431111111111111111);
1106}
1107
1108test "big.int div multi-multi (3.1/3.3 branch)" {
1109 var a = try Managed.initSet(testing.allocator, 0x11111111111111111111111111111111111111111111111111111111111111);
1110 defer a.deinit();
1111 var b = try Managed.initSet(testing.allocator, 0x1111111111111111111111111111111111111111171);
1112 defer b.deinit();
1113
1114 var q = try Managed.init(testing.allocator);
1115 defer q.deinit();
1116 var r = try Managed.init(testing.allocator);
1117 defer r.deinit();
1118 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
1119
1120 testing.expect((try q.to(u128)) == 0xfffffffffffffffffff);
1121 testing.expect((try r.to(u256)) == 0x1111111111111111111110b12222222222222222282);
1122}
1123
1124test "big.int div multi-single zero-limb trailing" {
1125 var a = try Managed.initSet(testing.allocator, 0x60000000000000000000000000000000000000000000000000000000000000000);
1126 defer a.deinit();
1127 var b = try Managed.initSet(testing.allocator, 0x10000000000000000);
1128 defer b.deinit();
1129
1130 var q = try Managed.init(testing.allocator);
1131 defer q.deinit();
1132 var r = try Managed.init(testing.allocator);
1133 defer r.deinit();
1134 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
1135
1136 var expected = try Managed.initSet(testing.allocator, 0x6000000000000000000000000000000000000000000000000);
1137 defer expected.deinit();
1138 testing.expect(q.eq(expected));
1139 testing.expect(r.eqZero());
1140}
1141
1142test "big.int div multi-multi zero-limb trailing (with rem)" {
1143 var a = try Managed.initSet(testing.allocator, 0x86666666555555558888888777777776111111111111111100000000000000000000000000000000);
1144 defer a.deinit();
1145 var b = try Managed.initSet(testing.allocator, 0x8666666655555555444444443333333300000000000000000000000000000000);
1146 defer b.deinit();
1147
1148 var q = try Managed.init(testing.allocator);
1149 defer q.deinit();
1150 var r = try Managed.init(testing.allocator);
1151 defer r.deinit();
1152 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
1153
1154 testing.expect((try q.to(u128)) == 0x10000000000000000);
1155
1156 const rs = try r.toString(testing.allocator, 16, false);
1157 defer testing.allocator.free(rs);
1158 testing.expect(std.mem.eql(u8, rs, "4444444344444443111111111111111100000000000000000000000000000000"));
1159}
1160
1161test "big.int div multi-multi zero-limb trailing (with rem) and dividend zero-limb count > divisor zero-limb count" {
1162 var a = try Managed.initSet(testing.allocator, 0x8666666655555555888888877777777611111111111111110000000000000000);
1163 defer a.deinit();
1164 var b = try Managed.initSet(testing.allocator, 0x8666666655555555444444443333333300000000000000000000000000000000);
1165 defer b.deinit();
1166
1167 var q = try Managed.init(testing.allocator);
1168 defer q.deinit();
1169 var r = try Managed.init(testing.allocator);
1170 defer r.deinit();
1171 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
1172
1173 testing.expect((try q.to(u128)) == 0x1);
1174
1175 const rs = try r.toString(testing.allocator, 16, false);
1176 defer testing.allocator.free(rs);
1177 testing.expect(std.mem.eql(u8, rs, "444444434444444311111111111111110000000000000000"));
1178}
1179
1180test "big.int div multi-multi zero-limb trailing (with rem) and dividend zero-limb count < divisor zero-limb count" {
1181 var a = try Managed.initSet(testing.allocator, 0x86666666555555558888888777777776111111111111111100000000000000000000000000000000);
1182 defer a.deinit();
1183 var b = try Managed.initSet(testing.allocator, 0x866666665555555544444444333333330000000000000000);
1184 defer b.deinit();
1185
1186 var q = try Managed.init(testing.allocator);
1187 defer q.deinit();
1188 var r = try Managed.init(testing.allocator);
1189 defer r.deinit();
1190 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
1191
1192 const qs = try q.toString(testing.allocator, 16, false);
1193 defer testing.allocator.free(qs);
1194 testing.expect(std.mem.eql(u8, qs, "10000000000000000820820803105186f"));
1195
1196 const rs = try r.toString(testing.allocator, 16, false);
1197 defer testing.allocator.free(rs);
1198 testing.expect(std.mem.eql(u8, rs, "4e11f2baa5896a321d463b543d0104e30000000000000000"));
1199}
1200
1201test "big.int div multi-multi fuzz case #1" {
1202 var a = try Managed.init(testing.allocator);
1203 defer a.deinit();
1204 var b = try Managed.init(testing.allocator);
1205 defer b.deinit();
1206
1207 try a.setString(16, "ffffffffffffffffffffffffffffc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
1208 try b.setString(16, "3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0000000000000000000000000000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffc000000000000000000000000000000007fffffffffff");
1209
1210 var q = try Managed.init(testing.allocator);
1211 defer q.deinit();
1212 var r = try Managed.init(testing.allocator);
1213 defer r.deinit();
1214 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
1215
1216 const qs = try q.toString(testing.allocator, 16, false);
1217 defer testing.allocator.free(qs);
1218 testing.expect(std.mem.eql(u8, qs, "3ffffffffffffffffffffffffffff0000000000000000000000000000000000001ffffffffffffffffffffffffffff7fffffffe000000000000000000000000000180000000000000000000003fffffbfffffffdfffffffffffffeffff800000100101000000100000000020003fffffdfbfffffe3ffffffffffffeffff7fffc00800a100000017ffe000002000400007efbfff7fe9f00000037ffff3fff7fffa004006100000009ffe00000190038200bf7d2ff7fefe80400060000f7d7f8fbf9401fe38e0403ffc0bdffffa51102c300d7be5ef9df4e5060007b0127ad3fa69f97d0f820b6605ff617ddf7f32ad7a05c0d03f2e7bc78a6000e087a8bbcdc59e07a5a079128a7861f553ddebed7e8e56701756f9ead39b48cd1b0831889ea6ec1fddf643d0565b075ff07e6caea4e2854ec9227fd635ed60a2f5eef2893052ffd54718fa08604acbf6a15e78a467c4a3c53c0278af06c4416573f925491b195e8fd79302cb1aaf7caf4ecfc9aec1254cc969786363ac729f914c6ddcc26738d6b0facd54eba026580aba2eb6482a088b0d224a8852420b91ec1"));
1219
1220 const rs = try r.toString(testing.allocator, 16, false);
1221 defer testing.allocator.free(rs);
1222 testing.expect(std.mem.eql(u8, rs, "310d1d4c414426b4836c2635bad1df3a424e50cbdd167ffccb4dfff57d36b4aae0d6ca0910698220171a0f3373c1060a046c2812f0027e321f72979daa5e7973214170d49e885de0c0ecc167837d44502430674a82522e5df6a0759548052420b91ec1"));
1223}
1224
1225test "big.int div multi-multi fuzz case #2" {
1226 var a = try Managed.init(testing.allocator);
1227 defer a.deinit();
1228 var b = try Managed.init(testing.allocator);
1229 defer b.deinit();
1230
1231 try a.setString(16, "3ffffffffe00000000000000000000000000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffe000000000000000000000000000000000000000000000000000000000000001fffffffffffffffff800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffc000000000000000000000000000000000000000000000000000000000000000");
1232 try b.setString(16, "ffc0000000000000000000000000000000000000000000000000");
1233
1234 var q = try Managed.init(testing.allocator);
1235 defer q.deinit();
1236 var r = try Managed.init(testing.allocator);
1237 defer r.deinit();
1238 try Managed.divTrunc(&q, &r, a.toConst(), b.toConst());
1239
1240 const qs = try q.toString(testing.allocator, 16, false);
1241 defer testing.allocator.free(qs);
1242 testing.expect(std.mem.eql(u8, qs, "40100400fe3f8fe3f8fe3f8fe3f8fe3f8fe4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f91e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4992649926499264991e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4792e4b92e4b92e4b92e4b92a4a92a4a92a4"));
1243
1244 const rs = try r.toString(testing.allocator, 16, false);
1245 defer testing.allocator.free(rs);
1246 testing.expect(std.mem.eql(u8, rs, "a900000000000000000000000000000000000000000000000000"));
1247}
1248
1249test "big.int shift-right single" {
1250 var a = try Managed.initSet(testing.allocator, 0xffff0000);
1251 defer a.deinit();
1252 try a.shiftRight(a, 16);
1253
1254 testing.expect((try a.to(u32)) == 0xffff);
1255}
1256
1257test "big.int shift-right multi" {
1258 var a = try Managed.initSet(testing.allocator, 0xffff0000eeee1111dddd2222cccc3333);
1259 defer a.deinit();
1260 try a.shiftRight(a, 67);
1261
1262 testing.expect((try a.to(u64)) == 0x1fffe0001dddc222);
1263}
1264
1265test "big.int shift-left single" {
1266 var a = try Managed.initSet(testing.allocator, 0xffff);
1267 defer a.deinit();
1268 try a.shiftLeft(a, 16);
1269
1270 testing.expect((try a.to(u64)) == 0xffff0000);
1271}
1272
1273test "big.int shift-left multi" {
1274 var a = try Managed.initSet(testing.allocator, 0x1fffe0001dddc222);
1275 defer a.deinit();
1276 try a.shiftLeft(a, 67);
1277
1278 testing.expect((try a.to(u128)) == 0xffff0000eeee11100000000000000000);
1279}
1280
1281test "big.int shift-right negative" {
1282 var a = try Managed.init(testing.allocator);
1283 defer a.deinit();
1284
1285 var arg = try Managed.initSet(testing.allocator, -20);
1286 defer arg.deinit();
1287 try a.shiftRight(arg, 2);
1288 testing.expect((try a.to(i32)) == -20 >> 2);
1289
1290 var arg2 = try Managed.initSet(testing.allocator, -5);
1291 defer arg2.deinit();
1292 try a.shiftRight(arg2, 10);
1293 testing.expect((try a.to(i32)) == -5 >> 10);
1294}
1295
1296test "big.int shift-left negative" {
1297 var a = try Managed.init(testing.allocator);
1298 defer a.deinit();
1299
1300 var arg = try Managed.initSet(testing.allocator, -10);
1301 defer arg.deinit();
1302 try a.shiftRight(arg, 1232);
1303 testing.expect((try a.to(i32)) == -10 >> 1232);
1304}
1305
1306test "big.int bitwise and simple" {
1307 var a = try Managed.initSet(testing.allocator, 0xffffffff11111111);
1308 defer a.deinit();
1309 var b = try Managed.initSet(testing.allocator, 0xeeeeeeee22222222);
1310 defer b.deinit();
1311
1312 try a.bitAnd(a, b);
1313
1314 testing.expect((try a.to(u64)) == 0xeeeeeeee00000000);
1315}
1316
1317test "big.int bitwise and multi-limb" {
1318 var a = try Managed.initSet(testing.allocator, maxInt(Limb) + 1);
1319 defer a.deinit();
1320 var b = try Managed.initSet(testing.allocator, maxInt(Limb));
1321 defer b.deinit();
1322
1323 try a.bitAnd(a, b);
1324
1325 testing.expect((try a.to(u128)) == 0);
1326}
1327
1328test "big.int bitwise xor simple" {
1329 var a = try Managed.initSet(testing.allocator, 0xffffffff11111111);
1330 defer a.deinit();
1331 var b = try Managed.initSet(testing.allocator, 0xeeeeeeee22222222);
1332 defer b.deinit();
1333
1334 try a.bitXor(a, b);
1335
1336 testing.expect((try a.to(u64)) == 0x1111111133333333);
1337}
1338
1339test "big.int bitwise xor multi-limb" {
1340 var a = try Managed.initSet(testing.allocator, maxInt(Limb) + 1);
1341 defer a.deinit();
1342 var b = try Managed.initSet(testing.allocator, maxInt(Limb));
1343 defer b.deinit();
1344
1345 try a.bitXor(a, b);
1346
1347 testing.expect((try a.to(DoubleLimb)) == (maxInt(Limb) + 1) ^ maxInt(Limb));
1348}
1349
1350test "big.int bitwise or simple" {
1351 var a = try Managed.initSet(testing.allocator, 0xffffffff11111111);
1352 defer a.deinit();
1353 var b = try Managed.initSet(testing.allocator, 0xeeeeeeee22222222);
1354 defer b.deinit();
1355
1356 try a.bitOr(a, b);
1357
1358 testing.expect((try a.to(u64)) == 0xffffffff33333333);
1359}
1360
1361test "big.int bitwise or multi-limb" {
1362 var a = try Managed.initSet(testing.allocator, maxInt(Limb) + 1);
1363 defer a.deinit();
1364 var b = try Managed.initSet(testing.allocator, maxInt(Limb));
1365 defer b.deinit();
1366
1367 try a.bitOr(a, b);
1368
1369 // TODO: big.int.cpp or is wrong on multi-limb.
1370 testing.expect((try a.to(DoubleLimb)) == (maxInt(Limb) + 1) + maxInt(Limb));
1371}
1372
1373test "big.int var args" {
1374 var a = try Managed.initSet(testing.allocator, 5);
1375 defer a.deinit();
1376
1377 var b = try Managed.initSet(testing.allocator, 6);
1378 defer b.deinit();
1379 try a.add(a.toConst(), b.toConst());
1380 testing.expect((try a.to(u64)) == 11);
1381
1382 var c = try Managed.initSet(testing.allocator, 11);
1383 defer c.deinit();
1384 testing.expect(a.order(c) == .eq);
1385
1386 var d = try Managed.initSet(testing.allocator, 14);
1387 defer d.deinit();
1388 testing.expect(a.order(d) != .gt);
1389}
1390
1391test "big.int gcd non-one small" {
1392 var a = try Managed.initSet(testing.allocator, 17);
1393 defer a.deinit();
1394 var b = try Managed.initSet(testing.allocator, 97);
1395 defer b.deinit();
1396 var r = try Managed.init(testing.allocator);
1397 defer r.deinit();
1398
1399 try r.gcd(a, b);
1400
1401 testing.expect((try r.to(u32)) == 1);
1402}
1403
1404test "big.int gcd non-one small" {
1405 var a = try Managed.initSet(testing.allocator, 4864);
1406 defer a.deinit();
1407 var b = try Managed.initSet(testing.allocator, 3458);
1408 defer b.deinit();
1409 var r = try Managed.init(testing.allocator);
1410 defer r.deinit();
1411
1412 try r.gcd(a, b);
1413
1414 testing.expect((try r.to(u32)) == 38);
1415}
1416
1417test "big.int gcd non-one large" {
1418 var a = try Managed.initSet(testing.allocator, 0xffffffffffffffff);
1419 defer a.deinit();
1420 var b = try Managed.initSet(testing.allocator, 0xffffffffffffffff7777);
1421 defer b.deinit();
1422 var r = try Managed.init(testing.allocator);
1423 defer r.deinit();
1424
1425 try r.gcd(a, b);
1426
1427 testing.expect((try r.to(u32)) == 4369);
1428}
1429
1430test "big.int gcd large multi-limb result" {
1431 var a = try Managed.initSet(testing.allocator, 0x12345678123456781234567812345678123456781234567812345678);
1432 defer a.deinit();
1433 var b = try Managed.initSet(testing.allocator, 0x12345671234567123456712345671234567123456712345671234567);
1434 defer b.deinit();
1435 var r = try Managed.init(testing.allocator);
1436 defer r.deinit();
1437
1438 try r.gcd(a, b);
1439
1440 const answer = (try r.to(u256));
1441 testing.expect(answer == 0xf000000ff00000fff0000ffff000fffff00ffffff1);
1442}
1443
1444test "big.int gcd one large" {
1445 var a = try Managed.initSet(testing.allocator, 1897056385327307);
1446 defer a.deinit();
1447 var b = try Managed.initSet(testing.allocator, 2251799813685248);
1448 defer b.deinit();
1449 var r = try Managed.init(testing.allocator);
1450 defer r.deinit();
1451
1452 try r.gcd(a, b);
1453
1454 testing.expect((try r.to(u64)) == 1);
1455}
lib/std/math/big/rational.zig+60-57
......@@ -5,10 +5,10 @@ const mem = std.mem;
55const testing = std.testing;
66const Allocator = mem.Allocator;
77
8const bn = @import("int.zig");
9const Limb = bn.Limb;
10const DoubleLimb = bn.DoubleLimb;
11const Int = bn.Int;
8const Limb = std.math.big.Limb;
9const DoubleLimb = std.math.big.DoubleLimb;
10const Int = std.math.big.int.Managed;
11const IntConst = std.math.big.int.Const;
1212
1313/// An arbitrary-precision rational number.
1414///
......@@ -17,6 +17,9 @@ const Int = bn.Int;
1717///
1818/// Rational's are always normalized. That is, for a Rational r = p/q where p and q are integers,
1919/// gcd(p, q) = 1 always.
20///
21/// TODO rework this to store its own allocator and use a non-managed big int, to avoid double
22/// allocator storage.
2023pub const Rational = struct {
2124 /// Numerator. Determines the sign of the Rational.
2225 p: Int,
......@@ -98,20 +101,20 @@ pub const Rational = struct {
98101 if (point) |i| {
99102 try self.p.setString(10, str[0..i]);
100103
101 const base = Int.initFixed(([_]Limb{10})[0..]);
104 const base = IntConst{ .limbs = &[_]Limb{10}, .positive = true };
102105
103106 var j: usize = start;
104107 while (j < str.len - i - 1) : (j += 1) {
105 try self.p.mul(self.p, base);
108 try self.p.mul(self.p.toConst(), base);
106109 }
107110
108111 try self.q.setString(10, str[i + 1 ..]);
109 try self.p.add(self.p, self.q);
112 try self.p.add(self.p.toConst(), self.q.toConst());
110113
111114 try self.q.set(1);
112115 var k: usize = i + 1;
113116 while (k < str.len) : (k += 1) {
114 try self.q.mul(self.q, base);
117 try self.q.mul(self.q.toConst(), base);
115118 }
116119
117120 try self.reduce();
......@@ -218,14 +221,14 @@ pub const Rational = struct {
218221 }
219222
220223 // 2. compute quotient and remainder
221 var q = try Int.init(self.p.allocator.?);
224 var q = try Int.init(self.p.allocator);
222225 defer q.deinit();
223226
224227 // unused
225 var r = try Int.init(self.p.allocator.?);
228 var r = try Int.init(self.p.allocator);
226229 defer r.deinit();
227230
228 try Int.divTrunc(&q, &r, a2, b2);
231 try Int.divTrunc(&q, &r, a2.toConst(), b2.toConst());
229232
230233 var mantissa = extractLowBits(q, BitReprType);
231234 var have_rem = r.len() > 0;
......@@ -293,14 +296,14 @@ pub const Rational = struct {
293296
294297 /// Set a Rational directly from an Int.
295298 pub fn copyInt(self: *Rational, a: Int) !void {
296 try self.p.copy(a);
299 try self.p.copy(a.toConst());
297300 try self.q.set(1);
298301 }
299302
300303 /// Set a Rational directly from a ratio of two Int's.
301304 pub fn copyRatio(self: *Rational, a: Int, b: Int) !void {
302 try self.p.copy(a);
303 try self.q.copy(b);
305 try self.p.copy(a.toConst());
306 try self.q.copy(b.toConst());
304307
305308 self.p.setSign(@boolToInt(self.p.isPositive()) ^ @boolToInt(self.q.isPositive()) == 0);
306309 self.q.setSign(true);
......@@ -327,13 +330,13 @@ pub const Rational = struct {
327330
328331 /// Returns math.Order.lt, math.Order.eq, math.Order.gt if a < b, a == b or a
329332 /// > b respectively.
330 pub fn cmp(a: Rational, b: Rational) !math.Order {
333 pub fn order(a: Rational, b: Rational) !math.Order {
331334 return cmpInternal(a, b, true);
332335 }
333336
334337 /// Returns math.Order.lt, math.Order.eq, math.Order.gt if |a| < |b|, |a| ==
335338 /// |b| or |a| > |b| respectively.
336 pub fn cmpAbs(a: Rational, b: Rational) !math.Order {
339 pub fn orderAbs(a: Rational, b: Rational) !math.Order {
337340 return cmpInternal(a, b, false);
338341 }
339342
......@@ -341,16 +344,16 @@ pub const Rational = struct {
341344 fn cmpInternal(a: Rational, b: Rational, is_abs: bool) !math.Order {
342345 // TODO: Would a div compare algorithm of sorts be viable and quicker? Can we avoid
343346 // the memory allocations here?
344 var q = try Int.init(a.p.allocator.?);
347 var q = try Int.init(a.p.allocator);
345348 defer q.deinit();
346349
347 var p = try Int.init(b.p.allocator.?);
350 var p = try Int.init(b.p.allocator);
348351 defer p.deinit();
349352
350 try q.mul(a.p, b.q);
351 try p.mul(b.p, a.q);
353 try q.mul(a.p.toConst(), b.q.toConst());
354 try p.mul(b.p.toConst(), a.q.toConst());
352355
353 return if (is_abs) q.cmpAbs(p) else q.cmp(p);
356 return if (is_abs) q.orderAbs(p) else q.order(p);
354357 }
355358
356359 /// rma = a + b.
......@@ -364,7 +367,7 @@ pub const Rational = struct {
364367
365368 var sr: Rational = undefined;
366369 if (aliased) {
367 sr = try Rational.init(rma.p.allocator.?);
370 sr = try Rational.init(rma.p.allocator);
368371 r = &sr;
369372 aliased = true;
370373 }
......@@ -373,11 +376,11 @@ pub const Rational = struct {
373376 r.deinit();
374377 };
375378
376 try r.p.mul(a.p, b.q);
377 try r.q.mul(b.p, a.q);
378 try r.p.add(r.p, r.q);
379 try r.p.mul(a.p.toConst(), b.q.toConst());
380 try r.q.mul(b.p.toConst(), a.q.toConst());
381 try r.p.add(r.p.toConst(), r.q.toConst());
379382
380 try r.q.mul(a.q, b.q);
383 try r.q.mul(a.q.toConst(), b.q.toConst());
381384 try r.reduce();
382385 }
383386
......@@ -392,7 +395,7 @@ pub const Rational = struct {
392395
393396 var sr: Rational = undefined;
394397 if (aliased) {
395 sr = try Rational.init(rma.p.allocator.?);
398 sr = try Rational.init(rma.p.allocator);
396399 r = &sr;
397400 aliased = true;
398401 }
......@@ -401,11 +404,11 @@ pub const Rational = struct {
401404 r.deinit();
402405 };
403406
404 try r.p.mul(a.p, b.q);
405 try r.q.mul(b.p, a.q);
406 try r.p.sub(r.p, r.q);
407 try r.p.mul(a.p.toConst(), b.q.toConst());
408 try r.q.mul(b.p.toConst(), a.q.toConst());
409 try r.p.sub(r.p.toConst(), r.q.toConst());
407410
408 try r.q.mul(a.q, b.q);
411 try r.q.mul(a.q.toConst(), b.q.toConst());
409412 try r.reduce();
410413 }
411414
......@@ -415,8 +418,8 @@ pub const Rational = struct {
415418 ///
416419 /// Returns an error if memory could not be allocated.
417420 pub fn mul(r: *Rational, a: Rational, b: Rational) !void {
418 try r.p.mul(a.p, b.p);
419 try r.q.mul(a.q, b.q);
421 try r.p.mul(a.p.toConst(), b.p.toConst());
422 try r.q.mul(a.q.toConst(), b.q.toConst());
420423 try r.reduce();
421424 }
422425
......@@ -430,8 +433,8 @@ pub const Rational = struct {
430433 @panic("division by zero");
431434 }
432435
433 try r.p.mul(a.p, b.q);
434 try r.q.mul(b.p, a.q);
436 try r.p.mul(a.p.toConst(), b.q.toConst());
437 try r.q.mul(b.p.toConst(), a.q.toConst());
435438 try r.reduce();
436439 }
437440
......@@ -442,7 +445,7 @@ pub const Rational = struct {
442445
443446 // reduce r/q such that gcd(r, q) = 1
444447 fn reduce(r: *Rational) !void {
445 var a = try Int.init(r.p.allocator.?);
448 var a = try Int.init(r.p.allocator);
446449 defer a.deinit();
447450
448451 const sign = r.p.isPositive();
......@@ -450,15 +453,15 @@ pub const Rational = struct {
450453 try a.gcd(r.p, r.q);
451454 r.p.setSign(sign);
452455
453 const one = Int.initFixed(([_]Limb{1})[0..]);
454 if (a.cmp(one) != .eq) {
455 var unused = try Int.init(r.p.allocator.?);
456 const one = IntConst{ .limbs = &[_]Limb{1}, .positive = true };
457 if (a.toConst().order(one) != .eq) {
458 var unused = try Int.init(r.p.allocator);
456459 defer unused.deinit();
457460
458461 // TODO: divexact would be useful here
459462 // TODO: don't copy r.q for div
460 try Int.divTrunc(&r.p, &unused, r.p, a);
461 try Int.divTrunc(&r.q, &unused, r.q, a);
463 try Int.divTrunc(&r.p, &unused, r.p.toConst(), a.toConst());
464 try Int.divTrunc(&r.q, &unused, r.q.toConst(), a.toConst());
462465 }
463466 }
464467};
......@@ -596,25 +599,25 @@ test "big.rational copy" {
596599 var a = try Rational.init(testing.allocator);
597600 defer a.deinit();
598601
599 const b = try Int.initSet(testing.allocator, 5);
602 var b = try Int.initSet(testing.allocator, 5);
600603 defer b.deinit();
601604
602605 try a.copyInt(b);
603606 testing.expect((try a.p.to(u32)) == 5);
604607 testing.expect((try a.q.to(u32)) == 1);
605608
606 const c = try Int.initSet(testing.allocator, 7);
609 var c = try Int.initSet(testing.allocator, 7);
607610 defer c.deinit();
608 const d = try Int.initSet(testing.allocator, 3);
611 var d = try Int.initSet(testing.allocator, 3);
609612 defer d.deinit();
610613
611614 try a.copyRatio(c, d);
612615 testing.expect((try a.p.to(u32)) == 7);
613616 testing.expect((try a.q.to(u32)) == 3);
614617
615 const e = try Int.initSet(testing.allocator, 9);
618 var e = try Int.initSet(testing.allocator, 9);
616619 defer e.deinit();
617 const f = try Int.initSet(testing.allocator, 3);
620 var f = try Int.initSet(testing.allocator, 3);
618621 defer f.deinit();
619622
620623 try a.copyRatio(e, f);
......@@ -680,7 +683,7 @@ test "big.rational swap" {
680683 testing.expect((try b.q.to(u32)) == 23);
681684}
682685
683test "big.rational cmp" {
686test "big.rational order" {
684687 var a = try Rational.init(testing.allocator);
685688 defer a.deinit();
686689 var b = try Rational.init(testing.allocator);
......@@ -688,11 +691,11 @@ test "big.rational cmp" {
688691
689692 try a.setRatio(500, 231);
690693 try b.setRatio(18903, 8584);
691 testing.expect((try a.cmp(b)) == .lt);
694 testing.expect((try a.order(b)) == .lt);
692695
693696 try a.setRatio(890, 10);
694697 try b.setRatio(89, 1);
695 testing.expect((try a.cmp(b)) == .eq);
698 testing.expect((try a.order(b)) == .eq);
696699}
697700
698701test "big.rational add single-limb" {
......@@ -703,11 +706,11 @@ test "big.rational add single-limb" {
703706
704707 try a.setRatio(500, 231);
705708 try b.setRatio(18903, 8584);
706 testing.expect((try a.cmp(b)) == .lt);
709 testing.expect((try a.order(b)) == .lt);
707710
708711 try a.setRatio(890, 10);
709712 try b.setRatio(89, 1);
710 testing.expect((try a.cmp(b)) == .eq);
713 testing.expect((try a.order(b)) == .eq);
711714}
712715
713716test "big.rational add" {
......@@ -723,7 +726,7 @@ test "big.rational add" {
723726 try a.add(a, b);
724727
725728 try r.setRatio(984786924199, 290395044174);
726 testing.expect((try a.cmp(r)) == .eq);
729 testing.expect((try a.order(r)) == .eq);
727730}
728731
729732test "big.rational sub" {
......@@ -739,7 +742,7 @@ test "big.rational sub" {
739742 try a.sub(a, b);
740743
741744 try r.setRatio(979040510045, 290395044174);
742 testing.expect((try a.cmp(r)) == .eq);
745 testing.expect((try a.order(r)) == .eq);
743746}
744747
745748test "big.rational mul" {
......@@ -755,7 +758,7 @@ test "big.rational mul" {
755758 try a.mul(a, b);
756759
757760 try r.setRatio(571481443, 17082061422);
758 testing.expect((try a.cmp(r)) == .eq);
761 testing.expect((try a.order(r)) == .eq);
759762}
760763
761764test "big.rational div" {
......@@ -771,7 +774,7 @@ test "big.rational div" {
771774 try a.div(a, b);
772775
773776 try r.setRatio(75531824394, 221015929);
774 testing.expect((try a.cmp(r)) == .eq);
777 testing.expect((try a.order(r)) == .eq);
775778}
776779
777780test "big.rational div" {
......@@ -784,11 +787,11 @@ test "big.rational div" {
784787 a.invert();
785788
786789 try r.setRatio(23341, 78923);
787 testing.expect((try a.cmp(r)) == .eq);
790 testing.expect((try a.order(r)) == .eq);
788791
789792 try a.setRatio(-78923, 23341);
790793 a.invert();
791794
792795 try r.setRatio(-23341, 78923);
793 testing.expect((try a.cmp(r)) == .eq);
796 testing.expect((try a.order(r)) == .eq);
794797}
lib/std/target.zig+1
......@@ -404,6 +404,7 @@ pub const Target = struct {
404404 };
405405
406406 pub const ObjectFormat = enum {
407 /// TODO Get rid of this one.
407408 unknown,
408409 coff,
409410 elf,
lib/std/testing.zig+39-1
......@@ -12,7 +12,7 @@ pub const failing_allocator = &failing_allocator_instance.allocator;
1212pub var failing_allocator_instance = FailingAllocator.init(&base_allocator_instance.allocator, 0);
1313
1414pub var base_allocator_instance = std.heap.ThreadSafeFixedBufferAllocator.init(allocator_mem[0..]);
15var allocator_mem: [1024 * 1024]u8 = undefined;
15var allocator_mem: [2 * 1024 * 1024]u8 = undefined;
1616
1717/// This function is intended to be used only in tests. It prints diagnostics to stderr
1818/// and then aborts when actual_error_union is not expected_error.
......@@ -193,6 +193,44 @@ pub fn expect(ok: bool) void {
193193 if (!ok) @panic("test failure");
194194}
195195
196pub const TmpDir = struct {
197 dir: std.fs.Dir,
198 parent_dir: std.fs.Dir,
199 sub_path: [sub_path_len]u8,
200
201 const random_bytes_count = 12;
202 const sub_path_len = std.base64.Base64Encoder.calcSize(random_bytes_count);
203
204 pub fn cleanup(self: *TmpDir) void {
205 self.dir.close();
206 self.parent_dir.deleteTree(&self.sub_path) catch {};
207 self.parent_dir.close();
208 self.* = undefined;
209 }
210};
211
212pub fn tmpDir(opts: std.fs.Dir.OpenDirOptions) TmpDir {
213 var random_bytes: [TmpDir.random_bytes_count]u8 = undefined;
214 std.crypto.randomBytes(&random_bytes) catch
215 @panic("unable to make tmp dir for testing: unable to get random bytes");
216 var sub_path: [TmpDir.sub_path_len]u8 = undefined;
217 std.fs.base64_encoder.encode(&sub_path, &random_bytes);
218
219 var cache_dir = std.fs.cwd().makeOpenPath("zig-cache", .{}) catch
220 @panic("unable to make tmp dir for testing: unable to make and open zig-cache dir");
221 defer cache_dir.close();
222 var parent_dir = cache_dir.makeOpenPath("tmp", .{}) catch
223 @panic("unable to make tmp dir for testing: unable to make and open zig-cache/tmp dir");
224 var dir = parent_dir.makeOpenPath(&sub_path, opts) catch
225 @panic("unable to make tmp dir for testing: unable to make and open the tmp dir");
226
227 return .{
228 .dir = dir,
229 .parent_dir = parent_dir,
230 .sub_path = sub_path,
231 };
232}
233
196234test "expectEqual nested array" {
197235 const a = [2][2]f32{
198236 [_]f32{ 1.0, 0.0 },
lib/std/zig.zig+17
......@@ -9,6 +9,23 @@ pub const ast = @import("zig/ast.zig");
99pub const system = @import("zig/system.zig");
1010pub const CrossTarget = @import("zig/cross_target.zig").CrossTarget;
1111
12pub fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usize, column: usize } {
13 var line: usize = 0;
14 var column: usize = 0;
15 for (source[0..byte_offset]) |byte| {
16 switch (byte) {
17 '\n' => {
18 line += 1;
19 column = 0;
20 },
21 else => {
22 column += 1;
23 },
24 }
25 }
26 return .{ .line = line, .column = column };
27}
28
1229test "" {
1330 @import("std").meta.refAllDecls(@This());
1431}
lib/std/zig/system.zig+6-1
......@@ -415,7 +415,12 @@ pub const NativeTargetInfo = struct {
415415 // over our own shared objects and find a dynamic linker.
416416 self_exe: {
417417 const lib_paths = try std.process.getSelfExeSharedLibPaths(allocator);
418 defer allocator.free(lib_paths);
418 defer {
419 for (lib_paths) |lib_path| {
420 allocator.free(lib_path);
421 }
422 allocator.free(lib_paths);
423 }
419424
420425 var found_ld_info: LdInfo = undefined;
421426 var found_ld_path: [:0]const u8 = undefined;
src-self-hosted/codegen.zig+47-8
......@@ -39,7 +39,7 @@ pub fn generateSymbol(typed_value: ir.TypedValue, module: ir.Module, code: *std.
3939 defer function.inst_table.deinit();
4040 defer function.errors.deinit();
4141
42 for (module_fn.body) |inst| {
42 for (module_fn.body.instructions) |inst| {
4343 const new_inst = function.genFuncInst(inst) catch |err| switch (err) {
4444 error.CodegenFail => {
4545 assert(function.errors.items.len != 0);
......@@ -77,32 +77,63 @@ const Function = struct {
7777
7878 fn genFuncInst(self: *Function, inst: *ir.Inst) !MCValue {
7979 switch (inst.tag) {
80 .unreach => return self.genPanic(inst.src),
80 .breakpoint => return self.genBreakpoint(inst.src),
81 .unreach => return MCValue{ .unreach = {} },
8182 .constant => unreachable, // excluded from function bodies
8283 .assembly => return self.genAsm(inst.cast(ir.Inst.Assembly).?),
8384 .ptrtoint => return self.genPtrToInt(inst.cast(ir.Inst.PtrToInt).?),
8485 .bitcast => return self.genBitCast(inst.cast(ir.Inst.BitCast).?),
86 .ret => return self.genRet(inst.cast(ir.Inst.Ret).?),
87 .cmp => return self.genCmp(inst.cast(ir.Inst.Cmp).?),
88 .condbr => return self.genCondBr(inst.cast(ir.Inst.CondBr).?),
89 .isnull => return self.genIsNull(inst.cast(ir.Inst.IsNull).?),
90 .isnonnull => return self.genIsNonNull(inst.cast(ir.Inst.IsNonNull).?),
8591 }
8692 }
8793
88 fn genPanic(self: *Function, src: usize) !MCValue {
89 // TODO change this to call the panic function
94 fn genBreakpoint(self: *Function, src: usize) !MCValue {
9095 switch (self.module.target.cpu.arch) {
9196 .i386, .x86_64 => {
9297 try self.code.append(0xcc); // int3
9398 },
94 else => return self.fail(src, "TODO implement panic for {}", .{self.module.target.cpu.arch}),
99 else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.module.target.cpu.arch}),
95100 }
96101 return .unreach;
97102 }
98103
99 fn genRet(self: *Function, src: usize) !void {
100 // TODO change this to call the panic function
104 fn genRet(self: *Function, inst: *ir.Inst.Ret) !MCValue {
101105 switch (self.module.target.cpu.arch) {
102106 .i386, .x86_64 => {
103107 try self.code.append(0xc3); // ret
104108 },
105 else => return self.fail(src, "TODO implement ret for {}", .{self.module.target.cpu.arch}),
109 else => return self.fail(inst.base.src, "TODO implement return for {}", .{self.module.target.cpu.arch}),
110 }
111 return .unreach;
112 }
113
114 fn genCmp(self: *Function, inst: *ir.Inst.Cmp) !MCValue {
115 switch (self.module.target.cpu.arch) {
116 else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.module.target.cpu.arch}),
117 }
118 }
119
120 fn genCondBr(self: *Function, inst: *ir.Inst.CondBr) !MCValue {
121 switch (self.module.target.cpu.arch) {
122 else => return self.fail(inst.base.src, "TODO implement condbr for {}", .{self.module.target.cpu.arch}),
123 }
124 }
125
126 fn genIsNull(self: *Function, inst: *ir.Inst.IsNull) !MCValue {
127 switch (self.module.target.cpu.arch) {
128 else => return self.fail(inst.base.src, "TODO implement isnull for {}", .{self.module.target.cpu.arch}),
129 }
130 }
131
132 fn genIsNonNull(self: *Function, inst: *ir.Inst.IsNonNull) !MCValue {
133 // Here you can specialize this instruction if it makes sense to, otherwise the default
134 // will call genIsNull and invert the result.
135 switch (self.module.target.cpu.arch) {
136 else => return self.fail(inst.base.src, "TODO call genIsNull and invert the result ", .{}),
106137 }
107138 }
108139
......@@ -501,11 +532,19 @@ fn Reg(comptime arch: Target.Cpu.Arch) type {
501532 bh,
502533 ch,
503534 dh,
535 bph,
536 sph,
537 sih,
538 dih,
504539
505540 al,
506541 bl,
507542 cl,
508543 dl,
544 bpl,
545 spl,
546 sil,
547 dil,
509548 r8b,
510549 r9b,
511550 r10b,
src-self-hosted/ir.zig+665-174
......@@ -4,10 +4,12 @@ const Allocator = std.mem.Allocator;
44const Value = @import("value.zig").Value;
55const Type = @import("type.zig").Type;
66const assert = std.debug.assert;
7const text = @import("ir/text.zig");
8const BigInt = std.math.big.Int;
7const BigIntConst = std.math.big.int.Const;
8const BigIntMutable = std.math.big.int.Mutable;
99const Target = std.Target;
1010
11pub const text = @import("ir/text.zig");
12
1113/// These are in-memory, analyzed instructions. See `text.Inst` for the representation
1214/// of instructions that correspond to the ZIR text format.
1315/// This struct owns the `Value` and `Type` memory. When the struct is deallocated,
......@@ -20,11 +22,17 @@ pub const Inst = struct {
2022 src: usize,
2123
2224 pub const Tag = enum {
23 unreach,
24 constant,
2525 assembly,
26 ptrtoint,
2726 bitcast,
27 breakpoint,
28 cmp,
29 condbr,
30 constant,
31 isnonnull,
32 isnull,
33 ptrtoint,
34 ret,
35 unreach,
2836 };
2937
3038 pub fn cast(base: *Inst, comptime T: type) ?*T {
......@@ -40,23 +48,64 @@ pub const Inst = struct {
4048
4149 /// Returns `null` if runtime-known.
4250 pub fn value(base: *Inst) ?Value {
43 return switch (base.tag) {
44 .unreach => Value.initTag(.noreturn_value),
45 .constant => base.cast(Constant).?.val,
46
47 .assembly,
48 .ptrtoint,
49 .bitcast,
50 => null,
51 };
51 if (base.ty.onePossibleValue())
52 return Value.initTag(.the_one_possible_value);
53
54 const inst = base.cast(Constant) orelse return null;
55 return inst.val;
5256 }
5357
54 pub const Unreach = struct {
55 pub const base_tag = Tag.unreach;
58 pub const Assembly = struct {
59 pub const base_tag = Tag.assembly;
60 base: Inst,
61
62 args: struct {
63 asm_source: []const u8,
64 is_volatile: bool,
65 output: ?[]const u8,
66 inputs: []const []const u8,
67 clobbers: []const []const u8,
68 args: []const *Inst,
69 },
70 };
71
72 pub const BitCast = struct {
73 pub const base_tag = Tag.bitcast;
74
75 base: Inst,
76 args: struct {
77 operand: *Inst,
78 },
79 };
80
81 pub const Breakpoint = struct {
82 pub const base_tag = Tag.breakpoint;
5683 base: Inst,
5784 args: void,
5885 };
5986
87 pub const Cmp = struct {
88 pub const base_tag = Tag.cmp;
89
90 base: Inst,
91 args: struct {
92 lhs: *Inst,
93 op: std.math.CompareOperator,
94 rhs: *Inst,
95 },
96 };
97
98 pub const CondBr = struct {
99 pub const base_tag = Tag.condbr;
100
101 base: Inst,
102 args: struct {
103 condition: *Inst,
104 true_body: Module.Body,
105 false_body: Module.Body,
106 },
107 };
108
60109 pub const Constant = struct {
61110 pub const base_tag = Tag.constant;
62111 base: Inst,
......@@ -64,17 +113,21 @@ pub const Inst = struct {
64113 val: Value,
65114 };
66115
67 pub const Assembly = struct {
68 pub const base_tag = Tag.assembly;
116 pub const IsNonNull = struct {
117 pub const base_tag = Tag.isnonnull;
118
69119 base: Inst,
120 args: struct {
121 operand: *Inst,
122 },
123 };
70124
125 pub const IsNull = struct {
126 pub const base_tag = Tag.isnull;
127
128 base: Inst,
71129 args: struct {
72 asm_source: []const u8,
73 is_volatile: bool,
74 output: ?[]const u8,
75 inputs: []const []const u8,
76 clobbers: []const []const u8,
77 args: []const *Inst,
130 operand: *Inst,
78131 },
79132 };
80133
......@@ -87,13 +140,16 @@ pub const Inst = struct {
87140 },
88141 };
89142
90 pub const BitCast = struct {
91 pub const base_tag = Tag.bitcast;
143 pub const Ret = struct {
144 pub const base_tag = Tag.ret;
145 base: Inst,
146 args: void,
147 };
92148
149 pub const Unreach = struct {
150 pub const base_tag = Tag.unreach;
93151 base: Inst,
94 args: struct {
95 operand: *Inst,
96 },
152 args: void,
97153 };
98154};
99155
......@@ -108,6 +164,10 @@ pub const Module = struct {
108164 arena: std.heap.ArenaAllocator,
109165 fns: []Fn,
110166 target: Target,
167 link_mode: std.builtin.LinkMode,
168 output_mode: std.builtin.OutputMode,
169 object_format: std.Target.ObjectFormat,
170 optimize_mode: std.builtin.Mode,
111171
112172 pub const Export = struct {
113173 name: []const u8,
......@@ -117,13 +177,21 @@ pub const Module = struct {
117177
118178 pub const Fn = struct {
119179 analysis_status: enum { in_progress, failure, success },
120 body: []*Inst,
180 body: Body,
121181 fn_type: Type,
122182 };
123183
184 pub const Body = struct {
185 instructions: []*Inst,
186 };
187
124188 pub fn deinit(self: *Module, allocator: *Allocator) void {
125189 allocator.free(self.exports);
126190 allocator.free(self.errors);
191 for (self.fns) |f| {
192 allocator.free(f.body.instructions);
193 }
194 allocator.free(self.fns);
127195 self.arena.deinit();
128196 self.* = undefined;
129197 }
......@@ -134,7 +202,15 @@ pub const ErrorMsg = struct {
134202 msg: []const u8,
135203};
136204
137pub fn analyze(allocator: *Allocator, old_module: text.Module, target: Target) !Module {
205pub const AnalyzeOptions = struct {
206 target: Target,
207 output_mode: std.builtin.OutputMode,
208 link_mode: std.builtin.LinkMode,
209 object_format: ?std.Target.ObjectFormat = null,
210 optimize_mode: std.builtin.Mode,
211};
212
213pub fn analyze(allocator: *Allocator, old_module: text.Module, options: AnalyzeOptions) !Module {
138214 var ctx = Analyze{
139215 .allocator = allocator,
140216 .arena = std.heap.ArenaAllocator.init(allocator),
......@@ -143,7 +219,10 @@ pub fn analyze(allocator: *Allocator, old_module: text.Module, target: Target) !
143219 .decl_table = std.AutoHashMap(*text.Inst, Analyze.NewDecl).init(allocator),
144220 .exports = std.ArrayList(Module.Export).init(allocator),
145221 .fns = std.ArrayList(Module.Fn).init(allocator),
146 .target = target,
222 .target = options.target,
223 .optimize_mode = options.optimize_mode,
224 .link_mode = options.link_mode,
225 .output_mode = options.output_mode,
147226 };
148227 defer ctx.errors.deinit();
149228 defer ctx.decl_table.deinit();
......@@ -162,7 +241,11 @@ pub fn analyze(allocator: *Allocator, old_module: text.Module, target: Target) !
162241 .errors = ctx.errors.toOwnedSlice(),
163242 .fns = ctx.fns.toOwnedSlice(),
164243 .arena = ctx.arena,
165 .target = target,
244 .target = ctx.target,
245 .link_mode = ctx.link_mode,
246 .output_mode = ctx.output_mode,
247 .object_format = options.object_format orelse ctx.target.getObjectFormat(),
248 .optimize_mode = ctx.optimize_mode,
166249 };
167250}
168251
......@@ -175,6 +258,9 @@ const Analyze = struct {
175258 exports: std.ArrayList(Module.Export),
176259 fns: std.ArrayList(Module.Fn),
177260 target: Target,
261 link_mode: std.builtin.LinkMode,
262 optimize_mode: std.builtin.Mode,
263 output_mode: std.builtin.OutputMode,
178264
179265 const NewDecl = struct {
180266 /// null means a semantic analysis error happened
......@@ -187,10 +273,15 @@ const Analyze = struct {
187273 };
188274
189275 const Fn = struct {
190 body: std.ArrayList(*Inst),
191 inst_table: std.AutoHashMap(*text.Inst, NewInst),
192276 /// Index into Module fns array
193277 fn_index: usize,
278 inner_block: Block,
279 inst_table: std.AutoHashMap(*text.Inst, NewInst),
280 };
281
282 const Block = struct {
283 func: *Fn,
284 instructions: std.ArrayList(*Inst),
194285 };
195286
196287 const InnerError = error{ OutOfMemory, AnalysisFail };
......@@ -203,9 +294,9 @@ const Analyze = struct {
203294 }
204295 }
205296
206 fn resolveInst(self: *Analyze, opt_func: ?*Fn, old_inst: *text.Inst) InnerError!*Inst {
207 if (opt_func) |func| {
208 if (func.inst_table.get(old_inst)) |kv| {
297 fn resolveInst(self: *Analyze, opt_block: ?*Block, old_inst: *text.Inst) InnerError!*Inst {
298 if (opt_block) |block| {
299 if (block.func.inst_table.get(old_inst)) |kv| {
209300 return kv.value.ptr orelse return error.AnalysisFail;
210301 }
211302 }
......@@ -225,12 +316,12 @@ const Analyze = struct {
225316 }
226317 }
227318
228 fn requireFunctionBody(self: *Analyze, func: ?*Fn, src: usize) !*Fn {
229 return func orelse return self.fail(src, "instruction illegal outside function body", .{});
319 fn requireRuntimeBlock(self: *Analyze, block: ?*Block, src: usize) !*Block {
320 return block orelse return self.fail(src, "instruction illegal outside function body", .{});
230321 }
231322
232 fn resolveInstConst(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) InnerError!TypedValue {
233 const new_inst = try self.resolveInst(func, old_inst);
323 fn resolveInstConst(self: *Analyze, block: ?*Block, old_inst: *text.Inst) InnerError!TypedValue {
324 const new_inst = try self.resolveInst(block, old_inst);
234325 const val = try self.resolveConstValue(new_inst);
235326 return TypedValue{
236327 .ty = new_inst.ty,
......@@ -239,28 +330,39 @@ const Analyze = struct {
239330 }
240331
241332 fn resolveConstValue(self: *Analyze, base: *Inst) !Value {
242 return base.value() orelse return self.fail(base.src, "unable to resolve comptime value", .{});
333 return (try self.resolveDefinedValue(base)) orelse
334 return self.fail(base.src, "unable to resolve comptime value", .{});
243335 }
244336
245 fn resolveConstString(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) ![]u8 {
246 const new_inst = try self.resolveInst(func, old_inst);
337 fn resolveDefinedValue(self: *Analyze, base: *Inst) !?Value {
338 if (base.value()) |val| {
339 if (val.isUndef()) {
340 return self.fail(base.src, "use of undefined value here causes undefined behavior", .{});
341 }
342 return val;
343 }
344 return null;
345 }
346
347 fn resolveConstString(self: *Analyze, block: ?*Block, old_inst: *text.Inst) ![]u8 {
348 const new_inst = try self.resolveInst(block, old_inst);
247349 const wanted_type = Type.initTag(.const_slice_u8);
248 const coerced_inst = try self.coerce(func, wanted_type, new_inst);
350 const coerced_inst = try self.coerce(block, wanted_type, new_inst);
249351 const val = try self.resolveConstValue(coerced_inst);
250352 return val.toAllocatedBytes(&self.arena.allocator);
251353 }
252354
253 fn resolveType(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) !Type {
254 const new_inst = try self.resolveInst(func, old_inst);
355 fn resolveType(self: *Analyze, block: ?*Block, old_inst: *text.Inst) !Type {
356 const new_inst = try self.resolveInst(block, old_inst);
255357 const wanted_type = Type.initTag(.@"type");
256 const coerced_inst = try self.coerce(func, wanted_type, new_inst);
358 const coerced_inst = try self.coerce(block, wanted_type, new_inst);
257359 const val = try self.resolveConstValue(coerced_inst);
258360 return val.toType();
259361 }
260362
261 fn analyzeExport(self: *Analyze, func: ?*Fn, export_inst: *text.Inst.Export) !void {
262 const symbol_name = try self.resolveConstString(func, export_inst.positionals.symbol_name);
263 const typed_value = try self.resolveInstConst(func, export_inst.positionals.value);
363 fn analyzeExport(self: *Analyze, block: ?*Block, export_inst: *text.Inst.Export) !void {
364 const symbol_name = try self.resolveConstString(block, export_inst.positionals.symbol_name);
365 const typed_value = try self.resolveInstConst(block, export_inst.positionals.value);
264366
265367 switch (typed_value.ty.zigTypeTag()) {
266368 .Fn => {},
......@@ -280,18 +382,18 @@ const Analyze = struct {
280382 /// TODO should not need the cast on the last parameter at the callsites
281383 fn addNewInstArgs(
282384 self: *Analyze,
283 func: *Fn,
385 block: *Block,
284386 src: usize,
285387 ty: Type,
286388 comptime T: type,
287389 args: Inst.Args(T),
288390 ) !*Inst {
289 const inst = try self.addNewInst(func, src, ty, T);
391 const inst = try self.addNewInst(block, src, ty, T);
290392 inst.args = args;
291393 return &inst.base;
292394 }
293395
294 fn addNewInst(self: *Analyze, func: *Fn, src: usize, ty: Type, comptime T: type) !*T {
396 fn addNewInst(self: *Analyze, block: *Block, src: usize, ty: Type, comptime T: type) !*T {
295397 const inst = try self.arena.allocator.create(T);
296398 inst.* = .{
297399 .base = .{
......@@ -301,7 +403,7 @@ const Analyze = struct {
301403 },
302404 .args = undefined,
303405 };
304 try func.body.append(&inst.base);
406 try block.instructions.append(&inst.base);
305407 return inst;
306408 }
307409
......@@ -344,7 +446,21 @@ const Analyze = struct {
344446 fn constVoid(self: *Analyze, src: usize) !*Inst {
345447 return self.constInst(src, .{
346448 .ty = Type.initTag(.void),
347 .val = Value.initTag(.void_value),
449 .val = Value.initTag(.the_one_possible_value),
450 });
451 }
452
453 fn constUndef(self: *Analyze, src: usize, ty: Type) !*Inst {
454 return self.constInst(src, .{
455 .ty = ty,
456 .val = Value.initTag(.undef),
457 });
458 }
459
460 fn constBool(self: *Analyze, src: usize, v: bool) !*Inst {
461 return self.constInst(src, .{
462 .ty = Type.initTag(.bool),
463 .val = ([2]Value{ Value.initTag(.bool_false), Value.initTag(.bool_true) })[@boolToInt(v)],
348464 });
349465 }
350466
......@@ -368,34 +484,38 @@ const Analyze = struct {
368484 });
369485 }
370486
371 fn constIntBig(self: *Analyze, src: usize, ty: Type, big_int: BigInt) !*Inst {
372 if (big_int.isPositive()) {
487 fn constIntBig(self: *Analyze, src: usize, ty: Type, big_int: BigIntConst) !*Inst {
488 const val_payload = if (big_int.positive) blk: {
373489 if (big_int.to(u64)) |x| {
374490 return self.constIntUnsigned(src, ty, x);
375491 } else |err| switch (err) {
376492 error.NegativeIntoUnsigned => unreachable,
377493 error.TargetTooSmall => {}, // handled below
378494 }
379 } else {
495 const big_int_payload = try self.arena.allocator.create(Value.Payload.IntBigPositive);
496 big_int_payload.* = .{ .limbs = big_int.limbs };
497 break :blk &big_int_payload.base;
498 } else blk: {
380499 if (big_int.to(i64)) |x| {
381500 return self.constIntSigned(src, ty, x);
382501 } else |err| switch (err) {
383502 error.NegativeIntoUnsigned => unreachable,
384503 error.TargetTooSmall => {}, // handled below
385504 }
386 }
387
388 const big_int_payload = try self.arena.allocator.create(Value.Payload.IntBig);
389 big_int_payload.* = .{ .big_int = big_int };
505 const big_int_payload = try self.arena.allocator.create(Value.Payload.IntBigNegative);
506 big_int_payload.* = .{ .limbs = big_int.limbs };
507 break :blk &big_int_payload.base;
508 };
390509
391510 return self.constInst(src, .{
392511 .ty = ty,
393 .val = Value.initPayload(&big_int_payload.base),
512 .val = Value.initPayload(val_payload),
394513 });
395514 }
396515
397 fn analyzeInst(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) InnerError!*Inst {
516 fn analyzeInst(self: *Analyze, block: ?*Block, old_inst: *text.Inst) InnerError!*Inst {
398517 switch (old_inst.tag) {
518 .breakpoint => return self.analyzeInstBreakpoint(block, old_inst.cast(text.Inst.Breakpoint).?),
399519 .str => {
400520 // We can use this reference because Inst.Const's Value is arena-allocated.
401521 // The value would get copied to a MemoryCell before the `text.Inst.Str` lifetime ends.
......@@ -406,35 +526,49 @@ const Analyze = struct {
406526 const big_int = old_inst.cast(text.Inst.Int).?.positionals.int;
407527 return self.constIntBig(old_inst.src, Type.initTag(.comptime_int), big_int);
408528 },
409 .ptrtoint => return self.analyzeInstPtrToInt(func, old_inst.cast(text.Inst.PtrToInt).?),
410 .fieldptr => return self.analyzeInstFieldPtr(func, old_inst.cast(text.Inst.FieldPtr).?),
411 .deref => return self.analyzeInstDeref(func, old_inst.cast(text.Inst.Deref).?),
412 .as => return self.analyzeInstAs(func, old_inst.cast(text.Inst.As).?),
413 .@"asm" => return self.analyzeInstAsm(func, old_inst.cast(text.Inst.Asm).?),
414 .@"unreachable" => return self.analyzeInstUnreachable(func, old_inst.cast(text.Inst.Unreachable).?),
415 .@"fn" => return self.analyzeInstFn(func, old_inst.cast(text.Inst.Fn).?),
529 .ptrtoint => return self.analyzeInstPtrToInt(block, old_inst.cast(text.Inst.PtrToInt).?),
530 .fieldptr => return self.analyzeInstFieldPtr(block, old_inst.cast(text.Inst.FieldPtr).?),
531 .deref => return self.analyzeInstDeref(block, old_inst.cast(text.Inst.Deref).?),
532 .as => return self.analyzeInstAs(block, old_inst.cast(text.Inst.As).?),
533 .@"asm" => return self.analyzeInstAsm(block, old_inst.cast(text.Inst.Asm).?),
534 .@"unreachable" => return self.analyzeInstUnreachable(block, old_inst.cast(text.Inst.Unreachable).?),
535 .@"return" => return self.analyzeInstRet(block, old_inst.cast(text.Inst.Return).?),
536 .@"fn" => return self.analyzeInstFn(block, old_inst.cast(text.Inst.Fn).?),
416537 .@"export" => {
417 try self.analyzeExport(func, old_inst.cast(text.Inst.Export).?);
538 try self.analyzeExport(block, old_inst.cast(text.Inst.Export).?);
418539 return self.constVoid(old_inst.src);
419540 },
420 .primitive => return self.analyzeInstPrimitive(func, old_inst.cast(text.Inst.Primitive).?),
421 .fntype => return self.analyzeInstFnType(func, old_inst.cast(text.Inst.FnType).?),
422 .intcast => return self.analyzeInstIntCast(func, old_inst.cast(text.Inst.IntCast).?),
423 .bitcast => return self.analyzeInstBitCast(func, old_inst.cast(text.Inst.BitCast).?),
424 .elemptr => return self.analyzeInstElemPtr(func, old_inst.cast(text.Inst.ElemPtr).?),
425 .add => return self.analyzeInstAdd(func, old_inst.cast(text.Inst.Add).?),
541 .primitive => return self.analyzeInstPrimitive(old_inst.cast(text.Inst.Primitive).?),
542 .fntype => return self.analyzeInstFnType(block, old_inst.cast(text.Inst.FnType).?),
543 .intcast => return self.analyzeInstIntCast(block, old_inst.cast(text.Inst.IntCast).?),
544 .bitcast => return self.analyzeInstBitCast(block, old_inst.cast(text.Inst.BitCast).?),
545 .elemptr => return self.analyzeInstElemPtr(block, old_inst.cast(text.Inst.ElemPtr).?),
546 .add => return self.analyzeInstAdd(block, old_inst.cast(text.Inst.Add).?),
547 .cmp => return self.analyzeInstCmp(block, old_inst.cast(text.Inst.Cmp).?),
548 .condbr => return self.analyzeInstCondBr(block, old_inst.cast(text.Inst.CondBr).?),
549 .isnull => return self.analyzeInstIsNull(block, old_inst.cast(text.Inst.IsNull).?),
550 .isnonnull => return self.analyzeInstIsNonNull(block, old_inst.cast(text.Inst.IsNonNull).?),
426551 }
427552 }
428553
429 fn analyzeInstFn(self: *Analyze, opt_func: ?*Fn, fn_inst: *text.Inst.Fn) InnerError!*Inst {
430 const fn_type = try self.resolveType(opt_func, fn_inst.positionals.fn_type);
554 fn analyzeInstBreakpoint(self: *Analyze, block: ?*Block, inst: *text.Inst.Breakpoint) InnerError!*Inst {
555 const b = try self.requireRuntimeBlock(block, inst.base.src);
556 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Breakpoint, Inst.Args(Inst.Breakpoint){});
557 }
558
559 fn analyzeInstFn(self: *Analyze, block: ?*Block, fn_inst: *text.Inst.Fn) InnerError!*Inst {
560 const fn_type = try self.resolveType(block, fn_inst.positionals.fn_type);
431561
432562 var new_func: Fn = .{
433 .body = std.ArrayList(*Inst).init(self.allocator),
434 .inst_table = std.AutoHashMap(*text.Inst, NewInst).init(self.allocator),
435563 .fn_index = self.fns.items.len,
564 .inner_block = .{
565 .func = undefined,
566 .instructions = std.ArrayList(*Inst).init(self.allocator),
567 },
568 .inst_table = std.AutoHashMap(*text.Inst, NewInst).init(self.allocator),
436569 };
437 defer new_func.body.deinit();
570 new_func.inner_block.func = &new_func;
571 defer new_func.inner_block.instructions.deinit();
438572 defer new_func.inst_table.deinit();
439573 // Don't hang on to a reference to this when analyzing body instructions, since the memory
440574 // could become invalid.
......@@ -444,18 +578,11 @@ const Analyze = struct {
444578 .body = undefined,
445579 };
446580
447 for (fn_inst.positionals.body.instructions) |src_inst| {
448 const new_inst = self.analyzeInst(&new_func, src_inst) catch |err| {
449 self.fns.items[new_func.fn_index].analysis_status = .failure;
450 try new_func.inst_table.putNoClobber(src_inst, .{ .ptr = null });
451 return err;
452 };
453 try new_func.inst_table.putNoClobber(src_inst, .{ .ptr = new_inst });
454 }
581 try self.analyzeBody(&new_func.inner_block, fn_inst.positionals.body);
455582
456583 const f = &self.fns.items[new_func.fn_index];
457584 f.analysis_status = .success;
458 f.body = new_func.body.toOwnedSlice();
585 f.body = .{ .instructions = new_func.inner_block.instructions.toOwnedSlice() };
459586
460587 const fn_payload = try self.arena.allocator.create(Value.Payload.Function);
461588 fn_payload.* = .{ .index = new_func.fn_index };
......@@ -466,8 +593,8 @@ const Analyze = struct {
466593 });
467594 }
468595
469 fn analyzeInstFnType(self: *Analyze, func: ?*Fn, fntype: *text.Inst.FnType) InnerError!*Inst {
470 const return_type = try self.resolveType(func, fntype.positionals.return_type);
596 fn analyzeInstFnType(self: *Analyze, block: ?*Block, fntype: *text.Inst.FnType) InnerError!*Inst {
597 const return_type = try self.resolveType(block, fntype.positionals.return_type);
471598
472599 if (return_type.zigTypeTag() == .NoReturn and
473600 fntype.positionals.param_types.len == 0 and
......@@ -476,33 +603,40 @@ const Analyze = struct {
476603 return self.constType(fntype.base.src, Type.initTag(.fn_naked_noreturn_no_args));
477604 }
478605
606 if (return_type.zigTypeTag() == .Void and
607 fntype.positionals.param_types.len == 0 and
608 fntype.kw_args.cc == .C)
609 {
610 return self.constType(fntype.base.src, Type.initTag(.fn_ccc_void_no_args));
611 }
612
479613 return self.fail(fntype.base.src, "TODO implement fntype instruction more", .{});
480614 }
481615
482 fn analyzeInstPrimitive(self: *Analyze, func: ?*Fn, primitive: *text.Inst.Primitive) InnerError!*Inst {
616 fn analyzeInstPrimitive(self: *Analyze, primitive: *text.Inst.Primitive) InnerError!*Inst {
483617 return self.constType(primitive.base.src, primitive.positionals.tag.toType());
484618 }
485619
486 fn analyzeInstAs(self: *Analyze, func: ?*Fn, as: *text.Inst.As) InnerError!*Inst {
487 const dest_type = try self.resolveType(func, as.positionals.dest_type);
488 const new_inst = try self.resolveInst(func, as.positionals.value);
489 return self.coerce(func, dest_type, new_inst);
620 fn analyzeInstAs(self: *Analyze, block: ?*Block, as: *text.Inst.As) InnerError!*Inst {
621 const dest_type = try self.resolveType(block, as.positionals.dest_type);
622 const new_inst = try self.resolveInst(block, as.positionals.value);
623 return self.coerce(block, dest_type, new_inst);
490624 }
491625
492 fn analyzeInstPtrToInt(self: *Analyze, func: ?*Fn, ptrtoint: *text.Inst.PtrToInt) InnerError!*Inst {
493 const ptr = try self.resolveInst(func, ptrtoint.positionals.ptr);
626 fn analyzeInstPtrToInt(self: *Analyze, block: ?*Block, ptrtoint: *text.Inst.PtrToInt) InnerError!*Inst {
627 const ptr = try self.resolveInst(block, ptrtoint.positionals.ptr);
494628 if (ptr.ty.zigTypeTag() != .Pointer) {
495629 return self.fail(ptrtoint.positionals.ptr.src, "expected pointer, found '{}'", .{ptr.ty});
496630 }
497631 // TODO handle known-pointer-address
498 const f = try self.requireFunctionBody(func, ptrtoint.base.src);
632 const b = try self.requireRuntimeBlock(block, ptrtoint.base.src);
499633 const ty = Type.initTag(.usize);
500 return self.addNewInstArgs(f, ptrtoint.base.src, ty, Inst.PtrToInt, Inst.Args(Inst.PtrToInt){ .ptr = ptr });
634 return self.addNewInstArgs(b, ptrtoint.base.src, ty, Inst.PtrToInt, Inst.Args(Inst.PtrToInt){ .ptr = ptr });
501635 }
502636
503 fn analyzeInstFieldPtr(self: *Analyze, func: ?*Fn, fieldptr: *text.Inst.FieldPtr) InnerError!*Inst {
504 const object_ptr = try self.resolveInst(func, fieldptr.positionals.object_ptr);
505 const field_name = try self.resolveConstString(func, fieldptr.positionals.field_name);
637 fn analyzeInstFieldPtr(self: *Analyze, block: ?*Block, fieldptr: *text.Inst.FieldPtr) InnerError!*Inst {
638 const object_ptr = try self.resolveInst(block, fieldptr.positionals.object_ptr);
639 const field_name = try self.resolveConstString(block, fieldptr.positionals.field_name);
506640
507641 const elem_ty = switch (object_ptr.ty.zigTypeTag()) {
508642 .Pointer => object_ptr.ty.elemType(),
......@@ -533,9 +667,9 @@ const Analyze = struct {
533667 }
534668 }
535669
536 fn analyzeInstIntCast(self: *Analyze, func: ?*Fn, intcast: *text.Inst.IntCast) InnerError!*Inst {
537 const dest_type = try self.resolveType(func, intcast.positionals.dest_type);
538 const new_inst = try self.resolveInst(func, intcast.positionals.value);
670 fn analyzeInstIntCast(self: *Analyze, block: ?*Block, intcast: *text.Inst.IntCast) InnerError!*Inst {
671 const dest_type = try self.resolveType(block, intcast.positionals.dest_type);
672 const new_inst = try self.resolveInst(block, intcast.positionals.value);
539673
540674 const dest_is_comptime_int = switch (dest_type.zigTypeTag()) {
541675 .ComptimeInt => true,
......@@ -559,22 +693,22 @@ const Analyze = struct {
559693 }
560694
561695 if (dest_is_comptime_int or new_inst.value() != null) {
562 return self.coerce(func, dest_type, new_inst);
696 return self.coerce(block, dest_type, new_inst);
563697 }
564698
565699 return self.fail(intcast.base.src, "TODO implement analyze widen or shorten int", .{});
566700 }
567701
568 fn analyzeInstBitCast(self: *Analyze, func: ?*Fn, inst: *text.Inst.BitCast) InnerError!*Inst {
569 const dest_type = try self.resolveType(func, inst.positionals.dest_type);
570 const operand = try self.resolveInst(func, inst.positionals.operand);
571 return self.bitcast(func, dest_type, operand);
702 fn analyzeInstBitCast(self: *Analyze, block: ?*Block, inst: *text.Inst.BitCast) InnerError!*Inst {
703 const dest_type = try self.resolveType(block, inst.positionals.dest_type);
704 const operand = try self.resolveInst(block, inst.positionals.operand);
705 return self.bitcast(block, dest_type, operand);
572706 }
573707
574 fn analyzeInstElemPtr(self: *Analyze, func: ?*Fn, inst: *text.Inst.ElemPtr) InnerError!*Inst {
575 const array_ptr = try self.resolveInst(func, inst.positionals.array_ptr);
576 const uncasted_index = try self.resolveInst(func, inst.positionals.index);
577 const elem_index = try self.coerce(func, Type.initTag(.usize), uncasted_index);
708 fn analyzeInstElemPtr(self: *Analyze, block: ?*Block, inst: *text.Inst.ElemPtr) InnerError!*Inst {
709 const array_ptr = try self.resolveInst(block, inst.positionals.array_ptr);
710 const uncasted_index = try self.resolveInst(block, inst.positionals.index);
711 const elem_index = try self.coerce(block, Type.initTag(.usize), uncasted_index);
578712
579713 if (array_ptr.ty.isSinglePointer() and array_ptr.ty.elemType().zigTypeTag() == .Array) {
580714 if (array_ptr.value()) |array_ptr_val| {
......@@ -602,28 +736,44 @@ const Analyze = struct {
602736 return self.fail(inst.base.src, "TODO implement more analyze elemptr", .{});
603737 }
604738
605 fn analyzeInstAdd(self: *Analyze, func: ?*Fn, inst: *text.Inst.Add) InnerError!*Inst {
606 const lhs = try self.resolveInst(func, inst.positionals.lhs);
607 const rhs = try self.resolveInst(func, inst.positionals.rhs);
739 fn analyzeInstAdd(self: *Analyze, block: ?*Block, inst: *text.Inst.Add) InnerError!*Inst {
740 const lhs = try self.resolveInst(block, inst.positionals.lhs);
741 const rhs = try self.resolveInst(block, inst.positionals.rhs);
608742
609743 if (lhs.ty.zigTypeTag() == .Int and rhs.ty.zigTypeTag() == .Int) {
610744 if (lhs.value()) |lhs_val| {
611745 if (rhs.value()) |rhs_val| {
612 const lhs_bigint = try lhs_val.toBigInt(&self.arena.allocator);
613 const rhs_bigint = try rhs_val.toBigInt(&self.arena.allocator);
614 var result_bigint = try BigInt.init(&self.arena.allocator);
615 try BigInt.add(&result_bigint, lhs_bigint, rhs_bigint);
746 // TODO is this a performance issue? maybe we should try the operation without
747 // resorting to BigInt first.
748 var lhs_space: Value.BigIntSpace = undefined;
749 var rhs_space: Value.BigIntSpace = undefined;
750 const lhs_bigint = lhs_val.toBigInt(&lhs_space);
751 const rhs_bigint = rhs_val.toBigInt(&rhs_space);
752 const limbs = try self.arena.allocator.alloc(
753 std.math.big.Limb,
754 std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
755 );
756 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
757 result_bigint.add(lhs_bigint, rhs_bigint);
758 const result_limbs = result_bigint.limbs[0..result_bigint.len];
616759
617760 if (!lhs.ty.eql(rhs.ty)) {
618761 return self.fail(inst.base.src, "TODO implement peer type resolution", .{});
619762 }
620763
621 const val_payload = try self.arena.allocator.create(Value.Payload.IntBig);
622 val_payload.* = .{ .big_int = result_bigint };
764 const val_payload = if (result_bigint.positive) blk: {
765 const val_payload = try self.arena.allocator.create(Value.Payload.IntBigPositive);
766 val_payload.* = .{ .limbs = result_limbs };
767 break :blk &val_payload.base;
768 } else blk: {
769 const val_payload = try self.arena.allocator.create(Value.Payload.IntBigNegative);
770 val_payload.* = .{ .limbs = result_limbs };
771 break :blk &val_payload.base;
772 };
623773
624774 return self.constInst(inst.base.src, .{
625775 .ty = lhs.ty,
626 .val = Value.initPayload(&val_payload.base),
776 .val = Value.initPayload(val_payload),
627777 });
628778 }
629779 }
......@@ -632,8 +782,8 @@ const Analyze = struct {
632782 return self.fail(inst.base.src, "TODO implement more analyze add", .{});
633783 }
634784
635 fn analyzeInstDeref(self: *Analyze, func: ?*Fn, deref: *text.Inst.Deref) InnerError!*Inst {
636 const ptr = try self.resolveInst(func, deref.positionals.ptr);
785 fn analyzeInstDeref(self: *Analyze, block: ?*Block, deref: *text.Inst.Deref) InnerError!*Inst {
786 const ptr = try self.resolveInst(block, deref.positionals.ptr);
637787 const elem_ty = switch (ptr.ty.zigTypeTag()) {
638788 .Pointer => ptr.ty.elemType(),
639789 else => return self.fail(deref.positionals.ptr.src, "expected pointer, found '{}'", .{ptr.ty}),
......@@ -648,28 +798,28 @@ const Analyze = struct {
648798 return self.fail(deref.base.src, "TODO implement runtime deref", .{});
649799 }
650800
651 fn analyzeInstAsm(self: *Analyze, func: ?*Fn, assembly: *text.Inst.Asm) InnerError!*Inst {
652 const return_type = try self.resolveType(func, assembly.positionals.return_type);
653 const asm_source = try self.resolveConstString(func, assembly.positionals.asm_source);
654 const output = if (assembly.kw_args.output) |o| try self.resolveConstString(func, o) else null;
801 fn analyzeInstAsm(self: *Analyze, block: ?*Block, assembly: *text.Inst.Asm) InnerError!*Inst {
802 const return_type = try self.resolveType(block, assembly.positionals.return_type);
803 const asm_source = try self.resolveConstString(block, assembly.positionals.asm_source);
804 const output = if (assembly.kw_args.output) |o| try self.resolveConstString(block, o) else null;
655805
656806 const inputs = try self.arena.allocator.alloc([]const u8, assembly.kw_args.inputs.len);
657807 const clobbers = try self.arena.allocator.alloc([]const u8, assembly.kw_args.clobbers.len);
658808 const args = try self.arena.allocator.alloc(*Inst, assembly.kw_args.args.len);
659809
660810 for (inputs) |*elem, i| {
661 elem.* = try self.resolveConstString(func, assembly.kw_args.inputs[i]);
811 elem.* = try self.resolveConstString(block, assembly.kw_args.inputs[i]);
662812 }
663813 for (clobbers) |*elem, i| {
664 elem.* = try self.resolveConstString(func, assembly.kw_args.clobbers[i]);
814 elem.* = try self.resolveConstString(block, assembly.kw_args.clobbers[i]);
665815 }
666816 for (args) |*elem, i| {
667 const arg = try self.resolveInst(func, assembly.kw_args.args[i]);
668 elem.* = try self.coerce(func, Type.initTag(.usize), arg);
817 const arg = try self.resolveInst(block, assembly.kw_args.args[i]);
818 elem.* = try self.coerce(block, Type.initTag(.usize), arg);
669819 }
670820
671 const f = try self.requireFunctionBody(func, assembly.base.src);
672 return self.addNewInstArgs(f, assembly.base.src, return_type, Inst.Assembly, Inst.Args(Inst.Assembly){
821 const b = try self.requireRuntimeBlock(block, assembly.base.src);
822 return self.addNewInstArgs(b, assembly.base.src, return_type, Inst.Assembly, Inst.Args(Inst.Assembly){
673823 .asm_source = asm_source,
674824 .is_volatile = assembly.kw_args.@"volatile",
675825 .output = output,
......@@ -679,19 +829,370 @@ const Analyze = struct {
679829 });
680830 }
681831
682 fn analyzeInstUnreachable(self: *Analyze, func: ?*Fn, unreach: *text.Inst.Unreachable) InnerError!*Inst {
683 const f = try self.requireFunctionBody(func, unreach.base.src);
684 return self.addNewInstArgs(f, unreach.base.src, Type.initTag(.noreturn), Inst.Unreach, {});
832 fn analyzeInstCmp(self: *Analyze, block: ?*Block, inst: *text.Inst.Cmp) InnerError!*Inst {
833 const lhs = try self.resolveInst(block, inst.positionals.lhs);
834 const rhs = try self.resolveInst(block, inst.positionals.rhs);
835 const op = inst.positionals.op;
836
837 const is_equality_cmp = switch (op) {
838 .eq, .neq => true,
839 else => false,
840 };
841 const lhs_ty_tag = lhs.ty.zigTypeTag();
842 const rhs_ty_tag = rhs.ty.zigTypeTag();
843 if (is_equality_cmp and lhs_ty_tag == .Null and rhs_ty_tag == .Null) {
844 // null == null, null != null
845 return self.constBool(inst.base.src, op == .eq);
846 } else if (is_equality_cmp and
847 ((lhs_ty_tag == .Null and rhs_ty_tag == .Optional) or
848 rhs_ty_tag == .Null and lhs_ty_tag == .Optional))
849 {
850 // comparing null with optionals
851 const opt_operand = if (lhs_ty_tag == .Optional) lhs else rhs;
852 if (opt_operand.value()) |opt_val| {
853 const is_null = opt_val.isNull();
854 return self.constBool(inst.base.src, if (op == .eq) is_null else !is_null);
855 }
856 const b = try self.requireRuntimeBlock(block, inst.base.src);
857 switch (op) {
858 .eq => return self.addNewInstArgs(
859 b,
860 inst.base.src,
861 Type.initTag(.bool),
862 Inst.IsNull,
863 Inst.Args(Inst.IsNull){ .operand = opt_operand },
864 ),
865 .neq => return self.addNewInstArgs(
866 b,
867 inst.base.src,
868 Type.initTag(.bool),
869 Inst.IsNonNull,
870 Inst.Args(Inst.IsNonNull){ .operand = opt_operand },
871 ),
872 else => unreachable,
873 }
874 } else if (is_equality_cmp and
875 ((lhs_ty_tag == .Null and rhs.ty.isCPtr()) or (rhs_ty_tag == .Null and lhs.ty.isCPtr())))
876 {
877 return self.fail(inst.base.src, "TODO implement C pointer cmp", .{});
878 } else if (lhs_ty_tag == .Null or rhs_ty_tag == .Null) {
879 const non_null_type = if (lhs_ty_tag == .Null) rhs.ty else lhs.ty;
880 return self.fail(inst.base.src, "comparison of '{}' with null", .{non_null_type});
881 } else if (is_equality_cmp and
882 ((lhs_ty_tag == .EnumLiteral and rhs_ty_tag == .Union) or
883 (rhs_ty_tag == .EnumLiteral and lhs_ty_tag == .Union)))
884 {
885 return self.fail(inst.base.src, "TODO implement equality comparison between a union's tag value and an enum literal", .{});
886 } else if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) {
887 if (!is_equality_cmp) {
888 return self.fail(inst.base.src, "{} operator not allowed for errors", .{@tagName(op)});
889 }
890 return self.fail(inst.base.src, "TODO implement equality comparison between errors", .{});
891 } else if (lhs.ty.isNumeric() and rhs.ty.isNumeric()) {
892 // This operation allows any combination of integer and float types, regardless of the
893 // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for
894 // numeric types.
895 return self.cmpNumeric(block, inst.base.src, lhs, rhs, op);
896 }
897 return self.fail(inst.base.src, "TODO implement more cmp analysis", .{});
898 }
899
900 fn analyzeInstIsNull(self: *Analyze, block: ?*Block, inst: *text.Inst.IsNull) InnerError!*Inst {
901 const operand = try self.resolveInst(block, inst.positionals.operand);
902 return self.analyzeIsNull(block, inst.base.src, operand, true);
903 }
904
905 fn analyzeInstIsNonNull(self: *Analyze, block: ?*Block, inst: *text.Inst.IsNonNull) InnerError!*Inst {
906 const operand = try self.resolveInst(block, inst.positionals.operand);
907 return self.analyzeIsNull(block, inst.base.src, operand, false);
908 }
909
910 fn analyzeInstCondBr(self: *Analyze, block: ?*Block, inst: *text.Inst.CondBr) InnerError!*Inst {
911 const uncasted_cond = try self.resolveInst(block, inst.positionals.condition);
912 const cond = try self.coerce(block, Type.initTag(.bool), uncasted_cond);
913
914 if (try self.resolveDefinedValue(cond)) |cond_val| {
915 const body = if (cond_val.toBool()) &inst.positionals.true_body else &inst.positionals.false_body;
916 try self.analyzeBody(block, body.*);
917 return self.constVoid(inst.base.src);
918 }
919
920 const parent_block = try self.requireRuntimeBlock(block, inst.base.src);
921
922 var true_block: Block = .{
923 .func = parent_block.func,
924 .instructions = std.ArrayList(*Inst).init(self.allocator),
925 };
926 defer true_block.instructions.deinit();
927 try self.analyzeBody(&true_block, inst.positionals.true_body);
928
929 var false_block: Block = .{
930 .func = parent_block.func,
931 .instructions = std.ArrayList(*Inst).init(self.allocator),
932 };
933 defer false_block.instructions.deinit();
934 try self.analyzeBody(&false_block, inst.positionals.false_body);
935
936 // Copy the instruction pointers to the arena memory
937 const true_instructions = try self.arena.allocator.alloc(*Inst, true_block.instructions.items.len);
938 const false_instructions = try self.arena.allocator.alloc(*Inst, false_block.instructions.items.len);
939
940 mem.copy(*Inst, true_instructions, true_block.instructions.items);
941 mem.copy(*Inst, false_instructions, false_block.instructions.items);
942
943 return self.addNewInstArgs(parent_block, inst.base.src, Type.initTag(.void), Inst.CondBr, Inst.Args(Inst.CondBr){
944 .condition = cond,
945 .true_body = .{ .instructions = true_instructions },
946 .false_body = .{ .instructions = false_instructions },
947 });
948 }
949
950 fn wantSafety(self: *Analyze, block: ?*Block) bool {
951 return switch (self.optimize_mode) {
952 .Debug => true,
953 .ReleaseSafe => true,
954 .ReleaseFast => false,
955 .ReleaseSmall => false,
956 };
957 }
958
959 fn analyzeInstUnreachable(self: *Analyze, block: ?*Block, unreach: *text.Inst.Unreachable) InnerError!*Inst {
960 const b = try self.requireRuntimeBlock(block, unreach.base.src);
961 if (self.wantSafety(block)) {
962 // TODO Once we have a panic function to call, call it here instead of this.
963 _ = try self.addNewInstArgs(b, unreach.base.src, Type.initTag(.void), Inst.Breakpoint, {});
964 }
965 return self.addNewInstArgs(b, unreach.base.src, Type.initTag(.noreturn), Inst.Unreach, {});
966 }
967
968 fn analyzeInstRet(self: *Analyze, block: ?*Block, inst: *text.Inst.Return) InnerError!*Inst {
969 const b = try self.requireRuntimeBlock(block, inst.base.src);
970 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.noreturn), Inst.Ret, {});
971 }
972
973 fn analyzeBody(self: *Analyze, block: ?*Block, body: text.Module.Body) !void {
974 for (body.instructions) |src_inst| {
975 const new_inst = self.analyzeInst(block, src_inst) catch |err| {
976 if (block) |b| {
977 self.fns.items[b.func.fn_index].analysis_status = .failure;
978 try b.func.inst_table.putNoClobber(src_inst, .{ .ptr = null });
979 }
980 return err;
981 };
982 if (block) |b| try b.func.inst_table.putNoClobber(src_inst, .{ .ptr = new_inst });
983 }
984 }
985
986 fn analyzeIsNull(
987 self: *Analyze,
988 block: ?*Block,
989 src: usize,
990 operand: *Inst,
991 invert_logic: bool,
992 ) InnerError!*Inst {
993 return self.fail(src, "TODO implement analysis of isnull and isnotnull", .{});
994 }
995
996 /// Asserts that lhs and rhs types are both numeric.
997 fn cmpNumeric(
998 self: *Analyze,
999 block: ?*Block,
1000 src: usize,
1001 lhs: *Inst,
1002 rhs: *Inst,
1003 op: std.math.CompareOperator,
1004 ) !*Inst {
1005 assert(lhs.ty.isNumeric());
1006 assert(rhs.ty.isNumeric());
1007
1008 const lhs_ty_tag = lhs.ty.zigTypeTag();
1009 const rhs_ty_tag = rhs.ty.zigTypeTag();
1010
1011 if (lhs_ty_tag == .Vector and rhs_ty_tag == .Vector) {
1012 if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) {
1013 return self.fail(src, "vector length mismatch: {} and {}", .{
1014 lhs.ty.arrayLen(),
1015 rhs.ty.arrayLen(),
1016 });
1017 }
1018 return self.fail(src, "TODO implement support for vectors in cmpNumeric", .{});
1019 } else if (lhs_ty_tag == .Vector or rhs_ty_tag == .Vector) {
1020 return self.fail(src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{
1021 lhs.ty,
1022 rhs.ty,
1023 });
1024 }
1025
1026 if (lhs.value()) |lhs_val| {
1027 if (rhs.value()) |rhs_val| {
1028 return self.constBool(src, Value.compare(lhs_val, op, rhs_val));
1029 }
1030 }
1031
1032 // TODO handle comparisons against lazy zero values
1033 // Some values can be compared against zero without being runtime known or without forcing
1034 // a full resolution of their value, for example `@sizeOf(@Frame(function))` is known to
1035 // always be nonzero, and we benefit from not forcing the full evaluation and stack frame layout
1036 // of this function if we don't need to.
1037
1038 // It must be a runtime comparison.
1039 const b = try self.requireRuntimeBlock(block, src);
1040 // For floats, emit a float comparison instruction.
1041 const lhs_is_float = switch (lhs_ty_tag) {
1042 .Float, .ComptimeFloat => true,
1043 else => false,
1044 };
1045 const rhs_is_float = switch (rhs_ty_tag) {
1046 .Float, .ComptimeFloat => true,
1047 else => false,
1048 };
1049 if (lhs_is_float and rhs_is_float) {
1050 // Implicit cast the smaller one to the larger one.
1051 const dest_type = x: {
1052 if (lhs_ty_tag == .ComptimeFloat) {
1053 break :x rhs.ty;
1054 } else if (rhs_ty_tag == .ComptimeFloat) {
1055 break :x lhs.ty;
1056 }
1057 if (lhs.ty.floatBits(self.target) >= rhs.ty.floatBits(self.target)) {
1058 break :x lhs.ty;
1059 } else {
1060 break :x rhs.ty;
1061 }
1062 };
1063 const casted_lhs = try self.coerce(block, dest_type, lhs);
1064 const casted_rhs = try self.coerce(block, dest_type, rhs);
1065 return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, Inst.Args(Inst.Cmp){
1066 .lhs = casted_lhs,
1067 .rhs = casted_rhs,
1068 .op = op,
1069 });
1070 }
1071 // For mixed unsigned integer sizes, implicit cast both operands to the larger integer.
1072 // For mixed signed and unsigned integers, implicit cast both operands to a signed
1073 // integer with + 1 bit.
1074 // For mixed floats and integers, extract the integer part from the float, cast that to
1075 // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float,
1076 // add/subtract 1.
1077 const lhs_is_signed = if (lhs.value()) |lhs_val|
1078 lhs_val.compareWithZero(.lt)
1079 else
1080 (lhs.ty.isFloat() or lhs.ty.isSignedInt());
1081 const rhs_is_signed = if (rhs.value()) |rhs_val|
1082 rhs_val.compareWithZero(.lt)
1083 else
1084 (rhs.ty.isFloat() or rhs.ty.isSignedInt());
1085 const dest_int_is_signed = lhs_is_signed or rhs_is_signed;
1086
1087 var dest_float_type: ?Type = null;
1088
1089 var lhs_bits: usize = undefined;
1090 if (lhs.value()) |lhs_val| {
1091 if (lhs_val.isUndef())
1092 return self.constUndef(src, Type.initTag(.bool));
1093 const is_unsigned = if (lhs_is_float) x: {
1094 var bigint_space: Value.BigIntSpace = undefined;
1095 var bigint = try lhs_val.toBigInt(&bigint_space).toManaged(self.allocator);
1096 defer bigint.deinit();
1097 const zcmp = lhs_val.orderAgainstZero();
1098 if (lhs_val.floatHasFraction()) {
1099 switch (op) {
1100 .eq => return self.constBool(src, false),
1101 .neq => return self.constBool(src, true),
1102 else => {},
1103 }
1104 if (zcmp == .lt) {
1105 try bigint.addScalar(bigint.toConst(), -1);
1106 } else {
1107 try bigint.addScalar(bigint.toConst(), 1);
1108 }
1109 }
1110 lhs_bits = bigint.toConst().bitCountTwosComp();
1111 break :x (zcmp != .lt);
1112 } else x: {
1113 lhs_bits = lhs_val.intBitCountTwosComp();
1114 break :x (lhs_val.orderAgainstZero() != .lt);
1115 };
1116 lhs_bits += @boolToInt(is_unsigned and dest_int_is_signed);
1117 } else if (lhs_is_float) {
1118 dest_float_type = lhs.ty;
1119 } else {
1120 const int_info = lhs.ty.intInfo(self.target);
1121 lhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed);
1122 }
1123
1124 var rhs_bits: usize = undefined;
1125 if (rhs.value()) |rhs_val| {
1126 if (rhs_val.isUndef())
1127 return self.constUndef(src, Type.initTag(.bool));
1128 const is_unsigned = if (rhs_is_float) x: {
1129 var bigint_space: Value.BigIntSpace = undefined;
1130 var bigint = try rhs_val.toBigInt(&bigint_space).toManaged(self.allocator);
1131 defer bigint.deinit();
1132 const zcmp = rhs_val.orderAgainstZero();
1133 if (rhs_val.floatHasFraction()) {
1134 switch (op) {
1135 .eq => return self.constBool(src, false),
1136 .neq => return self.constBool(src, true),
1137 else => {},
1138 }
1139 if (zcmp == .lt) {
1140 try bigint.addScalar(bigint.toConst(), -1);
1141 } else {
1142 try bigint.addScalar(bigint.toConst(), 1);
1143 }
1144 }
1145 rhs_bits = bigint.toConst().bitCountTwosComp();
1146 break :x (zcmp != .lt);
1147 } else x: {
1148 rhs_bits = rhs_val.intBitCountTwosComp();
1149 break :x (rhs_val.orderAgainstZero() != .lt);
1150 };
1151 rhs_bits += @boolToInt(is_unsigned and dest_int_is_signed);
1152 } else if (rhs_is_float) {
1153 dest_float_type = rhs.ty;
1154 } else {
1155 const int_info = rhs.ty.intInfo(self.target);
1156 rhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed);
1157 }
1158
1159 const dest_type = if (dest_float_type) |ft| ft else blk: {
1160 const max_bits = std.math.max(lhs_bits, rhs_bits);
1161 const casted_bits = std.math.cast(u16, max_bits) catch |err| switch (err) {
1162 error.Overflow => return self.fail(src, "{} exceeds maximum integer bit count", .{max_bits}),
1163 };
1164 break :blk try self.makeIntType(dest_int_is_signed, casted_bits);
1165 };
1166 const casted_lhs = try self.coerce(block, dest_type, lhs);
1167 const casted_rhs = try self.coerce(block, dest_type, lhs);
1168
1169 return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, Inst.Args(Inst.Cmp){
1170 .lhs = casted_lhs,
1171 .rhs = casted_rhs,
1172 .op = op,
1173 });
1174 }
1175
1176 fn makeIntType(self: *Analyze, signed: bool, bits: u16) !Type {
1177 if (signed) {
1178 const int_payload = try self.arena.allocator.create(Type.Payload.IntSigned);
1179 int_payload.* = .{ .bits = bits };
1180 return Type.initPayload(&int_payload.base);
1181 } else {
1182 const int_payload = try self.arena.allocator.create(Type.Payload.IntUnsigned);
1183 int_payload.* = .{ .bits = bits };
1184 return Type.initPayload(&int_payload.base);
1185 }
6851186 }
6861187
687 fn coerce(self: *Analyze, func: ?*Fn, dest_type: Type, inst: *Inst) !*Inst {
1188 fn coerce(self: *Analyze, block: ?*Block, dest_type: Type, inst: *Inst) !*Inst {
6881189 // If the types are the same, we can return the operand.
6891190 if (dest_type.eql(inst.ty))
6901191 return inst;
6911192
6921193 const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty);
6931194 if (in_memory_result == .ok) {
694 return self.bitcast(func, dest_type, inst);
1195 return self.bitcast(block, dest_type, inst);
6951196 }
6961197
6971198 // *[N]T to []T
......@@ -735,14 +1236,14 @@ const Analyze = struct {
7351236 return self.fail(inst.src, "TODO implement type coercion from {} to {}", .{ inst.ty, dest_type });
7361237 }
7371238
738 fn bitcast(self: *Analyze, func: ?*Fn, dest_type: Type, inst: *Inst) !*Inst {
1239 fn bitcast(self: *Analyze, block: ?*Block, dest_type: Type, inst: *Inst) !*Inst {
7391240 if (inst.value()) |val| {
7401241 // Keep the comptime Value representation; take the new type.
7411242 return self.constInst(inst.src, .{ .ty = dest_type, .val = val });
7421243 }
7431244 // TODO validate the type size and other compile errors
744 const f = try self.requireFunctionBody(func, inst.src);
745 return self.addNewInstArgs(f, inst.src, dest_type, Inst.BitCast, Inst.Args(Inst.BitCast){ .operand = inst });
1245 const b = try self.requireRuntimeBlock(block, inst.src);
1246 return self.addNewInstArgs(b, inst.src, dest_type, Inst.BitCast, Inst.Args(Inst.BitCast){ .operand = inst });
7461247 }
7471248
7481249 fn coerceArrayPtrToSlice(self: *Analyze, dest_type: Type, inst: *Inst) !*Inst {
......@@ -784,18 +1285,20 @@ pub fn main() anyerror!void {
7841285 const allocator = if (std.builtin.link_libc) std.heap.c_allocator else &arena.allocator;
7851286
7861287 const args = try std.process.argsAlloc(allocator);
1288 defer std.process.argsFree(allocator, args);
7871289
7881290 const src_path = args[1];
7891291 const debug_error_trace = true;
7901292
7911293 const source = try std.fs.cwd().readFileAllocOptions(allocator, src_path, std.math.maxInt(u32), 1, 0);
1294 defer allocator.free(source);
7921295
7931296 var zir_module = try text.parse(allocator, source);
7941297 defer zir_module.deinit(allocator);
7951298
7961299 if (zir_module.errors.len != 0) {
7971300 for (zir_module.errors) |err_msg| {
798 const loc = findLineColumn(source, err_msg.byte_offset);
1301 const loc = std.zig.findLineColumn(source, err_msg.byte_offset);
7991302 std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg });
8001303 }
8011304 if (debug_error_trace) return error.ParseFailure;
......@@ -804,15 +1307,20 @@ pub fn main() anyerror!void {
8041307
8051308 const native_info = try std.zig.system.NativeTargetInfo.detect(allocator, .{});
8061309
807 var analyzed_module = try analyze(allocator, zir_module, native_info.target);
1310 var analyzed_module = try analyze(allocator, zir_module, .{
1311 .target = native_info.target,
1312 .output_mode = .Obj,
1313 .link_mode = .Static,
1314 .optimize_mode = .Debug,
1315 });
8081316 defer analyzed_module.deinit(allocator);
8091317
8101318 if (analyzed_module.errors.len != 0) {
8111319 for (analyzed_module.errors) |err_msg| {
812 const loc = findLineColumn(source, err_msg.byte_offset);
1320 const loc = std.zig.findLineColumn(source, err_msg.byte_offset);
8131321 std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg });
8141322 }
815 if (debug_error_trace) return error.ParseFailure;
1323 if (debug_error_trace) return error.AnalysisFail;
8161324 std.process.exit(1);
8171325 }
8181326
......@@ -827,34 +1335,17 @@ pub fn main() anyerror!void {
8271335 }
8281336
8291337 const link = @import("link.zig");
830 var result = try link.updateExecutableFilePath(allocator, analyzed_module, std.fs.cwd(), "a.out");
1338 var result = try link.updateFilePath(allocator, analyzed_module, std.fs.cwd(), "zir.o");
8311339 defer result.deinit(allocator);
8321340 if (result.errors.len != 0) {
8331341 for (result.errors) |err_msg| {
834 const loc = findLineColumn(source, err_msg.byte_offset);
1342 const loc = std.zig.findLineColumn(source, err_msg.byte_offset);
8351343 std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg });
8361344 }
837 if (debug_error_trace) return error.ParseFailure;
1345 if (debug_error_trace) return error.LinkFailure;
8381346 std.process.exit(1);
8391347 }
8401348}
8411349
842fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usize, column: usize } {
843 var line: usize = 0;
844 var column: usize = 0;
845 for (source[0..byte_offset]) |byte| {
846 switch (byte) {
847 '\n' => {
848 line += 1;
849 column = 0;
850 },
851 else => {
852 column += 1;
853 },
854 }
855 }
856 return .{ .line = line, .column = column };
857}
858
8591350// Performance optimization ideas:
8601351// * when analyzing use a field in the Inst instead of HashMap to track corresponding instructions
src-self-hosted/ir/text.zig+292-130
......@@ -4,7 +4,8 @@ const std = @import("std");
44const mem = std.mem;
55const Allocator = std.mem.Allocator;
66const assert = std.debug.assert;
7const BigInt = std.math.big.Int;
7const BigIntConst = std.math.big.int.Const;
8const BigIntMutable = std.math.big.int.Mutable;
89const Type = @import("../type.zig").Type;
910const Value = @import("../value.zig").Value;
1011const ir = @import("../ir.zig");
......@@ -18,6 +19,7 @@ pub const Inst = struct {
1819
1920 /// These names are used directly as the instruction names in the text format.
2021 pub const Tag = enum {
22 breakpoint,
2123 str,
2224 int,
2325 ptrtoint,
......@@ -26,6 +28,7 @@ pub const Inst = struct {
2628 as,
2729 @"asm",
2830 @"unreachable",
31 @"return",
2932 @"fn",
3033 @"export",
3134 primitive,
......@@ -34,10 +37,15 @@ pub const Inst = struct {
3437 bitcast,
3538 elemptr,
3639 add,
40 cmp,
41 condbr,
42 isnull,
43 isnonnull,
3744 };
3845
3946 pub fn TagToType(tag: Tag) type {
4047 return switch (tag) {
48 .breakpoint => Breakpoint,
4149 .str => Str,
4250 .int => Int,
4351 .ptrtoint => PtrToInt,
......@@ -46,6 +54,7 @@ pub const Inst = struct {
4654 .as => As,
4755 .@"asm" => Asm,
4856 .@"unreachable" => Unreachable,
57 .@"return" => Return,
4958 .@"fn" => Fn,
5059 .@"export" => Export,
5160 .primitive => Primitive,
......@@ -54,6 +63,10 @@ pub const Inst = struct {
5463 .bitcast => BitCast,
5564 .elemptr => ElemPtr,
5665 .add => Add,
66 .cmp => Cmp,
67 .condbr => CondBr,
68 .isnull => IsNull,
69 .isnonnull => IsNonNull,
5770 };
5871 }
5972
......@@ -64,6 +77,14 @@ pub const Inst = struct {
6477 return @fieldParentPtr(T, "base", base);
6578 }
6679
80 pub const Breakpoint = struct {
81 pub const base_tag = Tag.breakpoint;
82 base: Inst,
83
84 positionals: struct {},
85 kw_args: struct {},
86 };
87
6788 pub const Str = struct {
6889 pub const base_tag = Tag.str;
6990 base: Inst,
......@@ -79,7 +100,7 @@ pub const Inst = struct {
79100 base: Inst,
80101
81102 positionals: struct {
82 int: BigInt,
103 int: BigIntConst,
83104 },
84105 kw_args: struct {},
85106 };
......@@ -151,19 +172,23 @@ pub const Inst = struct {
151172 kw_args: struct {},
152173 };
153174
175 pub const Return = struct {
176 pub const base_tag = Tag.@"return";
177 base: Inst,
178
179 positionals: struct {},
180 kw_args: struct {},
181 };
182
154183 pub const Fn = struct {
155184 pub const base_tag = Tag.@"fn";
156185 base: Inst,
157186
158187 positionals: struct {
159188 fn_type: *Inst,
160 body: Body,
189 body: Module.Body,
161190 },
162191 kw_args: struct {},
163
164 pub const Body = struct {
165 instructions: []*Inst,
166 };
167192 };
168193
169194 pub const Export = struct {
......@@ -297,6 +322,50 @@ pub const Inst = struct {
297322 },
298323 kw_args: struct {},
299324 };
325
326 pub const Cmp = struct {
327 pub const base_tag = Tag.cmp;
328 base: Inst,
329
330 positionals: struct {
331 lhs: *Inst,
332 op: std.math.CompareOperator,
333 rhs: *Inst,
334 },
335 kw_args: struct {},
336 };
337
338 pub const CondBr = struct {
339 pub const base_tag = Tag.condbr;
340 base: Inst,
341
342 positionals: struct {
343 condition: *Inst,
344 true_body: Module.Body,
345 false_body: Module.Body,
346 },
347 kw_args: struct {},
348 };
349
350 pub const IsNull = struct {
351 pub const base_tag = Tag.isnull;
352 base: Inst,
353
354 positionals: struct {
355 operand: *Inst,
356 },
357 kw_args: struct {},
358 };
359
360 pub const IsNonNull = struct {
361 pub const base_tag = Tag.isnonnull;
362 base: Inst,
363
364 positionals: struct {
365 operand: *Inst,
366 },
367 kw_args: struct {},
368 };
300369};
301370
302371pub const ErrorMsg = struct {
......@@ -309,6 +378,10 @@ pub const Module = struct {
309378 errors: []ErrorMsg,
310379 arena: std.heap.ArenaAllocator,
311380
381 pub const Body = struct {
382 instructions: []*Inst,
383 };
384
312385 pub fn deinit(self: *Module, allocator: *Allocator) void {
313386 allocator.free(self.decls);
314387 allocator.free(self.errors);
......@@ -321,7 +394,7 @@ pub const Module = struct {
321394 self.writeToStream(std.heap.page_allocator, std.io.getStdErr().outStream()) catch {};
322395 }
323396
324 const InstPtrTable = std.AutoHashMap(*Inst, struct { index: usize, fn_body: ?*Inst.Fn.Body });
397 const InstPtrTable = std.AutoHashMap(*Inst, struct { index: usize, fn_body: ?*Module.Body });
325398
326399 /// The allocator is used for temporary storage, but this function always returns
327400 /// with no resources allocated.
......@@ -357,6 +430,7 @@ pub const Module = struct {
357430 ) @TypeOf(stream).Error!void {
358431 // TODO I tried implementing this with an inline for loop and hit a compiler bug
359432 switch (decl.tag) {
433 .breakpoint => return self.writeInstToStreamGeneric(stream, .breakpoint, decl, inst_table),
360434 .str => return self.writeInstToStreamGeneric(stream, .str, decl, inst_table),
361435 .int => return self.writeInstToStreamGeneric(stream, .int, decl, inst_table),
362436 .ptrtoint => return self.writeInstToStreamGeneric(stream, .ptrtoint, decl, inst_table),
......@@ -365,6 +439,7 @@ pub const Module = struct {
365439 .as => return self.writeInstToStreamGeneric(stream, .as, decl, inst_table),
366440 .@"asm" => return self.writeInstToStreamGeneric(stream, .@"asm", decl, inst_table),
367441 .@"unreachable" => return self.writeInstToStreamGeneric(stream, .@"unreachable", decl, inst_table),
442 .@"return" => return self.writeInstToStreamGeneric(stream, .@"return", decl, inst_table),
368443 .@"fn" => return self.writeInstToStreamGeneric(stream, .@"fn", decl, inst_table),
369444 .@"export" => return self.writeInstToStreamGeneric(stream, .@"export", decl, inst_table),
370445 .primitive => return self.writeInstToStreamGeneric(stream, .primitive, decl, inst_table),
......@@ -373,6 +448,10 @@ pub const Module = struct {
373448 .bitcast => return self.writeInstToStreamGeneric(stream, .bitcast, decl, inst_table),
374449 .elemptr => return self.writeInstToStreamGeneric(stream, .elemptr, decl, inst_table),
375450 .add => return self.writeInstToStreamGeneric(stream, .add, decl, inst_table),
451 .cmp => return self.writeInstToStreamGeneric(stream, .cmp, decl, inst_table),
452 .condbr => return self.writeInstToStreamGeneric(stream, .condbr, decl, inst_table),
453 .isnull => return self.writeInstToStreamGeneric(stream, .isnull, decl, inst_table),
454 .isnonnull => return self.writeInstToStreamGeneric(stream, .isnonnull, decl, inst_table),
376455 }
377456 }
378457
......@@ -432,7 +511,7 @@ pub const Module = struct {
432511 }
433512 try stream.writeByte(']');
434513 },
435 Inst.Fn.Body => {
514 Module.Body => {
436515 try stream.writeAll("{\n");
437516 for (param.instructions) |inst, i| {
438517 try stream.print(" %{} ", .{i});
......@@ -443,7 +522,7 @@ pub const Module = struct {
443522 },
444523 bool => return stream.writeByte("01"[@boolToInt(param)]),
445524 []u8, []const u8 => return std.zig.renderStringLiteral(param, stream),
446 BigInt => return stream.print("{}", .{param}),
525 BigIntConst => return stream.print("{}", .{param}),
447526 else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)),
448527 }
449528 }
......@@ -497,7 +576,7 @@ const Parser = struct {
497576 name_map: std.StringHashMap(usize),
498577 };
499578
500 fn parseBody(self: *Parser) !Inst.Fn.Body {
579 fn parseBody(self: *Parser) !Module.Body {
501580 var body_context = Body{
502581 .instructions = std.ArrayList(*Inst).init(self.allocator),
503582 .name_map = std.StringHashMap(usize).init(self.allocator),
......@@ -532,9 +611,10 @@ const Parser = struct {
532611 else => |byte| return self.failByte(byte),
533612 };
534613
535 return Inst.Fn.Body{
536 .instructions = body_context.instructions.toOwnedSlice(),
537 };
614 // Move the instructions to the arena
615 const instrs = try self.arena.allocator.alloc(*Inst, body_context.instructions.items.len);
616 mem.copy(*Inst, instrs, body_context.instructions.items);
617 return Module.Body{ .instructions = instrs };
538618 }
539619
540620 fn parseStringLiteral(self: *Parser) ![]u8 {
......@@ -565,7 +645,7 @@ const Parser = struct {
565645 };
566646 }
567647
568 fn parseIntegerLiteral(self: *Parser) !BigInt {
648 fn parseIntegerLiteral(self: *Parser) !BigIntConst {
569649 const start = self.i;
570650 if (self.source[self.i] == '-') self.i += 1;
571651 while (true) : (self.i += 1) switch (self.source[self.i]) {
......@@ -573,41 +653,46 @@ const Parser = struct {
573653 else => break,
574654 };
575655 const number_text = self.source[start..self.i];
576 var result = try BigInt.init(&self.arena.allocator);
577 result.setString(10, number_text) catch |err| {
578 self.i = start;
579 switch (err) {
580 error.InvalidBase => unreachable,
581 error.InvalidCharForDigit => return self.fail("invalid digit in integer literal", .{}),
582 error.DigitTooLargeForBase => return self.fail("digit too large in integer literal", .{}),
583 else => |e| return e,
584 }
656 const base = 10;
657 // TODO reuse the same array list for this
658 const limbs_buffer_len = std.math.big.int.calcSetStringLimbsBufferLen(base, number_text.len);
659 const limbs_buffer = try self.allocator.alloc(std.math.big.Limb, limbs_buffer_len);
660 defer self.allocator.free(limbs_buffer);
661 const limb_len = std.math.big.int.calcSetStringLimbCount(base, number_text.len);
662 const limbs = try self.arena.allocator.alloc(std.math.big.Limb, limb_len);
663 var result = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
664 result.setString(base, number_text, limbs_buffer, self.allocator) catch |err| switch (err) {
665 error.InvalidCharacter => {
666 self.i = start;
667 return self.fail("invalid digit in integer literal", .{});
668 },
585669 };
586 return result;
670 return result.toConst();
587671 }
588672
589673 fn parseRoot(self: *Parser) !void {
590674 // The IR format is designed so that it can be tokenized and parsed at the same time.
591 while (true) : (self.i += 1) switch (self.source[self.i]) {
592 ';' => _ = try skipToAndOver(self, '\n'),
593 '@' => {
594 self.i += 1;
595 const ident = try skipToAndOver(self, ' ');
596 skipSpace(self);
597 try requireEatBytes(self, "=");
598 skipSpace(self);
599 const inst = try parseInstruction(self, null);
600 const ident_index = self.decls.items.len;
601 if (try self.global_name_map.put(ident, ident_index)) |_| {
602 return self.fail("redefinition of identifier '{}'", .{ident});
603 }
604 try self.decls.append(inst);
605 continue;
606 },
607 ' ', '\n' => continue,
608 0 => break,
609 else => |byte| return self.fail("unexpected byte: '{c}'", .{byte}),
610 };
675 while (true) {
676 switch (self.source[self.i]) {
677 ';' => _ = try skipToAndOver(self, '\n'),
678 '@' => {
679 self.i += 1;
680 const ident = try skipToAndOver(self, ' ');
681 skipSpace(self);
682 try requireEatBytes(self, "=");
683 skipSpace(self);
684 const inst = try parseInstruction(self, null);
685 const ident_index = self.decls.items.len;
686 if (try self.global_name_map.put(ident, ident_index)) |_| {
687 return self.fail("redefinition of identifier '{}'", .{ident});
688 }
689 try self.decls.append(inst);
690 },
691 ' ', '\n' => self.i += 1,
692 0 => break,
693 else => |byte| return self.fail("unexpected byte: '{c}'", .{byte}),
694 }
695 }
611696 }
612697
613698 fn eatByte(self: *Parser, byte: u8) bool {
......@@ -752,7 +837,7 @@ const Parser = struct {
752837 };
753838 }
754839 switch (T) {
755 Inst.Fn.Body => return parseBody(self),
840 Module.Body => return parseBody(self),
756841 bool => {
757842 const bool_value = switch (self.source[self.i]) {
758843 '0' => false,
......@@ -779,7 +864,7 @@ const Parser = struct {
779864 },
780865 *Inst => return parseParameterInst(self, body_ctx),
781866 []u8, []const u8 => return self.parseStringLiteral(),
782 BigInt => return self.parseIntegerLiteral(),
867 BigIntConst => return self.parseIntegerLiteral(),
783868 else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)),
784869 }
785870 return self.fail("TODO parse parameter {}", .{@typeName(T)});
......@@ -878,11 +963,12 @@ const EmitZIR = struct {
878963 }
879964
880965 fn emitComptimeIntVal(self: *EmitZIR, src: usize, val: Value) !*Inst {
966 const big_int_space = try self.arena.allocator.create(Value.BigIntSpace);
881967 const int_inst = try self.arena.allocator.create(Inst.Int);
882968 int_inst.* = .{
883969 .base = .{ .src = src, .tag = Inst.Int.base_tag },
884970 .positionals = .{
885 .int = try val.toBigInt(&self.arena.allocator),
971 .int = val.toBigInt(big_int_space),
886972 },
887973 .kw_args = .{},
888974 };
......@@ -937,96 +1023,19 @@ const EmitZIR = struct {
9371023 var instructions = std.ArrayList(*Inst).init(self.allocator);
9381024 defer instructions.deinit();
9391025
940 for (module_fn.body) |inst| {
941 const new_inst = switch (inst.tag) {
942 .unreach => blk: {
943 const unreach_inst = try self.arena.allocator.create(Inst.Unreachable);
944 unreach_inst.* = .{
945 .base = .{ .src = inst.src, .tag = Inst.Unreachable.base_tag },
946 .positionals = .{},
947 .kw_args = .{},
948 };
949 break :blk &unreach_inst.base;
950 },
951 .constant => unreachable, // excluded from function bodies
952 .assembly => blk: {
953 const old_inst = inst.cast(ir.Inst.Assembly).?;
954 const new_inst = try self.arena.allocator.create(Inst.Asm);
955
956 const inputs = try self.arena.allocator.alloc(*Inst, old_inst.args.inputs.len);
957 for (inputs) |*elem, i| {
958 elem.* = try self.emitStringLiteral(inst.src, old_inst.args.inputs[i]);
959 }
960
961 const clobbers = try self.arena.allocator.alloc(*Inst, old_inst.args.clobbers.len);
962 for (clobbers) |*elem, i| {
963 elem.* = try self.emitStringLiteral(inst.src, old_inst.args.clobbers[i]);
964 }
965
966 const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len);
967 for (args) |*elem, i| {
968 elem.* = try self.resolveInst(&inst_table, old_inst.args.args[i]);
969 }
970
971 new_inst.* = .{
972 .base = .{ .src = inst.src, .tag = Inst.Asm.base_tag },
973 .positionals = .{
974 .asm_source = try self.emitStringLiteral(inst.src, old_inst.args.asm_source),
975 .return_type = try self.emitType(inst.src, inst.ty),
976 },
977 .kw_args = .{
978 .@"volatile" = old_inst.args.is_volatile,
979 .output = if (old_inst.args.output) |o|
980 try self.emitStringLiteral(inst.src, o)
981 else
982 null,
983 .inputs = inputs,
984 .clobbers = clobbers,
985 .args = args,
986 },
987 };
988 break :blk &new_inst.base;
989 },
990 .ptrtoint => blk: {
991 const old_inst = inst.cast(ir.Inst.PtrToInt).?;
992 const new_inst = try self.arena.allocator.create(Inst.PtrToInt);
993 new_inst.* = .{
994 .base = .{ .src = inst.src, .tag = Inst.PtrToInt.base_tag },
995 .positionals = .{
996 .ptr = try self.resolveInst(&inst_table, old_inst.args.ptr),
997 },
998 .kw_args = .{},
999 };
1000 break :blk &new_inst.base;
1001 },
1002 .bitcast => blk: {
1003 const old_inst = inst.cast(ir.Inst.BitCast).?;
1004 const new_inst = try self.arena.allocator.create(Inst.BitCast);
1005 new_inst.* = .{
1006 .base = .{ .src = inst.src, .tag = Inst.BitCast.base_tag },
1007 .positionals = .{
1008 .dest_type = try self.emitType(inst.src, inst.ty),
1009 .operand = try self.resolveInst(&inst_table, old_inst.args.operand),
1010 },
1011 .kw_args = .{},
1012 };
1013 break :blk &new_inst.base;
1014 },
1015 };
1016 try instructions.append(new_inst);
1017 try inst_table.putNoClobber(inst, new_inst);
1018 }
1026 try self.emitBody(module_fn.body, &inst_table, &instructions);
10191027
10201028 const fn_type = try self.emitType(src, module_fn.fn_type);
10211029
1030 const arena_instrs = try self.arena.allocator.alloc(*Inst, instructions.items.len);
1031 mem.copy(*Inst, arena_instrs, instructions.items);
1032
10221033 const fn_inst = try self.arena.allocator.create(Inst.Fn);
10231034 fn_inst.* = .{
10241035 .base = .{ .src = src, .tag = Inst.Fn.base_tag },
10251036 .positionals = .{
10261037 .fn_type = fn_type,
1027 .body = .{
1028 .instructions = instructions.toOwnedSlice(),
1029 },
1038 .body = .{ .instructions = arena_instrs },
10301039 },
10311040 .kw_args = .{},
10321041 };
......@@ -1037,6 +1046,159 @@ const EmitZIR = struct {
10371046 }
10381047 }
10391048
1049 fn emitTrivial(self: *EmitZIR, src: usize, comptime T: type) Allocator.Error!*Inst {
1050 const new_inst = try self.arena.allocator.create(T);
1051 new_inst.* = .{
1052 .base = .{ .src = src, .tag = T.base_tag },
1053 .positionals = .{},
1054 .kw_args = .{},
1055 };
1056 return &new_inst.base;
1057 }
1058
1059 fn emitBody(
1060 self: *EmitZIR,
1061 body: ir.Module.Body,
1062 inst_table: *std.AutoHashMap(*ir.Inst, *Inst),
1063 instructions: *std.ArrayList(*Inst),
1064 ) Allocator.Error!void {
1065 for (body.instructions) |inst| {
1066 const new_inst = switch (inst.tag) {
1067 .breakpoint => try self.emitTrivial(inst.src, Inst.Breakpoint),
1068 .unreach => try self.emitTrivial(inst.src, Inst.Unreachable),
1069 .ret => try self.emitTrivial(inst.src, Inst.Return),
1070 .constant => unreachable, // excluded from function bodies
1071 .assembly => blk: {
1072 const old_inst = inst.cast(ir.Inst.Assembly).?;
1073 const new_inst = try self.arena.allocator.create(Inst.Asm);
1074
1075 const inputs = try self.arena.allocator.alloc(*Inst, old_inst.args.inputs.len);
1076 for (inputs) |*elem, i| {
1077 elem.* = try self.emitStringLiteral(inst.src, old_inst.args.inputs[i]);
1078 }
1079
1080 const clobbers = try self.arena.allocator.alloc(*Inst, old_inst.args.clobbers.len);
1081 for (clobbers) |*elem, i| {
1082 elem.* = try self.emitStringLiteral(inst.src, old_inst.args.clobbers[i]);
1083 }
1084
1085 const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len);
1086 for (args) |*elem, i| {
1087 elem.* = try self.resolveInst(inst_table, old_inst.args.args[i]);
1088 }
1089
1090 new_inst.* = .{
1091 .base = .{ .src = inst.src, .tag = Inst.Asm.base_tag },
1092 .positionals = .{
1093 .asm_source = try self.emitStringLiteral(inst.src, old_inst.args.asm_source),
1094 .return_type = try self.emitType(inst.src, inst.ty),
1095 },
1096 .kw_args = .{
1097 .@"volatile" = old_inst.args.is_volatile,
1098 .output = if (old_inst.args.output) |o|
1099 try self.emitStringLiteral(inst.src, o)
1100 else
1101 null,
1102 .inputs = inputs,
1103 .clobbers = clobbers,
1104 .args = args,
1105 },
1106 };
1107 break :blk &new_inst.base;
1108 },
1109 .ptrtoint => blk: {
1110 const old_inst = inst.cast(ir.Inst.PtrToInt).?;
1111 const new_inst = try self.arena.allocator.create(Inst.PtrToInt);
1112 new_inst.* = .{
1113 .base = .{ .src = inst.src, .tag = Inst.PtrToInt.base_tag },
1114 .positionals = .{
1115 .ptr = try self.resolveInst(inst_table, old_inst.args.ptr),
1116 },
1117 .kw_args = .{},
1118 };
1119 break :blk &new_inst.base;
1120 },
1121 .bitcast => blk: {
1122 const old_inst = inst.cast(ir.Inst.BitCast).?;
1123 const new_inst = try self.arena.allocator.create(Inst.BitCast);
1124 new_inst.* = .{
1125 .base = .{ .src = inst.src, .tag = Inst.BitCast.base_tag },
1126 .positionals = .{
1127 .dest_type = try self.emitType(inst.src, inst.ty),
1128 .operand = try self.resolveInst(inst_table, old_inst.args.operand),
1129 },
1130 .kw_args = .{},
1131 };
1132 break :blk &new_inst.base;
1133 },
1134 .cmp => blk: {
1135 const old_inst = inst.cast(ir.Inst.Cmp).?;
1136 const new_inst = try self.arena.allocator.create(Inst.Cmp);
1137 new_inst.* = .{
1138 .base = .{ .src = inst.src, .tag = Inst.Cmp.base_tag },
1139 .positionals = .{
1140 .lhs = try self.resolveInst(inst_table, old_inst.args.lhs),
1141 .rhs = try self.resolveInst(inst_table, old_inst.args.rhs),
1142 .op = old_inst.args.op,
1143 },
1144 .kw_args = .{},
1145 };
1146 break :blk &new_inst.base;
1147 },
1148 .condbr => blk: {
1149 const old_inst = inst.cast(ir.Inst.CondBr).?;
1150
1151 var true_body = std.ArrayList(*Inst).init(self.allocator);
1152 var false_body = std.ArrayList(*Inst).init(self.allocator);
1153
1154 defer true_body.deinit();
1155 defer false_body.deinit();
1156
1157 try self.emitBody(old_inst.args.true_body, inst_table, &true_body);
1158 try self.emitBody(old_inst.args.false_body, inst_table, &false_body);
1159
1160 const new_inst = try self.arena.allocator.create(Inst.CondBr);
1161 new_inst.* = .{
1162 .base = .{ .src = inst.src, .tag = Inst.CondBr.base_tag },
1163 .positionals = .{
1164 .condition = try self.resolveInst(inst_table, old_inst.args.condition),
1165 .true_body = .{ .instructions = true_body.toOwnedSlice() },
1166 .false_body = .{ .instructions = false_body.toOwnedSlice() },
1167 },
1168 .kw_args = .{},
1169 };
1170 break :blk &new_inst.base;
1171 },
1172 .isnull => blk: {
1173 const old_inst = inst.cast(ir.Inst.IsNull).?;
1174 const new_inst = try self.arena.allocator.create(Inst.IsNull);
1175 new_inst.* = .{
1176 .base = .{ .src = inst.src, .tag = Inst.IsNull.base_tag },
1177 .positionals = .{
1178 .operand = try self.resolveInst(inst_table, old_inst.args.operand),
1179 },
1180 .kw_args = .{},
1181 };
1182 break :blk &new_inst.base;
1183 },
1184 .isnonnull => blk: {
1185 const old_inst = inst.cast(ir.Inst.IsNonNull).?;
1186 const new_inst = try self.arena.allocator.create(Inst.IsNonNull);
1187 new_inst.* = .{
1188 .base = .{ .src = inst.src, .tag = Inst.IsNonNull.base_tag },
1189 .positionals = .{
1190 .operand = try self.resolveInst(inst_table, old_inst.args.operand),
1191 },
1192 .kw_args = .{},
1193 };
1194 break :blk &new_inst.base;
1195 },
1196 };
1197 try instructions.append(new_inst);
1198 try inst_table.putNoClobber(inst, new_inst);
1199 }
1200 }
1201
10401202 fn emitType(self: *EmitZIR, src: usize, ty: Type) Allocator.Error!*Inst {
10411203 switch (ty.tag()) {
10421204 .isize => return self.emitPrimitiveType(src, .isize),
src-self-hosted/link.zig+54-21
......@@ -7,11 +7,6 @@ const fs = std.fs;
77const elf = std.elf;
88const codegen = @import("codegen.zig");
99
10/// On common systems with a 0o022 umask, 0o777 will still result in a file created
11/// with 0o755 permissions, but it works appropriately if the system is configured
12/// more leniently. As another data point, C's fopen seems to open files with the
13/// 666 mode.
14const executable_mode = 0o777;
1510const default_entry_addr = 0x8000000;
1611
1712pub const ErrorMsg = struct {
......@@ -35,29 +30,29 @@ pub const Result = struct {
3530/// If incremental linking fails, falls back to truncating the file and rewriting it.
3631/// A malicious file is detected as incremental link failure and does not cause Illegal Behavior.
3732/// This operation is not atomic.
38pub fn updateExecutableFilePath(
33pub fn updateFilePath(
3934 allocator: *Allocator,
4035 module: ir.Module,
4136 dir: fs.Dir,
4237 sub_path: []const u8,
4338) !Result {
44 const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = executable_mode });
39 const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = determineMode(module) });
4540 defer file.close();
4641
47 return updateExecutableFile(allocator, module, file);
42 return updateFile(allocator, module, file);
4843}
4944
5045/// Atomically overwrites the old file, if present.
51pub fn writeExecutableFilePath(
46pub fn writeFilePath(
5247 allocator: *Allocator,
5348 module: ir.Module,
5449 dir: fs.Dir,
5550 sub_path: []const u8,
5651) !Result {
57 const af = try dir.atomicFile(sub_path, .{ .mode = executable_mode });
52 const af = try dir.atomicFile(sub_path, .{ .mode = determineMode(module) });
5853 defer af.deinit();
5954
60 const result = try writeExecutableFile(allocator, module, af.file);
55 const result = try writeFile(allocator, module, af.file);
6156 try af.finish();
6257 return result;
6358}
......@@ -67,10 +62,10 @@ pub fn writeExecutableFilePath(
6762/// Returns an error if `file` is not already open with +read +write +seek abilities.
6863/// A malicious file is detected as incremental link failure and does not cause Illegal Behavior.
6964/// This operation is not atomic.
70pub fn updateExecutableFile(allocator: *Allocator, module: ir.Module, file: fs.File) !Result {
71 return updateExecutableFileInner(allocator, module, file) catch |err| switch (err) {
65pub fn updateFile(allocator: *Allocator, module: ir.Module, file: fs.File) !Result {
66 return updateFileInner(allocator, module, file) catch |err| switch (err) {
7267 error.IncrFailed => {
73 return writeExecutableFile(allocator, module, file);
68 return writeFile(allocator, module, file);
7469 },
7570 else => |e| return e,
7671 };
......@@ -436,7 +431,7 @@ const Update = struct {
436431 },
437432 }
438433 }
439 if (self.entry_addr == null) {
434 if (self.entry_addr == null and self.module.output_mode == .Exe) {
440435 const msg = try std.fmt.allocPrint(self.errors.allocator, "no entry point found", .{});
441436 errdefer self.errors.allocator.free(msg);
442437 try self.errors.append(.{
......@@ -485,7 +480,15 @@ const Update = struct {
485480
486481 assert(index == 16);
487482
488 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(elf.ET.EXEC), endian);
483 const elf_type = switch (self.module.output_mode) {
484 .Exe => elf.ET.EXEC,
485 .Obj => elf.ET.REL,
486 .Lib => switch (self.module.link_mode) {
487 .Static => elf.ET.REL,
488 .Dynamic => elf.ET.DYN,
489 },
490 };
491 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(elf_type), endian);
489492 index += 2;
490493
491494 const machine = self.module.target.cpu.arch.toElfMachine();
......@@ -496,10 +499,11 @@ const Update = struct {
496499 mem.writeInt(u32, hdr_buf[index..][0..4], 1, endian);
497500 index += 4;
498501
502 const e_entry = if (elf_type == .REL) 0 else self.entry_addr.?;
503
499504 switch (ptr_width) {
500505 .p32 => {
501 // e_entry
502 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.entry_addr.?), endian);
506 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, e_entry), endian);
503507 index += 4;
504508
505509 // e_phoff
......@@ -512,7 +516,7 @@ const Update = struct {
512516 },
513517 .p64 => {
514518 // e_entry
515 mem.writeInt(u64, hdr_buf[index..][0..8], self.entry_addr.?, endian);
519 mem.writeInt(u64, hdr_buf[index..][0..8], e_entry, endian);
516520 index += 8;
517521
518522 // e_phoff
......@@ -750,7 +754,20 @@ const Update = struct {
750754
751755/// Truncates the existing file contents and overwrites the contents.
752756/// Returns an error if `file` is not already open with +read +write +seek abilities.
753pub fn writeExecutableFile(allocator: *Allocator, module: ir.Module, file: fs.File) !Result {
757pub fn writeFile(allocator: *Allocator, module: ir.Module, file: fs.File) !Result {
758 switch (module.output_mode) {
759 .Exe => {},
760 .Obj => {},
761 .Lib => return error.TODOImplementWritingLibFiles,
762 }
763 switch (module.object_format) {
764 .unknown => unreachable, // TODO remove this tag from the enum
765 .coff => return error.TODOImplementWritingCOFF,
766 .elf => {},
767 .macho => return error.TODOImplementWritingMachO,
768 .wasm => return error.TODOImplementWritingWasmObjects,
769 }
770
754771 var update = Update{
755772 .file = file,
756773 .module = &module,
......@@ -778,7 +795,7 @@ pub fn writeExecutableFile(allocator: *Allocator, module: ir.Module, file: fs.Fi
778795}
779796
780797/// Returns error.IncrFailed if incremental update could not be performed.
781fn updateExecutableFileInner(allocator: *Allocator, module: ir.Module, file: fs.File) !Result {
798fn updateFileInner(allocator: *Allocator, module: ir.Module, file: fs.File) !Result {
782799 //var ehdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
783800
784801 // TODO implement incremental linking
......@@ -822,3 +839,19 @@ fn sectHeaderTo32(shdr: elf.Elf64_Shdr) elf.Elf32_Shdr {
822839 .sh_entsize = @intCast(u32, shdr.sh_entsize),
823840 };
824841}
842
843fn determineMode(module: ir.Module) fs.File.Mode {
844 // On common systems with a 0o022 umask, 0o777 will still result in a file created
845 // with 0o755 permissions, but it works appropriately if the system is configured
846 // more leniently. As another data point, C's fopen seems to open files with the
847 // 666 mode.
848 const executable_mode = if (std.Target.current.os.tag == .windows) 0 else 0o777;
849 switch (module.output_mode) {
850 .Lib => return switch (module.link_mode) {
851 .Dynamic => executable_mode,
852 .Static => fs.File.default_mode,
853 },
854 .Exe => return executable_mode,
855 .Obj => return fs.File.default_mode,
856 }
857}
src-self-hosted/test.zig+209-198
......@@ -1,237 +1,248 @@
11const std = @import("std");
2const mem = std.mem;
3const Target = std.Target;
4const Compilation = @import("compilation.zig").Compilation;
5const introspect = @import("introspect.zig");
6const testing = std.testing;
7const errmsg = @import("errmsg.zig");
8const ZigCompiler = @import("compilation.zig").ZigCompiler;
2const link = @import("link.zig");
3const ir = @import("ir.zig");
4const Allocator = std.mem.Allocator;
95
10var ctx: TestContext = undefined;
6var global_ctx: TestContext = undefined;
117
12test "stage2" {
13 // TODO provide a way to run tests in evented I/O mode
14 if (!std.io.is_async) return error.SkipZigTest;
8test "self-hosted" {
9 try global_ctx.init();
10 defer global_ctx.deinit();
1511
16 // TODO https://github.com/ziglang/zig/issues/1364
17 // TODO https://github.com/ziglang/zig/issues/3117
18 if (true) return error.SkipZigTest;
12 try @import("stage2_tests").addCases(&global_ctx);
1913
20 try ctx.init();
21 defer ctx.deinit();
22
23 try @import("stage2_tests").addCases(&ctx);
24
25 try ctx.run();
14 try global_ctx.run();
2615}
2716
28const file1 = "1.zig";
29// TODO https://github.com/ziglang/zig/issues/3783
30const allocator = std.heap.page_allocator;
31
3217pub const TestContext = struct {
33 zig_compiler: ZigCompiler,
34 zig_lib_dir: []u8,
35 file_index: std.atomic.Int(usize),
36 group: std.event.Group(anyerror!void),
37 any_err: anyerror!void,
18 zir_cmp_output_cases: std.ArrayList(ZIRCompareOutputCase),
19 zir_transform_cases: std.ArrayList(ZIRTransformCase),
20
21 pub const ZIRCompareOutputCase = struct {
22 name: []const u8,
23 src: [:0]const u8,
24 expected_stdout: []const u8,
25 };
26
27 pub const ZIRTransformCase = struct {
28 name: []const u8,
29 src: [:0]const u8,
30 expected_zir: []const u8,
31 };
32
33 pub fn addZIRCompareOutput(
34 ctx: *TestContext,
35 name: []const u8,
36 src: [:0]const u8,
37 expected_stdout: []const u8,
38 ) void {
39 ctx.zir_cmp_output_cases.append(.{
40 .name = name,
41 .src = src,
42 .expected_stdout = expected_stdout,
43 }) catch unreachable;
44 }
3845
39 const tmp_dir_name = "stage2_test_tmp";
46 pub fn addZIRTransform(
47 ctx: *TestContext,
48 name: []const u8,
49 src: [:0]const u8,
50 expected_zir: []const u8,
51 ) void {
52 ctx.zir_transform_cases.append(.{
53 .name = name,
54 .src = src,
55 .expected_zir = expected_zir,
56 }) catch unreachable;
57 }
4058
4159 fn init(self: *TestContext) !void {
42 self.* = TestContext{
43 .any_err = {},
44 .zig_compiler = undefined,
45 .zig_lib_dir = undefined,
46 .group = undefined,
47 .file_index = std.atomic.Int(usize).init(0),
60 self.* = .{
61 .zir_cmp_output_cases = std.ArrayList(ZIRCompareOutputCase).init(std.heap.page_allocator),
62 .zir_transform_cases = std.ArrayList(ZIRTransformCase).init(std.heap.page_allocator),
4863 };
49
50 self.zig_compiler = try ZigCompiler.init(allocator);
51 errdefer self.zig_compiler.deinit();
52
53 self.group = std.event.Group(anyerror!void).init(allocator);
54 errdefer self.group.wait() catch {};
55
56 self.zig_lib_dir = try introspect.resolveZigLibDir(allocator);
57 errdefer allocator.free(self.zig_lib_dir);
58
59 try std.fs.cwd().makePath(tmp_dir_name);
60 errdefer std.fs.cwd().deleteTree(tmp_dir_name) catch {};
6164 }
6265
6366 fn deinit(self: *TestContext) void {
64 std.fs.cwd().deleteTree(tmp_dir_name) catch {};
65 allocator.free(self.zig_lib_dir);
66 self.zig_compiler.deinit();
67 self.zir_cmp_output_cases.deinit();
68 self.zir_transform_cases.deinit();
69 self.* = undefined;
6770 }
6871
6972 fn run(self: *TestContext) !void {
70 std.event.Loop.startCpuBoundOperation();
71 self.any_err = self.group.wait();
72 return self.any_err;
73 var progress = std.Progress{};
74 const root_node = try progress.start("zir", self.zir_cmp_output_cases.items.len +
75 self.zir_transform_cases.items.len);
76 defer root_node.end();
77
78 const native_info = try std.zig.system.NativeTargetInfo.detect(std.heap.page_allocator, .{});
79
80 for (self.zir_cmp_output_cases.items) |case| {
81 std.testing.base_allocator_instance.reset();
82 try self.runOneZIRCmpOutputCase(std.testing.allocator, root_node, case, native_info.target);
83 try std.testing.allocator_instance.validate();
84 }
85 for (self.zir_transform_cases.items) |case| {
86 std.testing.base_allocator_instance.reset();
87 try self.runOneZIRTransformCase(std.testing.allocator, root_node, case, native_info.target);
88 try std.testing.allocator_instance.validate();
89 }
7390 }
7491
75 fn testCompileError(
92 fn runOneZIRCmpOutputCase(
7693 self: *TestContext,
77 source: []const u8,
78 path: []const u8,
79 line: usize,
80 column: usize,
81 msg: []const u8,
94 allocator: *Allocator,
95 root_node: *std.Progress.Node,
96 case: ZIRCompareOutputCase,
97 target: std.Target,
8298 ) !void {
83 var file_index_buf: [20]u8 = undefined;
84 const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", .{self.file_index.incr()});
85 const file1_path = try std.fs.path.join(allocator, [_][]const u8{ tmp_dir_name, file_index, file1 });
99 var tmp = std.testing.tmpDir(.{ .share_with_child_process = true });
100 defer tmp.cleanup();
86101
87 if (std.fs.path.dirname(file1_path)) |dirname| {
88 try std.fs.cwd().makePath(dirname);
89 }
102 var prg_node = root_node.start(case.name, 4);
103 prg_node.activate();
104 defer prg_node.end();
90105
91 try std.fs.cwd().writeFile(file1_path, source);
106 var zir_module = x: {
107 var parse_node = prg_node.start("parse", null);
108 parse_node.activate();
109 defer parse_node.end();
92110
93 var comp = try Compilation.create(
94 &self.zig_compiler,
95 "test",
96 file1_path,
97 .Native,
98 .Obj,
99 .Debug,
100 true, // is_static
101 self.zig_lib_dir,
102 );
103 errdefer comp.destroy();
104
105 comp.start();
111 break :x try ir.text.parse(allocator, case.src);
112 };
113 defer zir_module.deinit(allocator);
114 if (zir_module.errors.len != 0) {
115 debugPrintErrors(case.src, zir_module.errors);
116 return error.ParseFailure;
117 }
106118
107 try self.group.call(getModuleEvent, comp, source, path, line, column, msg);
108 }
119 var analyzed_module = x: {
120 var analyze_node = prg_node.start("analyze", null);
121 analyze_node.activate();
122 defer analyze_node.end();
123
124 break :x try ir.analyze(allocator, zir_module, .{
125 .target = target,
126 .output_mode = .Exe,
127 .link_mode = .Static,
128 .optimize_mode = .Debug,
129 });
130 };
131 defer analyzed_module.deinit(allocator);
132 if (analyzed_module.errors.len != 0) {
133 debugPrintErrors(case.src, analyzed_module.errors);
134 return error.ParseFailure;
135 }
109136
110 fn testCompareOutputLibC(
111 self: *TestContext,
112 source: []const u8,
113 expected_output: []const u8,
114 ) !void {
115 var file_index_buf: [20]u8 = undefined;
116 const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", .{self.file_index.incr()});
117 const file1_path = try std.fs.path.join(allocator, [_][]const u8{ tmp_dir_name, file_index, file1 });
137 var link_result = x: {
138 var link_node = prg_node.start("link", null);
139 link_node.activate();
140 defer link_node.end();
118141
119 const output_file = try std.fmt.allocPrint(allocator, "{}-out{}", .{ file1_path, (Target{ .Native = {} }).exeFileExt() });
120 if (std.fs.path.dirname(file1_path)) |dirname| {
121 try std.fs.cwd().makePath(dirname);
142 break :x try link.updateFilePath(allocator, analyzed_module, tmp.dir, "a.out");
143 };
144 defer link_result.deinit(allocator);
145 if (link_result.errors.len != 0) {
146 debugPrintErrors(case.src, link_result.errors);
147 return error.LinkFailure;
122148 }
123149
124 try std.fs.cwd().writeFile(file1_path, source);
125
126 var comp = try Compilation.create(
127 &self.zig_compiler,
128 "test",
129 file1_path,
130 .Native,
131 .Exe,
132 .Debug,
133 false,
134 self.zig_lib_dir,
135 );
136 errdefer comp.destroy();
137
138 _ = try comp.addLinkLib("c", true);
139 comp.link_out_file = output_file;
140 comp.start();
141
142 try self.group.call(getModuleEventSuccess, comp, output_file, expected_output);
143 }
150 var exec_result = x: {
151 var exec_node = prg_node.start("execute", null);
152 exec_node.activate();
153 defer exec_node.end();
144154
145 async fn getModuleEventSuccess(
146 comp: *Compilation,
147 exe_file: []const u8,
148 expected_output: []const u8,
149 ) anyerror!void {
150 defer comp.destroy();
151 const build_event = comp.events.get();
152
153 switch (build_event) {
154 .Ok => {
155 const argv = [_][]const u8{exe_file};
156 // TODO use event loop
157 const child = try std.ChildProcess.exec(.{
158 .allocator = allocator,
159 .argv = argv,
160 .max_output_bytes = 1024 * 1024,
161 });
162 switch (child.term) {
163 .Exited => |code| {
164 if (code != 0) {
165 return error.BadReturnCode;
166 }
167 },
168 else => {
169 return error.Crashed;
170 },
171 }
172 if (!mem.eql(u8, child.stdout, expected_output)) {
173 return error.OutputMismatch;
174 }
175 },
176 .Error => @panic("Cannot return error: https://github.com/ziglang/zig/issues/3190"), // |err| return err,
177 .Fail => |msgs| {
178 const stderr = std.io.getStdErr();
179 try stderr.write("build incorrectly failed:\n");
180 for (msgs) |msg| {
181 defer msg.destroy();
182 try msg.printToFile(stderr, .Auto);
155 break :x try std.ChildProcess.exec(.{
156 .allocator = allocator,
157 .argv = &[_][]const u8{"./a.out"},
158 .cwd_dir = tmp.dir,
159 });
160 };
161 defer allocator.free(exec_result.stdout);
162 defer allocator.free(exec_result.stderr);
163 switch (exec_result.term) {
164 .Exited => |code| {
165 if (code != 0) {
166 std.debug.warn("elf file exited with code {}\n", .{code});
167 return error.BinaryBadExitCode;
183168 }
184169 },
170 else => return error.BinaryCrashed,
185171 }
172 std.testing.expectEqualSlices(u8, case.expected_stdout, exec_result.stdout);
186173 }
187174
188 async fn getModuleEvent(
189 comp: *Compilation,
190 source: []const u8,
191 path: []const u8,
192 line: usize,
193 column: usize,
194 text: []const u8,
195 ) anyerror!void {
196 defer comp.destroy();
197 const build_event = comp.events.get();
198
199 switch (build_event) {
200 .Ok => {
201 @panic("build incorrectly succeeded");
202 },
203 .Error => |err| {
204 @panic("build incorrectly failed");
205 },
206 .Fail => |msgs| {
207 testing.expect(msgs.len != 0);
208 for (msgs) |msg| {
209 if (mem.endsWith(u8, msg.realpath, path) and mem.eql(u8, msg.text, text)) {
210 const span = msg.getSpan();
211 const first_token = msg.getTree().tokens.at(span.first);
212 const last_token = msg.getTree().tokens.at(span.first);
213 const start_loc = msg.getTree().tokenLocationPtr(0, first_token);
214 if (start_loc.line + 1 == line and start_loc.column + 1 == column) {
215 return;
216 }
217 }
218 }
219 std.debug.warn("\n=====source:=======\n{}\n====expected:========\n{}:{}:{}: error: {}\n", .{
220 source,
221 path,
222 line,
223 column,
224 text,
225 });
226 std.debug.warn("\n====found:========\n", .{});
227 const stderr = std.io.getStdErr();
228 for (msgs) |msg| {
229 defer msg.destroy();
230 try msg.printToFile(stderr, errmsg.Color.Auto);
231 }
232 std.debug.warn("============\n", .{});
233 return error.TestFailed;
234 },
175 fn runOneZIRTransformCase(
176 self: *TestContext,
177 allocator: *Allocator,
178 root_node: *std.Progress.Node,
179 case: ZIRTransformCase,
180 target: std.Target,
181 ) !void {
182 var prg_node = root_node.start(case.name, 4);
183 prg_node.activate();
184 defer prg_node.end();
185
186 var parse_node = prg_node.start("parse", null);
187 parse_node.activate();
188 var zir_module = try ir.text.parse(allocator, case.src);
189 defer zir_module.deinit(allocator);
190 if (zir_module.errors.len != 0) {
191 debugPrintErrors(case.src, zir_module.errors);
192 return error.ParseFailure;
235193 }
194 parse_node.end();
195
196 var analyze_node = prg_node.start("analyze", null);
197 analyze_node.activate();
198 var analyzed_module = try ir.analyze(allocator, zir_module, .{
199 .target = target,
200 .output_mode = .Obj,
201 .link_mode = .Static,
202 .optimize_mode = .Debug,
203 });
204 defer analyzed_module.deinit(allocator);
205 if (analyzed_module.errors.len != 0) {
206 debugPrintErrors(case.src, analyzed_module.errors);
207 return error.ParseFailure;
208 }
209 analyze_node.end();
210
211 var emit_node = prg_node.start("emit", null);
212 emit_node.activate();
213 var new_zir_module = try ir.text.emit_zir(allocator, analyzed_module);
214 defer new_zir_module.deinit(allocator);
215 emit_node.end();
216
217 var write_node = prg_node.start("write", null);
218 write_node.activate();
219 var out_zir = std.ArrayList(u8).init(allocator);
220 defer out_zir.deinit();
221 try new_zir_module.writeToStream(allocator, out_zir.outStream());
222 write_node.end();
223
224 std.testing.expectEqualSlices(u8, case.expected_zir, out_zir.items);
236225 }
237226};
227
228fn debugPrintErrors(src: []const u8, errors: var) void {
229 std.debug.warn("\n", .{});
230 var nl = true;
231 var line: usize = 1;
232 for (src) |byte| {
233 if (nl) {
234 std.debug.warn("{: >3}| ", .{line});
235 nl = false;
236 }
237 if (byte == '\n') {
238 nl = true;
239 line += 1;
240 }
241 std.debug.warn("{c}", .{byte});
242 }
243 std.debug.warn("\n", .{});
244 for (errors) |err_msg| {
245 const loc = std.zig.findLineColumn(src, err_msg.byte_offset);
246 std.debug.warn("{}:{}: error: {}\n", .{ loc.line + 1, loc.column + 1, err_msg.msg });
247 }
248}
src-self-hosted/translate_c.zig+16-14
......@@ -3913,18 +3913,20 @@ fn transCreateNodeAPInt(c: *Context, int: *const ZigClangAPSInt) !*ast.Node {
39133913 };
39143914 var aps_int = int;
39153915 const is_negative = ZigClangAPSInt_isSigned(int) and ZigClangAPSInt_isNegative(int);
3916 if (is_negative)
3917 aps_int = ZigClangAPSInt_negate(aps_int);
3918 var big = try math.big.Int.initCapacity(c.a(), num_limbs);
3919 if (is_negative)
3920 big.negate();
3921 defer big.deinit();
3916 if (is_negative) aps_int = ZigClangAPSInt_negate(aps_int);
3917 defer if (is_negative) {
3918 ZigClangAPSInt_free(aps_int);
3919 };
3920
3921 const limbs = try c.a().alloc(math.big.Limb, num_limbs);
3922 defer c.a().free(limbs);
3923
39223924 const data = ZigClangAPSInt_getRawData(aps_int);
3923 switch (@sizeOf(std.math.big.Limb)) {
3925 switch (@sizeOf(math.big.Limb)) {
39243926 8 => {
39253927 var i: usize = 0;
39263928 while (i < num_limbs) : (i += 1) {
3927 big.limbs[i] = data[i];
3929 limbs[i] = data[i];
39283930 }
39293931 },
39303932 4 => {
......@@ -3934,23 +3936,23 @@ fn transCreateNodeAPInt(c: *Context, int: *const ZigClangAPSInt) !*ast.Node {
39343936 limb_i += 2;
39353937 data_i += 1;
39363938 }) {
3937 big.limbs[limb_i] = @truncate(u32, data[data_i]);
3938 big.limbs[limb_i + 1] = @truncate(u32, data[data_i] >> 32);
3939 limbs[limb_i] = @truncate(u32, data[data_i]);
3940 limbs[limb_i + 1] = @truncate(u32, data[data_i] >> 32);
39393941 }
39403942 },
39413943 else => @compileError("unimplemented"),
39423944 }
3943 const str = big.toString(c.a(), 10, false) catch |err| switch (err) {
3945
3946 const big: math.big.int.Const = .{ .limbs = limbs, .positive = !is_negative };
3947 const str = big.toStringAlloc(c.a(), 10, false) catch |err| switch (err) {
39443948 error.OutOfMemory => return error.OutOfMemory,
3945 else => unreachable,
39463949 };
3950 defer c.a().free(str);
39473951 const token = try appendToken(c, .IntegerLiteral, str);
39483952 const node = try c.a().create(ast.Node.IntegerLiteral);
39493953 node.* = .{
39503954 .token = token,
39513955 };
3952 if (is_negative)
3953 ZigClangAPSInt_free(aps_int);
39543956 return &node.base;
39553957}
39563958
src-self-hosted/type.zig+473-204
......@@ -20,37 +20,40 @@ pub const Type = extern union {
2020
2121 pub fn zigTypeTag(self: Type) std.builtin.TypeId {
2222 switch (self.tag()) {
23 .@"u8",
24 .@"i8",
25 .@"isize",
26 .@"usize",
27 .@"c_short",
28 .@"c_ushort",
29 .@"c_int",
30 .@"c_uint",
31 .@"c_long",
32 .@"c_ulong",
33 .@"c_longlong",
34 .@"c_ulonglong",
35 .@"c_longdouble",
23 .u8,
24 .i8,
25 .isize,
26 .usize,
27 .c_short,
28 .c_ushort,
29 .c_int,
30 .c_uint,
31 .c_long,
32 .c_ulong,
33 .c_longlong,
34 .c_ulonglong,
35 .c_longdouble,
36 .int_signed,
37 .int_unsigned,
3638 => return .Int,
3739
38 .@"f16",
39 .@"f32",
40 .@"f64",
41 .@"f128",
40 .f16,
41 .f32,
42 .f64,
43 .f128,
4244 => return .Float,
4345
44 .@"c_void" => return .Opaque,
45 .@"bool" => return .Bool,
46 .@"void" => return .Void,
47 .@"type" => return .Type,
48 .@"anyerror" => return .ErrorSet,
49 .@"comptime_int" => return .ComptimeInt,
50 .@"comptime_float" => return .ComptimeFloat,
51 .@"noreturn" => return .NoReturn,
46 .c_void => return .Opaque,
47 .bool => return .Bool,
48 .void => return .Void,
49 .type => return .Type,
50 .anyerror => return .ErrorSet,
51 .comptime_int => return .ComptimeInt,
52 .comptime_float => return .ComptimeFloat,
53 .noreturn => return .NoReturn,
5254
5355 .fn_naked_noreturn_no_args => return .Fn,
56 .fn_ccc_void_no_args => return .Fn,
5457
5558 .array, .array_u8_sentinel_0 => return .Array,
5659 .single_const_pointer => return .Pointer,
......@@ -153,35 +156,36 @@ pub const Type = extern union {
153156 while (true) {
154157 const t = ty.tag();
155158 switch (t) {
156 .@"u8",
157 .@"i8",
158 .@"isize",
159 .@"usize",
160 .@"c_short",
161 .@"c_ushort",
162 .@"c_int",
163 .@"c_uint",
164 .@"c_long",
165 .@"c_ulong",
166 .@"c_longlong",
167 .@"c_ulonglong",
168 .@"c_longdouble",
169 .@"c_void",
170 .@"f16",
171 .@"f32",
172 .@"f64",
173 .@"f128",
174 .@"bool",
175 .@"void",
176 .@"type",
177 .@"anyerror",
178 .@"comptime_int",
179 .@"comptime_float",
180 .@"noreturn",
159 .u8,
160 .i8,
161 .isize,
162 .usize,
163 .c_short,
164 .c_ushort,
165 .c_int,
166 .c_uint,
167 .c_long,
168 .c_ulong,
169 .c_longlong,
170 .c_ulonglong,
171 .c_longdouble,
172 .c_void,
173 .f16,
174 .f32,
175 .f64,
176 .f128,
177 .bool,
178 .void,
179 .type,
180 .anyerror,
181 .comptime_int,
182 .comptime_float,
183 .noreturn,
181184 => return out_stream.writeAll(@tagName(t)),
182185
183186 .const_slice_u8 => return out_stream.writeAll("[]const u8"),
184187 .fn_naked_noreturn_no_args => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
188 .fn_ccc_void_no_args => return out_stream.writeAll("fn() callconv(.C) void"),
185189 .single_const_pointer_to_comptime_int => return out_stream.writeAll("*const comptime_int"),
186190
187191 .array_u8_sentinel_0 => {
......@@ -200,6 +204,14 @@ pub const Type = extern union {
200204 ty = payload.pointee_type;
201205 continue;
202206 },
207 .int_signed => {
208 const payload = @fieldParentPtr(Payload.IntSigned, "base", ty.ptr_otherwise);
209 return out_stream.print("i{}", .{payload.bits});
210 },
211 .int_unsigned => {
212 const payload = @fieldParentPtr(Payload.IntUnsigned, "base", ty.ptr_otherwise);
213 return out_stream.print("u{}", .{payload.bits});
214 },
203215 }
204216 unreachable;
205217 }
......@@ -207,32 +219,33 @@ pub const Type = extern union {
207219
208220 pub fn toValue(self: Type, allocator: *Allocator) Allocator.Error!Value {
209221 switch (self.tag()) {
210 .@"u8" => return Value.initTag(.u8_type),
211 .@"i8" => return Value.initTag(.i8_type),
212 .@"isize" => return Value.initTag(.isize_type),
213 .@"usize" => return Value.initTag(.usize_type),
214 .@"c_short" => return Value.initTag(.c_short_type),
215 .@"c_ushort" => return Value.initTag(.c_ushort_type),
216 .@"c_int" => return Value.initTag(.c_int_type),
217 .@"c_uint" => return Value.initTag(.c_uint_type),
218 .@"c_long" => return Value.initTag(.c_long_type),
219 .@"c_ulong" => return Value.initTag(.c_ulong_type),
220 .@"c_longlong" => return Value.initTag(.c_longlong_type),
221 .@"c_ulonglong" => return Value.initTag(.c_ulonglong_type),
222 .@"c_longdouble" => return Value.initTag(.c_longdouble_type),
223 .@"c_void" => return Value.initTag(.c_void_type),
224 .@"f16" => return Value.initTag(.f16_type),
225 .@"f32" => return Value.initTag(.f32_type),
226 .@"f64" => return Value.initTag(.f64_type),
227 .@"f128" => return Value.initTag(.f128_type),
228 .@"bool" => return Value.initTag(.bool_type),
229 .@"void" => return Value.initTag(.void_type),
230 .@"type" => return Value.initTag(.type_type),
231 .@"anyerror" => return Value.initTag(.anyerror_type),
232 .@"comptime_int" => return Value.initTag(.comptime_int_type),
233 .@"comptime_float" => return Value.initTag(.comptime_float_type),
234 .@"noreturn" => return Value.initTag(.noreturn_type),
222 .u8 => return Value.initTag(.u8_type),
223 .i8 => return Value.initTag(.i8_type),
224 .isize => return Value.initTag(.isize_type),
225 .usize => return Value.initTag(.usize_type),
226 .c_short => return Value.initTag(.c_short_type),
227 .c_ushort => return Value.initTag(.c_ushort_type),
228 .c_int => return Value.initTag(.c_int_type),
229 .c_uint => return Value.initTag(.c_uint_type),
230 .c_long => return Value.initTag(.c_long_type),
231 .c_ulong => return Value.initTag(.c_ulong_type),
232 .c_longlong => return Value.initTag(.c_longlong_type),
233 .c_ulonglong => return Value.initTag(.c_ulonglong_type),
234 .c_longdouble => return Value.initTag(.c_longdouble_type),
235 .c_void => return Value.initTag(.c_void_type),
236 .f16 => return Value.initTag(.f16_type),
237 .f32 => return Value.initTag(.f32_type),
238 .f64 => return Value.initTag(.f64_type),
239 .f128 => return Value.initTag(.f128_type),
240 .bool => return Value.initTag(.bool_type),
241 .void => return Value.initTag(.void_type),
242 .type => return Value.initTag(.type_type),
243 .anyerror => return Value.initTag(.anyerror_type),
244 .comptime_int => return Value.initTag(.comptime_int_type),
245 .comptime_float => return Value.initTag(.comptime_float_type),
246 .noreturn => return Value.initTag(.noreturn_type),
235247 .fn_naked_noreturn_no_args => return Value.initTag(.fn_naked_noreturn_no_args_type),
248 .fn_ccc_void_no_args => return Value.initTag(.fn_ccc_void_no_args_type),
236249 .single_const_pointer_to_comptime_int => return Value.initTag(.single_const_pointer_to_comptime_int_type),
237250 .const_slice_u8 => return Value.initTag(.const_slice_u8_type),
238251 else => {
......@@ -245,35 +258,38 @@ pub const Type = extern union {
245258
246259 pub fn isSinglePointer(self: Type) bool {
247260 return switch (self.tag()) {
248 .@"u8",
249 .@"i8",
250 .@"isize",
251 .@"usize",
252 .@"c_short",
253 .@"c_ushort",
254 .@"c_int",
255 .@"c_uint",
256 .@"c_long",
257 .@"c_ulong",
258 .@"c_longlong",
259 .@"c_ulonglong",
260 .@"c_longdouble",
261 .@"f16",
262 .@"f32",
263 .@"f64",
264 .@"f128",
265 .@"c_void",
266 .@"bool",
267 .@"void",
268 .@"type",
269 .@"anyerror",
270 .@"comptime_int",
271 .@"comptime_float",
272 .@"noreturn",
261 .u8,
262 .i8,
263 .isize,
264 .usize,
265 .c_short,
266 .c_ushort,
267 .c_int,
268 .c_uint,
269 .c_long,
270 .c_ulong,
271 .c_longlong,
272 .c_ulonglong,
273 .c_longdouble,
274 .f16,
275 .f32,
276 .f64,
277 .f128,
278 .c_void,
279 .bool,
280 .void,
281 .type,
282 .anyerror,
283 .comptime_int,
284 .comptime_float,
285 .noreturn,
273286 .array,
274287 .array_u8_sentinel_0,
275288 .const_slice_u8,
276289 .fn_naked_noreturn_no_args,
290 .fn_ccc_void_no_args,
291 .int_unsigned,
292 .int_signed,
277293 => false,
278294
279295 .single_const_pointer,
......@@ -284,36 +300,39 @@ pub const Type = extern union {
284300
285301 pub fn isSlice(self: Type) bool {
286302 return switch (self.tag()) {
287 .@"u8",
288 .@"i8",
289 .@"isize",
290 .@"usize",
291 .@"c_short",
292 .@"c_ushort",
293 .@"c_int",
294 .@"c_uint",
295 .@"c_long",
296 .@"c_ulong",
297 .@"c_longlong",
298 .@"c_ulonglong",
299 .@"c_longdouble",
300 .@"f16",
301 .@"f32",
302 .@"f64",
303 .@"f128",
304 .@"c_void",
305 .@"bool",
306 .@"void",
307 .@"type",
308 .@"anyerror",
309 .@"comptime_int",
310 .@"comptime_float",
311 .@"noreturn",
303 .u8,
304 .i8,
305 .isize,
306 .usize,
307 .c_short,
308 .c_ushort,
309 .c_int,
310 .c_uint,
311 .c_long,
312 .c_ulong,
313 .c_longlong,
314 .c_ulonglong,
315 .c_longdouble,
316 .f16,
317 .f32,
318 .f64,
319 .f128,
320 .c_void,
321 .bool,
322 .void,
323 .type,
324 .anyerror,
325 .comptime_int,
326 .comptime_float,
327 .noreturn,
312328 .array,
313329 .array_u8_sentinel_0,
314330 .single_const_pointer,
315331 .single_const_pointer_to_comptime_int,
316332 .fn_naked_noreturn_no_args,
333 .fn_ccc_void_no_args,
334 .int_unsigned,
335 .int_signed,
317336 => false,
318337
319338 .const_slice_u8 => true,
......@@ -323,34 +342,37 @@ pub const Type = extern union {
323342 /// Asserts the type is a pointer type.
324343 pub fn pointerIsConst(self: Type) bool {
325344 return switch (self.tag()) {
326 .@"u8",
327 .@"i8",
328 .@"isize",
329 .@"usize",
330 .@"c_short",
331 .@"c_ushort",
332 .@"c_int",
333 .@"c_uint",
334 .@"c_long",
335 .@"c_ulong",
336 .@"c_longlong",
337 .@"c_ulonglong",
338 .@"c_longdouble",
339 .@"f16",
340 .@"f32",
341 .@"f64",
342 .@"f128",
343 .@"c_void",
344 .@"bool",
345 .@"void",
346 .@"type",
347 .@"anyerror",
348 .@"comptime_int",
349 .@"comptime_float",
350 .@"noreturn",
345 .u8,
346 .i8,
347 .isize,
348 .usize,
349 .c_short,
350 .c_ushort,
351 .c_int,
352 .c_uint,
353 .c_long,
354 .c_ulong,
355 .c_longlong,
356 .c_ulonglong,
357 .c_longdouble,
358 .f16,
359 .f32,
360 .f64,
361 .f128,
362 .c_void,
363 .bool,
364 .void,
365 .type,
366 .anyerror,
367 .comptime_int,
368 .comptime_float,
369 .noreturn,
351370 .array,
352371 .array_u8_sentinel_0,
353372 .fn_naked_noreturn_no_args,
373 .fn_ccc_void_no_args,
374 .int_unsigned,
375 .int_signed,
354376 => unreachable,
355377
356378 .single_const_pointer,
......@@ -363,32 +385,35 @@ pub const Type = extern union {
363385 /// Asserts the type is a pointer or array type.
364386 pub fn elemType(self: Type) Type {
365387 return switch (self.tag()) {
366 .@"u8",
367 .@"i8",
368 .@"isize",
369 .@"usize",
370 .@"c_short",
371 .@"c_ushort",
372 .@"c_int",
373 .@"c_uint",
374 .@"c_long",
375 .@"c_ulong",
376 .@"c_longlong",
377 .@"c_ulonglong",
378 .@"c_longdouble",
379 .@"f16",
380 .@"f32",
381 .@"f64",
382 .@"f128",
383 .@"c_void",
384 .@"bool",
385 .@"void",
386 .@"type",
387 .@"anyerror",
388 .@"comptime_int",
389 .@"comptime_float",
390 .@"noreturn",
388 .u8,
389 .i8,
390 .isize,
391 .usize,
392 .c_short,
393 .c_ushort,
394 .c_int,
395 .c_uint,
396 .c_long,
397 .c_ulong,
398 .c_longlong,
399 .c_ulonglong,
400 .c_longdouble,
401 .f16,
402 .f32,
403 .f64,
404 .f128,
405 .c_void,
406 .bool,
407 .void,
408 .type,
409 .anyerror,
410 .comptime_int,
411 .comptime_float,
412 .noreturn,
391413 .fn_naked_noreturn_no_args,
414 .fn_ccc_void_no_args,
415 .int_unsigned,
416 .int_signed,
392417 => unreachable,
393418
394419 .array => self.cast(Payload.Array).?.elem_type,
......@@ -398,7 +423,7 @@ pub const Type = extern union {
398423 };
399424 }
400425
401 /// Asserts the type is an array.
426 /// Asserts the type is an array or vector.
402427 pub fn arrayLen(self: Type) u64 {
403428 return switch (self.tag()) {
404429 .u8,
......@@ -427,9 +452,12 @@ pub const Type = extern union {
427452 .comptime_float,
428453 .noreturn,
429454 .fn_naked_noreturn_no_args,
455 .fn_ccc_void_no_args,
430456 .single_const_pointer,
431457 .single_const_pointer_to_comptime_int,
432458 .const_slice_u8,
459 .int_unsigned,
460 .int_signed,
433461 => unreachable,
434462
435463 .array => self.cast(Payload.Array).?.len,
......@@ -437,23 +465,67 @@ pub const Type = extern union {
437465 };
438466 }
439467
468 /// Returns true if and only if the type is a fixed-width, signed integer.
469 pub fn isSignedInt(self: Type) bool {
470 return switch (self.tag()) {
471 .f16,
472 .f32,
473 .f64,
474 .f128,
475 .c_longdouble,
476 .c_void,
477 .bool,
478 .void,
479 .type,
480 .anyerror,
481 .comptime_int,
482 .comptime_float,
483 .noreturn,
484 .fn_naked_noreturn_no_args,
485 .fn_ccc_void_no_args,
486 .array,
487 .single_const_pointer,
488 .single_const_pointer_to_comptime_int,
489 .array_u8_sentinel_0,
490 .const_slice_u8,
491 .int_unsigned,
492 .u8,
493 .usize,
494 .c_ushort,
495 .c_uint,
496 .c_ulong,
497 .c_ulonglong,
498 => false,
499
500 .int_signed,
501 .i8,
502 .isize,
503 .c_short,
504 .c_int,
505 .c_long,
506 .c_longlong,
507 => true,
508 };
509 }
510
440511 /// Asserts the type is a fixed-width integer.
441512 pub fn intInfo(self: Type, target: Target) struct { signed: bool, bits: u16 } {
442513 return switch (self.tag()) {
443 .@"f16",
444 .@"f32",
445 .@"f64",
446 .@"f128",
447 .@"c_longdouble",
448 .@"c_void",
449 .@"bool",
450 .@"void",
451 .@"type",
452 .@"anyerror",
453 .@"comptime_int",
454 .@"comptime_float",
455 .@"noreturn",
514 .f16,
515 .f32,
516 .f64,
517 .f128,
518 .c_longdouble,
519 .c_void,
520 .bool,
521 .void,
522 .type,
523 .anyerror,
524 .comptime_int,
525 .comptime_float,
526 .noreturn,
456527 .fn_naked_noreturn_no_args,
528 .fn_ccc_void_no_args,
457529 .array,
458530 .single_const_pointer,
459531 .single_const_pointer_to_comptime_int,
......@@ -461,18 +533,46 @@ pub const Type = extern union {
461533 .const_slice_u8,
462534 => unreachable,
463535
464 .@"u8" => .{ .signed = false, .bits = 8 },
465 .@"i8" => .{ .signed = true, .bits = 8 },
466 .@"usize" => .{ .signed = false, .bits = target.cpu.arch.ptrBitWidth() },
467 .@"isize" => .{ .signed = true, .bits = target.cpu.arch.ptrBitWidth() },
468 .@"c_short" => .{ .signed = true, .bits = CInteger.short.sizeInBits(target) },
469 .@"c_ushort" => .{ .signed = false, .bits = CInteger.ushort.sizeInBits(target) },
470 .@"c_int" => .{ .signed = true, .bits = CInteger.int.sizeInBits(target) },
471 .@"c_uint" => .{ .signed = false, .bits = CInteger.uint.sizeInBits(target) },
472 .@"c_long" => .{ .signed = true, .bits = CInteger.long.sizeInBits(target) },
473 .@"c_ulong" => .{ .signed = false, .bits = CInteger.ulong.sizeInBits(target) },
474 .@"c_longlong" => .{ .signed = true, .bits = CInteger.longlong.sizeInBits(target) },
475 .@"c_ulonglong" => .{ .signed = false, .bits = CInteger.ulonglong.sizeInBits(target) },
536 .int_unsigned => .{ .signed = false, .bits = self.cast(Payload.IntUnsigned).?.bits },
537 .int_signed => .{ .signed = true, .bits = self.cast(Payload.IntSigned).?.bits },
538 .u8 => .{ .signed = false, .bits = 8 },
539 .i8 => .{ .signed = true, .bits = 8 },
540 .usize => .{ .signed = false, .bits = target.cpu.arch.ptrBitWidth() },
541 .isize => .{ .signed = true, .bits = target.cpu.arch.ptrBitWidth() },
542 .c_short => .{ .signed = true, .bits = CType.short.sizeInBits(target) },
543 .c_ushort => .{ .signed = false, .bits = CType.ushort.sizeInBits(target) },
544 .c_int => .{ .signed = true, .bits = CType.int.sizeInBits(target) },
545 .c_uint => .{ .signed = false, .bits = CType.uint.sizeInBits(target) },
546 .c_long => .{ .signed = true, .bits = CType.long.sizeInBits(target) },
547 .c_ulong => .{ .signed = false, .bits = CType.ulong.sizeInBits(target) },
548 .c_longlong => .{ .signed = true, .bits = CType.longlong.sizeInBits(target) },
549 .c_ulonglong => .{ .signed = false, .bits = CType.ulonglong.sizeInBits(target) },
550 };
551 }
552
553 pub fn isFloat(self: Type) bool {
554 return switch (self.tag()) {
555 .f16,
556 .f32,
557 .f64,
558 .f128,
559 .c_longdouble,
560 => true,
561
562 else => false,
563 };
564 }
565
566 /// Asserts the type is a fixed-size float.
567 pub fn floatBits(self: Type, target: Target) u16 {
568 return switch (self.tag()) {
569 .f16 => 16,
570 .f32 => 32,
571 .f64 => 64,
572 .f128 => 128,
573 .c_longdouble => CType.longdouble.sizeInBits(target),
574
575 else => unreachable,
476576 };
477577 }
478578
......@@ -480,6 +580,7 @@ pub const Type = extern union {
480580 pub fn fnParamLen(self: Type) usize {
481581 return switch (self.tag()) {
482582 .fn_naked_noreturn_no_args => 0,
583 .fn_ccc_void_no_args => 0,
483584
484585 .f16,
485586 .f32,
......@@ -511,6 +612,8 @@ pub const Type = extern union {
511612 .c_ulong,
512613 .c_longlong,
513614 .c_ulonglong,
615 .int_unsigned,
616 .int_signed,
514617 => unreachable,
515618 };
516619 }
......@@ -520,6 +623,7 @@ pub const Type = extern union {
520623 pub fn fnParamTypes(self: Type, types: []Type) void {
521624 switch (self.tag()) {
522625 .fn_naked_noreturn_no_args => return,
626 .fn_ccc_void_no_args => return,
523627
524628 .f16,
525629 .f32,
......@@ -551,6 +655,8 @@ pub const Type = extern union {
551655 .c_ulong,
552656 .c_longlong,
553657 .c_ulonglong,
658 .int_unsigned,
659 .int_signed,
554660 => unreachable,
555661 }
556662 }
......@@ -559,6 +665,7 @@ pub const Type = extern union {
559665 pub fn fnReturnType(self: Type) Type {
560666 return switch (self.tag()) {
561667 .fn_naked_noreturn_no_args => Type.initTag(.noreturn),
668 .fn_ccc_void_no_args => Type.initTag(.void),
562669
563670 .f16,
564671 .f32,
......@@ -590,6 +697,8 @@ pub const Type = extern union {
590697 .c_ulong,
591698 .c_longlong,
592699 .c_ulonglong,
700 .int_unsigned,
701 .int_signed,
593702 => unreachable,
594703 };
595704 }
......@@ -598,6 +707,7 @@ pub const Type = extern union {
598707 pub fn fnCallingConvention(self: Type) std.builtin.CallingConvention {
599708 return switch (self.tag()) {
600709 .fn_naked_noreturn_no_args => .Naked,
710 .fn_ccc_void_no_args => .C,
601711
602712 .f16,
603713 .f32,
......@@ -629,10 +739,148 @@ pub const Type = extern union {
629739 .c_ulong,
630740 .c_longlong,
631741 .c_ulonglong,
742 .int_unsigned,
743 .int_signed,
632744 => unreachable,
633745 };
634746 }
635747
748 pub fn isNumeric(self: Type) bool {
749 return switch (self.tag()) {
750 .f16,
751 .f32,
752 .f64,
753 .f128,
754 .c_longdouble,
755 .comptime_int,
756 .comptime_float,
757 .u8,
758 .i8,
759 .usize,
760 .isize,
761 .c_short,
762 .c_ushort,
763 .c_int,
764 .c_uint,
765 .c_long,
766 .c_ulong,
767 .c_longlong,
768 .c_ulonglong,
769 .int_unsigned,
770 .int_signed,
771 => true,
772
773 .c_void,
774 .bool,
775 .void,
776 .type,
777 .anyerror,
778 .noreturn,
779 .fn_naked_noreturn_no_args,
780 .fn_ccc_void_no_args,
781 .array,
782 .single_const_pointer,
783 .single_const_pointer_to_comptime_int,
784 .array_u8_sentinel_0,
785 .const_slice_u8,
786 => false,
787 };
788 }
789
790 pub fn onePossibleValue(self: Type) bool {
791 var ty = self;
792 while (true) switch (ty.tag()) {
793 .f16,
794 .f32,
795 .f64,
796 .f128,
797 .c_longdouble,
798 .comptime_int,
799 .comptime_float,
800 .u8,
801 .i8,
802 .usize,
803 .isize,
804 .c_short,
805 .c_ushort,
806 .c_int,
807 .c_uint,
808 .c_long,
809 .c_ulong,
810 .c_longlong,
811 .c_ulonglong,
812 .bool,
813 .type,
814 .anyerror,
815 .fn_naked_noreturn_no_args,
816 .fn_ccc_void_no_args,
817 .single_const_pointer_to_comptime_int,
818 .array_u8_sentinel_0,
819 .const_slice_u8,
820 => return false,
821
822 .c_void,
823 .void,
824 .noreturn,
825 => return true,
826
827 .int_unsigned => return ty.cast(Payload.IntUnsigned).?.bits == 0,
828 .int_signed => return ty.cast(Payload.IntSigned).?.bits == 0,
829 .array => {
830 const array = ty.cast(Payload.Array).?;
831 if (array.len == 0)
832 return true;
833 ty = array.elem_type;
834 continue;
835 },
836 .single_const_pointer => {
837 const ptr = ty.cast(Payload.SingleConstPointer).?;
838 ty = ptr.pointee_type;
839 continue;
840 },
841 };
842 }
843
844 pub fn isCPtr(self: Type) bool {
845 return switch (self.tag()) {
846 .f16,
847 .f32,
848 .f64,
849 .f128,
850 .c_longdouble,
851 .comptime_int,
852 .comptime_float,
853 .u8,
854 .i8,
855 .usize,
856 .isize,
857 .c_short,
858 .c_ushort,
859 .c_int,
860 .c_uint,
861 .c_long,
862 .c_ulong,
863 .c_longlong,
864 .c_ulonglong,
865 .bool,
866 .type,
867 .anyerror,
868 .fn_naked_noreturn_no_args,
869 .fn_ccc_void_no_args,
870 .single_const_pointer_to_comptime_int,
871 .array_u8_sentinel_0,
872 .const_slice_u8,
873 .c_void,
874 .void,
875 .noreturn,
876 .int_unsigned,
877 .int_signed,
878 .array,
879 .single_const_pointer,
880 => return false,
881 };
882 }
883
636884 /// This enum does not directly correspond to `std.builtin.TypeId` because
637885 /// it has extra enum tags in it, as a way of using less memory. For example,
638886 /// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types
......@@ -667,6 +915,7 @@ pub const Type = extern union {
667915 comptime_float,
668916 noreturn,
669917 fn_naked_noreturn_no_args,
918 fn_ccc_void_no_args,
670919 single_const_pointer_to_comptime_int,
671920 const_slice_u8, // See last_no_payload_tag below.
672921 // After this, the tag requires a payload.
......@@ -674,6 +923,8 @@ pub const Type = extern union {
674923 array_u8_sentinel_0,
675924 array,
676925 single_const_pointer,
926 int_signed,
927 int_unsigned,
677928
678929 pub const last_no_payload_tag = Tag.const_slice_u8;
679930 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
......@@ -700,10 +951,22 @@ pub const Type = extern union {
700951
701952 pointee_type: Type,
702953 };
954
955 pub const IntSigned = struct {
956 base: Payload = Payload{ .tag = .int_signed },
957
958 bits: u16,
959 };
960
961 pub const IntUnsigned = struct {
962 base: Payload = Payload{ .tag = .int_unsigned },
963
964 bits: u16,
965 };
703966 };
704967};
705968
706pub const CInteger = enum {
969pub const CType = enum {
707970 short,
708971 ushort,
709972 int,
......@@ -712,8 +975,9 @@ pub const CInteger = enum {
712975 ulong,
713976 longlong,
714977 ulonglong,
978 longdouble,
715979
716 pub fn sizeInBits(self: CInteger, target: Target) u16 {
980 pub fn sizeInBits(self: CType, target: Target) u16 {
717981 const arch = target.cpu.arch;
718982 switch (target.os.tag) {
719983 .freestanding, .other => switch (target.cpu.arch) {
......@@ -729,6 +993,7 @@ pub const CInteger = enum {
729993 .longlong,
730994 .ulonglong,
731995 => return 64,
996 .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"),
732997 },
733998 else => switch (self) {
734999 .short,
......@@ -743,6 +1008,7 @@ pub const CInteger = enum {
7431008 .longlong,
7441009 .ulonglong,
7451010 => return 64,
1011 .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"),
7461012 },
7471013 },
7481014
......@@ -767,6 +1033,7 @@ pub const CInteger = enum {
7671033 .longlong,
7681034 .ulonglong,
7691035 => return 64,
1036 .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"),
7701037 },
7711038
7721039 .windows, .uefi => switch (self) {
......@@ -781,6 +1048,7 @@ pub const CInteger = enum {
7811048 .longlong,
7821049 .ulonglong,
7831050 => return 64,
1051 .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"),
7841052 },
7851053
7861054 .ios => switch (self) {
......@@ -795,6 +1063,7 @@ pub const CInteger = enum {
7951063 .longlong,
7961064 .ulonglong,
7971065 => return 64,
1066 .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"),
7981067 },
7991068
8001069 .ananas,
......@@ -821,7 +1090,7 @@ pub const CInteger = enum {
8211090 .amdpal,
8221091 .hermit,
8231092 .hurd,
824 => @panic("TODO specify the C integer type sizes for this OS"),
1093 => @panic("TODO specify the C integer and float type sizes for this OS"),
8251094 }
8261095 }
8271096};
src-self-hosted/value.zig+377-39
......@@ -2,7 +2,8 @@ const std = @import("std");
22const Type = @import("type.zig").Type;
33const log2 = std.math.log2;
44const assert = std.debug.assert;
5const BigInt = std.math.big.Int;
5const BigIntConst = std.math.big.int.Const;
6const BigIntMutable = std.math.big.int.Mutable;
67const Target = std.Target;
78const Allocator = std.mem.Allocator;
89
......@@ -45,12 +46,14 @@ pub const Value = extern union {
4546 comptime_float_type,
4647 noreturn_type,
4748 fn_naked_noreturn_no_args_type,
49 fn_ccc_void_no_args_type,
4850 single_const_pointer_to_comptime_int_type,
4951 const_slice_u8_type,
5052
53 undef,
5154 zero,
52 void_value,
53 noreturn_value,
55 the_one_possible_value, // when the type only has one possible value
56 null_value,
5457 bool_true,
5558 bool_false, // See last_no_payload_tag below.
5659 // After this, the tag requires a payload.
......@@ -58,11 +61,13 @@ pub const Value = extern union {
5861 ty,
5962 int_u64,
6063 int_i64,
61 int_big,
64 int_big_positive,
65 int_big_negative,
6266 function,
6367 ref,
6468 ref_val,
6569 bytes,
70 repeated, // the value is a value repeated some number of times
6671
6772 pub const last_no_payload_tag = Tag.bool_false;
6873 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
......@@ -132,18 +137,21 @@ pub const Value = extern union {
132137 .comptime_float_type => return out_stream.writeAll("comptime_float"),
133138 .noreturn_type => return out_stream.writeAll("noreturn"),
134139 .fn_naked_noreturn_no_args_type => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
140 .fn_ccc_void_no_args_type => return out_stream.writeAll("fn() callconv(.C) void"),
135141 .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"),
136142 .const_slice_u8_type => return out_stream.writeAll("[]const u8"),
137143
144 .null_value => return out_stream.writeAll("null"),
145 .undef => return out_stream.writeAll("undefined"),
138146 .zero => return out_stream.writeAll("0"),
139 .void_value => return out_stream.writeAll("{}"),
140 .noreturn_value => return out_stream.writeAll("unreachable"),
147 .the_one_possible_value => return out_stream.writeAll("(one possible value)"),
141148 .bool_true => return out_stream.writeAll("true"),
142149 .bool_false => return out_stream.writeAll("false"),
143150 .ty => return val.cast(Payload.Ty).?.ty.format("", options, out_stream),
144151 .int_u64 => return std.fmt.formatIntValue(val.cast(Payload.Int_u64).?.int, "", options, out_stream),
145152 .int_i64 => return std.fmt.formatIntValue(val.cast(Payload.Int_i64).?.int, "", options, out_stream),
146 .int_big => return out_stream.print("{}", .{val.cast(Payload.IntBig).?.big_int}),
153 .int_big_positive => return out_stream.print("{}", .{val.cast(Payload.IntBigPositive).?.asBigInt()}),
154 .int_big_negative => return out_stream.print("{}", .{val.cast(Payload.IntBigNegative).?.asBigInt()}),
147155 .function => return out_stream.writeAll("(function)"),
148156 .ref => return out_stream.writeAll("(ref)"),
149157 .ref_val => {
......@@ -152,6 +160,10 @@ pub const Value = extern union {
152160 continue;
153161 },
154162 .bytes => return std.zig.renderStringLiteral(self.cast(Payload.Bytes).?.data, out_stream),
163 .repeated => {
164 try out_stream.writeAll("(repeated) ");
165 val = val.cast(Payload.Repeated).?.val;
166 },
155167 };
156168 }
157169
......@@ -195,27 +207,31 @@ pub const Value = extern union {
195207 .comptime_float_type => Type.initTag(.@"comptime_float"),
196208 .noreturn_type => Type.initTag(.@"noreturn"),
197209 .fn_naked_noreturn_no_args_type => Type.initTag(.fn_naked_noreturn_no_args),
210 .fn_ccc_void_no_args_type => Type.initTag(.fn_ccc_void_no_args),
198211 .single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int),
199212 .const_slice_u8_type => Type.initTag(.const_slice_u8),
200213
214 .undef,
201215 .zero,
202 .void_value,
203 .noreturn_value,
216 .the_one_possible_value,
204217 .bool_true,
205218 .bool_false,
219 .null_value,
206220 .int_u64,
207221 .int_i64,
208 .int_big,
222 .int_big_positive,
223 .int_big_negative,
209224 .function,
210225 .ref,
211226 .ref_val,
212227 .bytes,
228 .repeated,
213229 => unreachable,
214230 };
215231 }
216232
217233 /// Asserts the value is an integer.
218 pub fn toBigInt(self: Value, allocator: *Allocator) Allocator.Error!BigInt {
234 pub fn toBigInt(self: Value, space: *BigIntSpace) BigIntConst {
219235 switch (self.tag()) {
220236 .ty,
221237 .u8_type,
......@@ -244,23 +260,28 @@ pub const Value = extern union {
244260 .comptime_float_type,
245261 .noreturn_type,
246262 .fn_naked_noreturn_no_args_type,
263 .fn_ccc_void_no_args_type,
247264 .single_const_pointer_to_comptime_int_type,
248265 .const_slice_u8_type,
249 .void_value,
250 .noreturn_value,
251266 .bool_true,
252267 .bool_false,
268 .null_value,
253269 .function,
254270 .ref,
255271 .ref_val,
256272 .bytes,
273 .undef,
274 .repeated,
257275 => unreachable,
258276
259 .zero => return BigInt.initSet(allocator, 0),
277 .the_one_possible_value, // An integer with one possible value is always zero.
278 .zero,
279 => return BigIntMutable.init(&space.limbs, 0).toConst(),
260280
261 .int_u64 => return BigInt.initSet(allocator, self.cast(Payload.Int_u64).?.int),
262 .int_i64 => return BigInt.initSet(allocator, self.cast(Payload.Int_i64).?.int),
263 .int_big => return self.cast(Payload.IntBig).?.big_int,
281 .int_u64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_u64).?.int).toConst(),
282 .int_i64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_i64).?.int).toConst(),
283 .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt(),
284 .int_big_negative => return self.cast(Payload.IntBigPositive).?.asBigInt(),
264285 }
265286 }
266287
......@@ -294,23 +315,90 @@ pub const Value = extern union {
294315 .comptime_float_type,
295316 .noreturn_type,
296317 .fn_naked_noreturn_no_args_type,
318 .fn_ccc_void_no_args_type,
297319 .single_const_pointer_to_comptime_int_type,
298320 .const_slice_u8_type,
299 .void_value,
300 .noreturn_value,
301321 .bool_true,
302322 .bool_false,
323 .null_value,
303324 .function,
304325 .ref,
305326 .ref_val,
306327 .bytes,
328 .undef,
329 .repeated,
307330 => unreachable,
308331
309 .zero => return 0,
332 .zero,
333 .the_one_possible_value, // an integer with one possible value is always zero
334 => return 0,
310335
311336 .int_u64 => return self.cast(Payload.Int_u64).?.int,
312337 .int_i64 => return @intCast(u64, self.cast(Payload.Int_u64).?.int),
313 .int_big => return self.cast(Payload.IntBig).?.big_int.to(u64) catch unreachable,
338 .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt().to(u64) catch unreachable,
339 .int_big_negative => return self.cast(Payload.IntBigNegative).?.asBigInt().to(u64) catch unreachable,
340 }
341 }
342
343 /// Asserts the value is an integer and not undefined.
344 /// Returns the number of bits the value requires to represent stored in twos complement form.
345 pub fn intBitCountTwosComp(self: Value) usize {
346 switch (self.tag()) {
347 .ty,
348 .u8_type,
349 .i8_type,
350 .isize_type,
351 .usize_type,
352 .c_short_type,
353 .c_ushort_type,
354 .c_int_type,
355 .c_uint_type,
356 .c_long_type,
357 .c_ulong_type,
358 .c_longlong_type,
359 .c_ulonglong_type,
360 .c_longdouble_type,
361 .f16_type,
362 .f32_type,
363 .f64_type,
364 .f128_type,
365 .c_void_type,
366 .bool_type,
367 .void_type,
368 .type_type,
369 .anyerror_type,
370 .comptime_int_type,
371 .comptime_float_type,
372 .noreturn_type,
373 .fn_naked_noreturn_no_args_type,
374 .fn_ccc_void_no_args_type,
375 .single_const_pointer_to_comptime_int_type,
376 .const_slice_u8_type,
377 .bool_true,
378 .bool_false,
379 .null_value,
380 .function,
381 .ref,
382 .ref_val,
383 .bytes,
384 .undef,
385 .repeated,
386 => unreachable,
387
388 .the_one_possible_value, // an integer with one possible value is always zero
389 .zero,
390 => return 0,
391
392 .int_u64 => {
393 const x = self.cast(Payload.Int_u64).?.int;
394 if (x == 0) return 0;
395 return std.math.log2(x) + 1;
396 },
397 .int_i64 => {
398 @panic("TODO implement i64 intBitCountTwosComp");
399 },
400 .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt().bitCountTwosComp(),
401 .int_big_negative => return self.cast(Payload.IntBigNegative).?.asBigInt().bitCountTwosComp(),
314402 }
315403 }
316404
......@@ -344,19 +432,23 @@ pub const Value = extern union {
344432 .comptime_float_type,
345433 .noreturn_type,
346434 .fn_naked_noreturn_no_args_type,
435 .fn_ccc_void_no_args_type,
347436 .single_const_pointer_to_comptime_int_type,
348437 .const_slice_u8_type,
349 .void_value,
350 .noreturn_value,
351438 .bool_true,
352439 .bool_false,
440 .null_value,
353441 .function,
354442 .ref,
355443 .ref_val,
356444 .bytes,
445 .repeated,
357446 => unreachable,
358447
359 .zero => return true,
448 .zero,
449 .undef,
450 .the_one_possible_value, // an integer with one possible value is always zero
451 => return true,
360452
361453 .int_u64 => switch (ty.zigTypeTag()) {
362454 .Int => {
......@@ -381,20 +473,171 @@ pub const Value = extern union {
381473 .ComptimeInt => return true,
382474 else => unreachable,
383475 },
384 .int_big => switch (ty.zigTypeTag()) {
476 .int_big_positive => switch (ty.zigTypeTag()) {
385477 .Int => {
386478 const info = ty.intInfo(target);
387 return self.cast(Payload.IntBig).?.big_int.fitsInTwosComp(info.signed, info.bits);
479 return self.cast(Payload.IntBigPositive).?.asBigInt().fitsInTwosComp(info.signed, info.bits);
388480 },
389481 .ComptimeInt => return true,
390482 else => unreachable,
391483 },
484 .int_big_negative => switch (ty.zigTypeTag()) {
485 .Int => {
486 const info = ty.intInfo(target);
487 return self.cast(Payload.IntBigNegative).?.asBigInt().fitsInTwosComp(info.signed, info.bits);
488 },
489 .ComptimeInt => return true,
490 else => unreachable,
491 },
492 }
493 }
494
495 /// Asserts the value is a float
496 pub fn floatHasFraction(self: Value) bool {
497 return switch (self.tag()) {
498 .ty,
499 .u8_type,
500 .i8_type,
501 .isize_type,
502 .usize_type,
503 .c_short_type,
504 .c_ushort_type,
505 .c_int_type,
506 .c_uint_type,
507 .c_long_type,
508 .c_ulong_type,
509 .c_longlong_type,
510 .c_ulonglong_type,
511 .c_longdouble_type,
512 .f16_type,
513 .f32_type,
514 .f64_type,
515 .f128_type,
516 .c_void_type,
517 .bool_type,
518 .void_type,
519 .type_type,
520 .anyerror_type,
521 .comptime_int_type,
522 .comptime_float_type,
523 .noreturn_type,
524 .fn_naked_noreturn_no_args_type,
525 .fn_ccc_void_no_args_type,
526 .single_const_pointer_to_comptime_int_type,
527 .const_slice_u8_type,
528 .bool_true,
529 .bool_false,
530 .null_value,
531 .function,
532 .ref,
533 .ref_val,
534 .bytes,
535 .repeated,
536 .undef,
537 .int_u64,
538 .int_i64,
539 .int_big_positive,
540 .int_big_negative,
541 .the_one_possible_value,
542 => unreachable,
543
544 .zero => false,
545 };
546 }
547
548 pub fn orderAgainstZero(lhs: Value) std.math.Order {
549 switch (lhs.tag()) {
550 .ty,
551 .u8_type,
552 .i8_type,
553 .isize_type,
554 .usize_type,
555 .c_short_type,
556 .c_ushort_type,
557 .c_int_type,
558 .c_uint_type,
559 .c_long_type,
560 .c_ulong_type,
561 .c_longlong_type,
562 .c_ulonglong_type,
563 .c_longdouble_type,
564 .f16_type,
565 .f32_type,
566 .f64_type,
567 .f128_type,
568 .c_void_type,
569 .bool_type,
570 .void_type,
571 .type_type,
572 .anyerror_type,
573 .comptime_int_type,
574 .comptime_float_type,
575 .noreturn_type,
576 .fn_naked_noreturn_no_args_type,
577 .fn_ccc_void_no_args_type,
578 .single_const_pointer_to_comptime_int_type,
579 .const_slice_u8_type,
580 .bool_true,
581 .bool_false,
582 .null_value,
583 .function,
584 .ref,
585 .ref_val,
586 .bytes,
587 .repeated,
588 .undef,
589 => unreachable,
590
591 .zero,
592 .the_one_possible_value, // an integer with one possible value is always zero
593 => return .eq,
594
595 .int_u64 => return std.math.order(lhs.cast(Payload.Int_u64).?.int, 0),
596 .int_i64 => return std.math.order(lhs.cast(Payload.Int_i64).?.int, 0),
597 .int_big_positive => return lhs.cast(Payload.IntBigPositive).?.asBigInt().orderAgainstScalar(0),
598 .int_big_negative => return lhs.cast(Payload.IntBigNegative).?.asBigInt().orderAgainstScalar(0),
392599 }
393600 }
394601
602 /// Asserts the value is comparable.
603 pub fn order(lhs: Value, rhs: Value) std.math.Order {
604 const lhs_tag = lhs.tag();
605 const rhs_tag = lhs.tag();
606 const lhs_is_zero = lhs_tag == .zero or lhs_tag == .the_one_possible_value;
607 const rhs_is_zero = rhs_tag == .zero or rhs_tag == .the_one_possible_value;
608 if (lhs_is_zero) return rhs.orderAgainstZero().invert();
609 if (rhs_is_zero) return lhs.orderAgainstZero();
610
611 // TODO floats
612
613 var lhs_bigint_space: BigIntSpace = undefined;
614 var rhs_bigint_space: BigIntSpace = undefined;
615 const lhs_bigint = lhs.toBigInt(&lhs_bigint_space);
616 const rhs_bigint = rhs.toBigInt(&rhs_bigint_space);
617 return lhs_bigint.order(rhs_bigint);
618 }
619
620 /// Asserts the value is comparable.
621 pub fn compare(lhs: Value, op: std.math.CompareOperator, rhs: Value) bool {
622 return order(lhs, rhs).compare(op);
623 }
624
625 /// Asserts the value is comparable.
626 pub fn compareWithZero(lhs: Value, op: std.math.CompareOperator) bool {
627 return orderAgainstZero(lhs).compare(op);
628 }
629
630 pub fn toBool(self: Value) bool {
631 return switch (self.tag()) {
632 .bool_true => true,
633 .bool_false => false,
634 else => unreachable,
635 };
636 }
637
395638 /// Asserts the value is a pointer and dereferences it.
396639 pub fn pointerDeref(self: Value) Value {
397 switch (self.tag()) {
640 return switch (self.tag()) {
398641 .ty,
399642 .u8_type,
400643 .i8_type,
......@@ -422,23 +665,27 @@ pub const Value = extern union {
422665 .comptime_float_type,
423666 .noreturn_type,
424667 .fn_naked_noreturn_no_args_type,
668 .fn_ccc_void_no_args_type,
425669 .single_const_pointer_to_comptime_int_type,
426670 .const_slice_u8_type,
427671 .zero,
428 .void_value,
429 .noreturn_value,
430672 .bool_true,
431673 .bool_false,
674 .null_value,
432675 .function,
433676 .int_u64,
434677 .int_i64,
435 .int_big,
678 .int_big_positive,
679 .int_big_negative,
436680 .bytes,
681 .undef,
682 .repeated,
437683 => unreachable,
438684
439 .ref => return self.cast(Payload.Ref).?.cell.contents,
440 .ref_val => return self.cast(Payload.RefVal).?.val,
441 }
685 .the_one_possible_value => Value.initTag(.the_one_possible_value),
686 .ref => self.cast(Payload.Ref).?.cell.contents,
687 .ref_val => self.cast(Payload.RefVal).?.val,
688 };
442689 }
443690
444691 /// Asserts the value is a single-item pointer to an array, or an array,
......@@ -472,17 +719,20 @@ pub const Value = extern union {
472719 .comptime_float_type,
473720 .noreturn_type,
474721 .fn_naked_noreturn_no_args_type,
722 .fn_ccc_void_no_args_type,
475723 .single_const_pointer_to_comptime_int_type,
476724 .const_slice_u8_type,
477725 .zero,
478 .void_value,
479 .noreturn_value,
726 .the_one_possible_value,
480727 .bool_true,
481728 .bool_false,
729 .null_value,
482730 .function,
483731 .int_u64,
484732 .int_i64,
485 .int_big,
733 .int_big_positive,
734 .int_big_negative,
735 .undef,
486736 => unreachable,
487737
488738 .ref => @panic("TODO figure out how MemoryCell works"),
......@@ -493,9 +743,70 @@ pub const Value = extern union {
493743 int_payload.* = .{ .int = self.cast(Payload.Bytes).?.data[index] };
494744 return Value.initPayload(&int_payload.base);
495745 },
746
747 // No matter the index; all the elements are the same!
748 .repeated => return self.cast(Payload.Repeated).?.val,
496749 }
497750 }
498751
752 pub fn isUndef(self: Value) bool {
753 return self.tag() == .undef;
754 }
755
756 /// Valid for all types. Asserts the value is not undefined.
757 /// `.the_one_possible_value` is reported as not null.
758 pub fn isNull(self: Value) bool {
759 return switch (self.tag()) {
760 .ty,
761 .u8_type,
762 .i8_type,
763 .isize_type,
764 .usize_type,
765 .c_short_type,
766 .c_ushort_type,
767 .c_int_type,
768 .c_uint_type,
769 .c_long_type,
770 .c_ulong_type,
771 .c_longlong_type,
772 .c_ulonglong_type,
773 .c_longdouble_type,
774 .f16_type,
775 .f32_type,
776 .f64_type,
777 .f128_type,
778 .c_void_type,
779 .bool_type,
780 .void_type,
781 .type_type,
782 .anyerror_type,
783 .comptime_int_type,
784 .comptime_float_type,
785 .noreturn_type,
786 .fn_naked_noreturn_no_args_type,
787 .fn_ccc_void_no_args_type,
788 .single_const_pointer_to_comptime_int_type,
789 .const_slice_u8_type,
790 .zero,
791 .the_one_possible_value,
792 .bool_true,
793 .bool_false,
794 .function,
795 .int_u64,
796 .int_i64,
797 .int_big_positive,
798 .int_big_negative,
799 .ref,
800 .ref_val,
801 .bytes,
802 .repeated,
803 => false,
804
805 .undef => unreachable,
806 .null_value => true,
807 };
808 }
809
499810 /// This type is not copyable since it may contain pointers to its inner data.
500811 pub const Payload = struct {
501812 tag: Tag,
......@@ -510,9 +821,22 @@ pub const Value = extern union {
510821 int: i64,
511822 };
512823
513 pub const IntBig = struct {
514 base: Payload = Payload{ .tag = .int_big },
515 big_int: BigInt,
824 pub const IntBigPositive = struct {
825 base: Payload = Payload{ .tag = .int_big_positive },
826 limbs: []const std.math.big.Limb,
827
828 pub fn asBigInt(self: IntBigPositive) BigIntConst {
829 return BigIntConst{ .limbs = self.limbs, .positive = true };
830 }
831 };
832
833 pub const IntBigNegative = struct {
834 base: Payload = Payload{ .tag = .int_big_negative },
835 limbs: []const std.math.big.Limb,
836
837 pub fn asBigInt(self: IntBigNegative) BigIntConst {
838 return BigIntConst{ .limbs = self.limbs, .positive = false };
839 }
516840 };
517841
518842 pub const Function = struct {
......@@ -550,6 +874,20 @@ pub const Value = extern union {
550874 base: Payload = Payload{ .tag = .ty },
551875 ty: Type,
552876 };
877
878 pub const Repeated = struct {
879 base: Payload = Payload{ .tag = .ty },
880 /// This value is repeated some number of times. The amount of times to repeat
881 /// is stored externally.
882 val: Value,
883 };
884 };
885
886 /// Big enough to fit any non-BigInt value
887 pub const BigIntSpace = struct {
888 /// The +1 is headroom so that operations such as incrementing once or decrementing once
889 /// are possible without using an allocator.
890 limbs: [(@sizeOf(u64) / @sizeOf(std.math.big.Limb)) + 1]std.math.big.Limb,
553891 };
554892};
555893
test/stage2/compare_output.zig+22-19
......@@ -2,24 +2,27 @@ const std = @import("std");
22const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
33
44pub fn addCases(ctx: *TestContext) !void {
5 // hello world
6 try ctx.testCompareOutputLibC(
7 \\extern fn puts([*]const u8) void;
8 \\pub export fn main() c_int {
9 \\ puts("Hello, world!");
10 \\ return 0;
11 \\}
12 , "Hello, world!" ++ std.cstr.line_sep);
5 // TODO: re-enable these tests.
6 // https://github.com/ziglang/zig/issues/1364
137
14 // function calling another function
15 try ctx.testCompareOutputLibC(
16 \\extern fn puts(s: [*]const u8) void;
17 \\pub export fn main() c_int {
18 \\ return foo("OK");
19 \\}
20 \\fn foo(s: [*]const u8) c_int {
21 \\ puts(s);
22 \\ return 0;
23 \\}
24 , "OK" ++ std.cstr.line_sep);
8 //// hello world
9 //try ctx.testCompareOutputLibC(
10 // \\extern fn puts([*]const u8) void;
11 // \\pub export fn main() c_int {
12 // \\ puts("Hello, world!");
13 // \\ return 0;
14 // \\}
15 //, "Hello, world!" ++ std.cstr.line_sep);
16
17 //// function calling another function
18 //try ctx.testCompareOutputLibC(
19 // \\extern fn puts(s: [*]const u8) void;
20 // \\pub export fn main() c_int {
21 // \\ return foo("OK");
22 // \\}
23 // \\fn foo(s: [*]const u8) c_int {
24 // \\ puts(s);
25 // \\ return 0;
26 // \\}
27 //, "OK" ++ std.cstr.line_sep);
2528}
test/stage2/compile_errors.zig+53-50
......@@ -1,54 +1,57 @@
11const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
22
33pub fn addCases(ctx: *TestContext) !void {
4 try ctx.testCompileError(
5 \\export fn entry() void {}
6 \\export fn entry() void {}
7 , "1.zig", 2, 8, "exported symbol collision: 'entry'");
8
9 try ctx.testCompileError(
10 \\fn() void {}
11 , "1.zig", 1, 1, "missing function name");
12
13 try ctx.testCompileError(
14 \\comptime {
15 \\ return;
16 \\}
17 , "1.zig", 2, 5, "return expression outside function definition");
18
19 try ctx.testCompileError(
20 \\export fn entry() void {
21 \\ defer return;
22 \\}
23 , "1.zig", 2, 11, "cannot return from defer expression");
24
25 try ctx.testCompileError(
26 \\export fn entry() c_int {
27 \\ return 36893488147419103232;
28 \\}
29 , "1.zig", 2, 12, "integer value '36893488147419103232' cannot be stored in type 'c_int'");
30
31 try ctx.testCompileError(
32 \\comptime {
33 \\ var a: *align(4) align(4) i32 = 0;
34 \\}
35 , "1.zig", 2, 22, "Extra align qualifier");
36
37 try ctx.testCompileError(
38 \\comptime {
39 \\ var b: *const const i32 = 0;
40 \\}
41 , "1.zig", 2, 19, "Extra align qualifier");
42
43 try ctx.testCompileError(
44 \\comptime {
45 \\ var c: *volatile volatile i32 = 0;
46 \\}
47 , "1.zig", 2, 22, "Extra align qualifier");
48
49 try ctx.testCompileError(
50 \\comptime {
51 \\ var d: *allowzero allowzero i32 = 0;
52 \\}
53 , "1.zig", 2, 23, "Extra align qualifier");
4 // TODO: re-enable these tests.
5 // https://github.com/ziglang/zig/issues/1364
6
7 //try ctx.testCompileError(
8 // \\export fn entry() void {}
9 // \\export fn entry() void {}
10 //, "1.zig", 2, 8, "exported symbol collision: 'entry'");
11
12 //try ctx.testCompileError(
13 // \\fn() void {}
14 //, "1.zig", 1, 1, "missing function name");
15
16 //try ctx.testCompileError(
17 // \\comptime {
18 // \\ return;
19 // \\}
20 //, "1.zig", 2, 5, "return expression outside function definition");
21
22 //try ctx.testCompileError(
23 // \\export fn entry() void {
24 // \\ defer return;
25 // \\}
26 //, "1.zig", 2, 11, "cannot return from defer expression");
27
28 //try ctx.testCompileError(
29 // \\export fn entry() c_int {
30 // \\ return 36893488147419103232;
31 // \\}
32 //, "1.zig", 2, 12, "integer value '36893488147419103232' cannot be stored in type 'c_int'");
33
34 //try ctx.testCompileError(
35 // \\comptime {
36 // \\ var a: *align(4) align(4) i32 = 0;
37 // \\}
38 //, "1.zig", 2, 22, "Extra align qualifier");
39
40 //try ctx.testCompileError(
41 // \\comptime {
42 // \\ var b: *const const i32 = 0;
43 // \\}
44 //, "1.zig", 2, 19, "Extra align qualifier");
45
46 //try ctx.testCompileError(
47 // \\comptime {
48 // \\ var c: *volatile volatile i32 = 0;
49 // \\}
50 //, "1.zig", 2, 22, "Extra align qualifier");
51
52 //try ctx.testCompileError(
53 // \\comptime {
54 // \\ var d: *allowzero allowzero i32 = 0;
55 // \\}
56 //, "1.zig", 2, 23, "Extra align qualifier");
5457}
test/stage2/ir.zig deleted-54
......@@ -1,54 +0,0 @@
1test "hello world IR" {
2 exeCmp(
3 \\@0 = str("Hello, world!\n")
4 \\@1 = primitive(void)
5 \\@2 = primitive(usize)
6 \\@3 = fntype([], @1, cc=Naked)
7 \\@4 = int(0)
8 \\@5 = int(1)
9 \\@6 = int(231)
10 \\@7 = str("len")
11 \\
12 \\@8 = fn(@3, {
13 \\ %0 = as(@2, @5) ; SYS_write
14 \\ %1 = as(@2, @5) ; STDOUT_FILENO
15 \\ %2 = ptrtoint(@0) ; msg ptr
16 \\ %3 = fieldptr(@0, @7) ; msg len ptr
17 \\ %4 = deref(%3) ; msg len
18 \\ %sysoutreg = str("={rax}")
19 \\ %rax = str("{rax}")
20 \\ %rdi = str("{rdi}")
21 \\ %rsi = str("{rsi}")
22 \\ %rdx = str("{rdx}")
23 \\ %rcx = str("rcx")
24 \\ %r11 = str("r11")
25 \\ %memory = str("memory")
26 \\ %syscall = str("syscall")
27 \\ %5 = asm(%syscall, @2,
28 \\ volatile=1,
29 \\ output=%sysoutreg,
30 \\ inputs=[%rax, %rdi, %rsi, %rdx],
31 \\ clobbers=[%rcx, %r11, %memory],
32 \\ args=[%0, %1, %2, %4])
33 \\
34 \\ %6 = as(@2, @6) ;SYS_exit_group
35 \\ %7 = as(@2, @4) ;exit code
36 \\ %8 = asm(%syscall, @2,
37 \\ volatile=1,
38 \\ output=%sysoutreg,
39 \\ inputs=[%rax, %rdi],
40 \\ clobbers=[%rcx, %r11, %memory],
41 \\ args=[%6, %7])
42 \\
43 \\ %9 = unreachable()
44 \\})
45 \\
46 \\@9 = str("_start")
47 \\@10 = export(@9, @8)
48 ,
49 \\Hello, world!
50 \\
51 );
52}
53
54fn exeCmp(src: []const u8, expected_stdout: []const u8) void {}
test/stage2/test.zig+1
......@@ -3,4 +3,5 @@ const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
33pub fn addCases(ctx: *TestContext) !void {
44 try @import("compile_errors.zig").addCases(ctx);
55 try @import("compare_output.zig").addCases(ctx);
6 @import("zir.zig").addCases(ctx);
67}
test/stage2/zir.zig created+107
......@@ -0,0 +1,107 @@
1const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
2
3pub fn addCases(ctx: *TestContext) void {
4 ctx.addZIRTransform("elemptr, add, cmp, condbr, return, breakpoint",
5 \\@void = primitive(void)
6 \\@usize = primitive(usize)
7 \\@fnty = fntype([], @void, cc=C)
8 \\@0 = int(0)
9 \\@1 = int(1)
10 \\@2 = int(2)
11 \\@3 = int(3)
12 \\
13 \\@entry = fn(@fnty, {
14 \\ %a = str("\x32\x08\x01\x0a")
15 \\ %eptr0 = elemptr(%a, @0)
16 \\ %eptr1 = elemptr(%a, @1)
17 \\ %eptr2 = elemptr(%a, @2)
18 \\ %eptr3 = elemptr(%a, @3)
19 \\ %v0 = deref(%eptr0)
20 \\ %v1 = deref(%eptr1)
21 \\ %v2 = deref(%eptr2)
22 \\ %v3 = deref(%eptr3)
23 \\ %x0 = add(%v0, %v1)
24 \\ %x1 = add(%v2, %v3)
25 \\ %result = add(%x0, %x1)
26 \\
27 \\ %expected = int(69)
28 \\ %ok = cmp(%result, eq, %expected)
29 \\ %10 = condbr(%ok, {
30 \\ %11 = return()
31 \\ }, {
32 \\ %12 = breakpoint()
33 \\ })
34 \\})
35 \\
36 \\@9 = str("entry")
37 \\@10 = export(@9, @entry)
38 ,
39 \\@0 = primitive(void)
40 \\@1 = fntype([], @0, cc=C)
41 \\@2 = fn(@1, {
42 \\ %0 = return()
43 \\})
44 \\@3 = str("entry")
45 \\@4 = export(@3, @2)
46 \\
47 );
48
49 if (@import("std").Target.current.os.tag != .linux or
50 @import("std").Target.current.cpu.arch != .x86_64)
51 {
52 // TODO implement self-hosted PE (.exe file) linking
53 // TODO implement more ZIR so we don't depend on x86_64-linux
54 return;
55 }
56
57 ctx.addZIRCompareOutput("hello world ZIR",
58 \\@0 = str("Hello, world!\n")
59 \\@1 = primitive(noreturn)
60 \\@2 = primitive(usize)
61 \\@3 = fntype([], @1, cc=Naked)
62 \\@4 = int(0)
63 \\@5 = int(1)
64 \\@6 = int(231)
65 \\@7 = str("len")
66 \\
67 \\@8 = fn(@3, {
68 \\ %0 = as(@2, @5) ; SYS_write
69 \\ %1 = as(@2, @5) ; STDOUT_FILENO
70 \\ %2 = ptrtoint(@0) ; msg ptr
71 \\ %3 = fieldptr(@0, @7) ; msg len ptr
72 \\ %4 = deref(%3) ; msg len
73 \\ %sysoutreg = str("={rax}")
74 \\ %rax = str("{rax}")
75 \\ %rdi = str("{rdi}")
76 \\ %rsi = str("{rsi}")
77 \\ %rdx = str("{rdx}")
78 \\ %rcx = str("rcx")
79 \\ %r11 = str("r11")
80 \\ %memory = str("memory")
81 \\ %syscall = str("syscall")
82 \\ %5 = asm(%syscall, @2,
83 \\ volatile=1,
84 \\ output=%sysoutreg,
85 \\ inputs=[%rax, %rdi, %rsi, %rdx],
86 \\ clobbers=[%rcx, %r11, %memory],
87 \\ args=[%0, %1, %2, %4])
88 \\
89 \\ %6 = as(@2, @6) ;SYS_exit_group
90 \\ %7 = as(@2, @4) ;exit code
91 \\ %8 = asm(%syscall, @2,
92 \\ volatile=1,
93 \\ output=%sysoutreg,
94 \\ inputs=[%rax, %rdi],
95 \\ clobbers=[%rcx, %r11, %memory],
96 \\ args=[%6, %7])
97 \\
98 \\ %9 = unreachable()
99 \\})
100 \\
101 \\@9 = str("_start")
102 \\@10 = export(@9, @8)
103 ,
104 \\Hello, world!
105 \\
106 );
107}