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 {...@@ -44,7 +44,7 @@ pub fn build(b: *Builder) !void {
44 try findAndReadConfigH(b);44 try findAndReadConfigH(b);
4545
46 var test_stage2 = b.addTest("src-self-hosted/test.zig");46 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
48 test_stage2.addPackagePath("stage2_tests", "test/stage2/test.zig");48 test_stage2.addPackagePath("stage2_tests", "test/stage2/test.zig");
4949
50 const fmt_build_zig = b.addFmt(&[_][]const u8{"build.zig"});50 const fmt_build_zig = b.addFmt(&[_][]const u8{"build.zig"});
...@@ -68,7 +68,6 @@ pub fn build(b: *Builder) !void {...@@ -68,7 +68,6 @@ pub fn build(b: *Builder) !void {
68 var ctx = parseConfigH(b, config_h_text);68 var ctx = parseConfigH(b, config_h_text);
69 ctx.llvm = try findLLVM(b, ctx.llvm_config_exe);69 ctx.llvm = try findLLVM(b, ctx.llvm_config_exe);
7070
71 try configureStage2(b, test_stage2, ctx);
72 try configureStage2(b, exe, ctx);71 try configureStage2(b, exe, ctx);
7372
74 b.default_step.dependOn(&exe.step);73 b.default_step.dependOn(&exe.step);
lib/std/child_process.zig+11-1
...@@ -46,6 +46,12 @@ pub const ChildProcess = struct {...@@ -46,6 +46,12 @@ pub const ChildProcess = struct {
4646
47 /// Set to change the current working directory when spawning the child process.47 /// Set to change the current working directory when spawning the child process.
48 cwd: ?[]const u8,48 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
50 err_pipe: if (builtin.os.tag == .windows) void else [2]os.fd_t,56 err_pipe: if (builtin.os.tag == .windows) void else [2]os.fd_t,
5157
...@@ -183,6 +189,7 @@ pub const ChildProcess = struct {...@@ -183,6 +189,7 @@ pub const ChildProcess = struct {
183 allocator: *mem.Allocator,189 allocator: *mem.Allocator,
184 argv: []const []const u8,190 argv: []const []const u8,
185 cwd: ?[]const u8 = null,191 cwd: ?[]const u8 = null,
192 cwd_dir: ?fs.Dir = null,
186 env_map: ?*const BufMap = null,193 env_map: ?*const BufMap = null,
187 max_output_bytes: usize = 50 * 1024,194 max_output_bytes: usize = 50 * 1024,
188 expand_arg0: Arg0Expand = .no_expand,195 expand_arg0: Arg0Expand = .no_expand,
...@@ -194,6 +201,7 @@ pub const ChildProcess = struct {...@@ -194,6 +201,7 @@ pub const ChildProcess = struct {
194 child.stdout_behavior = .Pipe;201 child.stdout_behavior = .Pipe;
195 child.stderr_behavior = .Pipe;202 child.stderr_behavior = .Pipe;
196 child.cwd = args.cwd;203 child.cwd = args.cwd;
204 child.cwd_dir = args.cwd_dir;
197 child.env_map = args.env_map;205 child.env_map = args.env_map;
198 child.expand_arg0 = args.expand_arg0;206 child.expand_arg0 = args.expand_arg0;
199207
...@@ -414,7 +422,9 @@ pub const ChildProcess = struct {...@@ -414,7 +422,9 @@ pub const ChildProcess = struct {
414 os.close(stderr_pipe[1]);422 os.close(stderr_pipe[1]);
415 }423 }
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| {
418 os.chdir(cwd) catch |err| forkChildErrReport(err_pipe[1], err);428 os.chdir(cwd) catch |err| forkChildErrReport(err_pipe[1], err);
419 }429 }
420430
lib/std/fmt.zig+1-1
...@@ -1058,7 +1058,7 @@ pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) {...@@ -1058,7 +1058,7 @@ pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) {
1058 return value;1058 return value;
1059}1059}
10601060
1061fn digitToChar(digit: u8, uppercase: bool) u8 {1061pub fn digitToChar(digit: u8, uppercase: bool) u8 {
1062 return switch (digit) {1062 return switch (digit) {
1063 0...9 => digit + '0',1063 0...9 => digit + '0',
1064 10...35 => digit + ((if (uppercase) @as(u8, 'A') else @as(u8, 'a')) - 10),1064 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 {...@@ -606,7 +606,8 @@ pub const Dir = struct {
606 } else 0;606 } else 0;
607607
608 const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0;608 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)
610 @as(u32, os.O_RDWR)611 @as(u32, os.O_RDWR)
611 else if (flags.write)612 else if (flags.write)
612 @as(u32, os.O_WRONLY)613 @as(u32, os.O_WRONLY)
...@@ -689,7 +690,8 @@ pub const Dir = struct {...@@ -689,7 +690,8 @@ pub const Dir = struct {
689 } else 0;690 } else 0;
690691
691 const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0;692 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 |
693 (if (flags.truncate) @as(u32, os.O_TRUNC) else 0) |695 (if (flags.truncate) @as(u32, os.O_TRUNC) else 0) |
694 (if (flags.read) @as(u32, os.O_RDWR) else os.O_WRONLY) |696 (if (flags.read) @as(u32, os.O_RDWR) else os.O_WRONLY) |
695 (if (flags.exclusive) @as(u32, os.O_EXCL) else 0);697 (if (flags.exclusive) @as(u32, os.O_EXCL) else 0);
...@@ -787,6 +789,15 @@ pub const Dir = struct {...@@ -787,6 +789,15 @@ pub const Dir = struct {
787 }789 }
788 }790 }
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
790 /// Changes the current working directory to the open directory handle.801 /// Changes the current working directory to the open directory handle.
791 /// This modifies global state and can have surprising effects in multi-802 /// This modifies global state and can have surprising effects in multi-
792 /// threaded applications. Most applications and especially libraries should803 /// threaded applications. Most applications and especially libraries should
...@@ -807,6 +818,11 @@ pub const Dir = struct {...@@ -807,6 +818,11 @@ pub const Dir = struct {
807 /// `true` means the opened directory can be scanned for the files and sub-directories818 /// `true` means the opened directory can be scanned for the files and sub-directories
808 /// of the result. It means the `iterate` function can be called.819 /// of the result. It means the `iterate` function can be called.
809 iterate: bool = false,820 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,
810 };826 };
811827
812 /// Opens a directory at the given path. The directory is a system resource that remains828 /// Opens a directory at the given path. The directory is a system resource that remains
...@@ -832,9 +848,11 @@ pub const Dir = struct {...@@ -832,9 +848,11 @@ pub const Dir = struct {
832 return self.openDirW(&sub_path_w, args);848 return self.openDirW(&sub_path_w, args);
833 } else if (!args.iterate) {849 } else if (!args.iterate) {
834 const O_PATH = if (@hasDecl(os, "O_PATH")) os.O_PATH else 0;850 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);
836 } else {853 } 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);
838 }856 }
839 }857 }
840858
lib/std/fs/file.zig+10
...@@ -69,6 +69,11 @@ pub const File = struct {...@@ -69,6 +69,11 @@ pub const File = struct {
69 /// It allows the use of `noasync` when calling functions related to opening69 /// It allows the use of `noasync` when calling functions related to opening
70 /// the file, reading, and writing.70 /// the file, reading, and writing.
71 always_blocking: bool = false,71 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,
72 };77 };
7378
74 /// TODO https://github.com/ziglang/zig/issues/380279 /// TODO https://github.com/ziglang/zig/issues/3802
...@@ -107,6 +112,11 @@ pub const File = struct {...@@ -107,6 +112,11 @@ pub const File = struct {
107 /// For POSIX systems this is the file system mode the file will112 /// For POSIX systems this is the file system mode the file will
108 /// be created with.113 /// be created with.
109 mode: Mode = default_mode,114 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,
110 };120 };
111121
112 /// Upon success, the stream is in an uninitialized state. To continue using it,122 /// 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 {...@@ -986,6 +986,43 @@ pub const Order = enum {
986986
987 /// Greater than (`>`)987 /// Greater than (`>`)
988 gt,988 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 }
989};1026};
9901027
991/// Given two numbers, this function returns the order they are with respect to each other.1028/// 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 @@...@@ -1,7 +1,24 @@
1pub usingnamespace @import("big/int.zig");1const std = @import("../std.zig");
2pub usingnamespace @import("big/rational.zig");2const assert = std.debug.assert;
33
4test "math.big" {4pub const Rational = @import("big/rational.zig").Rational;
5 _ = @import("big/int.zig");5pub const int = @import("big/int.zig");
6 _ = @import("big/rational.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;
7}24}
lib/std/math/big/int.zig+1681-2497
...@@ -1,293 +1,196 @@...@@ -1,293 +1,196 @@
1const std = @import("../../std.zig");1const std = @import("../../std.zig");
2const debug = std.debug;
3const testing = std.testing;
4const math = std.math;2const 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;
5const mem = std.mem;8const mem = std.mem;
6const Allocator = mem.Allocator;
7const ArrayList = std.ArrayList;
8const maxInt = std.math.maxInt;9const maxInt = std.math.maxInt;
9const minInt = std.math.minInt;10const minInt = std.math.minInt;
11const assert = std.debug.assert;
1012
11pub const Limb = usize;13/// Returns the number of limbs needed to store `scalar`, which must be a
12pub const DoubleLimb = std.meta.Int(false, 2 * Limb.bit_count);14/// primitive integer value.
13pub const SignedDoubleLimb = std.meta.Int(true, DoubleLimb.bit_count);15pub fn calcLimbLen(scalar: var) usize {
14pub const Log2Limb = math.Log2Int(Limb);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 {30pub fn calcToStringLimbsBufferLen(a_len: usize, base: u8) usize {
17 debug.assert(math.floorPowerOfTwo(usize, Limb.bit_count) == Limb.bit_count);31 if (math.isPowerOfTwo(base))
18 debug.assert(Limb.bit_count <= 64); // u128 set is unsupported32 return 0;
19 debug.assert(Limb.is_signed == false);33 return a_len + 2 + a_len + calcDivLimbsBufferLen(a_len, 1);
20}34}
2135
22/// An arbitrary-precision big integer.36pub fn calcDivLimbsBufferLen(a_len: usize, b_len: usize) usize {
23///37 return calcMulLimbsBufferLen(a_len, b_len, 2) * 4;
24/// Memory is allocated by an Int as needed to ensure operations never overflow. The range of an38}
25/// Int is bounded only by available memory.
26pub const Int = struct {
27 const sign_bit: usize = 1 << (usize.bit_count - 1);
2839
29 /// Default number of limbs to allocate on creation of an Int.40pub fn calcMulLimbsBufferLen(a_len: usize, b_len: usize, aliases: usize) usize {
30 pub const default_capacity = 4;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.49pub fn calcSetStringLimbCount(base: u8, string_len: usize) usize {
33 allocator: ?*Allocator,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 {
35 /// Raw digits. These are:78 /// Raw digits. These are:
36 ///79 ///
37 /// * Little-endian ordered80 /// * Little-endian ordered
38 /// * limbs.len >= 181 /// * 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.
40 ///83 ///
41 /// Accessing limbs directly should be avoided.84 /// Accessing limbs directly should be avoided.
85 /// These are allocated limbs; the `len` field tells the valid range.
42 limbs: []Limb,86 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.90 pub fn toConst(self: Mutable) Const {
45 /// The remaining bits represent the number of limbs used by Int.91 return .{
46 metadata: usize,92 .limbs = self.limbs[0..self.len],
4793 .positive = self.positive,
48 /// Creates a new Int. default_capacity limbs will be allocated immediately.94 };
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;
61 }95 }
6296
63 /// Creates a new Int with a specific capacity. If capacity < default_capacity then the97 /// Asserts that the allocator owns the limbs memory. If this is not the case,
64 /// default capacity will be used instead.98 /// use `toConst().toManaged()`.
65 pub fn initCapacity(allocator: *Allocator, capacity: usize) !Int {99 pub fn toManaged(self: Mutable, allocator: *Allocator) Managed {
66 return Int{100 return .{
67 .allocator = allocator,101 .allocator = allocator,
68 .metadata = 1,102 .limbs = limbs,
69 .limbs = block: {103 .metadata = if (self.positive)
70 var limbs = try allocator.alloc(Limb, math.max(default_capacity, capacity));104 self.len & ~Managed.sign_bit
71 limbs[0] = 0;105 else
72 break :block limbs;106 self.len | Managed.sign_bit,
73 },
74 };107 };
75 }108 }
76109
77 /// Returns the number of limbs currently in use.110 /// `value` is a primitive integer type.
78 pub fn len(self: Int) usize {111 /// Asserts the value fits within the provided `limbs_buffer`.
79 return self.metadata & ~sign_bit;112 /// Note: `calcLimbLen` can be used to figure out how big an array to allocate for `limbs_buffer`.
80 }113 pub fn init(limbs_buffer: []Limb, value: var) Mutable {
81114 limbs_buffer[0] = 0;
82 /// Returns whether an Int is positive.115 var self: Mutable = .{
83 pub fn isPositive(self: Int) bool {116 .limbs = limbs_buffer,
84 return self.metadata & sign_bit == 0;117 .len = 1,
85 }118 .positive = true,
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],
113 };119 };
114120 self.set(value);
115 self.normalize(limbs.len);
116 return self;121 return self;
117 }122 }
118123
119 /// Ensures an Int has enough space allocated for capacity limbs. If the Int does not have124 /// Copies the value of a Const to an existing Mutable so that they both have the same value.
120 /// sufficient capacity, the exact amount will be allocated. This occurs even if the requested125 /// Asserts the value fits in the limbs buffer.
121 /// capacity is only greater than the current capacity by one limb.126 pub fn copy(self: *Mutable, other: Const) void {
122 pub fn ensureCapacity(self: *Int, capacity: usize) !void {127 if (self.limbs.ptr != other.limbs.ptr) {
123 self.assertWritable();128 mem.copy(Limb, self.limbs[0..], other.limbs[0..other.limbs.len]);
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;
167 }129 }
168130 self.positive = other.positive;
169 try self.ensureCapacity(other.len());131 self.len = other.limbs.len;
170 mem.copy(Limb, self.limbs[0..], other.limbs[0..other.len()]);
171 self.metadata = other.metadata;
172 }132 }
173133
174 /// Efficiently swap an Int with another. This swaps the limb pointers and a full copy is not134 /// Efficiently swap an Mutable with another. This swaps the limb pointers and a full copy is not
175 /// performed. The address of the limbs field will not be the same after this function.135 /// performed. The address of the limbs field will not be the same after this function.
176 pub fn swap(self: *Int, other: *Int) void {136 pub fn swap(self: *Mutable, other: *Mutable) void {
177 self.assertWritable();137 mem.swap(Mutable, self, other);
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]));
211 }138 }
212139
213 /// Returns the number of bits required to represent the integer in twos-complement form.140 pub fn dump(self: Mutable) void {
214 ///141 for (self.limbs[0..self.len]) |limb| {
215 /// If the integer is negative the value returned is the number of bits needed by a signed142 std.debug.warn("{x} ", .{limb});
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 }
238 }143 }
239144 std.debug.warn("capacity={} positive={}\n", .{ self.limbs.len, self.positive });
240 return bits;
241 }145 }
242146
243 pub fn fitsInTwosComp(self: Int, is_signed: bool, bit_count: usize) bool {147 /// Clones an Mutable and returns a new Mutable with the same value. The new Mutable is a deep copy and
244 if (self.eqZero()) {148 /// can be modified separately from the original.
245 return true;149 /// Asserts that limbs is big enough to store the value.
246 }150 pub fn clone(other: Mutable, limbs: []Limb) Mutable {
247 if (!is_signed and !self.isPositive()) {151 mem.copy(Limb, limbs, other.limbs[0..other.len]);
248 return false;152 return .{
249 }153 .limbs = limbs,
250154 .len = other.len,
251 const req_bits = self.bitCountTwosComp() + @boolToInt(self.isPositive() and is_signed);155 .positive = other.positive,
252 return bit_count >= req_bits;156 };
253 }157 }
254158
255 /// Returns whether self can fit into an integer of the requested type.159 pub fn negate(self: *Mutable) void {
256 pub fn fits(self: Int, comptime T: type) bool {160 self.positive = !self.positive;
257 return self.fitsInTwosComp(T.is_signed, T.bit_count);
258 }161 }
259162
260 /// Returns the approximate size of the integer in the given base. Negative values accommodate for163 /// Modify to become the absolute value
261 /// the minus sign. This is used for determining the number of characters needed to print the164 pub fn abs(self: *Mutable) void {
262 /// value. It is inexact and may exceed the given value by ~1-2 bytes.165 self.positive = true;
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;
266 }166 }
267167
268 /// Sets an Int to value. Value must be an primitive integer type.168 /// Sets the Mutable to value. Value must be an primitive integer type.
269 pub fn set(self: *Int, value: var) Allocator.Error!void {169 /// Asserts the value fits within the limbs buffer.
270 self.assertWritable();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 {
271 const T = @TypeOf(value);173 const T = @TypeOf(value);
272174
273 switch (@typeInfo(T)) {175 switch (@typeInfo(T)) {
274 .Int => |info| {176 .Int => |info| {
275 const UT = if (T.is_signed) std.meta.Int(false, T.bit_count - 1) else T;177 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));179 const needed_limbs = @sizeOf(UT) / @sizeOf(Limb);
278 self.metadata = 0;180 assert(needed_limbs <= self.limbs.len); // value too big
279 self.setSign(value >= 0);181 self.len = 0;
182 self.positive = value >= 0;
280183
281 var w_value: UT = if (value < 0) @intCast(UT, -value) else @intCast(UT, value);184 var w_value: UT = if (value < 0) @intCast(UT, -value) else @intCast(UT, value);
282185
283 if (info.bits <= Limb.bit_count) {186 if (info.bits <= Limb.bit_count) {
284 self.limbs[0] = @as(Limb, w_value);187 self.limbs[0] = @as(Limb, w_value);
285 self.metadata += 1;188 self.len += 1;
286 } else {189 } else {
287 var i: usize = 0;190 var i: usize = 0;
288 while (w_value != 0) : (i += 1) {191 while (w_value != 0) : (i += 1) {
289 self.limbs[i] = @truncate(Limb, w_value);192 self.limbs[i] = @truncate(Limb, w_value);
290 self.metadata += 1;193 self.len += 1;
291194
292 // TODO: shift == 64 at compile-time fails. Fails on u128 limbs.195 // TODO: shift == 64 at compile-time fails. Fails on u128 limbs.
293 w_value >>= Limb.bit_count / 2;196 w_value >>= Limb.bit_count / 2;
...@@ -299,10 +202,10 @@ pub const Int = struct {...@@ -299,10 +202,10 @@ pub const Int = struct {
299 comptime var w_value = if (value < 0) -value else value;202 comptime var w_value = if (value < 0) -value else value;
300203
301 const req_limbs = @divFloor(math.log2(w_value), Limb.bit_count) + 1;204 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;207 self.len = req_limbs;
305 self.setSign(value >= 0);208 self.positive = value >= 0;
306209
307 if (w_value <= maxInt(Limb)) {210 if (w_value <= maxInt(Limb)) {
308 self.limbs[0] = w_value;211 self.limbs[0] = w_value;
...@@ -318,98 +221,35 @@ pub const Int = struct {...@@ -318,98 +221,35 @@ pub const Int = struct {
318 }221 }
319 }222 }
320 },223 },
321 else => {224 else => @compileError("cannot set Mutable using type " ++ @typeName(T)),
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 },
373 }225 }
374 }226 }
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
400 /// Set self from the string representation `value`.228 /// Set self from the string representation `value`.
401 ///229 ///
402 /// `value` must contain only digits <= `base` and is case insensitive. Base prefixes are230 /// `value` must contain only digits <= `base` and is case insensitive. Base prefixes are
403 /// not allowed (e.g. 0x43 should simply be 43). Underscores in the input string are231 /// not allowed (e.g. 0x43 should simply be 43). Underscores in the input string are
404 /// ignored and can be used as digit separators.232 /// ignored and can be used as digit separators.
405 ///233 ///
406 /// Returns an error if memory could not be allocated or `value` has invalid digits for the234 /// Asserts there is enough memory for the value in `self.limbs`. An upper bound on number of limbs can
407 /// requested base.235 /// be determined with `calcSetStringLimbCount`.
408 pub fn setString(self: *Int, base: u8, value: []const u8) !void {236 /// Asserts the base is in the range [2, 16].
409 self.assertWritable();237 ///
410 if (base < 2 or base > 16) {238 /// Returns an error if the value has invalid digits for the requested base.
411 return error.InvalidBase;239 ///
412 }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
414 var i: usize = 0;254 var i: usize = 0;
415 var positive = true;255 var positive = true;
...@@ -418,753 +258,561 @@ pub const Int = struct {...@@ -418,753 +258,561 @@ pub const Int = struct {
418 i += 1;258 i += 1;
419 }259 }
420260
421 const ap_base = Int.initFixed(([_]Limb{base})[0..]);261 const ap_base: Const = .{ .limbs = &[_]Limb{base}, .positive = true };
422 try self.set(0);262 self.set(0);
423263
424 for (value[i..]) |ch| {264 for (value[i..]) |ch| {
425 if (ch == '_') {265 if (ch == '_') {
426 continue;266 continue;
427 }267 }
428 const d = try charToDigit(ch, base);268 const d = try std.fmt.charToDigit(ch, base);
429269 const ap_d: Const = .{ .limbs = &[_]Limb{d}, .positive = true };
430 const ap_d = Int.initFixed(([_]Limb{d})[0..]);
431270
432 try self.mul(self.*, ap_base);271 self.mul(self.toConst(), ap_base, limbs_buffer, allocator);
433 try self.add(self.*, ap_d);272 self.add(self.toConst(), ap_d);
434 }273 }
435 self.setSign(positive);274 self.positive = positive;
436 }275 }
437276
438 /// Converts self to a string in the requested base. Memory is allocated from the provided277 /// r = a + scalar
439 /// allocator and not the one present in self.278 ///
440 /// TODO make this call format instead of the other way around279 /// r and a may be aliases.
441 pub fn toString(self: Int, allocator: *Allocator, base: u8, uppercase: bool) ![]const u8 {280 /// scalar is a primitive integer type.
442 if (base < 2 or base > 16) {281 ///
443 return error.InvalidBase;282 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
444 }283 /// r is `math.max(a.limbs.len, calcLimbLen(scalar)) + 1`.
445284 pub fn addScalar(r: *Mutable, a: Const, scalar: var) void {
446 var digits = ArrayList(u8).init(allocator);285 var limbs: [calcLimbLen(scalar)]Limb = undefined;
447 try digits.ensureCapacity(self.sizeInBase(base) + 1);286 const operand = init(&limbs, scalar).toConst();
448 defer digits.deinit();287 return add(r, a, operand);
288 }
449289
450 if (self.eqZero()) {290 /// r = a + b
451 try digits.append('0');291 ///
452 return digits.toOwnedSlice();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;
453 }303 }
454304
455 // Power of two: can do a single pass and use masks to extract digits.305 if (a.limbs.len == 1 and b.limbs.len == 1 and a.positive == b.positive) {
456 if (math.isPowerOfTwo(base)) {306 if (!@addWithOverflow(Limb, a.limbs[0], b.limbs[0], &r.limbs[0])) {
457 const base_shift = math.log2_int(Limb, base);307 r.len = 1;
458308 r.positive = a.positive;
459 for (self.limbs[0..self.len()]) |limb| {309 return;
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 }
466 }310 }
311 }
467312
468 while (true) {313 if (a.positive != b.positive) {
469 // always will have a non-zero digit somewhere314 if (a.positive) {
470 const c = digits.pop();315 // (a) + (-b) => a - b
471 if (c != '0') {316 r.sub(a, b.abs());
472 digits.append(c) catch unreachable;317 } else {
473 break;318 // (-a) + (b) => b - a
474 }319 r.sub(b, a.abs());
475 }320 }
476 } else {321 } else {
477 // Non power-of-two: batch divisions per word size.322 if (a.limbs.len >= b.limbs.len) {
478 const digits_per_limb = math.log(Limb, base, maxInt(Limb));323 lladd(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]);
479 var limb_base: Limb = 1;324 r.normalize(a.limbs.len + 1);
480 var j: usize = 0;325 } else {
481 while (j < digits_per_limb) : (j += 1) {326 lladd(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]);
482 limb_base *= base;327 r.normalize(b.limbs.len + 1);
483 }328 }
484329
485 var q = try self.clone2(allocator);330 r.positive = a.positive;
486 defer q.deinit();331 }
487 q.abs();332 }
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);
495333
496 var r_word = r.limbs[0];334 /// r = a - b
497 var i: usize = 0;335 ///
498 while (i < digits_per_limb) : (i += 1) {336 /// r, a and b may be aliases.
499 const ch = try digitToChar(@intCast(u8, r_word % base), base, uppercase);337 ///
500 r_word /= base;338 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
501 try digits.append(ch);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;
502 }372 }
503 }373 }
374 }
375 }
504376
505 {377 /// rma = a * b
506 debug.assert(q.len() == 1);378 ///
507379 /// `rma` may alias with `a` or `b`.
508 var r_word = q.limbs[0];380 /// `a` and `b` may alias with each other.
509 while (r_word != 0) {381 ///
510 const ch = try digitToChar(@intCast(u8, r_word % base), base, uppercase);382 /// Asserts the result fits in `rma`. An upper bound on the number of limbs needed by
511 r_word /= base;383 /// rma is given by `a.limbs.len + b.limbs.len + 1`.
512 try digits.append(ch);384 ///
513 }385 /// `limbs_buffer` is used for temporary storage. The amount required is given by `calcMulLimbsBufferLen`.
514 }386 pub fn mul(rma: *Mutable, a: Const, b: Const, limbs_buffer: []Limb, allocator: ?*Allocator) void {
515 }387 var buf_index: usize = 0;
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 }
525388
526 /// To allow `std.fmt.printf` to work with Int.389 const a_copy = if (rma.limbs.ptr == a.limbs.ptr) blk: {
527 /// TODO make this non-allocating390 const start = buf_index;
528 /// TODO support read-only fixed integers391 mem.copy(Limb, limbs_buffer[buf_index..], a.limbs);
529 pub fn format(392 buf_index += a.limbs.len;
530 self: Int,393 break :blk a.toMutable(limbs_buffer[start..buf_index]).toConst();
531 comptime fmt: []const u8,394 } else a;
532 options: std.fmt.FormatOptions,
533 out_stream: var,
534 ) !void {
535 comptime var radix = 10;
536 comptime var uppercase = false;
537395
538 if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "d")) {396 const b_copy = if (rma.limbs.ptr == b.limbs.ptr) blk: {
539 radix = 10;397 const start = buf_index;
540 uppercase = false;398 mem.copy(Limb, limbs_buffer[buf_index..], b.limbs);
541 } else if (comptime std.mem.eql(u8, fmt, "b")) {399 buf_index += b.limbs.len;
542 radix = 2;400 break :blk b.toMutable(limbs_buffer[start..buf_index]).toConst();
543 uppercase = false;401 } else b;
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 }
553402
554 var buf: [4096]u8 = undefined;403 return rma.mulNoAlias(a_copy, b_copy, allocator);
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);
558 }404 }
559405
560 /// Returns math.Order.lt, math.Order.eq, math.Order.gt if |a| < |b|, |a| ==406 /// rma = a * b
561 /// |b| or |a| > |b| respectively.407 ///
562 pub fn cmpAbs(a: Int, b: Int) math.Order {408 /// `rma` may not alias with `a` or `b`.
563 if (a.len() < b.len()) {409 /// `a` and `b` may alias with each other.
564 return .lt;410 ///
565 }411 /// Asserts the result fits in `rma`. An upper bound on the number of limbs needed by
566 if (a.len() > b.len()) {412 /// rma is given by `a.limbs.len + b.limbs.len + 1`.
567 return .gt;413 ///
568 }414 /// If `allocator` is provided, it will be used for temporary storage to improve
569415 /// multiplication performance. `error.OutOfMemory` is handled with a fallback algorithm.
570 var i: usize = a.len() - 1;416 pub fn mulNoAlias(rma: *Mutable, a: Const, b: Const, allocator: ?*Allocator) void {
571 while (i != 0) : (i -= 1) {417 assert(rma.limbs.ptr != a.limbs.ptr); // illegal aliasing
572 if (a.limbs[i] != b.limbs[i]) {418 assert(rma.limbs.ptr != b.limbs.ptr); // illegal aliasing
573 break;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;
574 }425 }
575 }426 }
576427
577 if (a.limbs[i] < b.limbs[i]) {428 mem.set(Limb, rma.limbs[0 .. a.limbs.len + b.limbs.len + 1], 0);
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 }
605429
606 /// Returns true if |a| == |b|.430 llmulacc(allocator, rma.limbs, a.limbs, b.limbs);
607 pub fn eqAbs(a: Int, b: Int) bool {
608 return cmpAbs(a, b) == .eq;
609 }
610431
611 /// Returns true if a == b.432 rma.normalize(a.limbs.len + b.limbs.len);
612 pub fn eq(a: Int, b: Int) bool {433 rma.positive = (a.positive == b.positive);
613 return cmp(a, b) == .eq;
614 }434 }
615435
616 // Normalize a possible sequence of leading zeros.436 /// q = a / b (rem r)
617 //437 ///
618 // [1, 2, 3, 4, 0] -> [1, 2, 3, 4]438 /// a / b are floored (rounded towards 0).
619 // [1, 2, 0, 0, 0] -> [1, 2]439 /// q may alias with a or b.
620 // [0, 0, 0, 0, 0] -> [0]440 ///
621 fn normalize(r: *Int, length: usize) void {441 /// Asserts there is enough memory to store q and r.
622 debug.assert(length > 0);442 /// The upper bound for r limb count is a.limbs.len.
623 debug.assert(length <= r.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;459 // Trunc -> Floor.
626 while (j > 0) : (j -= 1) {460 if (!q.positive) {
627 if (r.limbs[j - 1] != 0) {461 const one: Const = .{ .limbs = &[_]Limb{1}, .positive = true };
628 break;462 q.sub(q.toConst(), one);
629 }463 r.add(q.toConst(), one);
630 }464 }
631465 r.positive = b.positive;
632 // Handle zero
633 r.setLen(if (j != 0) j else 1);
634 }466 }
635467
636 // Cannot be used as a result argument to any function.468 /// q = a / b (rem r)
637 fn readOnlyPositive(a: Int) Int {469 ///
638 return Int{470 /// a / b are truncated (rounded towards -inf).
639 .allocator = null,471 /// q may alias with a or b.
640 .metadata = a.len(),472 ///
641 .limbs = a.limbs,473 /// Asserts there is enough memory to store q and r.
642 };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;
643 }492 }
644493
645 /// r = a + b494 /// r = a << shift, in other words, r = a * 2^shift
646 ///495 ///
647 /// r, a and b may be aliases.496 /// r and a may alias.
648 ///497 ///
649 /// Returns an error if memory could not be allocated.498 /// Asserts there is enough memory to fit the result. The upper bound Limb count is
650 pub fn add(r: *Int, a: Int, b: Int) Allocator.Error!void {499 /// `a.limbs.len + (shift / (@sizeOf(Limb) * 8))`.
651 r.assertWritable();500 pub fn shiftLeft(r: *Mutable, a: Const, shift: usize) void {
652 if (a.eqZero()) {501 llshl(r.limbs[0..], a.limbs[0..a.limbs.len], shift);
653 try r.copy(b);502 r.normalize(a.limbs.len + (shift / Limb.bit_count) + 1);
654 return;503 r.positive = a.positive;
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 }
681 }504 }
682505
683 // Knuth 4.3.1, Algorithm A.506 /// r = a >> shift
684 fn lladd(r: []Limb, a: []const Limb, b: []const Limb) void {507 /// r and a may alias.
685 @setRuntimeSafety(false);508 ///
686 debug.assert(a.len != 0 and b.len != 0);509 /// Asserts there is enough memory to fit the result. The upper bound Limb count is
687 debug.assert(a.len >= b.len);510 /// `a.limbs.len - (shift / (@sizeOf(Limb) * 8))`.
688 debug.assert(r.len >= a.len + 1);511 pub fn shiftRight(r: *Mutable, a: Const, shift: usize) void {
689512 if (a.limbs.len <= shift / Limb.bit_count) {
690 var i: usize = 0;513 r.len = 1;
691 var carry: Limb = 0;514 r.positive = true;
692515 r.limbs[0] = 0;
693 while (i < b.len) : (i += 1) {516 return;
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]));
702 }517 }
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;
705 }522 }
706523
707 /// r = a - b524 /// r = a | b
525 /// r may alias with a or b.
708 ///526 ///
709 /// r, a and b may be aliases.527 /// a and b are zero-extended to the longer of a or b.
710 ///528 ///
711 /// Returns an error if memory could not be allocated.529 /// Asserts that r has enough limbs to store the result. Upper bound is `math.max(a.limbs.len, b.limbs.len)`.
712 pub fn sub(r: *Int, a: Int, b: Int) !void {530 pub fn bitOr(r: *Mutable, a: Const, b: Const) void {
713 r.assertWritable();531 if (a.limbs.len > b.limbs.len) {
714 if (a.isPositive() != b.isPositive()) {532 llor(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]);
715 if (a.isPositive()) {533 r.len = a.limbs.len;
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 }
723 } else {534 } else {
724 if (a.isPositive()) {535 llor(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]);
725 // (a) - (b) => a - b536 r.len = b.limbs.len;
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 }
751 }537 }
752 }538 }
753539
754 // Knuth 4.3.1, Algorithm S.540 /// r = a & b
755 fn llsub(r: []Limb, a: []const Limb, b: []const Limb) void {541 /// r may alias with a or b.
756 @setRuntimeSafety(false);542 ///
757 debug.assert(a.len != 0 and b.len != 0);543 /// Asserts that r has enough limbs to store the result. Upper bound is `math.min(a.limbs.len, b.limbs.len)`.
758 debug.assert(a.len > b.len or (a.len == b.len and a[a.len - 1] >= b[b.len - 1]));544 pub fn bitAnd(r: *Mutable, a: Const, b: Const) void {
759 debug.assert(r.len >= a.len);545 if (a.limbs.len > b.limbs.len) {
760546 lland(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]);
761 var i: usize = 0;547 r.normalize(b.limbs.len);
762 var borrow: Limb = 0;548 } else {
763549 lland(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]);
764 while (i < b.len) : (i += 1) {550 r.normalize(a.limbs.len);
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;
769 }551 }
552 }
770553
771 while (i < a.len) : (i += 1) {554 /// r = a ^ b
772 borrow = @boolToInt(@subWithOverflow(Limb, a[i], borrow, &r[i]));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);
773 }565 }
774
775 debug.assert(borrow == 0);
776 }566 }
777567
778 /// rma = a * b568 /// 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)`.
779 ///572 ///
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`.
781 ///595 ///
782 /// Returns an error if memory could not be allocated.596 /// `limbs_buffer` is used for temporary storage during the operation.
783 pub fn mul(rma: *Int, a: Int, b: Int) !void {597 pub fn gcdNoAlias(rma: *Mutable, x: Const, y: Const, limbs_buffer: *std.ArrayList(Limb)) !void {
784 rma.assertWritable();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;603 fn gcdLehmer(result: *Mutable, xa: Const, ya: Const, limbs_buffer: *std.ArrayList(Limb)) !void {
787 var aliased = rma.limbs.ptr == a.limbs.ptr or rma.limbs.ptr == b.limbs.ptr;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;612 if (x.toConst().order(y.toConst()) == .lt) {
790 if (aliased) {613 x.swap(&y);
791 sr = try Int.initCapacity(rma.allocator.?, a.len() + b.len());
792 r = &sr;
793 aliased = true;
794 }614 }
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());626 var xh: SignedDoubleLimb = x.limbs[x.len() - 1];
807 r.setSign(a.isPositive() == b.isPositive());627 var yh: SignedDoubleLimb = if (x.len() > y.len()) 0 else y.limbs[x.len() - 1];
808 }
809628
810 // a + b * c + *carry, sets carry to the overflow bits629 var A: SignedDoubleLimb = 1;
811 pub fn addMulLimbWithCarry(a: Limb, b: Limb, c: Limb, carry: *Limb) Limb {630 var B: SignedDoubleLimb = 0;
812 @setRuntimeSafety(false);631 var C: SignedDoubleLimb = 0;
813 var r1: Limb = undefined;632 var D: SignedDoubleLimb = 1;
814633
815 // r1 = a + *carry634 while (yh + C != 0 and yh + D != 0) {
816 const c1: Limb = @boolToInt(@addWithOverflow(Limb, a, carry.*, &r1));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 * c641 var t = A - q * C;
819 const bc = @as(DoubleLimb, math.mulWide(Limb, b, c));642 A = C;
820 const r2 = @truncate(Limb, bc);643 C = t;
821 const c2 = @truncate(Limb, bc >> Limb.bit_count);644 t = B - q * D;
645 B = D;
646 D = t;
822647
823 // r1 = r1 + r2648 t = xh - q * yh;
824 const c3: Limb = @boolToInt(@addWithOverflow(Limb, r1, r2, &r1));649 xh = yh;
650 yh = t;
651 }
825652
826 // This never overflows, c1, c3 are either 0 or 1 and if both are 1 then653 if (B == 0) {
827 // c2 is at least <= maxInt(Limb) - 2.654 // t_big = x % y, r is unused
828 carry.* = c1 + c2 + c3;655 try r.divTrunc(&t_big, x.toConst(), y.toConst());
656 assert(t_big.isPositive());
829657
830 return r1;658 x.swap(&y);
831 }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 {667 // t_big = Ax + By
834 @setRuntimeSafety(false);668 try r.mul(x.toConst(), Ap);
835 if (xi == 0) {669 try t_big.mul(y.toConst(), Bp);
836 return;670 try t_big.add(r.toConst(), t_big.toConst());
837 }
838671
839 var carry: usize = 0;672 // u = Cx + Dy, r as u
840 var a_lo = acc[0..y.len];673 try x.mul(x.toConst(), Cp);
841 var a_hi = acc[y.len..];674 try r.mul(y.toConst(), Dp);
675 try r.add(x.toConst(), r.toConst());
842676
843 var j: usize = 0;677 x.swap(&t_big);
844 while (j < a_lo.len) : (j += 1) {678 y.swap(&r);
845 a_lo[j] = @call(.{ .modifier = .always_inline }, addMulLimbWithCarry, .{ a_lo[j], y[j], xi, &carry });679 }
846 }680 }
847681
848 j = 0;682 // euclidean algorithm
849 while ((carry != 0) and (j < a_hi.len)) : (j += 1) {683 assert(x.toConst().order(y.toConst()) != .lt);
850 carry = @boolToInt(@addWithOverflow(Limb, a_hi[j], carry, &a_hi[j]));684
685 while (!y.toConst().eqZero()) {
686 try t_big.divTrunc(&r, x.toConst(), y.toConst());
687 x.swap(&y);
688 y.swap(&r);
851 }689 }
690
691 result.copy(x.toConst());
852 }692 }
853693
854 // Knuth 4.3.1, Algorithm M.694 /// Truncates by default.
855 //695 fn div(quo: *Mutable, rem: *Mutable, a: Const, b: Const, limbs_buffer: []Limb, allocator: ?*Allocator) void {
856 // r MUST NOT alias any of a or b.696 assert(!b.eqZero()); // division by zero
857 fn llmulacc(allocator: *Allocator, r: []Limb, a: []const Limb, b: []const Limb) error{OutOfMemory}!void {697 assert(quo != rem); // illegal aliasing
858 @setRuntimeSafety(false);
859698
860 const a_norm = a[0..llnormalize(a)];699 if (a.orderAbs(b) == .lt) {
861 const b_norm = b[0..llnormalize(b)];700 // quo may alias a so handle rem first
862 var x = a_norm;701 rem.copy(a);
863 var y = b_norm;702 rem.positive = a.positive == b.positive;
864 if (a_norm.len > b_norm.len) {
865 x = b_norm;
866 y = a_norm;
867 }
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.710 // Handle trailing zero-words of divisor/dividend. These are not handled in the following
872 if (x.len <= 48) {711 // algorithms.
873 // Basecase multiplication712 const a_zero_limb_count = blk: {
874 var i: usize = 0;713 var i: usize = 0;
875 while (i < x.len) : (i += 1) {714 while (i < a.limbs.len) : (i += 1) {
876 llmulDigit(r[i..], y, x[i]);715 if (a.limbs[i] != 0) break;
877 }716 }
878 } else {717 break :blk i;
879 // Karatsuba multiplication718 };
880 const split = @divFloor(x.len, 2);719 const b_zero_limb_count = blk: {
881 var x0 = x[0..split];720 var i: usize = 0;
882 var x1 = x[split..x.len];721 while (i < b.limbs.len) : (i += 1) {
883 var y0 = y[0..split];722 if (b.limbs[i] != 0) break;
884 var y1 = y[split..y.len];723 }
885724 break :blk i;
886 var tmp = try allocator.alloc(Limb, x1.len + y1.len + 1);725 };
887 defer allocator.free(tmp);
888 mem.set(Limb, tmp, 0);
889
890 try llmulacc(allocator, tmp, x1, y1);
891726
892 var length = llnormalize(tmp);727 const ab_zero_limb_count = math.min(a_zero_limb_count, b_zero_limb_count);
893 _ = llaccum(r[split..], tmp[0..length]);
894 _ = llaccum(r[split * 2 ..], tmp[0..length]);
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);755 // Shrink x, y such that the trailing zero limbs shared between are removed.
901 _ = llaccum(r[0..], tmp[0..length]);756 mem.copy(Limb, x.limbs, a.limbs[ab_zero_limb_count..a.limbs.len]);
902 _ = llaccum(r[split..], tmp[0..length]);757 mem.copy(Limb, y.limbs, b.limbs[ab_zero_limb_count..b.limbs.len]);
903758
904 const x_cmp = llcmp(x1, x0);759 divN(quo, rem, &x, &y, t_limbs, mul_limbs_buf, allocator);
905 const y_cmp = llcmp(y1, y0);760 quo.positive = (a.positive == b.positive);
906 if (x_cmp * y_cmp == 0) {761 }
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 }
918762
919 const y0_len = llnormalize(y0);763 if (ab_zero_limb_count != 0) {
920 const y1_len = llnormalize(y1);764 rem.shiftLeft(rem.toConst(), ab_zero_limb_count * Limb.bit_count);
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 }
939 }765 }
940 }766 }
941767
942 // r = r + a768 /// Handbook of Applied Cryptography, 14.20
943 fn llaccum(r: []Limb, a: []const Limb) Limb {769 ///
944 @setRuntimeSafety(false);770 /// x = qy + r where 0 <= r < y
945 debug.assert(r.len != 0 and a.len != 0);771 fn divN(
946 debug.assert(r.len >= a.len);772 q: *Mutable,
947773 r: *Mutable,
948 var i: usize = 0;774 x: *Mutable,
949 var carry: Limb = 0;775 y: *Mutable,
950776 tmp_limbs: []Limb,
951 while (i < a.len) : (i += 1) {777 mul_limb_buf: []Limb,
952 var c: Limb = 0;778 allocator: ?*Allocator,
953 c += @boolToInt(@addWithOverflow(Limb, r[i], a[i], &r[i]));779 ) void {
954 c += @boolToInt(@addWithOverflow(Limb, r[i], carry, &r[i]));780 assert(y.len >= 2);
955 carry = c;781 assert(x.len >= y.len);
956 }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) {795 // Normalize so y > Limb.bit_count / 2 (i.e. leading bit is set) and even
959 carry = @boolToInt(@addWithOverflow(Limb, r[i], carry, &r[i]));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;
960 }799 }
800 x.shiftLeft(x.toConst(), norm_shift);
801 y.shiftLeft(y.toConst(), norm_shift);
961802
962 return carry;803 const n = x.len - 1;
963 }804 const t = y.len - 1;
964805
965 /// Returns -1, 0, 1 if |a| < |b|, |a| == |b| or |a| > |b| respectively for limbs.806 // 1.
966 pub fn llcmp(a: []const Limb, b: []const Limb) i8 {807 q.len = n - t + 1;
967 @setRuntimeSafety(false);808 q.positive = true;
968 const a_len = llnormalize(a);809 mem.set(Limb, q.limbs[0..q.len], 0);
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 }
976810
977 var i: usize = a_len - 1;811 // 2.
978 while (i != 0) : (i -= 1) {812 tmp.shiftLeft(y.toConst(), Limb.bit_count * (n - t));
979 if (a[i] != b[i]) {813 while (x.toConst().order(tmp.toConst()) != .lt) {
980 break;814 q.limbs[n - t] += 1;
981 }815 x.sub(x.toConst(), tmp.toConst());
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);
1168 }816 }
1169817
1170 // 3.818 // 3.
...@@ -1193,7 +841,7 @@ pub const Int = struct {...@@ -1193,7 +841,7 @@ pub const Int = struct {
1193 r.limbs[2] = carry;841 r.limbs[2] = carry;
1194 r.normalize(3);842 r.normalize(3);
1195843
1196 if (r.cmpAbs(tmp) != .gt) {844 if (r.toConst().orderAbs(tmp.toConst()) != .gt) {
1197 break;845 break;
1198 }846 }
1199847
...@@ -1201,1748 +849,1284 @@ pub const Int = struct {...@@ -1201,1748 +849,1284 @@ pub const Int = struct {
1201 }849 }
1202850
1203 // 3.3851 // 3.3
1204 try tmp.set(q.limbs[i - t - 1]);852 tmp.set(q.limbs[i - t - 1]);
1205 try tmp.mul(tmp, y.*);853 tmp.mul(tmp.toConst(), y.toConst(), mul_limb_buf, allocator);
1206 try tmp.shiftLeft(tmp, Limb.bit_count * (i - t - 1));854 tmp.shiftLeft(tmp.toConst(), Limb.bit_count * (i - t - 1));
1207 try x.sub(x.*, tmp);855 x.sub(x.toConst(), tmp.toConst());
1208856
1209 if (!x.isPositive()) {857 if (!x.positive) {
1210 try tmp.shiftLeft(y.*, Limb.bit_count * (i - t - 1));858 tmp.shiftLeft(y.toConst(), Limb.bit_count * (i - t - 1));
1211 try x.add(x.*, tmp);859 x.add(x.toConst(), tmp.toConst());
1212 q.limbs[i - t - 1] -= 1;860 q.limbs[i - t - 1] -= 1;
1213 }861 }
1214 }862 }
1215863
1216 // Denormalize864 // Denormalize
1217 q.normalize(q.len());865 q.normalize(q.len);
1218866
1219 try r.shiftRight(x.*, norm_shift);867 r.shiftRight(x.toConst(), norm_shift);
1220 r.normalize(r.len());868 r.normalize(r.len);
1221 }869 }
1222870
1223 /// r = a << shift, in other words, r = a * 2^shift871 /// Normalize a possible sequence of leading zeros.
1224 pub fn shiftLeft(r: *Int, a: Int, shift: usize) !void {872 ///
1225 r.assertWritable();873 /// [1, 2, 3, 4, 0] -> [1, 2, 3, 4]
1226874 /// [1, 2, 0, 0, 0] -> [1, 2]
1227 try r.ensureCapacity(a.len() + (shift / Limb.bit_count) + 1);875 /// [0, 0, 0, 0, 0] -> [0]
1228 llshl(r.limbs[0..], a.limbs[0..a.len()], shift);876 fn normalize(r: *Mutable, length: usize) void {
1229 r.normalize(a.len() + (shift / Limb.bit_count) + 1);877 r.len = llnormalize(r.limbs[0..length]);
1230 r.setSign(a.isPositive());
1231 }878 }
879};
1232880
1233 fn llshl(r: []Limb, a: []const Limb, shift: usize) void {881/// A arbitrary-precision big integer, with a fixed set of immutable limbs.
1234 @setRuntimeSafety(false);882pub const Const = struct {
1235 debug.assert(a.len >= 1);883 /// Raw digits. These are:
1236 debug.assert(r.len >= a.len + (shift / Limb.bit_count) + 1);884 ///
1237885 /// * Little-endian ordered
1238 const limb_shift = shift / Limb.bit_count + 1;886 /// * limbs.len >= 1
1239 const interior_limb_shift = @intCast(Log2Limb, shift % Limb.bit_count);887 /// * Zero is represented as limbs.len == 1 with limbs[0] == 0.
1240888 ///
1241 var carry: Limb = 0;889 /// Accessing limbs directly should be avoided.
1242 var i: usize = 0;890 limbs: []const Limb,
1243 while (i < a.len) : (i += 1) {891 positive: bool,
1244 const src_i = a.len - i - 1;892
1245 const dst_i = src_i + limb_shift;893 /// The result is an independent resource which is managed by the caller.
1246894 pub fn toManaged(self: Const, allocator: *Allocator) Allocator.Error!Managed {
1247 const src_digit = a[src_i];895 const limbs = try allocator.alloc(Limb, math.max(Managed.default_capacity, self.limbs.len));
1248 r[dst_i] = carry | @call(.{ .modifier = .always_inline }, math.shr, .{896 mem.copy(Limb, limbs, self.limbs);
1249 Limb,897 return Managed{
1250 src_digit,898 .allocator = allocator,
1251 Limb.bit_count - @intCast(Limb, interior_limb_shift),899 .limbs = limbs,
1252 });900 .metadata = if (self.positive)
1253 carry = (src_digit << interior_limb_shift);901 self.limbs.len & ~Managed.sign_bit
1254 }902 else
1255903 self.limbs.len | Managed.sign_bit,
1256 r[limb_shift - 1] = carry;904 };
1257 mem.set(Limb, r[0 .. limb_shift - 1], 0);
1258 }905 }
1259906
1260 /// r = a >> shift907 /// Asserts `limbs` is big enough to store the value.
1261 pub fn shiftRight(r: *Int, a: Int, shift: usize) !void {908 pub fn toMutable(self: Const, limbs: []Limb) Mutable {
1262 r.assertWritable();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) {917 pub fn dump(self: Const) void {
1265 r.metadata = 1;918 for (self.limbs[0..self.limbs.len]) |limb| {
1266 r.limbs[0] = 0;919 std.debug.warn("{x} ", .{limb});
1267 return;
1268 }920 }
921 std.debug.warn("positive={}\n", .{self.positive});
922 }
1269923
1270 try r.ensureCapacity(a.len() - (shift / Limb.bit_count));924 pub fn abs(self: Const) Const {
1271 const r_len = llshr(r.limbs[0..], a.limbs[0..a.len()], shift);925 return .{
1272 r.metadata = a.len() - (shift / Limb.bit_count);926 .limbs = self.limbs,
1273 r.setSign(a.isPositive());927 .positive = true,
928 };
1274 }929 }
1275930
1276 fn llshr(r: []Limb, a: []const Limb, shift: usize) void {931 pub fn isOdd(self: Const) bool {
1277 @setRuntimeSafety(false);932 return self.limbs[0] & 1 != 0;
1278 debug.assert(a.len >= 1);933 }
1279 debug.assert(r.len >= a.len - (shift / Limb.bit_count));
1280934
1281 const limb_shift = shift / Limb.bit_count;935 pub fn isEven(self: Const) bool {
1282 const interior_limb_shift = @intCast(Log2Limb, shift % Limb.bit_count);936 return !self.isOdd();
937 }
1283938
1284 var carry: Limb = 0;939 /// Returns the number of bits required to represent the absolute value of an integer.
1285 var i: usize = 0;940 pub fn bitCountAbs(self: Const) usize {
1286 while (i < a.len - limb_shift) : (i += 1) {941 return (self.limbs.len - 1) * Limb.bit_count + (Limb.bit_count - @clz(Limb, self.limbs[self.limbs.len - 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 }
1298 }942 }
1299943
1300 /// r = a | b944 /// Returns the number of bits required to represent the integer in twos-complement form.
1301 ///945 ///
1302 /// a and b are zero-extended to the longer of a or b.946 /// If the integer is negative the value returned is the number of bits needed by a signed
1303 pub fn bitOr(r: *Int, a: Int, b: Int) !void {947 /// integer to represent the value. If positive the value is the number of bits for an
1304 r.assertWritable();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()) {955 // If the entire value has only one bit set (e.g. 0b100000000) then the negation in twos
1307 try r.ensureCapacity(a.len());956 // complement requires one less bit.
1308 llor(r.limbs[0..], a.limbs[0..a.len()], b.limbs[0..b.len()]);957 if (!self.positive) block: {
1309 r.setLen(a.len());958 bits += 1;
1310 } else {959
1311 try r.ensureCapacity(b.len());960 if (@popCount(Limb, self.limbs[self.limbs.len - 1]) == 1) {
1312 llor(r.limbs[0..], b.limbs[0..b.len()], a.limbs[0..a.len()]);961 for (self.limbs[0 .. self.limbs.len - 1]) |limb| {
1313 r.setLen(b.len());962 if (@popCount(Limb, limb) != 0) {
963 break :block;
964 }
965 }
966
967 bits -= 1;
968 }
1314 }969 }
1315 }
1316970
1317 fn llor(r: []Limb, a: []const Limb, b: []const Limb) void {971 return bits;
1318 @setRuntimeSafety(false);972 }
1319 debug.assert(r.len >= a.len);
1320 debug.assert(a.len >= b.len);
1321973
1322 var i: usize = 0;974 pub fn fitsInTwosComp(self: Const, is_signed: bool, bit_count: usize) bool {
1323 while (i < b.len) : (i += 1) {975 if (self.eqZero()) {
1324 r[i] = a[i] | b[i];976 return true;
1325 }977 }
1326 while (i < a.len) : (i += 1) {978 if (!is_signed and !self.positive) {
1327 r[i] = a[i];979 return false;
1328 }980 }
981
982 const req_bits = self.bitCountTwosComp() + @boolToInt(self.positive and is_signed);
983 return bit_count >= req_bits;
1329 }984 }
1330985
1331 /// r = a & b986 /// Returns whether self can fit into an integer of the requested type.
1332 pub fn bitAnd(r: *Int, a: Int, b: Int) !void {987 pub fn fits(self: Const, comptime T: type) bool {
1333 r.assertWritable();988 const info = @typeInfo(T).Int;
989 return self.fitsInTwosComp(info.is_signed, info.bits);
990 }
1334991
1335 if (a.len() > b.len()) {992 /// Returns the approximate size of the integer in the given base. Negative values accommodate for
1336 try r.ensureCapacity(b.len());993 /// the minus sign. This is used for determining the number of characters needed to print the
1337 lland(r.limbs[0..], a.limbs[0..a.len()], b.limbs[0..b.len()]);994 /// value. It is inexact and may exceed the given value by ~1-2 bytes.
1338 r.normalize(b.len());995 /// TODO See if we can make this exact.
1339 } else {996 pub fn sizeInBaseUpperBound(self: Const, base: usize) usize {
1340 try r.ensureCapacity(a.len());997 const bit_count = @as(usize, @boolToInt(!self.positive)) + self.bitCountAbs();
1341 lland(r.limbs[0..], b.limbs[0..b.len()], a.limbs[0..a.len()]);998 return (bit_count / math.log2(base)) + 2;
1342 r.normalize(a.len());
1343 }
1344 }999 }
13451000
1346 fn lland(r: []Limb, a: []const Limb, b: []const Limb) void {1001 pub const ConvertError = error{
1347 @setRuntimeSafety(false);1002 NegativeIntoUnsigned,
1348 debug.assert(r.len >= b.len);1003 TargetTooSmall,
1349 debug.assert(a.len >= b.len);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;1014 if (self.bitCountTwosComp() > T.bit_count) {
1352 while (i < b.len) : (i += 1) {1015 return error.TargetTooSmall;
1353 r[i] = a[i] & b[i];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)),
1354 }1045 }
1355 }1046 }
13561047
1357 /// r = a ^ b1048 /// To allow `std.fmt.format` to work with this type.
1358 pub fn bitXor(r: *Int, a: Int, b: Int) !void {1049 /// If the integer is larger than `pow(2, 64 * @sizeOf(usize) * 8), this function will fail
1359 r.assertWritable();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()) {1062 if (fmt.len == 0 or comptime mem.eql(u8, fmt, "d")) {
1362 try r.ensureCapacity(a.len());1063 radix = 10;
1363 llxor(r.limbs[0..], a.limbs[0..a.len()], b.limbs[0..b.len()]);1064 uppercase = false;
1364 r.normalize(a.len());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;
1365 } else {1074 } else {
1366 try r.ensureCapacity(b.len());1075 @compileError("Unknown format string: '" ++ fmt ++ "'");
1367 llxor(r.limbs[0..], b.limbs[0..b.len()], a.limbs[0..a.len()]);
1368 r.normalize(b.len());
1369 }1076 }
1370 }
13711077
1372 fn llxor(r: []Limb, a: []const Limb, b: []const Limb) void {1078 var limbs: [128]Limb = undefined;
1373 @setRuntimeSafety(false);1079 const needed_limbs = calcDivLimbsBufferLen(self.limbs.len, 1);
1374 debug.assert(r.len >= a.len);1080 if (needed_limbs > limbs.len)
1375 debug.assert(a.len >= b.len);1081 return out_stream.writeAll("(BigInt)");
13761082
1377 var i: usize = 0;1083 // This is the inverse of calcDivLimbsBufferLen
1378 while (i < b.len) : (i += 1) {1084 const available_len = (limbs.len / 3) - 2;
1379 r[i] = a[i] ^ b[i];1085
1380 }1086 const biggest: Const = .{
1381 while (i < a.len) : (i += 1) {1087 .limbs = &([1]Limb{math.maxInt(Limb)} ** available_len),
1382 r[i] = a[i];1088 .positive = false,
1383 }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]);
1384 }1093 }
13851094
1386 pub fn gcd(rma: *Int, x: Int, y: Int) !void {1095 /// Converts self to a string in the requested base.
1387 rma.assertWritable();1096 /// Caller owns returned memory.
1388 var r = rma;1097 /// Asserts that `base` is in the range [2, 16].
1389 var aliased = rma.limbs.ptr == x.limbs.ptr or rma.limbs.ptr == y.limbs.ptr;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;1103 if (self.eqZero()) {
1392 if (aliased) {1104 return mem.dupe(allocator, u8, "0");
1393 sr = try Int.initCapacity(rma.allocator.?, math.max(x.len(), y.len()));
1394 r = &sr;
1395 aliased = true;
1396 }1105 }
1397 defer if (aliased) {1106 const string = try allocator.alloc(u8, self.sizeInBaseUpperBound(base));
1398 rma.swap(r);1107 errdefer allocator.free(string);
1399 r.deinit();
1400 };
14011108
1402 try gcdLehmer(r, x, y);1109 const limbs = try allocator.alloc(Limb, calcToStringLimbsBufferLen(self.limbs.len, base));
1403 }1110 defer allocator.free(limbs);
14041111
1405 fn gcdLehmer(r: *Int, xa: Int, ya: Int) !void {1112 return allocator.shrink(string, self.toString(string, base, uppercase, limbs));
1406 var x = try xa.clone();1113 }
1407 x.abs();
1408 defer x.deinit();
14091114
1410 var y = try ya.clone();1115 /// Converts self to a string in the requested base.
1411 y.abs();1116 /// Asserts that `base` is in the range [2, 16].
1412 defer y.deinit();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) {1128 if (self.eqZero()) {
1415 x.swap(&y);1129 string[0] = '0';
1130 return 1;
1416 }1131 }
14171132
1418 var T = try Int.init(r.allocator.?);1133 var digits_len: usize = 0;
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];
14271134
1428 var A: SignedDoubleLimb = 1;1135 // Power of two: can do a single pass and use masks to extract digits.
1429 var B: SignedDoubleLimb = 0;1136 if (math.isPowerOfTwo(base)) {
1430 var C: SignedDoubleLimb = 0;1137 const base_shift = math.log2_int(Limb, base);
1431 var D: SignedDoubleLimb = 1;
14321138
1433 while (yh + C != 0 and yh + D != 0) {1139 outer: for (self.limbs[0..self.limbs.len]) |limb| {
1434 const q = @divFloor(xh + A, yh + C);1140 var shift: usize = 0;
1435 const qp = @divFloor(xh + B, yh + D);1141 while (shift < Limb.bit_count) : (shift += base_shift) {
1436 if (q != qp) {1142 const r = @intCast(u8, (limb >> @intCast(Log2Limb, shift)) & @as(Limb, base - 1));
1437 break;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;
1438 }1148 }
1149 }
14391150
1440 var t = A - q * C;1151 // Always will have a non-zero digit somewhere.
1441 A = C;1152 while (string[digits_len - 1] == '0') {
1442 C = t;1153 digits_len -= 1;
1443 t = B - q * D;1154 }
1444 B = D;1155 } else {
1445 D = t;1156 // Non power-of-two: batch divisions per word size.
14461157 const digits_per_limb = math.log(Limb, base, maxInt(Limb));
1447 t = xh - q * yh;1158 var limb_base: Limb = 1;
1448 xh = yh;1159 var j: usize = 0;
1449 yh = t;1160 while (j < digits_per_limb) : (j += 1) {
1161 limb_base *= base;
1450 }1162 }
1163 const b: Const = .{ .limbs = &[_]Limb{limb_base}, .positive = true };
14511164
1452 if (B == 0) {1165 var q: Mutable = .{
1453 // T = x % y, r is unused1166 .limbs = limbs_buffer[0 .. self.limbs.len + 2],
1454 try Int.divTrunc(r, &T, x, y);1167 .positive = true, // Make absolute by ignoring self.positive.
1455 debug.assert(T.isPositive());1168 .len = self.limbs.len,
1169 };
1170 mem.copy(Limb, q.limbs, self.limbs);
14561171
1457 x.swap(&y);1172 var r: Mutable = .{
1458 y.swap(&T);1173 .limbs = limbs_buffer[q.limbs.len..][0..self.limbs.len],
1459 } else {1174 .positive = true,
1460 var storage: [8]Limb = undefined;1175 .len = 1,
1461 const Ap = FixedIntFromSignedDoubleLimb(A, storage[0..2]);1176 };
1462 const Bp = FixedIntFromSignedDoubleLimb(B, storage[2..4]);1177 r.limbs[0] = 0;
1463 const Cp = FixedIntFromSignedDoubleLimb(C, storage[4..6]);
1464 const Dp = FixedIntFromSignedDoubleLimb(D, storage[6..8]);
14651178
1466 // T = Ax + By1179 const rest_of_the_limbs_buf = limbs_buffer[q.limbs.len + r.limbs.len ..];
1467 try r.mul(x, Ap);
1468 try T.mul(y, Bp);
1469 try T.add(r.*, T);
14701180
1471 // u = Cx + Dy, r as u1181 while (q.len >= 2) {
1472 try x.mul(x, Cp);1182 // Passing an allocator here would not be helpful since this division is destroying
1473 try r.mul(y, Dp);1183 // information, not creating it. [TODO citation needed]
1474 try r.add(x, r.*);1184 q.divTrunc(&r, q.toConst(), b, rest_of_the_limbs_buf, null);
14751185
1476 x.swap(&T);1186 var r_word = r.limbs[0];
1477 y.swap(r);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 }
1478 }1194 }
1479 }
14801195
1481 // euclidean algorithm1196 {
1482 debug.assert(x.cmp(y) != .lt);1197 assert(q.len == 1);
14831198
1484 while (!y.eqZero()) {1199 var r_word = q.limbs[0];
1485 try Int.divTrunc(&T, r, x, y);1200 while (r_word != 0) {
1486 x.swap(&y);1201 const ch = std.fmt.digitToChar(@intCast(u8, r_word % base), uppercase);
1487 y.swap(r);1202 r_word /= base;
1203 string[digits_len] = ch;
1204 digits_len += 1;
1205 }
1206 }
1488 }1207 }
14891208
1490 r.swap(&x);1209 if (!self.positive) {
1491 }1210 string[digits_len] = '-';
1492};1211 digits_len += 1;
14931212 }
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;
15181213
1519 comptime var i: usize = 0;1214 const s = string[0..digits_len];
1520 inline while (i < s_limb_count) : (i += 1) {1215 mem.reverse(u8, s);
1521 const result = @as(Limb, s & maxInt(Limb));1216 return s.len;
1522 s >>= Limb.bit_count / 2;
1523 s >>= Limb.bit_count / 2;
1524 testing.expect(a.limbs[i] == result);
1525 }1217 }
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);1219 /// Returns `math.Order.lt`, `math.Order.eq`, `math.Order.gt` if
1669 testing.expect(a.bitCountTwosComp() == 0);1220 /// `|a| < |b|`, `|a| == |b|`, or `|a| > |b|` respectively.
16701221 pub fn orderAbs(a: Const, b: Const) math.Order {
1671 testing.expect((try a.to(u0)) == 0);1222 if (a.limbs.len < b.limbs.len) {
1672 testing.expect((try a.to(i0)) == 0);1223 return .lt;
16731224 }
1674 try a.set(-1);1225 if (a.limbs.len > b.limbs.len) {
1675 testing.expect(a.bitCountTwosComp() == 1);1226 return .gt;
1676 testing.expect((try a.to(i1)) == -1);1227 }
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);
22791228
2280 testing.expect((try q.to(u64)) == op1 / op2);1229 var i: usize = a.limbs.len - 1;
2281 testing.expect((try r.to(u64)) == 3);1230 while (i != 0) : (i -= 1) {
2282}1231 if (a.limbs[i] != b.limbs[i]) {
1232 break;
1233 }
1234 }
22831235
2284test "big.int div multi>2-single" {1236 if (a.limbs[i] < b.limbs[i]) {
2285 const op1 = 0xfefefefefefefefefefefefefefefefe;1237 return .lt;
2286 const op2 = 0xefab8;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);1245 /// Returns `math.Order.lt`, `math.Order.eq`, `math.Order.gt` if `a < b`, `a == b` or `a > b` respectively.
2289 defer a.deinit();1246 pub fn order(a: Const, b: Const) math.Order {
2290 var b = try Int.initSet(testing.allocator, op2);1247 if (a.positive != b.positive) {
2291 defer b.deinit();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);1259 /// Same as `order` but the right-hand operand is a primitive integer.
2294 defer q.deinit();1260 pub fn orderAgainstScalar(lhs: Const, scalar: var) math.Order {
2295 var r = try Int.init(testing.allocator);1261 var limbs: [calcLimbLen(scalar)]Limb = undefined;
2296 defer r.deinit();1262 const rhs = Mutable.init(&limbs, scalar);
2297 try Int.divTrunc(&q, &r, a, b);1263 return order(lhs, rhs.toConst());
1264 }
22981265
2299 testing.expect((try q.to(u128)) == op1 / op2);1266 /// Returns true if `a == 0`.
2300 testing.expect((try r.to(u32)) == 0x3e4e);1267 pub fn eqZero(a: Const) bool {
2301}1268 return a.limbs.len == 1 and a.limbs[0] == 0;
1269 }
23021270
2303test "big.int div single-single q < r" {1271 /// Returns true if `|a| == |b|`.
2304 var a = try Int.initSet(testing.allocator, 0x0078f432);1272 pub fn eqAbs(a: Const, b: Const) bool {
2305 defer a.deinit();1273 return orderAbs(a, b) == .eq;
2306 var b = try Int.initSet(testing.allocator, 0x01000000);1274 }
2307 defer b.deinit();
23081275
2309 var q = try Int.init(testing.allocator);1276 /// Returns true if `a == b`.
2310 defer q.deinit();1277 pub fn eq(a: Const, b: Const) bool {
2311 var r = try Int.init(testing.allocator);1278 return order(a, b) == .eq;
2312 defer r.deinit();1279 }
2313 try Int.divTrunc(&q, &r, a, b);1280};
23141281
2315 testing.expect((try q.to(u64)) == 0);1282/// An arbitrary-precision big integer along with an allocator which manages the memory.
2316 testing.expect((try r.to(u64)) == 0x0078f432);1283///
2317}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" {1289 /// Default number of limbs to allocate on creation of a `Managed`.
2320 var a = try Int.initSet(testing.allocator, 10);1290 pub const default_capacity = 4;
2321 defer a.deinit();
2322 var b = try Int.initSet(testing.allocator, 10);
2323 defer b.deinit();
23241291
2325 var q = try Int.init(testing.allocator);1292 /// Allocator used by the Managed when requesting memory.
2326 defer q.deinit();1293 allocator: *Allocator,
2327 var r = try Int.init(testing.allocator);
2328 defer r.deinit();
2329 try Int.divTrunc(&q, &r, a, b);
23301294
2331 testing.expect((try q.to(u64)) == 1);1295 /// Raw digits. These are:
2332 testing.expect((try r.to(u64)) == 0);1296 ///
2333}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" {1304 /// High bit is the sign bit. If set, Managed is negative, else Managed is positive.
2336 var a = try Int.initSet(testing.allocator, 3);1305 /// The remaining bits represent the number of limbs used by Managed.
2337 defer a.deinit();1306 metadata: usize,
2338 var b = try Int.initSet(testing.allocator, 10);
2339 defer b.deinit();
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);1314 pub fn toMutable(self: Managed) Mutable {
2344 testing.expect((try b.to(u64)) == 3);1315 return .{
2345}1316 .limbs = self.limbs,
1317 .positive = self.isPositive(),
1318 .len = self.len(),
1319 };
1320 }
23461321
2347test "big.int div multi-multi q < r" {1322 pub fn toConst(self: Managed) Const {
2348 const op1 = 0x1ffffffff0078f432;1323 return .{
2349 const op2 = 0x1ffffffff01000000;1324 .limbs = self.limbs[0..self.len()],
2350 var a = try Int.initSet(testing.allocator, op1);1325 .positive = self.isPositive(),
2351 defer a.deinit();1326 };
2352 var b = try Int.initSet(testing.allocator, op2);1327 }
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}
23641328
2365test "big.int div trunc single-single +/+" {1329 /// Creates a new `Managed` with value `value`.
2366 const u: i32 = 5;1330 ///
2367 const v: i32 = 3;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);1338 /// Creates a new Managed with a specific capacity. If capacity < default_capacity then the
2370 defer a.deinit();1339 /// default capacity will be used instead.
2371 var b = try Int.initSet(testing.allocator, v);1340 /// The integer value after initializing is `0`.
2372 defer b.deinit();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);1353 /// Returns the number of limbs currently in use.
2375 defer q.deinit();1354 pub fn len(self: Managed) usize {
2376 var r = try Int.init(testing.allocator);1355 return self.metadata & ~sign_bit;
2377 defer r.deinit();1356 }
2378 try Int.divTrunc(&q, &r, a, b);
23791357
2380 // n = q * d + r1358 /// Returns whether an Managed is positive.
2381 // 5 = 1 * 3 + 21359 pub fn isPositive(self: Managed) bool {
2382 const eq = @divTrunc(u, v);1360 return self.metadata & sign_bit == 0;
2383 const er = @mod(u, v);1361 }
23841362
2385 testing.expect((try q.to(i32)) == eq);1363 /// Sets the sign of an Managed.
2386 testing.expect((try r.to(i32)) == er);1364 pub fn setSign(self: *Managed, positive: bool) void {
2387}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 -/+" {1372 /// Sets the length of an Managed.
2390 const u: i32 = -5;1373 ///
2391 const v: i32 = 3;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);1380 pub fn setMetadata(self: *Managed, positive: bool, length: usize) void {
2394 defer a.deinit();1381 self.metadata = if (positive) length & ~sign_bit else length | sign_bit;
2395 var b = try Int.initSet(testing.allocator, v);1382 }
2396 defer b.deinit();
23971383
2398 var q = try Int.init(testing.allocator);1384 /// Ensures an Managed has enough space allocated for capacity limbs. If the Managed does not have
2399 defer q.deinit();1385 /// sufficient capacity, the exact amount will be allocated. This occurs even if the requested
2400 var r = try Int.init(testing.allocator);1386 /// capacity is only greater than the current capacity by one limb.
2401 defer r.deinit();1387 pub fn ensureCapacity(self: *Managed, capacity: usize) !void {
2402 try Int.divTrunc(&q, &r, a, b);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 + r1394 /// Frees all associated memory.
2405 // -5 = 1 * -3 - 21395 pub fn deinit(self: *Managed) void {
2406 const eq = -1;1396 self.allocator.free(self.limbs);
2407 const er = -2;1397 self.* = undefined;
1398 }
24081399
2409 testing.expect((try q.to(i32)) == eq);1400 /// Returns a `Managed` with the same value. The returned `Managed` is a deep copy and
2410 testing.expect((try r.to(i32)) == er);1401 /// can be modified separately from the original, and its resources are managed
2411}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 +/-" {1407 pub fn cloneWithDifferentAllocator(other: Managed, allocator: *Allocator) !Managed {
2414 const u: i32 = 5;1408 return Managed{
2415 const v: i32 = -3;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);1419 /// Copies the value of the integer to an existing `Managed` so that they both have the same value.
2418 defer a.deinit();1420 /// Extra memory will be allocated if the receiver does not have enough capacity.
2419 var b = try Int.initSet(testing.allocator, v);1421 pub fn copy(self: *Managed, other: Const) !void {
2420 defer b.deinit();1422 if (self.limbs.ptr == other.limbs.ptr) return;
24211423
2422 var q = try Int.init(testing.allocator);1424 try self.ensureCapacity(other.limbs.len);
2423 defer q.deinit();1425 mem.copy(Limb, self.limbs[0..], other.limbs[0..other.limbs.len]);
2424 var r = try Int.init(testing.allocator);1426 self.setMetadata(other.positive, other.limbs.len);
2425 defer r.deinit();1427 }
2426 try Int.divTrunc(&q, &r, a, b);
24271428
2428 // n = q * d + r1429 /// Efficiently swap a `Managed` with another. This swaps the limb pointers and a full copy is not
2429 // 5 = -1 * -3 + 21430 /// performed. The address of the limbs field will not be the same after this function.
2430 const eq = -1;1431 pub fn swap(self: *Managed, other: *Managed) void {
2431 const er = 2;1432 mem.swap(Managed, self, other);
1433 }
24321434
2433 testing.expect((try q.to(i32)) == eq);1435 /// Debugging tool: prints the state to stderr.
2434 testing.expect((try r.to(i32)) == er);1436 pub fn dump(self: Managed) void {
2435}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 -/-" {1443 /// Negate the sign.
2438 const u: i32 = -5;1444 pub fn negate(self: *Managed) void {
2439 const v: i32 = -3;1445 self.metadata ^= sign_bit;
1446 }
24401447
2441 var a = try Int.initSet(testing.allocator, u);1448 /// Make positive.
2442 defer a.deinit();1449 pub fn abs(self: *Managed) void {
2443 var b = try Int.initSet(testing.allocator, v);1450 self.metadata &= ~sign_bit;
2444 defer b.deinit();1451 }
24451452
2446 var q = try Int.init(testing.allocator);1453 pub fn isOdd(self: Managed) bool {
2447 defer q.deinit();1454 return self.limbs[0] & 1 != 0;
2448 var r = try Int.init(testing.allocator);1455 }
2449 defer r.deinit();
2450 try Int.divTrunc(&q, &r, a, b);
24511456
2452 // n = q * d + r1457 pub fn isEven(self: Managed) bool {
2453 // -5 = 1 * -3 - 21458 return !self.isOdd();
2454 const eq = 1;1459 }
2455 const er = -2;
24561460
2457 testing.expect((try q.to(i32)) == eq);1461 /// Returns the number of bits required to represent the absolute value of an integer.
2458 testing.expect((try r.to(i32)) == er);1462 pub fn bitCountAbs(self: Managed) usize {
2459}1463 return self.toConst().bitCountAbs();
1464 }
24601465
2461test "big.int div floor single-single +/+" {1466 /// Returns the number of bits required to represent the integer in twos-complement form.
2462 const u: i32 = 5;1467 ///
2463 const v: i32 = 3;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);1478 pub fn fitsInTwosComp(self: Managed, is_signed: bool, bit_count: usize) bool {
2466 defer a.deinit();1479 return self.toConst().fitsInTwosComp(is_signed, bit_count);
2467 var b = try Int.initSet(testing.allocator, v);1480 }
2468 defer b.deinit();
24691481
2470 var q = try Int.init(testing.allocator);1482 /// Returns whether self can fit into an integer of the requested type.
2471 defer q.deinit();1483 pub fn fits(self: Managed, comptime T: type) bool {
2472 var r = try Int.init(testing.allocator);1484 return self.toConst().fits(T);
2473 defer r.deinit();1485 }
2474 try Int.divFloor(&q, &r, a, b);
24751486
2476 // n = q * d + r1487 /// Returns the approximate size of the integer in the given base. Negative values accommodate for
2477 // 5 = 1 * 3 + 21488 /// the minus sign. This is used for determining the number of characters needed to print the
2478 const eq = 1;1489 /// value. It is inexact and may exceed the given value by ~1-2 bytes.
2479 const er = 2;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);1494 /// Sets an Managed to value. Value must be an primitive integer type.
2482 testing.expect((try r.to(i32)) == er);1495 pub fn set(self: *Managed, value: var) Allocator.Error!void {
2483}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 -/+" {1502 pub const ConvertError = Const.ConvertError;
2486 const u: i32 = -5;
2487 const v: i32 = 3;
24881503
2489 var a = try Int.initSet(testing.allocator, u);1504 /// Convert self to type T.
2490 defer a.deinit();1505 ///
2491 var b = try Int.initSet(testing.allocator, v);1506 /// Returns an error if self cannot be narrowed into the requested type without truncation.
2492 defer b.deinit();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);1511 /// Set self from the string representation `value`.
2495 defer q.deinit();1512 ///
2496 var r = try Int.init(testing.allocator);1513 /// `value` must contain only digits <= `base` and is case insensitive. Base prefixes are
2497 defer r.deinit();1514 /// not allowed (e.g. 0x43 should simply be 43). Underscores in the input string are
2498 try Int.divFloor(&q, &r, a, b);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 + r1532 /// Converts self to a string in the requested base. Memory is allocated from the provided
2501 // -5 = -2 * 3 + 11533 /// allocator and not the one present in self.
2502 const eq = -2;1534 pub fn toString(self: Managed, allocator: *Allocator, base: u8, uppercase: bool) ![]u8 {
2503 const er = 1;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);1539 /// To allow `std.fmt.format` to work with `Managed`.
2506 testing.expect((try r.to(i32)) == er);1540 /// If the integer is larger than `pow(2, 64 * @sizeOf(usize) * 8), this function will fail
2507}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 +/-" {1553 /// Returns math.Order.lt, math.Order.eq, math.Order.gt if |a| < |b|, |a| ==
2510 const u: i32 = 5;1554 /// |b| or |a| > |b| respectively.
2511 const v: i32 = -3;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);1559 /// Returns math.Order.lt, math.Order.eq, math.Order.gt if a < b, a == b or a
2514 defer a.deinit();1560 /// > b respectively.
2515 var b = try Int.initSet(testing.allocator, v);1561 pub fn order(a: Managed, b: Managed) math.Order {
2516 defer b.deinit();1562 return a.toConst().order(b.toConst());
1563 }
25171564
2518 var q = try Int.init(testing.allocator);1565 /// Returns true if a == 0.
2519 defer q.deinit();1566 pub fn eqZero(a: Managed) bool {
2520 var r = try Int.init(testing.allocator);1567 return a.toConst().eqZero();
2521 defer r.deinit();1568 }
2522 try Int.divFloor(&q, &r, a, b);
25231569
2524 // n = q * d + r1570 /// Returns true if |a| == |b|.
2525 // 5 = -2 * -3 - 11571 pub fn eqAbs(a: Managed, b: Managed) bool {
2526 const eq = -2;1572 return a.toConst().eqAbs(b.toConst());
2527 const er = -1;1573 }
25281574
2529 testing.expect((try q.to(i32)) == eq);1575 /// Returns true if a == b.
2530 testing.expect((try r.to(i32)) == er);1576 pub fn eq(a: Managed, b: Managed) bool {
2531}1577 return a.toConst().eq(b.toConst());
1578 }
25321579
2533test "big.int div floor single-single -/-" {1580 /// Normalize a possible sequence of leading zeros.
2534 const u: i32 = -5;1581 ///
2535 const v: i32 = -3;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);1589 var j = length;
2538 defer a.deinit();1590 while (j > 0) : (j -= 1) {
2539 var b = try Int.initSet(testing.allocator, v);1591 if (r.limbs[j - 1] != 0) {
2540 defer b.deinit();1592 break;
1593 }
1594 }
25411595
2542 var q = try Int.init(testing.allocator);1596 // Handle zero
2543 defer q.deinit();1597 r.setLen(if (j != 0) j else 1);
2544 var r = try Int.init(testing.allocator);1598 }
2545 defer r.deinit();
2546 try Int.divFloor(&q, &r, a, b);
25471599
2548 // n = q * d + r1600 /// r = a + scalar
2549 // -5 = 2 * -3 + 11601 ///
2550 const eq = 1;1602 /// r and a may be aliases.
2551 const er = -2;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);1613 /// r = a + b
2554 testing.expect((try r.to(i32)) == er);1614 ///
2555}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" {1625 /// r = a - b
2558 var a = try Int.initSet(testing.allocator, 0x8888999911110000ffffeeeeddddccccbbbbaaaa9999);1626 ///
2559 defer a.deinit();1627 /// r, a and b may be aliases.
2560 var b = try Int.initSet(testing.allocator, 0x99990000111122223333);1628 ///
2561 defer b.deinit();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);1637 /// rma = a * b
2564 defer q.deinit();1638 ///
2565 var r = try Int.init(testing.allocator);1639 /// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b.
2566 defer r.deinit();1640 ///
2567 try Int.divTrunc(&q, &r, a, b);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);1663 /// q = a / b (rem r)
2570 testing.expect((try r.to(u128)) == 0x28de0acacd806823638);1664 ///
2571}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" {1682 /// q = a / b (rem r)
2574 var a = try Int.initSet(testing.allocator, 0x8888999911110000ffffeeeedb4fec200ee3a4286361);1683 ///
2575 defer a.deinit();1684 /// a / b are truncated (rounded towards -inf).
2576 var b = try Int.initSet(testing.allocator, 0x99990000111122223333);1685 ///
2577 defer b.deinit();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);1701 /// r = a << shift, in other words, r = a * 2^shift
2580 defer q.deinit();1702 pub fn shiftLeft(r: *Managed, a: Managed, shift: usize) !void {
2581 var r = try Int.init(testing.allocator);1703 try r.ensureCapacity(a.len() + (shift / Limb.bit_count) + 1);
2582 defer r.deinit();1704 var m = r.toMutable();
2583 try Int.divTrunc(&q, &r, a, b);1705 m.shiftLeft(a.toConst(), shift);
1706 r.setMetadata(m.positive, m.len);
1707 }
25841708
2585 testing.expect((try q.to(u128)) == 0xe38f38e39161aaabd03f0f1b);1709 /// r = a >> shift
2586 testing.expect((try r.to(u128)) == 0);1710 pub fn shiftRight(r: *Managed, a: Managed, shift: usize) !void {
2587}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)" {1717 try r.ensureCapacity(a.len() - (shift / Limb.bit_count));
2590 var a = try Int.initSet(testing.allocator, 0x866666665555555588888887777777761111111111111111);1718 var m = r.toMutable();
2591 defer a.deinit();1719 m.shiftRight(a.toConst(), shift);
2592 var b = try Int.initSet(testing.allocator, 0x86666666555555554444444433333333);1720 r.setMetadata(m.positive, m.len);
2593 defer b.deinit();1721 }
25941722
2595 var q = try Int.init(testing.allocator);1723 /// r = a | b
2596 defer q.deinit();1724 ///
2597 var r = try Int.init(testing.allocator);1725 /// a and b are zero-extended to the longer of a or b.
2598 defer r.deinit();1726 pub fn bitOr(r: *Managed, a: Managed, b: Managed) !void {
2599 try Int.divTrunc(&q, &r, a, b);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);1733 /// r = a & b
2602 testing.expect((try r.to(u128)) == 0x44444443444444431111111111111111);1734 pub fn bitAnd(r: *Managed, a: Managed, b: Managed) !void {
2603}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)" {1741 /// r = a ^ b
2606 var a = try Int.initSet(testing.allocator, 0x11111111111111111111111111111111111111111111111111111111111111);1742 pub fn bitXor(r: *Managed, a: Managed, b: Managed) !void {
2607 defer a.deinit();1743 try r.ensureCapacity(math.max(a.len(), b.len()));
2608 var b = try Int.initSet(testing.allocator, 0x1111111111111111111111111111111111111111171);1744 var m = r.toMutable();
2609 defer b.deinit();1745 m.bitXor(a.toConst(), b.toConst());
1746 r.setMetadata(m.positive, m.len);
1747 }
26101748
2611 var q = try Int.init(testing.allocator);1749 /// rma may alias x or y.
2612 defer q.deinit();1750 /// x and y may alias each other.
2613 var r = try Int.init(testing.allocator);1751 ///
2614 defer r.deinit();1752 /// rma's allocator is used for temporary storage to boost multiplication performance.
2615 try Int.divTrunc(&q, &r, a, b);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);1763/// Knuth 4.3.1, Algorithm M.
2618 testing.expect((try r.to(u256)) == 0x1111111111111111111110b12222222222222222282);1764///
2619}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" {1789 // Basecase multiplication
2622 var a = try Int.initSet(testing.allocator, 0x60000000000000000000000000000000000000000000000000000000000000000);1790 var i: usize = 0;
2623 defer a.deinit();1791 while (i < x.len) : (i += 1) {
2624 var b = try Int.initSet(testing.allocator, 0x10000000000000000);1792 llmulDigit(r[i..], y, x[i]);
2625 defer b.deinit();1793 }
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());
2637}1794}
26381795
2639test "big.int div multi-multi zero-limb trailing (with rem)" {1796/// Knuth 4.3.1, Algorithm M.
2640 var a = try Int.initSet(testing.allocator, 0x86666666555555558888888777777776111111111111111100000000000000000000000000000000);1797///
2641 defer a.deinit();1798/// r MUST NOT alias any of a or b.
2642 var b = try Int.initSet(testing.allocator, 0x8666666655555555444444443333333300000000000000000000000000000000);1799fn llmulacc_karatsuba(allocator: *Allocator, r: []Limb, x: []const Limb, y: []const Limb) error{OutOfMemory}!void {
2643 defer b.deinit();1800 @setRuntimeSafety(false);
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);
26521801
2653 const rs = try r.toString(testing.allocator, 16, false);1802 assert(r.len >= x.len + y.len + 1);
2654 defer testing.allocator.free(rs);
2655 testing.expect(std.mem.eql(u8, rs, "4444444344444443111111111111111100000000000000000000000000000000"));
2656}
26571803
2658test "big.int div multi-multi zero-limb trailing (with rem) and dividend zero-limb count > divisor zero-limb count" {1804 const split = @divFloor(x.len, 2);
2659 var a = try Int.initSet(testing.allocator, 0x8666666655555555888888877777777611111111111111110000000000000000);1805 var x0 = x[0..split];
2660 defer a.deinit();1806 var x1 = x[split..x.len];
2661 var b = try Int.initSet(testing.allocator, 0x8666666655555555444444443333333300000000000000000000000000000000);1807 var y0 = y[0..split];
2662 defer b.deinit();1808 var y1 = y[split..y.len];
26631809
2664 var q = try Int.init(testing.allocator);1810 var tmp = try allocator.alloc(Limb, x1.len + y1.len + 1);
2665 defer q.deinit();1811 defer allocator.free(tmp);
2666 var r = try Int.init(testing.allocator);1812 mem.set(Limb, tmp, 0);
2667 defer r.deinit();
2668 try Int.divTrunc(&q, &r, a, b);
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);1816 var length = llnormalize(tmp);
2673 defer testing.allocator.free(rs);1817 _ = llaccum(r[split..], tmp[0..length]);
2674 testing.expect(std.mem.eql(u8, rs, "444444434444444311111111111111110000000000000000"));1818 _ = llaccum(r[split * 2 ..], tmp[0..length]);
2675}
26761819
2677test "big.int div multi-multi zero-limb trailing (with rem) and dividend zero-limb count < divisor zero-limb count" {1820 mem.set(Limb, tmp[0..length], 0);
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}
26971821
2698test "big.int div multi-multi fuzz case #1" {1822 llmulacc(allocator, tmp, x0, y0);
2699 var a = try Int.init(testing.allocator);
2700 defer a.deinit();
2701 var b = try Int.init(testing.allocator);
2702 defer b.deinit();
27031823
2704 try a.setString(16, "ffffffffffffffffffffffffffffc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");1824 length = llnormalize(tmp);
2705 try b.setString(16, "3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0000000000000000000000000000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffc000000000000000000000000000000007fffffffffff");1825 _ = llaccum(r[0..], tmp[0..length]);
1826 _ = llaccum(r[split..], tmp[0..length]);
27061827
2707 var q = try Int.init(testing.allocator);1828 const x_cmp = llcmp(x1, x0);
2708 defer q.deinit();1829 const y_cmp = llcmp(y1, y0);
2709 var r = try Int.init(testing.allocator);1830 if (x_cmp * y_cmp == 0) {
2710 defer r.deinit();1831 return;
2711 try Int.divTrunc(&q, &r, a, b);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);1843 const y0_len = llnormalize(y0);
2714 defer testing.allocator.free(qs);1844 const y1_len = llnormalize(y1);
2715 testing.expect(std.mem.eql(u8, qs, "3ffffffffffffffffffffffffffff0000000000000000000000000000000000001ffffffffffffffffffffffffffff7fffffffe000000000000000000000000000180000000000000000000003fffffbfffffffdfffffffffffffeffff800000100101000000100000000020003fffffdfbfffffe3ffffffffffffeffff7fffc00800a100000017ffe000002000400007efbfff7fe9f00000037ffff3fff7fffa004006100000009ffe00000190038200bf7d2ff7fefe80400060000f7d7f8fbf9401fe38e0403ffc0bdffffa51102c300d7be5ef9df4e5060007b0127ad3fa69f97d0f820b6605ff617ddf7f32ad7a05c0d03f2e7bc78a6000e087a8bbcdc59e07a5a079128a7861f553ddebed7e8e56701756f9ead39b48cd1b0831889ea6ec1fddf643d0565b075ff07e6caea4e2854ec9227fd635ed60a2f5eef2893052ffd54718fa08604acbf6a15e78a467c4a3c53c0278af06c4416573f925491b195e8fd79302cb1aaf7caf4ecfc9aec1254cc969786363ac729f914c6ddcc26738d6b0facd54eba026580aba2eb6482a088b0d224a8852420b91ec1"));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);1858 length = llnormalize(tmp);
2718 defer testing.allocator.free(rs);1859 llsub(r[split..], r[split..], tmp[0..length]);
2719 testing.expect(std.mem.eql(u8, rs, "310d1d4c414426b4836c2635bad1df3a424e50cbdd167ffccb4dfff57d36b4aae0d6ca0910698220171a0f3373c1060a046c2812f0027e321f72979daa5e7973214170d49e885de0c0ecc167837d44502430674a82522e5df6a0759548052420b91ec1"));1860 } else {
1861 llmulacc(allocator, r[split..], j0, j1);
1862 }
2720}1863}
27211864
2722test "big.int div multi-multi fuzz case #2" {1865// r = r + a
2723 var a = try Int.init(testing.allocator);1866fn llaccum(r: []Limb, a: []const Limb) Limb {
2724 defer a.deinit();1867 @setRuntimeSafety(false);
2725 var b = try Int.init(testing.allocator);1868 assert(r.len != 0 and a.len != 0);
2726 defer b.deinit();1869 assert(r.len >= a.len);
27271870
2728 try a.setString(16, "3ffffffffe00000000000000000000000000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffe000000000000000000000000000000000000000000000000000000000000001fffffffffffffffff800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffc000000000000000000000000000000000000000000000000000000000000000");1871 var i: usize = 0;
2729 try b.setString(16, "ffc0000000000000000000000000000000000000000000000000");1872 var carry: Limb = 0;
27301873
2731 var q = try Int.init(testing.allocator);1874 while (i < a.len) : (i += 1) {
2732 defer q.deinit();1875 var c: Limb = 0;
2733 var r = try Int.init(testing.allocator);1876 c += @boolToInt(@addWithOverflow(Limb, r[i], a[i], &r[i]));
2734 defer r.deinit();1877 c += @boolToInt(@addWithOverflow(Limb, r[i], carry, &r[i]));
2735 try Int.divTrunc(&q, &r, a, b);1878 carry = c;
1879 }
27361880
2737 const qs = try q.toString(testing.allocator, 16, false);1881 while ((carry != 0) and i < r.len) : (i += 1) {
2738 defer testing.allocator.free(qs);1882 carry = @boolToInt(@addWithOverflow(Limb, r[i], carry, &r[i]));
2739 testing.expect(std.mem.eql(u8, qs, "40100400fe3f8fe3f8fe3f8fe3f8fe3f8fe4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f91e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4992649926499264991e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4792e4b92e4b92e4b92e4b92a4a92a4a92a4"));1883 }
27401884
2741 const rs = try r.toString(testing.allocator, 16, false);1885 return carry;
2742 defer testing.allocator.free(rs);
2743 testing.expect(std.mem.eql(u8, rs, "a900000000000000000000000000000000000000000000000000"));
2744}1886}
27451887
2746test "big.int shift-right single" {1888/// Returns -1, 0, 1 if |a| < |b|, |a| == |b| or |a| > |b| respectively for limbs.
2747 var a = try Int.initSet(testing.allocator, 0xffff0000);1889pub fn llcmp(a: []const Limb, b: []const Limb) i8 {
2748 defer a.deinit();1890 @setRuntimeSafety(false);
2749 try a.shiftRight(a, 16);1891 const a_len = llnormalize(a);
27501892 const b_len = llnormalize(b);
2751 testing.expect((try a.to(u32)) == 0xffff);1893 if (a_len < b_len) {
2752}1894 return -1;
1895 }
1896 if (a_len > b_len) {
1897 return 1;
1898 }
27531899
2754test "big.int shift-right multi" {1900 var i: usize = a_len - 1;
2755 var a = try Int.initSet(testing.allocator, 0xffff0000eeee1111dddd2222cccc3333);1901 while (i != 0) : (i -= 1) {
2756 defer a.deinit();1902 if (a[i] != b[i]) {
2757 try a.shiftRight(a, 67);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 }
2760}1914}
27611915
2762test "big.int shift-left single" {1916fn llmulDigit(acc: []Limb, y: []const Limb, xi: Limb) void {
2763 var a = try Int.initSet(testing.allocator, 0xffff);1917 @setRuntimeSafety(false);
2764 defer a.deinit();1918 if (xi == 0) {
2765 try a.shiftLeft(a, 16);1919 return;
1920 }
27661921
2767 testing.expect((try a.to(u64)) == 0xffff0000);1922 var carry: usize = 0;
2768}1923 var a_lo = acc[0..y.len];
1924 var a_hi = acc[y.len..];
27691925
2770test "big.int shift-left multi" {1926 var j: usize = 0;
2771 var a = try Int.initSet(testing.allocator, 0x1fffe0001dddc222);1927 while (j < a_lo.len) : (j += 1) {
2772 defer a.deinit();1928 a_lo[j] = @call(.{ .modifier = .always_inline }, addMulLimbWithCarry, .{ a_lo[j], y[j], xi, &carry });
2773 try a.shiftLeft(a, 67);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 }
2776}1935}
27771936
2778test "big.int shift-right negative" {1937/// returns the min length the limb could be.
2779 var a = try Int.init(testing.allocator);1938fn llnormalize(a: []const Limb) usize {
2780 defer a.deinit();1939 @setRuntimeSafety(false);
27811940 var j = a.len;
2782 try a.shiftRight(try Int.initSet(testing.allocator, -20), 2);1941 while (j > 0) : (j -= 1) {
2783 defer a.deinit();1942 if (a[j - 1] != 0) {
2784 testing.expect((try a.to(i32)) == -20 >> 2);1943 break;
1944 }
1945 }
27851946
2786 try a.shiftRight(try Int.initSet(testing.allocator, -5), 10);1947 // Handle zero
2787 defer a.deinit();1948 return if (j != 0) j else 1;
2788 testing.expect((try a.to(i32)) == -5 >> 10);
2789}1949}
27901950
2791test "big.int shift-left negative" {1951/// Knuth 4.3.1, Algorithm S.
2792 var a = try Int.init(testing.allocator);1952fn llsub(r: []Limb, a: []const Limb, b: []const Limb) void {
2793 defer a.deinit();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);1958 var i: usize = 0;
2796 defer a.deinit();1959 var borrow: Limb = 0;
2797 testing.expect((try a.to(i32)) == -10 >> 1232);
2798}
27991960
2800test "big.int bitwise and simple" {1961 while (i < b.len) : (i += 1) {
2801 var a = try Int.initSet(testing.allocator, 0xffffffff11111111);1962 var c: Limb = 0;
2802 defer a.deinit();1963 c += @boolToInt(@subWithOverflow(Limb, a[i], b[i], &r[i]));
2803 var b = try Int.initSet(testing.allocator, 0xeeeeeeee22222222);1964 c += @boolToInt(@subWithOverflow(Limb, r[i], borrow, &r[i]));
2804 defer b.deinit();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);
2809}1973}
28101974
2811test "big.int bitwise and multi-limb" {1975/// Knuth 4.3.1, Algorithm A.
2812 var a = try Int.initSet(testing.allocator, maxInt(Limb) + 1);1976fn lladd(r: []Limb, a: []const Limb, b: []const Limb) void {
2813 defer a.deinit();1977 @setRuntimeSafety(false);
2814 var b = try Int.initSet(testing.allocator, maxInt(Limb));1978 assert(a.len != 0 and b.len != 0);
2815 defer b.deinit();1979 assert(a.len >= b.len);
28161980 assert(r.len >= a.len + 1);
2817 try a.bitAnd(a, b);
28181981
2819 testing.expect((try a.to(u128)) == 0);1982 var i: usize = 0;
2820}1983 var carry: Limb = 0;
28211984
2822test "big.int bitwise xor simple" {1985 while (i < b.len) : (i += 1) {
2823 var a = try Int.initSet(testing.allocator, 0xffffffff11111111);1986 var c: Limb = 0;
2824 defer a.deinit();1987 c += @boolToInt(@addWithOverflow(Limb, a[i], b[i], &r[i]));
2825 var b = try Int.initSet(testing.allocator, 0xeeeeeeee22222222);1988 c += @boolToInt(@addWithOverflow(Limb, r[i], carry, &r[i]));
2826 defer b.deinit();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;
2831}1997}
28321998
2833test "big.int bitwise xor multi-limb" {1999/// Knuth 4.3.1, Exercise 16.
2834 var a = try Int.initSet(testing.allocator, maxInt(Limb) + 1);2000fn lldiv1(quo: []Limb, rem: *Limb, a: []const Limb, b: Limb) void {
2835 defer a.deinit();2001 @setRuntimeSafety(false);
2836 var b = try Int.initSet(testing.allocator, maxInt(Limb));2002 assert(a.len > 1 or a[0] >= b);
2837 defer b.deinit();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 }
2842}2024}
28432025
2844test "big.int bitwise or simple" {2026fn llshl(r: []Limb, a: []const Limb, shift: usize) void {
2845 var a = try Int.initSet(testing.allocator, 0xffffffff11111111);2027 @setRuntimeSafety(false);
2846 defer a.deinit();2028 assert(a.len >= 1);
2847 var b = try Int.initSet(testing.allocator, 0xeeeeeeee22222222);2029 assert(r.len >= a.len + (shift / Limb.bit_count) + 1);
2848 defer b.deinit();
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);2034 var carry: Limb = 0;
2853}2035 var i: usize = 0;
28542036 while (i < a.len) : (i += 1) {
2855test "big.int bitwise or multi-limb" {2037 const src_i = a.len - i - 1;
2856 var a = try Int.initSet(testing.allocator, maxInt(Limb) + 1);2038 const dst_i = src_i + limb_shift;
2857 defer a.deinit();
2858 var b = try Int.initSet(testing.allocator, maxInt(Limb));
2859 defer b.deinit();
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.2049 r[limb_shift - 1] = carry;
2864 testing.expect((try a.to(DoubleLimb)) == (maxInt(Limb) + 1) + maxInt(Limb));2050 mem.set(Limb, r[0 .. limb_shift - 1], 0);
2865}2051}
28662052
2867test "big.int var args" {2053fn llshr(r: []Limb, a: []const Limb, shift: usize) void {
2868 var a = try Int.initSet(testing.allocator, 5);2054 @setRuntimeSafety(false);
2869 defer a.deinit();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);2058 const limb_shift = shift / Limb.bit_count;
2872 defer b.deinit();2059 const interior_limb_shift = @intCast(Log2Limb, shift % Limb.bit_count);
2873 try a.add(a, b);
2874 testing.expect((try a.to(u64)) == 11);
28752060
2876 const c = try Int.initSet(testing.allocator, 11);2061 var carry: Limb = 0;
2877 defer c.deinit();2062 var i: usize = 0;
2878 testing.expect(a.cmp(c) == .eq);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);2067 const src_digit = a[src_i];
2881 defer d.deinit();2068 r[dst_i] = carry | (src_digit >> interior_limb_shift);
2882 testing.expect(a.cmp(d) != .gt);2069 carry = @call(.{ .modifier = .always_inline }, math.shl, .{
2070 Limb,
2071 src_digit,
2072 Limb.bit_count - @intCast(Limb, interior_limb_shift),
2073 });
2074 }
2883}2075}
28842076
2885test "big.int gcd non-one small" {2077fn llor(r: []Limb, a: []const Limb, b: []const Limb) void {
2886 var a = try Int.initSet(testing.allocator, 17);2078 @setRuntimeSafety(false);
2887 defer a.deinit();2079 assert(r.len >= a.len);
2888 var b = try Int.initSet(testing.allocator, 97);2080 assert(a.len >= b.len);
2889 defer b.deinit();
2890 var r = try Int.init(testing.allocator);
2891 defer r.deinit();
28922081
2893 try r.gcd(a, b);2082 var i: usize = 0;
28942083 while (i < b.len) : (i += 1) {
2895 testing.expect((try r.to(u32)) == 1);2084 r[i] = a[i] | b[i];
2085 }
2086 while (i < a.len) : (i += 1) {
2087 r[i] = a[i];
2088 }
2896}2089}
28972090
2898test "big.int gcd non-one small" {2091fn lland(r: []Limb, a: []const Limb, b: []const Limb) void {
2899 var a = try Int.initSet(testing.allocator, 4864);2092 @setRuntimeSafety(false);
2900 defer a.deinit();2093 assert(r.len >= b.len);
2901 var b = try Int.initSet(testing.allocator, 3458);2094 assert(a.len >= b.len);
2902 defer b.deinit();
2903 var r = try Int.init(testing.allocator);
2904 defer r.deinit();
2905
2906 try r.gcd(a, b);
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 }
2909}2100}
29102101
2911test "big.int gcd non-one large" {2102fn llxor(r: []Limb, a: []const Limb, b: []const Limb) void {
2912 var a = try Int.initSet(testing.allocator, 0xffffffffffffffff);2103 assert(r.len >= a.len);
2913 defer a.deinit();2104 assert(a.len >= b.len);
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);
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 }
2922}2113}
29232114
2924test "big.int gcd large multi-limb result" {2115// Storage must live for the lifetime of the returned value
2925 var a = try Int.initSet(testing.allocator, 0x12345678123456781234567812345678123456781234567812345678);2116fn fixedIntFromSignedDoubleLimb(A: SignedDoubleLimb, storage: []Limb) Mutable {
2926 defer a.deinit();2117 assert(storage.len >= 2);
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);
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 };
2935}2128}
29362129
2937test "big.int gcd one large" {2130test "" {
2938 var a = try Int.initSet(testing.allocator, 1897056385327307);2131 _ = @import("int_test.zig");
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);
2948}2132}
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;...@@ -5,10 +5,10 @@ const mem = std.mem;
5const testing = std.testing;5const testing = std.testing;
6const Allocator = mem.Allocator;6const Allocator = mem.Allocator;
77
8const bn = @import("int.zig");8const Limb = std.math.big.Limb;
9const Limb = bn.Limb;9const DoubleLimb = std.math.big.DoubleLimb;
10const DoubleLimb = bn.DoubleLimb;10const Int = std.math.big.int.Managed;
11const Int = bn.Int;11const IntConst = std.math.big.int.Const;
1212
13/// An arbitrary-precision rational number.13/// An arbitrary-precision rational number.
14///14///
...@@ -17,6 +17,9 @@ const Int = bn.Int;...@@ -17,6 +17,9 @@ const Int = bn.Int;
17///17///
18/// Rational's are always normalized. That is, for a Rational r = p/q where p and q are integers,18/// Rational's are always normalized. That is, for a Rational r = p/q where p and q are integers,
19/// gcd(p, q) = 1 always.19/// 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.
20pub const Rational = struct {23pub const Rational = struct {
21 /// Numerator. Determines the sign of the Rational.24 /// Numerator. Determines the sign of the Rational.
22 p: Int,25 p: Int,
...@@ -98,20 +101,20 @@ pub const Rational = struct {...@@ -98,20 +101,20 @@ pub const Rational = struct {
98 if (point) |i| {101 if (point) |i| {
99 try self.p.setString(10, str[0..i]);102 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
103 var j: usize = start;106 var j: usize = start;
104 while (j < str.len - i - 1) : (j += 1) {107 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);
106 }109 }
107110
108 try self.q.setString(10, str[i + 1 ..]);111 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
111 try self.q.set(1);114 try self.q.set(1);
112 var k: usize = i + 1;115 var k: usize = i + 1;
113 while (k < str.len) : (k += 1) {116 while (k < str.len) : (k += 1) {
114 try self.q.mul(self.q, base);117 try self.q.mul(self.q.toConst(), base);
115 }118 }
116119
117 try self.reduce();120 try self.reduce();
...@@ -218,14 +221,14 @@ pub const Rational = struct {...@@ -218,14 +221,14 @@ pub const Rational = struct {
218 }221 }
219222
220 // 2. compute quotient and remainder223 // 2. compute quotient and remainder
221 var q = try Int.init(self.p.allocator.?);224 var q = try Int.init(self.p.allocator);
222 defer q.deinit();225 defer q.deinit();
223226
224 // unused227 // unused
225 var r = try Int.init(self.p.allocator.?);228 var r = try Int.init(self.p.allocator);
226 defer r.deinit();229 defer r.deinit();
227230
228 try Int.divTrunc(&q, &r, a2, b2);231 try Int.divTrunc(&q, &r, a2.toConst(), b2.toConst());
229232
230 var mantissa = extractLowBits(q, BitReprType);233 var mantissa = extractLowBits(q, BitReprType);
231 var have_rem = r.len() > 0;234 var have_rem = r.len() > 0;
...@@ -293,14 +296,14 @@ pub const Rational = struct {...@@ -293,14 +296,14 @@ pub const Rational = struct {
293296
294 /// Set a Rational directly from an Int.297 /// Set a Rational directly from an Int.
295 pub fn copyInt(self: *Rational, a: Int) !void {298 pub fn copyInt(self: *Rational, a: Int) !void {
296 try self.p.copy(a);299 try self.p.copy(a.toConst());
297 try self.q.set(1);300 try self.q.set(1);
298 }301 }
299302
300 /// Set a Rational directly from a ratio of two Int's.303 /// Set a Rational directly from a ratio of two Int's.
301 pub fn copyRatio(self: *Rational, a: Int, b: Int) !void {304 pub fn copyRatio(self: *Rational, a: Int, b: Int) !void {
302 try self.p.copy(a);305 try self.p.copy(a.toConst());
303 try self.q.copy(b);306 try self.q.copy(b.toConst());
304307
305 self.p.setSign(@boolToInt(self.p.isPositive()) ^ @boolToInt(self.q.isPositive()) == 0);308 self.p.setSign(@boolToInt(self.p.isPositive()) ^ @boolToInt(self.q.isPositive()) == 0);
306 self.q.setSign(true);309 self.q.setSign(true);
...@@ -327,13 +330,13 @@ pub const Rational = struct {...@@ -327,13 +330,13 @@ pub const Rational = struct {
327330
328 /// Returns math.Order.lt, math.Order.eq, math.Order.gt if a < b, a == b or a331 /// Returns math.Order.lt, math.Order.eq, math.Order.gt if a < b, a == b or a
329 /// > b respectively.332 /// > b respectively.
330 pub fn cmp(a: Rational, b: Rational) !math.Order {333 pub fn order(a: Rational, b: Rational) !math.Order {
331 return cmpInternal(a, b, true);334 return cmpInternal(a, b, true);
332 }335 }
333336
334 /// Returns math.Order.lt, math.Order.eq, math.Order.gt if |a| < |b|, |a| ==337 /// Returns math.Order.lt, math.Order.eq, math.Order.gt if |a| < |b|, |a| ==
335 /// |b| or |a| > |b| respectively.338 /// |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 {
337 return cmpInternal(a, b, false);340 return cmpInternal(a, b, false);
338 }341 }
339342
...@@ -341,16 +344,16 @@ pub const Rational = struct {...@@ -341,16 +344,16 @@ pub const Rational = struct {
341 fn cmpInternal(a: Rational, b: Rational, is_abs: bool) !math.Order {344 fn cmpInternal(a: Rational, b: Rational, is_abs: bool) !math.Order {
342 // TODO: Would a div compare algorithm of sorts be viable and quicker? Can we avoid345 // TODO: Would a div compare algorithm of sorts be viable and quicker? Can we avoid
343 // the memory allocations here?346 // the memory allocations here?
344 var q = try Int.init(a.p.allocator.?);347 var q = try Int.init(a.p.allocator);
345 defer q.deinit();348 defer q.deinit();
346349
347 var p = try Int.init(b.p.allocator.?);350 var p = try Int.init(b.p.allocator);
348 defer p.deinit();351 defer p.deinit();
349352
350 try q.mul(a.p, b.q);353 try q.mul(a.p.toConst(), b.q.toConst());
351 try p.mul(b.p, a.q);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);
354 }357 }
355358
356 /// rma = a + b.359 /// rma = a + b.
...@@ -364,7 +367,7 @@ pub const Rational = struct {...@@ -364,7 +367,7 @@ pub const Rational = struct {
364367
365 var sr: Rational = undefined;368 var sr: Rational = undefined;
366 if (aliased) {369 if (aliased) {
367 sr = try Rational.init(rma.p.allocator.?);370 sr = try Rational.init(rma.p.allocator);
368 r = &sr;371 r = &sr;
369 aliased = true;372 aliased = true;
370 }373 }
...@@ -373,11 +376,11 @@ pub const Rational = struct {...@@ -373,11 +376,11 @@ pub const Rational = struct {
373 r.deinit();376 r.deinit();
374 };377 };
375378
376 try r.p.mul(a.p, b.q);379 try r.p.mul(a.p.toConst(), b.q.toConst());
377 try r.q.mul(b.p, a.q);380 try r.q.mul(b.p.toConst(), a.q.toConst());
378 try r.p.add(r.p, r.q);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());
381 try r.reduce();384 try r.reduce();
382 }385 }
383386
...@@ -392,7 +395,7 @@ pub const Rational = struct {...@@ -392,7 +395,7 @@ pub const Rational = struct {
392395
393 var sr: Rational = undefined;396 var sr: Rational = undefined;
394 if (aliased) {397 if (aliased) {
395 sr = try Rational.init(rma.p.allocator.?);398 sr = try Rational.init(rma.p.allocator);
396 r = &sr;399 r = &sr;
397 aliased = true;400 aliased = true;
398 }401 }
...@@ -401,11 +404,11 @@ pub const Rational = struct {...@@ -401,11 +404,11 @@ pub const Rational = struct {
401 r.deinit();404 r.deinit();
402 };405 };
403406
404 try r.p.mul(a.p, b.q);407 try r.p.mul(a.p.toConst(), b.q.toConst());
405 try r.q.mul(b.p, a.q);408 try r.q.mul(b.p.toConst(), a.q.toConst());
406 try r.p.sub(r.p, r.q);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());
409 try r.reduce();412 try r.reduce();
410 }413 }
411414
...@@ -415,8 +418,8 @@ pub const Rational = struct {...@@ -415,8 +418,8 @@ pub const Rational = struct {
415 ///418 ///
416 /// Returns an error if memory could not be allocated.419 /// Returns an error if memory could not be allocated.
417 pub fn mul(r: *Rational, a: Rational, b: Rational) !void {420 pub fn mul(r: *Rational, a: Rational, b: Rational) !void {
418 try r.p.mul(a.p, b.p);421 try r.p.mul(a.p.toConst(), b.p.toConst());
419 try r.q.mul(a.q, b.q);422 try r.q.mul(a.q.toConst(), b.q.toConst());
420 try r.reduce();423 try r.reduce();
421 }424 }
422425
...@@ -430,8 +433,8 @@ pub const Rational = struct {...@@ -430,8 +433,8 @@ pub const Rational = struct {
430 @panic("division by zero");433 @panic("division by zero");
431 }434 }
432435
433 try r.p.mul(a.p, b.q);436 try r.p.mul(a.p.toConst(), b.q.toConst());
434 try r.q.mul(b.p, a.q);437 try r.q.mul(b.p.toConst(), a.q.toConst());
435 try r.reduce();438 try r.reduce();
436 }439 }
437440
...@@ -442,7 +445,7 @@ pub const Rational = struct {...@@ -442,7 +445,7 @@ pub const Rational = struct {
442445
443 // reduce r/q such that gcd(r, q) = 1446 // reduce r/q such that gcd(r, q) = 1
444 fn reduce(r: *Rational) !void {447 fn reduce(r: *Rational) !void {
445 var a = try Int.init(r.p.allocator.?);448 var a = try Int.init(r.p.allocator);
446 defer a.deinit();449 defer a.deinit();
447450
448 const sign = r.p.isPositive();451 const sign = r.p.isPositive();
...@@ -450,15 +453,15 @@ pub const Rational = struct {...@@ -450,15 +453,15 @@ pub const Rational = struct {
450 try a.gcd(r.p, r.q);453 try a.gcd(r.p, r.q);
451 r.p.setSign(sign);454 r.p.setSign(sign);
452455
453 const one = Int.initFixed(([_]Limb{1})[0..]);456 const one = IntConst{ .limbs = &[_]Limb{1}, .positive = true };
454 if (a.cmp(one) != .eq) {457 if (a.toConst().order(one) != .eq) {
455 var unused = try Int.init(r.p.allocator.?);458 var unused = try Int.init(r.p.allocator);
456 defer unused.deinit();459 defer unused.deinit();
457460
458 // TODO: divexact would be useful here461 // TODO: divexact would be useful here
459 // TODO: don't copy r.q for div462 // TODO: don't copy r.q for div
460 try Int.divTrunc(&r.p, &unused, r.p, a);463 try Int.divTrunc(&r.p, &unused, r.p.toConst(), a.toConst());
461 try Int.divTrunc(&r.q, &unused, r.q, a);464 try Int.divTrunc(&r.q, &unused, r.q.toConst(), a.toConst());
462 }465 }
463 }466 }
464};467};
...@@ -596,25 +599,25 @@ test "big.rational copy" {...@@ -596,25 +599,25 @@ test "big.rational copy" {
596 var a = try Rational.init(testing.allocator);599 var a = try Rational.init(testing.allocator);
597 defer a.deinit();600 defer a.deinit();
598601
599 const b = try Int.initSet(testing.allocator, 5);602 var b = try Int.initSet(testing.allocator, 5);
600 defer b.deinit();603 defer b.deinit();
601604
602 try a.copyInt(b);605 try a.copyInt(b);
603 testing.expect((try a.p.to(u32)) == 5);606 testing.expect((try a.p.to(u32)) == 5);
604 testing.expect((try a.q.to(u32)) == 1);607 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);
607 defer c.deinit();610 defer c.deinit();
608 const d = try Int.initSet(testing.allocator, 3);611 var d = try Int.initSet(testing.allocator, 3);
609 defer d.deinit();612 defer d.deinit();
610613
611 try a.copyRatio(c, d);614 try a.copyRatio(c, d);
612 testing.expect((try a.p.to(u32)) == 7);615 testing.expect((try a.p.to(u32)) == 7);
613 testing.expect((try a.q.to(u32)) == 3);616 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);
616 defer e.deinit();619 defer e.deinit();
617 const f = try Int.initSet(testing.allocator, 3);620 var f = try Int.initSet(testing.allocator, 3);
618 defer f.deinit();621 defer f.deinit();
619622
620 try a.copyRatio(e, f);623 try a.copyRatio(e, f);
...@@ -680,7 +683,7 @@ test "big.rational swap" {...@@ -680,7 +683,7 @@ test "big.rational swap" {
680 testing.expect((try b.q.to(u32)) == 23);683 testing.expect((try b.q.to(u32)) == 23);
681}684}
682685
683test "big.rational cmp" {686test "big.rational order" {
684 var a = try Rational.init(testing.allocator);687 var a = try Rational.init(testing.allocator);
685 defer a.deinit();688 defer a.deinit();
686 var b = try Rational.init(testing.allocator);689 var b = try Rational.init(testing.allocator);
...@@ -688,11 +691,11 @@ test "big.rational cmp" {...@@ -688,11 +691,11 @@ test "big.rational cmp" {
688691
689 try a.setRatio(500, 231);692 try a.setRatio(500, 231);
690 try b.setRatio(18903, 8584);693 try b.setRatio(18903, 8584);
691 testing.expect((try a.cmp(b)) == .lt);694 testing.expect((try a.order(b)) == .lt);
692695
693 try a.setRatio(890, 10);696 try a.setRatio(890, 10);
694 try b.setRatio(89, 1);697 try b.setRatio(89, 1);
695 testing.expect((try a.cmp(b)) == .eq);698 testing.expect((try a.order(b)) == .eq);
696}699}
697700
698test "big.rational add single-limb" {701test "big.rational add single-limb" {
...@@ -703,11 +706,11 @@ test "big.rational add single-limb" {...@@ -703,11 +706,11 @@ test "big.rational add single-limb" {
703706
704 try a.setRatio(500, 231);707 try a.setRatio(500, 231);
705 try b.setRatio(18903, 8584);708 try b.setRatio(18903, 8584);
706 testing.expect((try a.cmp(b)) == .lt);709 testing.expect((try a.order(b)) == .lt);
707710
708 try a.setRatio(890, 10);711 try a.setRatio(890, 10);
709 try b.setRatio(89, 1);712 try b.setRatio(89, 1);
710 testing.expect((try a.cmp(b)) == .eq);713 testing.expect((try a.order(b)) == .eq);
711}714}
712715
713test "big.rational add" {716test "big.rational add" {
...@@ -723,7 +726,7 @@ test "big.rational add" {...@@ -723,7 +726,7 @@ test "big.rational add" {
723 try a.add(a, b);726 try a.add(a, b);
724727
725 try r.setRatio(984786924199, 290395044174);728 try r.setRatio(984786924199, 290395044174);
726 testing.expect((try a.cmp(r)) == .eq);729 testing.expect((try a.order(r)) == .eq);
727}730}
728731
729test "big.rational sub" {732test "big.rational sub" {
...@@ -739,7 +742,7 @@ test "big.rational sub" {...@@ -739,7 +742,7 @@ test "big.rational sub" {
739 try a.sub(a, b);742 try a.sub(a, b);
740743
741 try r.setRatio(979040510045, 290395044174);744 try r.setRatio(979040510045, 290395044174);
742 testing.expect((try a.cmp(r)) == .eq);745 testing.expect((try a.order(r)) == .eq);
743}746}
744747
745test "big.rational mul" {748test "big.rational mul" {
...@@ -755,7 +758,7 @@ test "big.rational mul" {...@@ -755,7 +758,7 @@ test "big.rational mul" {
755 try a.mul(a, b);758 try a.mul(a, b);
756759
757 try r.setRatio(571481443, 17082061422);760 try r.setRatio(571481443, 17082061422);
758 testing.expect((try a.cmp(r)) == .eq);761 testing.expect((try a.order(r)) == .eq);
759}762}
760763
761test "big.rational div" {764test "big.rational div" {
...@@ -771,7 +774,7 @@ test "big.rational div" {...@@ -771,7 +774,7 @@ test "big.rational div" {
771 try a.div(a, b);774 try a.div(a, b);
772775
773 try r.setRatio(75531824394, 221015929);776 try r.setRatio(75531824394, 221015929);
774 testing.expect((try a.cmp(r)) == .eq);777 testing.expect((try a.order(r)) == .eq);
775}778}
776779
777test "big.rational div" {780test "big.rational div" {
...@@ -784,11 +787,11 @@ test "big.rational div" {...@@ -784,11 +787,11 @@ test "big.rational div" {
784 a.invert();787 a.invert();
785788
786 try r.setRatio(23341, 78923);789 try r.setRatio(23341, 78923);
787 testing.expect((try a.cmp(r)) == .eq);790 testing.expect((try a.order(r)) == .eq);
788791
789 try a.setRatio(-78923, 23341);792 try a.setRatio(-78923, 23341);
790 a.invert();793 a.invert();
791794
792 try r.setRatio(-23341, 78923);795 try r.setRatio(-23341, 78923);
793 testing.expect((try a.cmp(r)) == .eq);796 testing.expect((try a.order(r)) == .eq);
794}797}
lib/std/target.zig+1
...@@ -404,6 +404,7 @@ pub const Target = struct {...@@ -404,6 +404,7 @@ pub const Target = struct {
404 };404 };
405405
406 pub const ObjectFormat = enum {406 pub const ObjectFormat = enum {
407 /// TODO Get rid of this one.
407 unknown,408 unknown,
408 coff,409 coff,
409 elf,410 elf,
lib/std/testing.zig+39-1
...@@ -12,7 +12,7 @@ pub const failing_allocator = &failing_allocator_instance.allocator;...@@ -12,7 +12,7 @@ pub const failing_allocator = &failing_allocator_instance.allocator;
12pub var failing_allocator_instance = FailingAllocator.init(&base_allocator_instance.allocator, 0);12pub var failing_allocator_instance = FailingAllocator.init(&base_allocator_instance.allocator, 0);
1313
14pub var base_allocator_instance = std.heap.ThreadSafeFixedBufferAllocator.init(allocator_mem[0..]);14pub 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
17/// This function is intended to be used only in tests. It prints diagnostics to stderr17/// This function is intended to be used only in tests. It prints diagnostics to stderr
18/// and then aborts when actual_error_union is not expected_error.18/// and then aborts when actual_error_union is not expected_error.
...@@ -193,6 +193,44 @@ pub fn expect(ok: bool) void {...@@ -193,6 +193,44 @@ pub fn expect(ok: bool) void {
193 if (!ok) @panic("test failure");193 if (!ok) @panic("test failure");
194}194}
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
196test "expectEqual nested array" {234test "expectEqual nested array" {
197 const a = [2][2]f32{235 const a = [2][2]f32{
198 [_]f32{ 1.0, 0.0 },236 [_]f32{ 1.0, 0.0 },
lib/std/zig.zig+17
...@@ -9,6 +9,23 @@ pub const ast = @import("zig/ast.zig");...@@ -9,6 +9,23 @@ pub const ast = @import("zig/ast.zig");
9pub const system = @import("zig/system.zig");9pub const system = @import("zig/system.zig");
10pub const CrossTarget = @import("zig/cross_target.zig").CrossTarget;10pub 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
12test "" {29test "" {
13 @import("std").meta.refAllDecls(@This());30 @import("std").meta.refAllDecls(@This());
14}31}
lib/std/zig/system.zig+6-1
...@@ -415,7 +415,12 @@ pub const NativeTargetInfo = struct {...@@ -415,7 +415,12 @@ pub const NativeTargetInfo = struct {
415 // over our own shared objects and find a dynamic linker.415 // over our own shared objects and find a dynamic linker.
416 self_exe: {416 self_exe: {
417 const lib_paths = try std.process.getSelfExeSharedLibPaths(allocator);417 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
420 var found_ld_info: LdInfo = undefined;425 var found_ld_info: LdInfo = undefined;
421 var found_ld_path: [:0]const u8 = undefined;426 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....@@ -39,7 +39,7 @@ pub fn generateSymbol(typed_value: ir.TypedValue, module: ir.Module, code: *std.
39 defer function.inst_table.deinit();39 defer function.inst_table.deinit();
40 defer function.errors.deinit();40 defer function.errors.deinit();
4141
42 for (module_fn.body) |inst| {42 for (module_fn.body.instructions) |inst| {
43 const new_inst = function.genFuncInst(inst) catch |err| switch (err) {43 const new_inst = function.genFuncInst(inst) catch |err| switch (err) {
44 error.CodegenFail => {44 error.CodegenFail => {
45 assert(function.errors.items.len != 0);45 assert(function.errors.items.len != 0);
...@@ -77,32 +77,63 @@ const Function = struct {...@@ -77,32 +77,63 @@ const Function = struct {
7777
78 fn genFuncInst(self: *Function, inst: *ir.Inst) !MCValue {78 fn genFuncInst(self: *Function, inst: *ir.Inst) !MCValue {
79 switch (inst.tag) {79 switch (inst.tag) {
80 .unreach => return self.genPanic(inst.src),80 .breakpoint => return self.genBreakpoint(inst.src),
81 .unreach => return MCValue{ .unreach = {} },
81 .constant => unreachable, // excluded from function bodies82 .constant => unreachable, // excluded from function bodies
82 .assembly => return self.genAsm(inst.cast(ir.Inst.Assembly).?),83 .assembly => return self.genAsm(inst.cast(ir.Inst.Assembly).?),
83 .ptrtoint => return self.genPtrToInt(inst.cast(ir.Inst.PtrToInt).?),84 .ptrtoint => return self.genPtrToInt(inst.cast(ir.Inst.PtrToInt).?),
84 .bitcast => return self.genBitCast(inst.cast(ir.Inst.BitCast).?),85 .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).?),
85 }91 }
86 }92 }
8793
88 fn genPanic(self: *Function, src: usize) !MCValue {94 fn genBreakpoint(self: *Function, src: usize) !MCValue {
89 // TODO change this to call the panic function
90 switch (self.module.target.cpu.arch) {95 switch (self.module.target.cpu.arch) {
91 .i386, .x86_64 => {96 .i386, .x86_64 => {
92 try self.code.append(0xcc); // int397 try self.code.append(0xcc); // int3
93 },98 },
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}),
95 }100 }
96 return .unreach;101 return .unreach;
97 }102 }
98103
99 fn genRet(self: *Function, src: usize) !void {104 fn genRet(self: *Function, inst: *ir.Inst.Ret) !MCValue {
100 // TODO change this to call the panic function
101 switch (self.module.target.cpu.arch) {105 switch (self.module.target.cpu.arch) {
102 .i386, .x86_64 => {106 .i386, .x86_64 => {
103 try self.code.append(0xc3); // ret107 try self.code.append(0xc3); // ret
104 },108 },
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 ", .{}),
106 }137 }
107 }138 }
108139
...@@ -501,11 +532,19 @@ fn Reg(comptime arch: Target.Cpu.Arch) type {...@@ -501,11 +532,19 @@ fn Reg(comptime arch: Target.Cpu.Arch) type {
501 bh,532 bh,
502 ch,533 ch,
503 dh,534 dh,
535 bph,
536 sph,
537 sih,
538 dih,
504539
505 al,540 al,
506 bl,541 bl,
507 cl,542 cl,
508 dl,543 dl,
544 bpl,
545 spl,
546 sil,
547 dil,
509 r8b,548 r8b,
510 r9b,549 r9b,
511 r10b,550 r10b,
src-self-hosted/ir.zig+665-174
...@@ -4,10 +4,12 @@ const Allocator = std.mem.Allocator;...@@ -4,10 +4,12 @@ const Allocator = std.mem.Allocator;
4const Value = @import("value.zig").Value;4const Value = @import("value.zig").Value;
5const Type = @import("type.zig").Type;5const Type = @import("type.zig").Type;
6const assert = std.debug.assert;6const assert = std.debug.assert;
7const text = @import("ir/text.zig");7const BigIntConst = std.math.big.int.Const;
8const BigInt = std.math.big.Int;8const BigIntMutable = std.math.big.int.Mutable;
9const Target = std.Target;9const Target = std.Target;
1010
11pub const text = @import("ir/text.zig");
12
11/// These are in-memory, analyzed instructions. See `text.Inst` for the representation13/// These are in-memory, analyzed instructions. See `text.Inst` for the representation
12/// of instructions that correspond to the ZIR text format.14/// of instructions that correspond to the ZIR text format.
13/// This struct owns the `Value` and `Type` memory. When the struct is deallocated,15/// This struct owns the `Value` and `Type` memory. When the struct is deallocated,
...@@ -20,11 +22,17 @@ pub const Inst = struct {...@@ -20,11 +22,17 @@ pub const Inst = struct {
20 src: usize,22 src: usize,
2123
22 pub const Tag = enum {24 pub const Tag = enum {
23 unreach,
24 constant,
25 assembly,25 assembly,
26 ptrtoint,
27 bitcast,26 bitcast,
27 breakpoint,
28 cmp,
29 condbr,
30 constant,
31 isnonnull,
32 isnull,
33 ptrtoint,
34 ret,
35 unreach,
28 };36 };
2937
30 pub fn cast(base: *Inst, comptime T: type) ?*T {38 pub fn cast(base: *Inst, comptime T: type) ?*T {
...@@ -40,23 +48,64 @@ pub const Inst = struct {...@@ -40,23 +48,64 @@ pub const Inst = struct {
4048
41 /// Returns `null` if runtime-known.49 /// Returns `null` if runtime-known.
42 pub fn value(base: *Inst) ?Value {50 pub fn value(base: *Inst) ?Value {
43 return switch (base.tag) {51 if (base.ty.onePossibleValue())
44 .unreach => Value.initTag(.noreturn_value),52 return Value.initTag(.the_one_possible_value);
45 .constant => base.cast(Constant).?.val,53
4654 const inst = base.cast(Constant) orelse return null;
47 .assembly,55 return inst.val;
48 .ptrtoint,
49 .bitcast,
50 => null,
51 };
52 }56 }
5357
54 pub const Unreach = struct {58 pub const Assembly = struct {
55 pub const base_tag = Tag.unreach;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;
56 base: Inst,83 base: Inst,
57 args: void,84 args: void,
58 };85 };
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
60 pub const Constant = struct {109 pub const Constant = struct {
61 pub const base_tag = Tag.constant;110 pub const base_tag = Tag.constant;
62 base: Inst,111 base: Inst,
...@@ -64,17 +113,21 @@ pub const Inst = struct {...@@ -64,17 +113,21 @@ pub const Inst = struct {
64 val: Value,113 val: Value,
65 };114 };
66115
67 pub const Assembly = struct {116 pub const IsNonNull = struct {
68 pub const base_tag = Tag.assembly;117 pub const base_tag = Tag.isnonnull;
118
69 base: Inst,119 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,
71 args: struct {129 args: struct {
72 asm_source: []const u8,130 operand: *Inst,
73 is_volatile: bool,
74 output: ?[]const u8,
75 inputs: []const []const u8,
76 clobbers: []const []const u8,
77 args: []const *Inst,
78 },131 },
79 };132 };
80133
...@@ -87,13 +140,16 @@ pub const Inst = struct {...@@ -87,13 +140,16 @@ pub const Inst = struct {
87 },140 },
88 };141 };
89142
90 pub const BitCast = struct {143 pub const Ret = struct {
91 pub const base_tag = Tag.bitcast;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;
93 base: Inst,151 base: Inst,
94 args: struct {152 args: void,
95 operand: *Inst,
96 },
97 };153 };
98};154};
99155
...@@ -108,6 +164,10 @@ pub const Module = struct {...@@ -108,6 +164,10 @@ pub const Module = struct {
108 arena: std.heap.ArenaAllocator,164 arena: std.heap.ArenaAllocator,
109 fns: []Fn,165 fns: []Fn,
110 target: Target,166 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
112 pub const Export = struct {172 pub const Export = struct {
113 name: []const u8,173 name: []const u8,
...@@ -117,13 +177,21 @@ pub const Module = struct {...@@ -117,13 +177,21 @@ pub const Module = struct {
117177
118 pub const Fn = struct {178 pub const Fn = struct {
119 analysis_status: enum { in_progress, failure, success },179 analysis_status: enum { in_progress, failure, success },
120 body: []*Inst,180 body: Body,
121 fn_type: Type,181 fn_type: Type,
122 };182 };
123183
184 pub const Body = struct {
185 instructions: []*Inst,
186 };
187
124 pub fn deinit(self: *Module, allocator: *Allocator) void {188 pub fn deinit(self: *Module, allocator: *Allocator) void {
125 allocator.free(self.exports);189 allocator.free(self.exports);
126 allocator.free(self.errors);190 allocator.free(self.errors);
191 for (self.fns) |f| {
192 allocator.free(f.body.instructions);
193 }
194 allocator.free(self.fns);
127 self.arena.deinit();195 self.arena.deinit();
128 self.* = undefined;196 self.* = undefined;
129 }197 }
...@@ -134,7 +202,15 @@ pub const ErrorMsg = struct {...@@ -134,7 +202,15 @@ pub const ErrorMsg = struct {
134 msg: []const u8,202 msg: []const u8,
135};203};
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 {
138 var ctx = Analyze{214 var ctx = Analyze{
139 .allocator = allocator,215 .allocator = allocator,
140 .arena = std.heap.ArenaAllocator.init(allocator),216 .arena = std.heap.ArenaAllocator.init(allocator),
...@@ -143,7 +219,10 @@ pub fn analyze(allocator: *Allocator, old_module: text.Module, target: Target) !...@@ -143,7 +219,10 @@ pub fn analyze(allocator: *Allocator, old_module: text.Module, target: Target) !
143 .decl_table = std.AutoHashMap(*text.Inst, Analyze.NewDecl).init(allocator),219 .decl_table = std.AutoHashMap(*text.Inst, Analyze.NewDecl).init(allocator),
144 .exports = std.ArrayList(Module.Export).init(allocator),220 .exports = std.ArrayList(Module.Export).init(allocator),
145 .fns = std.ArrayList(Module.Fn).init(allocator),221 .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,
147 };226 };
148 defer ctx.errors.deinit();227 defer ctx.errors.deinit();
149 defer ctx.decl_table.deinit();228 defer ctx.decl_table.deinit();
...@@ -162,7 +241,11 @@ pub fn analyze(allocator: *Allocator, old_module: text.Module, target: Target) !...@@ -162,7 +241,11 @@ pub fn analyze(allocator: *Allocator, old_module: text.Module, target: Target) !
162 .errors = ctx.errors.toOwnedSlice(),241 .errors = ctx.errors.toOwnedSlice(),
163 .fns = ctx.fns.toOwnedSlice(),242 .fns = ctx.fns.toOwnedSlice(),
164 .arena = ctx.arena,243 .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,
166 };249 };
167}250}
168251
...@@ -175,6 +258,9 @@ const Analyze = struct {...@@ -175,6 +258,9 @@ const Analyze = struct {
175 exports: std.ArrayList(Module.Export),258 exports: std.ArrayList(Module.Export),
176 fns: std.ArrayList(Module.Fn),259 fns: std.ArrayList(Module.Fn),
177 target: Target,260 target: Target,
261 link_mode: std.builtin.LinkMode,
262 optimize_mode: std.builtin.Mode,
263 output_mode: std.builtin.OutputMode,
178264
179 const NewDecl = struct {265 const NewDecl = struct {
180 /// null means a semantic analysis error happened266 /// null means a semantic analysis error happened
...@@ -187,10 +273,15 @@ const Analyze = struct {...@@ -187,10 +273,15 @@ const Analyze = struct {
187 };273 };
188274
189 const Fn = struct {275 const Fn = struct {
190 body: std.ArrayList(*Inst),
191 inst_table: std.AutoHashMap(*text.Inst, NewInst),
192 /// Index into Module fns array276 /// Index into Module fns array
193 fn_index: usize,277 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),
194 };285 };
195286
196 const InnerError = error{ OutOfMemory, AnalysisFail };287 const InnerError = error{ OutOfMemory, AnalysisFail };
...@@ -203,9 +294,9 @@ const Analyze = struct {...@@ -203,9 +294,9 @@ const Analyze = struct {
203 }294 }
204 }295 }
205296
206 fn resolveInst(self: *Analyze, opt_func: ?*Fn, old_inst: *text.Inst) InnerError!*Inst {297 fn resolveInst(self: *Analyze, opt_block: ?*Block, old_inst: *text.Inst) InnerError!*Inst {
207 if (opt_func) |func| {298 if (opt_block) |block| {
208 if (func.inst_table.get(old_inst)) |kv| {299 if (block.func.inst_table.get(old_inst)) |kv| {
209 return kv.value.ptr orelse return error.AnalysisFail;300 return kv.value.ptr orelse return error.AnalysisFail;
210 }301 }
211 }302 }
...@@ -225,12 +316,12 @@ const Analyze = struct {...@@ -225,12 +316,12 @@ const Analyze = struct {
225 }316 }
226 }317 }
227318
228 fn requireFunctionBody(self: *Analyze, func: ?*Fn, src: usize) !*Fn {319 fn requireRuntimeBlock(self: *Analyze, block: ?*Block, src: usize) !*Block {
229 return func orelse return self.fail(src, "instruction illegal outside function body", .{});320 return block orelse return self.fail(src, "instruction illegal outside function body", .{});
230 }321 }
231322
232 fn resolveInstConst(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) InnerError!TypedValue {323 fn resolveInstConst(self: *Analyze, block: ?*Block, old_inst: *text.Inst) InnerError!TypedValue {
233 const new_inst = try self.resolveInst(func, old_inst);324 const new_inst = try self.resolveInst(block, old_inst);
234 const val = try self.resolveConstValue(new_inst);325 const val = try self.resolveConstValue(new_inst);
235 return TypedValue{326 return TypedValue{
236 .ty = new_inst.ty,327 .ty = new_inst.ty,
...@@ -239,28 +330,39 @@ const Analyze = struct {...@@ -239,28 +330,39 @@ const Analyze = struct {
239 }330 }
240331
241 fn resolveConstValue(self: *Analyze, base: *Inst) !Value {332 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", .{});
243 }335 }
244336
245 fn resolveConstString(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) ![]u8 {337 fn resolveDefinedValue(self: *Analyze, base: *Inst) !?Value {
246 const new_inst = try self.resolveInst(func, old_inst);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);
247 const wanted_type = Type.initTag(.const_slice_u8);349 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);
249 const val = try self.resolveConstValue(coerced_inst);351 const val = try self.resolveConstValue(coerced_inst);
250 return val.toAllocatedBytes(&self.arena.allocator);352 return val.toAllocatedBytes(&self.arena.allocator);
251 }353 }
252354
253 fn resolveType(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) !Type {355 fn resolveType(self: *Analyze, block: ?*Block, old_inst: *text.Inst) !Type {
254 const new_inst = try self.resolveInst(func, old_inst);356 const new_inst = try self.resolveInst(block, old_inst);
255 const wanted_type = Type.initTag(.@"type");357 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);
257 const val = try self.resolveConstValue(coerced_inst);359 const val = try self.resolveConstValue(coerced_inst);
258 return val.toType();360 return val.toType();
259 }361 }
260362
261 fn analyzeExport(self: *Analyze, func: ?*Fn, export_inst: *text.Inst.Export) !void {363 fn analyzeExport(self: *Analyze, block: ?*Block, export_inst: *text.Inst.Export) !void {
262 const symbol_name = try self.resolveConstString(func, export_inst.positionals.symbol_name);364 const symbol_name = try self.resolveConstString(block, export_inst.positionals.symbol_name);
263 const typed_value = try self.resolveInstConst(func, export_inst.positionals.value);365 const typed_value = try self.resolveInstConst(block, export_inst.positionals.value);
264366
265 switch (typed_value.ty.zigTypeTag()) {367 switch (typed_value.ty.zigTypeTag()) {
266 .Fn => {},368 .Fn => {},
...@@ -280,18 +382,18 @@ const Analyze = struct {...@@ -280,18 +382,18 @@ const Analyze = struct {
280 /// TODO should not need the cast on the last parameter at the callsites382 /// TODO should not need the cast on the last parameter at the callsites
281 fn addNewInstArgs(383 fn addNewInstArgs(
282 self: *Analyze,384 self: *Analyze,
283 func: *Fn,385 block: *Block,
284 src: usize,386 src: usize,
285 ty: Type,387 ty: Type,
286 comptime T: type,388 comptime T: type,
287 args: Inst.Args(T),389 args: Inst.Args(T),
288 ) !*Inst {390 ) !*Inst {
289 const inst = try self.addNewInst(func, src, ty, T);391 const inst = try self.addNewInst(block, src, ty, T);
290 inst.args = args;392 inst.args = args;
291 return &inst.base;393 return &inst.base;
292 }394 }
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 {
295 const inst = try self.arena.allocator.create(T);397 const inst = try self.arena.allocator.create(T);
296 inst.* = .{398 inst.* = .{
297 .base = .{399 .base = .{
...@@ -301,7 +403,7 @@ const Analyze = struct {...@@ -301,7 +403,7 @@ const Analyze = struct {
301 },403 },
302 .args = undefined,404 .args = undefined,
303 };405 };
304 try func.body.append(&inst.base);406 try block.instructions.append(&inst.base);
305 return inst;407 return inst;
306 }408 }
307409
...@@ -344,7 +446,21 @@ const Analyze = struct {...@@ -344,7 +446,21 @@ const Analyze = struct {
344 fn constVoid(self: *Analyze, src: usize) !*Inst {446 fn constVoid(self: *Analyze, src: usize) !*Inst {
345 return self.constInst(src, .{447 return self.constInst(src, .{
346 .ty = Type.initTag(.void),448 .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)],
348 });464 });
349 }465 }
350466
...@@ -368,34 +484,38 @@ const Analyze = struct {...@@ -368,34 +484,38 @@ const Analyze = struct {
368 });484 });
369 }485 }
370486
371 fn constIntBig(self: *Analyze, src: usize, ty: Type, big_int: BigInt) !*Inst {487 fn constIntBig(self: *Analyze, src: usize, ty: Type, big_int: BigIntConst) !*Inst {
372 if (big_int.isPositive()) {488 const val_payload = if (big_int.positive) blk: {
373 if (big_int.to(u64)) |x| {489 if (big_int.to(u64)) |x| {
374 return self.constIntUnsigned(src, ty, x);490 return self.constIntUnsigned(src, ty, x);
375 } else |err| switch (err) {491 } else |err| switch (err) {
376 error.NegativeIntoUnsigned => unreachable,492 error.NegativeIntoUnsigned => unreachable,
377 error.TargetTooSmall => {}, // handled below493 error.TargetTooSmall => {}, // handled below
378 }494 }
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: {
380 if (big_int.to(i64)) |x| {499 if (big_int.to(i64)) |x| {
381 return self.constIntSigned(src, ty, x);500 return self.constIntSigned(src, ty, x);
382 } else |err| switch (err) {501 } else |err| switch (err) {
383 error.NegativeIntoUnsigned => unreachable,502 error.NegativeIntoUnsigned => unreachable,
384 error.TargetTooSmall => {}, // handled below503 error.TargetTooSmall => {}, // handled below
385 }504 }
386 }505 const big_int_payload = try self.arena.allocator.create(Value.Payload.IntBigNegative);
387506 big_int_payload.* = .{ .limbs = big_int.limbs };
388 const big_int_payload = try self.arena.allocator.create(Value.Payload.IntBig);507 break :blk &big_int_payload.base;
389 big_int_payload.* = .{ .big_int = big_int };508 };
390509
391 return self.constInst(src, .{510 return self.constInst(src, .{
392 .ty = ty,511 .ty = ty,
393 .val = Value.initPayload(&big_int_payload.base),512 .val = Value.initPayload(val_payload),
394 });513 });
395 }514 }
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 {
398 switch (old_inst.tag) {517 switch (old_inst.tag) {
518 .breakpoint => return self.analyzeInstBreakpoint(block, old_inst.cast(text.Inst.Breakpoint).?),
399 .str => {519 .str => {
400 // We can use this reference because Inst.Const's Value is arena-allocated.520 // We can use this reference because Inst.Const's Value is arena-allocated.
401 // The value would get copied to a MemoryCell before the `text.Inst.Str` lifetime ends.521 // The value would get copied to a MemoryCell before the `text.Inst.Str` lifetime ends.
...@@ -406,35 +526,49 @@ const Analyze = struct {...@@ -406,35 +526,49 @@ const Analyze = struct {
406 const big_int = old_inst.cast(text.Inst.Int).?.positionals.int;526 const big_int = old_inst.cast(text.Inst.Int).?.positionals.int;
407 return self.constIntBig(old_inst.src, Type.initTag(.comptime_int), big_int);527 return self.constIntBig(old_inst.src, Type.initTag(.comptime_int), big_int);
408 },528 },
409 .ptrtoint => return self.analyzeInstPtrToInt(func, old_inst.cast(text.Inst.PtrToInt).?),529 .ptrtoint => return self.analyzeInstPtrToInt(block, old_inst.cast(text.Inst.PtrToInt).?),
410 .fieldptr => return self.analyzeInstFieldPtr(func, old_inst.cast(text.Inst.FieldPtr).?),530 .fieldptr => return self.analyzeInstFieldPtr(block, old_inst.cast(text.Inst.FieldPtr).?),
411 .deref => return self.analyzeInstDeref(func, old_inst.cast(text.Inst.Deref).?),531 .deref => return self.analyzeInstDeref(block, old_inst.cast(text.Inst.Deref).?),
412 .as => return self.analyzeInstAs(func, old_inst.cast(text.Inst.As).?),532 .as => return self.analyzeInstAs(block, old_inst.cast(text.Inst.As).?),
413 .@"asm" => return self.analyzeInstAsm(func, old_inst.cast(text.Inst.Asm).?),533 .@"asm" => return self.analyzeInstAsm(block, old_inst.cast(text.Inst.Asm).?),
414 .@"unreachable" => return self.analyzeInstUnreachable(func, old_inst.cast(text.Inst.Unreachable).?),534 .@"unreachable" => return self.analyzeInstUnreachable(block, old_inst.cast(text.Inst.Unreachable).?),
415 .@"fn" => return self.analyzeInstFn(func, old_inst.cast(text.Inst.Fn).?),535 .@"return" => return self.analyzeInstRet(block, old_inst.cast(text.Inst.Return).?),
536 .@"fn" => return self.analyzeInstFn(block, old_inst.cast(text.Inst.Fn).?),
416 .@"export" => {537 .@"export" => {
417 try self.analyzeExport(func, old_inst.cast(text.Inst.Export).?);538 try self.analyzeExport(block, old_inst.cast(text.Inst.Export).?);
418 return self.constVoid(old_inst.src);539 return self.constVoid(old_inst.src);
419 },540 },
420 .primitive => return self.analyzeInstPrimitive(func, old_inst.cast(text.Inst.Primitive).?),541 .primitive => return self.analyzeInstPrimitive(old_inst.cast(text.Inst.Primitive).?),
421 .fntype => return self.analyzeInstFnType(func, old_inst.cast(text.Inst.FnType).?),542 .fntype => return self.analyzeInstFnType(block, old_inst.cast(text.Inst.FnType).?),
422 .intcast => return self.analyzeInstIntCast(func, old_inst.cast(text.Inst.IntCast).?),543 .intcast => return self.analyzeInstIntCast(block, old_inst.cast(text.Inst.IntCast).?),
423 .bitcast => return self.analyzeInstBitCast(func, old_inst.cast(text.Inst.BitCast).?),544 .bitcast => return self.analyzeInstBitCast(block, old_inst.cast(text.Inst.BitCast).?),
424 .elemptr => return self.analyzeInstElemPtr(func, old_inst.cast(text.Inst.ElemPtr).?),545 .elemptr => return self.analyzeInstElemPtr(block, old_inst.cast(text.Inst.ElemPtr).?),
425 .add => return self.analyzeInstAdd(func, old_inst.cast(text.Inst.Add).?),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).?),
426 }551 }
427 }552 }
428553
429 fn analyzeInstFn(self: *Analyze, opt_func: ?*Fn, fn_inst: *text.Inst.Fn) InnerError!*Inst {554 fn analyzeInstBreakpoint(self: *Analyze, block: ?*Block, inst: *text.Inst.Breakpoint) InnerError!*Inst {
430 const fn_type = try self.resolveType(opt_func, fn_inst.positionals.fn_type);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
432 var new_func: Fn = .{562 var new_func: Fn = .{
433 .body = std.ArrayList(*Inst).init(self.allocator),
434 .inst_table = std.AutoHashMap(*text.Inst, NewInst).init(self.allocator),
435 .fn_index = self.fns.items.len,563 .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),
436 };569 };
437 defer new_func.body.deinit();570 new_func.inner_block.func = &new_func;
571 defer new_func.inner_block.instructions.deinit();
438 defer new_func.inst_table.deinit();572 defer new_func.inst_table.deinit();
439 // Don't hang on to a reference to this when analyzing body instructions, since the memory573 // Don't hang on to a reference to this when analyzing body instructions, since the memory
440 // could become invalid.574 // could become invalid.
...@@ -444,18 +578,11 @@ const Analyze = struct {...@@ -444,18 +578,11 @@ const Analyze = struct {
444 .body = undefined,578 .body = undefined,
445 };579 };
446580
447 for (fn_inst.positionals.body.instructions) |src_inst| {581 try self.analyzeBody(&new_func.inner_block, fn_inst.positionals.body);
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 }
455582
456 const f = &self.fns.items[new_func.fn_index];583 const f = &self.fns.items[new_func.fn_index];
457 f.analysis_status = .success;584 f.analysis_status = .success;
458 f.body = new_func.body.toOwnedSlice();585 f.body = .{ .instructions = new_func.inner_block.instructions.toOwnedSlice() };
459586
460 const fn_payload = try self.arena.allocator.create(Value.Payload.Function);587 const fn_payload = try self.arena.allocator.create(Value.Payload.Function);
461 fn_payload.* = .{ .index = new_func.fn_index };588 fn_payload.* = .{ .index = new_func.fn_index };
...@@ -466,8 +593,8 @@ const Analyze = struct {...@@ -466,8 +593,8 @@ const Analyze = struct {
466 });593 });
467 }594 }
468595
469 fn analyzeInstFnType(self: *Analyze, func: ?*Fn, fntype: *text.Inst.FnType) InnerError!*Inst {596 fn analyzeInstFnType(self: *Analyze, block: ?*Block, fntype: *text.Inst.FnType) InnerError!*Inst {
470 const return_type = try self.resolveType(func, fntype.positionals.return_type);597 const return_type = try self.resolveType(block, fntype.positionals.return_type);
471598
472 if (return_type.zigTypeTag() == .NoReturn and599 if (return_type.zigTypeTag() == .NoReturn and
473 fntype.positionals.param_types.len == 0 and600 fntype.positionals.param_types.len == 0 and
...@@ -476,33 +603,40 @@ const Analyze = struct {...@@ -476,33 +603,40 @@ const Analyze = struct {
476 return self.constType(fntype.base.src, Type.initTag(.fn_naked_noreturn_no_args));603 return self.constType(fntype.base.src, Type.initTag(.fn_naked_noreturn_no_args));
477 }604 }
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
479 return self.fail(fntype.base.src, "TODO implement fntype instruction more", .{});613 return self.fail(fntype.base.src, "TODO implement fntype instruction more", .{});
480 }614 }
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 {
483 return self.constType(primitive.base.src, primitive.positionals.tag.toType());617 return self.constType(primitive.base.src, primitive.positionals.tag.toType());
484 }618 }
485619
486 fn analyzeInstAs(self: *Analyze, func: ?*Fn, as: *text.Inst.As) InnerError!*Inst {620 fn analyzeInstAs(self: *Analyze, block: ?*Block, as: *text.Inst.As) InnerError!*Inst {
487 const dest_type = try self.resolveType(func, as.positionals.dest_type);621 const dest_type = try self.resolveType(block, as.positionals.dest_type);
488 const new_inst = try self.resolveInst(func, as.positionals.value);622 const new_inst = try self.resolveInst(block, as.positionals.value);
489 return self.coerce(func, dest_type, new_inst);623 return self.coerce(block, dest_type, new_inst);
490 }624 }
491625
492 fn analyzeInstPtrToInt(self: *Analyze, func: ?*Fn, ptrtoint: *text.Inst.PtrToInt) InnerError!*Inst {626 fn analyzeInstPtrToInt(self: *Analyze, block: ?*Block, ptrtoint: *text.Inst.PtrToInt) InnerError!*Inst {
493 const ptr = try self.resolveInst(func, ptrtoint.positionals.ptr);627 const ptr = try self.resolveInst(block, ptrtoint.positionals.ptr);
494 if (ptr.ty.zigTypeTag() != .Pointer) {628 if (ptr.ty.zigTypeTag() != .Pointer) {
495 return self.fail(ptrtoint.positionals.ptr.src, "expected pointer, found '{}'", .{ptr.ty});629 return self.fail(ptrtoint.positionals.ptr.src, "expected pointer, found '{}'", .{ptr.ty});
496 }630 }
497 // TODO handle known-pointer-address631 // 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);
499 const ty = Type.initTag(.usize);633 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 });
501 }635 }
502636
503 fn analyzeInstFieldPtr(self: *Analyze, func: ?*Fn, fieldptr: *text.Inst.FieldPtr) InnerError!*Inst {637 fn analyzeInstFieldPtr(self: *Analyze, block: ?*Block, fieldptr: *text.Inst.FieldPtr) InnerError!*Inst {
504 const object_ptr = try self.resolveInst(func, fieldptr.positionals.object_ptr);638 const object_ptr = try self.resolveInst(block, fieldptr.positionals.object_ptr);
505 const field_name = try self.resolveConstString(func, fieldptr.positionals.field_name);639 const field_name = try self.resolveConstString(block, fieldptr.positionals.field_name);
506640
507 const elem_ty = switch (object_ptr.ty.zigTypeTag()) {641 const elem_ty = switch (object_ptr.ty.zigTypeTag()) {
508 .Pointer => object_ptr.ty.elemType(),642 .Pointer => object_ptr.ty.elemType(),
...@@ -533,9 +667,9 @@ const Analyze = struct {...@@ -533,9 +667,9 @@ const Analyze = struct {
533 }667 }
534 }668 }
535669
536 fn analyzeInstIntCast(self: *Analyze, func: ?*Fn, intcast: *text.Inst.IntCast) InnerError!*Inst {670 fn analyzeInstIntCast(self: *Analyze, block: ?*Block, intcast: *text.Inst.IntCast) InnerError!*Inst {
537 const dest_type = try self.resolveType(func, intcast.positionals.dest_type);671 const dest_type = try self.resolveType(block, intcast.positionals.dest_type);
538 const new_inst = try self.resolveInst(func, intcast.positionals.value);672 const new_inst = try self.resolveInst(block, intcast.positionals.value);
539673
540 const dest_is_comptime_int = switch (dest_type.zigTypeTag()) {674 const dest_is_comptime_int = switch (dest_type.zigTypeTag()) {
541 .ComptimeInt => true,675 .ComptimeInt => true,
...@@ -559,22 +693,22 @@ const Analyze = struct {...@@ -559,22 +693,22 @@ const Analyze = struct {
559 }693 }
560694
561 if (dest_is_comptime_int or new_inst.value() != null) {695 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);
563 }697 }
564698
565 return self.fail(intcast.base.src, "TODO implement analyze widen or shorten int", .{});699 return self.fail(intcast.base.src, "TODO implement analyze widen or shorten int", .{});
566 }700 }
567701
568 fn analyzeInstBitCast(self: *Analyze, func: ?*Fn, inst: *text.Inst.BitCast) InnerError!*Inst {702 fn analyzeInstBitCast(self: *Analyze, block: ?*Block, inst: *text.Inst.BitCast) InnerError!*Inst {
569 const dest_type = try self.resolveType(func, inst.positionals.dest_type);703 const dest_type = try self.resolveType(block, inst.positionals.dest_type);
570 const operand = try self.resolveInst(func, inst.positionals.operand);704 const operand = try self.resolveInst(block, inst.positionals.operand);
571 return self.bitcast(func, dest_type, operand);705 return self.bitcast(block, dest_type, operand);
572 }706 }
573707
574 fn analyzeInstElemPtr(self: *Analyze, func: ?*Fn, inst: *text.Inst.ElemPtr) InnerError!*Inst {708 fn analyzeInstElemPtr(self: *Analyze, block: ?*Block, inst: *text.Inst.ElemPtr) InnerError!*Inst {
575 const array_ptr = try self.resolveInst(func, inst.positionals.array_ptr);709 const array_ptr = try self.resolveInst(block, inst.positionals.array_ptr);
576 const uncasted_index = try self.resolveInst(func, inst.positionals.index);710 const uncasted_index = try self.resolveInst(block, inst.positionals.index);
577 const elem_index = try self.coerce(func, Type.initTag(.usize), uncasted_index);711 const elem_index = try self.coerce(block, Type.initTag(.usize), uncasted_index);
578712
579 if (array_ptr.ty.isSinglePointer() and array_ptr.ty.elemType().zigTypeTag() == .Array) {713 if (array_ptr.ty.isSinglePointer() and array_ptr.ty.elemType().zigTypeTag() == .Array) {
580 if (array_ptr.value()) |array_ptr_val| {714 if (array_ptr.value()) |array_ptr_val| {
...@@ -602,28 +736,44 @@ const Analyze = struct {...@@ -602,28 +736,44 @@ const Analyze = struct {
602 return self.fail(inst.base.src, "TODO implement more analyze elemptr", .{});736 return self.fail(inst.base.src, "TODO implement more analyze elemptr", .{});
603 }737 }
604738
605 fn analyzeInstAdd(self: *Analyze, func: ?*Fn, inst: *text.Inst.Add) InnerError!*Inst {739 fn analyzeInstAdd(self: *Analyze, block: ?*Block, inst: *text.Inst.Add) InnerError!*Inst {
606 const lhs = try self.resolveInst(func, inst.positionals.lhs);740 const lhs = try self.resolveInst(block, inst.positionals.lhs);
607 const rhs = try self.resolveInst(func, inst.positionals.rhs);741 const rhs = try self.resolveInst(block, inst.positionals.rhs);
608742
609 if (lhs.ty.zigTypeTag() == .Int and rhs.ty.zigTypeTag() == .Int) {743 if (lhs.ty.zigTypeTag() == .Int and rhs.ty.zigTypeTag() == .Int) {
610 if (lhs.value()) |lhs_val| {744 if (lhs.value()) |lhs_val| {
611 if (rhs.value()) |rhs_val| {745 if (rhs.value()) |rhs_val| {
612 const lhs_bigint = try lhs_val.toBigInt(&self.arena.allocator);746 // TODO is this a performance issue? maybe we should try the operation without
613 const rhs_bigint = try rhs_val.toBigInt(&self.arena.allocator);747 // resorting to BigInt first.
614 var result_bigint = try BigInt.init(&self.arena.allocator);748 var lhs_space: Value.BigIntSpace = undefined;
615 try BigInt.add(&result_bigint, lhs_bigint, rhs_bigint);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
617 if (!lhs.ty.eql(rhs.ty)) {760 if (!lhs.ty.eql(rhs.ty)) {
618 return self.fail(inst.base.src, "TODO implement peer type resolution", .{});761 return self.fail(inst.base.src, "TODO implement peer type resolution", .{});
619 }762 }
620763
621 const val_payload = try self.arena.allocator.create(Value.Payload.IntBig);764 const val_payload = if (result_bigint.positive) blk: {
622 val_payload.* = .{ .big_int = result_bigint };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
624 return self.constInst(inst.base.src, .{774 return self.constInst(inst.base.src, .{
625 .ty = lhs.ty,775 .ty = lhs.ty,
626 .val = Value.initPayload(&val_payload.base),776 .val = Value.initPayload(val_payload),
627 });777 });
628 }778 }
629 }779 }
...@@ -632,8 +782,8 @@ const Analyze = struct {...@@ -632,8 +782,8 @@ const Analyze = struct {
632 return self.fail(inst.base.src, "TODO implement more analyze add", .{});782 return self.fail(inst.base.src, "TODO implement more analyze add", .{});
633 }783 }
634784
635 fn analyzeInstDeref(self: *Analyze, func: ?*Fn, deref: *text.Inst.Deref) InnerError!*Inst {785 fn analyzeInstDeref(self: *Analyze, block: ?*Block, deref: *text.Inst.Deref) InnerError!*Inst {
636 const ptr = try self.resolveInst(func, deref.positionals.ptr);786 const ptr = try self.resolveInst(block, deref.positionals.ptr);
637 const elem_ty = switch (ptr.ty.zigTypeTag()) {787 const elem_ty = switch (ptr.ty.zigTypeTag()) {
638 .Pointer => ptr.ty.elemType(),788 .Pointer => ptr.ty.elemType(),
639 else => return self.fail(deref.positionals.ptr.src, "expected pointer, found '{}'", .{ptr.ty}),789 else => return self.fail(deref.positionals.ptr.src, "expected pointer, found '{}'", .{ptr.ty}),
...@@ -648,28 +798,28 @@ const Analyze = struct {...@@ -648,28 +798,28 @@ const Analyze = struct {
648 return self.fail(deref.base.src, "TODO implement runtime deref", .{});798 return self.fail(deref.base.src, "TODO implement runtime deref", .{});
649 }799 }
650800
651 fn analyzeInstAsm(self: *Analyze, func: ?*Fn, assembly: *text.Inst.Asm) InnerError!*Inst {801 fn analyzeInstAsm(self: *Analyze, block: ?*Block, assembly: *text.Inst.Asm) InnerError!*Inst {
652 const return_type = try self.resolveType(func, assembly.positionals.return_type);802 const return_type = try self.resolveType(block, assembly.positionals.return_type);
653 const asm_source = try self.resolveConstString(func, assembly.positionals.asm_source);803 const asm_source = try self.resolveConstString(block, assembly.positionals.asm_source);
654 const output = if (assembly.kw_args.output) |o| try self.resolveConstString(func, o) else null;804 const output = if (assembly.kw_args.output) |o| try self.resolveConstString(block, o) else null;
655805
656 const inputs = try self.arena.allocator.alloc([]const u8, assembly.kw_args.inputs.len);806 const inputs = try self.arena.allocator.alloc([]const u8, assembly.kw_args.inputs.len);
657 const clobbers = try self.arena.allocator.alloc([]const u8, assembly.kw_args.clobbers.len);807 const clobbers = try self.arena.allocator.alloc([]const u8, assembly.kw_args.clobbers.len);
658 const args = try self.arena.allocator.alloc(*Inst, assembly.kw_args.args.len);808 const args = try self.arena.allocator.alloc(*Inst, assembly.kw_args.args.len);
659809
660 for (inputs) |*elem, i| {810 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]);
662 }812 }
663 for (clobbers) |*elem, i| {813 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]);
665 }815 }
666 for (args) |*elem, i| {816 for (args) |*elem, i| {
667 const arg = try self.resolveInst(func, assembly.kw_args.args[i]);817 const arg = try self.resolveInst(block, assembly.kw_args.args[i]);
668 elem.* = try self.coerce(func, Type.initTag(.usize), arg);818 elem.* = try self.coerce(block, Type.initTag(.usize), arg);
669 }819 }
670820
671 const f = try self.requireFunctionBody(func, assembly.base.src);821 const b = try self.requireRuntimeBlock(block, assembly.base.src);
672 return self.addNewInstArgs(f, assembly.base.src, return_type, Inst.Assembly, Inst.Args(Inst.Assembly){822 return self.addNewInstArgs(b, assembly.base.src, return_type, Inst.Assembly, Inst.Args(Inst.Assembly){
673 .asm_source = asm_source,823 .asm_source = asm_source,
674 .is_volatile = assembly.kw_args.@"volatile",824 .is_volatile = assembly.kw_args.@"volatile",
675 .output = output,825 .output = output,
...@@ -679,19 +829,370 @@ const Analyze = struct {...@@ -679,19 +829,370 @@ const Analyze = struct {
679 });829 });
680 }830 }
681831
682 fn analyzeInstUnreachable(self: *Analyze, func: ?*Fn, unreach: *text.Inst.Unreachable) InnerError!*Inst {832 fn analyzeInstCmp(self: *Analyze, block: ?*Block, inst: *text.Inst.Cmp) InnerError!*Inst {
683 const f = try self.requireFunctionBody(func, unreach.base.src);833 const lhs = try self.resolveInst(block, inst.positionals.lhs);
684 return self.addNewInstArgs(f, unreach.base.src, Type.initTag(.noreturn), Inst.Unreach, {});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 }
685 }1186 }
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 {
688 // If the types are the same, we can return the operand.1189 // If the types are the same, we can return the operand.
689 if (dest_type.eql(inst.ty))1190 if (dest_type.eql(inst.ty))
690 return inst;1191 return inst;
6911192
692 const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty);1193 const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty);
693 if (in_memory_result == .ok) {1194 if (in_memory_result == .ok) {
694 return self.bitcast(func, dest_type, inst);1195 return self.bitcast(block, dest_type, inst);
695 }1196 }
6961197
697 // *[N]T to []T1198 // *[N]T to []T
...@@ -735,14 +1236,14 @@ const Analyze = struct {...@@ -735,14 +1236,14 @@ const Analyze = struct {
735 return self.fail(inst.src, "TODO implement type coercion from {} to {}", .{ inst.ty, dest_type });1236 return self.fail(inst.src, "TODO implement type coercion from {} to {}", .{ inst.ty, dest_type });
736 }1237 }
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 {
739 if (inst.value()) |val| {1240 if (inst.value()) |val| {
740 // Keep the comptime Value representation; take the new type.1241 // Keep the comptime Value representation; take the new type.
741 return self.constInst(inst.src, .{ .ty = dest_type, .val = val });1242 return self.constInst(inst.src, .{ .ty = dest_type, .val = val });
742 }1243 }
743 // TODO validate the type size and other compile errors1244 // TODO validate the type size and other compile errors
744 const f = try self.requireFunctionBody(func, inst.src);1245 const b = try self.requireRuntimeBlock(block, inst.src);
745 return self.addNewInstArgs(f, inst.src, dest_type, Inst.BitCast, Inst.Args(Inst.BitCast){ .operand = inst });1246 return self.addNewInstArgs(b, inst.src, dest_type, Inst.BitCast, Inst.Args(Inst.BitCast){ .operand = inst });
746 }1247 }
7471248
748 fn coerceArrayPtrToSlice(self: *Analyze, dest_type: Type, inst: *Inst) !*Inst {1249 fn coerceArrayPtrToSlice(self: *Analyze, dest_type: Type, inst: *Inst) !*Inst {
...@@ -784,18 +1285,20 @@ pub fn main() anyerror!void {...@@ -784,18 +1285,20 @@ pub fn main() anyerror!void {
784 const allocator = if (std.builtin.link_libc) std.heap.c_allocator else &arena.allocator;1285 const allocator = if (std.builtin.link_libc) std.heap.c_allocator else &arena.allocator;
7851286
786 const args = try std.process.argsAlloc(allocator);1287 const args = try std.process.argsAlloc(allocator);
1288 defer std.process.argsFree(allocator, args);
7871289
788 const src_path = args[1];1290 const src_path = args[1];
789 const debug_error_trace = true;1291 const debug_error_trace = true;
7901292
791 const source = try std.fs.cwd().readFileAllocOptions(allocator, src_path, std.math.maxInt(u32), 1, 0);1293 const source = try std.fs.cwd().readFileAllocOptions(allocator, src_path, std.math.maxInt(u32), 1, 0);
1294 defer allocator.free(source);
7921295
793 var zir_module = try text.parse(allocator, source);1296 var zir_module = try text.parse(allocator, source);
794 defer zir_module.deinit(allocator);1297 defer zir_module.deinit(allocator);
7951298
796 if (zir_module.errors.len != 0) {1299 if (zir_module.errors.len != 0) {
797 for (zir_module.errors) |err_msg| {1300 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);
799 std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg });1302 std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg });
800 }1303 }
801 if (debug_error_trace) return error.ParseFailure;1304 if (debug_error_trace) return error.ParseFailure;
...@@ -804,15 +1307,20 @@ pub fn main() anyerror!void {...@@ -804,15 +1307,20 @@ pub fn main() anyerror!void {
8041307
805 const native_info = try std.zig.system.NativeTargetInfo.detect(allocator, .{});1308 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 });
808 defer analyzed_module.deinit(allocator);1316 defer analyzed_module.deinit(allocator);
8091317
810 if (analyzed_module.errors.len != 0) {1318 if (analyzed_module.errors.len != 0) {
811 for (analyzed_module.errors) |err_msg| {1319 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);
813 std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg });1321 std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg });
814 }1322 }
815 if (debug_error_trace) return error.ParseFailure;1323 if (debug_error_trace) return error.AnalysisFail;
816 std.process.exit(1);1324 std.process.exit(1);
817 }1325 }
8181326
...@@ -827,34 +1335,17 @@ pub fn main() anyerror!void {...@@ -827,34 +1335,17 @@ pub fn main() anyerror!void {
827 }1335 }
8281336
829 const link = @import("link.zig");1337 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");
831 defer result.deinit(allocator);1339 defer result.deinit(allocator);
832 if (result.errors.len != 0) {1340 if (result.errors.len != 0) {
833 for (result.errors) |err_msg| {1341 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);
835 std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg });1343 std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg });
836 }1344 }
837 if (debug_error_trace) return error.ParseFailure;1345 if (debug_error_trace) return error.LinkFailure;
838 std.process.exit(1);1346 std.process.exit(1);
839 }1347 }
840}1348}
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
859// Performance optimization ideas:1350// Performance optimization ideas:
860// * when analyzing use a field in the Inst instead of HashMap to track corresponding instructions1351// * 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");...@@ -4,7 +4,8 @@ const std = @import("std");
4const mem = std.mem;4const mem = std.mem;
5const Allocator = std.mem.Allocator;5const Allocator = std.mem.Allocator;
6const assert = std.debug.assert;6const assert = std.debug.assert;
7const BigInt = std.math.big.Int;7const BigIntConst = std.math.big.int.Const;
8const BigIntMutable = std.math.big.int.Mutable;
8const Type = @import("../type.zig").Type;9const Type = @import("../type.zig").Type;
9const Value = @import("../value.zig").Value;10const Value = @import("../value.zig").Value;
10const ir = @import("../ir.zig");11const ir = @import("../ir.zig");
...@@ -18,6 +19,7 @@ pub const Inst = struct {...@@ -18,6 +19,7 @@ pub const Inst = struct {
1819
19 /// These names are used directly as the instruction names in the text format.20 /// These names are used directly as the instruction names in the text format.
20 pub const Tag = enum {21 pub const Tag = enum {
22 breakpoint,
21 str,23 str,
22 int,24 int,
23 ptrtoint,25 ptrtoint,
...@@ -26,6 +28,7 @@ pub const Inst = struct {...@@ -26,6 +28,7 @@ pub const Inst = struct {
26 as,28 as,
27 @"asm",29 @"asm",
28 @"unreachable",30 @"unreachable",
31 @"return",
29 @"fn",32 @"fn",
30 @"export",33 @"export",
31 primitive,34 primitive,
...@@ -34,10 +37,15 @@ pub const Inst = struct {...@@ -34,10 +37,15 @@ pub const Inst = struct {
34 bitcast,37 bitcast,
35 elemptr,38 elemptr,
36 add,39 add,
40 cmp,
41 condbr,
42 isnull,
43 isnonnull,
37 };44 };
3845
39 pub fn TagToType(tag: Tag) type {46 pub fn TagToType(tag: Tag) type {
40 return switch (tag) {47 return switch (tag) {
48 .breakpoint => Breakpoint,
41 .str => Str,49 .str => Str,
42 .int => Int,50 .int => Int,
43 .ptrtoint => PtrToInt,51 .ptrtoint => PtrToInt,
...@@ -46,6 +54,7 @@ pub const Inst = struct {...@@ -46,6 +54,7 @@ pub const Inst = struct {
46 .as => As,54 .as => As,
47 .@"asm" => Asm,55 .@"asm" => Asm,
48 .@"unreachable" => Unreachable,56 .@"unreachable" => Unreachable,
57 .@"return" => Return,
49 .@"fn" => Fn,58 .@"fn" => Fn,
50 .@"export" => Export,59 .@"export" => Export,
51 .primitive => Primitive,60 .primitive => Primitive,
...@@ -54,6 +63,10 @@ pub const Inst = struct {...@@ -54,6 +63,10 @@ pub const Inst = struct {
54 .bitcast => BitCast,63 .bitcast => BitCast,
55 .elemptr => ElemPtr,64 .elemptr => ElemPtr,
56 .add => Add,65 .add => Add,
66 .cmp => Cmp,
67 .condbr => CondBr,
68 .isnull => IsNull,
69 .isnonnull => IsNonNull,
57 };70 };
58 }71 }
5972
...@@ -64,6 +77,14 @@ pub const Inst = struct {...@@ -64,6 +77,14 @@ pub const Inst = struct {
64 return @fieldParentPtr(T, "base", base);77 return @fieldParentPtr(T, "base", base);
65 }78 }
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
67 pub const Str = struct {88 pub const Str = struct {
68 pub const base_tag = Tag.str;89 pub const base_tag = Tag.str;
69 base: Inst,90 base: Inst,
...@@ -79,7 +100,7 @@ pub const Inst = struct {...@@ -79,7 +100,7 @@ pub const Inst = struct {
79 base: Inst,100 base: Inst,
80101
81 positionals: struct {102 positionals: struct {
82 int: BigInt,103 int: BigIntConst,
83 },104 },
84 kw_args: struct {},105 kw_args: struct {},
85 };106 };
...@@ -151,19 +172,23 @@ pub const Inst = struct {...@@ -151,19 +172,23 @@ pub const Inst = struct {
151 kw_args: struct {},172 kw_args: struct {},
152 };173 };
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
154 pub const Fn = struct {183 pub const Fn = struct {
155 pub const base_tag = Tag.@"fn";184 pub const base_tag = Tag.@"fn";
156 base: Inst,185 base: Inst,
157186
158 positionals: struct {187 positionals: struct {
159 fn_type: *Inst,188 fn_type: *Inst,
160 body: Body,189 body: Module.Body,
161 },190 },
162 kw_args: struct {},191 kw_args: struct {},
163
164 pub const Body = struct {
165 instructions: []*Inst,
166 };
167 };192 };
168193
169 pub const Export = struct {194 pub const Export = struct {
...@@ -297,6 +322,50 @@ pub const Inst = struct {...@@ -297,6 +322,50 @@ pub const Inst = struct {
297 },322 },
298 kw_args: struct {},323 kw_args: struct {},
299 };324 };
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 };
300};369};
301370
302pub const ErrorMsg = struct {371pub const ErrorMsg = struct {
...@@ -309,6 +378,10 @@ pub const Module = struct {...@@ -309,6 +378,10 @@ pub const Module = struct {
309 errors: []ErrorMsg,378 errors: []ErrorMsg,
310 arena: std.heap.ArenaAllocator,379 arena: std.heap.ArenaAllocator,
311380
381 pub const Body = struct {
382 instructions: []*Inst,
383 };
384
312 pub fn deinit(self: *Module, allocator: *Allocator) void {385 pub fn deinit(self: *Module, allocator: *Allocator) void {
313 allocator.free(self.decls);386 allocator.free(self.decls);
314 allocator.free(self.errors);387 allocator.free(self.errors);
...@@ -321,7 +394,7 @@ pub const Module = struct {...@@ -321,7 +394,7 @@ pub const Module = struct {
321 self.writeToStream(std.heap.page_allocator, std.io.getStdErr().outStream()) catch {};394 self.writeToStream(std.heap.page_allocator, std.io.getStdErr().outStream()) catch {};
322 }395 }
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
326 /// The allocator is used for temporary storage, but this function always returns399 /// The allocator is used for temporary storage, but this function always returns
327 /// with no resources allocated.400 /// with no resources allocated.
...@@ -357,6 +430,7 @@ pub const Module = struct {...@@ -357,6 +430,7 @@ pub const Module = struct {
357 ) @TypeOf(stream).Error!void {430 ) @TypeOf(stream).Error!void {
358 // TODO I tried implementing this with an inline for loop and hit a compiler bug431 // TODO I tried implementing this with an inline for loop and hit a compiler bug
359 switch (decl.tag) {432 switch (decl.tag) {
433 .breakpoint => return self.writeInstToStreamGeneric(stream, .breakpoint, decl, inst_table),
360 .str => return self.writeInstToStreamGeneric(stream, .str, decl, inst_table),434 .str => return self.writeInstToStreamGeneric(stream, .str, decl, inst_table),
361 .int => return self.writeInstToStreamGeneric(stream, .int, decl, inst_table),435 .int => return self.writeInstToStreamGeneric(stream, .int, decl, inst_table),
362 .ptrtoint => return self.writeInstToStreamGeneric(stream, .ptrtoint, decl, inst_table),436 .ptrtoint => return self.writeInstToStreamGeneric(stream, .ptrtoint, decl, inst_table),
...@@ -365,6 +439,7 @@ pub const Module = struct {...@@ -365,6 +439,7 @@ pub const Module = struct {
365 .as => return self.writeInstToStreamGeneric(stream, .as, decl, inst_table),439 .as => return self.writeInstToStreamGeneric(stream, .as, decl, inst_table),
366 .@"asm" => return self.writeInstToStreamGeneric(stream, .@"asm", decl, inst_table),440 .@"asm" => return self.writeInstToStreamGeneric(stream, .@"asm", decl, inst_table),
367 .@"unreachable" => return self.writeInstToStreamGeneric(stream, .@"unreachable", decl, inst_table),441 .@"unreachable" => return self.writeInstToStreamGeneric(stream, .@"unreachable", decl, inst_table),
442 .@"return" => return self.writeInstToStreamGeneric(stream, .@"return", decl, inst_table),
368 .@"fn" => return self.writeInstToStreamGeneric(stream, .@"fn", decl, inst_table),443 .@"fn" => return self.writeInstToStreamGeneric(stream, .@"fn", decl, inst_table),
369 .@"export" => return self.writeInstToStreamGeneric(stream, .@"export", decl, inst_table),444 .@"export" => return self.writeInstToStreamGeneric(stream, .@"export", decl, inst_table),
370 .primitive => return self.writeInstToStreamGeneric(stream, .primitive, decl, inst_table),445 .primitive => return self.writeInstToStreamGeneric(stream, .primitive, decl, inst_table),
...@@ -373,6 +448,10 @@ pub const Module = struct {...@@ -373,6 +448,10 @@ pub const Module = struct {
373 .bitcast => return self.writeInstToStreamGeneric(stream, .bitcast, decl, inst_table),448 .bitcast => return self.writeInstToStreamGeneric(stream, .bitcast, decl, inst_table),
374 .elemptr => return self.writeInstToStreamGeneric(stream, .elemptr, decl, inst_table),449 .elemptr => return self.writeInstToStreamGeneric(stream, .elemptr, decl, inst_table),
375 .add => return self.writeInstToStreamGeneric(stream, .add, decl, inst_table),450 .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),
376 }455 }
377 }456 }
378457
...@@ -432,7 +511,7 @@ pub const Module = struct {...@@ -432,7 +511,7 @@ pub const Module = struct {
432 }511 }
433 try stream.writeByte(']');512 try stream.writeByte(']');
434 },513 },
435 Inst.Fn.Body => {514 Module.Body => {
436 try stream.writeAll("{\n");515 try stream.writeAll("{\n");
437 for (param.instructions) |inst, i| {516 for (param.instructions) |inst, i| {
438 try stream.print(" %{} ", .{i});517 try stream.print(" %{} ", .{i});
...@@ -443,7 +522,7 @@ pub const Module = struct {...@@ -443,7 +522,7 @@ pub const Module = struct {
443 },522 },
444 bool => return stream.writeByte("01"[@boolToInt(param)]),523 bool => return stream.writeByte("01"[@boolToInt(param)]),
445 []u8, []const u8 => return std.zig.renderStringLiteral(param, stream),524 []u8, []const u8 => return std.zig.renderStringLiteral(param, stream),
446 BigInt => return stream.print("{}", .{param}),525 BigIntConst => return stream.print("{}", .{param}),
447 else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)),526 else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)),
448 }527 }
449 }528 }
...@@ -497,7 +576,7 @@ const Parser = struct {...@@ -497,7 +576,7 @@ const Parser = struct {
497 name_map: std.StringHashMap(usize),576 name_map: std.StringHashMap(usize),
498 };577 };
499578
500 fn parseBody(self: *Parser) !Inst.Fn.Body {579 fn parseBody(self: *Parser) !Module.Body {
501 var body_context = Body{580 var body_context = Body{
502 .instructions = std.ArrayList(*Inst).init(self.allocator),581 .instructions = std.ArrayList(*Inst).init(self.allocator),
503 .name_map = std.StringHashMap(usize).init(self.allocator),582 .name_map = std.StringHashMap(usize).init(self.allocator),
...@@ -532,9 +611,10 @@ const Parser = struct {...@@ -532,9 +611,10 @@ const Parser = struct {
532 else => |byte| return self.failByte(byte),611 else => |byte| return self.failByte(byte),
533 };612 };
534613
535 return Inst.Fn.Body{614 // Move the instructions to the arena
536 .instructions = body_context.instructions.toOwnedSlice(),615 const instrs = try self.arena.allocator.alloc(*Inst, body_context.instructions.items.len);
537 };616 mem.copy(*Inst, instrs, body_context.instructions.items);
617 return Module.Body{ .instructions = instrs };
538 }618 }
539619
540 fn parseStringLiteral(self: *Parser) ![]u8 {620 fn parseStringLiteral(self: *Parser) ![]u8 {
...@@ -565,7 +645,7 @@ const Parser = struct {...@@ -565,7 +645,7 @@ const Parser = struct {
565 };645 };
566 }646 }
567647
568 fn parseIntegerLiteral(self: *Parser) !BigInt {648 fn parseIntegerLiteral(self: *Parser) !BigIntConst {
569 const start = self.i;649 const start = self.i;
570 if (self.source[self.i] == '-') self.i += 1;650 if (self.source[self.i] == '-') self.i += 1;
571 while (true) : (self.i += 1) switch (self.source[self.i]) {651 while (true) : (self.i += 1) switch (self.source[self.i]) {
...@@ -573,41 +653,46 @@ const Parser = struct {...@@ -573,41 +653,46 @@ const Parser = struct {
573 else => break,653 else => break,
574 };654 };
575 const number_text = self.source[start..self.i];655 const number_text = self.source[start..self.i];
576 var result = try BigInt.init(&self.arena.allocator);656 const base = 10;
577 result.setString(10, number_text) catch |err| {657 // TODO reuse the same array list for this
578 self.i = start;658 const limbs_buffer_len = std.math.big.int.calcSetStringLimbsBufferLen(base, number_text.len);
579 switch (err) {659 const limbs_buffer = try self.allocator.alloc(std.math.big.Limb, limbs_buffer_len);
580 error.InvalidBase => unreachable,660 defer self.allocator.free(limbs_buffer);
581 error.InvalidCharForDigit => return self.fail("invalid digit in integer literal", .{}),661 const limb_len = std.math.big.int.calcSetStringLimbCount(base, number_text.len);
582 error.DigitTooLargeForBase => return self.fail("digit too large in integer literal", .{}),662 const limbs = try self.arena.allocator.alloc(std.math.big.Limb, limb_len);
583 else => |e| return e,663 var result = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
584 }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 },
585 };669 };
586 return result;670 return result.toConst();
587 }671 }
588672
589 fn parseRoot(self: *Parser) !void {673 fn parseRoot(self: *Parser) !void {
590 // The IR format is designed so that it can be tokenized and parsed at the same time.674 // 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]) {675 while (true) {
592 ';' => _ = try skipToAndOver(self, '\n'),676 switch (self.source[self.i]) {
593 '@' => {677 ';' => _ = try skipToAndOver(self, '\n'),
594 self.i += 1;678 '@' => {
595 const ident = try skipToAndOver(self, ' ');679 self.i += 1;
596 skipSpace(self);680 const ident = try skipToAndOver(self, ' ');
597 try requireEatBytes(self, "=");681 skipSpace(self);
598 skipSpace(self);682 try requireEatBytes(self, "=");
599 const inst = try parseInstruction(self, null);683 skipSpace(self);
600 const ident_index = self.decls.items.len;684 const inst = try parseInstruction(self, null);
601 if (try self.global_name_map.put(ident, ident_index)) |_| {685 const ident_index = self.decls.items.len;
602 return self.fail("redefinition of identifier '{}'", .{ident});686 if (try self.global_name_map.put(ident, ident_index)) |_| {
603 }687 return self.fail("redefinition of identifier '{}'", .{ident});
604 try self.decls.append(inst);688 }
605 continue;689 try self.decls.append(inst);
606 },690 },
607 ' ', '\n' => continue,691 ' ', '\n' => self.i += 1,
608 0 => break,692 0 => break,
609 else => |byte| return self.fail("unexpected byte: '{c}'", .{byte}),693 else => |byte| return self.fail("unexpected byte: '{c}'", .{byte}),
610 };694 }
695 }
611 }696 }
612697
613 fn eatByte(self: *Parser, byte: u8) bool {698 fn eatByte(self: *Parser, byte: u8) bool {
...@@ -752,7 +837,7 @@ const Parser = struct {...@@ -752,7 +837,7 @@ const Parser = struct {
752 };837 };
753 }838 }
754 switch (T) {839 switch (T) {
755 Inst.Fn.Body => return parseBody(self),840 Module.Body => return parseBody(self),
756 bool => {841 bool => {
757 const bool_value = switch (self.source[self.i]) {842 const bool_value = switch (self.source[self.i]) {
758 '0' => false,843 '0' => false,
...@@ -779,7 +864,7 @@ const Parser = struct {...@@ -779,7 +864,7 @@ const Parser = struct {
779 },864 },
780 *Inst => return parseParameterInst(self, body_ctx),865 *Inst => return parseParameterInst(self, body_ctx),
781 []u8, []const u8 => return self.parseStringLiteral(),866 []u8, []const u8 => return self.parseStringLiteral(),
782 BigInt => return self.parseIntegerLiteral(),867 BigIntConst => return self.parseIntegerLiteral(),
783 else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)),868 else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)),
784 }869 }
785 return self.fail("TODO parse parameter {}", .{@typeName(T)});870 return self.fail("TODO parse parameter {}", .{@typeName(T)});
...@@ -878,11 +963,12 @@ const EmitZIR = struct {...@@ -878,11 +963,12 @@ const EmitZIR = struct {
878 }963 }
879964
880 fn emitComptimeIntVal(self: *EmitZIR, src: usize, val: Value) !*Inst {965 fn emitComptimeIntVal(self: *EmitZIR, src: usize, val: Value) !*Inst {
966 const big_int_space = try self.arena.allocator.create(Value.BigIntSpace);
881 const int_inst = try self.arena.allocator.create(Inst.Int);967 const int_inst = try self.arena.allocator.create(Inst.Int);
882 int_inst.* = .{968 int_inst.* = .{
883 .base = .{ .src = src, .tag = Inst.Int.base_tag },969 .base = .{ .src = src, .tag = Inst.Int.base_tag },
884 .positionals = .{970 .positionals = .{
885 .int = try val.toBigInt(&self.arena.allocator),971 .int = val.toBigInt(big_int_space),
886 },972 },
887 .kw_args = .{},973 .kw_args = .{},
888 };974 };
...@@ -937,96 +1023,19 @@ const EmitZIR = struct {...@@ -937,96 +1023,19 @@ const EmitZIR = struct {
937 var instructions = std.ArrayList(*Inst).init(self.allocator);1023 var instructions = std.ArrayList(*Inst).init(self.allocator);
938 defer instructions.deinit();1024 defer instructions.deinit();
9391025
940 for (module_fn.body) |inst| {1026 try self.emitBody(module_fn.body, &inst_table, &instructions);
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 }
10191027
1020 const fn_type = try self.emitType(src, module_fn.fn_type);1028 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
1022 const fn_inst = try self.arena.allocator.create(Inst.Fn);1033 const fn_inst = try self.arena.allocator.create(Inst.Fn);
1023 fn_inst.* = .{1034 fn_inst.* = .{
1024 .base = .{ .src = src, .tag = Inst.Fn.base_tag },1035 .base = .{ .src = src, .tag = Inst.Fn.base_tag },
1025 .positionals = .{1036 .positionals = .{
1026 .fn_type = fn_type,1037 .fn_type = fn_type,
1027 .body = .{1038 .body = .{ .instructions = arena_instrs },
1028 .instructions = instructions.toOwnedSlice(),
1029 },
1030 },1039 },
1031 .kw_args = .{},1040 .kw_args = .{},
1032 };1041 };
...@@ -1037,6 +1046,159 @@ const EmitZIR = struct {...@@ -1037,6 +1046,159 @@ const EmitZIR = struct {
1037 }1046 }
1038 }1047 }
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
1040 fn emitType(self: *EmitZIR, src: usize, ty: Type) Allocator.Error!*Inst {1202 fn emitType(self: *EmitZIR, src: usize, ty: Type) Allocator.Error!*Inst {
1041 switch (ty.tag()) {1203 switch (ty.tag()) {
1042 .isize => return self.emitPrimitiveType(src, .isize),1204 .isize => return self.emitPrimitiveType(src, .isize),
src-self-hosted/link.zig+54-21
...@@ -7,11 +7,6 @@ const fs = std.fs;...@@ -7,11 +7,6 @@ const fs = std.fs;
7const elf = std.elf;7const elf = std.elf;
8const codegen = @import("codegen.zig");8const 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;
15const default_entry_addr = 0x8000000;10const default_entry_addr = 0x8000000;
1611
17pub const ErrorMsg = struct {12pub const ErrorMsg = struct {
...@@ -35,29 +30,29 @@ pub const Result = struct {...@@ -35,29 +30,29 @@ pub const Result = struct {
35/// If incremental linking fails, falls back to truncating the file and rewriting it.30/// If incremental linking fails, falls back to truncating the file and rewriting it.
36/// A malicious file is detected as incremental link failure and does not cause Illegal Behavior.31/// A malicious file is detected as incremental link failure and does not cause Illegal Behavior.
37/// This operation is not atomic.32/// This operation is not atomic.
38pub fn updateExecutableFilePath(33pub fn updateFilePath(
39 allocator: *Allocator,34 allocator: *Allocator,
40 module: ir.Module,35 module: ir.Module,
41 dir: fs.Dir,36 dir: fs.Dir,
42 sub_path: []const u8,37 sub_path: []const u8,
43) !Result {38) !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) });
45 defer file.close();40 defer file.close();
4641
47 return updateExecutableFile(allocator, module, file);42 return updateFile(allocator, module, file);
48}43}
4944
50/// Atomically overwrites the old file, if present.45/// Atomically overwrites the old file, if present.
51pub fn writeExecutableFilePath(46pub fn writeFilePath(
52 allocator: *Allocator,47 allocator: *Allocator,
53 module: ir.Module,48 module: ir.Module,
54 dir: fs.Dir,49 dir: fs.Dir,
55 sub_path: []const u8,50 sub_path: []const u8,
56) !Result {51) !Result {
57 const af = try dir.atomicFile(sub_path, .{ .mode = executable_mode });52 const af = try dir.atomicFile(sub_path, .{ .mode = determineMode(module) });
58 defer af.deinit();53 defer af.deinit();
5954
60 const result = try writeExecutableFile(allocator, module, af.file);55 const result = try writeFile(allocator, module, af.file);
61 try af.finish();56 try af.finish();
62 return result;57 return result;
63}58}
...@@ -67,10 +62,10 @@ pub fn writeExecutableFilePath(...@@ -67,10 +62,10 @@ pub fn writeExecutableFilePath(
67/// Returns an error if `file` is not already open with +read +write +seek abilities.62/// Returns an error if `file` is not already open with +read +write +seek abilities.
68/// A malicious file is detected as incremental link failure and does not cause Illegal Behavior.63/// A malicious file is detected as incremental link failure and does not cause Illegal Behavior.
69/// This operation is not atomic.64/// This operation is not atomic.
70pub fn updateExecutableFile(allocator: *Allocator, module: ir.Module, file: fs.File) !Result {65pub fn updateFile(allocator: *Allocator, module: ir.Module, file: fs.File) !Result {
71 return updateExecutableFileInner(allocator, module, file) catch |err| switch (err) {66 return updateFileInner(allocator, module, file) catch |err| switch (err) {
72 error.IncrFailed => {67 error.IncrFailed => {
73 return writeExecutableFile(allocator, module, file);68 return writeFile(allocator, module, file);
74 },69 },
75 else => |e| return e,70 else => |e| return e,
76 };71 };
...@@ -436,7 +431,7 @@ const Update = struct {...@@ -436,7 +431,7 @@ const Update = struct {
436 },431 },
437 }432 }
438 }433 }
439 if (self.entry_addr == null) {434 if (self.entry_addr == null and self.module.output_mode == .Exe) {
440 const msg = try std.fmt.allocPrint(self.errors.allocator, "no entry point found", .{});435 const msg = try std.fmt.allocPrint(self.errors.allocator, "no entry point found", .{});
441 errdefer self.errors.allocator.free(msg);436 errdefer self.errors.allocator.free(msg);
442 try self.errors.append(.{437 try self.errors.append(.{
...@@ -485,7 +480,15 @@ const Update = struct {...@@ -485,7 +480,15 @@ const Update = struct {
485480
486 assert(index == 16);481 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);
489 index += 2;492 index += 2;
490493
491 const machine = self.module.target.cpu.arch.toElfMachine();494 const machine = self.module.target.cpu.arch.toElfMachine();
...@@ -496,10 +499,11 @@ const Update = struct {...@@ -496,10 +499,11 @@ const Update = struct {
496 mem.writeInt(u32, hdr_buf[index..][0..4], 1, endian);499 mem.writeInt(u32, hdr_buf[index..][0..4], 1, endian);
497 index += 4;500 index += 4;
498501
502 const e_entry = if (elf_type == .REL) 0 else self.entry_addr.?;
503
499 switch (ptr_width) {504 switch (ptr_width) {
500 .p32 => {505 .p32 => {
501 // e_entry506 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, e_entry), endian);
502 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.entry_addr.?), endian);
503 index += 4;507 index += 4;
504508
505 // e_phoff509 // e_phoff
...@@ -512,7 +516,7 @@ const Update = struct {...@@ -512,7 +516,7 @@ const Update = struct {
512 },516 },
513 .p64 => {517 .p64 => {
514 // e_entry518 // 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);
516 index += 8;520 index += 8;
517521
518 // e_phoff522 // e_phoff
...@@ -750,7 +754,20 @@ const Update = struct {...@@ -750,7 +754,20 @@ const Update = struct {
750754
751/// Truncates the existing file contents and overwrites the contents.755/// Truncates the existing file contents and overwrites the contents.
752/// Returns an error if `file` is not already open with +read +write +seek abilities.756/// 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
754 var update = Update{771 var update = Update{
755 .file = file,772 .file = file,
756 .module = &module,773 .module = &module,
...@@ -778,7 +795,7 @@ pub fn writeExecutableFile(allocator: *Allocator, module: ir.Module, file: fs.Fi...@@ -778,7 +795,7 @@ pub fn writeExecutableFile(allocator: *Allocator, module: ir.Module, file: fs.Fi
778}795}
779796
780/// Returns error.IncrFailed if incremental update could not be performed.797/// 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 {
782 //var ehdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;799 //var ehdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
783800
784 // TODO implement incremental linking801 // TODO implement incremental linking
...@@ -822,3 +839,19 @@ fn sectHeaderTo32(shdr: elf.Elf64_Shdr) elf.Elf32_Shdr {...@@ -822,3 +839,19 @@ fn sectHeaderTo32(shdr: elf.Elf64_Shdr) elf.Elf32_Shdr {
822 .sh_entsize = @intCast(u32, shdr.sh_entsize),839 .sh_entsize = @intCast(u32, shdr.sh_entsize),
823 };840 };
824}841}
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 @@...@@ -1,237 +1,248 @@
1const std = @import("std");1const std = @import("std");
2const mem = std.mem;2const link = @import("link.zig");
3const Target = std.Target;3const ir = @import("ir.zig");
4const Compilation = @import("compilation.zig").Compilation;4const Allocator = std.mem.Allocator;
5const introspect = @import("introspect.zig");
6const testing = std.testing;
7const errmsg = @import("errmsg.zig");
8const ZigCompiler = @import("compilation.zig").ZigCompiler;
95
10var ctx: TestContext = undefined;6var global_ctx: TestContext = undefined;
117
12test "stage2" {8test "self-hosted" {
13 // TODO provide a way to run tests in evented I/O mode9 try global_ctx.init();
14 if (!std.io.is_async) return error.SkipZigTest;10 defer global_ctx.deinit();
1511
16 // TODO https://github.com/ziglang/zig/issues/136412 try @import("stage2_tests").addCases(&global_ctx);
17 // TODO https://github.com/ziglang/zig/issues/3117
18 if (true) return error.SkipZigTest;
1913
20 try ctx.init();14 try global_ctx.run();
21 defer ctx.deinit();
22
23 try @import("stage2_tests").addCases(&ctx);
24
25 try ctx.run();
26}15}
2716
28const file1 = "1.zig";
29// TODO https://github.com/ziglang/zig/issues/3783
30const allocator = std.heap.page_allocator;
31
32pub const TestContext = struct {17pub const TestContext = struct {
33 zig_compiler: ZigCompiler,18 zir_cmp_output_cases: std.ArrayList(ZIRCompareOutputCase),
34 zig_lib_dir: []u8,19 zir_transform_cases: std.ArrayList(ZIRTransformCase),
35 file_index: std.atomic.Int(usize),20
36 group: std.event.Group(anyerror!void),21 pub const ZIRCompareOutputCase = struct {
37 any_err: anyerror!void,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
41 fn init(self: *TestContext) !void {59 fn init(self: *TestContext) !void {
42 self.* = TestContext{60 self.* = .{
43 .any_err = {},61 .zir_cmp_output_cases = std.ArrayList(ZIRCompareOutputCase).init(std.heap.page_allocator),
44 .zig_compiler = undefined,62 .zir_transform_cases = std.ArrayList(ZIRTransformCase).init(std.heap.page_allocator),
45 .zig_lib_dir = undefined,
46 .group = undefined,
47 .file_index = std.atomic.Int(usize).init(0),
48 };63 };
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 {};
61 }64 }
6265
63 fn deinit(self: *TestContext) void {66 fn deinit(self: *TestContext) void {
64 std.fs.cwd().deleteTree(tmp_dir_name) catch {};67 self.zir_cmp_output_cases.deinit();
65 allocator.free(self.zig_lib_dir);68 self.zir_transform_cases.deinit();
66 self.zig_compiler.deinit();69 self.* = undefined;
67 }70 }
6871
69 fn run(self: *TestContext) !void {72 fn run(self: *TestContext) !void {
70 std.event.Loop.startCpuBoundOperation();73 var progress = std.Progress{};
71 self.any_err = self.group.wait();74 const root_node = try progress.start("zir", self.zir_cmp_output_cases.items.len +
72 return self.any_err;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 }
73 }90 }
7491
75 fn testCompileError(92 fn runOneZIRCmpOutputCase(
76 self: *TestContext,93 self: *TestContext,
77 source: []const u8,94 allocator: *Allocator,
78 path: []const u8,95 root_node: *std.Progress.Node,
79 line: usize,96 case: ZIRCompareOutputCase,
80 column: usize,97 target: std.Target,
81 msg: []const u8,
82 ) !void {98 ) !void {
83 var file_index_buf: [20]u8 = undefined;99 var tmp = std.testing.tmpDir(.{ .share_with_child_process = true });
84 const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", .{self.file_index.incr()});100 defer tmp.cleanup();
85 const file1_path = try std.fs.path.join(allocator, [_][]const u8{ tmp_dir_name, file_index, file1 });
86101
87 if (std.fs.path.dirname(file1_path)) |dirname| {102 var prg_node = root_node.start(case.name, 4);
88 try std.fs.cwd().makePath(dirname);103 prg_node.activate();
89 }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(111 break :x try ir.text.parse(allocator, case.src);
94 &self.zig_compiler,112 };
95 "test",113 defer zir_module.deinit(allocator);
96 file1_path,114 if (zir_module.errors.len != 0) {
97 .Native,115 debugPrintErrors(case.src, zir_module.errors);
98 .Obj,116 return error.ParseFailure;
99 .Debug,117 }
100 true, // is_static
101 self.zig_lib_dir,
102 );
103 errdefer comp.destroy();
104
105 comp.start();
106118
107 try self.group.call(getModuleEvent, comp, source, path, line, column, msg);119 var analyzed_module = x: {
108 }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(137 var link_result = x: {
111 self: *TestContext,138 var link_node = prg_node.start("link", null);
112 source: []const u8,139 link_node.activate();
113 expected_output: []const u8,140 defer link_node.end();
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 });
118141
119 const output_file = try std.fmt.allocPrint(allocator, "{}-out{}", .{ file1_path, (Target{ .Native = {} }).exeFileExt() });142 break :x try link.updateFilePath(allocator, analyzed_module, tmp.dir, "a.out");
120 if (std.fs.path.dirname(file1_path)) |dirname| {143 };
121 try std.fs.cwd().makePath(dirname);144 defer link_result.deinit(allocator);
145 if (link_result.errors.len != 0) {
146 debugPrintErrors(case.src, link_result.errors);
147 return error.LinkFailure;
122 }148 }
123149
124 try std.fs.cwd().writeFile(file1_path, source);150 var exec_result = x: {
125151 var exec_node = prg_node.start("execute", null);
126 var comp = try Compilation.create(152 exec_node.activate();
127 &self.zig_compiler,153 defer exec_node.end();
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 }
144154
145 async fn getModuleEventSuccess(155 break :x try std.ChildProcess.exec(.{
146 comp: *Compilation,156 .allocator = allocator,
147 exe_file: []const u8,157 .argv = &[_][]const u8{"./a.out"},
148 expected_output: []const u8,158 .cwd_dir = tmp.dir,
149 ) anyerror!void {159 });
150 defer comp.destroy();160 };
151 const build_event = comp.events.get();161 defer allocator.free(exec_result.stdout);
152162 defer allocator.free(exec_result.stderr);
153 switch (build_event) {163 switch (exec_result.term) {
154 .Ok => {164 .Exited => |code| {
155 const argv = [_][]const u8{exe_file};165 if (code != 0) {
156 // TODO use event loop166 std.debug.warn("elf file exited with code {}\n", .{code});
157 const child = try std.ChildProcess.exec(.{167 return error.BinaryBadExitCode;
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);
183 }168 }
184 },169 },
170 else => return error.BinaryCrashed,
185 }171 }
172 std.testing.expectEqualSlices(u8, case.expected_stdout, exec_result.stdout);
186 }173 }
187174
188 async fn getModuleEvent(175 fn runOneZIRTransformCase(
189 comp: *Compilation,176 self: *TestContext,
190 source: []const u8,177 allocator: *Allocator,
191 path: []const u8,178 root_node: *std.Progress.Node,
192 line: usize,179 case: ZIRTransformCase,
193 column: usize,180 target: std.Target,
194 text: []const u8,181 ) !void {
195 ) anyerror!void {182 var prg_node = root_node.start(case.name, 4);
196 defer comp.destroy();183 prg_node.activate();
197 const build_event = comp.events.get();184 defer prg_node.end();
198185
199 switch (build_event) {186 var parse_node = prg_node.start("parse", null);
200 .Ok => {187 parse_node.activate();
201 @panic("build incorrectly succeeded");188 var zir_module = try ir.text.parse(allocator, case.src);
202 },189 defer zir_module.deinit(allocator);
203 .Error => |err| {190 if (zir_module.errors.len != 0) {
204 @panic("build incorrectly failed");191 debugPrintErrors(case.src, zir_module.errors);
205 },192 return error.ParseFailure;
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 },
235 }193 }
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);
236 }225 }
237};226};
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 {...@@ -3913,18 +3913,20 @@ fn transCreateNodeAPInt(c: *Context, int: *const ZigClangAPSInt) !*ast.Node {
3913 };3913 };
3914 var aps_int = int;3914 var aps_int = int;
3915 const is_negative = ZigClangAPSInt_isSigned(int) and ZigClangAPSInt_isNegative(int);3915 const is_negative = ZigClangAPSInt_isSigned(int) and ZigClangAPSInt_isNegative(int);
3916 if (is_negative)3916 if (is_negative) aps_int = ZigClangAPSInt_negate(aps_int);
3917 aps_int = ZigClangAPSInt_negate(aps_int);3917 defer if (is_negative) {
3918 var big = try math.big.Int.initCapacity(c.a(), num_limbs);3918 ZigClangAPSInt_free(aps_int);
3919 if (is_negative)3919 };
3920 big.negate();3920
3921 defer big.deinit();3921 const limbs = try c.a().alloc(math.big.Limb, num_limbs);
3922 defer c.a().free(limbs);
3923
3922 const data = ZigClangAPSInt_getRawData(aps_int);3924 const data = ZigClangAPSInt_getRawData(aps_int);
3923 switch (@sizeOf(std.math.big.Limb)) {3925 switch (@sizeOf(math.big.Limb)) {
3924 8 => {3926 8 => {
3925 var i: usize = 0;3927 var i: usize = 0;
3926 while (i < num_limbs) : (i += 1) {3928 while (i < num_limbs) : (i += 1) {
3927 big.limbs[i] = data[i];3929 limbs[i] = data[i];
3928 }3930 }
3929 },3931 },
3930 4 => {3932 4 => {
...@@ -3934,23 +3936,23 @@ fn transCreateNodeAPInt(c: *Context, int: *const ZigClangAPSInt) !*ast.Node {...@@ -3934,23 +3936,23 @@ fn transCreateNodeAPInt(c: *Context, int: *const ZigClangAPSInt) !*ast.Node {
3934 limb_i += 2;3936 limb_i += 2;
3935 data_i += 1;3937 data_i += 1;
3936 }) {3938 }) {
3937 big.limbs[limb_i] = @truncate(u32, data[data_i]);3939 limbs[limb_i] = @truncate(u32, data[data_i]);
3938 big.limbs[limb_i + 1] = @truncate(u32, data[data_i] >> 32);3940 limbs[limb_i + 1] = @truncate(u32, data[data_i] >> 32);
3939 }3941 }
3940 },3942 },
3941 else => @compileError("unimplemented"),3943 else => @compileError("unimplemented"),
3942 }3944 }
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) {
3944 error.OutOfMemory => return error.OutOfMemory,3948 error.OutOfMemory => return error.OutOfMemory,
3945 else => unreachable,
3946 };3949 };
3950 defer c.a().free(str);
3947 const token = try appendToken(c, .IntegerLiteral, str);3951 const token = try appendToken(c, .IntegerLiteral, str);
3948 const node = try c.a().create(ast.Node.IntegerLiteral);3952 const node = try c.a().create(ast.Node.IntegerLiteral);
3949 node.* = .{3953 node.* = .{
3950 .token = token,3954 .token = token,
3951 };3955 };
3952 if (is_negative)
3953 ZigClangAPSInt_free(aps_int);
3954 return &node.base;3956 return &node.base;
3955}3957}
39563958
src-self-hosted/type.zig+473-204
...@@ -20,37 +20,40 @@ pub const Type = extern union {...@@ -20,37 +20,40 @@ pub const Type = extern union {
2020
21 pub fn zigTypeTag(self: Type) std.builtin.TypeId {21 pub fn zigTypeTag(self: Type) std.builtin.TypeId {
22 switch (self.tag()) {22 switch (self.tag()) {
23 .@"u8",23 .u8,
24 .@"i8",24 .i8,
25 .@"isize",25 .isize,
26 .@"usize",26 .usize,
27 .@"c_short",27 .c_short,
28 .@"c_ushort",28 .c_ushort,
29 .@"c_int",29 .c_int,
30 .@"c_uint",30 .c_uint,
31 .@"c_long",31 .c_long,
32 .@"c_ulong",32 .c_ulong,
33 .@"c_longlong",33 .c_longlong,
34 .@"c_ulonglong",34 .c_ulonglong,
35 .@"c_longdouble",35 .c_longdouble,
36 .int_signed,
37 .int_unsigned,
36 => return .Int,38 => return .Int,
3739
38 .@"f16",40 .f16,
39 .@"f32",41 .f32,
40 .@"f64",42 .f64,
41 .@"f128",43 .f128,
42 => return .Float,44 => return .Float,
4345
44 .@"c_void" => return .Opaque,46 .c_void => return .Opaque,
45 .@"bool" => return .Bool,47 .bool => return .Bool,
46 .@"void" => return .Void,48 .void => return .Void,
47 .@"type" => return .Type,49 .type => return .Type,
48 .@"anyerror" => return .ErrorSet,50 .anyerror => return .ErrorSet,
49 .@"comptime_int" => return .ComptimeInt,51 .comptime_int => return .ComptimeInt,
50 .@"comptime_float" => return .ComptimeFloat,52 .comptime_float => return .ComptimeFloat,
51 .@"noreturn" => return .NoReturn,53 .noreturn => return .NoReturn,
5254
53 .fn_naked_noreturn_no_args => return .Fn,55 .fn_naked_noreturn_no_args => return .Fn,
56 .fn_ccc_void_no_args => return .Fn,
5457
55 .array, .array_u8_sentinel_0 => return .Array,58 .array, .array_u8_sentinel_0 => return .Array,
56 .single_const_pointer => return .Pointer,59 .single_const_pointer => return .Pointer,
...@@ -153,35 +156,36 @@ pub const Type = extern union {...@@ -153,35 +156,36 @@ pub const Type = extern union {
153 while (true) {156 while (true) {
154 const t = ty.tag();157 const t = ty.tag();
155 switch (t) {158 switch (t) {
156 .@"u8",159 .u8,
157 .@"i8",160 .i8,
158 .@"isize",161 .isize,
159 .@"usize",162 .usize,
160 .@"c_short",163 .c_short,
161 .@"c_ushort",164 .c_ushort,
162 .@"c_int",165 .c_int,
163 .@"c_uint",166 .c_uint,
164 .@"c_long",167 .c_long,
165 .@"c_ulong",168 .c_ulong,
166 .@"c_longlong",169 .c_longlong,
167 .@"c_ulonglong",170 .c_ulonglong,
168 .@"c_longdouble",171 .c_longdouble,
169 .@"c_void",172 .c_void,
170 .@"f16",173 .f16,
171 .@"f32",174 .f32,
172 .@"f64",175 .f64,
173 .@"f128",176 .f128,
174 .@"bool",177 .bool,
175 .@"void",178 .void,
176 .@"type",179 .type,
177 .@"anyerror",180 .anyerror,
178 .@"comptime_int",181 .comptime_int,
179 .@"comptime_float",182 .comptime_float,
180 .@"noreturn",183 .noreturn,
181 => return out_stream.writeAll(@tagName(t)),184 => return out_stream.writeAll(@tagName(t)),
182185
183 .const_slice_u8 => return out_stream.writeAll("[]const u8"),186 .const_slice_u8 => return out_stream.writeAll("[]const u8"),
184 .fn_naked_noreturn_no_args => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),187 .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"),
185 .single_const_pointer_to_comptime_int => return out_stream.writeAll("*const comptime_int"),189 .single_const_pointer_to_comptime_int => return out_stream.writeAll("*const comptime_int"),
186190
187 .array_u8_sentinel_0 => {191 .array_u8_sentinel_0 => {
...@@ -200,6 +204,14 @@ pub const Type = extern union {...@@ -200,6 +204,14 @@ pub const Type = extern union {
200 ty = payload.pointee_type;204 ty = payload.pointee_type;
201 continue;205 continue;
202 },206 },
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 },
203 }215 }
204 unreachable;216 unreachable;
205 }217 }
...@@ -207,32 +219,33 @@ pub const Type = extern union {...@@ -207,32 +219,33 @@ pub const Type = extern union {
207219
208 pub fn toValue(self: Type, allocator: *Allocator) Allocator.Error!Value {220 pub fn toValue(self: Type, allocator: *Allocator) Allocator.Error!Value {
209 switch (self.tag()) {221 switch (self.tag()) {
210 .@"u8" => return Value.initTag(.u8_type),222 .u8 => return Value.initTag(.u8_type),
211 .@"i8" => return Value.initTag(.i8_type),223 .i8 => return Value.initTag(.i8_type),
212 .@"isize" => return Value.initTag(.isize_type),224 .isize => return Value.initTag(.isize_type),
213 .@"usize" => return Value.initTag(.usize_type),225 .usize => return Value.initTag(.usize_type),
214 .@"c_short" => return Value.initTag(.c_short_type),226 .c_short => return Value.initTag(.c_short_type),
215 .@"c_ushort" => return Value.initTag(.c_ushort_type),227 .c_ushort => return Value.initTag(.c_ushort_type),
216 .@"c_int" => return Value.initTag(.c_int_type),228 .c_int => return Value.initTag(.c_int_type),
217 .@"c_uint" => return Value.initTag(.c_uint_type),229 .c_uint => return Value.initTag(.c_uint_type),
218 .@"c_long" => return Value.initTag(.c_long_type),230 .c_long => return Value.initTag(.c_long_type),
219 .@"c_ulong" => return Value.initTag(.c_ulong_type),231 .c_ulong => return Value.initTag(.c_ulong_type),
220 .@"c_longlong" => return Value.initTag(.c_longlong_type),232 .c_longlong => return Value.initTag(.c_longlong_type),
221 .@"c_ulonglong" => return Value.initTag(.c_ulonglong_type),233 .c_ulonglong => return Value.initTag(.c_ulonglong_type),
222 .@"c_longdouble" => return Value.initTag(.c_longdouble_type),234 .c_longdouble => return Value.initTag(.c_longdouble_type),
223 .@"c_void" => return Value.initTag(.c_void_type),235 .c_void => return Value.initTag(.c_void_type),
224 .@"f16" => return Value.initTag(.f16_type),236 .f16 => return Value.initTag(.f16_type),
225 .@"f32" => return Value.initTag(.f32_type),237 .f32 => return Value.initTag(.f32_type),
226 .@"f64" => return Value.initTag(.f64_type),238 .f64 => return Value.initTag(.f64_type),
227 .@"f128" => return Value.initTag(.f128_type),239 .f128 => return Value.initTag(.f128_type),
228 .@"bool" => return Value.initTag(.bool_type),240 .bool => return Value.initTag(.bool_type),
229 .@"void" => return Value.initTag(.void_type),241 .void => return Value.initTag(.void_type),
230 .@"type" => return Value.initTag(.type_type),242 .type => return Value.initTag(.type_type),
231 .@"anyerror" => return Value.initTag(.anyerror_type),243 .anyerror => return Value.initTag(.anyerror_type),
232 .@"comptime_int" => return Value.initTag(.comptime_int_type),244 .comptime_int => return Value.initTag(.comptime_int_type),
233 .@"comptime_float" => return Value.initTag(.comptime_float_type),245 .comptime_float => return Value.initTag(.comptime_float_type),
234 .@"noreturn" => return Value.initTag(.noreturn_type),246 .noreturn => return Value.initTag(.noreturn_type),
235 .fn_naked_noreturn_no_args => return Value.initTag(.fn_naked_noreturn_no_args_type),247 .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),
236 .single_const_pointer_to_comptime_int => return Value.initTag(.single_const_pointer_to_comptime_int_type),249 .single_const_pointer_to_comptime_int => return Value.initTag(.single_const_pointer_to_comptime_int_type),
237 .const_slice_u8 => return Value.initTag(.const_slice_u8_type),250 .const_slice_u8 => return Value.initTag(.const_slice_u8_type),
238 else => {251 else => {
...@@ -245,35 +258,38 @@ pub const Type = extern union {...@@ -245,35 +258,38 @@ pub const Type = extern union {
245258
246 pub fn isSinglePointer(self: Type) bool {259 pub fn isSinglePointer(self: Type) bool {
247 return switch (self.tag()) {260 return switch (self.tag()) {
248 .@"u8",261 .u8,
249 .@"i8",262 .i8,
250 .@"isize",263 .isize,
251 .@"usize",264 .usize,
252 .@"c_short",265 .c_short,
253 .@"c_ushort",266 .c_ushort,
254 .@"c_int",267 .c_int,
255 .@"c_uint",268 .c_uint,
256 .@"c_long",269 .c_long,
257 .@"c_ulong",270 .c_ulong,
258 .@"c_longlong",271 .c_longlong,
259 .@"c_ulonglong",272 .c_ulonglong,
260 .@"c_longdouble",273 .c_longdouble,
261 .@"f16",274 .f16,
262 .@"f32",275 .f32,
263 .@"f64",276 .f64,
264 .@"f128",277 .f128,
265 .@"c_void",278 .c_void,
266 .@"bool",279 .bool,
267 .@"void",280 .void,
268 .@"type",281 .type,
269 .@"anyerror",282 .anyerror,
270 .@"comptime_int",283 .comptime_int,
271 .@"comptime_float",284 .comptime_float,
272 .@"noreturn",285 .noreturn,
273 .array,286 .array,
274 .array_u8_sentinel_0,287 .array_u8_sentinel_0,
275 .const_slice_u8,288 .const_slice_u8,
276 .fn_naked_noreturn_no_args,289 .fn_naked_noreturn_no_args,
290 .fn_ccc_void_no_args,
291 .int_unsigned,
292 .int_signed,
277 => false,293 => false,
278294
279 .single_const_pointer,295 .single_const_pointer,
...@@ -284,36 +300,39 @@ pub const Type = extern union {...@@ -284,36 +300,39 @@ pub const Type = extern union {
284300
285 pub fn isSlice(self: Type) bool {301 pub fn isSlice(self: Type) bool {
286 return switch (self.tag()) {302 return switch (self.tag()) {
287 .@"u8",303 .u8,
288 .@"i8",304 .i8,
289 .@"isize",305 .isize,
290 .@"usize",306 .usize,
291 .@"c_short",307 .c_short,
292 .@"c_ushort",308 .c_ushort,
293 .@"c_int",309 .c_int,
294 .@"c_uint",310 .c_uint,
295 .@"c_long",311 .c_long,
296 .@"c_ulong",312 .c_ulong,
297 .@"c_longlong",313 .c_longlong,
298 .@"c_ulonglong",314 .c_ulonglong,
299 .@"c_longdouble",315 .c_longdouble,
300 .@"f16",316 .f16,
301 .@"f32",317 .f32,
302 .@"f64",318 .f64,
303 .@"f128",319 .f128,
304 .@"c_void",320 .c_void,
305 .@"bool",321 .bool,
306 .@"void",322 .void,
307 .@"type",323 .type,
308 .@"anyerror",324 .anyerror,
309 .@"comptime_int",325 .comptime_int,
310 .@"comptime_float",326 .comptime_float,
311 .@"noreturn",327 .noreturn,
312 .array,328 .array,
313 .array_u8_sentinel_0,329 .array_u8_sentinel_0,
314 .single_const_pointer,330 .single_const_pointer,
315 .single_const_pointer_to_comptime_int,331 .single_const_pointer_to_comptime_int,
316 .fn_naked_noreturn_no_args,332 .fn_naked_noreturn_no_args,
333 .fn_ccc_void_no_args,
334 .int_unsigned,
335 .int_signed,
317 => false,336 => false,
318337
319 .const_slice_u8 => true,338 .const_slice_u8 => true,
...@@ -323,34 +342,37 @@ pub const Type = extern union {...@@ -323,34 +342,37 @@ pub const Type = extern union {
323 /// Asserts the type is a pointer type.342 /// Asserts the type is a pointer type.
324 pub fn pointerIsConst(self: Type) bool {343 pub fn pointerIsConst(self: Type) bool {
325 return switch (self.tag()) {344 return switch (self.tag()) {
326 .@"u8",345 .u8,
327 .@"i8",346 .i8,
328 .@"isize",347 .isize,
329 .@"usize",348 .usize,
330 .@"c_short",349 .c_short,
331 .@"c_ushort",350 .c_ushort,
332 .@"c_int",351 .c_int,
333 .@"c_uint",352 .c_uint,
334 .@"c_long",353 .c_long,
335 .@"c_ulong",354 .c_ulong,
336 .@"c_longlong",355 .c_longlong,
337 .@"c_ulonglong",356 .c_ulonglong,
338 .@"c_longdouble",357 .c_longdouble,
339 .@"f16",358 .f16,
340 .@"f32",359 .f32,
341 .@"f64",360 .f64,
342 .@"f128",361 .f128,
343 .@"c_void",362 .c_void,
344 .@"bool",363 .bool,
345 .@"void",364 .void,
346 .@"type",365 .type,
347 .@"anyerror",366 .anyerror,
348 .@"comptime_int",367 .comptime_int,
349 .@"comptime_float",368 .comptime_float,
350 .@"noreturn",369 .noreturn,
351 .array,370 .array,
352 .array_u8_sentinel_0,371 .array_u8_sentinel_0,
353 .fn_naked_noreturn_no_args,372 .fn_naked_noreturn_no_args,
373 .fn_ccc_void_no_args,
374 .int_unsigned,
375 .int_signed,
354 => unreachable,376 => unreachable,
355377
356 .single_const_pointer,378 .single_const_pointer,
...@@ -363,32 +385,35 @@ pub const Type = extern union {...@@ -363,32 +385,35 @@ pub const Type = extern union {
363 /// Asserts the type is a pointer or array type.385 /// Asserts the type is a pointer or array type.
364 pub fn elemType(self: Type) Type {386 pub fn elemType(self: Type) Type {
365 return switch (self.tag()) {387 return switch (self.tag()) {
366 .@"u8",388 .u8,
367 .@"i8",389 .i8,
368 .@"isize",390 .isize,
369 .@"usize",391 .usize,
370 .@"c_short",392 .c_short,
371 .@"c_ushort",393 .c_ushort,
372 .@"c_int",394 .c_int,
373 .@"c_uint",395 .c_uint,
374 .@"c_long",396 .c_long,
375 .@"c_ulong",397 .c_ulong,
376 .@"c_longlong",398 .c_longlong,
377 .@"c_ulonglong",399 .c_ulonglong,
378 .@"c_longdouble",400 .c_longdouble,
379 .@"f16",401 .f16,
380 .@"f32",402 .f32,
381 .@"f64",403 .f64,
382 .@"f128",404 .f128,
383 .@"c_void",405 .c_void,
384 .@"bool",406 .bool,
385 .@"void",407 .void,
386 .@"type",408 .type,
387 .@"anyerror",409 .anyerror,
388 .@"comptime_int",410 .comptime_int,
389 .@"comptime_float",411 .comptime_float,
390 .@"noreturn",412 .noreturn,
391 .fn_naked_noreturn_no_args,413 .fn_naked_noreturn_no_args,
414 .fn_ccc_void_no_args,
415 .int_unsigned,
416 .int_signed,
392 => unreachable,417 => unreachable,
393418
394 .array => self.cast(Payload.Array).?.elem_type,419 .array => self.cast(Payload.Array).?.elem_type,
...@@ -398,7 +423,7 @@ pub const Type = extern union {...@@ -398,7 +423,7 @@ pub const Type = extern union {
398 };423 };
399 }424 }
400425
401 /// Asserts the type is an array.426 /// Asserts the type is an array or vector.
402 pub fn arrayLen(self: Type) u64 {427 pub fn arrayLen(self: Type) u64 {
403 return switch (self.tag()) {428 return switch (self.tag()) {
404 .u8,429 .u8,
...@@ -427,9 +452,12 @@ pub const Type = extern union {...@@ -427,9 +452,12 @@ pub const Type = extern union {
427 .comptime_float,452 .comptime_float,
428 .noreturn,453 .noreturn,
429 .fn_naked_noreturn_no_args,454 .fn_naked_noreturn_no_args,
455 .fn_ccc_void_no_args,
430 .single_const_pointer,456 .single_const_pointer,
431 .single_const_pointer_to_comptime_int,457 .single_const_pointer_to_comptime_int,
432 .const_slice_u8,458 .const_slice_u8,
459 .int_unsigned,
460 .int_signed,
433 => unreachable,461 => unreachable,
434462
435 .array => self.cast(Payload.Array).?.len,463 .array => self.cast(Payload.Array).?.len,
...@@ -437,23 +465,67 @@ pub const Type = extern union {...@@ -437,23 +465,67 @@ pub const Type = extern union {
437 };465 };
438 }466 }
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
440 /// Asserts the type is a fixed-width integer.511 /// Asserts the type is a fixed-width integer.
441 pub fn intInfo(self: Type, target: Target) struct { signed: bool, bits: u16 } {512 pub fn intInfo(self: Type, target: Target) struct { signed: bool, bits: u16 } {
442 return switch (self.tag()) {513 return switch (self.tag()) {
443 .@"f16",514 .f16,
444 .@"f32",515 .f32,
445 .@"f64",516 .f64,
446 .@"f128",517 .f128,
447 .@"c_longdouble",518 .c_longdouble,
448 .@"c_void",519 .c_void,
449 .@"bool",520 .bool,
450 .@"void",521 .void,
451 .@"type",522 .type,
452 .@"anyerror",523 .anyerror,
453 .@"comptime_int",524 .comptime_int,
454 .@"comptime_float",525 .comptime_float,
455 .@"noreturn",526 .noreturn,
456 .fn_naked_noreturn_no_args,527 .fn_naked_noreturn_no_args,
528 .fn_ccc_void_no_args,
457 .array,529 .array,
458 .single_const_pointer,530 .single_const_pointer,
459 .single_const_pointer_to_comptime_int,531 .single_const_pointer_to_comptime_int,
...@@ -461,18 +533,46 @@ pub const Type = extern union {...@@ -461,18 +533,46 @@ pub const Type = extern union {
461 .const_slice_u8,533 .const_slice_u8,
462 => unreachable,534 => unreachable,
463535
464 .@"u8" => .{ .signed = false, .bits = 8 },536 .int_unsigned => .{ .signed = false, .bits = self.cast(Payload.IntUnsigned).?.bits },
465 .@"i8" => .{ .signed = true, .bits = 8 },537 .int_signed => .{ .signed = true, .bits = self.cast(Payload.IntSigned).?.bits },
466 .@"usize" => .{ .signed = false, .bits = target.cpu.arch.ptrBitWidth() },538 .u8 => .{ .signed = false, .bits = 8 },
467 .@"isize" => .{ .signed = true, .bits = target.cpu.arch.ptrBitWidth() },539 .i8 => .{ .signed = true, .bits = 8 },
468 .@"c_short" => .{ .signed = true, .bits = CInteger.short.sizeInBits(target) },540 .usize => .{ .signed = false, .bits = target.cpu.arch.ptrBitWidth() },
469 .@"c_ushort" => .{ .signed = false, .bits = CInteger.ushort.sizeInBits(target) },541 .isize => .{ .signed = true, .bits = target.cpu.arch.ptrBitWidth() },
470 .@"c_int" => .{ .signed = true, .bits = CInteger.int.sizeInBits(target) },542 .c_short => .{ .signed = true, .bits = CType.short.sizeInBits(target) },
471 .@"c_uint" => .{ .signed = false, .bits = CInteger.uint.sizeInBits(target) },543 .c_ushort => .{ .signed = false, .bits = CType.ushort.sizeInBits(target) },
472 .@"c_long" => .{ .signed = true, .bits = CInteger.long.sizeInBits(target) },544 .c_int => .{ .signed = true, .bits = CType.int.sizeInBits(target) },
473 .@"c_ulong" => .{ .signed = false, .bits = CInteger.ulong.sizeInBits(target) },545 .c_uint => .{ .signed = false, .bits = CType.uint.sizeInBits(target) },
474 .@"c_longlong" => .{ .signed = true, .bits = CInteger.longlong.sizeInBits(target) },546 .c_long => .{ .signed = true, .bits = CType.long.sizeInBits(target) },
475 .@"c_ulonglong" => .{ .signed = false, .bits = CInteger.ulonglong.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,
476 };576 };
477 }577 }
478578
...@@ -480,6 +580,7 @@ pub const Type = extern union {...@@ -480,6 +580,7 @@ pub const Type = extern union {
480 pub fn fnParamLen(self: Type) usize {580 pub fn fnParamLen(self: Type) usize {
481 return switch (self.tag()) {581 return switch (self.tag()) {
482 .fn_naked_noreturn_no_args => 0,582 .fn_naked_noreturn_no_args => 0,
583 .fn_ccc_void_no_args => 0,
483584
484 .f16,585 .f16,
485 .f32,586 .f32,
...@@ -511,6 +612,8 @@ pub const Type = extern union {...@@ -511,6 +612,8 @@ pub const Type = extern union {
511 .c_ulong,612 .c_ulong,
512 .c_longlong,613 .c_longlong,
513 .c_ulonglong,614 .c_ulonglong,
615 .int_unsigned,
616 .int_signed,
514 => unreachable,617 => unreachable,
515 };618 };
516 }619 }
...@@ -520,6 +623,7 @@ pub const Type = extern union {...@@ -520,6 +623,7 @@ pub const Type = extern union {
520 pub fn fnParamTypes(self: Type, types: []Type) void {623 pub fn fnParamTypes(self: Type, types: []Type) void {
521 switch (self.tag()) {624 switch (self.tag()) {
522 .fn_naked_noreturn_no_args => return,625 .fn_naked_noreturn_no_args => return,
626 .fn_ccc_void_no_args => return,
523627
524 .f16,628 .f16,
525 .f32,629 .f32,
...@@ -551,6 +655,8 @@ pub const Type = extern union {...@@ -551,6 +655,8 @@ pub const Type = extern union {
551 .c_ulong,655 .c_ulong,
552 .c_longlong,656 .c_longlong,
553 .c_ulonglong,657 .c_ulonglong,
658 .int_unsigned,
659 .int_signed,
554 => unreachable,660 => unreachable,
555 }661 }
556 }662 }
...@@ -559,6 +665,7 @@ pub const Type = extern union {...@@ -559,6 +665,7 @@ pub const Type = extern union {
559 pub fn fnReturnType(self: Type) Type {665 pub fn fnReturnType(self: Type) Type {
560 return switch (self.tag()) {666 return switch (self.tag()) {
561 .fn_naked_noreturn_no_args => Type.initTag(.noreturn),667 .fn_naked_noreturn_no_args => Type.initTag(.noreturn),
668 .fn_ccc_void_no_args => Type.initTag(.void),
562669
563 .f16,670 .f16,
564 .f32,671 .f32,
...@@ -590,6 +697,8 @@ pub const Type = extern union {...@@ -590,6 +697,8 @@ pub const Type = extern union {
590 .c_ulong,697 .c_ulong,
591 .c_longlong,698 .c_longlong,
592 .c_ulonglong,699 .c_ulonglong,
700 .int_unsigned,
701 .int_signed,
593 => unreachable,702 => unreachable,
594 };703 };
595 }704 }
...@@ -598,6 +707,7 @@ pub const Type = extern union {...@@ -598,6 +707,7 @@ pub const Type = extern union {
598 pub fn fnCallingConvention(self: Type) std.builtin.CallingConvention {707 pub fn fnCallingConvention(self: Type) std.builtin.CallingConvention {
599 return switch (self.tag()) {708 return switch (self.tag()) {
600 .fn_naked_noreturn_no_args => .Naked,709 .fn_naked_noreturn_no_args => .Naked,
710 .fn_ccc_void_no_args => .C,
601711
602 .f16,712 .f16,
603 .f32,713 .f32,
...@@ -629,10 +739,148 @@ pub const Type = extern union {...@@ -629,10 +739,148 @@ pub const Type = extern union {
629 .c_ulong,739 .c_ulong,
630 .c_longlong,740 .c_longlong,
631 .c_ulonglong,741 .c_ulonglong,
742 .int_unsigned,
743 .int_signed,
632 => unreachable,744 => unreachable,
633 };745 };
634 }746 }
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
636 /// This enum does not directly correspond to `std.builtin.TypeId` because884 /// This enum does not directly correspond to `std.builtin.TypeId` because
637 /// it has extra enum tags in it, as a way of using less memory. For example,885 /// it has extra enum tags in it, as a way of using less memory. For example,
638 /// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types886 /// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types
...@@ -667,6 +915,7 @@ pub const Type = extern union {...@@ -667,6 +915,7 @@ pub const Type = extern union {
667 comptime_float,915 comptime_float,
668 noreturn,916 noreturn,
669 fn_naked_noreturn_no_args,917 fn_naked_noreturn_no_args,
918 fn_ccc_void_no_args,
670 single_const_pointer_to_comptime_int,919 single_const_pointer_to_comptime_int,
671 const_slice_u8, // See last_no_payload_tag below.920 const_slice_u8, // See last_no_payload_tag below.
672 // After this, the tag requires a payload.921 // After this, the tag requires a payload.
...@@ -674,6 +923,8 @@ pub const Type = extern union {...@@ -674,6 +923,8 @@ pub const Type = extern union {
674 array_u8_sentinel_0,923 array_u8_sentinel_0,
675 array,924 array,
676 single_const_pointer,925 single_const_pointer,
926 int_signed,
927 int_unsigned,
677928
678 pub const last_no_payload_tag = Tag.const_slice_u8;929 pub const last_no_payload_tag = Tag.const_slice_u8;
679 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;930 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
...@@ -700,10 +951,22 @@ pub const Type = extern union {...@@ -700,10 +951,22 @@ pub const Type = extern union {
700951
701 pointee_type: Type,952 pointee_type: Type,
702 };953 };
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 };
703 };966 };
704};967};
705968
706pub const CInteger = enum {969pub const CType = enum {
707 short,970 short,
708 ushort,971 ushort,
709 int,972 int,
...@@ -712,8 +975,9 @@ pub const CInteger = enum {...@@ -712,8 +975,9 @@ pub const CInteger = enum {
712 ulong,975 ulong,
713 longlong,976 longlong,
714 ulonglong,977 ulonglong,
978 longdouble,
715979
716 pub fn sizeInBits(self: CInteger, target: Target) u16 {980 pub fn sizeInBits(self: CType, target: Target) u16 {
717 const arch = target.cpu.arch;981 const arch = target.cpu.arch;
718 switch (target.os.tag) {982 switch (target.os.tag) {
719 .freestanding, .other => switch (target.cpu.arch) {983 .freestanding, .other => switch (target.cpu.arch) {
...@@ -729,6 +993,7 @@ pub const CInteger = enum {...@@ -729,6 +993,7 @@ pub const CInteger = enum {
729 .longlong,993 .longlong,
730 .ulonglong,994 .ulonglong,
731 => return 64,995 => return 64,
996 .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"),
732 },997 },
733 else => switch (self) {998 else => switch (self) {
734 .short,999 .short,
...@@ -743,6 +1008,7 @@ pub const CInteger = enum {...@@ -743,6 +1008,7 @@ pub const CInteger = enum {
743 .longlong,1008 .longlong,
744 .ulonglong,1009 .ulonglong,
745 => return 64,1010 => return 64,
1011 .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"),
746 },1012 },
747 },1013 },
7481014
...@@ -767,6 +1033,7 @@ pub const CInteger = enum {...@@ -767,6 +1033,7 @@ pub const CInteger = enum {
767 .longlong,1033 .longlong,
768 .ulonglong,1034 .ulonglong,
769 => return 64,1035 => return 64,
1036 .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"),
770 },1037 },
7711038
772 .windows, .uefi => switch (self) {1039 .windows, .uefi => switch (self) {
...@@ -781,6 +1048,7 @@ pub const CInteger = enum {...@@ -781,6 +1048,7 @@ pub const CInteger = enum {
781 .longlong,1048 .longlong,
782 .ulonglong,1049 .ulonglong,
783 => return 64,1050 => return 64,
1051 .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"),
784 },1052 },
7851053
786 .ios => switch (self) {1054 .ios => switch (self) {
...@@ -795,6 +1063,7 @@ pub const CInteger = enum {...@@ -795,6 +1063,7 @@ pub const CInteger = enum {
795 .longlong,1063 .longlong,
796 .ulonglong,1064 .ulonglong,
797 => return 64,1065 => return 64,
1066 .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"),
798 },1067 },
7991068
800 .ananas,1069 .ananas,
...@@ -821,7 +1090,7 @@ pub const CInteger = enum {...@@ -821,7 +1090,7 @@ pub const CInteger = enum {
821 .amdpal,1090 .amdpal,
822 .hermit,1091 .hermit,
823 .hurd,1092 .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"),
825 }1094 }
826 }1095 }
827};1096};
src-self-hosted/value.zig+377-39
...@@ -2,7 +2,8 @@ const std = @import("std");...@@ -2,7 +2,8 @@ const std = @import("std");
2const Type = @import("type.zig").Type;2const Type = @import("type.zig").Type;
3const log2 = std.math.log2;3const log2 = std.math.log2;
4const assert = std.debug.assert;4const assert = std.debug.assert;
5const BigInt = std.math.big.Int;5const BigIntConst = std.math.big.int.Const;
6const BigIntMutable = std.math.big.int.Mutable;
6const Target = std.Target;7const Target = std.Target;
7const Allocator = std.mem.Allocator;8const Allocator = std.mem.Allocator;
89
...@@ -45,12 +46,14 @@ pub const Value = extern union {...@@ -45,12 +46,14 @@ pub const Value = extern union {
45 comptime_float_type,46 comptime_float_type,
46 noreturn_type,47 noreturn_type,
47 fn_naked_noreturn_no_args_type,48 fn_naked_noreturn_no_args_type,
49 fn_ccc_void_no_args_type,
48 single_const_pointer_to_comptime_int_type,50 single_const_pointer_to_comptime_int_type,
49 const_slice_u8_type,51 const_slice_u8_type,
5052
53 undef,
51 zero,54 zero,
52 void_value,55 the_one_possible_value, // when the type only has one possible value
53 noreturn_value,56 null_value,
54 bool_true,57 bool_true,
55 bool_false, // See last_no_payload_tag below.58 bool_false, // See last_no_payload_tag below.
56 // After this, the tag requires a payload.59 // After this, the tag requires a payload.
...@@ -58,11 +61,13 @@ pub const Value = extern union {...@@ -58,11 +61,13 @@ pub const Value = extern union {
58 ty,61 ty,
59 int_u64,62 int_u64,
60 int_i64,63 int_i64,
61 int_big,64 int_big_positive,
65 int_big_negative,
62 function,66 function,
63 ref,67 ref,
64 ref_val,68 ref_val,
65 bytes,69 bytes,
70 repeated, // the value is a value repeated some number of times
6671
67 pub const last_no_payload_tag = Tag.bool_false;72 pub const last_no_payload_tag = Tag.bool_false;
68 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;73 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
...@@ -132,18 +137,21 @@ pub const Value = extern union {...@@ -132,18 +137,21 @@ pub const Value = extern union {
132 .comptime_float_type => return out_stream.writeAll("comptime_float"),137 .comptime_float_type => return out_stream.writeAll("comptime_float"),
133 .noreturn_type => return out_stream.writeAll("noreturn"),138 .noreturn_type => return out_stream.writeAll("noreturn"),
134 .fn_naked_noreturn_no_args_type => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),139 .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"),
135 .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"),141 .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"),
136 .const_slice_u8_type => return out_stream.writeAll("[]const u8"),142 .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"),
138 .zero => return out_stream.writeAll("0"),146 .zero => return out_stream.writeAll("0"),
139 .void_value => return out_stream.writeAll("{}"),147 .the_one_possible_value => return out_stream.writeAll("(one possible value)"),
140 .noreturn_value => return out_stream.writeAll("unreachable"),
141 .bool_true => return out_stream.writeAll("true"),148 .bool_true => return out_stream.writeAll("true"),
142 .bool_false => return out_stream.writeAll("false"),149 .bool_false => return out_stream.writeAll("false"),
143 .ty => return val.cast(Payload.Ty).?.ty.format("", options, out_stream),150 .ty => return val.cast(Payload.Ty).?.ty.format("", options, out_stream),
144 .int_u64 => return std.fmt.formatIntValue(val.cast(Payload.Int_u64).?.int, "", options, out_stream),151 .int_u64 => return std.fmt.formatIntValue(val.cast(Payload.Int_u64).?.int, "", options, out_stream),
145 .int_i64 => return std.fmt.formatIntValue(val.cast(Payload.Int_i64).?.int, "", options, out_stream),152 .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()}),
147 .function => return out_stream.writeAll("(function)"),155 .function => return out_stream.writeAll("(function)"),
148 .ref => return out_stream.writeAll("(ref)"),156 .ref => return out_stream.writeAll("(ref)"),
149 .ref_val => {157 .ref_val => {
...@@ -152,6 +160,10 @@ pub const Value = extern union {...@@ -152,6 +160,10 @@ pub const Value = extern union {
152 continue;160 continue;
153 },161 },
154 .bytes => return std.zig.renderStringLiteral(self.cast(Payload.Bytes).?.data, out_stream),162 .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 },
155 };167 };
156 }168 }
157169
...@@ -195,27 +207,31 @@ pub const Value = extern union {...@@ -195,27 +207,31 @@ pub const Value = extern union {
195 .comptime_float_type => Type.initTag(.@"comptime_float"),207 .comptime_float_type => Type.initTag(.@"comptime_float"),
196 .noreturn_type => Type.initTag(.@"noreturn"),208 .noreturn_type => Type.initTag(.@"noreturn"),
197 .fn_naked_noreturn_no_args_type => Type.initTag(.fn_naked_noreturn_no_args),209 .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),
198 .single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int),211 .single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int),
199 .const_slice_u8_type => Type.initTag(.const_slice_u8),212 .const_slice_u8_type => Type.initTag(.const_slice_u8),
200213
214 .undef,
201 .zero,215 .zero,
202 .void_value,216 .the_one_possible_value,
203 .noreturn_value,
204 .bool_true,217 .bool_true,
205 .bool_false,218 .bool_false,
219 .null_value,
206 .int_u64,220 .int_u64,
207 .int_i64,221 .int_i64,
208 .int_big,222 .int_big_positive,
223 .int_big_negative,
209 .function,224 .function,
210 .ref,225 .ref,
211 .ref_val,226 .ref_val,
212 .bytes,227 .bytes,
228 .repeated,
213 => unreachable,229 => unreachable,
214 };230 };
215 }231 }
216232
217 /// Asserts the value is an integer.233 /// 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 {
219 switch (self.tag()) {235 switch (self.tag()) {
220 .ty,236 .ty,
221 .u8_type,237 .u8_type,
...@@ -244,23 +260,28 @@ pub const Value = extern union {...@@ -244,23 +260,28 @@ pub const Value = extern union {
244 .comptime_float_type,260 .comptime_float_type,
245 .noreturn_type,261 .noreturn_type,
246 .fn_naked_noreturn_no_args_type,262 .fn_naked_noreturn_no_args_type,
263 .fn_ccc_void_no_args_type,
247 .single_const_pointer_to_comptime_int_type,264 .single_const_pointer_to_comptime_int_type,
248 .const_slice_u8_type,265 .const_slice_u8_type,
249 .void_value,
250 .noreturn_value,
251 .bool_true,266 .bool_true,
252 .bool_false,267 .bool_false,
268 .null_value,
253 .function,269 .function,
254 .ref,270 .ref,
255 .ref_val,271 .ref_val,
256 .bytes,272 .bytes,
273 .undef,
274 .repeated,
257 => unreachable,275 => 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),281 .int_u64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_u64).?.int).toConst(),
262 .int_i64 => return BigInt.initSet(allocator, self.cast(Payload.Int_i64).?.int),282 .int_i64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_i64).?.int).toConst(),
263 .int_big => return self.cast(Payload.IntBig).?.big_int,283 .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt(),
284 .int_big_negative => return self.cast(Payload.IntBigPositive).?.asBigInt(),
264 }285 }
265 }286 }
266287
...@@ -294,23 +315,90 @@ pub const Value = extern union {...@@ -294,23 +315,90 @@ pub const Value = extern union {
294 .comptime_float_type,315 .comptime_float_type,
295 .noreturn_type,316 .noreturn_type,
296 .fn_naked_noreturn_no_args_type,317 .fn_naked_noreturn_no_args_type,
318 .fn_ccc_void_no_args_type,
297 .single_const_pointer_to_comptime_int_type,319 .single_const_pointer_to_comptime_int_type,
298 .const_slice_u8_type,320 .const_slice_u8_type,
299 .void_value,
300 .noreturn_value,
301 .bool_true,321 .bool_true,
302 .bool_false,322 .bool_false,
323 .null_value,
303 .function,324 .function,
304 .ref,325 .ref,
305 .ref_val,326 .ref_val,
306 .bytes,327 .bytes,
328 .undef,
329 .repeated,
307 => unreachable,330 => 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
311 .int_u64 => return self.cast(Payload.Int_u64).?.int,336 .int_u64 => return self.cast(Payload.Int_u64).?.int,
312 .int_i64 => return @intCast(u64, self.cast(Payload.Int_u64).?.int),337 .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(),
314 }402 }
315 }403 }
316404
...@@ -344,19 +432,23 @@ pub const Value = extern union {...@@ -344,19 +432,23 @@ pub const Value = extern union {
344 .comptime_float_type,432 .comptime_float_type,
345 .noreturn_type,433 .noreturn_type,
346 .fn_naked_noreturn_no_args_type,434 .fn_naked_noreturn_no_args_type,
435 .fn_ccc_void_no_args_type,
347 .single_const_pointer_to_comptime_int_type,436 .single_const_pointer_to_comptime_int_type,
348 .const_slice_u8_type,437 .const_slice_u8_type,
349 .void_value,
350 .noreturn_value,
351 .bool_true,438 .bool_true,
352 .bool_false,439 .bool_false,
440 .null_value,
353 .function,441 .function,
354 .ref,442 .ref,
355 .ref_val,443 .ref_val,
356 .bytes,444 .bytes,
445 .repeated,
357 => unreachable,446 => 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
361 .int_u64 => switch (ty.zigTypeTag()) {453 .int_u64 => switch (ty.zigTypeTag()) {
362 .Int => {454 .Int => {
...@@ -381,20 +473,171 @@ pub const Value = extern union {...@@ -381,20 +473,171 @@ pub const Value = extern union {
381 .ComptimeInt => return true,473 .ComptimeInt => return true,
382 else => unreachable,474 else => unreachable,
383 },475 },
384 .int_big => switch (ty.zigTypeTag()) {476 .int_big_positive => switch (ty.zigTypeTag()) {
385 .Int => {477 .Int => {
386 const info = ty.intInfo(target);478 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);
388 },480 },
389 .ComptimeInt => return true,481 .ComptimeInt => return true,
390 else => unreachable,482 else => unreachable,
391 },483 },
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),
392 }599 }
393 }600 }
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
395 /// Asserts the value is a pointer and dereferences it.638 /// Asserts the value is a pointer and dereferences it.
396 pub fn pointerDeref(self: Value) Value {639 pub fn pointerDeref(self: Value) Value {
397 switch (self.tag()) {640 return switch (self.tag()) {
398 .ty,641 .ty,
399 .u8_type,642 .u8_type,
400 .i8_type,643 .i8_type,
...@@ -422,23 +665,27 @@ pub const Value = extern union {...@@ -422,23 +665,27 @@ pub const Value = extern union {
422 .comptime_float_type,665 .comptime_float_type,
423 .noreturn_type,666 .noreturn_type,
424 .fn_naked_noreturn_no_args_type,667 .fn_naked_noreturn_no_args_type,
668 .fn_ccc_void_no_args_type,
425 .single_const_pointer_to_comptime_int_type,669 .single_const_pointer_to_comptime_int_type,
426 .const_slice_u8_type,670 .const_slice_u8_type,
427 .zero,671 .zero,
428 .void_value,
429 .noreturn_value,
430 .bool_true,672 .bool_true,
431 .bool_false,673 .bool_false,
674 .null_value,
432 .function,675 .function,
433 .int_u64,676 .int_u64,
434 .int_i64,677 .int_i64,
435 .int_big,678 .int_big_positive,
679 .int_big_negative,
436 .bytes,680 .bytes,
681 .undef,
682 .repeated,
437 => unreachable,683 => unreachable,
438684
439 .ref => return self.cast(Payload.Ref).?.cell.contents,685 .the_one_possible_value => Value.initTag(.the_one_possible_value),
440 .ref_val => return self.cast(Payload.RefVal).?.val,686 .ref => self.cast(Payload.Ref).?.cell.contents,
441 }687 .ref_val => self.cast(Payload.RefVal).?.val,
688 };
442 }689 }
443690
444 /// Asserts the value is a single-item pointer to an array, or an array,691 /// Asserts the value is a single-item pointer to an array, or an array,
...@@ -472,17 +719,20 @@ pub const Value = extern union {...@@ -472,17 +719,20 @@ pub const Value = extern union {
472 .comptime_float_type,719 .comptime_float_type,
473 .noreturn_type,720 .noreturn_type,
474 .fn_naked_noreturn_no_args_type,721 .fn_naked_noreturn_no_args_type,
722 .fn_ccc_void_no_args_type,
475 .single_const_pointer_to_comptime_int_type,723 .single_const_pointer_to_comptime_int_type,
476 .const_slice_u8_type,724 .const_slice_u8_type,
477 .zero,725 .zero,
478 .void_value,726 .the_one_possible_value,
479 .noreturn_value,
480 .bool_true,727 .bool_true,
481 .bool_false,728 .bool_false,
729 .null_value,
482 .function,730 .function,
483 .int_u64,731 .int_u64,
484 .int_i64,732 .int_i64,
485 .int_big,733 .int_big_positive,
734 .int_big_negative,
735 .undef,
486 => unreachable,736 => unreachable,
487737
488 .ref => @panic("TODO figure out how MemoryCell works"),738 .ref => @panic("TODO figure out how MemoryCell works"),
...@@ -493,9 +743,70 @@ pub const Value = extern union {...@@ -493,9 +743,70 @@ pub const Value = extern union {
493 int_payload.* = .{ .int = self.cast(Payload.Bytes).?.data[index] };743 int_payload.* = .{ .int = self.cast(Payload.Bytes).?.data[index] };
494 return Value.initPayload(&int_payload.base);744 return Value.initPayload(&int_payload.base);
495 },745 },
746
747 // No matter the index; all the elements are the same!
748 .repeated => return self.cast(Payload.Repeated).?.val,
496 }749 }
497 }750 }
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
499 /// This type is not copyable since it may contain pointers to its inner data.810 /// This type is not copyable since it may contain pointers to its inner data.
500 pub const Payload = struct {811 pub const Payload = struct {
501 tag: Tag,812 tag: Tag,
...@@ -510,9 +821,22 @@ pub const Value = extern union {...@@ -510,9 +821,22 @@ pub const Value = extern union {
510 int: i64,821 int: i64,
511 };822 };
512823
513 pub const IntBig = struct {824 pub const IntBigPositive = struct {
514 base: Payload = Payload{ .tag = .int_big },825 base: Payload = Payload{ .tag = .int_big_positive },
515 big_int: BigInt,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 }
516 };840 };
517841
518 pub const Function = struct {842 pub const Function = struct {
...@@ -550,6 +874,20 @@ pub const Value = extern union {...@@ -550,6 +874,20 @@ pub const Value = extern union {
550 base: Payload = Payload{ .tag = .ty },874 base: Payload = Payload{ .tag = .ty },
551 ty: Type,875 ty: Type,
552 };876 };
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,
553 };891 };
554};892};
555893
test/stage2/compare_output.zig+22-19
...@@ -2,24 +2,27 @@ const std = @import("std");...@@ -2,24 +2,27 @@ const std = @import("std");
2const TestContext = @import("../../src-self-hosted/test.zig").TestContext;2const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
33
4pub fn addCases(ctx: *TestContext) !void {4pub fn addCases(ctx: *TestContext) !void {
5 // hello world5 // TODO: re-enable these tests.
6 try ctx.testCompareOutputLibC(6 // https://github.com/ziglang/zig/issues/1364
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);
137
14 // function calling another function8 //// hello world
15 try ctx.testCompareOutputLibC(9 //try ctx.testCompareOutputLibC(
16 \\extern fn puts(s: [*]const u8) void;10 // \\extern fn puts([*]const u8) void;
17 \\pub export fn main() c_int {11 // \\pub export fn main() c_int {
18 \\ return foo("OK");12 // \\ puts("Hello, world!");
19 \\}13 // \\ return 0;
20 \\fn foo(s: [*]const u8) c_int {14 // \\}
21 \\ puts(s);15 //, "Hello, world!" ++ std.cstr.line_sep);
22 \\ return 0;16
23 \\}17 //// function calling another function
24 , "OK" ++ std.cstr.line_sep);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);
25}28}
test/stage2/compile_errors.zig+53-50
...@@ -1,54 +1,57 @@...@@ -1,54 +1,57 @@
1const TestContext = @import("../../src-self-hosted/test.zig").TestContext;1const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
22
3pub fn addCases(ctx: *TestContext) !void {3pub fn addCases(ctx: *TestContext) !void {
4 try ctx.testCompileError(4 // TODO: re-enable these tests.
5 \\export fn entry() void {}5 // https://github.com/ziglang/zig/issues/1364
6 \\export fn entry() void {}6
7 , "1.zig", 2, 8, "exported symbol collision: 'entry'");7 //try ctx.testCompileError(
88 // \\export fn entry() void {}
9 try ctx.testCompileError(9 // \\export fn entry() void {}
10 \\fn() void {}10 //, "1.zig", 2, 8, "exported symbol collision: 'entry'");
11 , "1.zig", 1, 1, "missing function name");11
1212 //try ctx.testCompileError(
13 try ctx.testCompileError(13 // \\fn() void {}
14 \\comptime {14 //, "1.zig", 1, 1, "missing function name");
15 \\ return;15
16 \\}16 //try ctx.testCompileError(
17 , "1.zig", 2, 5, "return expression outside function definition");17 // \\comptime {
1818 // \\ return;
19 try ctx.testCompileError(19 // \\}
20 \\export fn entry() void {20 //, "1.zig", 2, 5, "return expression outside function definition");
21 \\ defer return;21
22 \\}22 //try ctx.testCompileError(
23 , "1.zig", 2, 11, "cannot return from defer expression");23 // \\export fn entry() void {
2424 // \\ defer return;
25 try ctx.testCompileError(25 // \\}
26 \\export fn entry() c_int {26 //, "1.zig", 2, 11, "cannot return from defer expression");
27 \\ return 36893488147419103232;27
28 \\}28 //try ctx.testCompileError(
29 , "1.zig", 2, 12, "integer value '36893488147419103232' cannot be stored in type 'c_int'");29 // \\export fn entry() c_int {
3030 // \\ return 36893488147419103232;
31 try ctx.testCompileError(31 // \\}
32 \\comptime {32 //, "1.zig", 2, 12, "integer value '36893488147419103232' cannot be stored in type 'c_int'");
33 \\ var a: *align(4) align(4) i32 = 0;33
34 \\}34 //try ctx.testCompileError(
35 , "1.zig", 2, 22, "Extra align qualifier");35 // \\comptime {
3636 // \\ var a: *align(4) align(4) i32 = 0;
37 try ctx.testCompileError(37 // \\}
38 \\comptime {38 //, "1.zig", 2, 22, "Extra align qualifier");
39 \\ var b: *const const i32 = 0;39
40 \\}40 //try ctx.testCompileError(
41 , "1.zig", 2, 19, "Extra align qualifier");41 // \\comptime {
4242 // \\ var b: *const const i32 = 0;
43 try ctx.testCompileError(43 // \\}
44 \\comptime {44 //, "1.zig", 2, 19, "Extra align qualifier");
45 \\ var c: *volatile volatile i32 = 0;45
46 \\}46 //try ctx.testCompileError(
47 , "1.zig", 2, 22, "Extra align qualifier");47 // \\comptime {
4848 // \\ var c: *volatile volatile i32 = 0;
49 try ctx.testCompileError(49 // \\}
50 \\comptime {50 //, "1.zig", 2, 22, "Extra align qualifier");
51 \\ var d: *allowzero allowzero i32 = 0;51
52 \\}52 //try ctx.testCompileError(
53 , "1.zig", 2, 23, "Extra align qualifier");53 // \\comptime {
54 // \\ var d: *allowzero allowzero i32 = 0;
55 // \\}
56 //, "1.zig", 2, 23, "Extra align qualifier");
54}57}
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;...@@ -3,4 +3,5 @@ const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
3pub fn addCases(ctx: *TestContext) !void {3pub fn addCases(ctx: *TestContext) !void {
4 try @import("compile_errors.zig").addCases(ctx);4 try @import("compile_errors.zig").addCases(ctx);
5 try @import("compare_output.zig").addCases(ctx);5 try @import("compare_output.zig").addCases(ctx);
6 @import("zir.zig").addCases(ctx);
6}7}
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}