| author | |
| committer | |
| log | 615d45da779842715a3ab65b59233e9cfb4fa122 |
| tree | 9c269e8fa9beded00954d82ebc0c95d56c485322 |
| parent | 1d3f76bbda90f810a24845c15516235d91ee12ad |
| parent | 0dd0c9620d66afcfabaf3dcb21b636530fd0ccba |
Conflicts:
* src/codegen/spirv.zig
* src/link/SpirV.zig
We're going to want to improve the stage2 test harness to print
the source file name when a compile error occurs otherwise std lib
contributors are going to see some confusing CI failures when they cause
stage2 AstGen compile errors.35 files changed, 885 insertions(+), 297 deletions(-)
ci/azure/linux_script+1-1| ... | @@ -20,7 +20,7 @@ cd $HOME | ... | @@ -20,7 +20,7 @@ cd $HOME |
| 20 | wget -nv "https://ziglang.org/deps/$CACHE_BASENAME.tar.xz" | 20 | wget -nv "https://ziglang.org/deps/$CACHE_BASENAME.tar.xz" |
| 21 | tar xf "$CACHE_BASENAME.tar.xz" | 21 | tar xf "$CACHE_BASENAME.tar.xz" |
| 22 | 22 | ||
| 23 | QEMUBASE="qemu-linux-x86_64-5.2.0" | 23 | QEMUBASE="qemu-linux-x86_64-5.2.0.1" |
| 24 | wget -nv "https://ziglang.org/deps/$QEMUBASE.tar.xz" | 24 | wget -nv "https://ziglang.org/deps/$QEMUBASE.tar.xz" |
| 25 | tar xf "$QEMUBASE.tar.xz" | 25 | tar xf "$QEMUBASE.tar.xz" |
| 26 | export PATH="$(pwd)/$QEMUBASE/bin:$PATH" | 26 | export PATH="$(pwd)/$QEMUBASE/bin:$PATH" |
lib/std/crypto/tlcsprng.zig+75-42| ... | @@ -12,6 +12,7 @@ | ... | @@ -12,6 +12,7 @@ |
| 12 | const std = @import("std"); | 12 | const std = @import("std"); |
| 13 | const root = @import("root"); | 13 | const root = @import("root"); |
| 14 | const mem = std.mem; | 14 | const mem = std.mem; |
| 15 | const os = std.os; | ||
| 15 | 16 | ||
| 16 | /// We use this as a layer of indirection because global const pointers cannot | 17 | /// We use this as a layer of indirection because global const pointers cannot |
| 17 | /// point to thread-local variables. | 18 | /// point to thread-local variables. |
| ... | @@ -42,16 +43,12 @@ const maybe_have_wipe_on_fork = std.Target.current.os.isAtLeast(.linux, .{ | ... | @@ -42,16 +43,12 @@ const maybe_have_wipe_on_fork = std.Target.current.os.isAtLeast(.linux, .{ |
| 42 | .minor = 14, | 43 | .minor = 14, |
| 43 | }) orelse true; | 44 | }) orelse true; |
| 44 | 45 | ||
| 45 | const WipeMe = struct { | 46 | const Context = struct { |
| 46 | init_state: enum { uninitialized, initialized, failed }, | 47 | init_state: enum(u8) { uninitialized = 0, initialized, failed }, |
| 47 | gimli: std.crypto.core.Gimli, | 48 | gimli: std.crypto.core.Gimli, |
| 48 | }; | 49 | }; |
| 49 | const wipe_align = if (maybe_have_wipe_on_fork) mem.page_size else @alignOf(WipeMe); | ||
| 50 | 50 | ||
| 51 | threadlocal var wipe_me: WipeMe align(wipe_align) = .{ | 51 | threadlocal var wipe_mem: []align(mem.page_size) u8 = &[_]u8{}; |
| 52 | .gimli = undefined, | ||
| 53 | .init_state = .uninitialized, | ||
| 54 | }; | ||
| 55 | 52 | ||
| 56 | fn tlsCsprngFill(_: *const std.rand.Random, buffer: []u8) void { | 53 | fn tlsCsprngFill(_: *const std.rand.Random, buffer: []u8) void { |
| 57 | if (std.builtin.link_libc and @hasDecl(std.c, "arc4random_buf")) { | 54 | if (std.builtin.link_libc and @hasDecl(std.c, "arc4random_buf")) { |
| ... | @@ -64,35 +61,69 @@ fn tlsCsprngFill(_: *const std.rand.Random, buffer: []u8) void { | ... | @@ -64,35 +61,69 @@ fn tlsCsprngFill(_: *const std.rand.Random, buffer: []u8) void { |
| 64 | if (comptime std.meta.globalOption("crypto_always_getrandom", bool) orelse false) { | 61 | if (comptime std.meta.globalOption("crypto_always_getrandom", bool) orelse false) { |
| 65 | return fillWithOsEntropy(buffer); | 62 | return fillWithOsEntropy(buffer); |
| 66 | } | 63 | } |
| 67 | switch (wipe_me.init_state) { | 64 | |
| 65 | if (wipe_mem.len == 0) { | ||
| 66 | // Not initialized yet. | ||
| 67 | if (want_fork_safety and maybe_have_wipe_on_fork) { | ||
| 68 | // Allocate a per-process page, madvise operates with page | ||
| 69 | // granularity. | ||
| 70 | wipe_mem = os.mmap( | ||
| 71 | null, | ||
| 72 | @sizeOf(Context), | ||
| 73 | os.PROT_READ | os.PROT_WRITE, | ||
| 74 | os.MAP_PRIVATE | os.MAP_ANONYMOUS, | ||
| 75 | -1, | ||
| 76 | 0, | ||
| 77 | ) catch |err| { | ||
| 78 | // Could not allocate memory for the local state, fall back to | ||
| 79 | // the OS syscall. | ||
| 80 | return fillWithOsEntropy(buffer); | ||
| 81 | }; | ||
| 82 | // The memory is already zero-initialized. | ||
| 83 | } else { | ||
| 84 | // Use a static thread-local buffer. | ||
| 85 | const S = struct { | ||
| 86 | threadlocal var buf: Context align(mem.page_size) = .{ | ||
| 87 | .init_state = .uninitialized, | ||
| 88 | .gimli = undefined, | ||
| 89 | }; | ||
| 90 | }; | ||
| 91 | wipe_mem = mem.asBytes(&S.buf); | ||
| 92 | } | ||
| 93 | } | ||
| 94 | const ctx = @ptrCast(*Context, wipe_mem.ptr); | ||
| 95 | |||
| 96 | switch (ctx.init_state) { | ||
| 68 | .uninitialized => { | 97 | .uninitialized => { |
| 69 | if (want_fork_safety) { | 98 | if (!want_fork_safety) { |
| 70 | if (maybe_have_wipe_on_fork) { | ||
| 71 | if (std.os.madvise( | ||
| 72 | @ptrCast([*]align(mem.page_size) u8, &wipe_me), | ||
| 73 | @sizeOf(@TypeOf(wipe_me)), | ||
| 74 | std.os.MADV_WIPEONFORK, | ||
| 75 | )) |_| { | ||
| 76 | return initAndFill(buffer); | ||
| 77 | } else |_| if (std.Thread.use_pthreads) { | ||
| 78 | return setupPthreadAtforkAndFill(buffer); | ||
| 79 | } else { | ||
| 80 | // Since we failed to set up fork safety, we fall back to always | ||
| 81 | // calling getrandom every time. | ||
| 82 | wipe_me.init_state = .failed; | ||
| 83 | return fillWithOsEntropy(buffer); | ||
| 84 | } | ||
| 85 | } else if (std.Thread.use_pthreads) { | ||
| 86 | return setupPthreadAtforkAndFill(buffer); | ||
| 87 | } else { | ||
| 88 | // We have no mechanism to provide fork safety, but we want fork safety, | ||
| 89 | // so we fall back to calling getrandom every time. | ||
| 90 | wipe_me.init_state = .failed; | ||
| 91 | return fillWithOsEntropy(buffer); | ||
| 92 | } | ||
| 93 | } else { | ||
| 94 | return initAndFill(buffer); | 99 | return initAndFill(buffer); |
| 95 | } | 100 | } |
| 101 | |||
| 102 | if (maybe_have_wipe_on_fork) wof: { | ||
| 103 | // Qemu user-mode emulation ignores any valid/invalid madvise | ||
| 104 | // hint and returns success. Check if this is the case by | ||
| 105 | // passing bogus parameters, we expect EINVAL as result. | ||
| 106 | if (os.madvise(wipe_mem.ptr, 0, 0xffffffff)) |_| { | ||
| 107 | break :wof; | ||
| 108 | } else |_| {} | ||
| 109 | |||
| 110 | os.madvise( | ||
| 111 | wipe_mem.ptr, | ||
| 112 | wipe_mem.len, | ||
| 113 | os.MADV_WIPEONFORK, | ||
| 114 | ) catch { | ||
| 115 | return initAndFill(buffer); | ||
| 116 | }; | ||
| 117 | } | ||
| 118 | |||
| 119 | if (std.Thread.use_pthreads) { | ||
| 120 | return setupPthreadAtforkAndFill(buffer); | ||
| 121 | } | ||
| 122 | |||
| 123 | // Since we failed to set up fork safety, we fall back to always | ||
| 124 | // calling getrandom every time. | ||
| 125 | ctx.init_state = .failed; | ||
| 126 | return fillWithOsEntropy(buffer); | ||
| 96 | }, | 127 | }, |
| 97 | .initialized => { | 128 | .initialized => { |
| 98 | return fillWithCsprng(buffer); | 129 | return fillWithCsprng(buffer); |
| ... | @@ -110,7 +141,8 @@ fn tlsCsprngFill(_: *const std.rand.Random, buffer: []u8) void { | ... | @@ -110,7 +141,8 @@ fn tlsCsprngFill(_: *const std.rand.Random, buffer: []u8) void { |
| 110 | fn setupPthreadAtforkAndFill(buffer: []u8) void { | 141 | fn setupPthreadAtforkAndFill(buffer: []u8) void { |
| 111 | const failed = std.c.pthread_atfork(null, null, childAtForkHandler) != 0; | 142 | const failed = std.c.pthread_atfork(null, null, childAtForkHandler) != 0; |
| 112 | if (failed) { | 143 | if (failed) { |
| 113 | wipe_me.init_state = .failed; | 144 | const ctx = @ptrCast(*Context, wipe_mem.ptr); |
| 145 | ctx.init_state = .failed; | ||
| 114 | return fillWithOsEntropy(buffer); | 146 | return fillWithOsEntropy(buffer); |
| 115 | } else { | 147 | } else { |
| 116 | return initAndFill(buffer); | 148 | return initAndFill(buffer); |
| ... | @@ -118,21 +150,21 @@ fn setupPthreadAtforkAndFill(buffer: []u8) void { | ... | @@ -118,21 +150,21 @@ fn setupPthreadAtforkAndFill(buffer: []u8) void { |
| 118 | } | 150 | } |
| 119 | 151 | ||
| 120 | fn childAtForkHandler() callconv(.C) void { | 152 | fn childAtForkHandler() callconv(.C) void { |
| 121 | const wipe_slice = @ptrCast([*]u8, &wipe_me)[0..@sizeOf(@TypeOf(wipe_me))]; | 153 | std.crypto.utils.secureZero(u8, wipe_mem); |
| 122 | std.crypto.utils.secureZero(u8, wipe_slice); | ||
| 123 | } | 154 | } |
| 124 | 155 | ||
| 125 | fn fillWithCsprng(buffer: []u8) void { | 156 | fn fillWithCsprng(buffer: []u8) void { |
| 157 | const ctx = @ptrCast(*Context, wipe_mem.ptr); | ||
| 126 | if (buffer.len != 0) { | 158 | if (buffer.len != 0) { |
| 127 | wipe_me.gimli.squeeze(buffer); | 159 | ctx.gimli.squeeze(buffer); |
| 128 | } else { | 160 | } else { |
| 129 | wipe_me.gimli.permute(); | 161 | ctx.gimli.permute(); |
| 130 | } | 162 | } |
| 131 | mem.set(u8, wipe_me.gimli.toSlice()[0..std.crypto.core.Gimli.RATE], 0); | 163 | mem.set(u8, ctx.gimli.toSlice()[0..std.crypto.core.Gimli.RATE], 0); |
| 132 | } | 164 | } |
| 133 | 165 | ||
| 134 | fn fillWithOsEntropy(buffer: []u8) void { | 166 | fn fillWithOsEntropy(buffer: []u8) void { |
| 135 | std.os.getrandom(buffer) catch @panic("getrandom() failed to provide entropy"); | 167 | os.getrandom(buffer) catch @panic("getrandom() failed to provide entropy"); |
| 136 | } | 168 | } |
| 137 | 169 | ||
| 138 | fn initAndFill(buffer: []u8) void { | 170 | fn initAndFill(buffer: []u8) void { |
| ... | @@ -147,11 +179,12 @@ fn initAndFill(buffer: []u8) void { | ... | @@ -147,11 +179,12 @@ fn initAndFill(buffer: []u8) void { |
| 147 | fillWithOsEntropy(&seed); | 179 | fillWithOsEntropy(&seed); |
| 148 | } | 180 | } |
| 149 | 181 | ||
| 150 | wipe_me.gimli = std.crypto.core.Gimli.init(seed); | 182 | const ctx = @ptrCast(*Context, wipe_mem.ptr); |
| 183 | ctx.gimli = std.crypto.core.Gimli.init(seed); | ||
| 151 | 184 | ||
| 152 | // This is at the end so that accidental recursive dependencies result | 185 | // This is at the end so that accidental recursive dependencies result |
| 153 | // in stack overflows instead of invalid random data. | 186 | // in stack overflows instead of invalid random data. |
| 154 | wipe_me.init_state = .initialized; | 187 | ctx.init_state = .initialized; |
| 155 | 188 | ||
| 156 | return fillWithCsprng(buffer); | 189 | return fillWithCsprng(buffer); |
| 157 | } | 190 | } |
lib/std/math/complex.zig+15-12| ... | @@ -38,9 +38,12 @@ pub fn Complex(comptime T: type) type { | ... | @@ -38,9 +38,12 @@ pub fn Complex(comptime T: type) type { |
| 38 | 38 | ||
| 39 | /// Imaginary part. | 39 | /// Imaginary part. |
| 40 | im: T, | 40 | im: T, |
| 41 | |||
| 42 | /// Deprecated, use init() | ||
| 43 | pub const new = init; | ||
| 41 | 44 | ||
| 42 | /// Create a new Complex number from the given real and imaginary parts. | 45 | /// Create a new Complex number from the given real and imaginary parts. |
| 43 | pub fn new(re: T, im: T) Self { | 46 | pub fn init(re: T, im: T) Self { |
| 44 | return Self{ | 47 | return Self{ |
| 45 | .re = re, | 48 | .re = re, |
| 46 | .im = im, | 49 | .im = im, |
| ... | @@ -110,32 +113,32 @@ pub fn Complex(comptime T: type) type { | ... | @@ -110,32 +113,32 @@ pub fn Complex(comptime T: type) type { |
| 110 | const epsilon = 0.0001; | 113 | const epsilon = 0.0001; |
| 111 | 114 | ||
| 112 | test "complex.add" { | 115 | test "complex.add" { |
| 113 | const a = Complex(f32).new(5, 3); | 116 | const a = Complex(f32).init(5, 3); |
| 114 | const b = Complex(f32).new(2, 7); | 117 | const b = Complex(f32).init(2, 7); |
| 115 | const c = a.add(b); | 118 | const c = a.add(b); |
| 116 | 119 | ||
| 117 | try testing.expect(c.re == 7 and c.im == 10); | 120 | try testing.expect(c.re == 7 and c.im == 10); |
| 118 | } | 121 | } |
| 119 | 122 | ||
| 120 | test "complex.sub" { | 123 | test "complex.sub" { |
| 121 | const a = Complex(f32).new(5, 3); | 124 | const a = Complex(f32).init(5, 3); |
| 122 | const b = Complex(f32).new(2, 7); | 125 | const b = Complex(f32).init(2, 7); |
| 123 | const c = a.sub(b); | 126 | const c = a.sub(b); |
| 124 | 127 | ||
| 125 | try testing.expect(c.re == 3 and c.im == -4); | 128 | try testing.expect(c.re == 3 and c.im == -4); |
| 126 | } | 129 | } |
| 127 | 130 | ||
| 128 | test "complex.mul" { | 131 | test "complex.mul" { |
| 129 | const a = Complex(f32).new(5, 3); | 132 | const a = Complex(f32).init(5, 3); |
| 130 | const b = Complex(f32).new(2, 7); | 133 | const b = Complex(f32).init(2, 7); |
| 131 | const c = a.mul(b); | 134 | const c = a.mul(b); |
| 132 | 135 | ||
| 133 | try testing.expect(c.re == -11 and c.im == 41); | 136 | try testing.expect(c.re == -11 and c.im == 41); |
| 134 | } | 137 | } |
| 135 | 138 | ||
| 136 | test "complex.div" { | 139 | test "complex.div" { |
| 137 | const a = Complex(f32).new(5, 3); | 140 | const a = Complex(f32).init(5, 3); |
| 138 | const b = Complex(f32).new(2, 7); | 141 | const b = Complex(f32).init(2, 7); |
| 139 | const c = a.div(b); | 142 | const c = a.div(b); |
| 140 | 143 | ||
| 141 | try testing.expect(math.approxEqAbs(f32, c.re, @as(f32, 31) / 53, epsilon) and | 144 | try testing.expect(math.approxEqAbs(f32, c.re, @as(f32, 31) / 53, epsilon) and |
| ... | @@ -143,14 +146,14 @@ test "complex.div" { | ... | @@ -143,14 +146,14 @@ test "complex.div" { |
| 143 | } | 146 | } |
| 144 | 147 | ||
| 145 | test "complex.conjugate" { | 148 | test "complex.conjugate" { |
| 146 | const a = Complex(f32).new(5, 3); | 149 | const a = Complex(f32).init(5, 3); |
| 147 | const c = a.conjugate(); | 150 | const c = a.conjugate(); |
| 148 | 151 | ||
| 149 | try testing.expect(c.re == 5 and c.im == -3); | 152 | try testing.expect(c.re == 5 and c.im == -3); |
| 150 | } | 153 | } |
| 151 | 154 | ||
| 152 | test "complex.reciprocal" { | 155 | test "complex.reciprocal" { |
| 153 | const a = Complex(f32).new(5, 3); | 156 | const a = Complex(f32).init(5, 3); |
| 154 | const c = a.reciprocal(); | 157 | const c = a.reciprocal(); |
| 155 | 158 | ||
| 156 | try testing.expect(math.approxEqAbs(f32, c.re, @as(f32, 5) / 34, epsilon) and | 159 | try testing.expect(math.approxEqAbs(f32, c.re, @as(f32, 5) / 34, epsilon) and |
| ... | @@ -158,7 +161,7 @@ test "complex.reciprocal" { | ... | @@ -158,7 +161,7 @@ test "complex.reciprocal" { |
| 158 | } | 161 | } |
| 159 | 162 | ||
| 160 | test "complex.magnitude" { | 163 | test "complex.magnitude" { |
| 161 | const a = Complex(f32).new(5, 3); | 164 | const a = Complex(f32).init(5, 3); |
| 162 | const c = a.magnitude(); | 165 | const c = a.magnitude(); |
| 163 | 166 | ||
| 164 | try testing.expect(math.approxEqAbs(f32, c, 5.83095, epsilon)); | 167 | try testing.expect(math.approxEqAbs(f32, c, 5.83095, epsilon)); |
lib/std/math/complex/abs.zig+1-1| ... | @@ -18,7 +18,7 @@ pub fn abs(z: anytype) @TypeOf(z.re) { | ... | @@ -18,7 +18,7 @@ pub fn abs(z: anytype) @TypeOf(z.re) { |
| 18 | const epsilon = 0.0001; | 18 | const epsilon = 0.0001; |
| 19 | 19 | ||
| 20 | test "complex.cabs" { | 20 | test "complex.cabs" { |
| 21 | const a = Complex(f32).new(5, 3); | 21 | const a = Complex(f32).init(5, 3); |
| 22 | const c = abs(a); | 22 | const c = abs(a); |
| 23 | try testing.expect(math.approxEqAbs(f32, c, 5.83095, epsilon)); | 23 | try testing.expect(math.approxEqAbs(f32, c, 5.83095, epsilon)); |
| 24 | } | 24 | } |
lib/std/math/complex/acos.zig+2-2| ... | @@ -13,13 +13,13 @@ const Complex = cmath.Complex; | ... | @@ -13,13 +13,13 @@ const Complex = cmath.Complex; |
| 13 | pub fn acos(z: anytype) Complex(@TypeOf(z.re)) { | 13 | pub fn acos(z: anytype) Complex(@TypeOf(z.re)) { |
| 14 | const T = @TypeOf(z.re); | 14 | const T = @TypeOf(z.re); |
| 15 | const q = cmath.asin(z); | 15 | const q = cmath.asin(z); |
| 16 | return Complex(T).new(@as(T, math.pi) / 2 - q.re, -q.im); | 16 | return Complex(T).init(@as(T, math.pi) / 2 - q.re, -q.im); |
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | const epsilon = 0.0001; | 19 | const epsilon = 0.0001; |
| 20 | 20 | ||
| 21 | test "complex.cacos" { | 21 | test "complex.cacos" { |
| 22 | const a = Complex(f32).new(5, 3); | 22 | const a = Complex(f32).init(5, 3); |
| 23 | const c = acos(a); | 23 | const c = acos(a); |
| 24 | 24 | ||
| 25 | try testing.expect(math.approxEqAbs(f32, c.re, 0.546975, epsilon)); | 25 | try testing.expect(math.approxEqAbs(f32, c.re, 0.546975, epsilon)); |
lib/std/math/complex/acosh.zig+2-2| ... | @@ -13,13 +13,13 @@ const Complex = cmath.Complex; | ... | @@ -13,13 +13,13 @@ const Complex = cmath.Complex; |
| 13 | pub fn acosh(z: anytype) Complex(@TypeOf(z.re)) { | 13 | pub fn acosh(z: anytype) Complex(@TypeOf(z.re)) { |
| 14 | const T = @TypeOf(z.re); | 14 | const T = @TypeOf(z.re); |
| 15 | const q = cmath.acos(z); | 15 | const q = cmath.acos(z); |
| 16 | return Complex(T).new(-q.im, q.re); | 16 | return Complex(T).init(-q.im, q.re); |
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | const epsilon = 0.0001; | 19 | const epsilon = 0.0001; |
| 20 | 20 | ||
| 21 | test "complex.cacosh" { | 21 | test "complex.cacosh" { |
| 22 | const a = Complex(f32).new(5, 3); | 22 | const a = Complex(f32).init(5, 3); |
| 23 | const c = acosh(a); | 23 | const c = acosh(a); |
| 24 | 24 | ||
| 25 | try testing.expect(math.approxEqAbs(f32, c.re, 2.452914, epsilon)); | 25 | try testing.expect(math.approxEqAbs(f32, c.re, 2.452914, epsilon)); |
lib/std/math/complex/arg.zig+1-1| ... | @@ -18,7 +18,7 @@ pub fn arg(z: anytype) @TypeOf(z.re) { | ... | @@ -18,7 +18,7 @@ pub fn arg(z: anytype) @TypeOf(z.re) { |
| 18 | const epsilon = 0.0001; | 18 | const epsilon = 0.0001; |
| 19 | 19 | ||
| 20 | test "complex.carg" { | 20 | test "complex.carg" { |
| 21 | const a = Complex(f32).new(5, 3); | 21 | const a = Complex(f32).init(5, 3); |
| 22 | const c = arg(a); | 22 | const c = arg(a); |
| 23 | try testing.expect(math.approxEqAbs(f32, c, 0.540420, epsilon)); | 23 | try testing.expect(math.approxEqAbs(f32, c, 0.540420, epsilon)); |
| 24 | } | 24 | } |
lib/std/math/complex/asin.zig+4-4| ... | @@ -15,17 +15,17 @@ pub fn asin(z: anytype) Complex(@TypeOf(z.re)) { | ... | @@ -15,17 +15,17 @@ pub fn asin(z: anytype) Complex(@TypeOf(z.re)) { |
| 15 | const x = z.re; | 15 | const x = z.re; |
| 16 | const y = z.im; | 16 | const y = z.im; |
| 17 | 17 | ||
| 18 | const p = Complex(T).new(1.0 - (x - y) * (x + y), -2.0 * x * y); | 18 | const p = Complex(T).init(1.0 - (x - y) * (x + y), -2.0 * x * y); |
| 19 | const q = Complex(T).new(-y, x); | 19 | const q = Complex(T).init(-y, x); |
| 20 | const r = cmath.log(q.add(cmath.sqrt(p))); | 20 | const r = cmath.log(q.add(cmath.sqrt(p))); |
| 21 | 21 | ||
| 22 | return Complex(T).new(r.im, -r.re); | 22 | return Complex(T).init(r.im, -r.re); |
| 23 | } | 23 | } |
| 24 | 24 | ||
| 25 | const epsilon = 0.0001; | 25 | const epsilon = 0.0001; |
| 26 | 26 | ||
| 27 | test "complex.casin" { | 27 | test "complex.casin" { |
| 28 | const a = Complex(f32).new(5, 3); | 28 | const a = Complex(f32).init(5, 3); |
| 29 | const c = asin(a); | 29 | const c = asin(a); |
| 30 | 30 | ||
| 31 | try testing.expect(math.approxEqAbs(f32, c.re, 1.023822, epsilon)); | 31 | try testing.expect(math.approxEqAbs(f32, c.re, 1.023822, epsilon)); |
lib/std/math/complex/asinh.zig+3-3| ... | @@ -12,15 +12,15 @@ const Complex = cmath.Complex; | ... | @@ -12,15 +12,15 @@ const Complex = cmath.Complex; |
| 12 | /// Returns the hyperbolic arc-sine of z. | 12 | /// Returns the hyperbolic arc-sine of z. |
| 13 | pub fn asinh(z: anytype) Complex(@TypeOf(z.re)) { | 13 | pub fn asinh(z: anytype) Complex(@TypeOf(z.re)) { |
| 14 | const T = @TypeOf(z.re); | 14 | const T = @TypeOf(z.re); |
| 15 | const q = Complex(T).new(-z.im, z.re); | 15 | const q = Complex(T).init(-z.im, z.re); |
| 16 | const r = cmath.asin(q); | 16 | const r = cmath.asin(q); |
| 17 | return Complex(T).new(r.im, -r.re); | 17 | return Complex(T).init(r.im, -r.re); |
| 18 | } | 18 | } |
| 19 | 19 | ||
| 20 | const epsilon = 0.0001; | 20 | const epsilon = 0.0001; |
| 21 | 21 | ||
| 22 | test "complex.casinh" { | 22 | test "complex.casinh" { |
| 23 | const a = Complex(f32).new(5, 3); | 23 | const a = Complex(f32).init(5, 3); |
| 24 | const c = asinh(a); | 24 | const c = asinh(a); |
| 25 | 25 | ||
| 26 | try testing.expect(math.approxEqAbs(f32, c.re, 2.459831, epsilon)); | 26 | try testing.expect(math.approxEqAbs(f32, c.re, 2.459831, epsilon)); |
lib/std/math/complex/atan.zig+10-10| ... | @@ -49,14 +49,14 @@ fn atan32(z: Complex(f32)) Complex(f32) { | ... | @@ -49,14 +49,14 @@ fn atan32(z: Complex(f32)) Complex(f32) { |
| 49 | 49 | ||
| 50 | if ((x == 0.0) and (y > 1.0)) { | 50 | if ((x == 0.0) and (y > 1.0)) { |
| 51 | // overflow | 51 | // overflow |
| 52 | return Complex(f32).new(maxnum, maxnum); | 52 | return Complex(f32).init(maxnum, maxnum); |
| 53 | } | 53 | } |
| 54 | 54 | ||
| 55 | const x2 = x * x; | 55 | const x2 = x * x; |
| 56 | var a = 1.0 - x2 - (y * y); | 56 | var a = 1.0 - x2 - (y * y); |
| 57 | if (a == 0.0) { | 57 | if (a == 0.0) { |
| 58 | // overflow | 58 | // overflow |
| 59 | return Complex(f32).new(maxnum, maxnum); | 59 | return Complex(f32).init(maxnum, maxnum); |
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | var t = 0.5 * math.atan2(f32, 2.0 * x, a); | 62 | var t = 0.5 * math.atan2(f32, 2.0 * x, a); |
| ... | @@ -66,12 +66,12 @@ fn atan32(z: Complex(f32)) Complex(f32) { | ... | @@ -66,12 +66,12 @@ fn atan32(z: Complex(f32)) Complex(f32) { |
| 66 | a = x2 + t * t; | 66 | a = x2 + t * t; |
| 67 | if (a == 0.0) { | 67 | if (a == 0.0) { |
| 68 | // overflow | 68 | // overflow |
| 69 | return Complex(f32).new(maxnum, maxnum); | 69 | return Complex(f32).init(maxnum, maxnum); |
| 70 | } | 70 | } |
| 71 | 71 | ||
| 72 | t = y + 1.0; | 72 | t = y + 1.0; |
| 73 | a = (x2 + (t * t)) / a; | 73 | a = (x2 + (t * t)) / a; |
| 74 | return Complex(f32).new(w, 0.25 * math.ln(a)); | 74 | return Complex(f32).init(w, 0.25 * math.ln(a)); |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | fn redupif64(x: f64) f64 { | 77 | fn redupif64(x: f64) f64 { |
| ... | @@ -98,14 +98,14 @@ fn atan64(z: Complex(f64)) Complex(f64) { | ... | @@ -98,14 +98,14 @@ fn atan64(z: Complex(f64)) Complex(f64) { |
| 98 | 98 | ||
| 99 | if ((x == 0.0) and (y > 1.0)) { | 99 | if ((x == 0.0) and (y > 1.0)) { |
| 100 | // overflow | 100 | // overflow |
| 101 | return Complex(f64).new(maxnum, maxnum); | 101 | return Complex(f64).init(maxnum, maxnum); |
| 102 | } | 102 | } |
| 103 | 103 | ||
| 104 | const x2 = x * x; | 104 | const x2 = x * x; |
| 105 | var a = 1.0 - x2 - (y * y); | 105 | var a = 1.0 - x2 - (y * y); |
| 106 | if (a == 0.0) { | 106 | if (a == 0.0) { |
| 107 | // overflow | 107 | // overflow |
| 108 | return Complex(f64).new(maxnum, maxnum); | 108 | return Complex(f64).init(maxnum, maxnum); |
| 109 | } | 109 | } |
| 110 | 110 | ||
| 111 | var t = 0.5 * math.atan2(f64, 2.0 * x, a); | 111 | var t = 0.5 * math.atan2(f64, 2.0 * x, a); |
| ... | @@ -115,18 +115,18 @@ fn atan64(z: Complex(f64)) Complex(f64) { | ... | @@ -115,18 +115,18 @@ fn atan64(z: Complex(f64)) Complex(f64) { |
| 115 | a = x2 + t * t; | 115 | a = x2 + t * t; |
| 116 | if (a == 0.0) { | 116 | if (a == 0.0) { |
| 117 | // overflow | 117 | // overflow |
| 118 | return Complex(f64).new(maxnum, maxnum); | 118 | return Complex(f64).init(maxnum, maxnum); |
| 119 | } | 119 | } |
| 120 | 120 | ||
| 121 | t = y + 1.0; | 121 | t = y + 1.0; |
| 122 | a = (x2 + (t * t)) / a; | 122 | a = (x2 + (t * t)) / a; |
| 123 | return Complex(f64).new(w, 0.25 * math.ln(a)); | 123 | return Complex(f64).init(w, 0.25 * math.ln(a)); |
| 124 | } | 124 | } |
| 125 | 125 | ||
| 126 | const epsilon = 0.0001; | 126 | const epsilon = 0.0001; |
| 127 | 127 | ||
| 128 | test "complex.catan32" { | 128 | test "complex.catan32" { |
| 129 | const a = Complex(f32).new(5, 3); | 129 | const a = Complex(f32).init(5, 3); |
| 130 | const c = atan(a); | 130 | const c = atan(a); |
| 131 | 131 | ||
| 132 | try testing.expect(math.approxEqAbs(f32, c.re, 1.423679, epsilon)); | 132 | try testing.expect(math.approxEqAbs(f32, c.re, 1.423679, epsilon)); |
| ... | @@ -134,7 +134,7 @@ test "complex.catan32" { | ... | @@ -134,7 +134,7 @@ test "complex.catan32" { |
| 134 | } | 134 | } |
| 135 | 135 | ||
| 136 | test "complex.catan64" { | 136 | test "complex.catan64" { |
| 137 | const a = Complex(f64).new(5, 3); | 137 | const a = Complex(f64).init(5, 3); |
| 138 | const c = atan(a); | 138 | const c = atan(a); |
| 139 | 139 | ||
| 140 | try testing.expect(math.approxEqAbs(f64, c.re, 1.423679, epsilon)); | 140 | try testing.expect(math.approxEqAbs(f64, c.re, 1.423679, epsilon)); |
lib/std/math/complex/atanh.zig+3-3| ... | @@ -12,15 +12,15 @@ const Complex = cmath.Complex; | ... | @@ -12,15 +12,15 @@ const Complex = cmath.Complex; |
| 12 | /// Returns the hyperbolic arc-tangent of z. | 12 | /// Returns the hyperbolic arc-tangent of z. |
| 13 | pub fn atanh(z: anytype) Complex(@TypeOf(z.re)) { | 13 | pub fn atanh(z: anytype) Complex(@TypeOf(z.re)) { |
| 14 | const T = @TypeOf(z.re); | 14 | const T = @TypeOf(z.re); |
| 15 | const q = Complex(T).new(-z.im, z.re); | 15 | const q = Complex(T).init(-z.im, z.re); |
| 16 | const r = cmath.atan(q); | 16 | const r = cmath.atan(q); |
| 17 | return Complex(T).new(r.im, -r.re); | 17 | return Complex(T).init(r.im, -r.re); |
| 18 | } | 18 | } |
| 19 | 19 | ||
| 20 | const epsilon = 0.0001; | 20 | const epsilon = 0.0001; |
| 21 | 21 | ||
| 22 | test "complex.catanh" { | 22 | test "complex.catanh" { |
| 23 | const a = Complex(f32).new(5, 3); | 23 | const a = Complex(f32).init(5, 3); |
| 24 | const c = atanh(a); | 24 | const c = atanh(a); |
| 25 | 25 | ||
| 26 | try testing.expect(math.approxEqAbs(f32, c.re, 0.146947, epsilon)); | 26 | try testing.expect(math.approxEqAbs(f32, c.re, 0.146947, epsilon)); |
lib/std/math/complex/conj.zig+2-2| ... | @@ -12,11 +12,11 @@ const Complex = cmath.Complex; | ... | @@ -12,11 +12,11 @@ const Complex = cmath.Complex; |
| 12 | /// Returns the complex conjugate of z. | 12 | /// Returns the complex conjugate of z. |
| 13 | pub fn conj(z: anytype) Complex(@TypeOf(z.re)) { | 13 | pub fn conj(z: anytype) Complex(@TypeOf(z.re)) { |
| 14 | const T = @TypeOf(z.re); | 14 | const T = @TypeOf(z.re); |
| 15 | return Complex(T).new(z.re, -z.im); | 15 | return Complex(T).init(z.re, -z.im); |
| 16 | } | 16 | } |
| 17 | 17 | ||
| 18 | test "complex.conj" { | 18 | test "complex.conj" { |
| 19 | const a = Complex(f32).new(5, 3); | 19 | const a = Complex(f32).init(5, 3); |
| 20 | const c = a.conjugate(); | 20 | const c = a.conjugate(); |
| 21 | 21 | ||
| 22 | try testing.expect(c.re == 5 and c.im == -3); | 22 | try testing.expect(c.re == 5 and c.im == -3); |
lib/std/math/complex/cos.zig+2-2| ... | @@ -12,14 +12,14 @@ const Complex = cmath.Complex; | ... | @@ -12,14 +12,14 @@ const Complex = cmath.Complex; |
| 12 | /// Returns the cosine of z. | 12 | /// Returns the cosine of z. |
| 13 | pub fn cos(z: anytype) Complex(@TypeOf(z.re)) { | 13 | pub fn cos(z: anytype) Complex(@TypeOf(z.re)) { |
| 14 | const T = @TypeOf(z.re); | 14 | const T = @TypeOf(z.re); |
| 15 | const p = Complex(T).new(-z.im, z.re); | 15 | const p = Complex(T).init(-z.im, z.re); |
| 16 | return cmath.cosh(p); | 16 | return cmath.cosh(p); |
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | const epsilon = 0.0001; | 19 | const epsilon = 0.0001; |
| 20 | 20 | ||
| 21 | test "complex.ccos" { | 21 | test "complex.ccos" { |
| 22 | const a = Complex(f32).new(5, 3); | 22 | const a = Complex(f32).init(5, 3); |
| 23 | const c = cos(a); | 23 | const c = cos(a); |
| 24 | 24 | ||
| 25 | try testing.expect(math.approxEqAbs(f32, c.re, 2.855815, epsilon)); | 25 | try testing.expect(math.approxEqAbs(f32, c.re, 2.855815, epsilon)); |
lib/std/math/complex/cosh.zig+28-28| ... | @@ -39,55 +39,55 @@ fn cosh32(z: Complex(f32)) Complex(f32) { | ... | @@ -39,55 +39,55 @@ fn cosh32(z: Complex(f32)) Complex(f32) { |
| 39 | 39 | ||
| 40 | if (ix < 0x7f800000 and iy < 0x7f800000) { | 40 | if (ix < 0x7f800000 and iy < 0x7f800000) { |
| 41 | if (iy == 0) { | 41 | if (iy == 0) { |
| 42 | return Complex(f32).new(math.cosh(x), y); | 42 | return Complex(f32).init(math.cosh(x), y); |
| 43 | } | 43 | } |
| 44 | // small x: normal case | 44 | // small x: normal case |
| 45 | if (ix < 0x41100000) { | 45 | if (ix < 0x41100000) { |
| 46 | return Complex(f32).new(math.cosh(x) * math.cos(y), math.sinh(x) * math.sin(y)); | 46 | return Complex(f32).init(math.cosh(x) * math.cos(y), math.sinh(x) * math.sin(y)); |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | // |x|>= 9, so cosh(x) ~= exp(|x|) | 49 | // |x|>= 9, so cosh(x) ~= exp(|x|) |
| 50 | if (ix < 0x42b17218) { | 50 | if (ix < 0x42b17218) { |
| 51 | // x < 88.7: exp(|x|) won't overflow | 51 | // x < 88.7: exp(|x|) won't overflow |
| 52 | const h = math.exp(math.fabs(x)) * 0.5; | 52 | const h = math.exp(math.fabs(x)) * 0.5; |
| 53 | return Complex(f32).new(math.copysign(f32, h, x) * math.cos(y), h * math.sin(y)); | 53 | return Complex(f32).init(math.copysign(f32, h, x) * math.cos(y), h * math.sin(y)); |
| 54 | } | 54 | } |
| 55 | // x < 192.7: scale to avoid overflow | 55 | // x < 192.7: scale to avoid overflow |
| 56 | else if (ix < 0x4340b1e7) { | 56 | else if (ix < 0x4340b1e7) { |
| 57 | const v = Complex(f32).new(math.fabs(x), y); | 57 | const v = Complex(f32).init(math.fabs(x), y); |
| 58 | const r = ldexp_cexp(v, -1); | 58 | const r = ldexp_cexp(v, -1); |
| 59 | return Complex(f32).new(r.re, r.im * math.copysign(f32, 1, x)); | 59 | return Complex(f32).init(r.re, r.im * math.copysign(f32, 1, x)); |
| 60 | } | 60 | } |
| 61 | // x >= 192.7: result always overflows | 61 | // x >= 192.7: result always overflows |
| 62 | else { | 62 | else { |
| 63 | const h = 0x1p127 * x; | 63 | const h = 0x1p127 * x; |
| 64 | return Complex(f32).new(h * h * math.cos(y), h * math.sin(y)); | 64 | return Complex(f32).init(h * h * math.cos(y), h * math.sin(y)); |
| 65 | } | 65 | } |
| 66 | } | 66 | } |
| 67 | 67 | ||
| 68 | if (ix == 0 and iy >= 0x7f800000) { | 68 | if (ix == 0 and iy >= 0x7f800000) { |
| 69 | return Complex(f32).new(y - y, math.copysign(f32, 0, x * (y - y))); | 69 | return Complex(f32).init(y - y, math.copysign(f32, 0, x * (y - y))); |
| 70 | } | 70 | } |
| 71 | 71 | ||
| 72 | if (iy == 0 and ix >= 0x7f800000) { | 72 | if (iy == 0 and ix >= 0x7f800000) { |
| 73 | if (hx & 0x7fffff == 0) { | 73 | if (hx & 0x7fffff == 0) { |
| 74 | return Complex(f32).new(x * x, math.copysign(f32, 0, x) * y); | 74 | return Complex(f32).init(x * x, math.copysign(f32, 0, x) * y); |
| 75 | } | 75 | } |
| 76 | return Complex(f32).new(x, math.copysign(f32, 0, (x + x) * y)); | 76 | return Complex(f32).init(x, math.copysign(f32, 0, (x + x) * y)); |
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | if (ix < 0x7f800000 and iy >= 0x7f800000) { | 79 | if (ix < 0x7f800000 and iy >= 0x7f800000) { |
| 80 | return Complex(f32).new(y - y, x * (y - y)); | 80 | return Complex(f32).init(y - y, x * (y - y)); |
| 81 | } | 81 | } |
| 82 | 82 | ||
| 83 | if (ix >= 0x7f800000 and (hx & 0x7fffff) == 0) { | 83 | if (ix >= 0x7f800000 and (hx & 0x7fffff) == 0) { |
| 84 | if (iy >= 0x7f800000) { | 84 | if (iy >= 0x7f800000) { |
| 85 | return Complex(f32).new(x * x, x * (y - y)); | 85 | return Complex(f32).init(x * x, x * (y - y)); |
| 86 | } | 86 | } |
| 87 | return Complex(f32).new((x * x) * math.cos(y), x * math.sin(y)); | 87 | return Complex(f32).init((x * x) * math.cos(y), x * math.sin(y)); |
| 88 | } | 88 | } |
| 89 | 89 | ||
| 90 | return Complex(f32).new((x * x) * (y - y), (x + x) * (y - y)); | 90 | return Complex(f32).init((x * x) * (y - y), (x + x) * (y - y)); |
| 91 | } | 91 | } |
| 92 | 92 | ||
| 93 | fn cosh64(z: Complex(f64)) Complex(f64) { | 93 | fn cosh64(z: Complex(f64)) Complex(f64) { |
| ... | @@ -107,61 +107,61 @@ fn cosh64(z: Complex(f64)) Complex(f64) { | ... | @@ -107,61 +107,61 @@ fn cosh64(z: Complex(f64)) Complex(f64) { |
| 107 | // nearly non-exceptional case where x, y are finite | 107 | // nearly non-exceptional case where x, y are finite |
| 108 | if (ix < 0x7ff00000 and iy < 0x7ff00000) { | 108 | if (ix < 0x7ff00000 and iy < 0x7ff00000) { |
| 109 | if (iy | ly == 0) { | 109 | if (iy | ly == 0) { |
| 110 | return Complex(f64).new(math.cosh(x), x * y); | 110 | return Complex(f64).init(math.cosh(x), x * y); |
| 111 | } | 111 | } |
| 112 | // small x: normal case | 112 | // small x: normal case |
| 113 | if (ix < 0x40360000) { | 113 | if (ix < 0x40360000) { |
| 114 | return Complex(f64).new(math.cosh(x) * math.cos(y), math.sinh(x) * math.sin(y)); | 114 | return Complex(f64).init(math.cosh(x) * math.cos(y), math.sinh(x) * math.sin(y)); |
| 115 | } | 115 | } |
| 116 | 116 | ||
| 117 | // |x|>= 22, so cosh(x) ~= exp(|x|) | 117 | // |x|>= 22, so cosh(x) ~= exp(|x|) |
| 118 | if (ix < 0x40862e42) { | 118 | if (ix < 0x40862e42) { |
| 119 | // x < 710: exp(|x|) won't overflow | 119 | // x < 710: exp(|x|) won't overflow |
| 120 | const h = math.exp(math.fabs(x)) * 0.5; | 120 | const h = math.exp(math.fabs(x)) * 0.5; |
| 121 | return Complex(f64).new(h * math.cos(y), math.copysign(f64, h, x) * math.sin(y)); | 121 | return Complex(f64).init(h * math.cos(y), math.copysign(f64, h, x) * math.sin(y)); |
| 122 | } | 122 | } |
| 123 | // x < 1455: scale to avoid overflow | 123 | // x < 1455: scale to avoid overflow |
| 124 | else if (ix < 0x4096bbaa) { | 124 | else if (ix < 0x4096bbaa) { |
| 125 | const v = Complex(f64).new(math.fabs(x), y); | 125 | const v = Complex(f64).init(math.fabs(x), y); |
| 126 | const r = ldexp_cexp(v, -1); | 126 | const r = ldexp_cexp(v, -1); |
| 127 | return Complex(f64).new(r.re, r.im * math.copysign(f64, 1, x)); | 127 | return Complex(f64).init(r.re, r.im * math.copysign(f64, 1, x)); |
| 128 | } | 128 | } |
| 129 | // x >= 1455: result always overflows | 129 | // x >= 1455: result always overflows |
| 130 | else { | 130 | else { |
| 131 | const h = 0x1p1023; | 131 | const h = 0x1p1023; |
| 132 | return Complex(f64).new(h * h * math.cos(y), h * math.sin(y)); | 132 | return Complex(f64).init(h * h * math.cos(y), h * math.sin(y)); |
| 133 | } | 133 | } |
| 134 | } | 134 | } |
| 135 | 135 | ||
| 136 | if (ix | lx == 0 and iy >= 0x7ff00000) { | 136 | if (ix | lx == 0 and iy >= 0x7ff00000) { |
| 137 | return Complex(f64).new(y - y, math.copysign(f64, 0, x * (y - y))); | 137 | return Complex(f64).init(y - y, math.copysign(f64, 0, x * (y - y))); |
| 138 | } | 138 | } |
| 139 | 139 | ||
| 140 | if (iy | ly == 0 and ix >= 0x7ff00000) { | 140 | if (iy | ly == 0 and ix >= 0x7ff00000) { |
| 141 | if ((hx & 0xfffff) | lx == 0) { | 141 | if ((hx & 0xfffff) | lx == 0) { |
| 142 | return Complex(f64).new(x * x, math.copysign(f64, 0, x) * y); | 142 | return Complex(f64).init(x * x, math.copysign(f64, 0, x) * y); |
| 143 | } | 143 | } |
| 144 | return Complex(f64).new(x * x, math.copysign(f64, 0, (x + x) * y)); | 144 | return Complex(f64).init(x * x, math.copysign(f64, 0, (x + x) * y)); |
| 145 | } | 145 | } |
| 146 | 146 | ||
| 147 | if (ix < 0x7ff00000 and iy >= 0x7ff00000) { | 147 | if (ix < 0x7ff00000 and iy >= 0x7ff00000) { |
| 148 | return Complex(f64).new(y - y, x * (y - y)); | 148 | return Complex(f64).init(y - y, x * (y - y)); |
| 149 | } | 149 | } |
| 150 | 150 | ||
| 151 | if (ix >= 0x7ff00000 and (hx & 0xfffff) | lx == 0) { | 151 | if (ix >= 0x7ff00000 and (hx & 0xfffff) | lx == 0) { |
| 152 | if (iy >= 0x7ff00000) { | 152 | if (iy >= 0x7ff00000) { |
| 153 | return Complex(f64).new(x * x, x * (y - y)); | 153 | return Complex(f64).init(x * x, x * (y - y)); |
| 154 | } | 154 | } |
| 155 | return Complex(f64).new(x * x * math.cos(y), x * math.sin(y)); | 155 | return Complex(f64).init(x * x * math.cos(y), x * math.sin(y)); |
| 156 | } | 156 | } |
| 157 | 157 | ||
| 158 | return Complex(f64).new((x * x) * (y - y), (x + x) * (y - y)); | 158 | return Complex(f64).init((x * x) * (y - y), (x + x) * (y - y)); |
| 159 | } | 159 | } |
| 160 | 160 | ||
| 161 | const epsilon = 0.0001; | 161 | const epsilon = 0.0001; |
| 162 | 162 | ||
| 163 | test "complex.ccosh32" { | 163 | test "complex.ccosh32" { |
| 164 | const a = Complex(f32).new(5, 3); | 164 | const a = Complex(f32).init(5, 3); |
| 165 | const c = cosh(a); | 165 | const c = cosh(a); |
| 166 | 166 | ||
| 167 | try testing.expect(math.approxEqAbs(f32, c.re, -73.467300, epsilon)); | 167 | try testing.expect(math.approxEqAbs(f32, c.re, -73.467300, epsilon)); |
| ... | @@ -169,7 +169,7 @@ test "complex.ccosh32" { | ... | @@ -169,7 +169,7 @@ test "complex.ccosh32" { |
| 169 | } | 169 | } |
| 170 | 170 | ||
| 171 | test "complex.ccosh64" { | 171 | test "complex.ccosh64" { |
| 172 | const a = Complex(f64).new(5, 3); | 172 | const a = Complex(f64).init(5, 3); |
| 173 | const c = cosh(a); | 173 | const c = cosh(a); |
| 174 | 174 | ||
| 175 | try testing.expect(math.approxEqAbs(f64, c.re, -73.467300, epsilon)); | 175 | try testing.expect(math.approxEqAbs(f64, c.re, -73.467300, epsilon)); |
lib/std/math/complex/exp.zig+14-14| ... | @@ -38,25 +38,25 @@ fn exp32(z: Complex(f32)) Complex(f32) { | ... | @@ -38,25 +38,25 @@ fn exp32(z: Complex(f32)) Complex(f32) { |
| 38 | const hy = @bitCast(u32, y) & 0x7fffffff; | 38 | const hy = @bitCast(u32, y) & 0x7fffffff; |
| 39 | // cexp(x + i0) = exp(x) + i0 | 39 | // cexp(x + i0) = exp(x) + i0 |
| 40 | if (hy == 0) { | 40 | if (hy == 0) { |
| 41 | return Complex(f32).new(math.exp(x), y); | 41 | return Complex(f32).init(math.exp(x), y); |
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | const hx = @bitCast(u32, x); | 44 | const hx = @bitCast(u32, x); |
| 45 | // cexp(0 + iy) = cos(y) + isin(y) | 45 | // cexp(0 + iy) = cos(y) + isin(y) |
| 46 | if ((hx & 0x7fffffff) == 0) { | 46 | if ((hx & 0x7fffffff) == 0) { |
| 47 | return Complex(f32).new(math.cos(y), math.sin(y)); | 47 | return Complex(f32).init(math.cos(y), math.sin(y)); |
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | if (hy >= 0x7f800000) { | 50 | if (hy >= 0x7f800000) { |
| 51 | // cexp(finite|nan +- i inf|nan) = nan + i nan | 51 | // cexp(finite|nan +- i inf|nan) = nan + i nan |
| 52 | if ((hx & 0x7fffffff) != 0x7f800000) { | 52 | if ((hx & 0x7fffffff) != 0x7f800000) { |
| 53 | return Complex(f32).new(y - y, y - y); | 53 | return Complex(f32).init(y - y, y - y); |
| 54 | } // cexp(-inf +- i inf|nan) = 0 + i0 | 54 | } // cexp(-inf +- i inf|nan) = 0 + i0 |
| 55 | else if (hx & 0x80000000 != 0) { | 55 | else if (hx & 0x80000000 != 0) { |
| 56 | return Complex(f32).new(0, 0); | 56 | return Complex(f32).init(0, 0); |
| 57 | } // cexp(+inf +- i inf|nan) = inf + i nan | 57 | } // cexp(+inf +- i inf|nan) = inf + i nan |
| 58 | else { | 58 | else { |
| 59 | return Complex(f32).new(x, y - y); | 59 | return Complex(f32).init(x, y - y); |
| 60 | } | 60 | } |
| 61 | } | 61 | } |
| 62 | 62 | ||
| ... | @@ -69,7 +69,7 @@ fn exp32(z: Complex(f32)) Complex(f32) { | ... | @@ -69,7 +69,7 @@ fn exp32(z: Complex(f32)) Complex(f32) { |
| 69 | // - x = nan | 69 | // - x = nan |
| 70 | else { | 70 | else { |
| 71 | const exp_x = math.exp(x); | 71 | const exp_x = math.exp(x); |
| 72 | return Complex(f32).new(exp_x * math.cos(y), exp_x * math.sin(y)); | 72 | return Complex(f32).init(exp_x * math.cos(y), exp_x * math.sin(y)); |
| 73 | } | 73 | } |
| 74 | } | 74 | } |
| 75 | 75 | ||
| ... | @@ -86,7 +86,7 @@ fn exp64(z: Complex(f64)) Complex(f64) { | ... | @@ -86,7 +86,7 @@ fn exp64(z: Complex(f64)) Complex(f64) { |
| 86 | 86 | ||
| 87 | // cexp(x + i0) = exp(x) + i0 | 87 | // cexp(x + i0) = exp(x) + i0 |
| 88 | if (hy | ly == 0) { | 88 | if (hy | ly == 0) { |
| 89 | return Complex(f64).new(math.exp(x), y); | 89 | return Complex(f64).init(math.exp(x), y); |
| 90 | } | 90 | } |
| 91 | 91 | ||
| 92 | const fx = @bitCast(u64, x); | 92 | const fx = @bitCast(u64, x); |
| ... | @@ -95,19 +95,19 @@ fn exp64(z: Complex(f64)) Complex(f64) { | ... | @@ -95,19 +95,19 @@ fn exp64(z: Complex(f64)) Complex(f64) { |
| 95 | 95 | ||
| 96 | // cexp(0 + iy) = cos(y) + isin(y) | 96 | // cexp(0 + iy) = cos(y) + isin(y) |
| 97 | if ((hx & 0x7fffffff) | lx == 0) { | 97 | if ((hx & 0x7fffffff) | lx == 0) { |
| 98 | return Complex(f64).new(math.cos(y), math.sin(y)); | 98 | return Complex(f64).init(math.cos(y), math.sin(y)); |
| 99 | } | 99 | } |
| 100 | 100 | ||
| 101 | if (hy >= 0x7ff00000) { | 101 | if (hy >= 0x7ff00000) { |
| 102 | // cexp(finite|nan +- i inf|nan) = nan + i nan | 102 | // cexp(finite|nan +- i inf|nan) = nan + i nan |
| 103 | if (lx != 0 or (hx & 0x7fffffff) != 0x7ff00000) { | 103 | if (lx != 0 or (hx & 0x7fffffff) != 0x7ff00000) { |
| 104 | return Complex(f64).new(y - y, y - y); | 104 | return Complex(f64).init(y - y, y - y); |
| 105 | } // cexp(-inf +- i inf|nan) = 0 + i0 | 105 | } // cexp(-inf +- i inf|nan) = 0 + i0 |
| 106 | else if (hx & 0x80000000 != 0) { | 106 | else if (hx & 0x80000000 != 0) { |
| 107 | return Complex(f64).new(0, 0); | 107 | return Complex(f64).init(0, 0); |
| 108 | } // cexp(+inf +- i inf|nan) = inf + i nan | 108 | } // cexp(+inf +- i inf|nan) = inf + i nan |
| 109 | else { | 109 | else { |
| 110 | return Complex(f64).new(x, y - y); | 110 | return Complex(f64).init(x, y - y); |
| 111 | } | 111 | } |
| 112 | } | 112 | } |
| 113 | 113 | ||
| ... | @@ -120,14 +120,14 @@ fn exp64(z: Complex(f64)) Complex(f64) { | ... | @@ -120,14 +120,14 @@ fn exp64(z: Complex(f64)) Complex(f64) { |
| 120 | // - x = nan | 120 | // - x = nan |
| 121 | else { | 121 | else { |
| 122 | const exp_x = math.exp(x); | 122 | const exp_x = math.exp(x); |
| 123 | return Complex(f64).new(exp_x * math.cos(y), exp_x * math.sin(y)); | 123 | return Complex(f64).init(exp_x * math.cos(y), exp_x * math.sin(y)); |
| 124 | } | 124 | } |
| 125 | } | 125 | } |
| 126 | 126 | ||
| 127 | const epsilon = 0.0001; | 127 | const epsilon = 0.0001; |
| 128 | 128 | ||
| 129 | test "complex.cexp32" { | 129 | test "complex.cexp32" { |
| 130 | const a = Complex(f32).new(5, 3); | 130 | const a = Complex(f32).init(5, 3); |
| 131 | const c = exp(a); | 131 | const c = exp(a); |
| 132 | 132 | ||
| 133 | try testing.expect(math.approxEqAbs(f32, c.re, -146.927917, epsilon)); | 133 | try testing.expect(math.approxEqAbs(f32, c.re, -146.927917, epsilon)); |
| ... | @@ -135,7 +135,7 @@ test "complex.cexp32" { | ... | @@ -135,7 +135,7 @@ test "complex.cexp32" { |
| 135 | } | 135 | } |
| 136 | 136 | ||
| 137 | test "complex.cexp64" { | 137 | test "complex.cexp64" { |
| 138 | const a = Complex(f64).new(5, 3); | 138 | const a = Complex(f64).init(5, 3); |
| 139 | const c = exp(a); | 139 | const c = exp(a); |
| 140 | 140 | ||
| 141 | try testing.expect(math.approxEqAbs(f64, c.re, -146.927917, epsilon)); | 141 | try testing.expect(math.approxEqAbs(f64, c.re, -146.927917, epsilon)); |
lib/std/math/complex/ldexp.zig+2-2| ... | @@ -48,7 +48,7 @@ fn ldexp_cexp32(z: Complex(f32), expt: i32) Complex(f32) { | ... | @@ -48,7 +48,7 @@ fn ldexp_cexp32(z: Complex(f32), expt: i32) Complex(f32) { |
| 48 | const half_expt2 = exptf - half_expt1; | 48 | const half_expt2 = exptf - half_expt1; |
| 49 | const scale2 = @bitCast(f32, (0x7f + half_expt2) << 23); | 49 | const scale2 = @bitCast(f32, (0x7f + half_expt2) << 23); |
| 50 | 50 | ||
| 51 | return Complex(f32).new(math.cos(z.im) * exp_x * scale1 * scale2, math.sin(z.im) * exp_x * scale1 * scale2); | 51 | return Complex(f32).init(math.cos(z.im) * exp_x * scale1 * scale2, math.sin(z.im) * exp_x * scale1 * scale2); |
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | fn frexp_exp64(x: f64, expt: *i32) f64 { | 54 | fn frexp_exp64(x: f64, expt: *i32) f64 { |
| ... | @@ -78,7 +78,7 @@ fn ldexp_cexp64(z: Complex(f64), expt: i32) Complex(f64) { | ... | @@ -78,7 +78,7 @@ fn ldexp_cexp64(z: Complex(f64), expt: i32) Complex(f64) { |
| 78 | const half_expt2 = exptf - half_expt1; | 78 | const half_expt2 = exptf - half_expt1; |
| 79 | const scale2 = @bitCast(f64, (0x3ff + half_expt2) << 20); | 79 | const scale2 = @bitCast(f64, (0x3ff + half_expt2) << 20); |
| 80 | 80 | ||
| 81 | return Complex(f64).new( | 81 | return Complex(f64).init( |
| 82 | math.cos(z.im) * exp_x * scale1 * scale2, | 82 | math.cos(z.im) * exp_x * scale1 * scale2, |
| 83 | math.sin(z.im) * exp_x * scale1 * scale2, | 83 | math.sin(z.im) * exp_x * scale1 * scale2, |
| 84 | ); | 84 | ); |
lib/std/math/complex/log.zig+2-2| ... | @@ -15,13 +15,13 @@ pub fn log(z: anytype) Complex(@TypeOf(z.re)) { | ... | @@ -15,13 +15,13 @@ pub fn log(z: anytype) Complex(@TypeOf(z.re)) { |
| 15 | const r = cmath.abs(z); | 15 | const r = cmath.abs(z); |
| 16 | const phi = cmath.arg(z); | 16 | const phi = cmath.arg(z); |
| 17 | 17 | ||
| 18 | return Complex(T).new(math.ln(r), phi); | 18 | return Complex(T).init(math.ln(r), phi); |
| 19 | } | 19 | } |
| 20 | 20 | ||
| 21 | const epsilon = 0.0001; | 21 | const epsilon = 0.0001; |
| 22 | 22 | ||
| 23 | test "complex.clog" { | 23 | test "complex.clog" { |
| 24 | const a = Complex(f32).new(5, 3); | 24 | const a = Complex(f32).init(5, 3); |
| 25 | const c = log(a); | 25 | const c = log(a); |
| 26 | 26 | ||
| 27 | try testing.expect(math.approxEqAbs(f32, c.re, 1.763180, epsilon)); | 27 | try testing.expect(math.approxEqAbs(f32, c.re, 1.763180, epsilon)); |
lib/std/math/complex/pow.zig+2-2| ... | @@ -19,8 +19,8 @@ pub fn pow(comptime T: type, z: T, c: T) T { | ... | @@ -19,8 +19,8 @@ pub fn pow(comptime T: type, z: T, c: T) T { |
| 19 | const epsilon = 0.0001; | 19 | const epsilon = 0.0001; |
| 20 | 20 | ||
| 21 | test "complex.cpow" { | 21 | test "complex.cpow" { |
| 22 | const a = Complex(f32).new(5, 3); | 22 | const a = Complex(f32).init(5, 3); |
| 23 | const b = Complex(f32).new(2.3, -1.3); | 23 | const b = Complex(f32).init(2.3, -1.3); |
| 24 | const c = pow(Complex(f32), a, b); | 24 | const c = pow(Complex(f32), a, b); |
| 25 | 25 | ||
| 26 | try testing.expect(math.approxEqAbs(f32, c.re, 58.049110, epsilon)); | 26 | try testing.expect(math.approxEqAbs(f32, c.re, 58.049110, epsilon)); |
lib/std/math/complex/proj.zig+3-3| ... | @@ -14,16 +14,16 @@ pub fn proj(z: anytype) Complex(@TypeOf(z.re)) { | ... | @@ -14,16 +14,16 @@ pub fn proj(z: anytype) Complex(@TypeOf(z.re)) { |
| 14 | const T = @TypeOf(z.re); | 14 | const T = @TypeOf(z.re); |
| 15 | 15 | ||
| 16 | if (math.isInf(z.re) or math.isInf(z.im)) { | 16 | if (math.isInf(z.re) or math.isInf(z.im)) { |
| 17 | return Complex(T).new(math.inf(T), math.copysign(T, 0, z.re)); | 17 | return Complex(T).init(math.inf(T), math.copysign(T, 0, z.re)); |
| 18 | } | 18 | } |
| 19 | 19 | ||
| 20 | return Complex(T).new(z.re, z.im); | 20 | return Complex(T).init(z.re, z.im); |
| 21 | } | 21 | } |
| 22 | 22 | ||
| 23 | const epsilon = 0.0001; | 23 | const epsilon = 0.0001; |
| 24 | 24 | ||
| 25 | test "complex.cproj" { | 25 | test "complex.cproj" { |
| 26 | const a = Complex(f32).new(5, 3); | 26 | const a = Complex(f32).init(5, 3); |
| 27 | const c = proj(a); | 27 | const c = proj(a); |
| 28 | 28 | ||
| 29 | try testing.expect(c.re == 5 and c.im == 3); | 29 | try testing.expect(c.re == 5 and c.im == 3); |
lib/std/math/complex/sin.zig+3-3| ... | @@ -12,15 +12,15 @@ const Complex = cmath.Complex; | ... | @@ -12,15 +12,15 @@ const Complex = cmath.Complex; |
| 12 | /// Returns the sine of z. | 12 | /// Returns the sine of z. |
| 13 | pub fn sin(z: anytype) Complex(@TypeOf(z.re)) { | 13 | pub fn sin(z: anytype) Complex(@TypeOf(z.re)) { |
| 14 | const T = @TypeOf(z.re); | 14 | const T = @TypeOf(z.re); |
| 15 | const p = Complex(T).new(-z.im, z.re); | 15 | const p = Complex(T).init(-z.im, z.re); |
| 16 | const q = cmath.sinh(p); | 16 | const q = cmath.sinh(p); |
| 17 | return Complex(T).new(q.im, -q.re); | 17 | return Complex(T).init(q.im, -q.re); |
| 18 | } | 18 | } |
| 19 | 19 | ||
| 20 | const epsilon = 0.0001; | 20 | const epsilon = 0.0001; |
| 21 | 21 | ||
| 22 | test "complex.csin" { | 22 | test "complex.csin" { |
| 23 | const a = Complex(f32).new(5, 3); | 23 | const a = Complex(f32).init(5, 3); |
| 24 | const c = sin(a); | 24 | const c = sin(a); |
| 25 | 25 | ||
| 26 | try testing.expect(math.approxEqAbs(f32, c.re, -9.654126, epsilon)); | 26 | try testing.expect(math.approxEqAbs(f32, c.re, -9.654126, epsilon)); |
lib/std/math/complex/sinh.zig+28-28| ... | @@ -39,55 +39,55 @@ fn sinh32(z: Complex(f32)) Complex(f32) { | ... | @@ -39,55 +39,55 @@ fn sinh32(z: Complex(f32)) Complex(f32) { |
| 39 | 39 | ||
| 40 | if (ix < 0x7f800000 and iy < 0x7f800000) { | 40 | if (ix < 0x7f800000 and iy < 0x7f800000) { |
| 41 | if (iy == 0) { | 41 | if (iy == 0) { |
| 42 | return Complex(f32).new(math.sinh(x), y); | 42 | return Complex(f32).init(math.sinh(x), y); |
| 43 | } | 43 | } |
| 44 | // small x: normal case | 44 | // small x: normal case |
| 45 | if (ix < 0x41100000) { | 45 | if (ix < 0x41100000) { |
| 46 | return Complex(f32).new(math.sinh(x) * math.cos(y), math.cosh(x) * math.sin(y)); | 46 | return Complex(f32).init(math.sinh(x) * math.cos(y), math.cosh(x) * math.sin(y)); |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | // |x|>= 9, so cosh(x) ~= exp(|x|) | 49 | // |x|>= 9, so cosh(x) ~= exp(|x|) |
| 50 | if (ix < 0x42b17218) { | 50 | if (ix < 0x42b17218) { |
| 51 | // x < 88.7: exp(|x|) won't overflow | 51 | // x < 88.7: exp(|x|) won't overflow |
| 52 | const h = math.exp(math.fabs(x)) * 0.5; | 52 | const h = math.exp(math.fabs(x)) * 0.5; |
| 53 | return Complex(f32).new(math.copysign(f32, h, x) * math.cos(y), h * math.sin(y)); | 53 | return Complex(f32).init(math.copysign(f32, h, x) * math.cos(y), h * math.sin(y)); |
| 54 | } | 54 | } |
| 55 | // x < 192.7: scale to avoid overflow | 55 | // x < 192.7: scale to avoid overflow |
| 56 | else if (ix < 0x4340b1e7) { | 56 | else if (ix < 0x4340b1e7) { |
| 57 | const v = Complex(f32).new(math.fabs(x), y); | 57 | const v = Complex(f32).init(math.fabs(x), y); |
| 58 | const r = ldexp_cexp(v, -1); | 58 | const r = ldexp_cexp(v, -1); |
| 59 | return Complex(f32).new(r.re * math.copysign(f32, 1, x), r.im); | 59 | return Complex(f32).init(r.re * math.copysign(f32, 1, x), r.im); |
| 60 | } | 60 | } |
| 61 | // x >= 192.7: result always overflows | 61 | // x >= 192.7: result always overflows |
| 62 | else { | 62 | else { |
| 63 | const h = 0x1p127 * x; | 63 | const h = 0x1p127 * x; |
| 64 | return Complex(f32).new(h * math.cos(y), h * h * math.sin(y)); | 64 | return Complex(f32).init(h * math.cos(y), h * h * math.sin(y)); |
| 65 | } | 65 | } |
| 66 | } | 66 | } |
| 67 | 67 | ||
| 68 | if (ix == 0 and iy >= 0x7f800000) { | 68 | if (ix == 0 and iy >= 0x7f800000) { |
| 69 | return Complex(f32).new(math.copysign(f32, 0, x * (y - y)), y - y); | 69 | return Complex(f32).init(math.copysign(f32, 0, x * (y - y)), y - y); |
| 70 | } | 70 | } |
| 71 | 71 | ||
| 72 | if (iy == 0 and ix >= 0x7f800000) { | 72 | if (iy == 0 and ix >= 0x7f800000) { |
| 73 | if (hx & 0x7fffff == 0) { | 73 | if (hx & 0x7fffff == 0) { |
| 74 | return Complex(f32).new(x, y); | 74 | return Complex(f32).init(x, y); |
| 75 | } | 75 | } |
| 76 | return Complex(f32).new(x, math.copysign(f32, 0, y)); | 76 | return Complex(f32).init(x, math.copysign(f32, 0, y)); |
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | if (ix < 0x7f800000 and iy >= 0x7f800000) { | 79 | if (ix < 0x7f800000 and iy >= 0x7f800000) { |
| 80 | return Complex(f32).new(y - y, x * (y - y)); | 80 | return Complex(f32).init(y - y, x * (y - y)); |
| 81 | } | 81 | } |
| 82 | 82 | ||
| 83 | if (ix >= 0x7f800000 and (hx & 0x7fffff) == 0) { | 83 | if (ix >= 0x7f800000 and (hx & 0x7fffff) == 0) { |
| 84 | if (iy >= 0x7f800000) { | 84 | if (iy >= 0x7f800000) { |
| 85 | return Complex(f32).new(x * x, x * (y - y)); | 85 | return Complex(f32).init(x * x, x * (y - y)); |
| 86 | } | 86 | } |
| 87 | return Complex(f32).new(x * math.cos(y), math.inf_f32 * math.sin(y)); | 87 | return Complex(f32).init(x * math.cos(y), math.inf_f32 * math.sin(y)); |
| 88 | } | 88 | } |
| 89 | 89 | ||
| 90 | return Complex(f32).new((x * x) * (y - y), (x + x) * (y - y)); | 90 | return Complex(f32).init((x * x) * (y - y), (x + x) * (y - y)); |
| 91 | } | 91 | } |
| 92 | 92 | ||
| 93 | fn sinh64(z: Complex(f64)) Complex(f64) { | 93 | fn sinh64(z: Complex(f64)) Complex(f64) { |
| ... | @@ -106,61 +106,61 @@ fn sinh64(z: Complex(f64)) Complex(f64) { | ... | @@ -106,61 +106,61 @@ fn sinh64(z: Complex(f64)) Complex(f64) { |
| 106 | 106 | ||
| 107 | if (ix < 0x7ff00000 and iy < 0x7ff00000) { | 107 | if (ix < 0x7ff00000 and iy < 0x7ff00000) { |
| 108 | if (iy | ly == 0) { | 108 | if (iy | ly == 0) { |
| 109 | return Complex(f64).new(math.sinh(x), y); | 109 | return Complex(f64).init(math.sinh(x), y); |
| 110 | } | 110 | } |
| 111 | // small x: normal case | 111 | // small x: normal case |
| 112 | if (ix < 0x40360000) { | 112 | if (ix < 0x40360000) { |
| 113 | return Complex(f64).new(math.sinh(x) * math.cos(y), math.cosh(x) * math.sin(y)); | 113 | return Complex(f64).init(math.sinh(x) * math.cos(y), math.cosh(x) * math.sin(y)); |
| 114 | } | 114 | } |
| 115 | 115 | ||
| 116 | // |x|>= 22, so cosh(x) ~= exp(|x|) | 116 | // |x|>= 22, so cosh(x) ~= exp(|x|) |
| 117 | if (ix < 0x40862e42) { | 117 | if (ix < 0x40862e42) { |
| 118 | // x < 710: exp(|x|) won't overflow | 118 | // x < 710: exp(|x|) won't overflow |
| 119 | const h = math.exp(math.fabs(x)) * 0.5; | 119 | const h = math.exp(math.fabs(x)) * 0.5; |
| 120 | return Complex(f64).new(math.copysign(f64, h, x) * math.cos(y), h * math.sin(y)); | 120 | return Complex(f64).init(math.copysign(f64, h, x) * math.cos(y), h * math.sin(y)); |
| 121 | } | 121 | } |
| 122 | // x < 1455: scale to avoid overflow | 122 | // x < 1455: scale to avoid overflow |
| 123 | else if (ix < 0x4096bbaa) { | 123 | else if (ix < 0x4096bbaa) { |
| 124 | const v = Complex(f64).new(math.fabs(x), y); | 124 | const v = Complex(f64).init(math.fabs(x), y); |
| 125 | const r = ldexp_cexp(v, -1); | 125 | const r = ldexp_cexp(v, -1); |
| 126 | return Complex(f64).new(r.re * math.copysign(f64, 1, x), r.im); | 126 | return Complex(f64).init(r.re * math.copysign(f64, 1, x), r.im); |
| 127 | } | 127 | } |
| 128 | // x >= 1455: result always overflows | 128 | // x >= 1455: result always overflows |
| 129 | else { | 129 | else { |
| 130 | const h = 0x1p1023 * x; | 130 | const h = 0x1p1023 * x; |
| 131 | return Complex(f64).new(h * math.cos(y), h * h * math.sin(y)); | 131 | return Complex(f64).init(h * math.cos(y), h * h * math.sin(y)); |
| 132 | } | 132 | } |
| 133 | } | 133 | } |
| 134 | 134 | ||
| 135 | if (ix | lx == 0 and iy >= 0x7ff00000) { | 135 | if (ix | lx == 0 and iy >= 0x7ff00000) { |
| 136 | return Complex(f64).new(math.copysign(f64, 0, x * (y - y)), y - y); | 136 | return Complex(f64).init(math.copysign(f64, 0, x * (y - y)), y - y); |
| 137 | } | 137 | } |
| 138 | 138 | ||
| 139 | if (iy | ly == 0 and ix >= 0x7ff00000) { | 139 | if (iy | ly == 0 and ix >= 0x7ff00000) { |
| 140 | if ((hx & 0xfffff) | lx == 0) { | 140 | if ((hx & 0xfffff) | lx == 0) { |
| 141 | return Complex(f64).new(x, y); | 141 | return Complex(f64).init(x, y); |
| 142 | } | 142 | } |
| 143 | return Complex(f64).new(x, math.copysign(f64, 0, y)); | 143 | return Complex(f64).init(x, math.copysign(f64, 0, y)); |
| 144 | } | 144 | } |
| 145 | 145 | ||
| 146 | if (ix < 0x7ff00000 and iy >= 0x7ff00000) { | 146 | if (ix < 0x7ff00000 and iy >= 0x7ff00000) { |
| 147 | return Complex(f64).new(y - y, x * (y - y)); | 147 | return Complex(f64).init(y - y, x * (y - y)); |
| 148 | } | 148 | } |
| 149 | 149 | ||
| 150 | if (ix >= 0x7ff00000 and (hx & 0xfffff) | lx == 0) { | 150 | if (ix >= 0x7ff00000 and (hx & 0xfffff) | lx == 0) { |
| 151 | if (iy >= 0x7ff00000) { | 151 | if (iy >= 0x7ff00000) { |
| 152 | return Complex(f64).new(x * x, x * (y - y)); | 152 | return Complex(f64).init(x * x, x * (y - y)); |
| 153 | } | 153 | } |
| 154 | return Complex(f64).new(x * math.cos(y), math.inf_f64 * math.sin(y)); | 154 | return Complex(f64).init(x * math.cos(y), math.inf_f64 * math.sin(y)); |
| 155 | } | 155 | } |
| 156 | 156 | ||
| 157 | return Complex(f64).new((x * x) * (y - y), (x + x) * (y - y)); | 157 | return Complex(f64).init((x * x) * (y - y), (x + x) * (y - y)); |
| 158 | } | 158 | } |
| 159 | 159 | ||
| 160 | const epsilon = 0.0001; | 160 | const epsilon = 0.0001; |
| 161 | 161 | ||
| 162 | test "complex.csinh32" { | 162 | test "complex.csinh32" { |
| 163 | const a = Complex(f32).new(5, 3); | 163 | const a = Complex(f32).init(5, 3); |
| 164 | const c = sinh(a); | 164 | const c = sinh(a); |
| 165 | 165 | ||
| 166 | try testing.expect(math.approxEqAbs(f32, c.re, -73.460617, epsilon)); | 166 | try testing.expect(math.approxEqAbs(f32, c.re, -73.460617, epsilon)); |
| ... | @@ -168,7 +168,7 @@ test "complex.csinh32" { | ... | @@ -168,7 +168,7 @@ test "complex.csinh32" { |
| 168 | } | 168 | } |
| 169 | 169 | ||
| 170 | test "complex.csinh64" { | 170 | test "complex.csinh64" { |
| 171 | const a = Complex(f64).new(5, 3); | 171 | const a = Complex(f64).init(5, 3); |
| 172 | const c = sinh(a); | 172 | const c = sinh(a); |
| 173 | 173 | ||
| 174 | try testing.expect(math.approxEqAbs(f64, c.re, -73.460617, epsilon)); | 174 | try testing.expect(math.approxEqAbs(f64, c.re, -73.460617, epsilon)); |
lib/std/math/complex/sqrt.zig+16-16| ... | @@ -32,15 +32,15 @@ fn sqrt32(z: Complex(f32)) Complex(f32) { | ... | @@ -32,15 +32,15 @@ fn sqrt32(z: Complex(f32)) Complex(f32) { |
| 32 | const y = z.im; | 32 | const y = z.im; |
| 33 | 33 | ||
| 34 | if (x == 0 and y == 0) { | 34 | if (x == 0 and y == 0) { |
| 35 | return Complex(f32).new(0, y); | 35 | return Complex(f32).init(0, y); |
| 36 | } | 36 | } |
| 37 | if (math.isInf(y)) { | 37 | if (math.isInf(y)) { |
| 38 | return Complex(f32).new(math.inf(f32), y); | 38 | return Complex(f32).init(math.inf(f32), y); |
| 39 | } | 39 | } |
| 40 | if (math.isNan(x)) { | 40 | if (math.isNan(x)) { |
| 41 | // raise invalid if y is not nan | 41 | // raise invalid if y is not nan |
| 42 | const t = (y - y) / (y - y); | 42 | const t = (y - y) / (y - y); |
| 43 | return Complex(f32).new(x, t); | 43 | return Complex(f32).init(x, t); |
| 44 | } | 44 | } |
| 45 | if (math.isInf(x)) { | 45 | if (math.isInf(x)) { |
| 46 | // sqrt(inf + i nan) = inf + nan i | 46 | // sqrt(inf + i nan) = inf + nan i |
| ... | @@ -48,9 +48,9 @@ fn sqrt32(z: Complex(f32)) Complex(f32) { | ... | @@ -48,9 +48,9 @@ fn sqrt32(z: Complex(f32)) Complex(f32) { |
| 48 | // sqrt(-inf + i nan) = nan +- inf i | 48 | // sqrt(-inf + i nan) = nan +- inf i |
| 49 | // sqrt(-inf + iy) = 0 + inf i | 49 | // sqrt(-inf + iy) = 0 + inf i |
| 50 | if (math.signbit(x)) { | 50 | if (math.signbit(x)) { |
| 51 | return Complex(f32).new(math.fabs(x - y), math.copysign(f32, x, y)); | 51 | return Complex(f32).init(math.fabs(x - y), math.copysign(f32, x, y)); |
| 52 | } else { | 52 | } else { |
| 53 | return Complex(f32).new(x, math.copysign(f32, y - y, y)); | 53 | return Complex(f32).init(x, math.copysign(f32, y - y, y)); |
| 54 | } | 54 | } |
| 55 | } | 55 | } |
| 56 | 56 | ||
| ... | @@ -62,13 +62,13 @@ fn sqrt32(z: Complex(f32)) Complex(f32) { | ... | @@ -62,13 +62,13 @@ fn sqrt32(z: Complex(f32)) Complex(f32) { |
| 62 | 62 | ||
| 63 | if (dx >= 0) { | 63 | if (dx >= 0) { |
| 64 | const t = math.sqrt((dx + math.hypot(f64, dx, dy)) * 0.5); | 64 | const t = math.sqrt((dx + math.hypot(f64, dx, dy)) * 0.5); |
| 65 | return Complex(f32).new( | 65 | return Complex(f32).init( |
| 66 | @floatCast(f32, t), | 66 | @floatCast(f32, t), |
| 67 | @floatCast(f32, dy / (2.0 * t)), | 67 | @floatCast(f32, dy / (2.0 * t)), |
| 68 | ); | 68 | ); |
| 69 | } else { | 69 | } else { |
| 70 | const t = math.sqrt((-dx + math.hypot(f64, dx, dy)) * 0.5); | 70 | const t = math.sqrt((-dx + math.hypot(f64, dx, dy)) * 0.5); |
| 71 | return Complex(f32).new( | 71 | return Complex(f32).init( |
| 72 | @floatCast(f32, math.fabs(y) / (2.0 * t)), | 72 | @floatCast(f32, math.fabs(y) / (2.0 * t)), |
| 73 | @floatCast(f32, math.copysign(f64, t, y)), | 73 | @floatCast(f32, math.copysign(f64, t, y)), |
| 74 | ); | 74 | ); |
| ... | @@ -83,15 +83,15 @@ fn sqrt64(z: Complex(f64)) Complex(f64) { | ... | @@ -83,15 +83,15 @@ fn sqrt64(z: Complex(f64)) Complex(f64) { |
| 83 | var y = z.im; | 83 | var y = z.im; |
| 84 | 84 | ||
| 85 | if (x == 0 and y == 0) { | 85 | if (x == 0 and y == 0) { |
| 86 | return Complex(f64).new(0, y); | 86 | return Complex(f64).init(0, y); |
| 87 | } | 87 | } |
| 88 | if (math.isInf(y)) { | 88 | if (math.isInf(y)) { |
| 89 | return Complex(f64).new(math.inf(f64), y); | 89 | return Complex(f64).init(math.inf(f64), y); |
| 90 | } | 90 | } |
| 91 | if (math.isNan(x)) { | 91 | if (math.isNan(x)) { |
| 92 | // raise invalid if y is not nan | 92 | // raise invalid if y is not nan |
| 93 | const t = (y - y) / (y - y); | 93 | const t = (y - y) / (y - y); |
| 94 | return Complex(f64).new(x, t); | 94 | return Complex(f64).init(x, t); |
| 95 | } | 95 | } |
| 96 | if (math.isInf(x)) { | 96 | if (math.isInf(x)) { |
| 97 | // sqrt(inf + i nan) = inf + nan i | 97 | // sqrt(inf + i nan) = inf + nan i |
| ... | @@ -99,9 +99,9 @@ fn sqrt64(z: Complex(f64)) Complex(f64) { | ... | @@ -99,9 +99,9 @@ fn sqrt64(z: Complex(f64)) Complex(f64) { |
| 99 | // sqrt(-inf + i nan) = nan +- inf i | 99 | // sqrt(-inf + i nan) = nan +- inf i |
| 100 | // sqrt(-inf + iy) = 0 + inf i | 100 | // sqrt(-inf + iy) = 0 + inf i |
| 101 | if (math.signbit(x)) { | 101 | if (math.signbit(x)) { |
| 102 | return Complex(f64).new(math.fabs(x - y), math.copysign(f64, x, y)); | 102 | return Complex(f64).init(math.fabs(x - y), math.copysign(f64, x, y)); |
| 103 | } else { | 103 | } else { |
| 104 | return Complex(f64).new(x, math.copysign(f64, y - y, y)); | 104 | return Complex(f64).init(x, math.copysign(f64, y - y, y)); |
| 105 | } | 105 | } |
| 106 | } | 106 | } |
| 107 | 107 | ||
| ... | @@ -118,10 +118,10 @@ fn sqrt64(z: Complex(f64)) Complex(f64) { | ... | @@ -118,10 +118,10 @@ fn sqrt64(z: Complex(f64)) Complex(f64) { |
| 118 | var result: Complex(f64) = undefined; | 118 | var result: Complex(f64) = undefined; |
| 119 | if (x >= 0) { | 119 | if (x >= 0) { |
| 120 | const t = math.sqrt((x + math.hypot(f64, x, y)) * 0.5); | 120 | const t = math.sqrt((x + math.hypot(f64, x, y)) * 0.5); |
| 121 | result = Complex(f64).new(t, y / (2.0 * t)); | 121 | result = Complex(f64).init(t, y / (2.0 * t)); |
| 122 | } else { | 122 | } else { |
| 123 | const t = math.sqrt((-x + math.hypot(f64, x, y)) * 0.5); | 123 | const t = math.sqrt((-x + math.hypot(f64, x, y)) * 0.5); |
| 124 | result = Complex(f64).new(math.fabs(y) / (2.0 * t), math.copysign(f64, t, y)); | 124 | result = Complex(f64).init(math.fabs(y) / (2.0 * t), math.copysign(f64, t, y)); |
| 125 | } | 125 | } |
| 126 | 126 | ||
| 127 | if (scale) { | 127 | if (scale) { |
| ... | @@ -135,7 +135,7 @@ fn sqrt64(z: Complex(f64)) Complex(f64) { | ... | @@ -135,7 +135,7 @@ fn sqrt64(z: Complex(f64)) Complex(f64) { |
| 135 | const epsilon = 0.0001; | 135 | const epsilon = 0.0001; |
| 136 | 136 | ||
| 137 | test "complex.csqrt32" { | 137 | test "complex.csqrt32" { |
| 138 | const a = Complex(f32).new(5, 3); | 138 | const a = Complex(f32).init(5, 3); |
| 139 | const c = sqrt(a); | 139 | const c = sqrt(a); |
| 140 | 140 | ||
| 141 | try testing.expect(math.approxEqAbs(f32, c.re, 2.327117, epsilon)); | 141 | try testing.expect(math.approxEqAbs(f32, c.re, 2.327117, epsilon)); |
| ... | @@ -143,7 +143,7 @@ test "complex.csqrt32" { | ... | @@ -143,7 +143,7 @@ test "complex.csqrt32" { |
| 143 | } | 143 | } |
| 144 | 144 | ||
| 145 | test "complex.csqrt64" { | 145 | test "complex.csqrt64" { |
| 146 | const a = Complex(f64).new(5, 3); | 146 | const a = Complex(f64).init(5, 3); |
| 147 | const c = sqrt(a); | 147 | const c = sqrt(a); |
| 148 | 148 | ||
| 149 | try testing.expect(math.approxEqAbs(f64, c.re, 2.3271175190399496, epsilon)); | 149 | try testing.expect(math.approxEqAbs(f64, c.re, 2.3271175190399496, epsilon)); |
lib/std/math/complex/tan.zig+3-3| ... | @@ -12,15 +12,15 @@ const Complex = cmath.Complex; | ... | @@ -12,15 +12,15 @@ const Complex = cmath.Complex; |
| 12 | /// Returns the tanget of z. | 12 | /// Returns the tanget of z. |
| 13 | pub fn tan(z: anytype) Complex(@TypeOf(z.re)) { | 13 | pub fn tan(z: anytype) Complex(@TypeOf(z.re)) { |
| 14 | const T = @TypeOf(z.re); | 14 | const T = @TypeOf(z.re); |
| 15 | const q = Complex(T).new(-z.im, z.re); | 15 | const q = Complex(T).init(-z.im, z.re); |
| 16 | const r = cmath.tanh(q); | 16 | const r = cmath.tanh(q); |
| 17 | return Complex(T).new(r.im, -r.re); | 17 | return Complex(T).init(r.im, -r.re); |
| 18 | } | 18 | } |
| 19 | 19 | ||
| 20 | const epsilon = 0.0001; | 20 | const epsilon = 0.0001; |
| 21 | 21 | ||
| 22 | test "complex.ctan" { | 22 | test "complex.ctan" { |
| 23 | const a = Complex(f32).new(5, 3); | 23 | const a = Complex(f32).init(5, 3); |
| 24 | const c = tan(a); | 24 | const c = tan(a); |
| 25 | 25 | ||
| 26 | try testing.expect(math.approxEqAbs(f32, c.re, -0.002708233, epsilon)); | 26 | try testing.expect(math.approxEqAbs(f32, c.re, -0.002708233, epsilon)); |
lib/std/math/complex/tanh.zig+12-12| ... | @@ -35,22 +35,22 @@ fn tanh32(z: Complex(f32)) Complex(f32) { | ... | @@ -35,22 +35,22 @@ fn tanh32(z: Complex(f32)) Complex(f32) { |
| 35 | if (ix >= 0x7f800000) { | 35 | if (ix >= 0x7f800000) { |
| 36 | if (ix & 0x7fffff != 0) { | 36 | if (ix & 0x7fffff != 0) { |
| 37 | const r = if (y == 0) y else x * y; | 37 | const r = if (y == 0) y else x * y; |
| 38 | return Complex(f32).new(x, r); | 38 | return Complex(f32).init(x, r); |
| 39 | } | 39 | } |
| 40 | const xx = @bitCast(f32, hx - 0x40000000); | 40 | const xx = @bitCast(f32, hx - 0x40000000); |
| 41 | const r = if (math.isInf(y)) y else math.sin(y) * math.cos(y); | 41 | const r = if (math.isInf(y)) y else math.sin(y) * math.cos(y); |
| 42 | return Complex(f32).new(xx, math.copysign(f32, 0, r)); | 42 | return Complex(f32).init(xx, math.copysign(f32, 0, r)); |
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | if (!math.isFinite(y)) { | 45 | if (!math.isFinite(y)) { |
| 46 | const r = if (ix != 0) y - y else x; | 46 | const r = if (ix != 0) y - y else x; |
| 47 | return Complex(f32).new(r, y - y); | 47 | return Complex(f32).init(r, y - y); |
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | // x >= 11 | 50 | // x >= 11 |
| 51 | if (ix >= 0x41300000) { | 51 | if (ix >= 0x41300000) { |
| 52 | const exp_mx = math.exp(-math.fabs(x)); | 52 | const exp_mx = math.exp(-math.fabs(x)); |
| 53 | return Complex(f32).new(math.copysign(f32, 1, x), 4 * math.sin(y) * math.cos(y) * exp_mx * exp_mx); | 53 | return Complex(f32).init(math.copysign(f32, 1, x), 4 * math.sin(y) * math.cos(y) * exp_mx * exp_mx); |
| 54 | } | 54 | } |
| 55 | 55 | ||
| 56 | // Kahan's algorithm | 56 | // Kahan's algorithm |
| ... | @@ -60,7 +60,7 @@ fn tanh32(z: Complex(f32)) Complex(f32) { | ... | @@ -60,7 +60,7 @@ fn tanh32(z: Complex(f32)) Complex(f32) { |
| 60 | const rho = math.sqrt(1 + s * s); | 60 | const rho = math.sqrt(1 + s * s); |
| 61 | const den = 1 + beta * s * s; | 61 | const den = 1 + beta * s * s; |
| 62 | 62 | ||
| 63 | return Complex(f32).new((beta * rho * s) / den, t / den); | 63 | return Complex(f32).init((beta * rho * s) / den, t / den); |
| 64 | } | 64 | } |
| 65 | 65 | ||
| 66 | fn tanh64(z: Complex(f64)) Complex(f64) { | 66 | fn tanh64(z: Complex(f64)) Complex(f64) { |
| ... | @@ -77,23 +77,23 @@ fn tanh64(z: Complex(f64)) Complex(f64) { | ... | @@ -77,23 +77,23 @@ fn tanh64(z: Complex(f64)) Complex(f64) { |
| 77 | if (ix >= 0x7ff00000) { | 77 | if (ix >= 0x7ff00000) { |
| 78 | if ((ix & 0x7fffff) | lx != 0) { | 78 | if ((ix & 0x7fffff) | lx != 0) { |
| 79 | const r = if (y == 0) y else x * y; | 79 | const r = if (y == 0) y else x * y; |
| 80 | return Complex(f64).new(x, r); | 80 | return Complex(f64).init(x, r); |
| 81 | } | 81 | } |
| 82 | 82 | ||
| 83 | const xx = @bitCast(f64, (@as(u64, hx - 0x40000000) << 32) | lx); | 83 | const xx = @bitCast(f64, (@as(u64, hx - 0x40000000) << 32) | lx); |
| 84 | const r = if (math.isInf(y)) y else math.sin(y) * math.cos(y); | 84 | const r = if (math.isInf(y)) y else math.sin(y) * math.cos(y); |
| 85 | return Complex(f64).new(xx, math.copysign(f64, 0, r)); | 85 | return Complex(f64).init(xx, math.copysign(f64, 0, r)); |
| 86 | } | 86 | } |
| 87 | 87 | ||
| 88 | if (!math.isFinite(y)) { | 88 | if (!math.isFinite(y)) { |
| 89 | const r = if (ix != 0) y - y else x; | 89 | const r = if (ix != 0) y - y else x; |
| 90 | return Complex(f64).new(r, y - y); | 90 | return Complex(f64).init(r, y - y); |
| 91 | } | 91 | } |
| 92 | 92 | ||
| 93 | // x >= 22 | 93 | // x >= 22 |
| 94 | if (ix >= 0x40360000) { | 94 | if (ix >= 0x40360000) { |
| 95 | const exp_mx = math.exp(-math.fabs(x)); | 95 | const exp_mx = math.exp(-math.fabs(x)); |
| 96 | return Complex(f64).new(math.copysign(f64, 1, x), 4 * math.sin(y) * math.cos(y) * exp_mx * exp_mx); | 96 | return Complex(f64).init(math.copysign(f64, 1, x), 4 * math.sin(y) * math.cos(y) * exp_mx * exp_mx); |
| 97 | } | 97 | } |
| 98 | 98 | ||
| 99 | // Kahan's algorithm | 99 | // Kahan's algorithm |
| ... | @@ -103,13 +103,13 @@ fn tanh64(z: Complex(f64)) Complex(f64) { | ... | @@ -103,13 +103,13 @@ fn tanh64(z: Complex(f64)) Complex(f64) { |
| 103 | const rho = math.sqrt(1 + s * s); | 103 | const rho = math.sqrt(1 + s * s); |
| 104 | const den = 1 + beta * s * s; | 104 | const den = 1 + beta * s * s; |
| 105 | 105 | ||
| 106 | return Complex(f64).new((beta * rho * s) / den, t / den); | 106 | return Complex(f64).init((beta * rho * s) / den, t / den); |
| 107 | } | 107 | } |
| 108 | 108 | ||
| 109 | const epsilon = 0.0001; | 109 | const epsilon = 0.0001; |
| 110 | 110 | ||
| 111 | test "complex.ctanh32" { | 111 | test "complex.ctanh32" { |
| 112 | const a = Complex(f32).new(5, 3); | 112 | const a = Complex(f32).init(5, 3); |
| 113 | const c = tanh(a); | 113 | const c = tanh(a); |
| 114 | 114 | ||
| 115 | try testing.expect(math.approxEqAbs(f32, c.re, 0.999913, epsilon)); | 115 | try testing.expect(math.approxEqAbs(f32, c.re, 0.999913, epsilon)); |
| ... | @@ -117,7 +117,7 @@ test "complex.ctanh32" { | ... | @@ -117,7 +117,7 @@ test "complex.ctanh32" { |
| 117 | } | 117 | } |
| 118 | 118 | ||
| 119 | test "complex.ctanh64" { | 119 | test "complex.ctanh64" { |
| 120 | const a = Complex(f64).new(5, 3); | 120 | const a = Complex(f64).init(5, 3); |
| 121 | const c = tanh(a); | 121 | const c = tanh(a); |
| 122 | 122 | ||
| 123 | try testing.expect(math.approxEqAbs(f64, c.re, 0.999913, epsilon)); | 123 | try testing.expect(math.approxEqAbs(f64, c.re, 0.999913, epsilon)); |
lib/std/meta/trait.zig-14| ... | @@ -298,20 +298,6 @@ pub fn isNumber(comptime T: type) bool { | ... | @@ -298,20 +298,6 @@ pub fn isNumber(comptime T: type) bool { |
| 298 | }; | 298 | }; |
| 299 | } | 299 | } |
| 300 | 300 | ||
| 301 | pub fn isIntegerNumber(comptime T: type) bool { | ||
| 302 | return switch (@typeInfo(T)) { | ||
| 303 | .Int, .ComptimeInt => true, | ||
| 304 | else => false, | ||
| 305 | }; | ||
| 306 | } | ||
| 307 | |||
| 308 | pub fn isFloatingNumber(comptime T: type) bool { | ||
| 309 | return switch (@typeInfo(T)) { | ||
| 310 | .Float, .ComptimeFloat => true, | ||
| 311 | else => false, | ||
| 312 | }; | ||
| 313 | } | ||
| 314 | |||
| 315 | test "std.meta.trait.isNumber" { | 301 | test "std.meta.trait.isNumber" { |
| 316 | const NotANumber = struct { | 302 | const NotANumber = struct { |
| 317 | number: u8, | 303 | number: u8, |
lib/std/os.zig+1| ... | @@ -1244,6 +1244,7 @@ pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: u32, mode: mode_t) | ... | @@ -1244,6 +1244,7 @@ pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: u32, mode: mode_t) |
| 1244 | 1244 | ||
| 1245 | EFAULT => unreachable, | 1245 | EFAULT => unreachable, |
| 1246 | EINVAL => unreachable, | 1246 | EINVAL => unreachable, |
| 1247 | EBADF => unreachable, | ||
| 1247 | EACCES => return error.AccessDenied, | 1248 | EACCES => return error.AccessDenied, |
| 1248 | EFBIG => return error.FileTooBig, | 1249 | EFBIG => return error.FileTooBig, |
| 1249 | EOVERFLOW => return error.FileTooBig, | 1250 | EOVERFLOW => return error.FileTooBig, |
lib/std/zig/parser_test.zig+6-6| ... | @@ -1608,13 +1608,13 @@ test "zig fmt: if-else with comment before else" { | ... | @@ -1608,13 +1608,13 @@ test "zig fmt: if-else with comment before else" { |
| 1608 | \\comptime { | 1608 | \\comptime { |
| 1609 | \\ // cexp(finite|nan +- i inf|nan) = nan + i nan | 1609 | \\ // cexp(finite|nan +- i inf|nan) = nan + i nan |
| 1610 | \\ if ((hx & 0x7fffffff) != 0x7f800000) { | 1610 | \\ if ((hx & 0x7fffffff) != 0x7f800000) { |
| 1611 | \\ return Complex(f32).new(y - y, y - y); | 1611 | \\ return Complex(f32).init(y - y, y - y); |
| 1612 | \\ } // cexp(-inf +- i inf|nan) = 0 + i0 | 1612 | \\ } // cexp(-inf +- i inf|nan) = 0 + i0 |
| 1613 | \\ else if (hx & 0x80000000 != 0) { | 1613 | \\ else if (hx & 0x80000000 != 0) { |
| 1614 | \\ return Complex(f32).new(0, 0); | 1614 | \\ return Complex(f32).init(0, 0); |
| 1615 | \\ } // cexp(+inf +- i inf|nan) = inf + i nan | 1615 | \\ } // cexp(+inf +- i inf|nan) = inf + i nan |
| 1616 | \\ else { | 1616 | \\ else { |
| 1617 | \\ return Complex(f32).new(x, y - y); | 1617 | \\ return Complex(f32).init(x, y - y); |
| 1618 | \\ } | 1618 | \\ } |
| 1619 | \\} | 1619 | \\} |
| 1620 | \\ | 1620 | \\ |
| ... | @@ -2267,16 +2267,16 @@ test "zig fmt: line comment between if block and else keyword" { | ... | @@ -2267,16 +2267,16 @@ test "zig fmt: line comment between if block and else keyword" { |
| 2267 | \\test "aoeu" { | 2267 | \\test "aoeu" { |
| 2268 | \\ // cexp(finite|nan +- i inf|nan) = nan + i nan | 2268 | \\ // cexp(finite|nan +- i inf|nan) = nan + i nan |
| 2269 | \\ if ((hx & 0x7fffffff) != 0x7f800000) { | 2269 | \\ if ((hx & 0x7fffffff) != 0x7f800000) { |
| 2270 | \\ return Complex(f32).new(y - y, y - y); | 2270 | \\ return Complex(f32).init(y - y, y - y); |
| 2271 | \\ } | 2271 | \\ } |
| 2272 | \\ // cexp(-inf +- i inf|nan) = 0 + i0 | 2272 | \\ // cexp(-inf +- i inf|nan) = 0 + i0 |
| 2273 | \\ else if (hx & 0x80000000 != 0) { | 2273 | \\ else if (hx & 0x80000000 != 0) { |
| 2274 | \\ return Complex(f32).new(0, 0); | 2274 | \\ return Complex(f32).init(0, 0); |
| 2275 | \\ } | 2275 | \\ } |
| 2276 | \\ // cexp(+inf +- i inf|nan) = inf + i nan | 2276 | \\ // cexp(+inf +- i inf|nan) = inf + i nan |
| 2277 | \\ // another comment | 2277 | \\ // another comment |
| 2278 | \\ else { | 2278 | \\ else { |
| 2279 | \\ return Complex(f32).new(x, y - y); | 2279 | \\ return Complex(f32).init(x, y - y); |
| 2280 | \\ } | 2280 | \\ } |
| 2281 | \\} | 2281 | \\} |
| 2282 | \\ | 2282 | \\ |
src/Module.zig+1-1| ... | @@ -4430,7 +4430,7 @@ pub const SwitchProngSrc = union(enum) { | ... | @@ -4430,7 +4430,7 @@ pub const SwitchProngSrc = union(enum) { |
| 4430 | log.warn("unable to load {s}: {s}", .{ | 4430 | log.warn("unable to load {s}: {s}", .{ |
| 4431 | decl.namespace.file_scope.sub_file_path, @errorName(err), | 4431 | decl.namespace.file_scope.sub_file_path, @errorName(err), |
| 4432 | }); | 4432 | }); |
| 4433 | return LazySrcLoc{ .node_offset = 0}; | 4433 | return LazySrcLoc{ .node_offset = 0 }; |
| 4434 | }; | 4434 | }; |
| 4435 | const switch_node = decl.relativeToNodeIndex(switch_node_offset); | 4435 | const switch_node = decl.relativeToNodeIndex(switch_node_offset); |
| 4436 | const main_tokens = tree.nodes.items(.main_token); | 4436 | const main_tokens = tree.nodes.items(.main_token); |
src/Sema.zig+7-8| ... | @@ -4229,19 +4229,18 @@ fn resolveSwitchItemVal( | ... | @@ -4229,19 +4229,18 @@ fn resolveSwitchItemVal( |
| 4229 | switch_prong_src: Module.SwitchProngSrc, | 4229 | switch_prong_src: Module.SwitchProngSrc, |
| 4230 | range_expand: Module.SwitchProngSrc.RangeExpand, | 4230 | range_expand: Module.SwitchProngSrc.RangeExpand, |
| 4231 | ) InnerError!TypedValue { | 4231 | ) InnerError!TypedValue { |
| 4232 | const mod = sema.mod; | ||
| 4233 | const item = try sema.resolveInst(item_ref); | 4232 | const item = try sema.resolveInst(item_ref); |
| 4234 | // We have to avoid the other helper functions here because we cannot construct a LazySrcLoc | 4233 | // We have to avoid the other helper functions here because we cannot construct a LazySrcLoc |
| 4235 | // because we only have the switch AST node. Only if we know for sure we need to report | 4234 | // because we only have the switch AST node. Only if we know for sure we need to report |
| 4236 | // a compile error do we resolve the full source locations. | 4235 | // a compile error do we resolve the full source locations. |
| 4237 | if (item.value()) |val| { | 4236 | if (item.value()) |val| { |
| 4238 | if (val.isUndef()) { | 4237 | if (val.isUndef()) { |
| 4239 | const src = switch_prong_src.resolve(mod, block.src_decl, switch_node_offset, range_expand); | 4238 | const src = switch_prong_src.resolve(sema.gpa, block.src_decl, switch_node_offset, range_expand); |
| 4240 | return sema.failWithUseOfUndef(block, src); | 4239 | return sema.failWithUseOfUndef(block, src); |
| 4241 | } | 4240 | } |
| 4242 | return TypedValue{ .ty = item.ty, .val = val }; | 4241 | return TypedValue{ .ty = item.ty, .val = val }; |
| 4243 | } | 4242 | } |
| 4244 | const src = switch_prong_src.resolve(mod, block.src_decl, switch_node_offset, range_expand); | 4243 | const src = switch_prong_src.resolve(sema.gpa, block.src_decl, switch_node_offset, range_expand); |
| 4245 | return sema.failWithNeededComptime(block, src); | 4244 | return sema.failWithNeededComptime(block, src); |
| 4246 | } | 4245 | } |
| 4247 | 4246 | ||
| ... | @@ -4285,7 +4284,7 @@ fn validateSwitchItemEnum( | ... | @@ -4285,7 +4284,7 @@ fn validateSwitchItemEnum( |
| 4285 | const item_tv = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none); | 4284 | const item_tv = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none); |
| 4286 | const field_index = item_tv.ty.enumTagFieldIndex(item_tv.val) orelse { | 4285 | const field_index = item_tv.ty.enumTagFieldIndex(item_tv.val) orelse { |
| 4287 | const msg = msg: { | 4286 | const msg = msg: { |
| 4288 | const src = switch_prong_src.resolve(mod, block.src_decl, src_node_offset, .none); | 4287 | const src = switch_prong_src.resolve(sema.gpa, block.src_decl, src_node_offset, .none); |
| 4289 | const msg = try mod.errMsg( | 4288 | const msg = try mod.errMsg( |
| 4290 | &block.base, | 4289 | &block.base, |
| 4291 | src, | 4290 | src, |
| ... | @@ -4317,8 +4316,9 @@ fn validateSwitchDupe( | ... | @@ -4317,8 +4316,9 @@ fn validateSwitchDupe( |
| 4317 | ) InnerError!void { | 4316 | ) InnerError!void { |
| 4318 | const prev_prong_src = maybe_prev_src orelse return; | 4317 | const prev_prong_src = maybe_prev_src orelse return; |
| 4319 | const mod = sema.mod; | 4318 | const mod = sema.mod; |
| 4320 | const src = switch_prong_src.resolve(mod, block.src_decl, src_node_offset, .none); | 4319 | const gpa = sema.gpa; |
| 4321 | const prev_src = prev_prong_src.resolve(mod, block.src_decl, src_node_offset, .none); | 4320 | const src = switch_prong_src.resolve(gpa, block.src_decl, src_node_offset, .none); |
| 4321 | const prev_src = prev_prong_src.resolve(gpa, block.src_decl, src_node_offset, .none); | ||
| 4322 | const msg = msg: { | 4322 | const msg = msg: { |
| 4323 | const msg = try mod.errMsg( | 4323 | const msg = try mod.errMsg( |
| 4324 | &block.base, | 4324 | &block.base, |
| ... | @@ -4355,7 +4355,7 @@ fn validateSwitchItemBool( | ... | @@ -4355,7 +4355,7 @@ fn validateSwitchItemBool( |
| 4355 | false_count.* += 1; | 4355 | false_count.* += 1; |
| 4356 | } | 4356 | } |
| 4357 | if (true_count.* + false_count.* > 2) { | 4357 | if (true_count.* + false_count.* > 2) { |
| 4358 | const src = switch_prong_src.resolve(mod, block.src_decl, src_node_offset, .none); | 4358 | const src = switch_prong_src.resolve(sema.gpa, block.src_decl, src_node_offset, .none); |
| 4359 | return sema.mod.fail(&block.base, src, "duplicate switch value", .{}); | 4359 | return sema.mod.fail(&block.base, src, "duplicate switch value", .{}); |
| 4360 | } | 4360 | } |
| 4361 | } | 4361 | } |
| ... | @@ -7584,4 +7584,3 @@ fn enumFieldSrcLoc( | ... | @@ -7584,4 +7584,3 @@ fn enumFieldSrcLoc( |
| 7584 | } | 7584 | } |
| 7585 | } else unreachable; | 7585 | } else unreachable; |
| 7586 | } | 7586 | } |
| 7587 |
src/clang.zig+4-1| ... | @@ -1,3 +1,4 @@ | ... | @@ -1,3 +1,4 @@ |
| 1 | const std = @import("std"); | ||
| 1 | pub const builtin = @import("builtin"); | 2 | pub const builtin = @import("builtin"); |
| 2 | 3 | ||
| 3 | pub const SourceLocation = extern struct { | 4 | pub const SourceLocation = extern struct { |
| ... | @@ -115,7 +116,9 @@ pub const APFloatBaseSemantics = extern enum { | ... | @@ -115,7 +116,9 @@ pub const APFloatBaseSemantics = extern enum { |
| 115 | }; | 116 | }; |
| 116 | 117 | ||
| 117 | pub const APInt = opaque { | 118 | pub const APInt = opaque { |
| 118 | pub const getLimitedValue = ZigClangAPInt_getLimitedValue; | 119 | pub fn getLimitedValue(self: *const APInt, comptime T: type) T { |
| 120 | return @truncate(T, ZigClangAPInt_getLimitedValue(self, std.math.maxInt(T))); | ||
| 121 | } | ||
| 119 | extern fn ZigClangAPInt_getLimitedValue(*const APInt, limit: u64) u64; | 122 | extern fn ZigClangAPInt_getLimitedValue(*const APInt, limit: u64) u64; |
| 120 | }; | 123 | }; |
| 121 | 124 |
src/codegen.zig+52-17| ... | @@ -1571,28 +1571,63 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1571,28 +1571,63 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1571 | const lhs = try self.resolveInst(op_lhs); | 1571 | const lhs = try self.resolveInst(op_lhs); |
| 1572 | const rhs = try self.resolveInst(op_rhs); | 1572 | const rhs = try self.resolveInst(op_rhs); |
| 1573 | 1573 | ||
| 1574 | const lhs_is_register = lhs == .register; | ||
| 1575 | const rhs_is_register = rhs == .register; | ||
| 1576 | const reuse_lhs = lhs_is_register and self.reuseOperand(inst, 0, lhs); | ||
| 1577 | const reuse_rhs = !reuse_lhs and rhs_is_register and self.reuseOperand(inst, 1, rhs); | ||
| 1578 | |||
| 1574 | // Destination must be a register | 1579 | // Destination must be a register |
| 1575 | // LHS must be a register | 1580 | // LHS must be a register |
| 1576 | // RHS must be a register | 1581 | // RHS must be a register |
| 1577 | var dst_mcv: MCValue = undefined; | 1582 | var dst_mcv: MCValue = undefined; |
| 1578 | var lhs_mcv: MCValue = undefined; | 1583 | var lhs_mcv: MCValue = lhs; |
| 1579 | var rhs_mcv: MCValue = undefined; | 1584 | var rhs_mcv: MCValue = rhs; |
| 1580 | if (self.reuseOperand(inst, 0, lhs)) { | 1585 | |
| 1581 | // LHS is the destination | 1586 | // Allocate registers for operands and/or destination |
| 1582 | lhs_mcv = if (lhs != .register) try self.copyToNewRegister(inst, lhs) else lhs; | 1587 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 1583 | rhs_mcv = if (rhs != .register) try self.copyToNewRegister(inst, rhs) else rhs; | 1588 | if (reuse_lhs) { |
| 1584 | dst_mcv = lhs_mcv; | 1589 | // Allocate 0 or 1 registers |
| 1585 | } else if (self.reuseOperand(inst, 1, rhs)) { | 1590 | if (!rhs_is_register) { |
| 1586 | // RHS is the destination | 1591 | rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(op_rhs, &.{lhs.register}) }; |
| 1587 | lhs_mcv = if (lhs != .register) try self.copyToNewRegister(inst, lhs) else lhs; | 1592 | branch.inst_table.putAssumeCapacity(op_rhs, rhs_mcv); |
| 1588 | rhs_mcv = if (rhs != .register) try self.copyToNewRegister(inst, rhs) else rhs; | 1593 | } |
| 1589 | dst_mcv = rhs_mcv; | 1594 | dst_mcv = lhs; |
| 1595 | } else if (reuse_rhs) { | ||
| 1596 | // Allocate 0 or 1 registers | ||
| 1597 | if (!lhs_is_register) { | ||
| 1598 | lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(op_lhs, &.{rhs.register}) }; | ||
| 1599 | branch.inst_table.putAssumeCapacity(op_lhs, lhs_mcv); | ||
| 1600 | } | ||
| 1601 | dst_mcv = rhs; | ||
| 1590 | } else { | 1602 | } else { |
| 1591 | // TODO save 1 copy instruction by directly allocating the destination register | 1603 | // Allocate 1 or 2 registers |
| 1592 | // LHS is the destination | 1604 | if (lhs_is_register and rhs_is_register) { |
| 1593 | lhs_mcv = try self.copyToNewRegister(inst, lhs); | 1605 | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{ lhs.register, rhs.register }) }; |
| 1594 | rhs_mcv = if (rhs != .register) try self.copyToNewRegister(inst, rhs) else rhs; | 1606 | } else if (lhs_is_register) { |
| 1595 | dst_mcv = lhs_mcv; | 1607 | // Move RHS to register |
| 1608 | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{lhs.register}) }; | ||
| 1609 | rhs_mcv = dst_mcv; | ||
| 1610 | } else if (rhs_is_register) { | ||
| 1611 | // Move LHS to register | ||
| 1612 | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{rhs.register}) }; | ||
| 1613 | lhs_mcv = dst_mcv; | ||
| 1614 | } else { | ||
| 1615 | // Move LHS and RHS to register | ||
| 1616 | const regs = try self.register_manager.allocRegs(2, .{ inst, op_rhs }, &.{}); | ||
| 1617 | lhs_mcv = MCValue{ .register = regs[0] }; | ||
| 1618 | rhs_mcv = MCValue{ .register = regs[1] }; | ||
| 1619 | dst_mcv = lhs_mcv; | ||
| 1620 | |||
| 1621 | branch.inst_table.putAssumeCapacity(op_rhs, rhs_mcv); | ||
| 1622 | } | ||
| 1623 | } | ||
| 1624 | |||
| 1625 | // Move the operands to the newly allocated registers | ||
| 1626 | if (!lhs_is_register) { | ||
| 1627 | try self.genSetReg(op_lhs.src, op_lhs.ty, lhs_mcv.register, lhs); | ||
| 1628 | } | ||
| 1629 | if (!rhs_is_register) { | ||
| 1630 | try self.genSetReg(op_rhs.src, op_rhs.ty, rhs_mcv.register, rhs); | ||
| 1596 | } | 1631 | } |
| 1597 | 1632 | ||
| 1598 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.mul(.al, dst_mcv.register, lhs_mcv.register, rhs_mcv.register).toU32()); | 1633 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.mul(.al, dst_mcv.register, lhs_mcv.register, rhs_mcv.register).toU32()); |
src/codegen/spirv.zig+500-39| ... | @@ -1,44 +1,50 @@ | ... | @@ -1,44 +1,50 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const Allocator = std.mem.Allocator; | 2 | const Allocator = std.mem.Allocator; |
| 3 | const Target = std.Target; | ||
| 3 | const log = std.log.scoped(.codegen); | 4 | const log = std.log.scoped(.codegen); |
| 4 | 5 | ||
| 5 | const spec = @import("spirv/spec.zig"); | 6 | const spec = @import("spirv/spec.zig"); |
| 7 | const Opcode = spec.Opcode; | ||
| 8 | |||
| 6 | const Module = @import("../Module.zig"); | 9 | const Module = @import("../Module.zig"); |
| 7 | const Decl = Module.Decl; | 10 | const Decl = Module.Decl; |
| 8 | const Type = @import("../type.zig").Type; | 11 | const Type = @import("../type.zig").Type; |
| 12 | const Value = @import("../value.zig").Value; | ||
| 13 | const LazySrcLoc = Module.LazySrcLoc; | ||
| 14 | const ir = @import("../ir.zig"); | ||
| 15 | const Inst = ir.Inst; | ||
| 9 | 16 | ||
| 10 | pub const TypeMap = std.HashMap(Type, u32, Type.hash, Type.eql, std.hash_map.default_max_load_percentage); | 17 | pub const TypeMap = std.HashMap(Type, u32, Type.hash, Type.eql, std.hash_map.default_max_load_percentage); |
| 18 | pub const ValueMap = std.AutoHashMap(*Inst, u32); | ||
| 19 | |||
| 20 | pub fn writeOpcode(code: *std.ArrayList(u32), opcode: Opcode, arg_count: u32) !void { | ||
| 21 | const word_count = arg_count + 1; | ||
| 22 | try code.append((word_count << 16) | @enumToInt(opcode)); | ||
| 23 | } | ||
| 11 | 24 | ||
| 12 | pub fn writeInstruction(code: *std.ArrayList(u32), instr: spec.Opcode, args: []const u32) !void { | 25 | pub fn writeInstruction(code: *std.ArrayList(u32), opcode: Opcode, args: []const u32) !void { |
| 13 | const word_count = @intCast(u32, args.len + 1); | 26 | try writeOpcode(code, opcode, @intCast(u32, args.len)); |
| 14 | try code.append((word_count << 16) | @enumToInt(instr)); | ||
| 15 | try code.appendSlice(args); | 27 | try code.appendSlice(args); |
| 16 | } | 28 | } |
| 17 | 29 | ||
| 30 | /// This structure represents a SPIR-V binary module being compiled, and keeps track of relevant information | ||
| 31 | /// such as code for the different logical sections, and the next result-id. | ||
| 18 | pub const SPIRVModule = struct { | 32 | pub const SPIRVModule = struct { |
| 19 | next_result_id: u32 = 0, | 33 | next_result_id: u32, |
| 20 | 34 | types_globals_constants: std.ArrayList(u32), | |
| 21 | target: std.Target, | ||
| 22 | |||
| 23 | types: TypeMap, | ||
| 24 | |||
| 25 | types_and_globals: std.ArrayList(u32), | ||
| 26 | fn_decls: std.ArrayList(u32), | 35 | fn_decls: std.ArrayList(u32), |
| 27 | 36 | ||
| 28 | pub fn init(target: std.Target, allocator: *Allocator) SPIRVModule { | 37 | pub fn init(allocator: *Allocator) SPIRVModule { |
| 29 | return .{ | 38 | return .{ |
| 30 | .target = target, | 39 | .next_result_id = 1, // 0 is an invalid SPIR-V result ID. |
| 31 | .types = TypeMap.init(allocator), | 40 | .types_globals_constants = std.ArrayList(u32).init(allocator), |
| 32 | .types_and_globals = std.ArrayList(u32).init(allocator), | ||
| 33 | .fn_decls = std.ArrayList(u32).init(allocator), | 41 | .fn_decls = std.ArrayList(u32).init(allocator), |
| 34 | }; | 42 | }; |
| 35 | } | 43 | } |
| 36 | 44 | ||
| 37 | pub fn deinit(self: *SPIRVModule) void { | 45 | pub fn deinit(self: *SPIRVModule) void { |
| 46 | self.types_globals_constants.deinit(); | ||
| 38 | self.fn_decls.deinit(); | 47 | self.fn_decls.deinit(); |
| 39 | self.types_and_globals.deinit(); | ||
| 40 | self.types.deinit(); | ||
| 41 | self.* = undefined; | ||
| 42 | } | 48 | } |
| 43 | 49 | ||
| 44 | pub fn allocResultId(self: *SPIRVModule) u32 { | 50 | pub fn allocResultId(self: *SPIRVModule) u32 { |
| ... | @@ -49,31 +55,310 @@ pub const SPIRVModule = struct { | ... | @@ -49,31 +55,310 @@ pub const SPIRVModule = struct { |
| 49 | pub fn resultIdBound(self: *SPIRVModule) u32 { | 55 | pub fn resultIdBound(self: *SPIRVModule) u32 { |
| 50 | return self.next_result_id; | 56 | return self.next_result_id; |
| 51 | } | 57 | } |
| 58 | }; | ||
| 59 | |||
| 60 | /// This structure is used to compile a declaration, and contains all relevant meta-information to deal with that. | ||
| 61 | pub const DeclGen = struct { | ||
| 62 | module: *Module, | ||
| 63 | spv: *SPIRVModule, | ||
| 64 | |||
| 65 | args: std.ArrayList(u32), | ||
| 66 | next_arg_index: u32, | ||
| 67 | |||
| 68 | types: TypeMap, | ||
| 69 | values: ValueMap, | ||
| 70 | |||
| 71 | decl: *Decl, | ||
| 72 | error_msg: ?*Module.ErrorMsg, | ||
| 73 | |||
| 74 | const Error = error{ AnalysisFail, OutOfMemory }; | ||
| 75 | |||
| 76 | /// This structure is used to return information about a type typically used for arithmetic operations. | ||
| 77 | /// These types may either be integers, floats, or a vector of these. Most scalar operations also work on vectors, | ||
| 78 | /// so we can easily represent those as arithmetic types. | ||
| 79 | /// If the type is a scalar, 'inner type' refers to the scalar type. Otherwise, if its a vector, it refers | ||
| 80 | /// to the vector's element type. | ||
| 81 | const ArithmeticTypeInfo = struct { | ||
| 82 | /// A classification of the inner type. | ||
| 83 | const Class = enum { | ||
| 84 | /// A boolean. | ||
| 85 | bool, | ||
| 86 | |||
| 87 | /// A regular, **native**, integer. | ||
| 88 | /// This is only returned when the backend supports this int as a native type (when | ||
| 89 | /// the relevant capability is enabled). | ||
| 90 | integer, | ||
| 91 | |||
| 92 | /// A regular float. These are all required to be natively supported. Floating points for | ||
| 93 | /// which the relevant capability is not enabled are not emulated. | ||
| 94 | float, | ||
| 95 | |||
| 96 | /// An integer of a 'strange' size (which' bit size is not the same as its backing type. **Note**: this | ||
| 97 | /// may **also** include power-of-2 integers for which the relevant capability is not enabled), but still | ||
| 98 | /// within the limits of the largest natively supported integer type. | ||
| 99 | strange_integer, | ||
| 100 | |||
| 101 | /// An integer with more bits than the largest natively supported integer type. | ||
| 102 | composite_integer, | ||
| 103 | }; | ||
| 104 | |||
| 105 | /// The number of bits in the inner type. | ||
| 106 | /// Note: this is the actual number of bits of the type, not the size of the backing integer. | ||
| 107 | bits: u16, | ||
| 108 | |||
| 109 | /// Whether the type is a vector. | ||
| 110 | is_vector: bool, | ||
| 111 | |||
| 112 | /// Whether the inner type is signed. Only relevant for integers. | ||
| 113 | signedness: std.builtin.Signedness, | ||
| 114 | |||
| 115 | /// A classification of the inner type. These scenarios | ||
| 116 | /// will all have to be handled slightly different. | ||
| 117 | class: Class, | ||
| 118 | }; | ||
| 119 | |||
| 120 | fn fail(self: *DeclGen, src: LazySrcLoc, comptime format: []const u8, args: anytype) Error { | ||
| 121 | @setCold(true); | ||
| 122 | const src_loc = src.toSrcLocWithDecl(self.decl); | ||
| 123 | self.error_msg = try Module.ErrorMsg.create(self.module.gpa, src_loc, format, args); | ||
| 124 | return error.AnalysisFail; | ||
| 125 | } | ||
| 126 | |||
| 127 | fn resolve(self: *DeclGen, inst: *Inst) !u32 { | ||
| 128 | if (inst.value()) |val| { | ||
| 129 | return self.genConstant(inst.ty, val); | ||
| 130 | } | ||
| 131 | |||
| 132 | return self.values.get(inst).?; // Instruction does not dominate all uses! | ||
| 133 | } | ||
| 134 | |||
| 135 | /// SPIR-V requires enabling specific integer sizes through capabilities, and so if they are not enabled, we need | ||
| 136 | /// to emulate them in other instructions/types. This function returns, given an integer bit width (signed or unsigned, sign | ||
| 137 | /// included), the width of the underlying type which represents it, given the enabled features for the current target. | ||
| 138 | /// If the result is `null`, the largest type the target platform supports natively is not able to perform computations using | ||
| 139 | /// that size. In this case, multiple elements of the largest type should be used. | ||
| 140 | /// The backing type will be chosen as the smallest supported integer larger or equal to it in number of bits. | ||
| 141 | /// The result is valid to be used with OpTypeInt. | ||
| 142 | /// TODO: The extension SPV_INTEL_arbitrary_precision_integers allows any integer size (at least up to 32 bits). | ||
| 143 | /// TODO: This probably needs an ABI-version as well (especially in combination with SPV_INTEL_arbitrary_precision_integers). | ||
| 144 | /// TODO: Should the result of this function be cached? | ||
| 145 | fn backingIntBits(self: *DeclGen, bits: u16) ?u16 { | ||
| 146 | const target = self.module.getTarget(); | ||
| 147 | |||
| 148 | // TODO: Figure out what to do with u0/i0. | ||
| 149 | std.debug.assert(bits != 0); | ||
| 150 | |||
| 151 | // 8, 16 and 64-bit integers require the Int8, Int16 and Inr64 capabilities respectively. | ||
| 152 | // 32-bit integers are always supported (see spec, 2.16.1, Data rules). | ||
| 153 | const ints = [_]struct { bits: u16, feature: ?Target.spirv.Feature }{ | ||
| 154 | .{ .bits = 8, .feature = .Int8 }, | ||
| 155 | .{ .bits = 16, .feature = .Int16 }, | ||
| 156 | .{ .bits = 32, .feature = null }, | ||
| 157 | .{ .bits = 64, .feature = .Int64 }, | ||
| 158 | }; | ||
| 159 | |||
| 160 | for (ints) |int| { | ||
| 161 | const has_feature = if (int.feature) |feature| | ||
| 162 | Target.spirv.featureSetHas(target.cpu.features, feature) | ||
| 163 | else | ||
| 164 | true; | ||
| 165 | |||
| 166 | if (bits <= int.bits and has_feature) { | ||
| 167 | return int.bits; | ||
| 168 | } | ||
| 169 | } | ||
| 170 | |||
| 171 | return null; | ||
| 172 | } | ||
| 173 | |||
| 174 | /// Return the amount of bits in the largest supported integer type. This is either 32 (always supported), or 64 (if | ||
| 175 | /// the Int64 capability is enabled). | ||
| 176 | /// Note: The extension SPV_INTEL_arbitrary_precision_integers allows any integer size (at least up to 32 bits). | ||
| 177 | /// In theory that could also be used, but since the spec says that it only guarantees support up to 32-bit ints there | ||
| 178 | /// is no way of knowing whether those are actually supported. | ||
| 179 | /// TODO: Maybe this should be cached? | ||
| 180 | fn largestSupportedIntBits(self: *DeclGen) u16 { | ||
| 181 | const target = self.module.getTarget(); | ||
| 182 | return if (Target.spirv.featureSetHas(target.cpu.features, .Int64)) | ||
| 183 | 64 | ||
| 184 | else | ||
| 185 | 32; | ||
| 186 | } | ||
| 187 | |||
| 188 | /// Checks whether the type is "composite int", an integer consisting of multiple native integers. These are represented by | ||
| 189 | /// arrays of largestSupportedIntBits(). | ||
| 190 | /// Asserts `ty` is an integer. | ||
| 191 | fn isCompositeInt(self: *DeclGen, ty: Type) bool { | ||
| 192 | return self.backingIntBits(ty) == null; | ||
| 193 | } | ||
| 194 | |||
| 195 | fn arithmeticTypeInfo(self: *DeclGen, ty: Type) !ArithmeticTypeInfo { | ||
| 196 | const target = self.module.getTarget(); | ||
| 197 | |||
| 198 | return switch (ty.zigTypeTag()) { | ||
| 199 | .Bool => ArithmeticTypeInfo{ | ||
| 200 | .bits = 1, // Doesn't matter for this class. | ||
| 201 | .is_vector = false, | ||
| 202 | .signedness = .unsigned, // Technically, but doesn't matter for this class. | ||
| 203 | .class = .bool, | ||
| 204 | }, | ||
| 205 | .Float => ArithmeticTypeInfo{ | ||
| 206 | .bits = ty.floatBits(target), | ||
| 207 | .is_vector = false, | ||
| 208 | .signedness = .signed, // Technically, but doesn't matter for this class. | ||
| 209 | .class = .float, | ||
| 210 | }, | ||
| 211 | .Int => blk: { | ||
| 212 | const int_info = ty.intInfo(target); | ||
| 213 | // TODO: Maybe it's useful to also return this value. | ||
| 214 | const maybe_backing_bits = self.backingIntBits(int_info.bits); | ||
| 215 | break :blk ArithmeticTypeInfo{ .bits = int_info.bits, .is_vector = false, .signedness = int_info.signedness, .class = if (maybe_backing_bits) |backing_bits| | ||
| 216 | if (backing_bits == int_info.bits) | ||
| 217 | ArithmeticTypeInfo.Class.integer | ||
| 218 | else | ||
| 219 | ArithmeticTypeInfo.Class.strange_integer | ||
| 220 | else | ||
| 221 | .composite_integer }; | ||
| 222 | }, | ||
| 223 | // As of yet, there is no vector support in the self-hosted compiler. | ||
| 224 | .Vector => self.fail(.{ .node_offset = 0 }, "TODO: SPIR-V backend: implement arithmeticTypeInfo for Vector", .{}), | ||
| 225 | // TODO: For which types is this the case? | ||
| 226 | else => self.fail(.{ .node_offset = 0 }, "TODO: SPIR-V backend: implement arithmeticTypeInfo for {}", .{ty}), | ||
| 227 | }; | ||
| 228 | } | ||
| 229 | |||
| 230 | /// Generate a constant representing `val`. | ||
| 231 | /// TODO: Deduplication? | ||
| 232 | fn genConstant(self: *DeclGen, ty: Type, val: Value) Error!u32 { | ||
| 233 | const code = &self.spv.types_globals_constants; | ||
| 234 | const result_id = self.spv.allocResultId(); | ||
| 235 | const result_type_id = try self.getOrGenType(ty); | ||
| 236 | |||
| 237 | if (val.isUndef()) { | ||
| 238 | try writeInstruction(code, .OpUndef, &[_]u32{ result_type_id, result_id }); | ||
| 239 | return result_id; | ||
| 240 | } | ||
| 241 | |||
| 242 | switch (ty.zigTypeTag()) { | ||
| 243 | .Bool => { | ||
| 244 | const opcode: Opcode = if (val.toBool()) .OpConstantTrue else .OpConstantFalse; | ||
| 245 | try writeInstruction(code, opcode, &[_]u32{ result_type_id, result_id }); | ||
| 246 | }, | ||
| 247 | .Float => { | ||
| 248 | // At this point we are guaranteed that the target floating point type is supported, otherwise the function | ||
| 249 | // would have exited at getOrGenType(ty). | ||
| 250 | |||
| 251 | // f16 and f32 require one word of storage. f64 requires 2, low-order first. | ||
| 252 | |||
| 253 | switch (val.tag()) { | ||
| 254 | .float_16 => try writeInstruction(code, .OpConstant, &[_]u32{ result_type_id, result_id, @bitCast(u16, val.castTag(.float_16).?.data) }), | ||
| 255 | .float_32 => try writeInstruction(code, .OpConstant, &[_]u32{ result_type_id, result_id, @bitCast(u32, val.castTag(.float_32).?.data) }), | ||
| 256 | .float_64 => { | ||
| 257 | const float_bits = @bitCast(u64, val.castTag(.float_64).?.data); | ||
| 258 | try writeInstruction(code, .OpConstant, &[_]u32{ | ||
| 259 | result_type_id, | ||
| 260 | result_id, | ||
| 261 | @truncate(u32, float_bits), | ||
| 262 | @truncate(u32, float_bits >> 32), | ||
| 263 | }); | ||
| 264 | }, | ||
| 265 | .float_128 => unreachable, // Filtered out in the call to getOrGenType. | ||
| 266 | // TODO: What tags do we need to handle here anyway? | ||
| 267 | else => return self.fail(.{ .node_offset = 0 }, "TODO: SPIR-V backend: float constant generation of value {s}\n", .{val.tag()}), | ||
| 268 | } | ||
| 269 | }, | ||
| 270 | else => return self.fail(.{ .node_offset = 0 }, "TODO: SPIR-V backend: constant generation of type {s}\n", .{ty.zigTypeTag()}), | ||
| 271 | } | ||
| 272 | |||
| 273 | return result_id; | ||
| 274 | } | ||
| 52 | 275 | ||
| 53 | pub fn getOrGenType(self: *SPIRVModule, t: Type) !u32 { | 276 | fn getOrGenType(self: *DeclGen, ty: Type) Error!u32 { |
| 54 | // We can't use getOrPut here so we can recursively generate types. | 277 | // We can't use getOrPut here so we can recursively generate types. |
| 55 | if (self.types.get(t)) |already_generated| { | 278 | if (self.types.get(ty)) |already_generated| { |
| 56 | return already_generated; | 279 | return already_generated; |
| 57 | } | 280 | } |
| 58 | 281 | ||
| 59 | const result = self.allocResultId(); | 282 | const target = self.module.getTarget(); |
| 283 | const code = &self.spv.types_globals_constants; | ||
| 284 | const result_id = self.spv.allocResultId(); | ||
| 60 | 285 | ||
| 61 | switch (t.zigTypeTag()) { | 286 | switch (ty.zigTypeTag()) { |
| 62 | .Void => try writeInstruction(&self.types_and_globals, .OpTypeVoid, &[_]u32{ result }), | 287 | .Void => try writeInstruction(code, .OpTypeVoid, &[_]u32{result_id}), |
| 63 | .Bool => try writeInstruction(&self.types_and_globals, .OpTypeBool, &[_]u32{ result }), | 288 | .Bool => try writeInstruction(code, .OpTypeBool, &[_]u32{result_id}), |
| 64 | .Int => { | 289 | .Int => { |
| 65 | const int_info = t.intInfo(self.target); | 290 | const int_info = ty.intInfo(target); |
| 66 | try writeInstruction(&self.types_and_globals, .OpTypeInt, &[_]u32{ | 291 | const backing_bits = self.backingIntBits(int_info.bits) orelse { |
| 67 | result, | 292 | // Integers too big for any native type are represented as "composite integers": An array of largestSupportedIntBits. |
| 68 | int_info.bits, | 293 | return self.fail(.{ .node_offset = 0 }, "TODO: SPIR-V backend: implement composite ints {}", .{ty}); |
| 294 | }; | ||
| 295 | |||
| 296 | // TODO: If backing_bits != int_info.bits, a duplicate type might be generated here. | ||
| 297 | try writeInstruction(code, .OpTypeInt, &[_]u32{ | ||
| 298 | result_id, | ||
| 299 | backing_bits, | ||
| 69 | switch (int_info.signedness) { | 300 | switch (int_info.signedness) { |
| 70 | .unsigned => 0, | 301 | .unsigned => 0, |
| 71 | .signed => 1, | 302 | .signed => 1, |
| 72 | }, | 303 | }, |
| 73 | }); | 304 | }); |
| 74 | }, | 305 | }, |
| 75 | // TODO: Verify that floatBits() will be correct. | 306 | .Float => { |
| 76 | .Float => try writeInstruction(&self.types_and_globals, .OpTypeFloat, &[_]u32{ result, t.floatBits(self.target) }), | 307 | // We can (and want) not really emulate floating points with other floating point types like with the integer types, |
| 308 | // so if the float is not supported, just return an error. | ||
| 309 | const bits = ty.floatBits(target); | ||
| 310 | const supported = switch (bits) { | ||
| 311 | 16 => Target.spirv.featureSetHas(target.cpu.features, .Float16), | ||
| 312 | // 32-bit floats are always supported (see spec, 2.16.1, Data rules). | ||
| 313 | 32 => true, | ||
| 314 | 64 => Target.spirv.featureSetHas(target.cpu.features, .Float64), | ||
| 315 | else => false, | ||
| 316 | }; | ||
| 317 | |||
| 318 | if (!supported) { | ||
| 319 | return self.fail(.{ .node_offset = 0 }, "Floating point width of {} bits is not supported for the current SPIR-V feature set", .{bits}); | ||
| 320 | } | ||
| 321 | |||
| 322 | try writeInstruction(code, .OpTypeFloat, &[_]u32{ result_id, bits }); | ||
| 323 | }, | ||
| 324 | .Fn => { | ||
| 325 | // We only support zig-calling-convention functions, no varargs. | ||
| 326 | if (ty.fnCallingConvention() != .Unspecified) | ||
| 327 | return self.fail(.{ .node_offset = 0 }, "Unsupported calling convention for SPIR-V", .{}); | ||
| 328 | if (ty.fnIsVarArgs()) | ||
| 329 | return self.fail(.{ .node_offset = 0 }, "VarArgs unsupported for SPIR-V", .{}); | ||
| 330 | |||
| 331 | // In order to avoid a temporary here, first generate all the required types and then simply look them up | ||
| 332 | // when generating the function type. | ||
| 333 | const params = ty.fnParamLen(); | ||
| 334 | var i: usize = 0; | ||
| 335 | while (i < params) : (i += 1) { | ||
| 336 | _ = try self.getOrGenType(ty.fnParamType(i)); | ||
| 337 | } | ||
| 338 | |||
| 339 | const return_type_id = try self.getOrGenType(ty.fnReturnType()); | ||
| 340 | |||
| 341 | // result id + result type id + parameter type ids. | ||
| 342 | try writeOpcode(code, .OpTypeFunction, 2 + @intCast(u32, ty.fnParamLen())); | ||
| 343 | try code.appendSlice(&.{ result_id, return_type_id }); | ||
| 344 | |||
| 345 | i = 0; | ||
| 346 | while (i < params) : (i += 1) { | ||
| 347 | const param_type_id = self.types.get(ty.fnParamType(i)).?; | ||
| 348 | try code.append(param_type_id); | ||
| 349 | } | ||
| 350 | }, | ||
| 351 | .Vector => { | ||
| 352 | // Although not 100% the same, Zig vectors map quite neatly to SPIR-V vectors (including many integer and float operations | ||
| 353 | // which work on them), so simply use those. | ||
| 354 | // Note: SPIR-V vectors only support bools, ints and floats, so pointer vectors need to be supported another way. | ||
| 355 | // "composite integers" (larger than the largest supported native type) can probably be represented by an array of vectors. | ||
| 356 | // TODO: The SPIR-V spec mentions that vector sizes may be quite restricted! look into which we can use, and whether OpTypeVector | ||
| 357 | // is adequate at all for this. | ||
| 358 | |||
| 359 | // TODO: Vectors are not yet supported by the self-hosted compiler itself it seems. | ||
| 360 | return self.fail(.{ .node_offset = 0 }, "TODO: SPIR-V backend: implement type Vector", .{}); | ||
| 361 | }, | ||
| 77 | .Null, | 362 | .Null, |
| 78 | .Undefined, | 363 | .Undefined, |
| 79 | .EnumLiteral, | 364 | .EnumLiteral, |
| ... | @@ -84,21 +369,197 @@ pub const SPIRVModule = struct { | ... | @@ -84,21 +369,197 @@ pub const SPIRVModule = struct { |
| 84 | 369 | ||
| 85 | .BoundFn => unreachable, // this type will be deleted from the language. | 370 | .BoundFn => unreachable, // this type will be deleted from the language. |
| 86 | 371 | ||
| 87 | else => return error.TODO, | 372 | else => |tag| return self.fail(.{ .node_offset = 0 }, "TODO: SPIR-V backend: implement type {}s", .{tag}), |
| 88 | } | 373 | } |
| 89 | 374 | ||
| 90 | try self.types.put(t, result); | 375 | try self.types.putNoClobber(ty, result_id); |
| 91 | return result; | 376 | return result_id; |
| 92 | } | 377 | } |
| 93 | 378 | ||
| 94 | pub fn gen(self: *SPIRVModule, decl: *Decl) !void { | 379 | pub fn gen(self: *DeclGen) !void { |
| 95 | switch (decl.ty.zigTypeTag()) { | 380 | const decl = self.decl; |
| 96 | .Fn => { | 381 | const result_id = decl.fn_link.spirv.id; |
| 97 | log.debug("Generating code for function '{s}'", .{ std.mem.spanZ(decl.name) }); | ||
| 98 | 382 | ||
| 99 | _ = try self.getOrGenType(decl.ty.fnReturnType()); | 383 | if (decl.val.castTag(.function)) |func_payload| { |
| 100 | }, | 384 | std.debug.assert(decl.ty.zigTypeTag() == .Fn); |
| 101 | else => return error.TODO, | 385 | const prototype_id = try self.getOrGenType(decl.ty); |
| 386 | try writeInstruction(&self.spv.fn_decls, .OpFunction, &[_]u32{ | ||
| 387 | self.types.get(decl.ty.fnReturnType()).?, // This type should be generated along with the prototype. | ||
| 388 | result_id, | ||
| 389 | @bitCast(u32, spec.FunctionControl{}), // TODO: We can set inline here if the type requires it. | ||
| 390 | prototype_id, | ||
| 391 | }); | ||
| 392 | |||
| 393 | const params = decl.ty.fnParamLen(); | ||
| 394 | var i: usize = 0; | ||
| 395 | |||
| 396 | try self.args.ensureCapacity(params); | ||
| 397 | while (i < params) : (i += 1) { | ||
| 398 | const param_type_id = self.types.get(decl.ty.fnParamType(i)).?; | ||
| 399 | const arg_result_id = self.spv.allocResultId(); | ||
| 400 | try writeInstruction(&self.spv.fn_decls, .OpFunctionParameter, &[_]u32{ param_type_id, arg_result_id }); | ||
| 401 | self.args.appendAssumeCapacity(arg_result_id); | ||
| 402 | } | ||
| 403 | |||
| 404 | // TODO: This could probably be done in a better way... | ||
| 405 | const root_block_id = self.spv.allocResultId(); | ||
| 406 | _ = try writeInstruction(&self.spv.fn_decls, .OpLabel, &[_]u32{root_block_id}); | ||
| 407 | try self.genBody(func_payload.data.body); | ||
| 408 | |||
| 409 | try writeInstruction(&self.spv.fn_decls, .OpFunctionEnd, &[_]u32{}); | ||
| 410 | } else { | ||
| 411 | return self.fail(.{ .node_offset = 0 }, "TODO: SPIR-V backend: generate decl type {}", .{decl.ty.zigTypeTag()}); | ||
| 412 | } | ||
| 413 | } | ||
| 414 | |||
| 415 | fn genBody(self: *DeclGen, body: ir.Body) !void { | ||
| 416 | for (body.instructions) |inst| { | ||
| 417 | const maybe_result_id = try self.genInst(inst); | ||
| 418 | if (maybe_result_id) |result_id| | ||
| 419 | try self.values.putNoClobber(inst, result_id); | ||
| 102 | } | 420 | } |
| 103 | } | 421 | } |
| 422 | |||
| 423 | fn genInst(self: *DeclGen, inst: *Inst) !?u32 { | ||
| 424 | return switch (inst.tag) { | ||
| 425 | .add, .addwrap => try self.genBinOp(inst.castTag(.add).?), | ||
| 426 | .sub, .subwrap => try self.genBinOp(inst.castTag(.sub).?), | ||
| 427 | .mul, .mulwrap => try self.genBinOp(inst.castTag(.mul).?), | ||
| 428 | .div => try self.genBinOp(inst.castTag(.div).?), | ||
| 429 | .bit_and => try self.genBinOp(inst.castTag(.bit_and).?), | ||
| 430 | .bit_or => try self.genBinOp(inst.castTag(.bit_or).?), | ||
| 431 | .xor => try self.genBinOp(inst.castTag(.xor).?), | ||
| 432 | .cmp_eq => try self.genBinOp(inst.castTag(.cmp_eq).?), | ||
| 433 | .cmp_neq => try self.genBinOp(inst.castTag(.cmp_neq).?), | ||
| 434 | .cmp_gt => try self.genBinOp(inst.castTag(.cmp_gt).?), | ||
| 435 | .cmp_gte => try self.genBinOp(inst.castTag(.cmp_gte).?), | ||
| 436 | .cmp_lt => try self.genBinOp(inst.castTag(.cmp_lt).?), | ||
| 437 | .cmp_lte => try self.genBinOp(inst.castTag(.cmp_lte).?), | ||
| 438 | .bool_and => try self.genBinOp(inst.castTag(.bool_and).?), | ||
| 439 | .bool_or => try self.genBinOp(inst.castTag(.bool_or).?), | ||
| 440 | .not => try self.genUnOp(inst.castTag(.not).?), | ||
| 441 | .arg => self.genArg(), | ||
| 442 | // TODO: Breakpoints won't be supported in SPIR-V, but the compiler seems to insert them | ||
| 443 | // throughout the IR. | ||
| 444 | .breakpoint => null, | ||
| 445 | .dbg_stmt => null, | ||
| 446 | .ret => self.genRet(inst.castTag(.ret).?), | ||
| 447 | .retvoid => self.genRetVoid(), | ||
| 448 | .unreach => self.genUnreach(), | ||
| 449 | else => self.fail(.{ .node_offset = 0 }, "TODO: SPIR-V backend: implement inst {}", .{inst.tag}), | ||
| 450 | }; | ||
| 451 | } | ||
| 452 | |||
| 453 | fn genBinOp(self: *DeclGen, inst: *Inst.BinOp) !u32 { | ||
| 454 | // TODO: Will lhs and rhs have the same type? | ||
| 455 | const lhs_id = try self.resolve(inst.lhs); | ||
| 456 | const rhs_id = try self.resolve(inst.rhs); | ||
| 457 | |||
| 458 | const result_id = self.spv.allocResultId(); | ||
| 459 | const result_type_id = try self.getOrGenType(inst.base.ty); | ||
| 460 | |||
| 461 | // TODO: Is the result the same as the argument types? | ||
| 462 | // This is supposed to be the case for SPIR-V. | ||
| 463 | std.debug.assert(inst.rhs.ty.eql(inst.lhs.ty)); | ||
| 464 | std.debug.assert(inst.base.ty.tag() == .bool or inst.base.ty.eql(inst.lhs.ty)); | ||
| 465 | |||
| 466 | // Binary operations are generally applicable to both scalar and vector operations in SPIR-V, but int and float | ||
| 467 | // versions of operations require different opcodes. | ||
| 468 | // For operations which produce bools, the information of inst.base.ty is not useful, so just pick either operand | ||
| 469 | // instead. | ||
| 470 | const info = try self.arithmeticTypeInfo(inst.lhs.ty); | ||
| 471 | |||
| 472 | if (info.class == .composite_integer) | ||
| 473 | return self.fail(.{ .node_offset = 0 }, "TODO: SPIR-V backend: binary operations for composite integers", .{}); | ||
| 474 | |||
| 475 | const is_bool = info.class == .bool; | ||
| 476 | const is_float = info.class == .float; | ||
| 477 | const is_signed = info.signedness == .signed; | ||
| 478 | // **Note**: All these operations must be valid for vectors of floats, integers and bools as well! | ||
| 479 | // For floating points, we generally want ordered operations (which return false if either operand is nan). | ||
| 480 | const opcode = switch (inst.base.tag) { | ||
| 481 | // The regular integer operations are all defined for wrapping. Since theyre only relevant for integers, | ||
| 482 | // we can just switch on both cases here. | ||
| 483 | .add, .addwrap => if (is_float) Opcode.OpFAdd else Opcode.OpIAdd, | ||
| 484 | .sub, .subwrap => if (is_float) Opcode.OpFSub else Opcode.OpISub, | ||
| 485 | .mul, .mulwrap => if (is_float) Opcode.OpFMul else Opcode.OpIMul, | ||
| 486 | // TODO: Trap if divisor is 0? | ||
| 487 | // TODO: Figure out of OpSDiv for unsigned/OpUDiv for signed does anything useful. | ||
| 488 | // => Those are probably for divTrunc and divFloor, though the compiler does not yet generate those. | ||
| 489 | // => TODO: Figure out how those work on the SPIR-V side. | ||
| 490 | // => TODO: Test these. | ||
| 491 | .div => if (is_float) Opcode.OpFDiv else if (is_signed) Opcode.OpSDiv else Opcode.OpUDiv, | ||
| 492 | // Only integer versions for these. | ||
| 493 | .bit_and => Opcode.OpBitwiseAnd, | ||
| 494 | .bit_or => Opcode.OpBitwiseOr, | ||
| 495 | .xor => Opcode.OpBitwiseXor, | ||
| 496 | // Int/bool/float -> bool operations. | ||
| 497 | .cmp_eq => if (is_float) Opcode.OpFOrdEqual else if (is_bool) Opcode.OpLogicalEqual else Opcode.OpIEqual, | ||
| 498 | .cmp_neq => if (is_float) Opcode.OpFOrdNotEqual else if (is_bool) Opcode.OpLogicalNotEqual else Opcode.OpINotEqual, | ||
| 499 | // Int/float -> bool operations. | ||
| 500 | // TODO: Verify that these OpFOrd type operations produce the right value. | ||
| 501 | // TODO: Is there a more fundamental difference between OpU and OpS operations here than just the type? | ||
| 502 | .cmp_gt => if (is_float) Opcode.OpFOrdGreaterThan else if (is_signed) Opcode.OpSGreaterThan else Opcode.OpUGreaterThan, | ||
| 503 | .cmp_gte => if (is_float) Opcode.OpFOrdGreaterThanEqual else if (is_signed) Opcode.OpSGreaterThanEqual else Opcode.OpUGreaterThanEqual, | ||
| 504 | .cmp_lt => if (is_float) Opcode.OpFOrdLessThan else if (is_signed) Opcode.OpSLessThan else Opcode.OpULessThan, | ||
| 505 | .cmp_lte => if (is_float) Opcode.OpFOrdLessThanEqual else if (is_signed) Opcode.OpSLessThanEqual else Opcode.OpULessThanEqual, | ||
| 506 | // Bool -> bool operations. | ||
| 507 | .bool_and => Opcode.OpLogicalAnd, | ||
| 508 | .bool_or => Opcode.OpLogicalOr, | ||
| 509 | else => unreachable, | ||
| 510 | }; | ||
| 511 | |||
| 512 | try writeInstruction(&self.spv.fn_decls, opcode, &[_]u32{ result_type_id, result_id, lhs_id, rhs_id }); | ||
| 513 | |||
| 514 | // TODO: Trap on overflow? Probably going to be annoying. | ||
| 515 | // TODO: Look into SPV_KHR_no_integer_wrap_decoration which provides NoSignedWrap/NoUnsignedWrap. | ||
| 516 | |||
| 517 | if (info.class != .strange_integer) | ||
| 518 | return result_id; | ||
| 519 | |||
| 520 | return self.fail(.{ .node_offset = 0 }, "TODO: SPIR-V backend: strange integer operation mask", .{}); | ||
| 521 | } | ||
| 522 | |||
| 523 | fn genUnOp(self: *DeclGen, inst: *Inst.UnOp) !u32 { | ||
| 524 | const operand_id = try self.resolve(inst.operand); | ||
| 525 | |||
| 526 | const result_id = self.spv.allocResultId(); | ||
| 527 | const result_type_id = try self.getOrGenType(inst.base.ty); | ||
| 528 | |||
| 529 | const info = try self.arithmeticTypeInfo(inst.operand.ty); | ||
| 530 | |||
| 531 | const opcode = switch (inst.base.tag) { | ||
| 532 | // Bool -> bool | ||
| 533 | .not => Opcode.OpLogicalNot, | ||
| 534 | else => unreachable, | ||
| 535 | }; | ||
| 536 | |||
| 537 | try writeInstruction(&self.spv.fn_decls, opcode, &[_]u32{ result_type_id, result_id, operand_id }); | ||
| 538 | |||
| 539 | return result_id; | ||
| 540 | } | ||
| 541 | |||
| 542 | fn genArg(self: *DeclGen) u32 { | ||
| 543 | defer self.next_arg_index += 1; | ||
| 544 | return self.args.items[self.next_arg_index]; | ||
| 545 | } | ||
| 546 | |||
| 547 | fn genRet(self: *DeclGen, inst: *Inst.UnOp) !?u32 { | ||
| 548 | const operand_id = try self.resolve(inst.operand); | ||
| 549 | // TODO: This instruction needs to be the last in a block. Is that guaranteed? | ||
| 550 | try writeInstruction(&self.spv.fn_decls, .OpReturnValue, &[_]u32{operand_id}); | ||
| 551 | return null; | ||
| 552 | } | ||
| 553 | |||
| 554 | fn genRetVoid(self: *DeclGen) !?u32 { | ||
| 555 | // TODO: This instruction needs to be the last in a block. Is that guaranteed? | ||
| 556 | try writeInstruction(&self.spv.fn_decls, .OpReturn, &[_]u32{}); | ||
| 557 | return null; | ||
| 558 | } | ||
| 559 | |||
| 560 | fn genUnreach(self: *DeclGen) !?u32 { | ||
| 561 | // TODO: This instruction needs to be the last in a block. Is that guaranteed? | ||
| 562 | try writeInstruction(&self.spv.fn_decls, .OpUnreachable, &[_]u32{}); | ||
| 563 | return null; | ||
| 564 | } | ||
| 104 | }; | 565 | }; |
src/link/SpirV.zig+39-11| ... | @@ -37,10 +37,9 @@ const spec = @import("../codegen/spirv/spec.zig"); | ... | @@ -37,10 +37,9 @@ const spec = @import("../codegen/spirv/spec.zig"); |
| 37 | 37 | ||
| 38 | // TODO: Should this struct be used at all rather than just a hashmap of aux data for every decl? | 38 | // TODO: Should this struct be used at all rather than just a hashmap of aux data for every decl? |
| 39 | pub const FnData = struct { | 39 | pub const FnData = struct { |
| 40 | // We're going to fill these in flushModule, and we're going to fill them unconditionally, | 40 | // We're going to fill these in flushModule, and we're going to fill them unconditionally, |
| 41 | // so just set it to undefined. | 41 | // so just set it to undefined. |
| 42 | id: u32 = undefined | 42 | id: u32 = undefined }; |
| 43 | }; | ||
| 44 | 43 | ||
| 45 | base: link.File, | 44 | base: link.File, |
| 46 | 45 | ||
| ... | @@ -130,8 +129,8 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void { | ... | @@ -130,8 +129,8 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void { |
| 130 | const module = self.base.options.module.?; | 129 | const module = self.base.options.module.?; |
| 131 | const target = comp.getTarget(); | 130 | const target = comp.getTarget(); |
| 132 | 131 | ||
| 133 | var spirv_module = codegen.SPIRVModule.init(target, self.base.allocator); | 132 | var spv = codegen.SPIRVModule.init(self.base.allocator); |
| 134 | defer spirv_module.deinit(); | 133 | defer spv.deinit(); |
| 135 | 134 | ||
| 136 | // Allocate an ID for every declaration before generating code, | 135 | // Allocate an ID for every declaration before generating code, |
| 137 | // so that we can access them before processing them. | 136 | // so that we can access them before processing them. |
| ... | @@ -143,18 +142,47 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void { | ... | @@ -143,18 +142,47 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void { |
| 143 | const decl = entry.key; | 142 | const decl = entry.key; |
| 144 | if (!decl.has_tv) continue; | 143 | if (!decl.has_tv) continue; |
| 145 | 144 | ||
| 146 | decl.fn_link.spirv.id = spirv_module.allocResultId(); | 145 | decl.fn_link.spirv.id = spv.allocResultId(); |
| 147 | log.debug("Allocating id {} to '{s}'", .{ decl.fn_link.spirv.id, std.mem.spanZ(decl.name) }); | 146 | log.debug("Allocating id {} to '{s}'", .{ decl.fn_link.spirv.id, std.mem.spanZ(decl.name) }); |
| 148 | } | 147 | } |
| 149 | } | 148 | } |
| 150 | 149 | ||
| 151 | // Now, actually generate the code for all declarations. | 150 | // Now, actually generate the code for all declarations. |
| 152 | { | 151 | { |
| 152 | // We are just going to re-use this same DeclGen for every Decl, and we are just going to | ||
| 153 | // change the decl. Otherwise, we would have to keep a separate `args` and `types`, and re-construct this | ||
| 154 | // structure every time. | ||
| 155 | var decl_gen = codegen.DeclGen{ | ||
| 156 | .module = module, | ||
| 157 | .spv = &spv, | ||
| 158 | .args = std.ArrayList(u32).init(self.base.allocator), | ||
| 159 | .next_arg_index = undefined, | ||
| 160 | .types = codegen.TypeMap.init(self.base.allocator), | ||
| 161 | .values = codegen.ValueMap.init(self.base.allocator), | ||
| 162 | .decl = undefined, | ||
| 163 | .error_msg = undefined, | ||
| 164 | }; | ||
| 165 | |||
| 166 | defer decl_gen.values.deinit(); | ||
| 167 | defer decl_gen.types.deinit(); | ||
| 168 | defer decl_gen.args.deinit(); | ||
| 169 | |||
| 153 | for (self.decl_table.items()) |entry| { | 170 | for (self.decl_table.items()) |entry| { |
| 154 | const decl = entry.key; | 171 | const decl = entry.key; |
| 155 | if (!decl.has_tv) continue; | 172 | if (!decl.has_tv) continue; |
| 156 | 173 | ||
| 157 | try spirv_module.gen(decl); | 174 | decl_gen.args.items.len = 0; |
| 175 | decl_gen.next_arg_index = 0; | ||
| 176 | decl_gen.decl = decl; | ||
| 177 | decl_gen.error_msg = null; | ||
| 178 | |||
| 179 | decl_gen.gen() catch |err| switch (err) { | ||
| 180 | error.AnalysisFail => { | ||
| 181 | try module.failed_decls.put(module.gpa, decl, decl_gen.error_msg.?); | ||
| 182 | return; | ||
| 183 | }, | ||
| 184 | else => |e| return e, | ||
| 185 | }; | ||
| 158 | } | 186 | } |
| 159 | } | 187 | } |
| 160 | 188 | ||
| ... | @@ -165,7 +193,7 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void { | ... | @@ -165,7 +193,7 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void { |
| 165 | spec.magic_number, | 193 | spec.magic_number, |
| 166 | (spec.version.major << 16) | (spec.version.minor << 8), | 194 | (spec.version.major << 16) | (spec.version.minor << 8), |
| 167 | 0, // TODO: Register Zig compiler magic number. | 195 | 0, // TODO: Register Zig compiler magic number. |
| 168 | spirv_module.resultIdBound(), // ID bound. | 196 | spv.resultIdBound(), // ID bound. |
| 169 | 0, // Schema (currently reserved for future use in the SPIR-V spec). | 197 | 0, // Schema (currently reserved for future use in the SPIR-V spec). |
| 170 | }); | 198 | }); |
| 171 | 199 | ||
| ... | @@ -176,8 +204,8 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void { | ... | @@ -176,8 +204,8 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void { |
| 176 | // follows the SPIR-V logical module format! | 204 | // follows the SPIR-V logical module format! |
| 177 | var all_buffers = [_]std.os.iovec_const{ | 205 | var all_buffers = [_]std.os.iovec_const{ |
| 178 | wordsToIovConst(binary.items), | 206 | wordsToIovConst(binary.items), |
| 179 | wordsToIovConst(spirv_module.types_and_globals.items), | 207 | wordsToIovConst(spv.types_globals_constants.items), |
| 180 | wordsToIovConst(spirv_module.fn_decls.items), | 208 | wordsToIovConst(spv.fn_decls.items), |
| 181 | }; | 209 | }; |
| 182 | 210 | ||
| 183 | const file = self.base.file.?; | 211 | const file = self.base.file.?; |
src/translate_c.zig+2-2| ... | @@ -2341,7 +2341,7 @@ fn transInitListExprArray( | ... | @@ -2341,7 +2341,7 @@ fn transInitListExprArray( |
| 2341 | assert(@ptrCast(*const clang.Type, arr_type).isConstantArrayType()); | 2341 | assert(@ptrCast(*const clang.Type, arr_type).isConstantArrayType()); |
| 2342 | const const_arr_ty = @ptrCast(*const clang.ConstantArrayType, arr_type); | 2342 | const const_arr_ty = @ptrCast(*const clang.ConstantArrayType, arr_type); |
| 2343 | const size_ap_int = const_arr_ty.getSize(); | 2343 | const size_ap_int = const_arr_ty.getSize(); |
| 2344 | const all_count = size_ap_int.getLimitedValue(math.maxInt(usize)); | 2344 | const all_count = size_ap_int.getLimitedValue(usize); |
| 2345 | const leftover_count = all_count - init_count; | 2345 | const leftover_count = all_count - init_count; |
| 2346 | 2346 | ||
| 2347 | if (all_count == 0) { | 2347 | if (all_count == 0) { |
| ... | @@ -4266,7 +4266,7 @@ fn transType(c: *Context, scope: *Scope, ty: *const clang.Type, source_loc: clan | ... | @@ -4266,7 +4266,7 @@ fn transType(c: *Context, scope: *Scope, ty: *const clang.Type, source_loc: clan |
| 4266 | const const_arr_ty = @ptrCast(*const clang.ConstantArrayType, ty); | 4266 | const const_arr_ty = @ptrCast(*const clang.ConstantArrayType, ty); |
| 4267 | 4267 | ||
| 4268 | const size_ap_int = const_arr_ty.getSize(); | 4268 | const size_ap_int = const_arr_ty.getSize(); |
| 4269 | const size = size_ap_int.getLimitedValue(math.maxInt(usize)); | 4269 | const size = size_ap_int.getLimitedValue(usize); |
| 4270 | const elem_type = try transType(c, scope, const_arr_ty.getElementType().getTypePtr(), source_loc); | 4270 | const elem_type = try transType(c, scope, const_arr_ty.getElementType().getTypePtr(), source_loc); |
| 4271 | 4271 | ||
| 4272 | return Tag.array_type.create(c.arena, .{ .len = size, .elem_type = elem_type }); | 4272 | return Tag.array_type.create(c.arena, .{ .len = size, .elem_type = elem_type }); |
test/stage2/arm.zig+39| ... | @@ -367,5 +367,44 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -367,5 +367,44 @@ pub fn addCases(ctx: *TestContext) !void { |
| 367 | , | 367 | , |
| 368 | "", | 368 | "", |
| 369 | ); | 369 | ); |
| 370 | |||
| 371 | case.addCompareOutput( | ||
| 372 | \\pub fn main() void { | ||
| 373 | \\ assert(addMul(3, 4) == 357747496); | ||
| 374 | \\} | ||
| 375 | \\ | ||
| 376 | \\fn addMul(a: u32, b: u32) u32 { | ||
| 377 | \\ const x: u32 = blk: { | ||
| 378 | \\ const c = a + b; // 7 | ||
| 379 | \\ const d = a + c; // 10 | ||
| 380 | \\ const e = d + b; // 14 | ||
| 381 | \\ const f = d + e; // 24 | ||
| 382 | \\ const g = e + f; // 38 | ||
| 383 | \\ const h = f + g; // 62 | ||
| 384 | \\ const i = g + h; // 100 | ||
| 385 | \\ const j = i + d; // 110 | ||
| 386 | \\ const k = i + j; // 210 | ||
| 387 | \\ const l = k + c; // 217 | ||
| 388 | \\ const m = l * d; // 2170 | ||
| 389 | \\ const n = m + e; // 2184 | ||
| 390 | \\ const o = n * f; // 52416 | ||
| 391 | \\ const p = o + g; // 52454 | ||
| 392 | \\ const q = p * h; // 3252148 | ||
| 393 | \\ const r = q + i; // 3252248 | ||
| 394 | \\ const s = r * j; // 357747280 | ||
| 395 | \\ const t = s + k; // 357747490 | ||
| 396 | \\ break :blk t; | ||
| 397 | \\ }; | ||
| 398 | \\ const y = x + a; // 357747493 | ||
| 399 | \\ const z = y + a; // 357747496 | ||
| 400 | \\ return z; | ||
| 401 | \\} | ||
| 402 | \\ | ||
| 403 | \\fn assert(ok: bool) void { | ||
| 404 | \\ if (!ok) unreachable; | ||
| 405 | \\} | ||
| 406 | , | ||
| 407 | "", | ||
| 408 | ); | ||
| 370 | } | 409 | } |
| 371 | } | 410 | } |