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 92e03603c5d4efdbb57460a5b2a7fd23c2f6d8aa..f0b6426d1b25c1ea61e7274efa3dfef038f709f5 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 f2f55e508e783cef050f607a615d55e9e9b571b0..caa280595606781a1c5b05c9a70c74634a295f55 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" { -- 2.54.0 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 394164ff5450dfd37bab8d74474f5a8045c86a46..4f325812705ad7c12025cf630d5ba5ee0a99e319 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, }; } -- 2.54.0 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 58b0aff54af01bc6dbc1ab8bac778c883aba2edc..840cdccc8bc71b33a53a56e8ca697be1b1832ef6 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 71a233c964d9154a43e328a33355319ff1a46667..942b6028d924b5d13a2c48422a31ce50b47c15dd 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 8408a1511682fdc6384870a8cef7ee6501ce5118..0eebe7365e80ab3f9e8df085d15083a14e7fbe09 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 0a1f9589334ac5d1af151affd3765bc366598c26..42922a522685c01ea20657fb72c6f9591546994d 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 -- 2.54.0 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 470ac17c2c70eb323e618b4af4f4c9619f3af284..cae56449628410f134f3d2bfaa16da3922fab041 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; -- 2.54.0 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 68690c9af8ce99dca0e3459ef3cfef641d9dbf9d..3835aab0f4540e1442b4c2d7f9bacb9806173dfc 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(); +} -- 2.54.0 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 6d219c517d7f3fc1b5a53eb94b9df09844d7d91c..3bb1a7e022f2b87225db6ac5c43ac6fd8c4b968d 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: -- 2.54.0 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 c5b93fa51e8af61af537526010a6fe798d4d9653..59be8441fad2e4824e2715f1c5febb26f06993e3 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 6a3f69d8e952f3aea6c43d638175407e327b1d6b..d97a9c2502ee8a097145dd2e06f384d9b472c274 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); +} -- 2.54.0 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 cae56449628410f134f3d2bfaa16da3922fab041..e85aa363dbafd90a9e8b33602cb09d06107fc61f 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 6426614b0783e4c03945d77a89230a6ecd557425..4fae5d1fc0f2933b7bae0162b5cb9094cb75d222 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); +} -- 2.54.0 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 d97a9c2502ee8a097145dd2e06f384d9b472c274..f05d33eac37b6863fe88d7e03f8b3747f9924331 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); } -- 2.54.0 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 860b6874c09062c74721baf6c0ba2c9c4f4e9a5c..c6f800ca2cc314af834d3799ea7402c6f6d0101a 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 9a1215abd6a55d50d37995328042bbff22f357ae..8a2a1b62231a8efdfdf4a1d5c898ef006a737117 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 7e5d0da367f24f5d8410bdaa4240504406d7e53d..35686c11f9aff4c01df953e83adaf86c14bc88e2 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 142579c92cdf492392d7d84411c8d49d25cdc7f9..415d303f31d3425c96269372573d21cd98045b7e 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)); -- 2.54.0 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 ed08438425ba8f337d59487a430ad7a891c93b99..dbc1e2aee56a69c63ad69d40a91d3f5b0ae44e8e 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 { -- 2.54.0 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 fe7cc6a5d0688cd3ce0f80e483f8dad6ab303fdf..0994604f801a7d66ac34eb7666d8da1a036f60c8 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/" -- 2.54.0 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 9a058f35c06b18187d78ebf7ac408301f7ae89e6..95ae5f014991470bc03b7ceda97dc11e52c2d7d7 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()) { -- 2.54.0 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 e11f2c31112bb3999690d7beeb00a58bb759bc84..01f83aa2f5c72eda6b334242551121547d27a0e8 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); } -- 2.54.0 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 cf0d08c5ed132b44a0310117f13c1d2cabd0784e..5f9a97459f41e827c1d3c4fd343fbe5e18a53700 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")) { -- 2.54.0 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 05871efba8ef5569bf1a5260f2f73385624a3354..fa7fb9f018409e4b29edaad7b0282ebb1395d49a 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"); } -- 2.54.0 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 5f9a97459f41e827c1d3c4fd343fbe5e18a53700..f0772abbbd1aac6ee2fb13bda3b47469a3d2a074 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; -- 2.54.0 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 dbc1e2aee56a69c63ad69d40a91d3f5b0ae44e8e..23bd5dac6835e2b439414b0603f0c7ea2e770a2a 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"); -- 2.54.0 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 722b307c16144574264fec781a07e68f0ba5fd21..7ff7ef13745389bef66158cab3817a0135ce1a87 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"); -- 2.54.0 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 fa7fb9f018409e4b29edaad7b0282ebb1395d49a..fce3a0bcebf5b9c65a5d0cae28a40eda4b9a9208 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"); -- 2.54.0 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 1bed86f42d6602323f84722ad270504db295efc3..c433ee5ad8b753b5d23fd034ea8c134eaf6e2bbd 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 71d5e51bb94dada760a8d3ff224fcfa47ffc14be..9291f63e2f21de78b52febec9462f981944c1f6d 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); + // Write with the canonical single space. + try ais.underlying_writer.writeAll("// zig fmt: on\n"); ais.disabled_offset = null; - // Write with the canonical single space. - try ais.writer().writeAll("// zig fmt: on\n"); } 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"); -- 2.54.0 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 a345fd80581e2c0c36420ed8fe21d0dd180ed98b..aaab766336798412f816c7626437388097f11002 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, } -- 2.54.0 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 23bd5dac6835e2b439414b0603f0c7ea2e770a2a..e7826f89bfaa9e49b6a6f2a022d2d62ce07206f3 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"); -- 2.54.0 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 8bf58434257e0994acb022401419ff1c000f16c2..6cf47bc9ad47bce9341ecc920ca5d66eca05aee6 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 -- 2.54.0 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 8a2a1b62231a8efdfdf4a1d5c898ef006a737117..ac5c52ee0d95744cbd4f5b9457ff9169b8821082 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( -- 2.54.0 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 4be0fead973faed99b06dc794d44a9e17da079b8..dfc72b2a69ca499723d22c107c4ab950ed79dbf3 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 35686c11f9aff4c01df953e83adaf86c14bc88e2..314ef2889de25335c72e6dd917b4484aaf104231 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; + \\} + , ""); } -- 2.54.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 dfc72b2a69ca499723d22c107c4ab950ed79dbf3..61d28bb22daa43d70b8993b51e379eb3c0a5fca3 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 415d303f31d3425c96269372573d21cd98045b7e..5fa4c3204163f3bc4a31a4d7a680eca7fed6ebc8 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, -- 2.54.0 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 caa280595606781a1c5b05c9a70c74634a295f55..5112e76dfb50629082ad2cb0cb6416ef9da8b317 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. -- 2.54.0 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 5112e76dfb50629082ad2cb0cb6416ef9da8b317..bea172b81d21cb7fe771ba91069787a2478ff4c0 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. -- 2.54.0 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 bea172b81d21cb7fe771ba91069787a2478ff4c0..ffd31bea000bb1c2f45b3b553096c78ae28e0d1f 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. -- 2.54.0 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 ffd31bea000bb1c2f45b3b553096c78ae28e0d1f..379c4a40da4acbb9bf79f29a393b220cad547526 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)))); } -- 2.54.0 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 9a94b901373a1daffca6077bb70f123c59c17f6a..aaf88ad8154e174e3a375424619599580414e4fb 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 ae475ab30d2dcaa34aab6a0e4f0a33af9f4362d0..0fb1ab2145712c4183c7daab4334933e1c396cac 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 { -- 2.54.0 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 0fb1ab2145712c4183c7daab4334933e1c396cac..52761ef048e6290f3c8b0289f824c6669242ea94 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 a7dd0919b40fb09f8f275babde9f0e2044041b6b..d8e7cebddd490366340ea847df9742c3ab7a7630 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", .{}); }, } } -- 2.54.0 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 52761ef048e6290f3c8b0289f824c6669242ea94..4bdb419ffc9e8e6cc3eb5fa49d282472204fa6ed 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(); -- 2.54.0 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 4bdb419ffc9e8e6cc3eb5fa49d282472204fa6ed..4a3c307806fccbedaa44db8ee3fc3b5b9fdc35d5 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]; -- 2.54.0 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 f65a694f751c3762f52be0b616c11d5d21764f11..9e6c2bf68ac084ab93abee6667b72d0172394180 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 4a3c307806fccbedaa44db8ee3fc3b5b9fdc35d5..68312519662b66dbacfe46a2f5f4c2f64c36315e 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; -- 2.54.0 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 457b9130d92fb11d6c11bcd5f0e8553ba7f49e8c..e4ec50f5b763d0faf4ef4e958dc197d6560c1b0b 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 d3e51ad0e042c8c51032deedacc8289b3643f649..90b0e10c4f1eda06fe03f4b9641b45861bef4d47 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 e385e34f1285d3cc9ea239e80adffdd0614544ff..999b099bc62d5988a5b5297c46e38ff702d3b784 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 89b7b9b9f3312b6c3600c8df4d1c172d0a6b97e8..c147bed5976fae55287dc9041b7036b4047fbdce 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 b570e2d06b455e865b52023b112b70a9c7100269..5ac184080cacaa312ebc89648a24a981202ec940 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 4644b7622e67a8d81f3403285537ecbf0f92d00d..50f1580a805314992cfbb7a01697c1ea53c997d5 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 a4bf5aafcf83a71e057df382f25e03473b80b7f0..21578486e896e993040da40f910ab6ae008d45da 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 2d5312405688c4da3b2f92ebabbf419c6968e67a..07b1dc7a866856b1cd18912d8efd4278d327015d 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 3969d59e10975e243d846fc644f6c01359faeadd..59dcf04dac62464c454ed1987e3ce8d59ae9dd69 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 bcb1b4c5fa4872962ce3def151bd4706447b9e16..70746af0737bcd5b0506cac1bf88be9a065db14f 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 9eb0561d9f6af3e9395f5bba990b7744f3f615fa..658b3b97ce0d14735720b1bdced4c39ec13370aa 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 d00108b9c4ef7a8547f8990339a56705401e6828..51fb144b2f039afb963ff3adb3c01f239f6bed83 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 e1fe3e232d7ddc6dac19e0ebd53775e3676384fc..ea9eafd356752be2ae18405d5e5500cb31e891a8 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 4cb12bb8f7848f1c785f13787ea8fb2585c1a550..0000000000000000000000000000000000000000 --- 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 0000000000000000000000000000000000000000..4d79055919a7574053e46ba519f9144d6c9bca1a --- /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 111e0c52744e8b5434331948d137e07abe8f3888..fb67c25343677e6d99c01733db5e5ab8e22ddb97 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 5219742d85aeecec2ff7a1f12b74a5e5bcc6c13d..6deb4977bb7619f95294a397cc2037e49cb0b27c 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 575fb83006ca927d3955180f17277d31ab1ad72b..f93a5235af6b520cd6efaeae811f82374489b433 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 006767c93f38b71f11ab5c6f198a9cd0d4ada39c..2a06944adcff282caea28a89e91e853f5634d4be 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; } -- 2.54.0 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 3372f617a87568bbb02738b4b0303f8ffc691e25..c741ea225f26f022a0fde34caaa2d8f677534c30 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 4e2200a47f3b64678c74e95ee3b47abe0dc0b62f..db4d5dccdc7b3cfdb71190ea6ed1a78be5481b42 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 7e3c636c31dd5fc4fd2fb74d0e14d3a79eab279b..15143dfca4fc2b0892900017bbef40225cf95af0 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", -- 2.54.0 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 f66626bafebdecd92006f717448d83d67770333e..8f60d8fe94d2f008696152bfe4a7ec77402fade2 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 -- 2.54.0 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 e7826f89bfaa9e49b6a6f2a022d2d62ce07206f3..d8c7e99905a480f786d971b412044f7a88274037 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 c741ea225f26f022a0fde34caaa2d8f677534c30..40a5d14f2a0c294b37130c604966742a7549ded4 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, -- 2.54.0 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 baa6a57bcffffb96e8c9e004d7b744243613f3f3..158a5dab3d9c4a3ca630b10eccf62f43bb77d19f 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; -- 2.54.0 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 be8d8d0a3f2999a62010032b4f1899559754362b..7cdf83f2cd4af0e00397ca82f09554265ce33871 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, -- 2.54.0 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 3a7457a4fd043c14f710342eb764e0b01018e219..5d95724a5fa77e45e698ac1d9d887fe5a310f6b1 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 }); -- 2.54.0 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 5d95724a5fa77e45e698ac1d9d887fe5a310f6b1..b0cf37553d9ae340d23c26715a50b7be79d8ae40 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 }); -- 2.54.0 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 0eebe7365e80ab3f9e8df085d15083a14e7fbe09..d0b8d1a5b3c66b29db794dbaaa5542babfaffcf4 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 -- 2.54.0 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 63e4c946b6e33b206d56333dc89c4b811a738b24..41d8838892d564028ad2c1e00d05550eff2fc6d9 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#}

