From ebf97627fd4e1ee46c1b446108e4b4e3bf4b5769 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Mon, 12 Apr 2021 17:27:55 +0200 Subject: [PATCH 01/57] build: Test the c.zig file too * Add some more tests for the sqrt/sqrtf implementations. The idea is to cross-check the software impl with the HW one whenever possible. * Fix a broken test, oops. --- build.zig | 1 + lib/std/special/c.zig | 56 +++++++++++++++++++++++++++---------------- 2 files changed, 36 insertions(+), 21 deletions(-) diff --git a/build.zig b/build.zig index 92e03603c5..f0b6426d1b 100644 --- a/build.zig +++ b/build.zig @@ -259,6 +259,7 @@ pub fn build(b: *Builder) !void { test_step.dependOn(tests.addPkgTests(b, test_filter, "lib/std/std.zig", "std", "Run the standard library tests", modes, false, skip_non_native, skip_libc, is_wine_enabled, is_qemu_enabled, is_wasmtime_enabled, glibc_multi_dir)); test_step.dependOn(tests.addPkgTests(b, test_filter, "lib/std/special/compiler_rt.zig", "compiler-rt", "Run the compiler_rt tests", modes, true, skip_non_native, true, is_wine_enabled, is_qemu_enabled, is_wasmtime_enabled, glibc_multi_dir)); + test_step.dependOn(tests.addPkgTests(b, test_filter, "lib/std/special/c.zig", "minilibc", "Run the mini libc tests", modes, true, skip_non_native, true, is_wine_enabled, is_qemu_enabled, is_wasmtime_enabled, glibc_multi_dir)); test_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes)); test_step.dependOn(tests.addStandaloneTests(b, test_filter, modes)); diff --git a/lib/std/special/c.zig b/lib/std/special/c.zig index f2f55e508e..caa2805956 100644 --- a/lib/std/special/c.zig +++ b/lib/std/special/c.zig @@ -85,7 +85,7 @@ test "strncpy" { var s1: [9:0]u8 = undefined; s1[0] = 0; - _ = strncpy(&s1, "foobarbaz", 9); + _ = strncpy(&s1, "foobarbaz", @sizeOf(@TypeOf(s1))); std.testing.expectEqualSlices(u8, "foobarbaz", std.mem.spanZ(&s1)); } @@ -993,17 +993,24 @@ export fn sqrt(x: f64) f64 { } test "sqrt" { - const epsilon = 0.000001; + const V = [_]f64{ + 0.0, + 4.089288054930154, + 7.538757127071935, + 8.97780793672623, + 5.304443821913729, + 5.682408965311888, + 0.5846878579110049, + 3.650338664297043, + 0.3178091951800732, + 7.1505232436382835, + 3.6589165881946464, + }; - std.testing.expect(sqrt(0.0) == 0.0); - std.testing.expect(std.math.approxEqAbs(f64, sqrt(2.0), 1.414214, epsilon)); - std.testing.expect(std.math.approxEqAbs(f64, sqrt(3.6), 1.897367, epsilon)); - std.testing.expect(sqrt(4.0) == 2.0); - std.testing.expect(std.math.approxEqAbs(f64, sqrt(7.539840), 2.745877, epsilon)); - std.testing.expect(std.math.approxEqAbs(f64, sqrt(19.230934), 4.385309, epsilon)); - std.testing.expect(sqrt(64.0) == 8.0); - std.testing.expect(std.math.approxEqAbs(f64, sqrt(64.1), 8.006248, epsilon)); - std.testing.expect(std.math.approxEqAbs(f64, sqrt(8942.230469), 94.563367, epsilon)); + // Note that @sqrt will either generate the sqrt opcode (if supported by the + // target ISA) or a call to `sqrtf` otherwise. + for (V) |val| + std.testing.expectEqual(@sqrt(val), sqrt(val)); } test "sqrt special" { @@ -1091,17 +1098,24 @@ export fn sqrtf(x: f32) f32 { } test "sqrtf" { - const epsilon = 0.000001; + const V = [_]f32{ + 0.0, + 4.089288054930154, + 7.538757127071935, + 8.97780793672623, + 5.304443821913729, + 5.682408965311888, + 0.5846878579110049, + 3.650338664297043, + 0.3178091951800732, + 7.1505232436382835, + 3.6589165881946464, + }; - std.testing.expect(sqrtf(0.0) == 0.0); - std.testing.expect(std.math.approxEqAbs(f32, sqrtf(2.0), 1.414214, epsilon)); - std.testing.expect(std.math.approxEqAbs(f32, sqrtf(3.6), 1.897367, epsilon)); - std.testing.expect(sqrtf(4.0) == 2.0); - std.testing.expect(std.math.approxEqAbs(f32, sqrtf(7.539840), 2.745877, epsilon)); - std.testing.expect(std.math.approxEqAbs(f32, sqrtf(19.230934), 4.385309, epsilon)); - std.testing.expect(sqrtf(64.0) == 8.0); - std.testing.expect(std.math.approxEqAbs(f32, sqrtf(64.1), 8.006248, epsilon)); - std.testing.expect(std.math.approxEqAbs(f32, sqrtf(8942.230469), 94.563370, epsilon)); + // Note that @sqrt will either generate the sqrt opcode (if supported by the + // target ISA) or a call to `sqrtf` otherwise. + for (V) |val| + std.testing.expectEqual(@sqrt(val), sqrtf(val)); } test "sqrtf special" { From 2ebd6bd7060dab3347a736ffd8a0ca5914624dc4 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Wed, 14 Apr 2021 17:52:24 +0200 Subject: [PATCH 02/57] std: Fix sqrt for u0/u1 input types --- lib/std/math/sqrt.zig | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/std/math/sqrt.zig b/lib/std/math/sqrt.zig index 394164ff54..4f32581270 100644 --- a/lib/std/math/sqrt.zig +++ b/lib/std/math/sqrt.zig @@ -39,7 +39,13 @@ pub fn sqrt(x: anytype) Sqrt(@TypeOf(x)) { } } -fn sqrt_int(comptime T: type, value: T) std.meta.Int(.unsigned, @typeInfo(T).Int.bits / 2) { +fn sqrt_int(comptime T: type, value: T) Sqrt(T) { + switch (T) { + u0 => return 0, + u1 => return value, + else => {}, + } + var op = value; var res: T = 0; var one: T = 1 << (@typeInfo(T).Int.bits - 2); @@ -58,11 +64,13 @@ fn sqrt_int(comptime T: type, value: T) std.meta.Int(.unsigned, @typeInfo(T).Int one >>= 2; } - const ResultType = std.meta.Int(.unsigned, @typeInfo(T).Int.bits / 2); + const ResultType = Sqrt(T); return @intCast(ResultType, res); } test "math.sqrt_int" { + expect(sqrt_int(u0, 0) == 0); + expect(sqrt_int(u1, 1) == 1); expect(sqrt_int(u32, 3) == 1); expect(sqrt_int(u32, 4) == 2); expect(sqrt_int(u32, 5) == 2); @@ -74,7 +82,13 @@ test "math.sqrt_int" { /// Returns the return type `sqrt` will return given an operand of type `T`. pub fn Sqrt(comptime T: type) type { return switch (@typeInfo(T)) { - .Int => |int| std.meta.Int(.unsigned, int.bits / 2), + .Int => |int| { + return switch (int.bits) { + 0 => u0, + 1 => u1, + else => std.meta.Int(.unsigned, int.bits / 2), + }; + }, else => T, }; } From f9481402f0c6f2eb4618bb0385aff18c12912c26 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Thu, 15 Apr 2021 18:12:11 +0200 Subject: [PATCH 03/57] stage1: Fix negation for zero floating point values Toggling the sign by computing 0-x doesn't really work for zero values. --- src/stage1/bigfloat.cpp | 9 +++------ src/stage1/ir.cpp | 11 +++-------- src/stage1/softfloat_ext.cpp | 13 +++++++++++++ src/stage1/softfloat_ext.hpp | 3 +++ 4 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/stage1/bigfloat.cpp b/src/stage1/bigfloat.cpp index 58b0aff54a..840cdccc8b 100644 --- a/src/stage1/bigfloat.cpp +++ b/src/stage1/bigfloat.cpp @@ -9,6 +9,7 @@ #include "bigint.hpp" #include "buffer.hpp" #include "softfloat.hpp" +#include "softfloat_ext.hpp" #include "parse_f128.h" #include #include @@ -60,9 +61,7 @@ void bigfloat_init_bigint(BigFloat *dest, const BigInt *op) { if (i == 0) { if (op->is_negative) { - float128_t zero_f128; - ui32_to_f128M(0, &zero_f128); - f128M_sub(&zero_f128, &dest->value, &dest->value); + f128M_neg(&dest->value, &dest->value); } return; } @@ -89,9 +88,7 @@ void bigfloat_add(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) { } void bigfloat_negate(BigFloat *dest, const BigFloat *op) { - float128_t zero_f128; - ui32_to_f128M(0, &zero_f128); - f128M_sub(&zero_f128, &op->value, &dest->value); + f128M_neg(&op->value, &dest->value); } void bigfloat_sub(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) { diff --git a/src/stage1/ir.cpp b/src/stage1/ir.cpp index 71a233c964..942b6028d9 100644 --- a/src/stage1/ir.cpp +++ b/src/stage1/ir.cpp @@ -11363,11 +11363,8 @@ static void float_negate(ZigValue *out_val, ZigValue *op) { } else if (op->type->id == ZigTypeIdFloat) { switch (op->type->data.floating.bit_count) { case 16: - { - const float16_t zero = zig_double_to_f16(0); - out_val->data.x_f16 = f16_sub(zero, op->data.x_f16); - return; - } + out_val->data.x_f16 = f16_neg(op->data.x_f16); + return; case 32: out_val->data.x_f32 = -op->data.x_f32; return; @@ -11375,9 +11372,7 @@ static void float_negate(ZigValue *out_val, ZigValue *op) { out_val->data.x_f64 = -op->data.x_f64; return; case 128: - float128_t zero_f128; - ui32_to_f128M(0, &zero_f128); - f128M_sub(&zero_f128, &op->data.x_f128, &out_val->data.x_f128); + f128M_neg(&op->data.x_f128, &out_val->data.x_f128); return; default: zig_unreachable(); diff --git a/src/stage1/softfloat_ext.cpp b/src/stage1/softfloat_ext.cpp index 8408a15116..0eebe7365e 100644 --- a/src/stage1/softfloat_ext.cpp +++ b/src/stage1/softfloat_ext.cpp @@ -1,6 +1,8 @@ #include "softfloat_ext.hpp" extern "C" { + #include "platform.h" + #include "internals.h" #include "softfloat.h" } @@ -22,4 +24,15 @@ void f128M_trunc(const float128_t *aPtr, float128_t *zPtr) { } else { f128M_roundToInt(aPtr, softfloat_round_min, false, zPtr); } +} + +float16_t f16_neg(const float16_t a) { + union ui16_f16 uZ; + uZ.ui = a.v ^ (UINT16_C(1) << 15); + return uZ.f; +} + +void f128M_neg(const float128_t *aPtr, float128_t *zPtr) { + zPtr->v[indexWord(2,1)] = aPtr->v[indexWord(2,1)] ^ (UINT64_C(1) << 63); + zPtr->v[indexWord(2,0)] = aPtr->v[indexWord(2,0)]; } \ No newline at end of file diff --git a/src/stage1/softfloat_ext.hpp b/src/stage1/softfloat_ext.hpp index 0a1f958933..42922a5226 100644 --- a/src/stage1/softfloat_ext.hpp +++ b/src/stage1/softfloat_ext.hpp @@ -5,5 +5,8 @@ void f128M_abs(const float128_t *aPtr, float128_t *zPtr); void f128M_trunc(const float128_t *aPtr, float128_t *zPtr); +void f128M_neg(const float128_t *aPtr, float128_t *zPtr); + +float16_t f16_neg(const float16_t a); #endif \ No newline at end of file From 8e0b2f0e52b195933ae7df9d66f8763fd53b0281 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Thu, 15 Apr 2021 18:14:25 +0200 Subject: [PATCH 04/57] compiler-rt: Fix typo in implementation of fp truncation ops The problem went unnoticed for years, yay. --- lib/std/special/compiler_rt/truncXfYf2.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/special/compiler_rt/truncXfYf2.zig b/lib/std/special/compiler_rt/truncXfYf2.zig index 470ac17c2c..cae5644962 100644 --- a/lib/std/special/compiler_rt/truncXfYf2.zig +++ b/lib/std/special/compiler_rt/truncXfYf2.zig @@ -122,7 +122,7 @@ fn truncXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t { if (shift > srcSigBits) { absResult = 0; } else { - const sticky: src_rep_t = significand << @intCast(SrcShift, srcBits - shift); + const sticky: src_rep_t = @boolToInt(significand << @intCast(SrcShift, srcBits - shift) != 0); const denormalizedSignificand: src_rep_t = significand >> @intCast(SrcShift, shift) | sticky; absResult = @intCast(dst_rep_t, denormalizedSignificand >> (srcSigBits - dstSigBits)); const roundBits: src_rep_t = denormalizedSignificand & roundMask; From 2d00f17d155b46e8effb47c363e051f50b866d47 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Thu, 15 Apr 2021 18:15:44 +0200 Subject: [PATCH 05/57] test: Add test to ensure signed zeros are properly computed Ensure everything's ok at comptime and runtime. --- test/stage1/behavior/math.zig | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/stage1/behavior/math.zig b/test/stage1/behavior/math.zig index 68690c9af8..3835aab0f4 100644 --- a/test/stage1/behavior/math.zig +++ b/test/stage1/behavior/math.zig @@ -843,3 +843,20 @@ test "compare undefined literal with comptime_int" { x = true; expect(x); } + +test "signed zeros are represented properly" { + const S = struct { + fn doTheTest() void { + inline for ([_]type{ f16, f32, f64, f128 }) |T| { + const ST = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); + var as_fp_val = -@as(T, 0.0); + var as_uint_val = @bitCast(ST, as_fp_val); + // Ensure the sign bit is set. + expect(as_uint_val >> (@typeInfo(T).Float.bits - 1) == 1); + } + } + }; + + S.doTheTest(); + comptime S.doTheTest(); +} From 7a4dad7e87bd5561bbe62f5d38fda1b968f9d529 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Thu, 15 Apr 2021 18:17:31 +0200 Subject: [PATCH 06/57] stage1: More precise serialization of f16 values Taking a detour trough a f64 is dangerous as the softfloat library doesn't like converting sNaN values. The error went unnoticed as an exception is raised by the library but the stage1 compiler doesn't give a damn. --- src/stage1/codegen.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index 6d219c517d..3bb1a7e022 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -7436,7 +7436,10 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n case ZigTypeIdFloat: switch (type_entry->data.floating.bit_count) { case 16: - return LLVMConstReal(get_llvm_type(g, type_entry), zig_f16_to_double(const_val->data.x_f16)); + { + LLVMValueRef as_int = LLVMConstInt(LLVMInt16Type(), const_val->data.x_f16.v, false); + return LLVMConstBitCast(as_int, get_llvm_type(g, type_entry)); + } case 32: return LLVMConstReal(get_llvm_type(g, type_entry), const_val->data.x_f32); case 64: From b29677dd12b5a19618295757a6044c0b0d741dbb Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Thu, 15 Apr 2021 18:20:50 +0200 Subject: [PATCH 07/57] compiler-rt: Implement __extendhftf2 --- lib/std/special/compiler_rt/extendXfYf2.zig | 4 ++ .../special/compiler_rt/extendXfYf2_test.zig | 49 ++++++++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/lib/std/special/compiler_rt/extendXfYf2.zig b/lib/std/special/compiler_rt/extendXfYf2.zig index c5b93fa51e..59be8441fa 100644 --- a/lib/std/special/compiler_rt/extendXfYf2.zig +++ b/lib/std/special/compiler_rt/extendXfYf2.zig @@ -23,6 +23,10 @@ pub fn __extendhfsf2(a: u16) callconv(.C) f32 { return @call(.{ .modifier = .always_inline }, extendXfYf2, .{ f32, f16, a }); } +pub fn __extendhftf2(a: u16) callconv(.C) f128 { + return @call(.{ .modifier = .always_inline }, extendXfYf2, .{ f128, f16, a }); +} + pub fn __aeabi_h2f(arg: u16) callconv(.AAPCS) f32 { @setRuntimeSafety(false); return @call(.{ .modifier = .always_inline }, __extendhfsf2, .{arg}); diff --git a/lib/std/special/compiler_rt/extendXfYf2_test.zig b/lib/std/special/compiler_rt/extendXfYf2_test.zig index 6a3f69d8e9..d97a9c2502 100644 --- a/lib/std/special/compiler_rt/extendXfYf2_test.zig +++ b/lib/std/special/compiler_rt/extendXfYf2_test.zig @@ -4,9 +4,10 @@ // The MIT license requires this copyright notice to be included in all copies // and substantial portions of the software. const builtin = @import("builtin"); -const __extenddftf2 = @import("extendXfYf2.zig").__extenddftf2; const __extendhfsf2 = @import("extendXfYf2.zig").__extendhfsf2; +const __extendhftf2 = @import("extendXfYf2.zig").__extendhftf2; const __extendsftf2 = @import("extendXfYf2.zig").__extendsftf2; +const __extenddftf2 = @import("extendXfYf2.zig").__extenddftf2; fn test__extenddftf2(a: f64, expectedHi: u64, expectedLo: u64) void { const x = __extenddftf2(a); @@ -161,3 +162,49 @@ fn makeNaN32(rand: u32) f32 { fn makeInf32() f32 { return @bitCast(f32, @as(u32, 0x7f800000)); } + +fn test__extendhftf2(a: f16, expectedHi: u64, expectedLo: u64) void { + const x = __extendhftf2(@bitCast(u16, a)); + + const rep = @bitCast(u128, x); + const hi = @intCast(u64, rep >> 64); + const lo = @truncate(u64, rep); + + if (hi == expectedHi and lo == expectedLo) + return; + + // test other possible NaN representation(signal NaN) + if (expectedHi == 0x7fff800000000000 and expectedLo == 0x0) { + if ((hi & 0x7fff000000000000) == 0x7fff000000000000 and + ((hi & 0xffffffffffff) > 0 or lo > 0)) + { + return; + } + } + + @panic("__extendhftf2 test failure"); +} + +test "extendhftf2" { + // qNaN + test__extendhftf2(@bitCast(f16, @as(u16, 0x7e00)), 0x7fff800000000000, 0x0); + // NaN + test__extendhftf2(@bitCast(f16, @as(u16, 0x7d00)), 0x7fff400000000000, 0x0); + // inf + test__extendhftf2(@bitCast(f16, @as(u16, 0x7c00)), 0x7fff000000000000, 0x0); + test__extendhftf2(-@bitCast(f16, @as(u16, 0x7c00)), 0xffff000000000000, 0x0); + // zero + test__extendhftf2(@bitCast(f16, @as(u16, 0x0)), 0x0, 0x0); + test__extendhftf2(@bitCast(f16, @as(u16, 0x8000)), 0x8000000000000000, 0x0); + // denormal + test__extendhftf2(@bitCast(f16, @as(u16, 0x0010)), 0x3feb000000000000, 0x0000000000000000); + test__extendhftf2(@bitCast(f16, @as(u16, 0x0001)), 0x3fe7000000000000, 0x0000000000000000); + test__extendhftf2(@bitCast(f16, @as(u16, 0x8001)), 0xbfe7000000000000, 0x0000000000000000); + + // pi + test__extendhftf2(@bitCast(f16, @as(u16, 0x4248)), 0x4000920000000000, 0x0000000000000000); + test__extendhftf2(@bitCast(f16, @as(u16, 0xc248)), 0xc000920000000000, 0x0000000000000000); + + test__extendhftf2(@bitCast(f16, @as(u16, 0x508c)), 0x4004230000000000, 0x0); + test__extendhftf2(@bitCast(f16, @as(u16, 0x1bb7)), 0x3ff6edc000000000, 0x0); +} From 5bc1dc59e693287e1ffcd14023048b2abc7e1764 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Thu, 15 Apr 2021 18:21:15 +0200 Subject: [PATCH 08/57] compiler-rt: Implement __trunctfhf2 --- lib/std/special/compiler_rt/truncXfYf2.zig | 4 ++ .../special/compiler_rt/truncXfYf2_test.zig | 56 +++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/lib/std/special/compiler_rt/truncXfYf2.zig b/lib/std/special/compiler_rt/truncXfYf2.zig index cae5644962..e85aa363db 100644 --- a/lib/std/special/compiler_rt/truncXfYf2.zig +++ b/lib/std/special/compiler_rt/truncXfYf2.zig @@ -13,6 +13,10 @@ pub fn __truncdfhf2(a: f64) callconv(.C) u16 { return @bitCast(u16, @call(.{ .modifier = .always_inline }, truncXfYf2, .{ f16, f64, a })); } +pub fn __trunctfhf2(a: f128) callconv(.C) u16 { + return @bitCast(u16, @call(.{ .modifier = .always_inline }, truncXfYf2, .{ f16, f128, a })); +} + pub fn __trunctfsf2(a: f128) callconv(.C) f32 { return @call(.{ .modifier = .always_inline }, truncXfYf2, .{ f32, f128, a }); } diff --git a/lib/std/special/compiler_rt/truncXfYf2_test.zig b/lib/std/special/compiler_rt/truncXfYf2_test.zig index 6426614b07..4fae5d1fc0 100644 --- a/lib/std/special/compiler_rt/truncXfYf2_test.zig +++ b/lib/std/special/compiler_rt/truncXfYf2_test.zig @@ -242,3 +242,59 @@ test "truncdfsf2" { // huge number becomes inf test__truncdfsf2(340282366920938463463374607431768211456.0, 0x7f800000); } + +const __trunctfhf2 = @import("truncXfYf2.zig").__trunctfhf2; + +fn test__trunctfhf2(a: f128, expected: u16) void { + const x = __trunctfhf2(a); + + const rep = @bitCast(u16, x); + if (rep == expected) { + return; + } + + @import("std").debug.warn("got 0x{x} wanted 0x{x}\n", .{ rep, expected }); + + @panic("__trunctfhf2 test failure"); +} + +test "trunctfhf2" { + // qNaN + test__trunctfhf2(@bitCast(f128, @as(u128, 0x7fff8000000000000000000000000000)), 0x7e00); + // NaN + test__trunctfhf2(@bitCast(f128, @as(u128, 0x7fff0000000000000000000000000001)), 0x7e00); + // inf + test__trunctfhf2(@bitCast(f128, @as(u128, 0x7fff0000000000000000000000000000)), 0x7c00); + test__trunctfhf2(-@bitCast(f128, @as(u128, 0x7fff0000000000000000000000000000)), 0xfc00); + // zero + test__trunctfhf2(0.0, 0x0); + test__trunctfhf2(-0.0, 0x8000); + + test__trunctfhf2(3.1415926535, 0x4248); + test__trunctfhf2(-3.1415926535, 0xc248); + test__trunctfhf2(0x1.987124876876324p+100, 0x7c00); + test__trunctfhf2(0x1.987124876876324p+12, 0x6e62); + test__trunctfhf2(0x1.0p+0, 0x3c00); + test__trunctfhf2(0x1.0p-14, 0x0400); + // denormal + test__trunctfhf2(0x1.0p-20, 0x0010); + test__trunctfhf2(0x1.0p-24, 0x0001); + test__trunctfhf2(-0x1.0p-24, 0x8001); + test__trunctfhf2(0x1.5p-25, 0x0001); + // and back to zero + test__trunctfhf2(0x1.0p-25, 0x0000); + test__trunctfhf2(-0x1.0p-25, 0x8000); + // max (precise) + test__trunctfhf2(65504.0, 0x7bff); + // max (rounded) + test__trunctfhf2(65519.0, 0x7bff); + // max (to +inf) + test__trunctfhf2(65520.0, 0x7c00); + test__trunctfhf2(65536.0, 0x7c00); + test__trunctfhf2(-65520.0, 0xfc00); + + test__trunctfhf2(0x1.23a2abb4a2ddee355f36789abcdep+5, 0x508f); + test__trunctfhf2(0x1.e3d3c45bd3abfd98b76a54cc321fp-9, 0x1b8f); + test__trunctfhf2(0x1.234eebb5faa678f4488693abcdefp+453, 0x7c00); + test__trunctfhf2(0x1.edcba9bb8c76a5a43dd21f334634p-43, 0x0); +} From bd4421befee2157018dad88b31b684f9a5af73c2 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Thu, 15 Apr 2021 21:52:08 +0200 Subject: [PATCH 09/57] compiler-rt: Don't pass f16 around as arguments Fixes some failures on AArch64. f16 was a mistake. --- .../special/compiler_rt/extendXfYf2_test.zig | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/lib/std/special/compiler_rt/extendXfYf2_test.zig b/lib/std/special/compiler_rt/extendXfYf2_test.zig index d97a9c2502..f05d33eac3 100644 --- a/lib/std/special/compiler_rt/extendXfYf2_test.zig +++ b/lib/std/special/compiler_rt/extendXfYf2_test.zig @@ -163,8 +163,8 @@ fn makeInf32() f32 { return @bitCast(f32, @as(u32, 0x7f800000)); } -fn test__extendhftf2(a: f16, expectedHi: u64, expectedLo: u64) void { - const x = __extendhftf2(@bitCast(u16, a)); +fn test__extendhftf2(a: u16, expectedHi: u64, expectedLo: u64) void { + const x = __extendhftf2(a); const rep = @bitCast(u128, x); const hi = @intCast(u64, rep >> 64); @@ -187,24 +187,24 @@ fn test__extendhftf2(a: f16, expectedHi: u64, expectedLo: u64) void { test "extendhftf2" { // qNaN - test__extendhftf2(@bitCast(f16, @as(u16, 0x7e00)), 0x7fff800000000000, 0x0); + test__extendhftf2(0x7e00, 0x7fff800000000000, 0x0); // NaN - test__extendhftf2(@bitCast(f16, @as(u16, 0x7d00)), 0x7fff400000000000, 0x0); + test__extendhftf2(0x7d00, 0x7fff400000000000, 0x0); // inf - test__extendhftf2(@bitCast(f16, @as(u16, 0x7c00)), 0x7fff000000000000, 0x0); - test__extendhftf2(-@bitCast(f16, @as(u16, 0x7c00)), 0xffff000000000000, 0x0); + test__extendhftf2(0x7c00, 0x7fff000000000000, 0x0); + test__extendhftf2(0xfc00, 0xffff000000000000, 0x0); // zero - test__extendhftf2(@bitCast(f16, @as(u16, 0x0)), 0x0, 0x0); - test__extendhftf2(@bitCast(f16, @as(u16, 0x8000)), 0x8000000000000000, 0x0); + test__extendhftf2(0x0000, 0x0000000000000000, 0x0); + test__extendhftf2(0x8000, 0x8000000000000000, 0x0); // denormal - test__extendhftf2(@bitCast(f16, @as(u16, 0x0010)), 0x3feb000000000000, 0x0000000000000000); - test__extendhftf2(@bitCast(f16, @as(u16, 0x0001)), 0x3fe7000000000000, 0x0000000000000000); - test__extendhftf2(@bitCast(f16, @as(u16, 0x8001)), 0xbfe7000000000000, 0x0000000000000000); + test__extendhftf2(0x0010, 0x3feb000000000000, 0x0); + test__extendhftf2(0x0001, 0x3fe7000000000000, 0x0); + test__extendhftf2(0x8001, 0xbfe7000000000000, 0x0); // pi - test__extendhftf2(@bitCast(f16, @as(u16, 0x4248)), 0x4000920000000000, 0x0000000000000000); - test__extendhftf2(@bitCast(f16, @as(u16, 0xc248)), 0xc000920000000000, 0x0000000000000000); + test__extendhftf2(0x4248, 0x4000920000000000, 0x0); + test__extendhftf2(0xc248, 0xc000920000000000, 0x0); - test__extendhftf2(@bitCast(f16, @as(u16, 0x508c)), 0x4004230000000000, 0x0); - test__extendhftf2(@bitCast(f16, @as(u16, 0x1bb7)), 0x3ff6edc000000000, 0x0); + test__extendhftf2(0x508c, 0x4004230000000000, 0x0); + test__extendhftf2(0x1bb7, 0x3ff6edc000000000, 0x0); } From d4d21dd46d69961300cc796abdebe352dfe6eae8 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Tue, 13 Apr 2021 20:57:50 -0700 Subject: [PATCH 10/57] translate-c: better handling of int -> enum casts In std.meta.cast when casting to an enum type from an integer type, first do a C-style cast from the source value to the tag type of the enum. This ensures that we don't get an error due to the source value not being representable by the enum. In transCCast() use std.meta.cast instead of directly emitting the cast operation since the enum's underlying type may not be known at translation time due to an MSVC bug, see https://github.com/ziglang/zig/issues/8003 Fixes #6011 --- lib/std/meta.zig | 18 +++++++++++++++--- src/translate_c.zig | 4 ++-- test/run_translated_c.zig | 14 ++++++++++++++ test/translate_c.zig | 8 ++++---- 4 files changed, 35 insertions(+), 9 deletions(-) diff --git a/lib/std/meta.zig b/lib/std/meta.zig index 860b6874c0..c6f800ca2c 100644 --- a/lib/std/meta.zig +++ b/lib/std/meta.zig @@ -888,7 +888,7 @@ pub fn Vector(comptime len: u32, comptime child: type) type { /// Given a type and value, cast the value to the type as c would. /// This is for translate-c and is not intended for general use. pub fn cast(comptime DestType: type, target: anytype) DestType { - // this function should behave like transCCast in translate-c, except it's for macros + // this function should behave like transCCast in translate-c, except it's for macros and enums const SourceType = @TypeOf(target); switch (@typeInfo(DestType)) { .Pointer => { @@ -925,9 +925,10 @@ pub fn cast(comptime DestType: type, target: anytype) DestType { } } }, - .Enum => { + .Enum => |enum_type| { if (@typeInfo(SourceType) == .Int or @typeInfo(SourceType) == .ComptimeInt) { - return @intToEnum(DestType, target); + const intermediate = cast(enum_type.tag_type, target); + return @intToEnum(DestType, intermediate); } }, .Int => { @@ -1015,6 +1016,17 @@ test "std.meta.cast" { testing.expectEqual(@intToPtr(*u8, 2), cast(*u8, @intToPtr(*volatile u8, 2))); testing.expectEqual(@intToPtr(?*c_void, 2), cast(?*c_void, @intToPtr(*u8, 2))); + + const C_ENUM = extern enum(c_int) { + A = 0, + B, + C, + _, + }; + testing.expectEqual(cast(C_ENUM, @as(i64, -1)), @intToEnum(C_ENUM, -1)); + testing.expectEqual(cast(C_ENUM, @as(i8, 1)), .B); + testing.expectEqual(cast(C_ENUM, @as(u64, 1)), .B); + testing.expectEqual(cast(C_ENUM, @as(u64, 42)), @intToEnum(C_ENUM, 42)); } /// Given a value returns its size as C's sizeof operator would. diff --git a/src/translate_c.zig b/src/translate_c.zig index 9a1215abd6..8a2a1b6223 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -2161,8 +2161,8 @@ fn transCCast( return Tag.as.create(c.arena, .{ .lhs = dst_node, .rhs = bool_to_int }); } if (cIsEnum(dst_type)) { - // @intToEnum(dest_type, val) - return Tag.int_to_enum.create(c.arena, .{ .lhs = dst_node, .rhs = expr }); + // import("std").meta.cast(dest_type, val) + return Tag.std_meta_cast.create(c.arena, .{ .lhs = dst_node, .rhs = expr }); } if (cIsEnum(src_type) and !cIsEnum(dst_type)) { // @enumToInt(val) diff --git a/test/run_translated_c.zig b/test/run_translated_c.zig index 7e5d0da367..35686c11f9 100644 --- a/test/run_translated_c.zig +++ b/test/run_translated_c.zig @@ -1453,4 +1453,18 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { \\ return 0; \\} , ""); + + cases.add("Cast to enum from larger integral type. Issue #6011", + \\#include + \\#include + \\enum Foo { A, B, C }; + \\static inline enum Foo do_stuff(void) { + \\ int64_t i = 1; + \\ return (enum Foo)i; + \\} + \\int main(void) { + \\ if (do_stuff() != B) abort(); + \\ return 0; + \\} + , ""); } diff --git a/test/translate_c.zig b/test/translate_c.zig index 142579c92c..415d303f31 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -111,7 +111,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ const A = @enumToInt(enum_Foo.A); \\ const B = @enumToInt(enum_Foo.B); \\ const C = @enumToInt(enum_Foo.C); - \\ var a: enum_Foo = @intToEnum(enum_Foo, B); + \\ var a: enum_Foo = @import("std").meta.cast(enum_Foo, B); \\ { \\ const enum_Foo = extern enum(c_int) { \\ A, @@ -122,7 +122,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ const A_2 = @enumToInt(enum_Foo.A); \\ const B_3 = @enumToInt(enum_Foo.B); \\ const C_4 = @enumToInt(enum_Foo.C); - \\ var a_5: enum_Foo = @intToEnum(enum_Foo, B_3); + \\ var a_5: enum_Foo = @import("std").meta.cast(enum_Foo, B_3); \\ } \\} }); @@ -1676,7 +1676,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub const e = @enumToInt(enum_unnamed_1.e); \\pub const f = @enumToInt(enum_unnamed_1.f); \\pub const g = @enumToInt(enum_unnamed_1.g); - \\pub export var h: enum_unnamed_1 = @intToEnum(enum_unnamed_1, e); + \\pub export var h: enum_unnamed_1 = @import("std").meta.cast(enum_unnamed_1, e); \\const enum_unnamed_2 = extern enum(c_int) { \\ i, \\ j, @@ -2308,7 +2308,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ var a = arg_a; \\ var b = arg_b; \\ var c = arg_c; - \\ var d: enum_Foo = @intToEnum(enum_Foo, FooA); + \\ var d: enum_Foo = @import("std").meta.cast(enum_Foo, FooA); \\ var e: c_int = @boolToInt((a != 0) and (b != 0)); \\ var f: c_int = @boolToInt((b != 0) and (c != null)); \\ var g: c_int = @boolToInt((a != 0) and (c != null)); From d1a41feddc4b7a9d09e2982b9604b4c89285bcf8 Mon Sep 17 00:00:00 2001 From: Mahdi Khanalizadeh Date: Fri, 16 Apr 2021 12:10:26 +0200 Subject: [PATCH 11/57] linux: fix number of arguments for tgkill syscall --- lib/std/os/linux.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index ed08438425..dbc1e2aee5 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -632,7 +632,7 @@ pub fn tkill(tid: pid_t, sig: i32) usize { } pub fn tgkill(tgid: pid_t, tid: pid_t, sig: i32) usize { - return syscall2(.tgkill, @bitCast(usize, @as(isize, tgid)), @bitCast(usize, @as(isize, tid)), @bitCast(usize, @as(isize, sig))); + return syscall3(.tgkill, @bitCast(usize, @as(isize, tgid)), @bitCast(usize, @as(isize, tid)), @bitCast(usize, @as(isize, sig))); } pub fn link(oldpath: [*:0]const u8, newpath: [*:0]const u8, flags: i32) usize { From 235aa8c078cd63227cf547db93c2aa4e89dcdf89 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 16 Apr 2021 09:57:31 -0700 Subject: [PATCH 12/57] ci: drone: don't skip stage2 tests the stalls seem to be happening for a different reason --- ci/drone/linux_script | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ci/drone/linux_script b/ci/drone/linux_script index fe7cc6a5d0..0994604f80 100755 --- a/ci/drone/linux_script +++ b/ci/drone/linux_script @@ -23,13 +23,11 @@ cmake .. -DCMAKE_BUILD_TYPE=Release "-DCMAKE_INSTALL_PREFIX=$DISTDIR" -DZIG_STAT samu install # run-translated-c tests are skipped due to: https://github.com/ziglang/zig/issues/8537 -# stage2 tests are skipped due to: https://github.com/ziglang/zig/issues/8545 ./zig build test \ -Dskip-release \ -Dskip-non-native \ -Dskip-compile-errors \ - -Dskip-run-translated-c \ - -Dskip-stage2-tests + -Dskip-run-translated-c if [ -z "$DRONE_PULL_REQUEST" ]; then mv ../LICENSE "$DISTDIR/" From 2cd49d20e859af421d371d1c83349273819648d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Floru=C3=9F?= Date: Thu, 15 Apr 2021 14:10:07 +0200 Subject: [PATCH 13/57] Fix std.os.windows.user32.messageBoxW Arguments to `selectSymbol` were passed in the wrong order. --- lib/std/os/windows/user32.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/os/windows/user32.zig b/lib/std/os/windows/user32.zig index 9a058f35c0..95ae5f0149 100644 --- a/lib/std/os/windows/user32.zig +++ b/lib/std/os/windows/user32.zig @@ -663,7 +663,7 @@ pub fn messageBoxA(hWnd: ?HWND, lpText: [*:0]const u8, lpCaption: [*:0]const u8, pub extern "user32" fn MessageBoxW(hWnd: ?HWND, lpText: [*:0]const u16, lpCaption: ?[*:0]const u16, uType: UINT) callconv(WINAPI) i32; pub var pfnMessageBoxW: @TypeOf(MessageBoxW) = undefined; pub fn messageBoxW(hWnd: ?HWND, lpText: [*:0]const u16, lpCaption: [*:0]const u16, uType: u32) !i32 { - const function = selectSymbol(pfnMessageBoxW, MessageBoxW, .win2k); + const function = selectSymbol(MessageBoxW, pfnMessageBoxW, .win2k); const value = function(hWnd, lpText, lpCaption, uType); if (value != 0) return value; switch (GetLastError()) { From 5edabb3990c9a1dce61e1c3957a218d635e27210 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Sun, 11 Apr 2021 14:48:49 +0200 Subject: [PATCH 14/57] stage2 register manager: Add functions for allocating multiple registers --- src/register_manager.zig | 111 ++++++++++++++++++++++++++------------- 1 file changed, 74 insertions(+), 37 deletions(-) diff --git a/src/register_manager.zig b/src/register_manager.zig index e11f2c3111..01f83aa2f5 100644 --- a/src/register_manager.zig +++ b/src/register_manager.zig @@ -36,7 +36,7 @@ pub fn RegisterManager( } fn isTracked(reg: Register) bool { - return std.mem.indexOfScalar(Register, callee_preserved_regs, reg) != null; + return reg.allocIndex() != null; } fn markRegUsed(self: *Self, reg: Register) void { @@ -55,6 +55,7 @@ pub fn RegisterManager( self.free_registers |= @as(FreeRegInt, 1) << shift; } + /// Returns true when this register is not tracked pub fn isRegFree(self: Self, reg: Register) bool { if (FreeRegInt == u0) return true; const index = reg.allocIndex() orelse return true; @@ -63,7 +64,8 @@ pub fn RegisterManager( } /// Returns whether this register was allocated in the course - /// of this function + /// of this function. + /// Returns false when this register is not tracked pub fn isRegAllocated(self: Self, reg: Register) bool { if (FreeRegInt == u0) return false; const index = reg.allocIndex() orelse return false; @@ -71,57 +73,89 @@ pub fn RegisterManager( return self.allocated_registers & @as(FreeRegInt, 1) << shift != 0; } + /// Before calling, must ensureCapacity + count on self.registers. + /// Returns `null` if all registers are allocated. + pub fn tryAllocRegs(self: *Self, comptime count: comptime_int, insts: [count]*ir.Inst) ?[count]Register { + if (self.tryAllocRegsWithoutTracking(count)) |regs| { + for (regs) |reg, i| { + self.markRegUsed(reg); + self.registers.putAssumeCapacityNoClobber(reg, insts[i]); + } + + return regs; + } else { + return null; + } + } + /// Before calling, must ensureCapacity + 1 on self.registers. /// Returns `null` if all registers are allocated. pub fn tryAllocReg(self: *Self, inst: *ir.Inst) ?Register { - const free_index = @ctz(FreeRegInt, self.free_registers); - if (free_index >= callee_preserved_regs.len) { - return null; - } + return if (tryAllocRegs(self, 1, .{inst})) |regs| regs[0] else null; + } - // This is necessary because the return type of @ctz is 1 - // bit longer than ShiftInt if callee_preserved_regs.len - // is a power of two. This int cast is always safe because - // free_index < callee_preserved_regs.len - const shift = @intCast(ShiftInt, free_index); - const mask = @as(FreeRegInt, 1) << shift; - self.free_registers &= ~mask; - self.allocated_registers |= mask; + /// Before calling, must ensureCapacity + count on self.registers. + pub fn allocRegs(self: *Self, comptime count: comptime_int, insts: [count]*ir.Inst) ![count]Register { + comptime assert(count > 0 and count <= callee_preserved_regs.len); - const reg = callee_preserved_regs[free_index]; - self.registers.putAssumeCapacityNoClobber(reg, inst); - log.debug("alloc {} => {*}", .{ reg, inst }); - return reg; + return self.tryAllocRegs(count, insts) orelse blk: { + // We'll take over the first count registers. Spill + // the instructions that were previously there to a + // stack allocations. + var regs: [count]Register = undefined; + std.mem.copy(Register, ®s, callee_preserved_regs[0..count]); + + for (regs) |reg, i| { + if (self.isRegFree(reg)) { + self.markRegUsed(reg); + self.registers.putAssumeCapacityNoClobber(reg, insts[i]); + } else { + const regs_entry = self.registers.getEntry(reg).?; + const spilled_inst = regs_entry.value; + regs_entry.value = insts[i]; + try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); + } + } + + break :blk regs; + }; } /// Before calling, must ensureCapacity + 1 on self.registers. pub fn allocReg(self: *Self, inst: *ir.Inst) !Register { - return self.tryAllocReg(inst) orelse b: { - // We'll take over the first register. Move the instruction that was previously - // there to a stack allocation. - const reg = callee_preserved_regs[0]; - const regs_entry = self.registers.getEntry(reg).?; - const spilled_inst = regs_entry.value; - regs_entry.value = inst; - try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); + return (try allocRegs(self, 1, .{inst}))[0]; + } - break :b reg; - }; + /// Does not track the registers. + /// Returns `null` if not enough registers are free. + pub fn tryAllocRegsWithoutTracking(self: *Self, comptime count: comptime_int) ?[count]Register { + comptime if (callee_preserved_regs.len == 0) return null; + comptime assert(count > 0 and count <= callee_preserved_regs.len); + + const free_registers = @popCount(FreeRegInt, self.free_registers); + if (free_registers < count) return null; + + var regs: [count]Register = undefined; + var i: usize = 0; + for (callee_preserved_regs) |reg| { + if (i >= count) break; + if (self.isRegFree(reg)) { + regs[i] = reg; + i += 1; + } + } + return regs; } /// Does not track the register. /// Returns `null` if all registers are allocated. - pub fn findUnusedReg(self: *Self) ?Register { - const free_index = @ctz(FreeRegInt, self.free_registers); - if (free_index >= callee_preserved_regs.len) { - return null; - } - return callee_preserved_regs[free_index]; + pub fn tryAllocRegWithoutTracking(self: *Self) ?Register { + return if (tryAllocRegsWithoutTracking(self, 1)) |regs| regs[0] else null; } /// Does not track the register. pub fn allocRegWithoutTracking(self: *Self) !Register { - return self.findUnusedReg() orelse b: { + return self.tryAllocRegWithoutTracking() orelse b: { // We'll take over the first register. Move the instruction that was previously // there to a stack allocation. const reg = callee_preserved_regs[0]; @@ -190,7 +224,10 @@ pub fn RegisterManager( } const MockRegister = enum(u2) { - r0, r1, r2, r3, + r0, + r1, + r2, + r3, pub fn allocIndex(self: MockRegister) ?u2 { inline for (mock_callee_preserved_regs) |cpreg, i| { @@ -213,7 +250,7 @@ const MockFunction = struct { self.register_manager.deinit(self.allocator); self.spilled.deinit(self.allocator); } - + pub fn spillInstruction(self: *Self, src: LazySrcLoc, reg: MockRegister, inst: *ir.Inst) !void { try self.spilled.append(self.allocator, reg); } From 0e687d125bac2b0a282c01c39963ad22426b8147 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Mon, 22 Mar 2021 23:30:05 +1100 Subject: [PATCH 15/57] Add --(no-)allow-shlib-undefined to supported zig linking flags --- src/main.zig | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main.zig b/src/main.zig index cf0d08c5ed..5f9a97459f 100644 --- a/src/main.zig +++ b/src/main.zig @@ -344,6 +344,8 @@ const usage_build_generic = \\ --emit-relocs Enable output of relocation sections for post build tools \\ -dynamic Force output to be dynamically linked \\ -static Force output to be statically linked + \\ --allow-shlib-undefined Allows undefined symbols in shared libraries + \\ --no-allow-shlib-undefined Disallows undefined symbols in shared libraries \\ -Bsymbolic Bind global references locally \\ --subsystem [subsystem] (Windows) /SUBSYSTEM: to the linker \\ --stack [size] Override default stack size @@ -973,6 +975,10 @@ fn buildOutputType( link_eh_frame_hdr = true; } else if (mem.eql(u8, arg, "--emit-relocs")) { link_emit_relocs = true; + } else if (mem.eql(u8, arg, "--allow-shlib-undefined")) { + linker_allow_shlib_undefined = true; + } else if (mem.eql(u8, arg, "--no-allow-shlib-undefined")) { + linker_allow_shlib_undefined = false; } else if (mem.eql(u8, arg, "-Bsymbolic")) { linker_bind_global_refs_locally = true; } else if (mem.eql(u8, arg, "--verbose-link")) { From 99e7ba24b17d9b0a948c8fd01904d3597b5d4f84 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Mon, 22 Mar 2021 23:34:37 +1100 Subject: [PATCH 16/57] Add LibExeObjStep.linker_allow_shlib_undefined field to set --allow-shlib-undefined --- lib/std/build.zig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/std/build.zig b/lib/std/build.zig index 05871efba8..fa7fb9f018 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1386,6 +1386,8 @@ pub const LibExeObjStep = struct { /// safely garbage-collected during the linking phase. link_function_sections: bool = false, + linker_allow_shlib_undefined: ?bool = null, + /// Uses system Wine installation to run cross compiled Windows build artifacts. enable_wine: bool = false, @@ -2338,6 +2340,9 @@ pub const LibExeObjStep = struct { if (self.link_function_sections) { try zig_args.append("-ffunction-sections"); } + if (self.linker_allow_shlib_undefined) |x| { + try zig_args.append(if (x) "--allow-shlib-undefined" else "--no-allow-shlib-undefined"); + } if (self.single_threaded) { try zig_args.append("--single-threaded"); } From 01a1365857ad1bd209f9967c0ec8ce8aaa387138 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Fri, 16 Apr 2021 23:07:29 +0200 Subject: [PATCH 17/57] Rename --(no-)allow-shilb-undefined to -f(no-)allow-shilb-undefined This breaks with GNU ld but is consistent with our naming convention for all the rest of the flags. --- src/main.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main.zig b/src/main.zig index 5f9a97459f..f0772abbbd 100644 --- a/src/main.zig +++ b/src/main.zig @@ -340,12 +340,12 @@ const usage_build_generic = \\ -rpath [path] Add directory to the runtime library search path \\ -feach-lib-rpath Ensure adding rpath for each used dynamic library \\ -fno-each-lib-rpath Prevent adding rpath for each used dynamic library + \\ -fallow-shlib-undefined Allows undefined symbols in shared libraries + \\ -fno-allow-shlib-undefined Disallows undefined symbols in shared libraries \\ --eh-frame-hdr Enable C++ exception handling by passing --eh-frame-hdr to linker \\ --emit-relocs Enable output of relocation sections for post build tools \\ -dynamic Force output to be dynamically linked \\ -static Force output to be statically linked - \\ --allow-shlib-undefined Allows undefined symbols in shared libraries - \\ --no-allow-shlib-undefined Disallows undefined symbols in shared libraries \\ -Bsymbolic Bind global references locally \\ --subsystem [subsystem] (Windows) /SUBSYSTEM: to the linker \\ --stack [size] Override default stack size @@ -975,9 +975,9 @@ fn buildOutputType( link_eh_frame_hdr = true; } else if (mem.eql(u8, arg, "--emit-relocs")) { link_emit_relocs = true; - } else if (mem.eql(u8, arg, "--allow-shlib-undefined")) { + } else if (mem.eql(u8, arg, "-fallow-shlib-undefined")) { linker_allow_shlib_undefined = true; - } else if (mem.eql(u8, arg, "--no-allow-shlib-undefined")) { + } else if (mem.eql(u8, arg, "-fno-allow-shlib-undefined")) { linker_allow_shlib_undefined = false; } else if (mem.eql(u8, arg, "-Bsymbolic")) { linker_bind_global_refs_locally = true; From 5134fb72ef794f96bf936dea7e3e780d149dba8c Mon Sep 17 00:00:00 2001 From: Hubert Jasudowicz Date: Sat, 17 Apr 2021 01:08:13 +0200 Subject: [PATCH 18/57] std: Add pidfd wrappers --- lib/std/os/linux.zig | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index dbc1e2aee5..23bd5dac68 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -1387,6 +1387,29 @@ pub fn madvise(address: [*]u8, len: usize, advice: u32) usize { return syscall3(.madvise, @ptrToInt(address), len, advice); } +pub fn pidfd_open(pid: pid_t, flags: u32) usize { + return syscall2(.pidfd_open, @bitCast(usize, @as(isize, pid)), flags); +} + +pub fn pidfd_getfd(pidfd: fd_t, targetfd: fd_t, flags: u32) usize { + return syscall3( + .pidfd_getfd, + @bitCast(usize, @as(isize, pidfd)), + @bitCast(usize, @as(isize, targetfd)), + flags, + ); +} + +pub fn pidfd_send_signal(pidfd: fd_t, sig: i32, info: ?*siginfo_t, flags: u32) usize { + return syscall4( + .pidfd_send_signal, + @bitCast(usize, @as(isize, pidfd)), + @bitCast(usize, @as(isize, sig)), + @ptrToInt(info), + flags, + ); +} + test { if (builtin.os.tag == .linux) { _ = @import("linux/test.zig"); From afb9f695b1bdbf81185e7d55d5783bcbab880989 Mon Sep 17 00:00:00 2001 From: jacob gw Date: Fri, 16 Apr 2021 18:04:46 -0400 Subject: [PATCH 19/57] stage2: add support for zig cc assembler -mcpu option --- src/Compilation.zig | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 722b307c16..7ff7ef1374 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -2679,25 +2679,29 @@ pub fn addCCArgs( try argv.append("-fPIC"); } }, - .shared_library, .assembly, .ll, .bc, .unknown, .static_library, .object, .zig => {}, + .shared_library, .ll, .bc, .unknown, .static_library, .object, .zig => {}, + .assembly => { + // Argh, why doesn't the assembler accept the list of CPU features?! + // I don't see a way to do this other than hard coding everything. + switch (target.cpu.arch) { + .riscv32, .riscv64 => { + if (std.Target.riscv.featureSetHas(target.cpu.features, .relax)) { + try argv.append("-mrelax"); + } else { + try argv.append("-mno-relax"); + } + }, + else => { + // TODO + }, + } + if (target.cpu.model.llvm_name) |ln| + try argv.append(try std.fmt.allocPrint(arena, "-mcpu={s}", .{ln})); + }, } if (out_dep_path) |p| { try argv.appendSlice(&[_][]const u8{ "-MD", "-MV", "-MF", p }); } - // Argh, why doesn't the assembler accept the list of CPU features?! - // I don't see a way to do this other than hard coding everything. - switch (target.cpu.arch) { - .riscv32, .riscv64 => { - if (std.Target.riscv.featureSetHas(target.cpu.features, .relax)) { - try argv.append("-mrelax"); - } else { - try argv.append("-mno-relax"); - } - }, - else => { - // TODO - }, - } if (target.os.tag == .freestanding) { try argv.append("-ffreestanding"); From 8e6865c8eeff477375113f3c56f378d0e40ffecd Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Sat, 17 Apr 2021 14:05:13 +0200 Subject: [PATCH 20/57] std/build: fix linker_allow_shlib_undefined The relevant flags were renamed in 01a1365 but updating std.build was overlooked. --- lib/std/build.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index fa7fb9f018..fce3a0bceb 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -2341,7 +2341,7 @@ pub const LibExeObjStep = struct { try zig_args.append("-ffunction-sections"); } if (self.linker_allow_shlib_undefined) |x| { - try zig_args.append(if (x) "--allow-shlib-undefined" else "--no-allow-shlib-undefined"); + try zig_args.append(if (x) "-fallow-shlib-undefined" else "-fno-allow-shlib-undefined"); } if (self.single_threaded) { try zig_args.append("--single-threaded"); From 4081e0a475670cf3c0301c795c67e1bc692024e1 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Sat, 17 Apr 2021 23:34:59 +0200 Subject: [PATCH 21/57] zig fmt: bypass auto indentation for `// zig fmt: on` Currently an indented `// zig fmt: on` will be indented an additional time on every run of zig fmt. --- lib/std/zig/parser_test.zig | 21 +++++++++++++++++++++ lib/std/zig/render.zig | 4 ++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index 1bed86f42d..c433ee5ad8 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -1315,6 +1315,27 @@ test "zig fmt: 'zig fmt: (off|on)' works in the middle of code" { ); } +test "zig fmt: 'zig fmt: on' indentation is unchanged" { + try testCanonical( + \\fn initOptionsAndLayouts(output: *Output, context: *Context) !void { + \\ // zig fmt: off + \\ try output.main_amount.init(output, "main_amount"); errdefer optput.main_amount.deinit(); + \\ try output.main_factor.init(output, "main_factor"); errdefer optput.main_factor.deinit(); + \\ try output.view_padding.init(output, "view_padding"); errdefer optput.view_padding.deinit(); + \\ try output.outer_padding.init(output, "outer_padding"); errdefer optput.outer_padding.deinit(); + \\ // zig fmt: on + \\ + \\ // zig fmt: off + \\ try output.top.init(output, .top); errdefer optput.top.deinit(); + \\ try output.right.init(output, .right); errdefer optput.right.deinit(); + \\ try output.bottom.init(output, .bottom); errdefer optput.bottom.deinit(); + \\ try output.left.init(output, .left); errdefer optput.left.deinit(); + \\ // zig fmt: on + \\} + \\ + ); +} + test "zig fmt: pointer of unknown length" { try testCanonical( \\fn foo(ptr: [*]u8) void {} diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index 71d5e51bb9..9291f63e2f 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -2311,9 +2311,9 @@ fn renderComments(ais: *Ais, tree: ast.Tree, start: usize, end: usize) Error!boo // to the underlying writer, fixing up invaild whitespace. const disabled_source = tree.source[ais.disabled_offset.?..comment_start]; try writeFixingWhitespace(ais.underlying_writer, disabled_source); - ais.disabled_offset = null; // Write with the canonical single space. - try ais.writer().writeAll("// zig fmt: on\n"); + try ais.underlying_writer.writeAll("// zig fmt: on\n"); + ais.disabled_offset = null; } else if (ais.disabled_offset == null and mem.eql(u8, comment_content, "zig fmt: off")) { // Write with the canonical single space. try ais.writer().writeAll("// zig fmt: off\n"); From fbda9991f48a6cd3a008890634ba4482f946a6f4 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Sat, 17 Apr 2021 19:42:54 +0200 Subject: [PATCH 22/57] stage2 codegen: Fix silent bug in reuseOperand --- src/codegen.zig | 1 - 1 file changed, 1 deletion(-) diff --git a/src/codegen.zig b/src/codegen.zig index a345fd8058..aaab766336 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -1247,7 +1247,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { }, .stack_offset => |off| { log.debug("reusing stack offset {} => {*}", .{ off, inst }); - return true; }, else => return false, } From d605f02756038c8f1328b3478635ed9518d38239 Mon Sep 17 00:00:00 2001 From: Hubert Jasudowicz Date: Mon, 19 Apr 2021 13:37:55 +0200 Subject: [PATCH 23/57] std: Add process_vm_readv/writev wrappers --- lib/std/os/linux.zig | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index 23bd5dac68..e7826f89bf 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -1410,6 +1410,30 @@ pub fn pidfd_send_signal(pidfd: fd_t, sig: i32, info: ?*siginfo_t, flags: u32) u ); } +pub fn process_vm_readv(pid: pid_t, local: [*]const iovec, local_count: usize, remote: [*]const iovec, remote_count: usize, flags: usize) usize { + return syscall6( + .process_vm_readv, + @bitCast(usize, @as(isize, pid)), + @ptrToInt(local), + local_count, + @ptrToInt(remote), + remote_count, + flags, + ); +} + +pub fn process_vm_writev(pid: pid_t, local: [*]const iovec, local_count: usize, remote: [*]const iovec, remote_count: usize, flags: usize) usize { + return syscall6( + .process_vm_writev, + @bitCast(usize, @as(isize, pid)), + @ptrToInt(local), + local_count, + @ptrToInt(remote), + remote_count, + flags, + ); +} + test { if (builtin.os.tag == .linux) { _ = @import("linux/test.zig"); From e2cc02717e2c0180acfd2c5d0160b56721ae2397 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Mon, 19 Apr 2021 22:50:00 +0200 Subject: [PATCH 24/57] Fix .gitattributes rules that should be recursive These are currently incorrect according to the gitattributes(5) and gitignore(5) man pages. However, it seems github ended up treating them as we intended due to a bug until recently when that bug was fixed. --- .gitattributes | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.gitattributes b/.gitattributes index 8bf5843425..6cf47bc9ad 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,9 +3,9 @@ langref.html.in text eol=lf deps/SoftFloat-3e/*.txt text eol=crlf -deps/* linguist-vendored -lib/include/* linguist-vendored -lib/libc/* linguist-vendored -lib/libcxx/* linguist-vendored -lib/libcxxabi/* linguist-vendored -lib/libunwind/* linguist-vendored +deps/** linguist-vendored +lib/include/** linguist-vendored +lib/libc/** linguist-vendored +lib/libcxx/** linguist-vendored +lib/libcxxabi/** linguist-vendored +lib/libunwind/** linguist-vendored From 55e6bd10fe49614cd54eb02dd44c50d0643804c3 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Sun, 18 Apr 2021 20:55:05 -0700 Subject: [PATCH 25/57] translate-c: use transCreateNodeInfixOp for pointer arithmetic --- src/translate_c.zig | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/translate_c.zig b/src/translate_c.zig index 8a2a1b6223..ac5c52ee0d 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -1353,10 +1353,14 @@ fn transCreatePointerArithmeticSignedOp( const bitcast_node = try usizeCastForWrappingPtrArithmetic(c.arena, rhs_node); - const arith_args = .{ .lhs = lhs_node, .rhs = bitcast_node }; - const arith_node = try if (is_add) Tag.add.create(c.arena, arith_args) else Tag.sub.create(c.arena, arith_args); - - return maybeSuppressResult(c, scope, result_used, arith_node); + return transCreateNodeInfixOp( + c, + scope, + if (is_add) .add else .sub, + lhs_node, + bitcast_node, + result_used, + ); } fn transBinaryOperator( From 63304a871e3837f407943ac8eb63073fdc89f0fd Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Sun, 18 Apr 2021 20:56:00 -0700 Subject: [PATCH 26/57] translate-c: group LHS of array access if necessary --- src/translate_c/ast.zig | 2 +- test/run_translated_c.zig | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index 4be0fead97..dfc72b2a69 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -1665,7 +1665,7 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { }, .array_access => { const payload = node.castTag(.array_access).?.data; - const lhs = try renderNode(c, payload.lhs); + const lhs = try renderNodeGrouped(c, payload.lhs); const l_bracket = try c.addToken(.l_bracket, "["); const index_expr = try renderNode(c, payload.rhs); _ = try c.addToken(.r_bracket, "]"); diff --git a/test/run_translated_c.zig b/test/run_translated_c.zig index 35686c11f9..314ef2889d 100644 --- a/test/run_translated_c.zig +++ b/test/run_translated_c.zig @@ -1467,4 +1467,13 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { \\ return 0; \\} , ""); + + cases.add("Render array LHS as grouped node if necessary", + \\#include + \\int main(void) { + \\ int arr[] = {40, 41, 42, 43}; + \\ if ((arr + 1)[1] != 42) abort(); + \\ return 0; + \\} + , ""); } From be551d85b7610a7e0570023934f9b92ddf129966 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Tue, 20 Apr 2021 11:07:21 +0200 Subject: [PATCH 27/57] translate-c: Group field access LHS if necessary --- src/translate_c/ast.zig | 4 ++-- test/translate_c.zig | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index dfc72b2a69..61d28bb22d 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -1728,7 +1728,7 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { }, .field_access => { const payload = node.castTag(.field_access).?.data; - const lhs = try renderNode(c, payload.lhs); + const lhs = try renderNodeGrouped(c, payload.lhs); return renderFieldAccess(c, lhs, payload.field_name); }, .@"struct", .@"union" => return renderRecord(c, node), @@ -2073,7 +2073,7 @@ fn renderNullSentinelArrayType(c: *Context, len: usize, elem_type: Node) !NodeIn .main_token = l_bracket, .data = .{ .lhs = len_expr, - .rhs = try c.addExtra(std.zig.ast.Node.ArrayTypeSentinel { + .rhs = try c.addExtra(std.zig.ast.Node.ArrayTypeSentinel{ .sentinel = sentinel_expr, .elem_type = elem_type_expr, }), diff --git a/test/translate_c.zig b/test/translate_c.zig index 415d303f31..5fa4c32041 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -3,6 +3,22 @@ const std = @import("std"); const CrossTarget = std.zig.CrossTarget; pub fn addCases(cases: *tests.TranslateCContext) void { + cases.add("field access is grouped if necessary", + \\unsigned long foo(unsigned long x) { + \\ return ((union{unsigned long _x}){x})._x; + \\} + , &[_][]const u8{ + \\pub export fn foo(arg_x: c_ulong) c_ulong { + \\ var x = arg_x; + \\ const union_unnamed_1 = extern union { + \\ _x: c_ulong, + \\ }; + \\ return (union_unnamed_1{ + \\ ._x = x, + \\ })._x; + \\} + }); + cases.add("unnamed child types of typedef receive typedef's name", \\typedef enum { \\ FooA, From c7c77fb1b049070f9bf4009a36c9e563783fab62 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Tue, 20 Apr 2021 15:43:01 +0200 Subject: [PATCH 28/57] c: Add tests for generic_fmod implementation --- lib/std/special/c.zig | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/lib/std/special/c.zig b/lib/std/special/c.zig index caa2805956..5112e76dfb 100644 --- a/lib/std/special/c.zig +++ b/lib/std/special/c.zig @@ -859,6 +859,27 @@ fn generic_fmod(comptime T: type, x: T, y: T) T { return @bitCast(T, ux); } +test "fmod, fmodf" { + inline for ([_]type{ f32, f64 }) |T| { + const nan_val = math.nan(T); + const inf_val = math.inf(T); + + std.testing.expect(isNan(generic_fmod(T, nan_val, 1.0))); + std.testing.expect(isNan(generic_fmod(T, 1.0, nan_val))); + std.testing.expect(isNan(generic_fmod(T, inf_val, 1.0))); + std.testing.expect(isNan(generic_fmod(T, 0.0, 0.0))); + std.testing.expect(isNan(generic_fmod(T, 1.0, 0.0))); + + std.testing.expectEqual(@as(T, 0.0), generic_fmod(T, 0.0, 2.0)); + std.testing.expectEqual(@as(T, -0.0), generic_fmod(T, -0.0, 2.0)); + + std.testing.expectEqual(@as(T, -2.0), generic_fmod(T, -32.0, 10.0)); + std.testing.expectEqual(@as(T, -2.0), generic_fmod(T, -32.0, -10.0)); + std.testing.expectEqual(@as(T, 2.0), generic_fmod(T, 32.0, 10.0)); + std.testing.expectEqual(@as(T, 2.0), generic_fmod(T, 32.0, -10.0)); + } +} + // NOTE: The original code is full of implicit signed -> unsigned assumptions and u32 wraparound // behaviour. Most intermediate i32 values are changed to u32 where appropriate but there are // potentially some edge cases remaining that are not handled in the same way. From fcfe25710bcc7c3c8ccd23bbad32c02ae6df7c40 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Tue, 20 Apr 2021 15:43:46 +0200 Subject: [PATCH 29/57] c: Implement fmin and fminf --- lib/std/special/c.zig | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/lib/std/special/c.zig b/lib/std/special/c.zig index 5112e76dfb..bea172b81d 100644 --- a/lib/std/special/c.zig +++ b/lib/std/special/c.zig @@ -880,6 +880,35 @@ test "fmod, fmodf" { } } +fn generic_fmin(comptime T: type, x: T, y: T) T { + if (isNan(x)) + return y; + if (isNan(y)) + return x; + return if (x < y) x else y; +} + +export fn fminf(x: f32, y: f32) callconv(.C) f32 { + return generic_fmin(f32, x, y); +} + +export fn fmin(x: f64, y: f64) callconv(.C) f64 { + return generic_fmin(f64, x, y); +} + +test "fmin, fminf" { + inline for ([_]type{ f32, f64 }) |T| { + const nan_val = math.nan(T); + + std.testing.expect(isNan(generic_fmin(T, nan_val, nan_val))); + std.testing.expectEqual(@as(T, 1.0), generic_fmin(T, nan_val, 1.0)); + std.testing.expectEqual(@as(T, 1.0), generic_fmin(T, 1.0, nan_val)); + + std.testing.expectEqual(@as(T, 1.0), generic_fmin(T, 1.0, 10.0)); + std.testing.expectEqual(@as(T, -1.0), generic_fmin(T, 1.0, -1.0)); + } +} + // NOTE: The original code is full of implicit signed -> unsigned assumptions and u32 wraparound // behaviour. Most intermediate i32 values are changed to u32 where appropriate but there are // potentially some edge cases remaining that are not handled in the same way. From a4e2539b09dcf73ccf1bd57541b3b6c267ca96c1 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Tue, 20 Apr 2021 15:44:21 +0200 Subject: [PATCH 30/57] c: Implement fmax and fmaxf --- lib/std/special/c.zig | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/lib/std/special/c.zig b/lib/std/special/c.zig index bea172b81d..ffd31bea00 100644 --- a/lib/std/special/c.zig +++ b/lib/std/special/c.zig @@ -909,6 +909,35 @@ test "fmin, fminf" { } } +fn generic_fmax(comptime T: type, x: T, y: T) T { + if (isNan(x)) + return y; + if (isNan(y)) + return x; + return if (x < y) y else x; +} + +export fn fmaxf(x: f32, y: f32) callconv(.C) f32 { + return generic_fmax(f32, x, y); +} + +export fn fmax(x: f64, y: f64) callconv(.C) f64 { + return generic_fmax(f64, x, y); +} + +test "fmax, fmaxf" { + inline for ([_]type{ f32, f64 }) |T| { + const nan_val = math.nan(T); + + std.testing.expect(isNan(generic_fmax(T, nan_val, nan_val))); + std.testing.expectEqual(@as(T, 1.0), generic_fmax(T, nan_val, 1.0)); + std.testing.expectEqual(@as(T, 1.0), generic_fmax(T, 1.0, nan_val)); + + std.testing.expectEqual(@as(T, 10.0), generic_fmax(T, 1.0, 10.0)); + std.testing.expectEqual(@as(T, 1.0), generic_fmax(T, 1.0, -1.0)); + } +} + // NOTE: The original code is full of implicit signed -> unsigned assumptions and u32 wraparound // behaviour. Most intermediate i32 values are changed to u32 where appropriate but there are // potentially some edge cases remaining that are not handled in the same way. From a5a3ad5e10a6c5aa88af5eaf0170c5a15c9aad90 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Tue, 20 Apr 2021 15:44:54 +0200 Subject: [PATCH 31/57] c: Minor cleanup * Remove superfluous "test_" prefix in test names * Use aliased isNan everywhere --- lib/std/special/c.zig | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/std/special/c.zig b/lib/std/special/c.zig index ffd31bea00..379c4a40da 100644 --- a/lib/std/special/c.zig +++ b/lib/std/special/c.zig @@ -239,7 +239,7 @@ export fn memcmp(vl: ?[*]const u8, vr: ?[*]const u8, n: usize) callconv(.C) isiz return 0; } -test "test_memcmp" { +test "memcmp" { const base_arr = &[_]u8{ 1, 1, 1 }; const arr1 = &[_]u8{ 1, 1, 1 }; const arr2 = &[_]u8{ 1, 0, 1 }; @@ -263,7 +263,7 @@ export fn bcmp(vl: [*]allowzero const u8, vr: [*]allowzero const u8, n: usize) c return 0; } -test "test_bcmp" { +test "bcmp" { const base_arr = &[_]u8{ 1, 1, 1 }; const arr1 = &[_]u8{ 1, 1, 1 }; const arr2 = &[_]u8{ 1, 0, 1 }; @@ -1096,8 +1096,8 @@ test "sqrt special" { std.testing.expect(std.math.isPositiveInf(sqrt(std.math.inf(f64)))); std.testing.expect(sqrt(0.0) == 0.0); std.testing.expect(sqrt(-0.0) == -0.0); - std.testing.expect(std.math.isNan(sqrt(-1.0))); - std.testing.expect(std.math.isNan(sqrt(std.math.nan(f64)))); + std.testing.expect(isNan(sqrt(-1.0))); + std.testing.expect(isNan(sqrt(std.math.nan(f64)))); } export fn sqrtf(x: f32) f32 { @@ -1201,6 +1201,6 @@ test "sqrtf special" { std.testing.expect(std.math.isPositiveInf(sqrtf(std.math.inf(f32)))); std.testing.expect(sqrtf(0.0) == 0.0); std.testing.expect(sqrtf(-0.0) == -0.0); - std.testing.expect(std.math.isNan(sqrtf(-1.0))); - std.testing.expect(std.math.isNan(sqrtf(std.math.nan(f32)))); + std.testing.expect(isNan(sqrtf(-1.0))); + std.testing.expect(isNan(sqrtf(std.math.nan(f32)))); } From 3e73a3c29b597ff05e7a178106ff86056cf4494d Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 13 Apr 2021 22:48:31 +0200 Subject: [PATCH 32/57] zld: treat priv extern as weak symbol --- src/link/MachO.zig | 3 +-- src/link/MachO/Zld.zig | 29 +++++++++++++++++++++++++---- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 9a94b90137..aaf88ad815 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -645,8 +645,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { break :blk true; } - if (self.base.options.link_libcpp or - self.base.options.output_mode == .Lib or + if (self.base.options.output_mode == .Lib or self.base.options.linker_script != null) { // Fallback to LLD in this handful of cases on x86_64 only. diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index ae475ab30d..0fb1ab2145 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -1236,11 +1236,12 @@ fn resolveSymbolsInObject(self: *Zld, object_id: u16) !void { continue; } else if (Symbol.isGlobal(sym)) { const sym_name = object.getString(sym.n_strx); + const is_weak = Symbol.isWeakDef(sym) or Symbol.isPext(sym); const global = self.symtab.getEntry(sym_name) orelse { // Put new global symbol into the symbol table. const name = try self.allocator.dupe(u8, sym_name); try self.symtab.putNoClobber(self.allocator, name, .{ - .tag = if (Symbol.isWeakDef(sym)) .weak else .strong, + .tag = if (is_weak) .weak else .strong, .name = name, .address = 0, .section = 0, @@ -1251,10 +1252,15 @@ fn resolveSymbolsInObject(self: *Zld, object_id: u16) !void { }; switch (global.value.tag) { - .weak => continue, // If symbol is weak, nothing to do. + .weak => { + if (is_weak) continue; // Nothing to do for weak symbol. + }, .strong => { - log.err("symbol '{s}' defined multiple times", .{sym_name}); - return error.MultipleSymbolDefinitions; + if (!is_weak) { + log.err("symbol '{s}' defined multiple times", .{sym_name}); + return error.MultipleSymbolDefinitions; + } + continue; }, else => {}, } @@ -1340,6 +1346,21 @@ fn resolveSymbols(self: *Zld) !void { .section = 0, .file = 0, }); + + { + log.warn("symtab", .{}); + for (self.symtab.items()) |sym| { + switch (sym.value.tag) { + .weak, .strong => { + log.warn(" | {s} => {s}", .{ sym.key, self.objects.items[sym.value.file.?].name.? }); + }, + .import => { + log.warn(" | {s} => libSystem.B.dylib", .{sym.key}); + }, + else => unreachable, + } + } + } } fn resolveStubsAndGotEntries(self: *Zld) !void { From 8943a0aaaab1cda1285d3732ccc525ea5da8e794 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 14 Apr 2021 15:19:42 +0200 Subject: [PATCH 33/57] zld: dedup initializers and finalizers --- src/link/MachO/Zld.zig | 147 +++++++++++++++++++++++++++---- src/link/MachO/reloc/aarch64.zig | 4 +- 2 files changed, 132 insertions(+), 19 deletions(-) diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index 0fb1ab2145..52761ef048 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -81,12 +81,20 @@ threadlocal_offsets: std.ArrayListUnmanaged(u64) = .{}, local_rebases: std.ArrayListUnmanaged(Pointer) = .{}, stubs: std.StringArrayHashMapUnmanaged(u32) = .{}, got_entries: std.StringArrayHashMapUnmanaged(GotEntry) = .{}, +cpp_initializers: std.StringArrayHashMapUnmanaged(CppStatic) = .{}, +cpp_finalizers: std.StringArrayHashMapUnmanaged(CppStatic) = .{}, stub_helper_stubs_start_off: ?u64 = null, mappings: std.AutoHashMapUnmanaged(MappingKey, SectionMapping) = .{}, unhandled_sections: std.AutoHashMapUnmanaged(MappingKey, u0) = .{}, +const CppStatic = struct { + index: u32, + target_addr: u64, + file: u16, +}; + const GotEntry = struct { tag: enum { local, @@ -134,6 +142,16 @@ pub fn deinit(self: *Zld) void { } self.got_entries.deinit(self.allocator); + for (self.cpp_initializers.items()) |entry| { + self.allocator.free(entry.key); + } + self.cpp_initializers.deinit(self.allocator); + + for (self.cpp_finalizers.items()) |entry| { + self.allocator.free(entry.key); + } + self.cpp_finalizers.deinit(self.allocator); + for (self.load_commands.items) |*lc| { lc.deinit(self.allocator); } @@ -957,6 +975,38 @@ fn allocateStubsAndGotEntries(self: *Zld) !void { entry.value.target_addr, }); } + + for (self.cpp_initializers.items()) |*entry| { + const object = self.objects.items[entry.value.file]; + entry.value.target_addr = target_addr: { + if (object.locals.get(entry.key)) |local| { + break :target_addr local.address; + } + const global = self.symtab.get(entry.key) orelse unreachable; + break :target_addr global.address; + }; + + log.debug("resolving C++ initializer '{s}' at 0x{x}", .{ + entry.key, + entry.value.target_addr, + }); + } + + for (self.cpp_finalizers.items()) |*entry| { + const object = self.objects.items[entry.value.file]; + entry.value.target_addr = target_addr: { + if (object.locals.get(entry.key)) |local| { + break :target_addr local.address; + } + const global = self.symtab.get(entry.key) orelse unreachable; + break :target_addr global.address; + }; + + log.debug("resolving C++ finalizer '{s}' at 0x{x}", .{ + entry.key, + entry.value.target_addr, + }); + } } fn writeStubHelperCommon(self: *Zld) !void { @@ -1257,8 +1307,7 @@ fn resolveSymbolsInObject(self: *Zld, object_id: u16) !void { }, .strong => { if (!is_weak) { - log.err("symbol '{s}' defined multiple times", .{sym_name}); - return error.MultipleSymbolDefinitions; + log.debug("strong symbol '{s}' defined multiple times", .{sym_name}); } continue; }, @@ -1348,14 +1397,14 @@ fn resolveSymbols(self: *Zld) !void { }); { - log.warn("symtab", .{}); + log.debug("symtab", .{}); for (self.symtab.items()) |sym| { switch (sym.value.tag) { .weak, .strong => { - log.warn(" | {s} => {s}", .{ sym.key, self.objects.items[sym.value.file.?].name.? }); + log.debug(" | {s} => {s}", .{ sym.key, self.objects.items[sym.value.file.?].name.? }); }, .import => { - log.warn(" | {s} => libSystem.B.dylib", .{sym.key}); + log.debug(" | {s} => libSystem.B.dylib", .{sym.key}); }, else => unreachable, } @@ -1371,7 +1420,34 @@ fn resolveStubsAndGotEntries(self: *Zld) !void { const relocs = sect.relocs orelse continue; for (relocs) |rel| { switch (rel.@"type") { - .unsigned => continue, + .unsigned => { + if (rel.target != .symbol) continue; + + const sym = object.symtab.items[rel.target.symbol]; + const sym_name = object.getString(sym.n_strx); + + if (sect.inner.flags == macho.S_MOD_INIT_FUNC_POINTERS) { + if (self.cpp_initializers.contains(sym_name)) continue; + + var name = try self.allocator.dupe(u8, sym_name); + const index = @intCast(u32, self.cpp_initializers.items().len); + try self.cpp_initializers.putNoClobber(self.allocator, name, .{ + .index = index, + .target_addr = 0, + .file = @intCast(u16, object_id), + }); + } else if (sect.inner.flags == macho.S_MOD_TERM_FUNC_POINTERS) { + if (self.cpp_finalizers.contains(sym_name)) continue; + + var name = try self.allocator.dupe(u8, sym_name); + const index = @intCast(u32, self.cpp_finalizers.items().len); + try self.cpp_finalizers.putNoClobber(self.allocator, name, .{ + .index = index, + .target_addr = 0, + .file = @intCast(u16, object_id), + }); + } else continue; + }, .got_page, .got_page_off, .got_load, .got => { const sym = object.symtab.items[rel.target.symbol]; const sym_name = object.getString(sym.n_strx); @@ -1433,9 +1509,14 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void { log.debug("relocating object {s}", .{object.name}); for (object.sections.items) |sect, source_sect_id| { + if (sect.inner.flags == macho.S_MOD_INIT_FUNC_POINTERS or + sect.inner.flags == macho.S_MOD_TERM_FUNC_POINTERS) continue; + const segname = parseName(§.inner.segname); const sectname = parseName(§.inner.sectname); + log.debug("relocating section '{s},{s}'", .{ segname, sectname }); + // Get mapping const target_mapping = self.mappings.get(.{ .object_id = @intCast(u16, object_id), @@ -2061,6 +2142,42 @@ fn flush(self: *Zld) !void { try self.file.?.pwriteAll(buffer, sect.offset); } + if (self.mod_init_func_section_index) |index| { + const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; + const sect = &seg.sections.items[index]; + + var buffer = try self.allocator.alloc(u8, self.cpp_initializers.items().len * @sizeOf(u64)); + defer self.allocator.free(buffer); + + var stream = std.io.fixedBufferStream(buffer); + var writer = stream.writer(); + + for (self.cpp_initializers.items()) |entry| { + try writer.writeIntLittle(u64, entry.value.target_addr); + } + + _ = try self.file.?.pwriteAll(buffer, sect.offset); + sect.size = @intCast(u32, buffer.len); + } + + if (self.mod_term_func_section_index) |index| { + const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; + const sect = &seg.sections.items[index]; + + var buffer = try self.allocator.alloc(u8, self.cpp_finalizers.items().len * @sizeOf(u64)); + defer self.allocator.free(buffer); + + var stream = std.io.fixedBufferStream(buffer); + var writer = stream.writer(); + + for (self.cpp_finalizers.items()) |entry| { + try writer.writeIntLittle(u64, entry.value.target_addr); + } + + _ = try self.file.?.pwriteAll(buffer, sect.offset); + sect.size = @intCast(u32, buffer.len); + } + try self.writeGotEntries(); try self.setEntryPoint(); try self.writeRebaseInfoTable(); @@ -2160,15 +2277,12 @@ fn writeRebaseInfoTable(self: *Zld) !void { // TODO audit and investigate this. const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; const sect = seg.sections.items[idx]; - const npointers = sect.size * @sizeOf(u64); const base_offset = sect.addr - seg.inner.vmaddr; const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?); - try pointers.ensureCapacity(pointers.items.len + npointers); - var i: usize = 0; - while (i < npointers) : (i += 1) { - pointers.appendAssumeCapacity(.{ - .offset = base_offset + i * @sizeOf(u64), + for (self.cpp_initializers.items()) |entry| { + try pointers.append(.{ + .offset = base_offset + entry.value.index * @sizeOf(u64), .segment_id = segment_id, }); } @@ -2178,15 +2292,12 @@ fn writeRebaseInfoTable(self: *Zld) !void { // TODO audit and investigate this. const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; const sect = seg.sections.items[idx]; - const npointers = sect.size * @sizeOf(u64); const base_offset = sect.addr - seg.inner.vmaddr; const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?); - try pointers.ensureCapacity(pointers.items.len + npointers); - var i: usize = 0; - while (i < npointers) : (i += 1) { - pointers.appendAssumeCapacity(.{ - .offset = base_offset + i * @sizeOf(u64), + for (self.cpp_finalizers.items()) |entry| { + try pointers.append(.{ + .offset = base_offset + entry.value.index * @sizeOf(u64), .segment_id = segment_id, }); } diff --git a/src/link/MachO/reloc/aarch64.zig b/src/link/MachO/reloc/aarch64.zig index a7dd0919b4..d8e7cebddd 100644 --- a/src/link/MachO/reloc/aarch64.zig +++ b/src/link/MachO/reloc/aarch64.zig @@ -226,7 +226,9 @@ pub const Parser = struct { try parser.parseTlvpLoadPageOff(rel); }, .ARM64_RELOC_POINTER_TO_GOT => { - return error.ToDoRelocPointerToGot; + // TODO Handle pointer to GOT. This reloc seems to appear in + // __LD,__compact_unwind section which we currently don't handle. + log.debug("Unhandled relocation ARM64_RELOC_POINTER_TO_GOT", .{}); }, } } From e5b83056ae335b12caf5b8912ec40e9962ce27f7 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 19 Apr 2021 22:29:13 +0200 Subject: [PATCH 34/57] zld: resolve symbols as globals before resolving locals --- src/link/MachO/Zld.zig | 78 +++++++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 35 deletions(-) diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index 52761ef048..4bdb419ffc 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -1396,20 +1396,20 @@ fn resolveSymbols(self: *Zld) !void { .file = 0, }); - { - log.debug("symtab", .{}); - for (self.symtab.items()) |sym| { - switch (sym.value.tag) { - .weak, .strong => { - log.debug(" | {s} => {s}", .{ sym.key, self.objects.items[sym.value.file.?].name.? }); - }, - .import => { - log.debug(" | {s} => libSystem.B.dylib", .{sym.key}); - }, - else => unreachable, - } - } - } + // { + // log.warn("symtab", .{}); + // for (self.symtab.items()) |sym| { + // switch (sym.value.tag) { + // .weak, .strong => { + // log.warn(" | {s} => {s}", .{ sym.key, self.objects.items[sym.value.file.?].name.? }); + // }, + // .import => { + // log.warn(" | {s} => libSystem.B.dylib", .{sym.key}); + // }, + // else => unreachable, + // } + // } + // } } fn resolveStubsAndGotEntries(self: *Zld) !void { @@ -1654,7 +1654,31 @@ fn relocTargetAddr(self: *Zld, object_id: u16, target: reloc.Relocation.Target) const sym = object.symtab.items[sym_id]; const sym_name = object.getString(sym.n_strx); - if (Symbol.isSect(sym)) { + if (self.symtab.get(sym_name)) |global| { + switch (global.tag) { + .weak, .strong => { + log.debug(" | global symbol '{s}'", .{sym_name}); + break :blk global.address; + }, + .import => { + if (self.stubs.get(sym_name)) |index| { + log.debug(" | symbol stub '{s}'", .{sym_name}); + const segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; + const stubs = segment.sections.items[self.stubs_section_index.?]; + break :blk stubs.addr + index * stubs.reserved2; + } else if (mem.eql(u8, sym_name, "__tlv_bootstrap")) { + log.debug(" | symbol '__tlv_bootstrap'", .{}); + const segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment; + const tlv = segment.sections.items[self.tlv_section_index.?]; + break :blk tlv.addr; + } else { + log.err("failed to resolve symbol '{s}' as a relocation target", .{sym_name}); + return error.FailedToResolveRelocationTarget; + } + }, + else => unreachable, + } + } else if (Symbol.isSect(sym)) { log.debug(" | local symbol '{s}'", .{sym_name}); if (object.locals.get(sym_name)) |local| { break :blk local.address; @@ -1674,24 +1698,8 @@ fn relocTargetAddr(self: *Zld, object_id: u16, target: reloc.Relocation.Target) const target_addr = target_sect.addr + target_mapping.offset; break :blk sym.n_value - source_sect.addr + target_addr; } else { - if (self.stubs.get(sym_name)) |index| { - log.debug(" | symbol stub '{s}'", .{sym_name}); - const segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; - const stubs = segment.sections.items[self.stubs_section_index.?]; - break :blk stubs.addr + index * stubs.reserved2; - } else if (mem.eql(u8, sym_name, "__tlv_bootstrap")) { - log.debug(" | symbol '__tlv_bootstrap'", .{}); - const segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment; - const tlv = segment.sections.items[self.tlv_section_index.?]; - break :blk tlv.addr; - } else { - const global = self.symtab.get(sym_name) orelse { - log.err("failed to resolve symbol '{s}' as a relocation target", .{sym_name}); - return error.FailedToResolveRelocationTarget; - }; - log.debug(" | global symbol '{s}'", .{sym_name}); - break :blk global.address; - } + log.err("failed to resolve symbol '{s}' as a relocation target", .{sym_name}); + return error.FailedToResolveRelocationTarget; } }, .section => |sect_id| { @@ -2148,7 +2156,7 @@ fn flush(self: *Zld) !void { var buffer = try self.allocator.alloc(u8, self.cpp_initializers.items().len * @sizeOf(u64)); defer self.allocator.free(buffer); - + var stream = std.io.fixedBufferStream(buffer); var writer = stream.writer(); @@ -2166,7 +2174,7 @@ fn flush(self: *Zld) !void { var buffer = try self.allocator.alloc(u8, self.cpp_finalizers.items().len * @sizeOf(u64)); defer self.allocator.free(buffer); - + var stream = std.io.fixedBufferStream(buffer); var writer = stream.writer(); From 7bc6554a58274730f762bffb1f031af8dcfdb1fb Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 20 Apr 2021 07:23:21 +0200 Subject: [PATCH 35/57] zld: map __DATA,__common as __DATA_CONST,__common --- src/link/MachO/Zld.zig | 71 ++++++++++++++++++++++++++++++++---------- 1 file changed, 55 insertions(+), 16 deletions(-) diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index 4bdb419ffc..4a3c307806 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -64,6 +64,7 @@ got_section_index: ?u16 = null, mod_init_func_section_index: ?u16 = null, mod_term_func_section_index: ?u16 = null, data_const_section_index: ?u16 = null, +common_section_index: ?u16 = null, // __DATA segment sections tlv_section_index: ?u16 = null, @@ -483,23 +484,43 @@ fn updateMetadata(self: *Zld) !void { }, macho.S_ZEROFILL => { if (!mem.eql(u8, segname, "__DATA")) continue; - if (self.bss_section_index != null) continue; + if (mem.eql(u8, sectname, "__common")) { + if (self.common_section_index != null) continue; - self.bss_section_index = @intCast(u16, data_seg.sections.items.len); - try data_seg.addSection(self.allocator, .{ - .sectname = makeStaticString("__bss"), - .segname = makeStaticString("__DATA"), - .addr = 0, - .size = 0, - .offset = 0, - .@"align" = 0, - .reloff = 0, - .nreloc = 0, - .flags = macho.S_ZEROFILL, - .reserved1 = 0, - .reserved2 = 0, - .reserved3 = 0, - }); + self.common_section_index = @intCast(u16, data_const_seg.sections.items.len); + try data_const_seg.addSection(self.allocator, .{ + .sectname = makeStaticString("__common"), + .segname = makeStaticString("__DATA_CONST"), + .addr = 0, + .size = 0, + .offset = 0, + .@"align" = 0, + .reloff = 0, + .nreloc = 0, + .flags = macho.S_ZEROFILL, + .reserved1 = 0, + .reserved2 = 0, + .reserved3 = 0, + }); + } else { + if (self.bss_section_index != null) continue; + + self.bss_section_index = @intCast(u16, data_seg.sections.items.len); + try data_seg.addSection(self.allocator, .{ + .sectname = makeStaticString("__bss"), + .segname = makeStaticString("__DATA"), + .addr = 0, + .size = 0, + .offset = 0, + .@"align" = 0, + .reloff = 0, + .nreloc = 0, + .flags = macho.S_ZEROFILL, + .reserved1 = 0, + .reserved2 = 0, + .reserved3 = 0, + }); + } }, macho.S_THREAD_LOCAL_VARIABLES => { if (!mem.eql(u8, segname, "__DATA")) continue; @@ -586,7 +607,9 @@ fn updateMetadata(self: *Zld) !void { const segname = parseName(&source_sect.segname); const sectname = parseName(&source_sect.sectname); + log.debug("section '{s}/{s}' will be unmapped", .{ segname, sectname }); + try self.unhandled_sections.putNoClobber(self.allocator, .{ .object_id = object_id, .source_sect_id = source_sect_id, @@ -603,6 +626,7 @@ const MatchingSection = struct { fn getMatchingSection(self: *Zld, section: macho.section_64) ?MatchingSection { const segname = parseName(§ion.segname); const sectname = parseName(§ion.sectname); + const res: ?MatchingSection = blk: { switch (section.flags) { macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => { @@ -630,6 +654,12 @@ fn getMatchingSection(self: *Zld, section: macho.section_64) ?MatchingSection { }; }, macho.S_ZEROFILL => { + if (mem.eql(u8, sectname, "__common")) { + break :blk .{ + .seg = self.data_const_segment_cmd_index.?, + .sect = self.common_section_index.?, + }; + } break :blk .{ .seg = self.data_segment_cmd_index.?, .sect = self.bss_section_index.?, @@ -685,6 +715,7 @@ fn getMatchingSection(self: *Zld, section: macho.section_64) ?MatchingSection { }, } }; + return res; } @@ -733,6 +764,7 @@ fn sortSections(self: *Zld) !void { &self.mod_init_func_section_index, &self.mod_term_func_section_index, &self.data_const_section_index, + &self.common_section_index, }; for (indices) |maybe_index| { const new_index: u16 = if (maybe_index.*) |index| blk: { @@ -1634,6 +1666,7 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void { target_sect_off, target_sect_off + sect.code.len, }); + // Zero-out the space var zeroes = try self.allocator.alloc(u8, sect.code.len); defer self.allocator.free(zeroes); @@ -2118,6 +2151,12 @@ fn populateMetadata(self: *Zld) !void { } fn flush(self: *Zld) !void { + if (self.common_section_index) |index| { + const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; + const sect = &seg.sections.items[index]; + sect.offset = 0; + } + if (self.bss_section_index) |index| { const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; const sect = &seg.sections.items[index]; From d64c76e8f1faf493d5fa6f3c0d9324f05f99d68f Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 20 Apr 2021 16:55:08 +0200 Subject: [PATCH 36/57] zld: fix parsing of weak symbols --- src/link/MachO/Symbol.zig | 2 +- src/link/MachO/Zld.zig | 71 ++++++++++++++++++++------------------- 2 files changed, 37 insertions(+), 36 deletions(-) diff --git a/src/link/MachO/Symbol.zig b/src/link/MachO/Symbol.zig index f65a694f75..9e6c2bf68a 100644 --- a/src/link/MachO/Symbol.zig +++ b/src/link/MachO/Symbol.zig @@ -52,7 +52,7 @@ pub fn isUndf(sym: macho.nlist_64) bool { } pub fn isWeakDef(sym: macho.nlist_64) bool { - return sym.n_desc == macho.N_WEAK_DEF; + return (sym.n_desc & macho.N_WEAK_DEF) != 0; } /// Symbol is local if it is defined and not an extern. diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index 4a3c307806..6831251966 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -1340,13 +1340,14 @@ fn resolveSymbolsInObject(self: *Zld, object_id: u16) !void { .strong => { if (!is_weak) { log.debug("strong symbol '{s}' defined multiple times", .{sym_name}); + return error.MultipleSymbolDefinitions; } continue; }, else => {}, } - global.value.tag = .strong; + global.value.tag = if (is_weak) .weak else .strong; global.value.file = object_id; global.value.index = @intCast(u32, sym_id); } else if (Symbol.isUndef(sym)) { @@ -1428,20 +1429,20 @@ fn resolveSymbols(self: *Zld) !void { .file = 0, }); - // { - // log.warn("symtab", .{}); - // for (self.symtab.items()) |sym| { - // switch (sym.value.tag) { - // .weak, .strong => { - // log.warn(" | {s} => {s}", .{ sym.key, self.objects.items[sym.value.file.?].name.? }); - // }, - // .import => { - // log.warn(" | {s} => libSystem.B.dylib", .{sym.key}); - // }, - // else => unreachable, - // } - // } - // } + { + log.debug("symtab", .{}); + for (self.symtab.items()) |sym| { + switch (sym.value.tag) { + .weak, .strong => { + log.debug(" | {s} => {s}", .{ sym.key, self.objects.items[sym.value.file.?].name.? }); + }, + .import => { + log.debug(" | {s} => libSystem.B.dylib", .{sym.key}); + }, + else => unreachable, + } + } + } } fn resolveStubsAndGotEntries(self: *Zld) !void { @@ -1687,7 +1688,26 @@ fn relocTargetAddr(self: *Zld, object_id: u16, target: reloc.Relocation.Target) const sym = object.symtab.items[sym_id]; const sym_name = object.getString(sym.n_strx); - if (self.symtab.get(sym_name)) |global| { + if (Symbol.isSect(sym)) { + log.debug(" | local symbol '{s}'", .{sym_name}); + if (object.locals.get(sym_name)) |local| { + break :blk local.address; + } + // For temp locals, i.e., symbols prefixed with l... we relocate + // based on section addressing. + const source_sect_id = sym.n_sect - 1; + const target_mapping = self.mappings.get(.{ + .object_id = object_id, + .source_sect_id = source_sect_id, + }) orelse unreachable; + + const source_seg = object.load_commands.items[object.segment_cmd_index.?].Segment; + const source_sect = source_seg.sections.items[source_sect_id]; + const target_seg = self.load_commands.items[target_mapping.target_seg_id].Segment; + const target_sect = target_seg.sections.items[target_mapping.target_sect_id]; + const target_addr = target_sect.addr + target_mapping.offset; + break :blk sym.n_value - source_sect.addr + target_addr; + } else if (self.symtab.get(sym_name)) |global| { switch (global.tag) { .weak, .strong => { log.debug(" | global symbol '{s}'", .{sym_name}); @@ -1711,25 +1731,6 @@ fn relocTargetAddr(self: *Zld, object_id: u16, target: reloc.Relocation.Target) }, else => unreachable, } - } else if (Symbol.isSect(sym)) { - log.debug(" | local symbol '{s}'", .{sym_name}); - if (object.locals.get(sym_name)) |local| { - break :blk local.address; - } - // For temp locals, i.e., symbols prefixed with l... we relocate - // based on section addressing. - const source_sect_id = sym.n_sect - 1; - const target_mapping = self.mappings.get(.{ - .object_id = object_id, - .source_sect_id = source_sect_id, - }) orelse unreachable; - - const source_seg = object.load_commands.items[object.segment_cmd_index.?].Segment; - const source_sect = source_seg.sections.items[source_sect_id]; - const target_seg = self.load_commands.items[target_mapping.target_seg_id].Segment; - const target_sect = target_seg.sections.items[target_mapping.target_sect_id]; - const target_addr = target_sect.addr + target_mapping.offset; - break :blk sym.n_value - source_sect.addr + target_addr; } else { log.err("failed to resolve symbol '{s}' as a relocation target", .{sym_name}); return error.FailedToResolveRelocationTarget; From 10f2d6278946485f057f65bbb3c094a6fcde1adf Mon Sep 17 00:00:00 2001 From: Frank Denis <124872+jedisct1@users.noreply.github.com> Date: Tue, 20 Apr 2021 19:57:27 +0200 Subject: [PATCH 37/57] std/crypto: use finer-grained error sets in function signatures (#8558) std/crypto: use finer-grained error sets in function signatures Returning the `crypto.Error` error set for all crypto operations was very convenient to ensure that errors were used consistently, and to avoid having multiple error names for the same thing. The flipside is that callers were forced to always handle all possible errors, even those that could never be returned by a function. This PR makes all functions return union sets of the actual errors they can return. The error sets themselves are all limited to a single error. Larger sets are useful for platform-specific APIs, but we don't have any of these in `std/crypto`, and I couldn't find any meaningful way to build larger sets. --- lib/std/crypto.zig | 2 +- lib/std/crypto/25519/curve25519.zig | 20 ++++++++------ lib/std/crypto/25519/ed25519.zig | 25 +++++++++++------ lib/std/crypto/25519/edwards25519.zig | 40 +++++++++++++++------------ lib/std/crypto/25519/field.zig | 9 ++++-- lib/std/crypto/25519/ristretto255.zig | 14 ++++++---- lib/std/crypto/25519/scalar.zig | 5 ++-- lib/std/crypto/25519/x25519.zig | 15 ++++++---- lib/std/crypto/aegis.zig | 6 ++-- lib/std/crypto/aes_gcm.zig | 4 +-- lib/std/crypto/aes_ocb.zig | 4 +-- lib/std/crypto/bcrypt.zig | 11 ++++---- lib/std/crypto/chacha20.zig | 6 ++-- lib/std/crypto/error.zig | 34 ----------------------- lib/std/crypto/errors.zig | 35 +++++++++++++++++++++++ lib/std/crypto/gimli.zig | 4 +-- lib/std/crypto/isap.zig | 4 +-- lib/std/crypto/pbkdf2.zig | 5 ++-- lib/std/crypto/salsa20.zig | 19 +++++++------ 19 files changed, 148 insertions(+), 114 deletions(-) delete mode 100644 lib/std/crypto/error.zig create mode 100644 lib/std/crypto/errors.zig diff --git a/lib/std/crypto.zig b/lib/std/crypto.zig index 457b9130d9..e4ec50f5b7 100644 --- a/lib/std/crypto.zig +++ b/lib/std/crypto.zig @@ -154,7 +154,7 @@ pub const random = &@import("crypto/tlcsprng.zig").interface; const std = @import("std.zig"); -pub const Error = @import("crypto/error.zig").Error; +pub const errors = @import("crypto/errors.zig"); test "crypto" { const please_windows_dont_oom = std.Target.current.os.tag == .windows; diff --git a/lib/std/crypto/25519/curve25519.zig b/lib/std/crypto/25519/curve25519.zig index d3e51ad0e0..90b0e10c4f 100644 --- a/lib/std/crypto/25519/curve25519.zig +++ b/lib/std/crypto/25519/curve25519.zig @@ -4,7 +4,11 @@ // The MIT license requires this copyright notice to be included in all copies // and substantial portions of the software. const std = @import("std"); -const Error = std.crypto.Error; +const crypto = std.crypto; + +const IdentityElementError = crypto.errors.IdentityElementError; +const NonCanonicalError = crypto.errors.NonCanonicalError; +const WeakPublicKeyError = crypto.errors.WeakPublicKeyError; /// Group operations over Curve25519. pub const Curve25519 = struct { @@ -29,12 +33,12 @@ pub const Curve25519 = struct { pub const basePoint = Curve25519{ .x = Fe.curve25519BasePoint }; /// Check that the encoding of a Curve25519 point is canonical. - pub fn rejectNonCanonical(s: [32]u8) Error!void { + pub fn rejectNonCanonical(s: [32]u8) NonCanonicalError!void { return Fe.rejectNonCanonical(s, false); } /// Reject the neutral element. - pub fn rejectIdentity(p: Curve25519) Error!void { + pub fn rejectIdentity(p: Curve25519) IdentityElementError!void { if (p.x.isZero()) { return error.IdentityElement; } @@ -45,7 +49,7 @@ pub const Curve25519 = struct { return p.dbl().dbl().dbl(); } - fn ladder(p: Curve25519, s: [32]u8, comptime bits: usize) Error!Curve25519 { + fn ladder(p: Curve25519, s: [32]u8, comptime bits: usize) IdentityElementError!Curve25519 { var x1 = p.x; var x2 = Fe.one; var z2 = Fe.zero; @@ -86,7 +90,7 @@ pub const Curve25519 = struct { /// way to use Curve25519 for a DH operation. /// Return error.IdentityElement if the resulting point is /// the identity element. - pub fn clampedMul(p: Curve25519, s: [32]u8) Error!Curve25519 { + pub fn clampedMul(p: Curve25519, s: [32]u8) IdentityElementError!Curve25519 { var t: [32]u8 = s; scalar.clamp(&t); return try ladder(p, t, 255); @@ -96,16 +100,16 @@ pub const Curve25519 = struct { /// Return error.IdentityElement if the resulting point is /// the identity element or error.WeakPublicKey if the public /// key is a low-order point. - pub fn mul(p: Curve25519, s: [32]u8) Error!Curve25519 { + pub fn mul(p: Curve25519, s: [32]u8) (IdentityElementError || WeakPublicKeyError)!Curve25519 { const cofactor = [_]u8{8} ++ [_]u8{0} ** 31; _ = ladder(p, cofactor, 4) catch |_| return error.WeakPublicKey; return try ladder(p, s, 256); } /// Compute the Curve25519 equivalent to an Edwards25519 point. - pub fn fromEdwards25519(p: std.crypto.ecc.Edwards25519) Error!Curve25519 { + pub fn fromEdwards25519(p: crypto.ecc.Edwards25519) IdentityElementError!Curve25519 { try p.clearCofactor().rejectIdentity(); - const one = std.crypto.ecc.Edwards25519.Fe.one; + const one = crypto.ecc.Edwards25519.Fe.one; const x = one.add(p.y).mul(one.sub(p.y).invert()); // xMont=(1+yEd)/(1-yEd) return Curve25519{ .x = x }; } diff --git a/lib/std/crypto/25519/ed25519.zig b/lib/std/crypto/25519/ed25519.zig index e385e34f12..999b099bc6 100644 --- a/lib/std/crypto/25519/ed25519.zig +++ b/lib/std/crypto/25519/ed25519.zig @@ -8,8 +8,15 @@ const crypto = std.crypto; const debug = std.debug; const fmt = std.fmt; const mem = std.mem; + const Sha512 = crypto.hash.sha2.Sha512; -const Error = crypto.Error; + +const EncodingError = crypto.errors.EncodingError; +const IdentityElementError = crypto.errors.IdentityElementError; +const NonCanonicalError = crypto.errors.NonCanonicalError; +const SignatureVerificationError = crypto.errors.SignatureVerificationError; +const KeyMismatchError = crypto.errors.KeyMismatchError; +const WeakPublicKeyError = crypto.errors.WeakPublicKeyError; /// Ed25519 (EdDSA) signatures. pub const Ed25519 = struct { @@ -41,7 +48,7 @@ pub const Ed25519 = struct { /// /// For this reason, an EdDSA secret key is commonly called a seed, /// from which the actual secret is derived. - pub fn create(seed: ?[seed_length]u8) Error!KeyPair { + pub fn create(seed: ?[seed_length]u8) IdentityElementError!KeyPair { const ss = seed orelse ss: { var random_seed: [seed_length]u8 = undefined; crypto.random.bytes(&random_seed); @@ -51,7 +58,7 @@ pub const Ed25519 = struct { var h = Sha512.init(.{}); h.update(&ss); h.final(&az); - const p = try Curve.basePoint.clampedMul(az[0..32].*); + const p = Curve.basePoint.clampedMul(az[0..32].*) catch return error.IdentityElement; var sk: [secret_length]u8 = undefined; mem.copy(u8, &sk, &ss); const pk = p.toBytes(); @@ -72,7 +79,7 @@ pub const Ed25519 = struct { /// Sign a message using a key pair, and optional random noise. /// Having noise creates non-standard, non-deterministic signatures, /// but has been proven to increase resilience against fault attacks. - pub fn sign(msg: []const u8, key_pair: KeyPair, noise: ?[noise_length]u8) Error![signature_length]u8 { + pub fn sign(msg: []const u8, key_pair: KeyPair, noise: ?[noise_length]u8) (IdentityElementError || WeakPublicKeyError || KeyMismatchError)![signature_length]u8 { const seed = key_pair.secret_key[0..seed_length]; const public_key = key_pair.secret_key[seed_length..]; if (!mem.eql(u8, public_key, &key_pair.public_key)) { @@ -113,7 +120,7 @@ pub const Ed25519 = struct { /// Verify an Ed25519 signature given a message and a public key. /// Returns error.SignatureVerificationFailed is the signature verification failed. - pub fn verify(sig: [signature_length]u8, msg: []const u8, public_key: [public_length]u8) Error!void { + pub fn verify(sig: [signature_length]u8, msg: []const u8, public_key: [public_length]u8) (SignatureVerificationError || WeakPublicKeyError || EncodingError || NonCanonicalError || IdentityElementError)!void { const r = sig[0..32]; const s = sig[32..64]; try Curve.scalar.rejectNonCanonical(s.*); @@ -146,7 +153,7 @@ pub const Ed25519 = struct { }; /// Verify several signatures in a single operation, much faster than verifying signatures one-by-one - pub fn verifyBatch(comptime count: usize, signature_batch: [count]BatchElement) Error!void { + pub fn verifyBatch(comptime count: usize, signature_batch: [count]BatchElement) (SignatureVerificationError || IdentityElementError || WeakPublicKeyError || EncodingError || NonCanonicalError)!void { var r_batch: [count][32]u8 = undefined; var s_batch: [count][32]u8 = undefined; var a_batch: [count]Curve = undefined; @@ -180,7 +187,7 @@ pub const Ed25519 = struct { var z_batch: [count]Curve.scalar.CompressedScalar = undefined; for (z_batch) |*z| { - std.crypto.random.bytes(z[0..16]); + crypto.random.bytes(z[0..16]); mem.set(u8, z[16..], 0); } @@ -233,8 +240,8 @@ test "ed25519 batch verification" { const key_pair = try Ed25519.KeyPair.create(null); var msg1: [32]u8 = undefined; var msg2: [32]u8 = undefined; - std.crypto.random.bytes(&msg1); - std.crypto.random.bytes(&msg2); + crypto.random.bytes(&msg1); + crypto.random.bytes(&msg2); const sig1 = try Ed25519.sign(&msg1, key_pair, null); const sig2 = try Ed25519.sign(&msg2, key_pair, null); var signature_batch = [_]Ed25519.BatchElement{ diff --git a/lib/std/crypto/25519/edwards25519.zig b/lib/std/crypto/25519/edwards25519.zig index 89b7b9b9f3..c147bed597 100644 --- a/lib/std/crypto/25519/edwards25519.zig +++ b/lib/std/crypto/25519/edwards25519.zig @@ -4,10 +4,16 @@ // The MIT license requires this copyright notice to be included in all copies // and substantial portions of the software. const std = @import("std"); +const crypto = std.crypto; const debug = std.debug; const fmt = std.fmt; const mem = std.mem; -const Error = std.crypto.Error; + +const EncodingError = crypto.errors.EncodingError; +const IdentityElementError = crypto.errors.IdentityElementError; +const NonCanonicalError = crypto.errors.NonCanonicalError; +const NotSquareError = crypto.errors.NotSquareError; +const WeakPublicKeyError = crypto.errors.WeakPublicKeyError; /// Group operations over Edwards25519. pub const Edwards25519 = struct { @@ -26,7 +32,7 @@ pub const Edwards25519 = struct { is_base: bool = false, /// Decode an Edwards25519 point from its compressed (Y+sign) coordinates. - pub fn fromBytes(s: [encoded_length]u8) Error!Edwards25519 { + pub fn fromBytes(s: [encoded_length]u8) EncodingError!Edwards25519 { const z = Fe.one; const y = Fe.fromBytes(s); var u = y.sq(); @@ -56,7 +62,7 @@ pub const Edwards25519 = struct { } /// Check that the encoding of a point is canonical. - pub fn rejectNonCanonical(s: [32]u8) Error!void { + pub fn rejectNonCanonical(s: [32]u8) NonCanonicalError!void { return Fe.rejectNonCanonical(s, true); } @@ -81,7 +87,7 @@ pub const Edwards25519 = struct { const identityElement = Edwards25519{ .x = Fe.zero, .y = Fe.one, .z = Fe.one, .t = Fe.zero }; /// Reject the neutral element. - pub fn rejectIdentity(p: Edwards25519) Error!void { + pub fn rejectIdentity(p: Edwards25519) IdentityElementError!void { if (p.x.isZero()) { return error.IdentityElement; } @@ -177,7 +183,7 @@ pub const Edwards25519 = struct { // Based on real-world benchmarks, we only use this for multi-scalar multiplication. // NAF could be useful to half the size of precomputation tables, but we intentionally // avoid these to keep the standard library lightweight. - fn pcMul(pc: [9]Edwards25519, s: [32]u8, comptime vartime: bool) Error!Edwards25519 { + fn pcMul(pc: [9]Edwards25519, s: [32]u8, comptime vartime: bool) IdentityElementError!Edwards25519 { std.debug.assert(vartime); const e = nonAdjacentForm(s); var q = Edwards25519.identityElement; @@ -197,7 +203,7 @@ pub const Edwards25519 = struct { } // Scalar multiplication with a 4-bit window and the first 15 multiples. - fn pcMul16(pc: [16]Edwards25519, s: [32]u8, comptime vartime: bool) Error!Edwards25519 { + fn pcMul16(pc: [16]Edwards25519, s: [32]u8, comptime vartime: bool) IdentityElementError!Edwards25519 { var q = Edwards25519.identityElement; var pos: usize = 252; while (true) : (pos -= 4) { @@ -233,12 +239,12 @@ pub const Edwards25519 = struct { }; /// Multiply an Edwards25519 point by a scalar without clamping it. - /// Return error.WeakPublicKey if the resulting point is - /// the identity element. - pub fn mul(p: Edwards25519, s: [32]u8) Error!Edwards25519 { + /// Return error.WeakPublicKey if the base generates a small-order group, + /// and error.IdentityElement if the result is the identity element. + pub fn mul(p: Edwards25519, s: [32]u8) (IdentityElementError || WeakPublicKeyError)!Edwards25519 { const pc = if (p.is_base) basePointPc else pc: { const xpc = precompute(p, 15); - xpc[4].rejectIdentity() catch |_| return error.WeakPublicKey; + xpc[4].rejectIdentity() catch return error.WeakPublicKey; break :pc xpc; }; return pcMul16(pc, s, false); @@ -246,7 +252,7 @@ pub const Edwards25519 = struct { /// Multiply an Edwards25519 point by a *PUBLIC* scalar *IN VARIABLE TIME* /// This can be used for signature verification. - pub fn mulPublic(p: Edwards25519, s: [32]u8) Error!Edwards25519 { + pub fn mulPublic(p: Edwards25519, s: [32]u8) (IdentityElementError || WeakPublicKeyError)!Edwards25519 { if (p.is_base) { return pcMul16(basePointPc, s, true); } else { @@ -258,7 +264,7 @@ pub const Edwards25519 = struct { /// Multiscalar multiplication *IN VARIABLE TIME* for public data /// Computes ps0*ss0 + ps1*ss1 + ps2*ss2... faster than doing many of these operations individually - pub fn mulMulti(comptime count: usize, ps: [count]Edwards25519, ss: [count][32]u8) Error!Edwards25519 { + pub fn mulMulti(comptime count: usize, ps: [count]Edwards25519, ss: [count][32]u8) (IdentityElementError || WeakPublicKeyError)!Edwards25519 { var pcs: [count][9]Edwards25519 = undefined; for (ps) |p, i| { if (p.is_base) { @@ -297,14 +303,14 @@ pub const Edwards25519 = struct { /// This is strongly recommended for DH operations. /// Return error.WeakPublicKey if the resulting point is /// the identity element. - pub fn clampedMul(p: Edwards25519, s: [32]u8) Error!Edwards25519 { + pub fn clampedMul(p: Edwards25519, s: [32]u8) (IdentityElementError || WeakPublicKeyError)!Edwards25519 { var t: [32]u8 = s; scalar.clamp(&t); return mul(p, t); } // montgomery -- recover y = sqrt(x^3 + A*x^2 + x) - fn xmontToYmont(x: Fe) Error!Fe { + fn xmontToYmont(x: Fe) NotSquareError!Fe { var x2 = x.sq(); const x3 = x.mul(x2); x2 = x2.mul32(Fe.edwards25519a_32); @@ -367,7 +373,7 @@ pub const Edwards25519 = struct { fn stringToPoints(comptime n: usize, ctx: []const u8, s: []const u8) [n]Edwards25519 { debug.assert(n <= 2); - const H = std.crypto.hash.sha2.Sha512; + const H = crypto.hash.sha2.Sha512; const h_l: usize = 48; var xctx = ctx; var hctx: [H.digest_length]u8 = undefined; @@ -485,8 +491,8 @@ test "edwards25519 packing/unpacking" { test "edwards25519 point addition/substraction" { var s1: [32]u8 = undefined; var s2: [32]u8 = undefined; - std.crypto.random.bytes(&s1); - std.crypto.random.bytes(&s2); + crypto.random.bytes(&s1); + crypto.random.bytes(&s2); const p = try Edwards25519.basePoint.clampedMul(s1); const q = try Edwards25519.basePoint.clampedMul(s2); const r = p.add(q).add(q).sub(q).sub(q); diff --git a/lib/std/crypto/25519/field.zig b/lib/std/crypto/25519/field.zig index b570e2d06b..5ac184080c 100644 --- a/lib/std/crypto/25519/field.zig +++ b/lib/std/crypto/25519/field.zig @@ -4,9 +4,12 @@ // The MIT license requires this copyright notice to be included in all copies // and substantial portions of the software. const std = @import("std"); +const crypto = std.crypto; const readIntLittle = std.mem.readIntLittle; const writeIntLittle = std.mem.writeIntLittle; -const Error = std.crypto.Error; + +const NonCanonicalError = crypto.errors.NonCanonicalError; +const NotSquareError = crypto.errors.NotSquareError; pub const Fe = struct { limbs: [5]u64, @@ -113,7 +116,7 @@ pub const Fe = struct { } /// Reject non-canonical encodings of an element, possibly ignoring the top bit - pub fn rejectNonCanonical(s: [32]u8, comptime ignore_extra_bit: bool) Error!void { + pub fn rejectNonCanonical(s: [32]u8, comptime ignore_extra_bit: bool) NonCanonicalError!void { var c: u16 = (s[31] & 0x7f) ^ 0x7f; comptime var i = 30; inline while (i > 0) : (i -= 1) { @@ -413,7 +416,7 @@ pub const Fe = struct { } /// Compute the square root of `x2`, returning `error.NotSquare` if `x2` was not a square - pub fn sqrt(x2: Fe) Error!Fe { + pub fn sqrt(x2: Fe) NotSquareError!Fe { var x2_copy = x2; const x = x2.uncheckedSqrt(); const check = x.sq().sub(x2_copy); diff --git a/lib/std/crypto/25519/ristretto255.zig b/lib/std/crypto/25519/ristretto255.zig index 4644b7622e..50f1580a80 100644 --- a/lib/std/crypto/25519/ristretto255.zig +++ b/lib/std/crypto/25519/ristretto255.zig @@ -5,7 +5,11 @@ // and substantial portions of the software. const std = @import("std"); const fmt = std.fmt; -const Error = std.crypto.Error; + +const EncodingError = std.crypto.errors.EncodingError; +const IdentityElementError = std.crypto.errors.IdentityElementError; +const NonCanonicalError = std.crypto.errors.NonCanonicalError; +const WeakPublicKeyError = std.crypto.errors.WeakPublicKeyError; /// Group operations over Edwards25519. pub const Ristretto255 = struct { @@ -35,7 +39,7 @@ pub const Ristretto255 = struct { return .{ .ratio_is_square = @boolToInt(has_m_root) | @boolToInt(has_p_root), .root = x.abs() }; } - fn rejectNonCanonical(s: [encoded_length]u8) Error!void { + fn rejectNonCanonical(s: [encoded_length]u8) NonCanonicalError!void { if ((s[0] & 1) != 0) { return error.NonCanonical; } @@ -43,7 +47,7 @@ pub const Ristretto255 = struct { } /// Reject the neutral element. - pub fn rejectIdentity(p: Ristretto255) callconv(.Inline) Error!void { + pub fn rejectIdentity(p: Ristretto255) callconv(.Inline) IdentityElementError!void { return p.p.rejectIdentity(); } @@ -51,7 +55,7 @@ pub const Ristretto255 = struct { pub const basePoint = Ristretto255{ .p = Curve.basePoint }; /// Decode a Ristretto255 representative. - pub fn fromBytes(s: [encoded_length]u8) Error!Ristretto255 { + pub fn fromBytes(s: [encoded_length]u8) (NonCanonicalError || EncodingError)!Ristretto255 { try rejectNonCanonical(s); const s_ = Fe.fromBytes(s); const ss = s_.sq(); // s^2 @@ -154,7 +158,7 @@ pub const Ristretto255 = struct { /// Multiply a Ristretto255 element with a scalar. /// Return error.WeakPublicKey if the resulting element is /// the identity element. - pub fn mul(p: Ristretto255, s: [encoded_length]u8) callconv(.Inline) Error!Ristretto255 { + pub fn mul(p: Ristretto255, s: [encoded_length]u8) callconv(.Inline) (IdentityElementError || WeakPublicKeyError)!Ristretto255 { return Ristretto255{ .p = try p.p.mul(s) }; } diff --git a/lib/std/crypto/25519/scalar.zig b/lib/std/crypto/25519/scalar.zig index a4bf5aafcf..21578486e8 100644 --- a/lib/std/crypto/25519/scalar.zig +++ b/lib/std/crypto/25519/scalar.zig @@ -5,7 +5,8 @@ // and substantial portions of the software. const std = @import("std"); const mem = std.mem; -const Error = std.crypto.Error; + +const NonCanonicalError = std.crypto.errors.NonCanonicalError; /// 2^252 + 27742317777372353535851937790883648493 pub const field_size = [32]u8{ @@ -19,7 +20,7 @@ pub const CompressedScalar = [32]u8; pub const zero = [_]u8{0} ** 32; /// Reject a scalar whose encoding is not canonical. -pub fn rejectNonCanonical(s: [32]u8) Error!void { +pub fn rejectNonCanonical(s: [32]u8) NonCanonicalError!void { var c: u8 = 0; var n: u8 = 1; var i: usize = 31; diff --git a/lib/std/crypto/25519/x25519.zig b/lib/std/crypto/25519/x25519.zig index 2d53124056..07b1dc7a86 100644 --- a/lib/std/crypto/25519/x25519.zig +++ b/lib/std/crypto/25519/x25519.zig @@ -9,7 +9,10 @@ const mem = std.mem; const fmt = std.fmt; const Sha512 = crypto.hash.sha2.Sha512; -const Error = crypto.Error; + +const EncodingError = crypto.errors.EncodingError; +const IdentityElementError = crypto.errors.IdentityElementError; +const WeakPublicKeyError = crypto.errors.WeakPublicKeyError; /// X25519 DH function. pub const X25519 = struct { @@ -32,7 +35,7 @@ pub const X25519 = struct { secret_key: [secret_length]u8, /// Create a new key pair using an optional seed. - pub fn create(seed: ?[seed_length]u8) Error!KeyPair { + pub fn create(seed: ?[seed_length]u8) IdentityElementError!KeyPair { const sk = seed orelse sk: { var random_seed: [seed_length]u8 = undefined; crypto.random.bytes(&random_seed); @@ -45,7 +48,7 @@ pub const X25519 = struct { } /// Create a key pair from an Ed25519 key pair - pub fn fromEd25519(ed25519_key_pair: crypto.sign.Ed25519.KeyPair) Error!KeyPair { + pub fn fromEd25519(ed25519_key_pair: crypto.sign.Ed25519.KeyPair) (IdentityElementError || EncodingError)!KeyPair { const seed = ed25519_key_pair.secret_key[0..32]; var az: [Sha512.digest_length]u8 = undefined; Sha512.hash(seed, &az, .{}); @@ -60,13 +63,13 @@ pub const X25519 = struct { }; /// Compute the public key for a given private key. - pub fn recoverPublicKey(secret_key: [secret_length]u8) Error![public_length]u8 { + pub fn recoverPublicKey(secret_key: [secret_length]u8) IdentityElementError![public_length]u8 { const q = try Curve.basePoint.clampedMul(secret_key); return q.toBytes(); } /// Compute the X25519 equivalent to an Ed25519 public eky. - pub fn publicKeyFromEd25519(ed25519_public_key: [crypto.sign.Ed25519.public_length]u8) Error![public_length]u8 { + pub fn publicKeyFromEd25519(ed25519_public_key: [crypto.sign.Ed25519.public_length]u8) (IdentityElementError || EncodingError)![public_length]u8 { const pk_ed = try crypto.ecc.Edwards25519.fromBytes(ed25519_public_key); const pk = try Curve.fromEdwards25519(pk_ed); return pk.toBytes(); @@ -75,7 +78,7 @@ pub const X25519 = struct { /// Compute the scalar product of a public key and a secret scalar. /// Note that the output should not be used as a shared secret without /// hashing it first. - pub fn scalarmult(secret_key: [secret_length]u8, public_key: [public_length]u8) Error![shared_length]u8 { + pub fn scalarmult(secret_key: [secret_length]u8, public_key: [public_length]u8) IdentityElementError![shared_length]u8 { const q = try Curve.fromBytes(public_key).clampedMul(secret_key); return q.toBytes(); } diff --git a/lib/std/crypto/aegis.zig b/lib/std/crypto/aegis.zig index 3969d59e10..59dcf04dac 100644 --- a/lib/std/crypto/aegis.zig +++ b/lib/std/crypto/aegis.zig @@ -8,7 +8,7 @@ const std = @import("std"); const mem = std.mem; const assert = std.debug.assert; const AesBlock = std.crypto.core.aes.Block; -const Error = std.crypto.Error; +const AuthenticationError = std.crypto.errors.AuthenticationError; const State128L = struct { blocks: [8]AesBlock, @@ -137,7 +137,7 @@ pub const Aegis128L = struct { /// ad: Associated Data /// npub: public nonce /// k: private key - pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) Error!void { + pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) AuthenticationError!void { assert(c.len == m.len); var state = State128L.init(key, npub); var src: [32]u8 align(16) = undefined; @@ -299,7 +299,7 @@ pub const Aegis256 = struct { /// ad: Associated Data /// npub: public nonce /// k: private key - pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) Error!void { + pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) AuthenticationError!void { assert(c.len == m.len); var state = State256.init(key, npub); var src: [16]u8 align(16) = undefined; diff --git a/lib/std/crypto/aes_gcm.zig b/lib/std/crypto/aes_gcm.zig index bcb1b4c5fa..70746af073 100644 --- a/lib/std/crypto/aes_gcm.zig +++ b/lib/std/crypto/aes_gcm.zig @@ -12,7 +12,7 @@ const debug = std.debug; const Ghash = std.crypto.onetimeauth.Ghash; const mem = std.mem; const modes = crypto.core.modes; -const Error = crypto.Error; +const AuthenticationError = crypto.errors.AuthenticationError; pub const Aes128Gcm = AesGcm(crypto.core.aes.Aes128); pub const Aes256Gcm = AesGcm(crypto.core.aes.Aes256); @@ -60,7 +60,7 @@ fn AesGcm(comptime Aes: anytype) type { } } - pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) Error!void { + pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) AuthenticationError!void { assert(c.len == m.len); const aes = Aes.initEnc(key); diff --git a/lib/std/crypto/aes_ocb.zig b/lib/std/crypto/aes_ocb.zig index 9eb0561d9f..658b3b97ce 100644 --- a/lib/std/crypto/aes_ocb.zig +++ b/lib/std/crypto/aes_ocb.zig @@ -10,7 +10,7 @@ const aes = crypto.core.aes; const assert = std.debug.assert; const math = std.math; const mem = std.mem; -const Error = crypto.Error; +const AuthenticationError = crypto.errors.AuthenticationError; pub const Aes128Ocb = AesOcb(aes.Aes128); pub const Aes256Ocb = AesOcb(aes.Aes256); @@ -179,7 +179,7 @@ fn AesOcb(comptime Aes: anytype) type { /// ad: Associated Data /// npub: public nonce /// k: secret key - pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) Error!void { + pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) AuthenticationError!void { assert(c.len == m.len); const aes_enc_ctx = Aes.initEnc(key); diff --git a/lib/std/crypto/bcrypt.zig b/lib/std/crypto/bcrypt.zig index d00108b9c4..51fb144b2f 100644 --- a/lib/std/crypto/bcrypt.zig +++ b/lib/std/crypto/bcrypt.zig @@ -12,7 +12,8 @@ const mem = std.mem; const debug = std.debug; const testing = std.testing; const utils = crypto.utils; -const Error = crypto.Error; +const EncodingError = crypto.errors.EncodingError; +const PasswordVerificationError = crypto.errors.PasswordVerificationError; const salt_length: usize = 16; const salt_str_length: usize = 22; @@ -179,7 +180,7 @@ const Codec = struct { debug.assert(j == b64.len); } - fn decode(bin: []u8, b64: []const u8) Error!void { + fn decode(bin: []u8, b64: []const u8) EncodingError!void { var i: usize = 0; var j: usize = 0; while (j < bin.len) { @@ -204,7 +205,7 @@ const Codec = struct { } }; -fn strHashInternal(password: []const u8, rounds_log: u6, salt: [salt_length]u8) Error![hash_length]u8 { +fn strHashInternal(password: []const u8, rounds_log: u6, salt: [salt_length]u8) ![hash_length]u8 { var state = State{}; var password_buf: [73]u8 = undefined; const trimmed_len = math.min(password.len, password_buf.len - 1); @@ -252,14 +253,14 @@ fn strHashInternal(password: []const u8, rounds_log: u6, salt: [salt_length]u8) /// IMPORTANT: by design, bcrypt silently truncates passwords to 72 bytes. /// If this is an issue for your application, hash the password first using a function such as SHA-512, /// and then use the resulting hash as the password parameter for bcrypt. -pub fn strHash(password: []const u8, rounds_log: u6) Error![hash_length]u8 { +pub fn strHash(password: []const u8, rounds_log: u6) ![hash_length]u8 { var salt: [salt_length]u8 = undefined; crypto.random.bytes(&salt); return strHashInternal(password, rounds_log, salt); } /// Verify that a previously computed hash is valid for a given password. -pub fn strVerify(h: [hash_length]u8, password: []const u8) Error!void { +pub fn strVerify(h: [hash_length]u8, password: []const u8) (EncodingError || PasswordVerificationError)!void { if (!mem.eql(u8, "$2", h[0..2])) return error.InvalidEncoding; if (h[3] != '$' or h[6] != '$') return error.InvalidEncoding; const rounds_log_str = h[4..][0..2]; diff --git a/lib/std/crypto/chacha20.zig b/lib/std/crypto/chacha20.zig index e1fe3e232d..ea9eafd356 100644 --- a/lib/std/crypto/chacha20.zig +++ b/lib/std/crypto/chacha20.zig @@ -13,7 +13,7 @@ const testing = std.testing; const maxInt = math.maxInt; const Vector = std.meta.Vector; const Poly1305 = std.crypto.onetimeauth.Poly1305; -const Error = std.crypto.Error; +const AuthenticationError = std.crypto.errors.AuthenticationError; /// IETF-variant of the ChaCha20 stream cipher, as designed for TLS. pub const ChaCha20IETF = ChaChaIETF(20); @@ -521,7 +521,7 @@ fn ChaChaPoly1305(comptime rounds_nb: usize) type { /// npub: public nonce /// k: private key /// NOTE: the check of the authentication tag is currently not done in constant time - pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) Error!void { + pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) AuthenticationError!void { assert(c.len == m.len); var polyKey = [_]u8{0} ** 32; @@ -583,7 +583,7 @@ fn XChaChaPoly1305(comptime rounds_nb: usize) type { /// ad: Associated Data /// npub: public nonce /// k: private key - pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) Error!void { + pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) AuthenticationError!void { const extended = extend(k, npub, rounds_nb); return ChaChaPoly1305(rounds_nb).decrypt(m, c, tag, ad, extended.nonce, extended.key); } diff --git a/lib/std/crypto/error.zig b/lib/std/crypto/error.zig deleted file mode 100644 index 4cb12bb8f7..0000000000 --- a/lib/std/crypto/error.zig +++ /dev/null @@ -1,34 +0,0 @@ -pub const Error = error{ - /// MAC verification failed - The tag doesn't verify for the given ciphertext and secret key - AuthenticationFailed, - - /// The requested output length is too long for the chosen algorithm - OutputTooLong, - - /// Finite field operation returned the identity element - IdentityElement, - - /// Encoded input cannot be decoded - InvalidEncoding, - - /// The signature does't verify for the given message and public key - SignatureVerificationFailed, - - /// Both a public and secret key have been provided, but they are incompatible - KeyMismatch, - - /// Encoded input is not in canonical form - NonCanonical, - - /// Square root has no solutions - NotSquare, - - /// Verification string doesn't match the provided password and parameters - PasswordVerificationFailed, - - /// Parameters would be insecure to use - WeakParameters, - - /// Public key would be insecure to use - WeakPublicKey, -}; diff --git a/lib/std/crypto/errors.zig b/lib/std/crypto/errors.zig new file mode 100644 index 0000000000..4d79055919 --- /dev/null +++ b/lib/std/crypto/errors.zig @@ -0,0 +1,35 @@ +/// MAC verification failed - The tag doesn't verify for the given ciphertext and secret key +pub const AuthenticationError = error{AuthenticationFailed}; + +/// The requested output length is too long for the chosen algorithm +pub const OutputTooLongError = error{OutputTooLong}; + +/// Finite field operation returned the identity element +pub const IdentityElementError = error{IdentityElement}; + +/// Encoded input cannot be decoded +pub const EncodingError = error{InvalidEncoding}; + +/// The signature does't verify for the given message and public key +pub const SignatureVerificationError = error{SignatureVerificationFailed}; + +/// Both a public and secret key have been provided, but they are incompatible +pub const KeyMismatchError = error{KeyMismatch}; + +/// Encoded input is not in canonical form +pub const NonCanonicalError = error{NonCanonical}; + +/// Square root has no solutions +pub const NotSquareError = error{NotSquare}; + +/// Verification string doesn't match the provided password and parameters +pub const PasswordVerificationError = error{PasswordVerificationFailed}; + +/// Parameters would be insecure to use +pub const WeakParametersError = error{WeakParameters}; + +/// Public key would be insecure to use +pub const WeakPublicKeyError = error{WeakPublicKey}; + +/// Any error related to cryptography operations +pub const Error = AuthenticationError || OutputTooLongError || IdentityElementError || EncodingError || SignatureVerificationError || KeyMismatchError || NonCanonicalError || NotSquareError || PasswordVerificationError || WeakParametersError || WeakPublicKeyError; diff --git a/lib/std/crypto/gimli.zig b/lib/std/crypto/gimli.zig index 111e0c5274..fb67c25343 100644 --- a/lib/std/crypto/gimli.zig +++ b/lib/std/crypto/gimli.zig @@ -20,7 +20,7 @@ const assert = std.debug.assert; const testing = std.testing; const htest = @import("test.zig"); const Vector = std.meta.Vector; -const Error = std.crypto.Error; +const AuthenticationError = std.crypto.errors.AuthenticationError; pub const State = struct { pub const BLOCKBYTES = 48; @@ -393,7 +393,7 @@ pub const Aead = struct { /// npub: public nonce /// k: private key /// NOTE: the check of the authentication tag is currently not done in constant time - pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) Error!void { + pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) AuthenticationError!void { assert(c.len == m.len); var state = Aead.init(ad, npub, k); diff --git a/lib/std/crypto/isap.zig b/lib/std/crypto/isap.zig index 5219742d85..6deb4977bb 100644 --- a/lib/std/crypto/isap.zig +++ b/lib/std/crypto/isap.zig @@ -3,7 +3,7 @@ const debug = std.debug; const mem = std.mem; const math = std.math; const testing = std.testing; -const Error = std.crypto.Error; +const AuthenticationError = std.crypto.errors.AuthenticationError; /// ISAPv2 is an authenticated encryption system hardened against side channels and fault attacks. /// https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/round-2/spec-doc-rnd2/isap-spec-round2.pdf @@ -218,7 +218,7 @@ pub const IsapA128A = struct { tag.* = mac(c, ad, npub, key); } - pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) Error!void { + pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) AuthenticationError!void { var computed_tag = mac(c, ad, npub, key); var acc: u8 = 0; for (computed_tag) |_, j| { diff --git a/lib/std/crypto/pbkdf2.zig b/lib/std/crypto/pbkdf2.zig index 575fb83006..f93a5235af 100644 --- a/lib/std/crypto/pbkdf2.zig +++ b/lib/std/crypto/pbkdf2.zig @@ -7,7 +7,8 @@ const std = @import("std"); const mem = std.mem; const maxInt = std.math.maxInt; -const Error = std.crypto.Error; +const OutputTooLongError = std.crypto.errors.OutputTooLongError; +const WeakParametersError = std.crypto.errors.WeakParametersError; // RFC 2898 Section 5.2 // @@ -55,7 +56,7 @@ const Error = std.crypto.Error; /// the dk. It is common to tune this parameter to achieve approximately 100ms. /// /// Prf: Pseudo-random function to use. A common choice is `std.crypto.auth.hmac.HmacSha256`. -pub fn pbkdf2(dk: []u8, password: []const u8, salt: []const u8, rounds: u32, comptime Prf: type) Error!void { +pub fn pbkdf2(dk: []u8, password: []const u8, salt: []const u8, rounds: u32, comptime Prf: type) (WeakParametersError || OutputTooLongError)!void { if (rounds < 1) return error.WeakParameters; const dk_len = dk.len; diff --git a/lib/std/crypto/salsa20.zig b/lib/std/crypto/salsa20.zig index 006767c93f..2a06944adc 100644 --- a/lib/std/crypto/salsa20.zig +++ b/lib/std/crypto/salsa20.zig @@ -15,7 +15,10 @@ const Vector = std.meta.Vector; const Poly1305 = crypto.onetimeauth.Poly1305; const Blake2b = crypto.hash.blake2.Blake2b; const X25519 = crypto.dh.X25519; -const Error = crypto.Error; + +const AuthenticationError = crypto.errors.AuthenticationError; +const IdentityElementError = crypto.errors.IdentityElementError; +const WeakPublicKeyError = crypto.errors.WeakPublicKeyError; const Salsa20VecImpl = struct { const Lane = Vector(4, u32); @@ -399,7 +402,7 @@ pub const XSalsa20Poly1305 = struct { /// ad: Associated Data /// npub: public nonce /// k: private key - pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) Error!void { + pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) AuthenticationError!void { debug.assert(c.len == m.len); const extended = extend(k, npub); var block0 = [_]u8{0} ** 64; @@ -447,7 +450,7 @@ pub const SecretBox = struct { /// Verify and decrypt `c` using a nonce `npub` and a key `k`. /// `m` must be exactly `tag_length` smaller than `c`, as `c` includes an authentication tag in addition to the encrypted message. - pub fn open(m: []u8, c: []const u8, npub: [nonce_length]u8, k: [key_length]u8) Error!void { + pub fn open(m: []u8, c: []const u8, npub: [nonce_length]u8, k: [key_length]u8) AuthenticationError!void { if (c.len < tag_length) { return error.AuthenticationFailed; } @@ -482,20 +485,20 @@ pub const Box = struct { pub const KeyPair = X25519.KeyPair; /// Compute a secret suitable for `secretbox` given a recipent's public key and a sender's secret key. - pub fn createSharedSecret(public_key: [public_length]u8, secret_key: [secret_length]u8) Error![shared_length]u8 { + pub fn createSharedSecret(public_key: [public_length]u8, secret_key: [secret_length]u8) (IdentityElementError || WeakPublicKeyError)![shared_length]u8 { const p = try X25519.scalarmult(secret_key, public_key); const zero = [_]u8{0} ** 16; return Salsa20Impl.hsalsa20(zero, p); } /// Encrypt and authenticate a message using a recipient's public key `public_key` and a sender's `secret_key`. - pub fn seal(c: []u8, m: []const u8, npub: [nonce_length]u8, public_key: [public_length]u8, secret_key: [secret_length]u8) Error!void { + pub fn seal(c: []u8, m: []const u8, npub: [nonce_length]u8, public_key: [public_length]u8, secret_key: [secret_length]u8) (IdentityElementError || WeakPublicKeyError)!void { const shared_key = try createSharedSecret(public_key, secret_key); return SecretBox.seal(c, m, npub, shared_key); } /// Verify and decrypt a message using a recipient's secret key `public_key` and a sender's `public_key`. - pub fn open(m: []u8, c: []const u8, npub: [nonce_length]u8, public_key: [public_length]u8, secret_key: [secret_length]u8) Error!void { + pub fn open(m: []u8, c: []const u8, npub: [nonce_length]u8, public_key: [public_length]u8, secret_key: [secret_length]u8) (IdentityElementError || WeakPublicKeyError || AuthenticationError)!void { const shared_key = try createSharedSecret(public_key, secret_key); return SecretBox.open(m, c, npub, shared_key); } @@ -528,7 +531,7 @@ pub const SealedBox = struct { /// Encrypt a message `m` for a recipient whose public key is `public_key`. /// `c` must be `seal_length` bytes larger than `m`, so that the required metadata can be added. - pub fn seal(c: []u8, m: []const u8, public_key: [public_length]u8) Error!void { + pub fn seal(c: []u8, m: []const u8, public_key: [public_length]u8) (WeakPublicKeyError || IdentityElementError)!void { debug.assert(c.len == m.len + seal_length); var ekp = try KeyPair.create(null); const nonce = createNonce(ekp.public_key, public_key); @@ -539,7 +542,7 @@ pub const SealedBox = struct { /// Decrypt a message using a key pair. /// `m` must be exactly `seal_length` bytes smaller than `c`, as `c` also includes metadata. - pub fn open(m: []u8, c: []const u8, keypair: KeyPair) Error!void { + pub fn open(m: []u8, c: []const u8, keypair: KeyPair) (IdentityElementError || WeakPublicKeyError || AuthenticationError)!void { if (c.len < seal_length) { return error.AuthenticationFailed; } From 0c6dfa6f0c057a7517f889e360a985cb62a2d91f Mon Sep 17 00:00:00 2001 From: Michael Dusan Date: Tue, 20 Apr 2021 12:11:20 -0400 Subject: [PATCH 38/57] target: drop `ppc32` and prefer `ppc` - original PR #7949 (incorrectly) patched a generated-file and changes have subsequently been lost/overwritten - fix #7947 in a different way: drop `ppc32` because `ppc` already exists --- lib/std/target.zig | 4 ++-- lib/std/target/powerpc.zig | 7 ------- tools/update_cpu_features.zig | 6 ++++++ 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/lib/std/target.zig b/lib/std/target.zig index 3372f617a8..c741ea225f 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -1184,8 +1184,8 @@ pub const Target = struct { .mips, .mipsel => &mips.cpu.mips32, .mips64, .mips64el => &mips.cpu.mips64, .msp430 => &msp430.cpu.generic, - .powerpc => &powerpc.cpu.ppc32, - .powerpcle => &powerpc.cpu.ppc32, + .powerpc => &powerpc.cpu.ppc, + .powerpcle => &powerpc.cpu.ppc, .powerpc64 => &powerpc.cpu.ppc64, .powerpc64le => &powerpc.cpu.ppc64le, .amdgcn => &amdgpu.cpu.generic, diff --git a/lib/std/target/powerpc.zig b/lib/std/target/powerpc.zig index 4e2200a47f..db4d5dccdc 100644 --- a/lib/std/target/powerpc.zig +++ b/lib/std/target/powerpc.zig @@ -751,13 +751,6 @@ pub const cpu = struct { .hard_float, }), }; - pub const ppc32 = CpuModel{ - .name = "ppc32", - .llvm_name = "ppc32", - .features = featureSet(&[_]Feature{ - .hard_float, - }), - }; pub const ppc64 = CpuModel{ .name = "ppc64", .llvm_name = "ppc64", diff --git a/tools/update_cpu_features.zig b/tools/update_cpu_features.zig index 7e3c636c31..15143dfca4 100644 --- a/tools/update_cpu_features.zig +++ b/tools/update_cpu_features.zig @@ -663,6 +663,12 @@ const llvm_targets = [_]LlvmTarget{ .zig_name = "powerpc", .llvm_name = "PowerPC", .td_name = "PPC.td", + .feature_overrides = &.{ + .{ + .llvm_name = "ppc32", + .omit = true, + }, + }, }, .{ .zig_name = "riscv", From c8753aceef07161aa9b7d2eab75524042bcbc8bb Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 21 Apr 2021 06:49:34 +0200 Subject: [PATCH 39/57] macho: fix typos in consts defs --- lib/std/macho.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/macho.zig b/lib/std/macho.zig index f66626bafe..8f60d8fe94 100644 --- a/lib/std/macho.zig +++ b/lib/std/macho.zig @@ -1314,11 +1314,11 @@ pub const BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: u8 = 0x40; pub const BIND_OPCODE_SET_TYPE_IMM: u8 = 0x50; pub const BIND_OPCODE_SET_ADDEND_SLEB: u8 = 0x60; pub const BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: u8 = 0x70; -pub const BIND_OPCODE_ADD_ADDR_ULEB: 0x80; +pub const BIND_OPCODE_ADD_ADDR_ULEB: u8 = 0x80; pub const BIND_OPCODE_DO_BIND: u8 = 0x90; pub const BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: u8 = 0xa0; pub const BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: u8 = 0xb0; -pub const BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: u8 = xc0; +pub const BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: u8 = 0xc0; pub const reloc_type_x86_64 = packed enum(u4) { /// for absolute addresses From d82eb8dd404b5971d3469bdd1a5ed6907fbbb281 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Tue, 20 Apr 2021 22:04:48 +0200 Subject: [PATCH 40/57] std: Split syscall parameters for PowerPC targets --- lib/std/os/linux.zig | 1 + lib/std/target.zig | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index e7826f89bf..d8c7e99905 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -52,6 +52,7 @@ pub fn getauxval(index: usize) usize { // Some architectures (and some syscalls) require 64bit parameters to be passed // in a even-aligned register pair. const require_aligned_register_pair = + std.Target.current.cpu.arch.isPPC() or std.Target.current.cpu.arch.isMIPS() or std.Target.current.cpu.arch.isARM() or std.Target.current.cpu.arch.isThumb(); diff --git a/lib/std/target.zig b/lib/std/target.zig index c741ea225f..40a5d14f2a 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -800,6 +800,13 @@ pub const Target = struct { }; } + pub fn isPPC(arch: Arch) bool { + return switch (arch) { + .powerpc, .powerpcle => true, + else => false, + }; + } + pub fn isPPC64(arch: Arch) bool { return switch (arch) { .powerpc64, .powerpc64le => true, From e4f9a3041af7fc0c8fceb0bf15b14b51d54d3869 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Tue, 20 Apr 2021 22:05:23 +0200 Subject: [PATCH 41/57] std: Use stat definition with 32bit *time fields We're not ready for Y38K yet. --- lib/std/os/bits/linux/powerpc.zig | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/lib/std/os/bits/linux/powerpc.zig b/lib/std/os/bits/linux/powerpc.zig index baa6a57bcf..158a5dab3d 100644 --- a/lib/std/os/bits/linux/powerpc.zig +++ b/lib/std/os/bits/linux/powerpc.zig @@ -557,18 +557,10 @@ pub const kernel_stat = extern struct { size: off_t, blksize: blksize_t, blocks: blkcnt_t, - __atim32: timespec32, - __mtim32: timespec32, - __ctim32: timespec32, - __unused: [2]u32, atim: timespec, mtim: timespec, ctim: timespec, - - const timespec32 = extern struct { - tv_sec: i32, - tv_nsec: i32, - }; + __unused: [2]u32, pub fn atime(self: @This()) timespec { return self.atim; From 89d0cc4d8c222321939a87ede1cf7e4c6656d4cf Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Tue, 20 Apr 2021 22:08:19 +0200 Subject: [PATCH 42/57] test: Add 32bit PowerPC to the test plan Since MIPS is temporarily out of order due to some issues with LLD let's add PPC32 to have at least one big-endian platform in the testing plan. --- test/tests.zig | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/tests.zig b/test/tests.zig index be8d8d0a3f..7cdf83f2cd 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -212,6 +212,22 @@ const test_targets = blk: { // .link_libc = true, //}, + TestTarget{ + .target = .{ + .cpu_arch = .powerpc, + .os_tag = .linux, + .abi = .none, + }, + }, + TestTarget{ + .target = .{ + .cpu_arch = .powerpc, + .os_tag = .linux, + .abi = .musl, + }, + .link_libc = true, + }, + TestTarget{ .target = .{ .cpu_arch = .riscv64, From e0d7cfce5f5ae83e3eef5242d60dd2d4a1f9e5cc Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Wed, 21 Apr 2021 19:00:57 +0200 Subject: [PATCH 43/57] compiler-rt: Don't re-define PPC builtins in test mode Prevent linking errors when `zig test`-ing the compiler_rt module for powerpc-linux triples. --- lib/std/special/compiler_rt.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/special/compiler_rt.zig b/lib/std/special/compiler_rt.zig index 3a7457a4fd..5d95724a5f 100644 --- a/lib/std/special/compiler_rt.zig +++ b/lib/std/special/compiler_rt.zig @@ -296,7 +296,7 @@ comptime { @export(@import("compiler_rt/sparc.zig")._Qp_qtod, .{ .name = "_Qp_qtod", .linkage = linkage }); } - if (builtin.arch == .powerpc or builtin.arch.isPPC64()) { + if ((builtin.arch == .powerpc or builtin.arch.isPPC64()) and !is_test) { @export(@import("compiler_rt/addXf3.zig").__addtf3, .{ .name = "__addkf3", .linkage = linkage }); @export(@import("compiler_rt/addXf3.zig").__subtf3, .{ .name = "__subkf3", .linkage = linkage }); @export(@import("compiler_rt/mulXf3.zig").__multf3, .{ .name = "__mulkf3", .linkage = linkage }); From c5cc6b7082b132c6dd99df1c5bc8de817822bda4 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Wed, 21 Apr 2021 19:02:13 +0200 Subject: [PATCH 44/57] compiler-rt: Export __extendhftf2 and __trunctfhf2 Even though LLVM12 doesn't make use of those intrinsics at the moment let's export them anyway. --- lib/std/special/compiler_rt.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/std/special/compiler_rt.zig b/lib/std/special/compiler_rt.zig index 5d95724a5f..b0cf37553d 100644 --- a/lib/std/special/compiler_rt.zig +++ b/lib/std/special/compiler_rt.zig @@ -113,9 +113,11 @@ comptime { @export(@import("compiler_rt/extendXfYf2.zig").__extenddftf2, .{ .name = "__extenddftf2", .linkage = linkage }); @export(@import("compiler_rt/extendXfYf2.zig").__extendsftf2, .{ .name = "__extendsftf2", .linkage = linkage }); @export(@import("compiler_rt/extendXfYf2.zig").__extendhfsf2, .{ .name = "__extendhfsf2", .linkage = linkage }); + @export(@import("compiler_rt/extendXfYf2.zig").__extendhftf2, .{ .name = "__extendhftf2", .linkage = linkage }); @export(@import("compiler_rt/truncXfYf2.zig").__truncsfhf2, .{ .name = "__truncsfhf2", .linkage = linkage }); @export(@import("compiler_rt/truncXfYf2.zig").__truncdfhf2, .{ .name = "__truncdfhf2", .linkage = linkage }); + @export(@import("compiler_rt/truncXfYf2.zig").__trunctfhf2, .{ .name = "__trunctfhf2", .linkage = linkage }); @export(@import("compiler_rt/truncXfYf2.zig").__trunctfdf2, .{ .name = "__trunctfdf2", .linkage = linkage }); @export(@import("compiler_rt/truncXfYf2.zig").__trunctfsf2, .{ .name = "__trunctfsf2", .linkage = linkage }); From 6cd71931cb43c775864553b5a232709fbc771689 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Wed, 21 Apr 2021 18:43:22 +0200 Subject: [PATCH 45/57] stage1: Unbreak build on FreeBSD It turns out that the endianness-detection header delivered with the softfloat library is extremely brittle and gives wrong results when targeting FreeBSD (long story short, _BIG_ENDIAN is always defined there and that breaks the #if defined() chain). Use our own endianness detection header to work around any potential problem. --- src/stage1/softfloat_ext.cpp | 39 +++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/src/stage1/softfloat_ext.cpp b/src/stage1/softfloat_ext.cpp index 0eebe7365e..d0b8d1a5b3 100644 --- a/src/stage1/softfloat_ext.cpp +++ b/src/stage1/softfloat_ext.cpp @@ -1,19 +1,21 @@ #include "softfloat_ext.hpp" +#include "zigendian.h" extern "C" { - #include "platform.h" - #include "internals.h" #include "softfloat.h" } void f128M_abs(const float128_t *aPtr, float128_t *zPtr) { - float128_t zero_float; - ui32_to_f128M(0, &zero_float); - if (f128M_lt(aPtr, &zero_float)) { - f128M_sub(&zero_float, aPtr, zPtr); - } else { - *zPtr = *aPtr; - } + // Clear the sign bit. +#if ZIG_BYTE_ORDER == ZIG_LITTLE_ENDIAN + zPtr->v[1] = aPtr->v[1] & ~(UINT64_C(1) << 63); + zPtr->v[0] = aPtr->v[0]; +#elif ZIG_BYTE_ORDER == ZIG_BIG_ENDIAN + zPtr->v[0] = aPtr->v[0] & ~(UINT64_C(1) << 63); + zPtr->v[1] = aPtr->v[1]; +#else +#error Unsupported endian +#endif } void f128M_trunc(const float128_t *aPtr, float128_t *zPtr) { @@ -27,12 +29,21 @@ void f128M_trunc(const float128_t *aPtr, float128_t *zPtr) { } float16_t f16_neg(const float16_t a) { - union ui16_f16 uZ; - uZ.ui = a.v ^ (UINT16_C(1) << 15); - return uZ.f; + union { uint16_t ui; float16_t f; } uA; + // Toggle the sign bit. + uA.ui = a.v ^ (UINT16_C(1) << 15); + return uA.f; } void f128M_neg(const float128_t *aPtr, float128_t *zPtr) { - zPtr->v[indexWord(2,1)] = aPtr->v[indexWord(2,1)] ^ (UINT64_C(1) << 63); - zPtr->v[indexWord(2,0)] = aPtr->v[indexWord(2,0)]; + // Toggle the sign bit. +#if ZIG_BYTE_ORDER == ZIG_LITTLE_ENDIAN + zPtr->v[1] = aPtr->v[1] ^ (UINT64_C(1) << 63); + zPtr->v[0] = aPtr->v[0]; +#elif ZIG_BYTE_ORDER == ZIG_BIG_ENDIAN + zPtr->v[0] = aPtr->v[0] ^ (UINT64_C(1) << 63); + zPtr->v[1] = aPtr->v[1]; +#else +#error Unsupported endian +#endif } \ No newline at end of file From 41efdc73b9a57dfcc57474310c64724f58a95493 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 22 Apr 2021 16:24:14 -0700 Subject: [PATCH 46/57] langref: add docs for `@extern` --- doc/langref.html.in | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 63e4c946b6..41d8838892 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -7517,13 +7517,13 @@ test "main" { {#header_close#} {#header_open|@export#} -
{#syntax#}@export(target: anytype, comptime options: std.builtin.ExportOptions) void{#endsyntax#}
+
{#syntax#}@export(identifier, comptime options: std.builtin.ExportOptions) void{#endsyntax#}

Creates a symbol in the output object file.

This function can be called from a {#link|comptime#} block to conditionally export symbols. - When {#syntax#}target{#endsyntax#} is a function with the C calling convention and + When {#syntax#}identifier{#endsyntax#} is a function with the C calling convention and {#syntax#}options.linkage{#endsyntax#} is {#syntax#}Strong{#endsyntax#}, this is equivalent to the {#syntax#}export{#endsyntax#} keyword used on a function:

@@ -7550,6 +7550,14 @@ export fn @"A function name that is a complete sentence."() void {} {#see_also|Exporting a C Library#} {#header_close#} + {#header_open|@extern#} +
{#syntax#}@extern(T: type, comptime options: std.builtin.ExternOptions) *T{#endsyntax#}
+

+ Creates a reference to an external symbol in the output object file. +

+ {#see_also|@export#} + {#header_close#} + {#header_open|@fence#}
{#syntax#}@fence(order: AtomicOrder){#endsyntax#}

From cc8e49283d68075c4e842405d651d0d36b8d10af Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Fri, 23 Apr 2021 19:15:52 +0200 Subject: [PATCH 47/57] stage1: Allow wrapping negation on unsigned ints at comptime Closes #7951 Closes #8574 --- src/stage1/bigint.cpp | 4 ++-- src/stage1/bigint.hpp | 2 +- src/stage1/ir.cpp | 6 +++--- test/stage1/behavior/math.zig | 18 ++++++++++++++---- 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/stage1/bigint.cpp b/src/stage1/bigint.cpp index 79a05e95a5..acb3e18e41 100644 --- a/src/stage1/bigint.cpp +++ b/src/stage1/bigint.cpp @@ -1446,10 +1446,10 @@ void bigint_negate(BigInt *dest, const BigInt *op) { bigint_normalize(dest); } -void bigint_negate_wrap(BigInt *dest, const BigInt *op, size_t bit_count) { +void bigint_negate_wrap(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed) { BigInt zero; bigint_init_unsigned(&zero, 0); - bigint_sub_wrap(dest, &zero, op, bit_count, true); + bigint_sub_wrap(dest, &zero, op, bit_count, is_signed); } void bigint_not(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed) { diff --git a/src/stage1/bigint.hpp b/src/stage1/bigint.hpp index 044ea66423..aa37b9302a 100644 --- a/src/stage1/bigint.hpp +++ b/src/stage1/bigint.hpp @@ -75,7 +75,7 @@ void bigint_shl_trunc(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2); void bigint_negate(BigInt *dest, const BigInt *op); -void bigint_negate_wrap(BigInt *dest, const BigInt *op, size_t bit_count); +void bigint_negate_wrap(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed); void bigint_not(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed); void bigint_truncate(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed); diff --git a/src/stage1/ir.cpp b/src/stage1/ir.cpp index 942b6028d9..61a1345327 100644 --- a/src/stage1/ir.cpp +++ b/src/stage1/ir.cpp @@ -21660,8 +21660,8 @@ static ErrorMsg *ir_eval_negation_scalar(IrAnalyze *ira, IrInst* source_instr, Z { bool is_float = (scalar_type->id == ZigTypeIdFloat || scalar_type->id == ZigTypeIdComptimeFloat); - bool ok_type = ((scalar_type->id == ZigTypeIdInt && scalar_type->data.integral.is_signed) || - scalar_type->id == ZigTypeIdComptimeInt || (is_float && !is_wrap_op)); + bool ok_type = scalar_type->id == ZigTypeIdInt || scalar_type->id == ZigTypeIdComptimeInt || + (is_float && !is_wrap_op); if (!ok_type) { const char *fmt = is_wrap_op ? "invalid wrapping negation type: '%s'" : "invalid negation type: '%s'"; @@ -21672,7 +21672,7 @@ static ErrorMsg *ir_eval_negation_scalar(IrAnalyze *ira, IrInst* source_instr, Z float_negate(scalar_out_val, operand_val); } else if (is_wrap_op) { bigint_negate_wrap(&scalar_out_val->data.x_bigint, &operand_val->data.x_bigint, - scalar_type->data.integral.bit_count); + scalar_type->data.integral.bit_count, scalar_type->data.integral.is_signed); } else { bigint_negate(&scalar_out_val->data.x_bigint, &operand_val->data.x_bigint); } diff --git a/test/stage1/behavior/math.zig b/test/stage1/behavior/math.zig index 3835aab0f4..32f4842702 100644 --- a/test/stage1/behavior/math.zig +++ b/test/stage1/behavior/math.zig @@ -229,16 +229,26 @@ fn testSignedWrappingEval(x: i32) void { expect(max_val == maxInt(i32)); } -test "negation wrapping" { - testNegationWrappingEval(minInt(i16)); - comptime testNegationWrappingEval(minInt(i16)); +test "signed negation wrapping" { + testSignedNegationWrappingEval(minInt(i16)); + comptime testSignedNegationWrappingEval(minInt(i16)); } -fn testNegationWrappingEval(x: i16) void { +fn testSignedNegationWrappingEval(x: i16) void { expect(x == -32768); const neg = -%x; expect(neg == -32768); } +test "unsigned negation wrapping" { + testUnsignedNegationWrappingEval(1); + comptime testUnsignedNegationWrappingEval(1); +} +fn testUnsignedNegationWrappingEval(x: u16) void { + expect(x == 1); + const neg = -%x; + expect(neg == maxInt(u16)); +} + test "unsigned 64-bit division" { test_u64_div(); comptime test_u64_div(); From 9ac5f9820086f6760df321b0a1ffc60ec92c8ad8 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Fri, 23 Apr 2021 19:19:44 +0200 Subject: [PATCH 48/57] std: Remove unneeded workaround Now that -% works on unsigned integers at comptime this (clever) hack is not not needed anymore. --- lib/std/math.zig | 9 --------- 1 file changed, 9 deletions(-) diff --git a/lib/std/math.zig b/lib/std/math.zig index d71cafe5ef..558f541c00 100644 --- a/lib/std/math.zig +++ b/lib/std/math.zig @@ -1349,15 +1349,6 @@ pub fn boolMask(comptime MaskInt: type, value: bool) callconv(.Inline) MaskInt { return @bitCast(i1, @as(u1, @boolToInt(value))); } - // At comptime, -% is disallowed on unsigned values. - // So we need to jump through some hoops in that case. - // This is a workaround for #7951 - if (@typeInfo(@TypeOf(.{value})).Struct.fields[0].is_comptime) { - // Since it's comptime, we don't need this to generate nice code. - // We can just do a branch here. - return if (value) ~@as(MaskInt, 0) else 0; - } - return -%@intCast(MaskInt, @boolToInt(value)); } From e86a1df9f23386cc315ac8055ecfe7a6465c5ee3 Mon Sep 17 00:00:00 2001 From: Hadron67 <604700341@qq.com> Date: Sun, 18 Apr 2021 15:47:27 +0800 Subject: [PATCH 49/57] std.atomic: load should take const pointer to Self --- lib/std/atomic/bool.zig | 2 +- lib/std/atomic/int.zig | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/std/atomic/bool.zig b/lib/std/atomic/bool.zig index c968b862b9..0cffb99d38 100644 --- a/lib/std/atomic/bool.zig +++ b/lib/std/atomic/bool.zig @@ -28,7 +28,7 @@ pub const Bool = extern struct { return @atomicRmw(bool, &self.unprotected_value, .Xchg, operand, ordering); } - pub fn load(self: *Self, comptime ordering: std.builtin.AtomicOrder) bool { + pub fn load(self: *const Self, comptime ordering: std.builtin.AtomicOrder) bool { switch (ordering) { .Unordered, .Monotonic, .Acquire, .SeqCst => {}, else => @compileError("Invalid ordering '" ++ @tagName(ordering) ++ "' for a load operation"), diff --git a/lib/std/atomic/int.zig b/lib/std/atomic/int.zig index 1a3bead2df..2d1c5f80e9 100644 --- a/lib/std/atomic/int.zig +++ b/lib/std/atomic/int.zig @@ -31,7 +31,7 @@ pub fn Int(comptime T: type) type { return @atomicRmw(T, &self.unprotected_value, op, operand, ordering); } - pub fn load(self: *Self, comptime ordering: builtin.AtomicOrder) T { + pub fn load(self: *const Self, comptime ordering: builtin.AtomicOrder) T { switch (ordering) { .Unordered, .Monotonic, .Acquire, .SeqCst => {}, else => @compileError("Invalid ordering '" ++ @tagName(ordering) ++ "' for a load operation"), @@ -59,7 +59,7 @@ pub fn Int(comptime T: type) type { return self.rmw(.Sub, 1, .SeqCst); } - pub fn get(self: *Self) T { + pub fn get(self: *const Self) T { return self.load(.SeqCst); } From 4f7765de7c0203ceed3a5acbe0d4c07b678557b5 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 21 Apr 2021 06:48:12 +0200 Subject: [PATCH 50/57] zld: common section should be writable --- src/link/MachO/Zld.zig | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index 6831251966..bc28911757 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -64,7 +64,6 @@ got_section_index: ?u16 = null, mod_init_func_section_index: ?u16 = null, mod_term_func_section_index: ?u16 = null, data_const_section_index: ?u16 = null, -common_section_index: ?u16 = null, // __DATA segment sections tlv_section_index: ?u16 = null, @@ -73,6 +72,7 @@ tlv_bss_section_index: ?u16 = null, la_symbol_ptr_section_index: ?u16 = null, data_section_index: ?u16 = null, bss_section_index: ?u16 = null, +common_section_index: ?u16 = null, symtab: std.StringArrayHashMapUnmanaged(Symbol) = .{}, strtab: std.ArrayListUnmanaged(u8) = .{}, @@ -487,10 +487,10 @@ fn updateMetadata(self: *Zld) !void { if (mem.eql(u8, sectname, "__common")) { if (self.common_section_index != null) continue; - self.common_section_index = @intCast(u16, data_const_seg.sections.items.len); - try data_const_seg.addSection(self.allocator, .{ + self.common_section_index = @intCast(u16, data_seg.sections.items.len); + try data_seg.addSection(self.allocator, .{ .sectname = makeStaticString("__common"), - .segname = makeStaticString("__DATA_CONST"), + .segname = makeStaticString("__DATA"), .addr = 0, .size = 0, .offset = 0, @@ -656,7 +656,7 @@ fn getMatchingSection(self: *Zld, section: macho.section_64) ?MatchingSection { macho.S_ZEROFILL => { if (mem.eql(u8, sectname, "__common")) { break :blk .{ - .seg = self.data_const_segment_cmd_index.?, + .seg = self.data_segment_cmd_index.?, .sect = self.common_section_index.?, }; } @@ -764,7 +764,6 @@ fn sortSections(self: *Zld) !void { &self.mod_init_func_section_index, &self.mod_term_func_section_index, &self.data_const_section_index, - &self.common_section_index, }; for (indices) |maybe_index| { const new_index: u16 = if (maybe_index.*) |index| blk: { @@ -787,11 +786,12 @@ fn sortSections(self: *Zld) !void { // __DATA segment const indices = &[_]*?u16{ &self.la_symbol_ptr_section_index, - &self.tlv_section_index, &self.data_section_index, + &self.tlv_section_index, &self.tlv_data_section_index, &self.tlv_bss_section_index, &self.bss_section_index, + &self.common_section_index, }; for (indices) |maybe_index| { const new_index: u16 = if (maybe_index.*) |index| blk: { @@ -2153,7 +2153,7 @@ fn populateMetadata(self: *Zld) !void { fn flush(self: *Zld) !void { if (self.common_section_index) |index| { - const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; + const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; const sect = &seg.sections.items[index]; sect.offset = 0; } From b03983b4501031e6faed47bf886586b0ad2150aa Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 21 Apr 2021 17:53:07 +0200 Subject: [PATCH 51/57] zld: analyze static initializers --- src/link/MachO/Object.zig | 24 ++++++++++++++++++++---- src/link/MachO/Zld.zig | 2 +- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 7b5f23756a..8ccd320274 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -14,6 +14,7 @@ const Allocator = mem.Allocator; const Relocation = reloc.Relocation; const Symbol = @import("Symbol.zig"); const parseName = @import("Zld.zig").parseName; +const CppStatic = @import("Zld.zig").CppStatic; usingnamespace @import("commands.zig"); @@ -216,6 +217,7 @@ pub fn parse(self: *Object) !void { try self.parseSections(); if (self.symtab_cmd_index != null) try self.parseSymtab(); if (self.data_in_code_cmd_index != null) try self.readDataInCode(); + try self.parseInitializers(); try self.parseDebugInfo(); } @@ -298,28 +300,42 @@ pub fn parseSections(self: *Object) !void { var section = Section{ .inner = sect, .code = code, - .relocs = undefined, + .relocs = null, }; // Parse relocations - section.relocs = if (sect.nreloc > 0) relocs: { + if (sect.nreloc > 0) { var raw_relocs = try self.allocator.alloc(u8, @sizeOf(macho.relocation_info) * sect.nreloc); defer self.allocator.free(raw_relocs); _ = try self.file.?.preadAll(raw_relocs, sect.reloff); - break :relocs try reloc.parse( + section.relocs = try reloc.parse( self.allocator, self.arch.?, section.code, mem.bytesAsSlice(macho.relocation_info, raw_relocs), ); - } else null; + } self.sections.appendAssumeCapacity(section); } } +pub fn parseInitializers(self: *Object) !void { + for (self.sections.items) |section| { + if (section.inner.flags != macho.S_MOD_INIT_FUNC_POINTERS) continue; + log.warn("parsing initializers in {s}", .{self.name.?}); + // Parse C++ initializers + const relocs = section.relocs orelse unreachable; + for (relocs) |rel| { + const sym = self.symtab.items[rel.target.symbol]; + const sym_name = self.getString(sym.n_strx); + log.warn(" | {s}", .{sym_name}); + } + } +} + pub fn parseSymtab(self: *Object) !void { const symtab_cmd = self.load_commands.items[self.symtab_cmd_index.?].Symtab; diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index bc28911757..ff297c79e5 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -90,7 +90,7 @@ stub_helper_stubs_start_off: ?u64 = null, mappings: std.AutoHashMapUnmanaged(MappingKey, SectionMapping) = .{}, unhandled_sections: std.AutoHashMapUnmanaged(MappingKey, u0) = .{}, -const CppStatic = struct { +pub const CppStatic = struct { index: u32, target_addr: u64, file: u16, From b00d08b667ab8f0face2d5dc8ddc7b0b6f13a763 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 22 Apr 2021 10:51:35 +0200 Subject: [PATCH 52/57] zld: add temp debug info fix by setting mtime=0 --- src/link/MachO/Archive.zig | 5 ++--- src/link/MachO/Zld.zig | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/link/MachO/Archive.zig b/src/link/MachO/Archive.zig index 86e160ba4d..5a0b9609ad 100644 --- a/src/link/MachO/Archive.zig +++ b/src/link/MachO/Archive.zig @@ -208,14 +208,13 @@ pub fn parseObject(self: Archive, offset: u32) !Object { const object_name = try parseName(self.allocator, object_header, reader); defer self.allocator.free(object_name); - const object_basename = std.fs.path.basename(object_name); - log.debug("extracting object '{s}' from archive '{s}'", .{ object_basename, self.name.? }); + log.debug("extracting object '{s}' from archive '{s}'", .{ object_name, self.name.? }); const name = name: { var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; const path = try std.os.realpath(self.name.?, &buffer); - break :name try std.fmt.allocPrint(self.allocator, "{s}({s})", .{ path, object_basename }); + break :name try std.fmt.allocPrint(self.allocator, "{s}({s})", .{ path, object_name }); }; var object = Object.init(self.allocator); diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index ff297c79e5..02344d36d4 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -2627,7 +2627,7 @@ fn writeDebugInfo(self: *Zld) !void { .n_type = macho.N_OSO, .n_sect = 0, .n_desc = 1, - .n_value = tu_mtime, + .n_value = 0, //tu_mtime, TODO figure out why precalculated mtime value doesn't work }); for (object.stabs.items) |stab| { From 4ec6d174adc038741f1274d157f5253752eb8d51 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 23 Apr 2021 10:33:32 +0200 Subject: [PATCH 53/57] zld: new approach at handling static inits --- src/link/MachO/Object.zig | 45 ++++++++--- src/link/MachO/Zld.zig | 154 ++++++++------------------------------ 2 files changed, 66 insertions(+), 133 deletions(-) diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 8ccd320274..6703a5bfb7 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -14,7 +14,6 @@ const Allocator = mem.Allocator; const Relocation = reloc.Relocation; const Symbol = @import("Symbol.zig"); const parseName = @import("Zld.zig").parseName; -const CppStatic = @import("Zld.zig").CppStatic; usingnamespace @import("commands.zig"); @@ -33,7 +32,9 @@ symtab_cmd_index: ?u16 = null, dysymtab_cmd_index: ?u16 = null, build_version_cmd_index: ?u16 = null, data_in_code_cmd_index: ?u16 = null, + text_section_index: ?u16 = null, +mod_init_func_section_index: ?u16 = null, // __DWARF segment sections dwarf_debug_info_index: ?u16 = null, @@ -50,6 +51,7 @@ stabs: std.ArrayListUnmanaged(Stab) = .{}, tu_path: ?[]const u8 = null, tu_mtime: ?u64 = null, +initializers: std.ArrayListUnmanaged(CppStatic) = .{}, data_in_code_entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{}, pub const Section = struct { @@ -69,6 +71,11 @@ pub const Section = struct { } }; +const CppStatic = struct { + symbol: u32, + target_addr: u64, +}; + const Stab = struct { tag: Tag, symbol: u32, @@ -171,6 +178,7 @@ pub fn deinit(self: *Object) void { self.strtab.deinit(self.allocator); self.stabs.deinit(self.allocator); self.data_in_code_entries.deinit(self.allocator); + self.initializers.deinit(self.allocator); if (self.name) |n| { self.allocator.free(n); @@ -252,6 +260,10 @@ pub fn readLoadCommands(self: *Object, reader: anytype) !void { if (mem.eql(u8, sectname, "__text")) { self.text_section_index = index; } + } else if (mem.eql(u8, segname, "__DATA")) { + if (mem.eql(u8, sectname, "__mod_init_func")) { + self.mod_init_func_section_index = index; + } } sect.offset += offset; @@ -323,16 +335,27 @@ pub fn parseSections(self: *Object) !void { } pub fn parseInitializers(self: *Object) !void { - for (self.sections.items) |section| { - if (section.inner.flags != macho.S_MOD_INIT_FUNC_POINTERS) continue; - log.warn("parsing initializers in {s}", .{self.name.?}); - // Parse C++ initializers - const relocs = section.relocs orelse unreachable; - for (relocs) |rel| { - const sym = self.symtab.items[rel.target.symbol]; - const sym_name = self.getString(sym.n_strx); - log.warn(" | {s}", .{sym_name}); - } + const index = self.mod_init_func_section_index orelse return; + const section = self.sections.items[index]; + + log.debug("parsing initializers in {s}", .{self.name.?}); + + // Parse C++ initializers + const relocs = section.relocs orelse unreachable; + try self.initializers.ensureCapacity(self.allocator, relocs.len); + for (relocs) |rel| { + self.initializers.appendAssumeCapacity(.{ + .symbol = rel.target.symbol, + .target_addr = undefined, + }); + } + + mem.reverse(CppStatic, self.initializers.items); + + for (self.initializers.items) |initializer| { + const sym = self.symtab.items[initializer.symbol]; + const sym_name = self.getString(sym.n_strx); + log.debug(" | {s}", .{sym_name}); } } diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index 02344d36d4..a585b1fd1e 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -82,20 +82,12 @@ threadlocal_offsets: std.ArrayListUnmanaged(u64) = .{}, local_rebases: std.ArrayListUnmanaged(Pointer) = .{}, stubs: std.StringArrayHashMapUnmanaged(u32) = .{}, got_entries: std.StringArrayHashMapUnmanaged(GotEntry) = .{}, -cpp_initializers: std.StringArrayHashMapUnmanaged(CppStatic) = .{}, -cpp_finalizers: std.StringArrayHashMapUnmanaged(CppStatic) = .{}, stub_helper_stubs_start_off: ?u64 = null, mappings: std.AutoHashMapUnmanaged(MappingKey, SectionMapping) = .{}, unhandled_sections: std.AutoHashMapUnmanaged(MappingKey, u0) = .{}, -pub const CppStatic = struct { - index: u32, - target_addr: u64, - file: u16, -}; - const GotEntry = struct { tag: enum { local, @@ -143,16 +135,6 @@ pub fn deinit(self: *Zld) void { } self.got_entries.deinit(self.allocator); - for (self.cpp_initializers.items()) |entry| { - self.allocator.free(entry.key); - } - self.cpp_initializers.deinit(self.allocator); - - for (self.cpp_finalizers.items()) |entry| { - self.allocator.free(entry.key); - } - self.cpp_finalizers.deinit(self.allocator); - for (self.load_commands.items) |*lc| { lc.deinit(self.allocator); } @@ -243,6 +225,7 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void { self.allocateLinkeditSegment(); try self.allocateSymbols(); try self.allocateStubsAndGotEntries(); + try self.allocateCppStatics(); try self.writeStubHelperCommon(); try self.resolveRelocsAndWriteSections(); try self.flush(); @@ -1007,37 +990,20 @@ fn allocateStubsAndGotEntries(self: *Zld) !void { entry.value.target_addr, }); } +} - for (self.cpp_initializers.items()) |*entry| { - const object = self.objects.items[entry.value.file]; - entry.value.target_addr = target_addr: { - if (object.locals.get(entry.key)) |local| { - break :target_addr local.address; - } - const global = self.symtab.get(entry.key) orelse unreachable; - break :target_addr global.address; - }; +fn allocateCppStatics(self: *Zld) !void { + for (self.objects.items) |*object| { + for (object.initializers.items) |*initializer| { + const sym = object.symtab.items[initializer.symbol]; + const sym_name = object.getString(sym.n_strx); + initializer.target_addr = object.locals.get(sym_name).?.address; - log.debug("resolving C++ initializer '{s}' at 0x{x}", .{ - entry.key, - entry.value.target_addr, - }); - } - - for (self.cpp_finalizers.items()) |*entry| { - const object = self.objects.items[entry.value.file]; - entry.value.target_addr = target_addr: { - if (object.locals.get(entry.key)) |local| { - break :target_addr local.address; - } - const global = self.symtab.get(entry.key) orelse unreachable; - break :target_addr global.address; - }; - - log.debug("resolving C++ finalizer '{s}' at 0x{x}", .{ - entry.key, - entry.value.target_addr, - }); + log.debug("resolving C++ initializer '{s}' at 0x{x}", .{ + sym_name, + initializer.target_addr, + }); + } } } @@ -1453,34 +1419,7 @@ fn resolveStubsAndGotEntries(self: *Zld) !void { const relocs = sect.relocs orelse continue; for (relocs) |rel| { switch (rel.@"type") { - .unsigned => { - if (rel.target != .symbol) continue; - - const sym = object.symtab.items[rel.target.symbol]; - const sym_name = object.getString(sym.n_strx); - - if (sect.inner.flags == macho.S_MOD_INIT_FUNC_POINTERS) { - if (self.cpp_initializers.contains(sym_name)) continue; - - var name = try self.allocator.dupe(u8, sym_name); - const index = @intCast(u32, self.cpp_initializers.items().len); - try self.cpp_initializers.putNoClobber(self.allocator, name, .{ - .index = index, - .target_addr = 0, - .file = @intCast(u16, object_id), - }); - } else if (sect.inner.flags == macho.S_MOD_TERM_FUNC_POINTERS) { - if (self.cpp_finalizers.contains(sym_name)) continue; - - var name = try self.allocator.dupe(u8, sym_name); - const index = @intCast(u32, self.cpp_finalizers.items().len); - try self.cpp_finalizers.putNoClobber(self.allocator, name, .{ - .index = index, - .target_addr = 0, - .file = @intCast(u16, object_id), - }); - } else continue; - }, + .unsigned => continue, .got_page, .got_page_off, .got_load, .got => { const sym = object.symtab.items[rel.target.symbol]; const sym_name = object.getString(sym.n_strx); @@ -2194,36 +2133,18 @@ fn flush(self: *Zld) !void { const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; const sect = &seg.sections.items[index]; - var buffer = try self.allocator.alloc(u8, self.cpp_initializers.items().len * @sizeOf(u64)); - defer self.allocator.free(buffer); + var initializers = std.ArrayList(u64).init(self.allocator); + defer initializers.deinit(); - var stream = std.io.fixedBufferStream(buffer); - var writer = stream.writer(); - - for (self.cpp_initializers.items()) |entry| { - try writer.writeIntLittle(u64, entry.value.target_addr); + // TODO sort the initializers globally + for (self.objects.items) |object| { + for (object.initializers.items) |initializer| { + try initializers.append(initializer.target_addr); + } } - _ = try self.file.?.pwriteAll(buffer, sect.offset); - sect.size = @intCast(u32, buffer.len); - } - - if (self.mod_term_func_section_index) |index| { - const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; - const sect = &seg.sections.items[index]; - - var buffer = try self.allocator.alloc(u8, self.cpp_finalizers.items().len * @sizeOf(u64)); - defer self.allocator.free(buffer); - - var stream = std.io.fixedBufferStream(buffer); - var writer = stream.writer(); - - for (self.cpp_finalizers.items()) |entry| { - try writer.writeIntLittle(u64, entry.value.target_addr); - } - - _ = try self.file.?.pwriteAll(buffer, sect.offset); - sect.size = @intCast(u32, buffer.len); + _ = try self.file.?.pwriteAll(mem.sliceAsBytes(initializers.items), sect.offset); + sect.size = @intCast(u32, initializers.items.len * @sizeOf(u64)); } try self.writeGotEntries(); @@ -2328,26 +2249,15 @@ fn writeRebaseInfoTable(self: *Zld) !void { const base_offset = sect.addr - seg.inner.vmaddr; const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?); - for (self.cpp_initializers.items()) |entry| { - try pointers.append(.{ - .offset = base_offset + entry.value.index * @sizeOf(u64), - .segment_id = segment_id, - }); - } - } - - if (self.mod_term_func_section_index) |idx| { - // TODO audit and investigate this. - const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; - const sect = seg.sections.items[idx]; - const base_offset = sect.addr - seg.inner.vmaddr; - const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?); - - for (self.cpp_finalizers.items()) |entry| { - try pointers.append(.{ - .offset = base_offset + entry.value.index * @sizeOf(u64), - .segment_id = segment_id, - }); + var index: u64 = 0; + for (self.objects.items) |object| { + for (object.initializers.items) |_| { + try pointers.append(.{ + .offset = base_offset + index * @sizeOf(u64), + .segment_id = segment_id, + }); + index += 1; + } } } From 0aede1a8fcd59b259215c05b43827405ad5da9c7 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sat, 24 Apr 2021 10:20:16 +0200 Subject: [PATCH 54/57] stage1: Require a block after suspend Closes #8603 --- doc/langref.html.in | 10 ++-- lib/std/event/rwlock.zig | 4 +- lib/std/zig/parse.zig | 11 ++-- lib/std/zig/parser_test.zig | 18 +++++-- lib/std/zig/render.zig | 13 +---- src/stage1/ir.cpp | 16 +++--- src/stage1/parser.cpp | 5 +- test/compile_errors.zig | 14 +++--- test/runtime_safety.zig | 34 ++++++------- test/stage1/behavior/async_fn.zig | 84 +++++++++++++++---------------- 10 files changed, 100 insertions(+), 109 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 41d8838892..f52abeab73 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -6528,7 +6528,7 @@ test "suspend with no resume" { fn func() void { x += 1; - suspend; + suspend {} // This line is never reached because the suspend has no matching resume. x += 1; } @@ -6593,7 +6593,7 @@ fn testResumeFromSuspend(my_result: *i32) void { resume @frame(); } my_result.* += 1; - suspend; + suspend {} my_result.* += 1; } {#code_end#} @@ -6632,7 +6632,7 @@ fn amain() void { } fn func() void { - suspend; + suspend {} } {#code_end#}

@@ -6934,7 +6934,7 @@ test "async fn pointer in a struct field" { fn func(y: *i32) void { defer y.* += 2; y.* += 1; - suspend; + suspend {} } {#code_end#} {#header_close#} @@ -7667,7 +7667,7 @@ test "heap allocated frame" { } fn func() void { - suspend; + suspend {} } {#code_end#} {#header_close#} diff --git a/lib/std/event/rwlock.zig b/lib/std/event/rwlock.zig index 750131beda..04b45ee308 100644 --- a/lib/std/event/rwlock.zig +++ b/lib/std/event/rwlock.zig @@ -264,7 +264,7 @@ var shared_test_data = [1]i32{0} ** 10; var shared_test_index: usize = 0; var shared_count: usize = 0; fn writeRunner(lock: *RwLock) callconv(.Async) void { - suspend; // resumed by onNextTick + suspend {} // resumed by onNextTick var i: usize = 0; while (i < shared_test_data.len) : (i += 1) { @@ -281,7 +281,7 @@ fn writeRunner(lock: *RwLock) callconv(.Async) void { } } fn readRunner(lock: *RwLock) callconv(.Async) void { - suspend; // resumed by onNextTick + suspend {} // resumed by onNextTick std.time.sleep(1); var i: usize = 0; diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index 029d8ede50..778075bdee 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -852,7 +852,7 @@ const Parser = struct { /// <- KEYWORD_comptime? VarDecl /// / KEYWORD_comptime BlockExprStatement /// / KEYWORD_nosuspend BlockExprStatement - /// / KEYWORD_suspend (SEMICOLON / BlockExprStatement) + /// / KEYWORD_suspend BlockExprStatement /// / KEYWORD_defer BlockExprStatement /// / KEYWORD_errdefer Payload? BlockExprStatement /// / IfStatement @@ -891,16 +891,11 @@ const Parser = struct { }); }, .keyword_suspend => { - const token = p.nextToken(); - const block_expr: Node.Index = if (p.eatToken(.semicolon) != null) - 0 - else - try p.expectBlockExprStatement(); return p.addNode(.{ .tag = .@"suspend", - .main_token = token, + .main_token = p.nextToken(), .data = .{ - .lhs = block_expr, + .lhs = try p.expectBlockExprStatement(), .rhs = undefined, }, }); diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index c433ee5ad8..479da2b50c 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -3665,9 +3665,9 @@ test "zig fmt: async functions" { \\fn simpleAsyncFn() void { \\ const a = async a.b(); \\ x += 1; - \\ suspend; + \\ suspend {} \\ x += 1; - \\ suspend; + \\ suspend {} \\ const p: anyframe->void = async simpleAsyncFn() catch unreachable; \\ await p; \\} @@ -5022,6 +5022,18 @@ test "recovery: invalid comptime" { }); } +test "recovery: missing block after suspend" { + try testError( + \\fn foo() void { + \\ suspend; + \\ nosuspend; + \\} + , &[_]Error{ + .expected_block_or_expr, + .expected_block_or_expr, + }); +} + test "recovery: missing block after for/while loops" { try testError( \\test "" { while (foo) } @@ -5165,7 +5177,7 @@ fn testError(source: []const u8, expected_errors: []const Error) !void { var tree = try std.zig.parse(std.testing.allocator, source); defer tree.deinit(std.testing.allocator); - std.testing.expect(tree.errors.len == expected_errors.len); + std.testing.expectEqual(expected_errors.len, tree.errors.len); for (expected_errors) |expected, i| { std.testing.expectEqual(expected, tree.errors[i].tag); } diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index 9291f63e2f..a417947c6b 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -255,24 +255,13 @@ fn renderExpression(gpa: *Allocator, ais: *Ais, tree: ast.Tree, node: ast.Node.I try renderToken(ais, tree, defer_token, .space); return renderExpression(gpa, ais, tree, expr, space); }, - .@"comptime", .@"nosuspend" => { + .@"comptime", .@"suspend", .@"nosuspend" => { const comptime_token = main_tokens[node]; const block = datas[node].lhs; try renderToken(ais, tree, comptime_token, .space); return renderExpression(gpa, ais, tree, block, space); }, - .@"suspend" => { - const suspend_token = main_tokens[node]; - const body = datas[node].lhs; - if (body != 0) { - try renderToken(ais, tree, suspend_token, .space); - return renderExpression(gpa, ais, tree, body, space); - } else { - return renderToken(ais, tree, suspend_token, space); - } - }, - .@"catch" => { const main_token = main_tokens[node]; const fallback_first = tree.firstToken(datas[node].rhs); diff --git a/src/stage1/ir.cpp b/src/stage1/ir.cpp index 61a1345327..c59f63399c 100644 --- a/src/stage1/ir.cpp +++ b/src/stage1/ir.cpp @@ -9534,7 +9534,7 @@ static IrInstSrc *ir_gen_nosuspend(IrBuilderSrc *irb, Scope *parent_scope, AstNo Scope *child_scope = create_nosuspend_scope(irb->codegen, node, parent_scope); // purposefully pass null for result_loc and let EndExpr handle it - return ir_gen_node_extra(irb, node->data.comptime_expr.expr, child_scope, lval, nullptr); + return ir_gen_node_extra(irb, node->data.nosuspend_expr.expr, child_scope, lval, nullptr); } static IrInstSrc *ir_gen_return_from_block(IrBuilderSrc *irb, Scope *break_scope, AstNode *node, ScopeBlock *block_scope) { @@ -10199,14 +10199,12 @@ static IrInstSrc *ir_gen_suspend(IrBuilderSrc *irb, Scope *parent_scope, AstNode } IrInstSrcSuspendBegin *begin = ir_build_suspend_begin_src(irb, parent_scope, node); - if (node->data.suspend.block != nullptr) { - ScopeSuspend *suspend_scope = create_suspend_scope(irb->codegen, node, parent_scope); - Scope *child_scope = &suspend_scope->base; - IrInstSrc *susp_res = ir_gen_node(irb, node->data.suspend.block, child_scope); - if (susp_res == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.suspend.block, susp_res)); - } + ScopeSuspend *suspend_scope = create_suspend_scope(irb->codegen, node, parent_scope); + Scope *child_scope = &suspend_scope->base; + IrInstSrc *susp_res = ir_gen_node(irb, node->data.suspend.block, child_scope); + if (susp_res == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.suspend.block, susp_res)); return ir_mark_gen(ir_build_suspend_finish_src(irb, parent_scope, node, begin)); } diff --git a/src/stage1/parser.cpp b/src/stage1/parser.cpp index c37b3ffefb..d57277cd51 100644 --- a/src/stage1/parser.cpp +++ b/src/stage1/parser.cpp @@ -946,10 +946,7 @@ static AstNode *ast_parse_statement(ParseContext *pc) { Token *suspend = eat_token_if(pc, TokenIdKeywordSuspend); if (suspend != nullptr) { - AstNode *statement = nullptr; - if (eat_token_if(pc, TokenIdSemicolon) == nullptr) - statement = ast_expect(pc, ast_parse_block_expr_statement); - + AstNode *statement = ast_expect(pc, ast_parse_block_expr_statement); AstNode *res = ast_create_node(pc, NodeTypeSuspend, suspend); res->data.suspend.block = statement; return res; diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 808f0b31dc..bfa9b592b4 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1021,7 +1021,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry() void { \\ nosuspend { \\ const bar = async foo(); - \\ suspend; + \\ suspend {} \\ resume bar; \\ } \\} @@ -2120,7 +2120,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ non_async_fn = func; \\} \\fn func() void { - \\ suspend; + \\ suspend {} \\} , &[_][]const u8{ "tmp.zig:5:1: error: 'func' cannot be async", @@ -2198,7 +2198,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var x: anyframe = &f; \\} \\fn func() void { - \\ suspend; + \\ suspend {} \\} , &[_][]const u8{ "tmp.zig:3:12: error: expected type 'anyframe', found '*const @Frame(func)'", @@ -2231,10 +2231,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ frame = async bar(); \\} \\fn foo() void { - \\ suspend; + \\ suspend {} \\} \\fn bar() void { - \\ suspend; + \\ suspend {} \\} , &[_][]const u8{ "tmp.zig:3:13: error: expected type '*@Frame(bar)', found '*@Frame(foo)'", @@ -2269,7 +2269,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var result = await frame; \\} \\fn func() void { - \\ suspend; + \\ suspend {} \\} , &[_][]const u8{ "tmp.zig:1:1: error: function with calling convention 'C' cannot be async", @@ -2347,7 +2347,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ bar(); \\} \\fn bar() void { - \\ suspend; + \\ suspend {} \\} , &[_][]const u8{ "tmp.zig:1:1: error: function with calling convention 'C' cannot be async", diff --git a/test/runtime_safety.zig b/test/runtime_safety.zig index eb49b2dbc1..03a26a2c32 100644 --- a/test/runtime_safety.zig +++ b/test/runtime_safety.zig @@ -13,7 +13,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { cases.addRuntimeSafety("switch on corrupted enum value", \\const std = @import("std"); - ++ check_panic_msg ++ + ++ check_panic_msg ++ \\const E = enum(u32) { \\ X = 1, \\}; @@ -28,7 +28,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { cases.addRuntimeSafety("switch on corrupted union value", \\const std = @import("std"); - ++ check_panic_msg ++ + ++ check_panic_msg ++ \\const U = union(enum(u32)) { \\ X: u8, \\}; @@ -54,7 +54,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { cases.addRuntimeSafety("@tagName on corrupted enum value", \\const std = @import("std"); - ++ check_panic_msg ++ + ++ check_panic_msg ++ \\const E = enum(u32) { \\ X = 1, \\}; @@ -67,7 +67,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { cases.addRuntimeSafety("@tagName on corrupted union value", \\const std = @import("std"); - ++ check_panic_msg ++ + ++ check_panic_msg ++ \\const U = union(enum(u32)) { \\ X: u8, \\}; @@ -92,7 +92,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { cases.addRuntimeSafety("slicing operator with sentinel", \\const std = @import("std"); - ++ check_panic_msg ++ + ++ check_panic_msg ++ \\pub fn main() void { \\ var buf = [4]u8{'a','b','c',0}; \\ const slice = buf[0..4 :0]; @@ -100,7 +100,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("slicing operator with sentinel", \\const std = @import("std"); - ++ check_panic_msg ++ + ++ check_panic_msg ++ \\pub fn main() void { \\ var buf = [4]u8{'a','b','c',0}; \\ const slice = buf[0..:0]; @@ -108,7 +108,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("slicing operator with sentinel", \\const std = @import("std"); - ++ check_panic_msg ++ + ++ check_panic_msg ++ \\pub fn main() void { \\ var buf_zero = [0]u8{}; \\ const slice = buf_zero[0..0 :0]; @@ -116,7 +116,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("slicing operator with sentinel", \\const std = @import("std"); - ++ check_panic_msg ++ + ++ check_panic_msg ++ \\pub fn main() void { \\ var buf_zero = [0]u8{}; \\ const slice = buf_zero[0..:0]; @@ -124,7 +124,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("slicing operator with sentinel", \\const std = @import("std"); - ++ check_panic_msg ++ + ++ check_panic_msg ++ \\pub fn main() void { \\ var buf_sentinel = [2:0]u8{'a','b'}; \\ @ptrCast(*[3]u8, &buf_sentinel)[2] = 0; @@ -133,7 +133,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("slicing operator with sentinel", \\const std = @import("std"); - ++ check_panic_msg ++ + ++ check_panic_msg ++ \\pub fn main() void { \\ var buf_slice: []const u8 = &[3]u8{ 'a', 'b', 0 }; \\ const slice = buf_slice[0..3 :0]; @@ -141,7 +141,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("slicing operator with sentinel", \\const std = @import("std"); - ++ check_panic_msg ++ + ++ check_panic_msg ++ \\pub fn main() void { \\ var buf_slice: []const u8 = &[3]u8{ 'a', 'b', 0 }; \\ const slice = buf_slice[0.. :0]; @@ -367,7 +367,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\} \\fn add(a: i32, b: i32) i32 { \\ if (a > 100) { - \\ suspend; + \\ suspend {} \\ } \\ return a + b; \\} @@ -407,7 +407,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ var frame = @asyncCall(&bytes, {}, ptr, .{}); \\} \\fn other() callconv(.Async) void { - \\ suspend; + \\ suspend {} \\} ); @@ -424,7 +424,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ await frame; \\} \\fn other() void { - \\ suspend; + \\ suspend {} \\} ); @@ -440,7 +440,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ other(); \\} \\fn other() void { - \\ suspend; + \\ suspend {} \\} ); @@ -454,7 +454,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ resume p; //bad \\} \\fn suspendOnce() void { - \\ suspend; + \\ suspend {} \\} ); @@ -1019,7 +1019,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\} \\ \\fn failing() anyerror!void { - \\ suspend; + \\ suspend {} \\ return second(); \\} \\ diff --git a/test/stage1/behavior/async_fn.zig b/test/stage1/behavior/async_fn.zig index 40269df5ec..0765eac7e8 100644 --- a/test/stage1/behavior/async_fn.zig +++ b/test/stage1/behavior/async_fn.zig @@ -18,9 +18,9 @@ test "simple coroutine suspend and resume" { } fn simpleAsyncFn() void { global_x += 1; - suspend; + suspend {} global_x += 1; - suspend; + suspend {} global_x += 1; } @@ -34,7 +34,7 @@ test "pass parameter to coroutine" { } fn simpleAsyncFnWithArg(delta: i32) void { global_y += delta; - suspend; + suspend {} global_y += delta; } @@ -50,7 +50,7 @@ test "suspend at end of function" { fn suspendAtEnd() void { x += 1; - suspend; + suspend {} } }; S.doTheTest(); @@ -74,11 +74,11 @@ test "local variable in async function" { fn add(a: i32, b: i32) void { var accum: i32 = 0; - suspend; + suspend {} accum += a; - suspend; + suspend {} accum += b; - suspend; + suspend {} x = accum; } }; @@ -102,7 +102,7 @@ test "calling an inferred async function" { } fn other() void { other_frame = @frame(); - suspend; + suspend {} x += 1; } }; @@ -129,7 +129,7 @@ test "@frameSize" { } fn other(param: i32) void { var local: i32 = undefined; - suspend; + suspend {} } }; S.doTheTest(); @@ -269,7 +269,7 @@ test "async function with dot syntax" { var y: i32 = 1; fn foo() callconv(.Async) void { y += 1; - suspend; + suspend {} } }; const p = async S.foo(); @@ -298,7 +298,7 @@ fn doTheAwait(f: anyframe->void) void { fn simpleAsyncFn2(y: *i32) callconv(.Async) void { defer y.* += 2; y.* += 1; - suspend; + suspend {} } test "@asyncCall with return type" { @@ -312,7 +312,7 @@ test "@asyncCall with return type" { fn afunc() i32 { global_frame = @frame(); - suspend; + suspend {} return 1234; } }; @@ -348,7 +348,7 @@ test "async fn with inferred error set" { fn failing() !void { global_frame = @frame(); - suspend; + suspend {} return error.Fail; } }; @@ -375,7 +375,7 @@ fn nonFailing() (anyframe->anyerror!void) { return &Static.frame; } fn suspendThenFail() callconv(.Async) anyerror!void { - suspend; + suspend {} return error.Fail; } fn printTrace(p: anyframe->(anyerror!void)) callconv(.Async) void { @@ -400,7 +400,7 @@ fn testBreakFromSuspend(my_result: *i32) callconv(.Async) void { resume @frame(); } my_result.* += 1; - suspend; + suspend {} my_result.* += 1; } @@ -421,7 +421,7 @@ test "heap allocated async function frame" { fn someFunc() void { x += 1; - suspend; + suspend {} x += 1; } }; @@ -454,7 +454,7 @@ test "async function call return value" { fn other(x: i32, y: i32) Point { frame = @frame(); - suspend; + suspend {} return Point{ .x = x, .y = y, @@ -487,7 +487,7 @@ test "suspension points inside branching control flow" { fn func(b: bool) void { while (b) { - suspend; + suspend {} result += 1; } } @@ -541,7 +541,7 @@ test "pass string literal to async function" { fn hello(msg: []const u8) void { frame = @frame(); - suspend; + suspend {} expectEqualStrings("hello", msg); ok = true; } @@ -566,7 +566,7 @@ test "await inside an errdefer" { fn func() void { frame = @frame(); - suspend; + suspend {} } }; S.doTheTest(); @@ -590,7 +590,7 @@ test "try in an async function with error union and non-zero-bit payload" { fn theProblem() ![]u8 { frame = @frame(); - suspend; + suspend {} const result = try other(); return result; } @@ -622,7 +622,7 @@ test "returning a const error from async function" { fn fetchUrl(unused: i32, url: []const u8) ![]u8 { frame = @frame(); - suspend; + suspend {} ok = true; return error.OutOfMemory; } @@ -967,7 +967,7 @@ test "@asyncCall with comptime-known function, but not awaited directly" { fn failing() !void { global_frame = @frame(); - suspend; + suspend {} return error.Fail; } }; @@ -977,7 +977,7 @@ test "@asyncCall with comptime-known function, but not awaited directly" { test "@asyncCall with actual frame instead of byte buffer" { const S = struct { fn func() i32 { - suspend; + suspend {} return 1234; } }; @@ -993,7 +993,7 @@ test "@asyncCall using the result location inside the frame" { fn simple2(y: *i32) callconv(.Async) i32 { defer y.* += 2; y.* += 1; - suspend; + suspend {} return 1234; } fn getAnswer(f: anyframe->i32, out: *i32) void { @@ -1095,7 +1095,7 @@ test "nosuspend function call" { } fn add(a: i32, b: i32) i32 { if (a > 100) { - suspend; + suspend {} } return a + b; } @@ -1170,7 +1170,7 @@ test "suspend in for loop" { global_frame = @frame(); var sum: u32 = 0; for (stuff) |x| { - suspend; + suspend {} sum += x; } global_frame = null; @@ -1197,7 +1197,7 @@ test "suspend in while loop" { global_frame = @frame(); defer global_frame = null; while (stuff) |val| { - suspend; + suspend {} return val; } return 0; @@ -1206,7 +1206,7 @@ test "suspend in while loop" { global_frame = @frame(); defer global_frame = null; while (stuff) |val| { - suspend; + suspend {} return val; } else |err| { return 0; @@ -1339,7 +1339,7 @@ test "async function passed 0-bit arg after non-0-bit arg" { fn bar(x: i32, args: anytype) anyerror!void { global_frame = @frame(); - suspend; + suspend {} global_int = x; } }; @@ -1361,7 +1361,7 @@ test "async function passed align(16) arg after align(8) arg" { fn bar(x: u64, args: anytype) anyerror!void { expect(x == 10); global_frame = @frame(); - suspend; + suspend {} global_int = args[0]; } }; @@ -1383,7 +1383,7 @@ test "async function call resolves target fn frame, comptime func" { fn bar() anyerror!void { global_frame = @frame(); - suspend; + suspend {} global_int += 1; } }; @@ -1406,7 +1406,7 @@ test "async function call resolves target fn frame, runtime func" { fn bar() anyerror!void { global_frame = @frame(); - suspend; + suspend {} global_int += 1; } }; @@ -1430,7 +1430,7 @@ test "properly spill optional payload capture value" { fn bar() void { global_frame = @frame(); - suspend; + suspend {} global_int += 1; } }; @@ -1466,13 +1466,13 @@ test "handle defer interfering with return value spill" { fn bar() anyerror!void { global_frame1 = @frame(); - suspend; + suspend {} return error.Bad; } fn baz() void { global_frame2 = @frame(); - suspend; + suspend {} baz_happened = true; } }; @@ -1497,7 +1497,7 @@ test "take address of temporary async frame" { fn foo(arg: i32) i32 { global_frame = @frame(); - suspend; + suspend {} return arg + 1234; } @@ -1520,7 +1520,7 @@ test "nosuspend await" { fn foo(want_suspend: bool) i32 { if (want_suspend) { - suspend; + suspend {} } return 42; } @@ -1569,11 +1569,11 @@ test "nosuspend on async function calls" { // }; // const S1 = struct { // fn c() S0 { -// suspend; +// suspend {} // return S0{}; // } // fn d() !S0 { -// suspend; +// suspend {} // return S0{}; // } // }; @@ -1591,11 +1591,11 @@ test "nosuspend resume async function calls" { }; const S1 = struct { fn c() S0 { - suspend; + suspend {} return S0{}; } fn d() !S0 { - suspend; + suspend {} return S0{}; } }; From eabf378a56e0cd9720f43c36b85025228c4406d8 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sat, 24 Apr 2021 15:33:20 +0200 Subject: [PATCH 55/57] zig fmt: Automagically fix block-less suspend exprs --- lib/std/zig/parse.zig | 10 ++++++++-- lib/std/zig/parser_test.zig | 18 ++++++++++++++++++ lib/std/zig/render.zig | 18 +++++++++++++++++- 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index 778075bdee..d6880ad273 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -891,11 +891,17 @@ const Parser = struct { }); }, .keyword_suspend => { + const token = p.nextToken(); + // TODO remove this special case when 0.9.0 is released. + const block_expr: Node.Index = if (p.eatToken(.semicolon) != null) + 0 + else + try p.expectBlockExprStatement(); return p.addNode(.{ .tag = .@"suspend", - .main_token = p.nextToken(), + .main_token = token, .data = .{ - .lhs = try p.expectBlockExprStatement(), + .lhs = block_expr, .rhs = undefined, }, }); diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index 479da2b50c..2fa8baa185 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -40,6 +40,21 @@ test "zig fmt: rewrite inline functions as callconv(.Inline)" { ); } +// TODO Remove this after zig 0.9.0 is released. +test "zig fmt: rewrite suspend without block expression" { + try testTransform( + \\fn foo() void { + \\ suspend; + \\} + \\ + , + \\fn foo() void { + \\ suspend {} + \\} + \\ + ); +} + test "zig fmt: simple top level comptime block" { try testCanonical( \\// line comment @@ -5023,6 +5038,9 @@ test "recovery: invalid comptime" { } test "recovery: missing block after suspend" { + // TODO Enable this after zig 0.9.0 is released. + if (true) return error.SkipZigTest; + try testError( \\fn foo() void { \\ suspend; diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index a417947c6b..a762b78728 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -255,13 +255,29 @@ fn renderExpression(gpa: *Allocator, ais: *Ais, tree: ast.Tree, node: ast.Node.I try renderToken(ais, tree, defer_token, .space); return renderExpression(gpa, ais, tree, expr, space); }, - .@"comptime", .@"suspend", .@"nosuspend" => { + .@"comptime", .@"nosuspend" => { const comptime_token = main_tokens[node]; const block = datas[node].lhs; try renderToken(ais, tree, comptime_token, .space); return renderExpression(gpa, ais, tree, block, space); }, + .@"suspend" => { + const suspend_token = main_tokens[node]; + const body = datas[node].lhs; + if (body != 0) { + try renderToken(ais, tree, suspend_token, .space); + return renderExpression(gpa, ais, tree, body, space); + } else { + // TODO remove this special case when 0.9.0 is released. + assert(space == .semicolon); + try renderToken(ais, tree, suspend_token, .space); + try ais.writer().writeAll("{}"); + try ais.insertNewline(); + return; + } + }, + .@"catch" => { const main_token = main_tokens[node]; const fallback_first = tree.firstToken(datas[node].rhs); From 7a793a9b9d38adc6800834d2dc11a1037b1aef4d Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sat, 24 Apr 2021 14:55:01 +0200 Subject: [PATCH 56/57] ed25519: use double-base multiplication for signature verification This makes single signature verification about 60% faster. Also check that R is not the identity point. --- lib/std/crypto/25519/ed25519.zig | 9 +++--- lib/std/crypto/25519/edwards25519.zig | 45 +++++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/lib/std/crypto/25519/ed25519.zig b/lib/std/crypto/25519/ed25519.zig index 999b099bc6..b48cc24b4b 100644 --- a/lib/std/crypto/25519/ed25519.zig +++ b/lib/std/crypto/25519/ed25519.zig @@ -129,6 +129,7 @@ pub const Ed25519 = struct { try a.rejectIdentity(); try Curve.rejectNonCanonical(r.*); const expected_r = try Curve.fromBytes(r.*); + try expected_r.rejectIdentity(); var h = Sha512.init(.{}); h.update(r); @@ -138,8 +139,7 @@ pub const Ed25519 = struct { h.final(&hram64); const hram = Curve.scalar.reduce64(hram64); - const ah = try a.neg().mulPublic(hram); - const sb_ah = (try Curve.basePoint.mulPublic(s.*)).add(ah); + const sb_ah = try Curve.basePoint.mulDoubleBasePublic(s.*, a.neg(), hram); if (expected_r.sub(sb_ah).clearCofactor().rejectIdentity()) |_| { return error.SignatureVerificationFailed; } else |_| {} @@ -168,6 +168,7 @@ pub const Ed25519 = struct { try a.rejectIdentity(); try Curve.rejectNonCanonical(r.*); const expected_r = try Curve.fromBytes(r.*); + try expected_r.rejectIdentity(); expected_r_batch[i] = expected_r; r_batch[i] = r.*; s_batch[i] = s.*; @@ -324,13 +325,13 @@ test "ed25519 test vectors" { .msg_hex = "9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41", .public_key_hex = "f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43", .sig_hex = "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03be9678ac102edcd92b0210bb34d7428d12ffc5df5f37e359941266a4e35f0f", - .expected = error.SignatureVerificationFailed, // 8 - non-canonical R + .expected = error.IdentityElement, // 8 - non-canonical R }, Vec{ .msg_hex = "9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41", .public_key_hex = "f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43", .sig_hex = "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffca8c5b64cd208982aa38d4936621a4775aa233aa0505711d8fdcfdaa943d4908", - .expected = null, // 9 - non-canonical R + .expected = error.IdentityElement, // 9 - non-canonical R }, Vec{ .msg_hex = "e96b7021eb39c1a163b6da4e3093dcd3f21387da4cc4572be588fafae23c155b", diff --git a/lib/std/crypto/25519/edwards25519.zig b/lib/std/crypto/25519/edwards25519.zig index c147bed597..e8163a3960 100644 --- a/lib/std/crypto/25519/edwards25519.zig +++ b/lib/std/crypto/25519/edwards25519.zig @@ -238,6 +238,11 @@ pub const Edwards25519 = struct { break :pc precompute(Edwards25519.basePoint, 15); }; + const basePointPc8 = comptime pc: { + @setEvalBranchQuota(10000); + break :pc precompute(Edwards25519.basePoint, 8); + }; + /// Multiply an Edwards25519 point by a scalar without clamping it. /// Return error.WeakPublicKey if the base generates a small-order group, /// and error.IdentityElement if the result is the identity element. @@ -262,14 +267,50 @@ pub const Edwards25519 = struct { } } + /// Double-base multiplication of public parameters - Compute (p1*s1)+(p2*s2) *IN VARIABLE TIME* + /// This can be used for signature verification. + pub fn mulDoubleBasePublic(p1: Edwards25519, s1: [32]u8, p2: Edwards25519, s2: [32]u8) (IdentityElementError || WeakPublicKeyError)!Edwards25519 { + const pc1 = if (p1.is_base) basePointPc8 else pc: { + const xpc = precompute(p1, 8); + xpc[4].rejectIdentity() catch return error.WeakPublicKey; + break :pc xpc; + }; + const pc2 = if (p2.is_base) basePointPc8 else pc: { + const xpc = precompute(p2, 8); + xpc[4].rejectIdentity() catch return error.WeakPublicKey; + break :pc xpc; + }; + const e1 = nonAdjacentForm(s1); + const e2 = nonAdjacentForm(s2); + var q = Edwards25519.identityElement; + var pos: usize = 2 * 32 - 1; + while (true) : (pos -= 1) { + const slot1 = e1[pos]; + if (slot1 > 0) { + q = q.add(pc1[@intCast(usize, slot1)]); + } else if (slot1 < 0) { + q = q.sub(pc1[@intCast(usize, -slot1)]); + } + const slot2 = e2[pos]; + if (slot2 > 0) { + q = q.add(pc2[@intCast(usize, slot2)]); + } else if (slot2 < 0) { + q = q.sub(pc2[@intCast(usize, -slot2)]); + } + if (pos == 0) break; + q = q.dbl().dbl().dbl().dbl(); + } + try q.rejectIdentity(); + return q; + } + /// Multiscalar multiplication *IN VARIABLE TIME* for public data /// Computes ps0*ss0 + ps1*ss1 + ps2*ss2... faster than doing many of these operations individually pub fn mulMulti(comptime count: usize, ps: [count]Edwards25519, ss: [count][32]u8) (IdentityElementError || WeakPublicKeyError)!Edwards25519 { var pcs: [count][9]Edwards25519 = undefined; for (ps) |p, i| { if (p.is_base) { - @setEvalBranchQuota(10000); - pcs[i] = comptime precompute(Edwards25519.basePoint, 8); + pcs[i] = basePointPc8; } else { pcs[i] = precompute(p, 8); pcs[i][4].rejectIdentity() catch |_| return error.WeakPublicKey; From 8530b6b7242ebf43b5cb4ae3a2644593f4961a5e Mon Sep 17 00:00:00 2001 From: Matt Knight Date: Fri, 23 Apr 2021 23:57:48 -0700 Subject: [PATCH 57/57] fix import path --- lib/std/os/linux/bpf/btf.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/os/linux/bpf/btf.zig b/lib/std/os/linux/bpf/btf.zig index b28f65945a..9ba3a0f942 100644 --- a/lib/std/os/linux/bpf/btf.zig +++ b/lib/std/os/linux/bpf/btf.zig @@ -6,7 +6,7 @@ const magic = 0xeb9f; const version = 1; -pub const ext = @import("ext.zig"); +pub const ext = @import("btf_ext.zig"); /// All offsets are in bytes relative to the end of this header pub const Header = packed struct {