| 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 | 20 | wget -nv "https://ziglang.org/deps/$CACHE_BASENAME.tar.xz" |
| 21 | 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 | 24 | wget -nv "https://ziglang.org/deps/$QEMUBASE.tar.xz" |
| 25 | 25 | tar xf "$QEMUBASE.tar.xz" |
| 26 | 26 | export PATH="$(pwd)/$QEMUBASE/bin:$PATH" |
lib/std/crypto/tlcsprng.zig+75-42| ... | ... | @@ -12,6 +12,7 @@ |
| 12 | 12 | const std = @import("std"); |
| 13 | 13 | const root = @import("root"); |
| 14 | 14 | const mem = std.mem; |
| 15 | const os = std.os; | |
| 15 | 16 | |
| 16 | 17 | /// We use this as a layer of indirection because global const pointers cannot |
| 17 | 18 | /// point to thread-local variables. |
| ... | ... | @@ -42,16 +43,12 @@ const maybe_have_wipe_on_fork = std.Target.current.os.isAtLeast(.linux, .{ |
| 42 | 43 | .minor = 14, |
| 43 | 44 | }) orelse true; |
| 44 | 45 | |
| 45 | const WipeMe = struct { | |
| 46 | init_state: enum { uninitialized, initialized, failed }, | |
| 46 | const Context = struct { | |
| 47 | init_state: enum(u8) { uninitialized = 0, initialized, failed }, | |
| 47 | 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) = .{ | |
| 52 | .gimli = undefined, | |
| 53 | .init_state = .uninitialized, | |
| 54 | }; | |
| 51 | threadlocal var wipe_mem: []align(mem.page_size) u8 = &[_]u8{}; | |
| 55 | 52 | |
| 56 | 53 | fn tlsCsprngFill(_: *const std.rand.Random, buffer: []u8) void { |
| 57 | 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 | 61 | if (comptime std.meta.globalOption("crypto_always_getrandom", bool) orelse false) { |
| 65 | 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 | 97 | .uninitialized => { |
| 69 | 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 { | |
| 98 | if (!want_fork_safety) { | |
| 94 | 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 | 128 | .initialized => { |
| 98 | 129 | return fillWithCsprng(buffer); |
| ... | ... | @@ -110,7 +141,8 @@ fn tlsCsprngFill(_: *const std.rand.Random, buffer: []u8) void { |
| 110 | 141 | fn setupPthreadAtforkAndFill(buffer: []u8) void { |
| 111 | 142 | const failed = std.c.pthread_atfork(null, null, childAtForkHandler) != 0; |
| 112 | 143 | if (failed) { |
| 113 | wipe_me.init_state = .failed; | |
| 144 | const ctx = @ptrCast(*Context, wipe_mem.ptr); | |
| 145 | ctx.init_state = .failed; | |
| 114 | 146 | return fillWithOsEntropy(buffer); |
| 115 | 147 | } else { |
| 116 | 148 | return initAndFill(buffer); |
| ... | ... | @@ -118,21 +150,21 @@ fn setupPthreadAtforkAndFill(buffer: []u8) void { |
| 118 | 150 | } |
| 119 | 151 | |
| 120 | 152 | fn childAtForkHandler() callconv(.C) void { |
| 121 | const wipe_slice = @ptrCast([*]u8, &wipe_me)[0..@sizeOf(@TypeOf(wipe_me))]; | |
| 122 | std.crypto.utils.secureZero(u8, wipe_slice); | |
| 153 | std.crypto.utils.secureZero(u8, wipe_mem); | |
| 123 | 154 | } |
| 124 | 155 | |
| 125 | 156 | fn fillWithCsprng(buffer: []u8) void { |
| 157 | const ctx = @ptrCast(*Context, wipe_mem.ptr); | |
| 126 | 158 | if (buffer.len != 0) { |
| 127 | wipe_me.gimli.squeeze(buffer); | |
| 159 | ctx.gimli.squeeze(buffer); | |
| 128 | 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 | 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 | 170 | fn initAndFill(buffer: []u8) void { |
| ... | ... | @@ -147,11 +179,12 @@ fn initAndFill(buffer: []u8) void { |
| 147 | 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 | 185 | // This is at the end so that accidental recursive dependencies result |
| 153 | 186 | // in stack overflows instead of invalid random data. |
| 154 | wipe_me.init_state = .initialized; | |
| 187 | ctx.init_state = .initialized; | |
| 155 | 188 | |
| 156 | 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 | 38 | |
| 39 | 39 | /// Imaginary part. |
| 40 | 40 | im: T, |
| 41 | ||
| 42 | /// Deprecated, use init() | |
| 43 | pub const new = init; | |
| 41 | 44 | |
| 42 | 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 | 47 | return Self{ |
| 45 | 48 | .re = re, |
| 46 | 49 | .im = im, |
| ... | ... | @@ -110,32 +113,32 @@ pub fn Complex(comptime T: type) type { |
| 110 | 113 | const epsilon = 0.0001; |
| 111 | 114 | |
| 112 | 115 | test "complex.add" { |
| 113 | const a = Complex(f32).new(5, 3); | |
| 114 | const b = Complex(f32).new(2, 7); | |
| 116 | const a = Complex(f32).init(5, 3); | |
| 117 | const b = Complex(f32).init(2, 7); | |
| 115 | 118 | const c = a.add(b); |
| 116 | 119 | |
| 117 | 120 | try testing.expect(c.re == 7 and c.im == 10); |
| 118 | 121 | } |
| 119 | 122 | |
| 120 | 123 | test "complex.sub" { |
| 121 | const a = Complex(f32).new(5, 3); | |
| 122 | const b = Complex(f32).new(2, 7); | |
| 124 | const a = Complex(f32).init(5, 3); | |
| 125 | const b = Complex(f32).init(2, 7); | |
| 123 | 126 | const c = a.sub(b); |
| 124 | 127 | |
| 125 | 128 | try testing.expect(c.re == 3 and c.im == -4); |
| 126 | 129 | } |
| 127 | 130 | |
| 128 | 131 | test "complex.mul" { |
| 129 | const a = Complex(f32).new(5, 3); | |
| 130 | const b = Complex(f32).new(2, 7); | |
| 132 | const a = Complex(f32).init(5, 3); | |
| 133 | const b = Complex(f32).init(2, 7); | |
| 131 | 134 | const c = a.mul(b); |
| 132 | 135 | |
| 133 | 136 | try testing.expect(c.re == -11 and c.im == 41); |
| 134 | 137 | } |
| 135 | 138 | |
| 136 | 139 | test "complex.div" { |
| 137 | const a = Complex(f32).new(5, 3); | |
| 138 | const b = Complex(f32).new(2, 7); | |
| 140 | const a = Complex(f32).init(5, 3); | |
| 141 | const b = Complex(f32).init(2, 7); | |
| 139 | 142 | const c = a.div(b); |
| 140 | 143 | |
| 141 | 144 | try testing.expect(math.approxEqAbs(f32, c.re, @as(f32, 31) / 53, epsilon) and |
| ... | ... | @@ -143,14 +146,14 @@ test "complex.div" { |
| 143 | 146 | } |
| 144 | 147 | |
| 145 | 148 | test "complex.conjugate" { |
| 146 | const a = Complex(f32).new(5, 3); | |
| 149 | const a = Complex(f32).init(5, 3); | |
| 147 | 150 | const c = a.conjugate(); |
| 148 | 151 | |
| 149 | 152 | try testing.expect(c.re == 5 and c.im == -3); |
| 150 | 153 | } |
| 151 | 154 | |
| 152 | 155 | test "complex.reciprocal" { |
| 153 | const a = Complex(f32).new(5, 3); | |
| 156 | const a = Complex(f32).init(5, 3); | |
| 154 | 157 | const c = a.reciprocal(); |
| 155 | 158 | |
| 156 | 159 | try testing.expect(math.approxEqAbs(f32, c.re, @as(f32, 5) / 34, epsilon) and |
| ... | ... | @@ -158,7 +161,7 @@ test "complex.reciprocal" { |
| 158 | 161 | } |
| 159 | 162 | |
| 160 | 163 | test "complex.magnitude" { |
| 161 | const a = Complex(f32).new(5, 3); | |
| 164 | const a = Complex(f32).init(5, 3); | |
| 162 | 165 | const c = a.magnitude(); |
| 163 | 166 | |
| 164 | 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 | 18 | const epsilon = 0.0001; |
| 19 | 19 | |
| 20 | 20 | test "complex.cabs" { |
| 21 | const a = Complex(f32).new(5, 3); | |
| 21 | const a = Complex(f32).init(5, 3); | |
| 22 | 22 | const c = abs(a); |
| 23 | 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 | pub fn acos(z: anytype) Complex(@TypeOf(z.re)) { |
| 14 | 14 | const T = @TypeOf(z.re); |
| 15 | 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 | 19 | const epsilon = 0.0001; |
| 20 | 20 | |
| 21 | 21 | test "complex.cacos" { |
| 22 | const a = Complex(f32).new(5, 3); | |
| 22 | const a = Complex(f32).init(5, 3); | |
| 23 | 23 | const c = acos(a); |
| 24 | 24 | |
| 25 | 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 | pub fn acosh(z: anytype) Complex(@TypeOf(z.re)) { |
| 14 | 14 | const T = @TypeOf(z.re); |
| 15 | 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 | 19 | const epsilon = 0.0001; |
| 20 | 20 | |
| 21 | 21 | test "complex.cacosh" { |
| 22 | const a = Complex(f32).new(5, 3); | |
| 22 | const a = Complex(f32).init(5, 3); | |
| 23 | 23 | const c = acosh(a); |
| 24 | 24 | |
| 25 | 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 | 18 | const epsilon = 0.0001; |
| 19 | 19 | |
| 20 | 20 | test "complex.carg" { |
| 21 | const a = Complex(f32).new(5, 3); | |
| 21 | const a = Complex(f32).init(5, 3); | |
| 22 | 22 | const c = arg(a); |
| 23 | 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 | 15 | const x = z.re; |
| 16 | 16 | const y = z.im; |
| 17 | 17 | |
| 18 | const p = Complex(T).new(1.0 - (x - y) * (x + y), -2.0 * x * y); | |
| 19 | const q = Complex(T).new(-y, x); | |
| 18 | const p = Complex(T).init(1.0 - (x - y) * (x + y), -2.0 * x * y); | |
| 19 | const q = Complex(T).init(-y, x); | |
| 20 | 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 | 25 | const epsilon = 0.0001; |
| 26 | 26 | |
| 27 | 27 | test "complex.casin" { |
| 28 | const a = Complex(f32).new(5, 3); | |
| 28 | const a = Complex(f32).init(5, 3); | |
| 29 | 29 | const c = asin(a); |
| 30 | 30 | |
| 31 | 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 | 12 | /// Returns the hyperbolic arc-sine of z. |
| 13 | 13 | pub fn asinh(z: anytype) Complex(@TypeOf(z.re)) { |
| 14 | 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 | 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 | 20 | const epsilon = 0.0001; |
| 21 | 21 | |
| 22 | 22 | test "complex.casinh" { |
| 23 | const a = Complex(f32).new(5, 3); | |
| 23 | const a = Complex(f32).init(5, 3); | |
| 24 | 24 | const c = asinh(a); |
| 25 | 25 | |
| 26 | 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 | 49 | |
| 50 | 50 | if ((x == 0.0) and (y > 1.0)) { |
| 51 | 51 | // overflow |
| 52 | return Complex(f32).new(maxnum, maxnum); | |
| 52 | return Complex(f32).init(maxnum, maxnum); | |
| 53 | 53 | } |
| 54 | 54 | |
| 55 | 55 | const x2 = x * x; |
| 56 | 56 | var a = 1.0 - x2 - (y * y); |
| 57 | 57 | if (a == 0.0) { |
| 58 | 58 | // overflow |
| 59 | return Complex(f32).new(maxnum, maxnum); | |
| 59 | return Complex(f32).init(maxnum, maxnum); | |
| 60 | 60 | } |
| 61 | 61 | |
| 62 | 62 | var t = 0.5 * math.atan2(f32, 2.0 * x, a); |
| ... | ... | @@ -66,12 +66,12 @@ fn atan32(z: Complex(f32)) Complex(f32) { |
| 66 | 66 | a = x2 + t * t; |
| 67 | 67 | if (a == 0.0) { |
| 68 | 68 | // overflow |
| 69 | return Complex(f32).new(maxnum, maxnum); | |
| 69 | return Complex(f32).init(maxnum, maxnum); | |
| 70 | 70 | } |
| 71 | 71 | |
| 72 | 72 | t = y + 1.0; |
| 73 | 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 | 77 | fn redupif64(x: f64) f64 { |
| ... | ... | @@ -98,14 +98,14 @@ fn atan64(z: Complex(f64)) Complex(f64) { |
| 98 | 98 | |
| 99 | 99 | if ((x == 0.0) and (y > 1.0)) { |
| 100 | 100 | // overflow |
| 101 | return Complex(f64).new(maxnum, maxnum); | |
| 101 | return Complex(f64).init(maxnum, maxnum); | |
| 102 | 102 | } |
| 103 | 103 | |
| 104 | 104 | const x2 = x * x; |
| 105 | 105 | var a = 1.0 - x2 - (y * y); |
| 106 | 106 | if (a == 0.0) { |
| 107 | 107 | // overflow |
| 108 | return Complex(f64).new(maxnum, maxnum); | |
| 108 | return Complex(f64).init(maxnum, maxnum); | |
| 109 | 109 | } |
| 110 | 110 | |
| 111 | 111 | var t = 0.5 * math.atan2(f64, 2.0 * x, a); |
| ... | ... | @@ -115,18 +115,18 @@ fn atan64(z: Complex(f64)) Complex(f64) { |
| 115 | 115 | a = x2 + t * t; |
| 116 | 116 | if (a == 0.0) { |
| 117 | 117 | // overflow |
| 118 | return Complex(f64).new(maxnum, maxnum); | |
| 118 | return Complex(f64).init(maxnum, maxnum); | |
| 119 | 119 | } |
| 120 | 120 | |
| 121 | 121 | t = y + 1.0; |
| 122 | 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 | 126 | const epsilon = 0.0001; |
| 127 | 127 | |
| 128 | 128 | test "complex.catan32" { |
| 129 | const a = Complex(f32).new(5, 3); | |
| 129 | const a = Complex(f32).init(5, 3); | |
| 130 | 130 | const c = atan(a); |
| 131 | 131 | |
| 132 | 132 | try testing.expect(math.approxEqAbs(f32, c.re, 1.423679, epsilon)); |
| ... | ... | @@ -134,7 +134,7 @@ test "complex.catan32" { |
| 134 | 134 | } |
| 135 | 135 | |
| 136 | 136 | test "complex.catan64" { |
| 137 | const a = Complex(f64).new(5, 3); | |
| 137 | const a = Complex(f64).init(5, 3); | |
| 138 | 138 | const c = atan(a); |
| 139 | 139 | |
| 140 | 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 | 12 | /// Returns the hyperbolic arc-tangent of z. |
| 13 | 13 | pub fn atanh(z: anytype) Complex(@TypeOf(z.re)) { |
| 14 | 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 | 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 | 20 | const epsilon = 0.0001; |
| 21 | 21 | |
| 22 | 22 | test "complex.catanh" { |
| 23 | const a = Complex(f32).new(5, 3); | |
| 23 | const a = Complex(f32).init(5, 3); | |
| 24 | 24 | const c = atanh(a); |
| 25 | 25 | |
| 26 | 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 | 12 | /// Returns the complex conjugate of z. |
| 13 | 13 | pub fn conj(z: anytype) Complex(@TypeOf(z.re)) { |
| 14 | 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 | 18 | test "complex.conj" { |
| 19 | const a = Complex(f32).new(5, 3); | |
| 19 | const a = Complex(f32).init(5, 3); | |
| 20 | 20 | const c = a.conjugate(); |
| 21 | 21 | |
| 22 | 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 | 12 | /// Returns the cosine of z. |
| 13 | 13 | pub fn cos(z: anytype) Complex(@TypeOf(z.re)) { |
| 14 | 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 | 16 | return cmath.cosh(p); |
| 17 | 17 | } |
| 18 | 18 | |
| 19 | 19 | const epsilon = 0.0001; |
| 20 | 20 | |
| 21 | 21 | test "complex.ccos" { |
| 22 | const a = Complex(f32).new(5, 3); | |
| 22 | const a = Complex(f32).init(5, 3); | |
| 23 | 23 | const c = cos(a); |
| 24 | 24 | |
| 25 | 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 | 39 | |
| 40 | 40 | if (ix < 0x7f800000 and iy < 0x7f800000) { |
| 41 | 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 | 44 | // small x: normal case |
| 45 | 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 | 49 | // |x|>= 9, so cosh(x) ~= exp(|x|) |
| 50 | 50 | if (ix < 0x42b17218) { |
| 51 | 51 | // x < 88.7: exp(|x|) won't overflow |
| 52 | 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 | 55 | // x < 192.7: scale to avoid overflow |
| 56 | 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 | 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 | 61 | // x >= 192.7: result always overflows |
| 62 | 62 | else { |
| 63 | 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 | 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 | 72 | if (iy == 0 and ix >= 0x7f800000) { |
| 73 | 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 | 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 | 83 | if (ix >= 0x7f800000 and (hx & 0x7fffff) == 0) { |
| 84 | 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 | 93 | fn cosh64(z: Complex(f64)) Complex(f64) { |
| ... | ... | @@ -107,61 +107,61 @@ fn cosh64(z: Complex(f64)) Complex(f64) { |
| 107 | 107 | // nearly non-exceptional case where x, y are finite |
| 108 | 108 | if (ix < 0x7ff00000 and iy < 0x7ff00000) { |
| 109 | 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 | 112 | // small x: normal case |
| 113 | 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 | 117 | // |x|>= 22, so cosh(x) ~= exp(|x|) |
| 118 | 118 | if (ix < 0x40862e42) { |
| 119 | 119 | // x < 710: exp(|x|) won't overflow |
| 120 | 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 | 123 | // x < 1455: scale to avoid overflow |
| 124 | 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 | 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 | 129 | // x >= 1455: result always overflows |
| 130 | 130 | else { |
| 131 | 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 | 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 | 140 | if (iy | ly == 0 and ix >= 0x7ff00000) { |
| 141 | 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 | 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 | 151 | if (ix >= 0x7ff00000 and (hx & 0xfffff) | lx == 0) { |
| 152 | 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 | 161 | const epsilon = 0.0001; |
| 162 | 162 | |
| 163 | 163 | test "complex.ccosh32" { |
| 164 | const a = Complex(f32).new(5, 3); | |
| 164 | const a = Complex(f32).init(5, 3); | |
| 165 | 165 | const c = cosh(a); |
| 166 | 166 | |
| 167 | 167 | try testing.expect(math.approxEqAbs(f32, c.re, -73.467300, epsilon)); |
| ... | ... | @@ -169,7 +169,7 @@ test "complex.ccosh32" { |
| 169 | 169 | } |
| 170 | 170 | |
| 171 | 171 | test "complex.ccosh64" { |
| 172 | const a = Complex(f64).new(5, 3); | |
| 172 | const a = Complex(f64).init(5, 3); | |
| 173 | 173 | const c = cosh(a); |
| 174 | 174 | |
| 175 | 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 | 38 | const hy = @bitCast(u32, y) & 0x7fffffff; |
| 39 | 39 | // cexp(x + i0) = exp(x) + i0 |
| 40 | 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 | 44 | const hx = @bitCast(u32, x); |
| 45 | 45 | // cexp(0 + iy) = cos(y) + isin(y) |
| 46 | 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 | 50 | if (hy >= 0x7f800000) { |
| 51 | 51 | // cexp(finite|nan +- i inf|nan) = nan + i nan |
| 52 | 52 | if ((hx & 0x7fffffff) != 0x7f800000) { |
| 53 | return Complex(f32).new(y - y, y - y); | |
| 53 | return Complex(f32).init(y - y, y - y); | |
| 54 | 54 | } // cexp(-inf +- i inf|nan) = 0 + i0 |
| 55 | 55 | else if (hx & 0x80000000 != 0) { |
| 56 | return Complex(f32).new(0, 0); | |
| 56 | return Complex(f32).init(0, 0); | |
| 57 | 57 | } // cexp(+inf +- i inf|nan) = inf + i nan |
| 58 | 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 | 69 | // - x = nan |
| 70 | 70 | else { |
| 71 | 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 | 86 | |
| 87 | 87 | // cexp(x + i0) = exp(x) + i0 |
| 88 | 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 | 92 | const fx = @bitCast(u64, x); |
| ... | ... | @@ -95,19 +95,19 @@ fn exp64(z: Complex(f64)) Complex(f64) { |
| 95 | 95 | |
| 96 | 96 | // cexp(0 + iy) = cos(y) + isin(y) |
| 97 | 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 | 101 | if (hy >= 0x7ff00000) { |
| 102 | 102 | // cexp(finite|nan +- i inf|nan) = nan + i nan |
| 103 | 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 | 105 | } // cexp(-inf +- i inf|nan) = 0 + i0 |
| 106 | 106 | else if (hx & 0x80000000 != 0) { |
| 107 | return Complex(f64).new(0, 0); | |
| 107 | return Complex(f64).init(0, 0); | |
| 108 | 108 | } // cexp(+inf +- i inf|nan) = inf + i nan |
| 109 | 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 | 120 | // - x = nan |
| 121 | 121 | else { |
| 122 | 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 | 127 | const epsilon = 0.0001; |
| 128 | 128 | |
| 129 | 129 | test "complex.cexp32" { |
| 130 | const a = Complex(f32).new(5, 3); | |
| 130 | const a = Complex(f32).init(5, 3); | |
| 131 | 131 | const c = exp(a); |
| 132 | 132 | |
| 133 | 133 | try testing.expect(math.approxEqAbs(f32, c.re, -146.927917, epsilon)); |
| ... | ... | @@ -135,7 +135,7 @@ test "complex.cexp32" { |
| 135 | 135 | } |
| 136 | 136 | |
| 137 | 137 | test "complex.cexp64" { |
| 138 | const a = Complex(f64).new(5, 3); | |
| 138 | const a = Complex(f64).init(5, 3); | |
| 139 | 139 | const c = exp(a); |
| 140 | 140 | |
| 141 | 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 | 48 | const half_expt2 = exptf - half_expt1; |
| 49 | 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 | 54 | fn frexp_exp64(x: f64, expt: *i32) f64 { |
| ... | ... | @@ -78,7 +78,7 @@ fn ldexp_cexp64(z: Complex(f64), expt: i32) Complex(f64) { |
| 78 | 78 | const half_expt2 = exptf - half_expt1; |
| 79 | 79 | const scale2 = @bitCast(f64, (0x3ff + half_expt2) << 20); |
| 80 | 80 | |
| 81 | return Complex(f64).new( | |
| 81 | return Complex(f64).init( | |
| 82 | 82 | math.cos(z.im) * exp_x * scale1 * scale2, |
| 83 | 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 | 15 | const r = cmath.abs(z); |
| 16 | 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 | 21 | const epsilon = 0.0001; |
| 22 | 22 | |
| 23 | 23 | test "complex.clog" { |
| 24 | const a = Complex(f32).new(5, 3); | |
| 24 | const a = Complex(f32).init(5, 3); | |
| 25 | 25 | const c = log(a); |
| 26 | 26 | |
| 27 | 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 | 19 | const epsilon = 0.0001; |
| 20 | 20 | |
| 21 | 21 | test "complex.cpow" { |
| 22 | const a = Complex(f32).new(5, 3); | |
| 23 | const b = Complex(f32).new(2.3, -1.3); | |
| 22 | const a = Complex(f32).init(5, 3); | |
| 23 | const b = Complex(f32).init(2.3, -1.3); | |
| 24 | 24 | const c = pow(Complex(f32), a, b); |
| 25 | 25 | |
| 26 | 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 | 14 | const T = @TypeOf(z.re); |
| 15 | 15 | |
| 16 | 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 | 23 | const epsilon = 0.0001; |
| 24 | 24 | |
| 25 | 25 | test "complex.cproj" { |
| 26 | const a = Complex(f32).new(5, 3); | |
| 26 | const a = Complex(f32).init(5, 3); | |
| 27 | 27 | const c = proj(a); |
| 28 | 28 | |
| 29 | 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 | 12 | /// Returns the sine of z. |
| 13 | 13 | pub fn sin(z: anytype) Complex(@TypeOf(z.re)) { |
| 14 | 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 | 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 | 20 | const epsilon = 0.0001; |
| 21 | 21 | |
| 22 | 22 | test "complex.csin" { |
| 23 | const a = Complex(f32).new(5, 3); | |
| 23 | const a = Complex(f32).init(5, 3); | |
| 24 | 24 | const c = sin(a); |
| 25 | 25 | |
| 26 | 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 | 39 | |
| 40 | 40 | if (ix < 0x7f800000 and iy < 0x7f800000) { |
| 41 | 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 | 44 | // small x: normal case |
| 45 | 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 | 49 | // |x|>= 9, so cosh(x) ~= exp(|x|) |
| 50 | 50 | if (ix < 0x42b17218) { |
| 51 | 51 | // x < 88.7: exp(|x|) won't overflow |
| 52 | 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 | 55 | // x < 192.7: scale to avoid overflow |
| 56 | 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 | 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 | 61 | // x >= 192.7: result always overflows |
| 62 | 62 | else { |
| 63 | 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 | 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 | 72 | if (iy == 0 and ix >= 0x7f800000) { |
| 73 | 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 | 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 | 83 | if (ix >= 0x7f800000 and (hx & 0x7fffff) == 0) { |
| 84 | 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 | 93 | fn sinh64(z: Complex(f64)) Complex(f64) { |
| ... | ... | @@ -106,61 +106,61 @@ fn sinh64(z: Complex(f64)) Complex(f64) { |
| 106 | 106 | |
| 107 | 107 | if (ix < 0x7ff00000 and iy < 0x7ff00000) { |
| 108 | 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 | 111 | // small x: normal case |
| 112 | 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 | 116 | // |x|>= 22, so cosh(x) ~= exp(|x|) |
| 117 | 117 | if (ix < 0x40862e42) { |
| 118 | 118 | // x < 710: exp(|x|) won't overflow |
| 119 | 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 | 122 | // x < 1455: scale to avoid overflow |
| 123 | 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 | 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 | 128 | // x >= 1455: result always overflows |
| 129 | 129 | else { |
| 130 | 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 | 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 | 139 | if (iy | ly == 0 and ix >= 0x7ff00000) { |
| 140 | 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 | 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 | 150 | if (ix >= 0x7ff00000 and (hx & 0xfffff) | lx == 0) { |
| 151 | 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 | 160 | const epsilon = 0.0001; |
| 161 | 161 | |
| 162 | 162 | test "complex.csinh32" { |
| 163 | const a = Complex(f32).new(5, 3); | |
| 163 | const a = Complex(f32).init(5, 3); | |
| 164 | 164 | const c = sinh(a); |
| 165 | 165 | |
| 166 | 166 | try testing.expect(math.approxEqAbs(f32, c.re, -73.460617, epsilon)); |
| ... | ... | @@ -168,7 +168,7 @@ test "complex.csinh32" { |
| 168 | 168 | } |
| 169 | 169 | |
| 170 | 170 | test "complex.csinh64" { |
| 171 | const a = Complex(f64).new(5, 3); | |
| 171 | const a = Complex(f64).init(5, 3); | |
| 172 | 172 | const c = sinh(a); |
| 173 | 173 | |
| 174 | 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 | 32 | const y = z.im; |
| 33 | 33 | |
| 34 | 34 | if (x == 0 and y == 0) { |
| 35 | return Complex(f32).new(0, y); | |
| 35 | return Complex(f32).init(0, y); | |
| 36 | 36 | } |
| 37 | 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 | 40 | if (math.isNan(x)) { |
| 41 | 41 | // raise invalid if y is not nan |
| 42 | 42 | const t = (y - y) / (y - y); |
| 43 | return Complex(f32).new(x, t); | |
| 43 | return Complex(f32).init(x, t); | |
| 44 | 44 | } |
| 45 | 45 | if (math.isInf(x)) { |
| 46 | 46 | // sqrt(inf + i nan) = inf + nan i |
| ... | ... | @@ -48,9 +48,9 @@ fn sqrt32(z: Complex(f32)) Complex(f32) { |
| 48 | 48 | // sqrt(-inf + i nan) = nan +- inf i |
| 49 | 49 | // sqrt(-inf + iy) = 0 + inf i |
| 50 | 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 | 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 | 62 | |
| 63 | 63 | if (dx >= 0) { |
| 64 | 64 | const t = math.sqrt((dx + math.hypot(f64, dx, dy)) * 0.5); |
| 65 | return Complex(f32).new( | |
| 65 | return Complex(f32).init( | |
| 66 | 66 | @floatCast(f32, t), |
| 67 | 67 | @floatCast(f32, dy / (2.0 * t)), |
| 68 | 68 | ); |
| 69 | 69 | } else { |
| 70 | 70 | const t = math.sqrt((-dx + math.hypot(f64, dx, dy)) * 0.5); |
| 71 | return Complex(f32).new( | |
| 71 | return Complex(f32).init( | |
| 72 | 72 | @floatCast(f32, math.fabs(y) / (2.0 * t)), |
| 73 | 73 | @floatCast(f32, math.copysign(f64, t, y)), |
| 74 | 74 | ); |
| ... | ... | @@ -83,15 +83,15 @@ fn sqrt64(z: Complex(f64)) Complex(f64) { |
| 83 | 83 | var y = z.im; |
| 84 | 84 | |
| 85 | 85 | if (x == 0 and y == 0) { |
| 86 | return Complex(f64).new(0, y); | |
| 86 | return Complex(f64).init(0, y); | |
| 87 | 87 | } |
| 88 | 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 | 91 | if (math.isNan(x)) { |
| 92 | 92 | // raise invalid if y is not nan |
| 93 | 93 | const t = (y - y) / (y - y); |
| 94 | return Complex(f64).new(x, t); | |
| 94 | return Complex(f64).init(x, t); | |
| 95 | 95 | } |
| 96 | 96 | if (math.isInf(x)) { |
| 97 | 97 | // sqrt(inf + i nan) = inf + nan i |
| ... | ... | @@ -99,9 +99,9 @@ fn sqrt64(z: Complex(f64)) Complex(f64) { |
| 99 | 99 | // sqrt(-inf + i nan) = nan +- inf i |
| 100 | 100 | // sqrt(-inf + iy) = 0 + inf i |
| 101 | 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 | 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 | 118 | var result: Complex(f64) = undefined; |
| 119 | 119 | if (x >= 0) { |
| 120 | 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 | 122 | } else { |
| 123 | 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 | 127 | if (scale) { |
| ... | ... | @@ -135,7 +135,7 @@ fn sqrt64(z: Complex(f64)) Complex(f64) { |
| 135 | 135 | const epsilon = 0.0001; |
| 136 | 136 | |
| 137 | 137 | test "complex.csqrt32" { |
| 138 | const a = Complex(f32).new(5, 3); | |
| 138 | const a = Complex(f32).init(5, 3); | |
| 139 | 139 | const c = sqrt(a); |
| 140 | 140 | |
| 141 | 141 | try testing.expect(math.approxEqAbs(f32, c.re, 2.327117, epsilon)); |
| ... | ... | @@ -143,7 +143,7 @@ test "complex.csqrt32" { |
| 143 | 143 | } |
| 144 | 144 | |
| 145 | 145 | test "complex.csqrt64" { |
| 146 | const a = Complex(f64).new(5, 3); | |
| 146 | const a = Complex(f64).init(5, 3); | |
| 147 | 147 | const c = sqrt(a); |
| 148 | 148 | |
| 149 | 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 | 12 | /// Returns the tanget of z. |
| 13 | 13 | pub fn tan(z: anytype) Complex(@TypeOf(z.re)) { |
| 14 | 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 | 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 | 20 | const epsilon = 0.0001; |
| 21 | 21 | |
| 22 | 22 | test "complex.ctan" { |
| 23 | const a = Complex(f32).new(5, 3); | |
| 23 | const a = Complex(f32).init(5, 3); | |
| 24 | 24 | const c = tan(a); |
| 25 | 25 | |
| 26 | 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 | 35 | if (ix >= 0x7f800000) { |
| 36 | 36 | if (ix & 0x7fffff != 0) { |
| 37 | 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 | 40 | const xx = @bitCast(f32, hx - 0x40000000); |
| 41 | 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 | 45 | if (!math.isFinite(y)) { |
| 46 | 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 | 50 | // x >= 11 |
| 51 | 51 | if (ix >= 0x41300000) { |
| 52 | 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 | 56 | // Kahan's algorithm |
| ... | ... | @@ -60,7 +60,7 @@ fn tanh32(z: Complex(f32)) Complex(f32) { |
| 60 | 60 | const rho = math.sqrt(1 + s * s); |
| 61 | 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 | 66 | fn tanh64(z: Complex(f64)) Complex(f64) { |
| ... | ... | @@ -77,23 +77,23 @@ fn tanh64(z: Complex(f64)) Complex(f64) { |
| 77 | 77 | if (ix >= 0x7ff00000) { |
| 78 | 78 | if ((ix & 0x7fffff) | lx != 0) { |
| 79 | 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 | 83 | const xx = @bitCast(f64, (@as(u64, hx - 0x40000000) << 32) | lx); |
| 84 | 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 | 88 | if (!math.isFinite(y)) { |
| 89 | 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 | 93 | // x >= 22 |
| 94 | 94 | if (ix >= 0x40360000) { |
| 95 | 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 | 99 | // Kahan's algorithm |
| ... | ... | @@ -103,13 +103,13 @@ fn tanh64(z: Complex(f64)) Complex(f64) { |
| 103 | 103 | const rho = math.sqrt(1 + s * s); |
| 104 | 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 | 109 | const epsilon = 0.0001; |
| 110 | 110 | |
| 111 | 111 | test "complex.ctanh32" { |
| 112 | const a = Complex(f32).new(5, 3); | |
| 112 | const a = Complex(f32).init(5, 3); | |
| 113 | 113 | const c = tanh(a); |
| 114 | 114 | |
| 115 | 115 | try testing.expect(math.approxEqAbs(f32, c.re, 0.999913, epsilon)); |
| ... | ... | @@ -117,7 +117,7 @@ test "complex.ctanh32" { |
| 117 | 117 | } |
| 118 | 118 | |
| 119 | 119 | test "complex.ctanh64" { |
| 120 | const a = Complex(f64).new(5, 3); | |
| 120 | const a = Complex(f64).init(5, 3); | |
| 121 | 121 | const c = tanh(a); |
| 122 | 122 | |
| 123 | 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 | 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 | 301 | test "std.meta.trait.isNumber" { |
| 316 | 302 | const NotANumber = struct { |
| 317 | 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 | 1244 | |
| 1245 | 1245 | EFAULT => unreachable, |
| 1246 | 1246 | EINVAL => unreachable, |
| 1247 | EBADF => unreachable, | |
| 1247 | 1248 | EACCES => return error.AccessDenied, |
| 1248 | 1249 | EFBIG => return error.FileTooBig, |
| 1249 | 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 | 1608 | \\comptime { |
| 1609 | 1609 | \\ // cexp(finite|nan +- i inf|nan) = nan + i nan |
| 1610 | 1610 | \\ if ((hx & 0x7fffffff) != 0x7f800000) { |
| 1611 | \\ return Complex(f32).new(y - y, y - y); | |
| 1611 | \\ return Complex(f32).init(y - y, y - y); | |
| 1612 | 1612 | \\ } // cexp(-inf +- i inf|nan) = 0 + i0 |
| 1613 | 1613 | \\ else if (hx & 0x80000000 != 0) { |
| 1614 | \\ return Complex(f32).new(0, 0); | |
| 1614 | \\ return Complex(f32).init(0, 0); | |
| 1615 | 1615 | \\ } // cexp(+inf +- i inf|nan) = inf + i nan |
| 1616 | 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 | 2267 | \\test "aoeu" { |
| 2268 | 2268 | \\ // cexp(finite|nan +- i inf|nan) = nan + i nan |
| 2269 | 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 | 2272 | \\ // cexp(-inf +- i inf|nan) = 0 + i0 |
| 2273 | 2273 | \\ else if (hx & 0x80000000 != 0) { |
| 2274 | \\ return Complex(f32).new(0, 0); | |
| 2274 | \\ return Complex(f32).init(0, 0); | |
| 2275 | 2275 | \\ } |
| 2276 | 2276 | \\ // cexp(+inf +- i inf|nan) = inf + i nan |
| 2277 | 2277 | \\ // another comment |
| 2278 | 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 | 4430 | log.warn("unable to load {s}: {s}", .{ |
| 4431 | 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 | 4435 | const switch_node = decl.relativeToNodeIndex(switch_node_offset); |
| 4436 | 4436 | const main_tokens = tree.nodes.items(.main_token); |
src/Sema.zig+7-8| ... | ... | @@ -4229,19 +4229,18 @@ fn resolveSwitchItemVal( |
| 4229 | 4229 | switch_prong_src: Module.SwitchProngSrc, |
| 4230 | 4230 | range_expand: Module.SwitchProngSrc.RangeExpand, |
| 4231 | 4231 | ) InnerError!TypedValue { |
| 4232 | const mod = sema.mod; | |
| 4233 | 4232 | const item = try sema.resolveInst(item_ref); |
| 4234 | 4233 | // We have to avoid the other helper functions here because we cannot construct a LazySrcLoc |
| 4235 | 4234 | // because we only have the switch AST node. Only if we know for sure we need to report |
| 4236 | 4235 | // a compile error do we resolve the full source locations. |
| 4237 | 4236 | if (item.value()) |val| { |
| 4238 | 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 | 4239 | return sema.failWithUseOfUndef(block, src); |
| 4241 | 4240 | } |
| 4242 | 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 | 4244 | return sema.failWithNeededComptime(block, src); |
| 4246 | 4245 | } |
| 4247 | 4246 | |
| ... | ... | @@ -4285,7 +4284,7 @@ fn validateSwitchItemEnum( |
| 4285 | 4284 | const item_tv = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none); |
| 4286 | 4285 | const field_index = item_tv.ty.enumTagFieldIndex(item_tv.val) orelse { |
| 4287 | 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 | 4288 | const msg = try mod.errMsg( |
| 4290 | 4289 | &block.base, |
| 4291 | 4290 | src, |
| ... | ... | @@ -4317,8 +4316,9 @@ fn validateSwitchDupe( |
| 4317 | 4316 | ) InnerError!void { |
| 4318 | 4317 | const prev_prong_src = maybe_prev_src orelse return; |
| 4319 | 4318 | const mod = sema.mod; |
| 4320 | const src = switch_prong_src.resolve(mod, block.src_decl, src_node_offset, .none); | |
| 4321 | const prev_src = prev_prong_src.resolve(mod, block.src_decl, src_node_offset, .none); | |
| 4319 | const gpa = sema.gpa; | |
| 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 | 4322 | const msg = msg: { |
| 4323 | 4323 | const msg = try mod.errMsg( |
| 4324 | 4324 | &block.base, |
| ... | ... | @@ -4355,7 +4355,7 @@ fn validateSwitchItemBool( |
| 4355 | 4355 | false_count.* += 1; |
| 4356 | 4356 | } |
| 4357 | 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 | 4359 | return sema.mod.fail(&block.base, src, "duplicate switch value", .{}); |
| 4360 | 4360 | } |
| 4361 | 4361 | } |
| ... | ... | @@ -7584,4 +7584,3 @@ fn enumFieldSrcLoc( |
| 7584 | 7584 | } |
| 7585 | 7585 | } else unreachable; |
| 7586 | 7586 | } |
| 7587 |
src/clang.zig+4-1| ... | ... | @@ -1,3 +1,4 @@ |
| 1 | const std = @import("std"); | |
| 1 | 2 | pub const builtin = @import("builtin"); |
| 2 | 3 | |
| 3 | 4 | pub const SourceLocation = extern struct { |
| ... | ... | @@ -115,7 +116,9 @@ pub const APFloatBaseSemantics = extern enum { |
| 115 | 116 | }; |
| 116 | 117 | |
| 117 | 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 | 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 | 1571 | const lhs = try self.resolveInst(op_lhs); |
| 1572 | 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 | 1579 | // Destination must be a register |
| 1575 | 1580 | // LHS must be a register |
| 1576 | 1581 | // RHS must be a register |
| 1577 | 1582 | var dst_mcv: MCValue = undefined; |
| 1578 | var lhs_mcv: MCValue = undefined; | |
| 1579 | var rhs_mcv: MCValue = undefined; | |
| 1580 | if (self.reuseOperand(inst, 0, lhs)) { | |
| 1581 | // LHS is the destination | |
| 1582 | lhs_mcv = if (lhs != .register) try self.copyToNewRegister(inst, lhs) else lhs; | |
| 1583 | rhs_mcv = if (rhs != .register) try self.copyToNewRegister(inst, rhs) else rhs; | |
| 1584 | dst_mcv = lhs_mcv; | |
| 1585 | } else if (self.reuseOperand(inst, 1, rhs)) { | |
| 1586 | // RHS is the destination | |
| 1587 | lhs_mcv = if (lhs != .register) try self.copyToNewRegister(inst, lhs) else lhs; | |
| 1588 | rhs_mcv = if (rhs != .register) try self.copyToNewRegister(inst, rhs) else rhs; | |
| 1589 | dst_mcv = rhs_mcv; | |
| 1583 | var lhs_mcv: MCValue = lhs; | |
| 1584 | var rhs_mcv: MCValue = rhs; | |
| 1585 | ||
| 1586 | // Allocate registers for operands and/or destination | |
| 1587 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; | |
| 1588 | if (reuse_lhs) { | |
| 1589 | // Allocate 0 or 1 registers | |
| 1590 | if (!rhs_is_register) { | |
| 1591 | rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(op_rhs, &.{lhs.register}) }; | |
| 1592 | branch.inst_table.putAssumeCapacity(op_rhs, rhs_mcv); | |
| 1593 | } | |
| 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 | 1602 | } else { |
| 1591 | // TODO save 1 copy instruction by directly allocating the destination register | |
| 1592 | // LHS is the destination | |
| 1593 | lhs_mcv = try self.copyToNewRegister(inst, lhs); | |
| 1594 | rhs_mcv = if (rhs != .register) try self.copyToNewRegister(inst, rhs) else rhs; | |
| 1595 | dst_mcv = lhs_mcv; | |
| 1603 | // Allocate 1 or 2 registers | |
| 1604 | if (lhs_is_register and rhs_is_register) { | |
| 1605 | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{ lhs.register, rhs.register }) }; | |
| 1606 | } else if (lhs_is_register) { | |
| 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 | 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 | 1 | const std = @import("std"); |
| 2 | 2 | const Allocator = std.mem.Allocator; |
| 3 | const Target = std.Target; | |
| 3 | 4 | const log = std.log.scoped(.codegen); |
| 4 | 5 | |
| 5 | 6 | const spec = @import("spirv/spec.zig"); |
| 7 | const Opcode = spec.Opcode; | |
| 8 | ||
| 6 | 9 | const Module = @import("../Module.zig"); |
| 7 | 10 | const Decl = Module.Decl; |
| 8 | 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 | 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 { | |
| 13 | const word_count = @intCast(u32, args.len + 1); | |
| 14 | try code.append((word_count << 16) | @enumToInt(instr)); | |
| 25 | pub fn writeInstruction(code: *std.ArrayList(u32), opcode: Opcode, args: []const u32) !void { | |
| 26 | try writeOpcode(code, opcode, @intCast(u32, args.len)); | |
| 15 | 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 | 32 | pub const SPIRVModule = struct { |
| 19 | next_result_id: u32 = 0, | |
| 20 | ||
| 21 | target: std.Target, | |
| 22 | ||
| 23 | types: TypeMap, | |
| 24 | ||
| 25 | types_and_globals: std.ArrayList(u32), | |
| 33 | next_result_id: u32, | |
| 34 | types_globals_constants: std.ArrayList(u32), | |
| 26 | 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 | 38 | return .{ |
| 30 | .target = target, | |
| 31 | .types = TypeMap.init(allocator), | |
| 32 | .types_and_globals = std.ArrayList(u32).init(allocator), | |
| 39 | .next_result_id = 1, // 0 is an invalid SPIR-V result ID. | |
| 40 | .types_globals_constants = std.ArrayList(u32).init(allocator), | |
| 33 | 41 | .fn_decls = std.ArrayList(u32).init(allocator), |
| 34 | 42 | }; |
| 35 | 43 | } |
| 36 | 44 | |
| 37 | 45 | pub fn deinit(self: *SPIRVModule) void { |
| 46 | self.types_globals_constants.deinit(); | |
| 38 | 47 | self.fn_decls.deinit(); |
| 39 | self.types_and_globals.deinit(); | |
| 40 | self.types.deinit(); | |
| 41 | self.* = undefined; | |
| 42 | 48 | } |
| 43 | 49 | |
| 44 | 50 | pub fn allocResultId(self: *SPIRVModule) u32 { |
| ... | ... | @@ -49,31 +55,310 @@ pub const SPIRVModule = struct { |
| 49 | 55 | pub fn resultIdBound(self: *SPIRVModule) u32 { |
| 50 | 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 | 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 | 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()) { | |
| 62 | .Void => try writeInstruction(&self.types_and_globals, .OpTypeVoid, &[_]u32{ result }), | |
| 63 | .Bool => try writeInstruction(&self.types_and_globals, .OpTypeBool, &[_]u32{ result }), | |
| 286 | switch (ty.zigTypeTag()) { | |
| 287 | .Void => try writeInstruction(code, .OpTypeVoid, &[_]u32{result_id}), | |
| 288 | .Bool => try writeInstruction(code, .OpTypeBool, &[_]u32{result_id}), | |
| 64 | 289 | .Int => { |
| 65 | const int_info = t.intInfo(self.target); | |
| 66 | try writeInstruction(&self.types_and_globals, .OpTypeInt, &[_]u32{ | |
| 67 | result, | |
| 68 | int_info.bits, | |
| 290 | const int_info = ty.intInfo(target); | |
| 291 | const backing_bits = self.backingIntBits(int_info.bits) orelse { | |
| 292 | // Integers too big for any native type are represented as "composite integers": An array of largestSupportedIntBits. | |
| 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 | 300 | switch (int_info.signedness) { |
| 70 | 301 | .unsigned => 0, |
| 71 | 302 | .signed => 1, |
| 72 | 303 | }, |
| 73 | 304 | }); |
| 74 | 305 | }, |
| 75 | // TODO: Verify that floatBits() will be correct. | |
| 76 | .Float => try writeInstruction(&self.types_and_globals, .OpTypeFloat, &[_]u32{ result, t.floatBits(self.target) }), | |
| 306 | .Float => { | |
| 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 | 362 | .Null, |
| 78 | 363 | .Undefined, |
| 79 | 364 | .EnumLiteral, |
| ... | ... | @@ -84,21 +369,197 @@ pub const SPIRVModule = struct { |
| 84 | 369 | |
| 85 | 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); | |
| 91 | return result; | |
| 375 | try self.types.putNoClobber(ty, result_id); | |
| 376 | return result_id; | |
| 92 | 377 | } |
| 93 | 378 | |
| 94 | pub fn gen(self: *SPIRVModule, decl: *Decl) !void { | |
| 95 | switch (decl.ty.zigTypeTag()) { | |
| 96 | .Fn => { | |
| 97 | log.debug("Generating code for function '{s}'", .{ std.mem.spanZ(decl.name) }); | |
| 379 | pub fn gen(self: *DeclGen) !void { | |
| 380 | const decl = self.decl; | |
| 381 | const result_id = decl.fn_link.spirv.id; | |
| 98 | 382 | |
| 99 | _ = try self.getOrGenType(decl.ty.fnReturnType()); | |
| 100 | }, | |
| 101 | else => return error.TODO, | |
| 383 | if (decl.val.castTag(.function)) |func_payload| { | |
| 384 | std.debug.assert(decl.ty.zigTypeTag() == .Fn); | |
| 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 | 37 | |
| 38 | 38 | // TODO: Should this struct be used at all rather than just a hashmap of aux data for every decl? |
| 39 | 39 | pub const FnData = struct { |
| 40 | // We're going to fill these in flushModule, and we're going to fill them unconditionally, | |
| 41 | // so just set it to undefined. | |
| 42 | id: u32 = undefined | |
| 43 | }; | |
| 40 | // We're going to fill these in flushModule, and we're going to fill them unconditionally, | |
| 41 | // so just set it to undefined. | |
| 42 | id: u32 = undefined }; | |
| 44 | 43 | |
| 45 | 44 | base: link.File, |
| 46 | 45 | |
| ... | ... | @@ -130,8 +129,8 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void { |
| 130 | 129 | const module = self.base.options.module.?; |
| 131 | 130 | const target = comp.getTarget(); |
| 132 | 131 | |
| 133 | var spirv_module = codegen.SPIRVModule.init(target, self.base.allocator); | |
| 134 | defer spirv_module.deinit(); | |
| 132 | var spv = codegen.SPIRVModule.init(self.base.allocator); | |
| 133 | defer spv.deinit(); | |
| 135 | 134 | |
| 136 | 135 | // Allocate an ID for every declaration before generating code, |
| 137 | 136 | // so that we can access them before processing them. |
| ... | ... | @@ -143,18 +142,47 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void { |
| 143 | 142 | const decl = entry.key; |
| 144 | 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 | 146 | log.debug("Allocating id {} to '{s}'", .{ decl.fn_link.spirv.id, std.mem.spanZ(decl.name) }); |
| 148 | 147 | } |
| 149 | 148 | } |
| 150 | 149 | |
| 151 | 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 | 170 | for (self.decl_table.items()) |entry| { |
| 154 | 171 | const decl = entry.key; |
| 155 | 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 | 193 | spec.magic_number, |
| 166 | 194 | (spec.version.major << 16) | (spec.version.minor << 8), |
| 167 | 195 | 0, // TODO: Register Zig compiler magic number. |
| 168 | spirv_module.resultIdBound(), // ID bound. | |
| 196 | spv.resultIdBound(), // ID bound. | |
| 169 | 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 | 204 | // follows the SPIR-V logical module format! |
| 177 | 205 | var all_buffers = [_]std.os.iovec_const{ |
| 178 | 206 | wordsToIovConst(binary.items), |
| 179 | wordsToIovConst(spirv_module.types_and_globals.items), | |
| 180 | wordsToIovConst(spirv_module.fn_decls.items), | |
| 207 | wordsToIovConst(spv.types_globals_constants.items), | |
| 208 | wordsToIovConst(spv.fn_decls.items), | |
| 181 | 209 | }; |
| 182 | 210 | |
| 183 | 211 | const file = self.base.file.?; |
src/translate_c.zig+2-2| ... | ... | @@ -2341,7 +2341,7 @@ fn transInitListExprArray( |
| 2341 | 2341 | assert(@ptrCast(*const clang.Type, arr_type).isConstantArrayType()); |
| 2342 | 2342 | const const_arr_ty = @ptrCast(*const clang.ConstantArrayType, arr_type); |
| 2343 | 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 | 2345 | const leftover_count = all_count - init_count; |
| 2346 | 2346 | |
| 2347 | 2347 | if (all_count == 0) { |
| ... | ... | @@ -4266,7 +4266,7 @@ fn transType(c: *Context, scope: *Scope, ty: *const clang.Type, source_loc: clan |
| 4266 | 4266 | const const_arr_ty = @ptrCast(*const clang.ConstantArrayType, ty); |
| 4267 | 4267 | |
| 4268 | 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 | 4270 | const elem_type = try transType(c, scope, const_arr_ty.getElementType().getTypePtr(), source_loc); |
| 4271 | 4271 | |
| 4272 | 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 | 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 | } |