-- 2.54.0 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 79a05e95a52a862c8728be21318642cf2fecda45..acb3e18e41bf55e38dec85f791e23da7ae9ea8d0 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 044ea6642370e69e92ac3e291ed81c14d12dc360..aa37b9302aeb421478decfa07dbe96a7390bbf8c 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 942b6028d924b5d13a2c48422a31ce50b47c15dd..61a1345327d5cf1f8388965c6975e9e46663dd6d 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 3835aab0f4540e1442b4c2d7f9bacb9806173dfc..32f48427020385e5c999415d17b01f65dedca4a5 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(); -- 2.54.0 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 d71cafe5efc9c9122dca20e908b3f6019c8f33ac..558f541c00484740d1430ac7d5b298b0817933c9 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)); } -- 2.54.0 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 c968b862b9b62f049d364d2c2edb6585c3ac1f27..0cffb99d38aff87a817ec5cc2d6f8ab9bd962fe0 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 1a3bead2dfd1e2b7bbf814a77e44689b81d5cec2..2d1c5f80e99aa216e940ef35ab9d5b4962c83957 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); } -- 2.54.0 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 68312519662b66dbacfe46a2f5f4c2f64c36315e..bc28911757d911c04aaa448aa998584159d814d4 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; } -- 2.54.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 7b5f23756a7c38016ab90fb26bba9b7e5f65ebcf..8ccd32027455a5dc41a9e741628fabc327749eb4 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 bc28911757d911c04aaa448aa998584159d814d4..ff297c79e54f05c3916ce4f49584b7e312116af9 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, -- 2.54.0 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 86e160ba4db4e36c213cf997b7cbfb869bd5e62d..5a0b9609ad99bc082b33f63ea5064233c1766be4 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 ff297c79e54f05c3916ce4f49584b7e312116af9..02344d36d4898d57ad2e827ae2c5e0aa94ddfd20 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| { -- 2.54.0 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 | 160 +++++++++----------------------------- 2 files changed, 69 insertions(+), 136 deletions(-) diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 8ccd32027455a5dc41a9e741628fabc327749eb4..6703a5bfb75c18f9722bc8d8bef35e0a7f02e944 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 02344d36d4898d57ad2e827ae2c5e0aa94ddfd20..a585b1fd1e44f0d5d514fee99dc5e942f72ed4f1 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; - }; - - 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 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}", .{ + 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 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); + var initializers = std.ArrayList(u64).init(self.allocator); + defer initializers.deinit(); + + // 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); + _ = 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; + } } } -- 2.54.0 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 41d8838892d564028ad2c1e00d05550eff2fc6d9..f52abeab73657d4a8abf8bc1f28a28ff13b4cde1 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 750131beda4a29da205c313350d75926c777995b..04b45ee3086d6882dff3cf84d57c70604f8955b2 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 029d8ede50633deb2759daee0ac82629158dc0ad..778075bdeebc9121f766e245293b79f44c0dc690 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 c433ee5ad8b753b5d23fd034ea8c134eaf6e2bbd..479da2b50c6d24b42a454eb53d3d8f6df9c14e2c 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 9291f63e2f21de78b52febec9462f981944c1f6d..a417947c6b2049f2b74050074e974294d7d6a7d9 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 61a1345327d5cf1f8388965c6975e9e46663dd6d..c59f63399c23f8ce62b8d4f2432b8de9845f9f37 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 c37b3ffefbaf97e7484f372a3a73ac38f27dcd51..d57277cd5136a8f3e2c5472ff90d5ce286996913 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 808f0b31dc07401766f733dcc09622414c27939c..bfa9b592b438194da499a4d70a7992f652615d66 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 eb49b2dbc1c4a541dad41056b47e15bf5b935cd8..03a26a2c322a0c7606a004230f3e2d00086c4624 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 40269df5eca445ab6921fa74d743685cc8a7231f..0765eac7e8480addaf022c321f8c7571fbe41712 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{}; } }; -- 2.54.0 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 778075bdeebc9121f766e245293b79f44c0dc690..d6880ad2739efc272104a495d216f6adb1e4ce3c 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 479da2b50c6d24b42a454eb53d3d8f6df9c14e2c..2fa8baa185aeacbb91b68907618b636440e3595f 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 a417947c6b2049f2b74050074e974294d7d6a7d9..a762b78728a9c42c0ea11f902eaac9bdbc4cf4f2 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); -- 2.54.0 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 999b099bc62d5988a5b5297c46e38ff702d3b784..b48cc24b4b3b6d290127e0272768d411df55c7ed 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 c147bed5976fae55287dc9041b7036b4047fbdce..e8163a3960806879d9e9ccc32b8aae550857a630 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; -- 2.54.0 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 b28f65945a7663d2506c247d37e8661cd36c4724..9ba3a0f94255373f4ae18c7cff274618ac426364 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 { -- 2.54.0