authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-26 17:30:31-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-08-26 17:30:31-04:00
log091d693c5381a17d1de01ab372481f629b560c7c
tree2977e9fa1fcbbc58057684adf86ceb0291e16ef2
parent3abf9e1457ed9332474ab211eb8d996d594a33c3
parentad18078d53b448fc18a100dcebca88fe05a1e3ac
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #6164 from jedisct1/cryptobench

Improve crypto benchmarks

17 files changed, 150 insertions(+), 114 deletions(-)

lib/std/crypto/benchmark.zig+59-13
......@@ -7,6 +7,7 @@
77
88const builtin = @import("builtin");
99const std = @import("std");
10const mem = std.mem;
1011const time = std.time;
1112const Timer = time.Timer;
1213const crypto = std.crypto;
......@@ -46,6 +47,7 @@ pub fn benchmarkHash(comptime Hash: anytype, comptime bytes: comptime_int) !u64
4647 while (offset < bytes) : (offset += block.len) {
4748 h.update(block[0..]);
4849 }
50 mem.doNotOptimizeAway(&h);
4951 const end = timer.read();
5052
5153 const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
......@@ -67,19 +69,20 @@ const macs = [_]Crypto{
6769};
6870
6971pub fn benchmarkMac(comptime Mac: anytype, comptime bytes: comptime_int) !u64 {
70 std.debug.assert(64 >= Mac.mac_length and 32 >= Mac.minimum_key_length);
71
72 var in: [1 * MiB]u8 = undefined;
72 var in: [512 * KiB]u8 = undefined;
7373 prng.random.bytes(in[0..]);
7474
75 var key: [64]u8 = undefined;
75 const key_length = if (Mac.minimum_key_length == 0) 32 else Mac.minimum_key_length;
76 var key: [key_length]u8 = undefined;
7677 prng.random.bytes(key[0..]);
7778
79 var mac: [Mac.mac_length]u8 = undefined;
7880 var offset: usize = 0;
7981 var timer = try Timer.start();
8082 const start = timer.lap();
8183 while (offset < bytes) : (offset += in.len) {
82 Mac.create(key[0..], in[0..], key[0..]);
84 Mac.create(mac[0..], in[0..], key[0..]);
85 mem.doNotOptimizeAway(&mac);
8386 }
8487 const end = timer.read();
8588
......@@ -106,6 +109,7 @@ pub fn benchmarkKeyExchange(comptime DhKeyExchange: anytype, comptime exchange_c
106109 var i: usize = 0;
107110 while (i < exchange_count) : (i += 1) {
108111 _ = DhKeyExchange.create(out[0..], out[0..], in[0..]);
112 mem.doNotOptimizeAway(&out);
109113 }
110114 }
111115 const end = timer.read();
......@@ -118,7 +122,7 @@ pub fn benchmarkKeyExchange(comptime DhKeyExchange: anytype, comptime exchange_c
118122
119123const signatures = [_]Crypto{Crypto{ .ty = crypto.sign.Ed25519, .name = "ed25519" }};
120124
121pub fn benchmarkSignatures(comptime Signature: anytype, comptime signatures_count: comptime_int) !u64 {
125pub fn benchmarkSignature(comptime Signature: anytype, comptime signatures_count: comptime_int) !u64 {
122126 var seed: [Signature.seed_length]u8 = undefined;
123127 prng.random.bytes(seed[0..]);
124128 const msg = [_]u8{0} ** 64;
......@@ -129,7 +133,8 @@ pub fn benchmarkSignatures(comptime Signature: anytype, comptime signatures_coun
129133 {
130134 var i: usize = 0;
131135 while (i < signatures_count) : (i += 1) {
132 _ = try Signature.sign(&msg, key_pair, null);
136 const s = try Signature.sign(&msg, key_pair, null);
137 mem.doNotOptimizeAway(&s);
133138 }
134139 }
135140 const end = timer.read();
......@@ -140,6 +145,40 @@ pub fn benchmarkSignatures(comptime Signature: anytype, comptime signatures_coun
140145 return throughput;
141146}
142147
148const aeads = [_]Crypto{
149 Crypto{ .ty = crypto.aead.ChaCha20Poly1305, .name = "chacha20Poly1305" },
150 Crypto{ .ty = crypto.aead.XChaCha20Poly1305, .name = "xchacha20Poly1305" },
151 Crypto{ .ty = crypto.aead.Gimli, .name = "gimli-aead" },
152};
153
154pub fn benchmarkAead(comptime Aead: anytype, comptime bytes: comptime_int) !u64 {
155 var in: [512 * KiB]u8 = undefined;
156 prng.random.bytes(in[0..]);
157
158 var tag: [Aead.tag_length]u8 = undefined;
159
160 var key: [Aead.key_length]u8 = undefined;
161 prng.random.bytes(key[0..]);
162
163 var nonce: [Aead.nonce_length]u8 = undefined;
164 prng.random.bytes(nonce[0..]);
165
166 var offset: usize = 0;
167 var timer = try Timer.start();
168 const start = timer.lap();
169 while (offset < bytes) : (offset += in.len) {
170 Aead.encrypt(in[0..], tag[0..], in[0..], &[_]u8{}, nonce, key);
171 Aead.decrypt(in[0..], in[0..], tag, &[_]u8{}, nonce, key) catch unreachable;
172 }
173 mem.doNotOptimizeAway(&in);
174 const end = timer.read();
175
176 const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
177 const throughput = @floatToInt(u64, 2 * bytes / elapsed_s);
178
179 return throughput;
180}
181
143182fn usage() void {
144183 std.debug.warn(
145184 \\throughput_test [options]
......@@ -198,29 +237,36 @@ pub fn main() !void {
198237
199238 inline for (hashes) |H| {
200239 if (filter == null or std.mem.indexOf(u8, H.name, filter.?) != null) {
201 const throughput = try benchmarkHash(H.ty, mode(32 * MiB));
202 try stdout.print("{:>11}: {:5} MiB/s\n", .{ H.name, throughput / (1 * MiB) });
240 const throughput = try benchmarkHash(H.ty, mode(128 * MiB));
241 try stdout.print("{:>17}: {:7} MiB/s\n", .{ H.name, throughput / (1 * MiB) });
203242 }
204243 }
205244
206245 inline for (macs) |M| {
207246 if (filter == null or std.mem.indexOf(u8, M.name, filter.?) != null) {
208247 const throughput = try benchmarkMac(M.ty, mode(128 * MiB));
209 try stdout.print("{:>11}: {:5} MiB/s\n", .{ M.name, throughput / (1 * MiB) });
248 try stdout.print("{:>17}: {:7} MiB/s\n", .{ M.name, throughput / (1 * MiB) });
210249 }
211250 }
212251
213252 inline for (exchanges) |E| {
214253 if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
215254 const throughput = try benchmarkKeyExchange(E.ty, mode(1000));
216 try stdout.print("{:>11}: {:5} exchanges/s\n", .{ E.name, throughput });
255 try stdout.print("{:>17}: {:7} exchanges/s\n", .{ E.name, throughput });
217256 }
218257 }
219258
220259 inline for (signatures) |E| {
221260 if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
222 const throughput = try benchmarkSignatures(E.ty, mode(1000));
223 try stdout.print("{:>11}: {:5} signatures/s\n", .{ E.name, throughput });
261 const throughput = try benchmarkSignature(E.ty, mode(1000));
262 try stdout.print("{:>17}: {:7} signatures/s\n", .{ E.name, throughput });
263 }
264 }
265
266 inline for (aeads) |E| {
267 if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
268 const throughput = try benchmarkAead(E.ty, mode(128 * MiB));
269 try stdout.print("{:>17}: {:7} MiB/s\n", .{ E.name, throughput / (1 * MiB) });
224270 }
225271 }
226272}
lib/std/crypto/chacha20.zig+13-13
......@@ -47,7 +47,7 @@ fn initContext(key: [8]u32, d: [4]u32) [16]u32 {
4747}
4848
4949// The chacha family of ciphers are based on the salsa family.
50fn chacha20Core(x: []u32, input: [16]u32) void {
50inline fn chacha20Core(x: []u32, input: [16]u32) void {
5151 for (x) |_, i|
5252 x[i] = input[i];
5353
......@@ -744,26 +744,26 @@ pub const Chacha20Poly1305 = struct {
744744 pub const key_length = 32;
745745
746746 /// c: ciphertext: output buffer should be of size m.len
747 /// at: authentication tag: output MAC
747 /// tag: authentication tag: output MAC
748748 /// m: message
749749 /// ad: Associated Data
750750 /// npub: public nonce
751751 /// k: private key
752 pub fn encrypt(c: []u8, at: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void {
752 pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void {
753753 assert(c.len == m.len);
754 return chacha20poly1305SealDetached(c, at, m, ad, k, npub);
754 return chacha20poly1305SealDetached(c, tag, m, ad, k, npub);
755755 }
756756
757757 /// m: message: output buffer should be of size c.len
758758 /// c: ciphertext
759 /// at: authentication tag
759 /// tag: authentication tag
760760 /// ad: Associated Data
761761 /// npub: public nonce
762762 /// k: private key
763763 /// NOTE: the check of the authentication tag is currently not done in constant time
764 pub fn decrypt(m: []u8, c: []const u8, at: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) !void {
764 pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) !void {
765765 assert(c.len == m.len);
766 return try chacha20poly1305OpenDetached(m, c, at[0..], ad, k, npub);
766 return try chacha20poly1305OpenDetached(m, c, tag[0..], ad, k, npub);
767767 }
768768};
769769
......@@ -773,26 +773,26 @@ pub const XChacha20Poly1305 = struct {
773773 pub const key_length = 32;
774774
775775 /// c: ciphertext: output buffer should be of size m.len
776 /// at: authentication tag: output MAC
776 /// tag: authentication tag: output MAC
777777 /// m: message
778778 /// ad: Associated Data
779779 /// npub: public nonce
780780 /// k: private key
781 pub fn encrypt(c: []u8, at: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void {
781 pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void {
782782 assert(c.len == m.len);
783 return xchacha20poly1305SealDetached(c, at, m, ad, k, npub);
783 return xchacha20poly1305SealDetached(c, tag, m, ad, k, npub);
784784 }
785785
786786 /// m: message: output buffer should be of size c.len
787787 /// c: ciphertext
788 /// at: authentication tag
788 /// tag: authentication tag
789789 /// ad: Associated Data
790790 /// npub: public nonce
791791 /// k: private key
792792 /// NOTE: the check of the authentication tag is currently not done in constant time
793 pub fn decrypt(m: []u8, c: []const u8, at: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) !void {
793 pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) !void {
794794 assert(c.len == m.len);
795 return try xchacha20poly1305OpenDetached(m, c, at[0..], ad, k, npub);
795 return try xchacha20poly1305OpenDetached(m, c, tag[0..], ad, k, npub);
796796 }
797797};
798798
lib/std/crypto/gimli.zig+31-27
......@@ -180,10 +180,14 @@ test "hash" {
180180}
181181
182182pub const Aead = struct {
183 pub const tag_length = State.RATE;
184 pub const nonce_length = 16;
185 pub const key_length = 32;
186
183187 /// ad: Associated Data
184188 /// npub: public nonce
185189 /// k: private key
186 fn init(ad: []const u8, npub: [16]u8, k: [32]u8) State {
190 fn init(ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) State {
187191 var state = State{
188192 .data = undefined,
189193 };
......@@ -224,12 +228,12 @@ pub const Aead = struct {
224228 }
225229
226230 /// c: ciphertext: output buffer should be of size m.len
227 /// at: authentication tag: output MAC
231 /// tag: authentication tag: output MAC
228232 /// m: message
229233 /// ad: Associated Data
230234 /// npub: public nonce
231235 /// k: private key
232 pub fn encrypt(c: []u8, at: *[State.RATE]u8, m: []const u8, ad: []const u8, npub: [16]u8, k: [32]u8) void {
236 pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void {
233237 assert(c.len == m.len);
234238
235239 var state = Aead.init(ad, npub, k);
......@@ -265,17 +269,17 @@ pub const Aead = struct {
265269
266270 // After the final non-full block of plaintext, the first 16 bytes
267271 // of the state are output as an authentication tag.
268 std.mem.copy(u8, at, buf[0..State.RATE]);
272 std.mem.copy(u8, tag, buf[0..State.RATE]);
269273 }
270274
271275 /// m: message: output buffer should be of size c.len
272276 /// c: ciphertext
273 /// at: authentication tag
277 /// tag: authentication tag
274278 /// ad: Associated Data
275279 /// npub: public nonce
276280 /// k: private key
277281 /// NOTE: the check of the authentication tag is currently not done in constant time
278 pub fn decrypt(m: []u8, c: []const u8, at: [State.RATE]u8, ad: []const u8, npub: [16]u8, k: [32]u8) !void {
282 pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) !void {
279283 assert(c.len == m.len);
280284
281285 var state = Aead.init(ad, npub, k);
......@@ -308,7 +312,7 @@ pub const Aead = struct {
308312 // After the final non-full block of plaintext, the first 16 bytes
309313 // of the state are the authentication tag.
310314 // TODO: use a constant-time equality check here, see https://github.com/ziglang/zig/issues/1776
311 if (!mem.eql(u8, buf[0..State.RATE], &at)) {
315 if (!mem.eql(u8, buf[0..State.RATE], &tag)) {
312316 @memset(m.ptr, undefined, m.len);
313317 return error.InvalidMessage;
314318 }
......@@ -328,13 +332,13 @@ test "cipher" {
328332 const pt: [0]u8 = undefined;
329333
330334 var ct: [pt.len]u8 = undefined;
331 var at: [16]u8 = undefined;
332 Aead.encrypt(&ct, &at, &pt, &ad, nonce, key);
335 var tag: [16]u8 = undefined;
336 Aead.encrypt(&ct, &tag, &pt, &ad, nonce, key);
333337 htest.assertEqual("", &ct);
334 htest.assertEqual("14DA9BB7120BF58B985A8E00FDEBA15B", &at);
338 htest.assertEqual("14DA9BB7120BF58B985A8E00FDEBA15B", &tag);
335339
336340 var pt2: [pt.len]u8 = undefined;
337 try Aead.decrypt(&pt2, &ct, at, &ad, nonce, key);
341 try Aead.decrypt(&pt2, &ct, tag, &ad, nonce, key);
338342 testing.expectEqualSlices(u8, &pt, &pt2);
339343 }
340344 { // test vector (34) from NIST KAT submission.
......@@ -343,13 +347,13 @@ test "cipher" {
343347 try std.fmt.hexToBytes(&pt, "00");
344348
345349 var ct: [pt.len]u8 = undefined;
346 var at: [16]u8 = undefined;
347 Aead.encrypt(&ct, &at, &pt, &ad, nonce, key);
350 var tag: [16]u8 = undefined;
351 Aead.encrypt(&ct, &tag, &pt, &ad, nonce, key);
348352 htest.assertEqual("7F", &ct);
349 htest.assertEqual("80492C317B1CD58A1EDC3A0D3E9876FC", &at);
353 htest.assertEqual("80492C317B1CD58A1EDC3A0D3E9876FC", &tag);
350354
351355 var pt2: [pt.len]u8 = undefined;
352 try Aead.decrypt(&pt2, &ct, at, &ad, nonce, key);
356 try Aead.decrypt(&pt2, &ct, tag, &ad, nonce, key);
353357 testing.expectEqualSlices(u8, &pt, &pt2);
354358 }
355359 { // test vector (106) from NIST KAT submission.
......@@ -359,13 +363,13 @@ test "cipher" {
359363 try std.fmt.hexToBytes(&pt, "000102");
360364
361365 var ct: [pt.len]u8 = undefined;
362 var at: [16]u8 = undefined;
363 Aead.encrypt(&ct, &at, &pt, &ad, nonce, key);
366 var tag: [16]u8 = undefined;
367 Aead.encrypt(&ct, &tag, &pt, &ad, nonce, key);
364368 htest.assertEqual("484D35", &ct);
365 htest.assertEqual("030BBEA23B61C00CED60A923BDCF9147", &at);
369 htest.assertEqual("030BBEA23B61C00CED60A923BDCF9147", &tag);
366370
367371 var pt2: [pt.len]u8 = undefined;
368 try Aead.decrypt(&pt2, &ct, at, &ad, nonce, key);
372 try Aead.decrypt(&pt2, &ct, tag, &ad, nonce, key);
369373 testing.expectEqualSlices(u8, &pt, &pt2);
370374 }
371375 { // test vector (790) from NIST KAT submission.
......@@ -375,13 +379,13 @@ test "cipher" {
375379 try std.fmt.hexToBytes(&pt, "000102030405060708090A0B0C0D0E0F10111213141516");
376380
377381 var ct: [pt.len]u8 = undefined;
378 var at: [16]u8 = undefined;
379 Aead.encrypt(&ct, &at, &pt, &ad, nonce, key);
382 var tag: [16]u8 = undefined;
383 Aead.encrypt(&ct, &tag, &pt, &ad, nonce, key);
380384 htest.assertEqual("6815B4A0ECDAD01596EAD87D9E690697475D234C6A13D1", &ct);
381 htest.assertEqual("DFE23F1642508290D68245279558B2FB", &at);
385 htest.assertEqual("DFE23F1642508290D68245279558B2FB", &tag);
382386
383387 var pt2: [pt.len]u8 = undefined;
384 try Aead.decrypt(&pt2, &ct, at, &ad, nonce, key);
388 try Aead.decrypt(&pt2, &ct, tag, &ad, nonce, key);
385389 testing.expectEqualSlices(u8, &pt, &pt2);
386390 }
387391 { // test vector (1057) from NIST KAT submission.
......@@ -390,13 +394,13 @@ test "cipher" {
390394 try std.fmt.hexToBytes(&pt, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
391395
392396 var ct: [pt.len]u8 = undefined;
393 var at: [16]u8 = undefined;
394 Aead.encrypt(&ct, &at, &pt, &ad, nonce, key);
397 var tag: [16]u8 = undefined;
398 Aead.encrypt(&ct, &tag, &pt, &ad, nonce, key);
395399 htest.assertEqual("7F8A2CF4F52AA4D6B2E74105C30A2777B9D0C8AEFDD555DE35861BD3011F652F", &ct);
396 htest.assertEqual("7256456FA935AC34BBF55AE135F33257", &at);
400 htest.assertEqual("7256456FA935AC34BBF55AE135F33257", &tag);
397401
398402 var pt2: [pt.len]u8 = undefined;
399 try Aead.decrypt(&pt2, &ct, at, &ad, nonce, key);
403 try Aead.decrypt(&pt2, &ct, tag, &ad, nonce, key);
400404 testing.expectEqualSlices(u8, &pt, &pt2);
401405 }
402406}
lib/std/math.zig+3-28
......@@ -5,6 +5,7 @@
55// and substantial portions of the software.
66const std = @import("std.zig");
77const assert = std.debug.assert;
8const mem = std.mem;
89const testing = std.testing;
910
1011/// Euler's number (e)
......@@ -108,34 +109,8 @@ pub fn approxEq(comptime T: type, x: T, y: T, epsilon: T) bool {
108109 return fabs(x - y) < epsilon;
109110}
110111
111// TODO: Hide the following in an internal module.
112pub fn forceEval(value: anytype) void {
113 const T = @TypeOf(value);
114 switch (T) {
115 f16 => {
116 var x: f16 = undefined;
117 const p = @ptrCast(*volatile f16, &x);
118 p.* = x;
119 },
120 f32 => {
121 var x: f32 = undefined;
122 const p = @ptrCast(*volatile f32, &x);
123 p.* = x;
124 },
125 f64 => {
126 var x: f64 = undefined;
127 const p = @ptrCast(*volatile f64, &x);
128 p.* = x;
129 },
130 f128 => {
131 var x: f128 = undefined;
132 const p = @ptrCast(*volatile f128, &x);
133 p.* = x;
134 },
135 else => {
136 @compileError("forceEval not implemented for " ++ @typeName(T));
137 },
138 }
112pub fn doNotOptimizeAway(value: anytype) void {
113 mem.doNotOptimizeAway(value);
139114}
140115
141116pub fn raiseInvalid() void {
lib/std/math/asinh.zig+2-2
......@@ -56,7 +56,7 @@ fn asinh32(x: f32) f32 {
5656 }
5757 // |x| < 0x1p-12, inexact if x != 0
5858 else {
59 math.forceEval(x + 0x1.0p120);
59 math.doNotOptimizeAway(x + 0x1.0p120);
6060 }
6161
6262 return if (s != 0) -rx else rx;
......@@ -87,7 +87,7 @@ fn asinh64(x: f64) f64 {
8787 }
8888 // |x| < 0x1p-12, inexact if x != 0
8989 else {
90 math.forceEval(x + 0x1.0p120);
90 math.doNotOptimizeAway(x + 0x1.0p120);
9191 }
9292
9393 return if (s != 0) -rx else rx;
lib/std/math/atan.zig+2-2
......@@ -72,7 +72,7 @@ fn atan32(x_: f32) f32 {
7272 // |x| < 2^(-12)
7373 if (ix < 0x39800000) {
7474 if (ix < 0x00800000) {
75 math.forceEval(x * x);
75 math.doNotOptimizeAway(x * x);
7676 }
7777 return x;
7878 }
......@@ -170,7 +170,7 @@ fn atan64(x_: f64) f64 {
170170 // |x| < 2^(-27)
171171 if (ix < 0x3E400000) {
172172 if (ix < 0x00100000) {
173 math.forceEval(@floatCast(f32, x));
173 math.doNotOptimizeAway(@floatCast(f32, x));
174174 }
175175 return x;
176176 }
lib/std/math/atanh.zig+2-2
......@@ -45,7 +45,7 @@ fn atanh_32(x: f32) f32 {
4545 if (u < 0x3F800000 - (32 << 23)) {
4646 // underflow
4747 if (u < (1 << 23)) {
48 math.forceEval(y * y);
48 math.doNotOptimizeAway(y * y);
4949 }
5050 }
5151 // |x| < 0.5
......@@ -74,7 +74,7 @@ fn atanh_64(x: f64) f64 {
7474 if (e < 0x3FF - 32) {
7575 // underflow
7676 if (e == 0) {
77 math.forceEval(@floatCast(f32, y));
77 math.doNotOptimizeAway(@floatCast(f32, y));
7878 }
7979 }
8080 // |x| < 0.5
lib/std/math/ceil.zig+4-4
......@@ -47,14 +47,14 @@ fn ceil32(x: f32) f32 {
4747 if (u & m == 0) {
4848 return x;
4949 }
50 math.forceEval(x + 0x1.0p120);
50 math.doNotOptimizeAway(x + 0x1.0p120);
5151 if (u >> 31 == 0) {
5252 u += m;
5353 }
5454 u &= ~m;
5555 return @bitCast(f32, u);
5656 } else {
57 math.forceEval(x + 0x1.0p120);
57 math.doNotOptimizeAway(x + 0x1.0p120);
5858 if (u >> 31 != 0) {
5959 return -0.0;
6060 } else {
......@@ -79,7 +79,7 @@ fn ceil64(x: f64) f64 {
7979 }
8080
8181 if (e <= 0x3FF - 1) {
82 math.forceEval(y);
82 math.doNotOptimizeAway(y);
8383 if (u >> 63 != 0) {
8484 return -0.0;
8585 } else {
......@@ -106,7 +106,7 @@ fn ceil128(x: f128) f128 {
106106 }
107107
108108 if (e <= 0x3FFF - 1) {
109 math.forceEval(y);
109 math.doNotOptimizeAway(y);
110110 if (u >> 127 != 0) {
111111 return -0.0;
112112 } else {
lib/std/math/exp.zig+4-4
......@@ -56,7 +56,7 @@ fn exp32(x_: f32) f32 {
5656 return x * 0x1.0p127;
5757 }
5858 if (sign != 0) {
59 math.forceEval(-0x1.0p-149 / x); // overflow
59 math.doNotOptimizeAway(-0x1.0p-149 / x); // overflow
6060 // x <= -103.972084
6161 if (hx >= 0x42CFF1B5) {
6262 return 0;
......@@ -88,7 +88,7 @@ fn exp32(x_: f32) f32 {
8888 hi = x;
8989 lo = 0;
9090 } else {
91 math.forceEval(0x1.0p127 + x); // inexact
91 math.doNotOptimizeAway(0x1.0p127 + x); // inexact
9292 return 1 + x;
9393 }
9494
......@@ -139,7 +139,7 @@ fn exp64(x_: f64) f64 {
139139 }
140140 if (x < -708.39641853226410622) {
141141 // underflow if x != -inf
142 // math.forceEval(@as(f32, -0x1.0p-149 / x));
142 // math.doNotOptimizeAway(@as(f32, -0x1.0p-149 / x));
143143 if (x < -745.13321910194110842) {
144144 return 0;
145145 }
......@@ -172,7 +172,7 @@ fn exp64(x_: f64) f64 {
172172 lo = 0;
173173 } else {
174174 // inexact if x != 0
175 // math.forceEval(0x1.0p1023 + x);
175 // math.doNotOptimizeAway(0x1.0p1023 + x);
176176 return 1 + x;
177177 }
178178
lib/std/math/exp2.zig+2-2
......@@ -70,7 +70,7 @@ fn exp2_32(x: f32) f32 {
7070 // x < -126
7171 if (u >= 0x80000000) {
7272 if (u >= 0xC3160000 or u & 0x000FFFF != 0) {
73 math.forceEval(-0x1.0p-149 / x);
73 math.doNotOptimizeAway(-0x1.0p-149 / x);
7474 }
7575 // x <= -150
7676 if (u >= 0x3160000) {
......@@ -393,7 +393,7 @@ fn exp2_64(x: f64) f64 {
393393 if (ux >> 63 != 0) {
394394 // underflow
395395 if (x <= -1075 or x - 0x1.0p52 + 0x1.0p52 != x) {
396 math.forceEval(@floatCast(f32, -0x1.0p-149 / x));
396 math.doNotOptimizeAway(@floatCast(f32, -0x1.0p-149 / x));
397397 }
398398 if (x <= -1075) {
399399 return 0;
lib/std/math/expm1.zig+2-2
......@@ -106,7 +106,7 @@ fn expm1_32(x_: f32) f32 {
106106 // |x| < 2^(-25)
107107 else if (hx < 0x33000000) {
108108 if (hx < 0x00800000) {
109 math.forceEval(x * x);
109 math.doNotOptimizeAway(x * x);
110110 }
111111 return x;
112112 } else {
......@@ -237,7 +237,7 @@ fn expm1_64(x_: f64) f64 {
237237 // |x| < 2^(-54)
238238 else if (hx < 0x3C900000) {
239239 if (hx < 0x00100000) {
240 math.forceEval(@floatCast(f32, x));
240 math.doNotOptimizeAway(@floatCast(f32, x));
241241 }
242242 return x;
243243 } else {
lib/std/math/floor.zig+6-6
......@@ -50,13 +50,13 @@ fn floor16(x: f16) f16 {
5050 if (u & m == 0) {
5151 return x;
5252 }
53 math.forceEval(x + 0x1.0p120);
53 math.doNotOptimizeAway(x + 0x1.0p120);
5454 if (u >> 15 != 0) {
5555 u += m;
5656 }
5757 return @bitCast(f16, u & ~m);
5858 } else {
59 math.forceEval(x + 0x1.0p120);
59 math.doNotOptimizeAway(x + 0x1.0p120);
6060 if (u >> 15 == 0) {
6161 return 0.0;
6262 } else {
......@@ -84,13 +84,13 @@ fn floor32(x: f32) f32 {
8484 if (u & m == 0) {
8585 return x;
8686 }
87 math.forceEval(x + 0x1.0p120);
87 math.doNotOptimizeAway(x + 0x1.0p120);
8888 if (u >> 31 != 0) {
8989 u += m;
9090 }
9191 return @bitCast(f32, u & ~m);
9292 } else {
93 math.forceEval(x + 0x1.0p120);
93 math.doNotOptimizeAway(x + 0x1.0p120);
9494 if (u >> 31 == 0) {
9595 return 0.0;
9696 } else {
......@@ -115,7 +115,7 @@ fn floor64(x: f64) f64 {
115115 }
116116
117117 if (e <= 0x3FF - 1) {
118 math.forceEval(y);
118 math.doNotOptimizeAway(y);
119119 if (u >> 63 != 0) {
120120 return -1.0;
121121 } else {
......@@ -142,7 +142,7 @@ fn floor128(x: f128) f128 {
142142 }
143143
144144 if (e <= 0x3FFF - 1) {
145 math.forceEval(y);
145 math.doNotOptimizeAway(y);
146146 if (u >> 127 != 0) {
147147 return -1.0;
148148 } else {
lib/std/math/log1p.zig+1-1
......@@ -62,7 +62,7 @@ fn log1p_32(x: f32) f32 {
6262 if ((ix << 1) < (0x33800000 << 1)) {
6363 // underflow if subnormal
6464 if (ix & 0x7F800000 == 0) {
65 math.forceEval(x * x);
65 math.doNotOptimizeAway(x * x);
6666 }
6767 return x;
6868 }
lib/std/math/round.zig+3-3
......@@ -43,7 +43,7 @@ fn round32(x_: f32) f32 {
4343 x = -x;
4444 }
4545 if (e < 0x7F - 1) {
46 math.forceEval(x + math.f32_toint);
46 math.doNotOptimizeAway(x + math.f32_toint);
4747 return 0 * @bitCast(f32, u);
4848 }
4949
......@@ -76,7 +76,7 @@ fn round64(x_: f64) f64 {
7676 x = -x;
7777 }
7878 if (e < 0x3ff - 1) {
79 math.forceEval(x + math.f64_toint);
79 math.doNotOptimizeAway(x + math.f64_toint);
8080 return 0 * @bitCast(f64, u);
8181 }
8282
......@@ -109,7 +109,7 @@ fn round128(x_: f128) f128 {
109109 x = -x;
110110 }
111111 if (e < 0x3FFF - 1) {
112 math.forceEval(x + math.f64_toint);
112 math.doNotOptimizeAway(x + math.f64_toint);
113113 return 0 * @bitCast(f128, u);
114114 }
115115
lib/std/math/tanh.zig+2-2
......@@ -67,7 +67,7 @@ fn tanh32(x: f32) f32 {
6767 }
6868 // |x| is subnormal
6969 else {
70 math.forceEval(x * x);
70 math.doNotOptimizeAway(x * x);
7171 t = x;
7272 }
7373
......@@ -112,7 +112,7 @@ fn tanh64(x: f64) f64 {
112112 }
113113 // |x| is subnormal
114114 else {
115 math.forceEval(@floatCast(f32, x));
115 math.doNotOptimizeAway(@floatCast(f32, x));
116116 t = x;
117117 }
118118
lib/std/math/trunc.zig+3-3
......@@ -46,7 +46,7 @@ fn trunc32(x: f32) f32 {
4646 if (u & m == 0) {
4747 return x;
4848 } else {
49 math.forceEval(x + 0x1p120);
49 math.doNotOptimizeAway(x + 0x1p120);
5050 return @bitCast(f32, u & ~m);
5151 }
5252}
......@@ -67,7 +67,7 @@ fn trunc64(x: f64) f64 {
6767 if (u & m == 0) {
6868 return x;
6969 } else {
70 math.forceEval(x + 0x1p120);
70 math.doNotOptimizeAway(x + 0x1p120);
7171 return @bitCast(f64, u & ~m);
7272 }
7373}
......@@ -88,7 +88,7 @@ fn trunc128(x: f128) f128 {
8888 if (u & m == 0) {
8989 return x;
9090 } else {
91 math.forceEval(x + 0x1p120);
91 math.doNotOptimizeAway(x + 0x1p120);
9292 return @bitCast(f128, u & ~m);
9393 }
9494}
lib/std/mem.zig+11
......@@ -2158,6 +2158,17 @@ pub fn alignForwardGeneric(comptime T: type, addr: T, alignment: T) T {
21582158 return alignBackwardGeneric(T, addr + (alignment - 1), alignment);
21592159}
21602160
2161/// Force an evaluation of the expression; this tries to prevent
2162/// the compiler from optimizing the computation away even if the
2163/// result eventually gets discarded.
2164pub fn doNotOptimizeAway(val: anytype) void {
2165 asm volatile (""
2166 :
2167 : [val] "rm" (val)
2168 : "memory"
2169 );
2170}
2171
21612172test "alignForward" {
21622173 testing.expect(alignForward(1, 1) == 1);
21632174 testing.expect(alignForward(2, 1) == 2);