mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 13:54:21 +00:00
Merge remote-tracking branch 'origin/master' into stage2-whole-file-astgen
In particular I wanted the change that makes `suspend;` illegal in the parser.
This commit is contained in:
commit
e86cee258c
73 changed files with 1160 additions and 468 deletions
12
.gitattributes
vendored
12
.gitattributes
vendored
|
|
@ -3,9 +3,9 @@
|
||||||
langref.html.in text eol=lf
|
langref.html.in text eol=lf
|
||||||
deps/SoftFloat-3e/*.txt text eol=crlf
|
deps/SoftFloat-3e/*.txt text eol=crlf
|
||||||
|
|
||||||
deps/* linguist-vendored
|
deps/** linguist-vendored
|
||||||
lib/include/* linguist-vendored
|
lib/include/** linguist-vendored
|
||||||
lib/libc/* linguist-vendored
|
lib/libc/** linguist-vendored
|
||||||
lib/libcxx/* linguist-vendored
|
lib/libcxx/** linguist-vendored
|
||||||
lib/libcxxabi/* linguist-vendored
|
lib/libcxxabi/** linguist-vendored
|
||||||
lib/libunwind/* linguist-vendored
|
lib/libunwind/** linguist-vendored
|
||||||
|
|
|
||||||
|
|
@ -267,6 +267,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/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/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.addCompareOutputTests(b, test_filter, modes));
|
||||||
test_step.dependOn(tests.addStandaloneTests(b, test_filter, modes));
|
test_step.dependOn(tests.addStandaloneTests(b, test_filter, modes));
|
||||||
|
|
|
||||||
|
|
@ -23,13 +23,11 @@ cmake .. -DCMAKE_BUILD_TYPE=Release "-DCMAKE_INSTALL_PREFIX=$DISTDIR" -DZIG_STAT
|
||||||
|
|
||||||
samu install
|
samu install
|
||||||
# run-translated-c tests are skipped due to: https://github.com/ziglang/zig/issues/8537
|
# 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 \
|
./zig build test \
|
||||||
-Dskip-release \
|
-Dskip-release \
|
||||||
-Dskip-non-native \
|
-Dskip-non-native \
|
||||||
-Dskip-compile-errors \
|
-Dskip-compile-errors \
|
||||||
-Dskip-run-translated-c \
|
-Dskip-run-translated-c
|
||||||
-Dskip-stage2-tests
|
|
||||||
|
|
||||||
if [ -z "$DRONE_PULL_REQUEST" ]; then
|
if [ -z "$DRONE_PULL_REQUEST" ]; then
|
||||||
mv ../LICENSE "$DISTDIR/"
|
mv ../LICENSE "$DISTDIR/"
|
||||||
|
|
|
||||||
|
|
@ -6509,7 +6509,7 @@ test "suspend with no resume" {
|
||||||
|
|
||||||
fn func() void {
|
fn func() void {
|
||||||
x += 1;
|
x += 1;
|
||||||
suspend;
|
suspend {}
|
||||||
// This line is never reached because the suspend has no matching resume.
|
// This line is never reached because the suspend has no matching resume.
|
||||||
x += 1;
|
x += 1;
|
||||||
}
|
}
|
||||||
|
|
@ -6574,7 +6574,7 @@ fn testResumeFromSuspend(my_result: *i32) void {
|
||||||
resume @frame();
|
resume @frame();
|
||||||
}
|
}
|
||||||
my_result.* += 1;
|
my_result.* += 1;
|
||||||
suspend;
|
suspend {}
|
||||||
my_result.* += 1;
|
my_result.* += 1;
|
||||||
}
|
}
|
||||||
{#code_end#}
|
{#code_end#}
|
||||||
|
|
@ -6613,7 +6613,7 @@ fn amain() void {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn func() void {
|
fn func() void {
|
||||||
suspend;
|
suspend {}
|
||||||
}
|
}
|
||||||
{#code_end#}
|
{#code_end#}
|
||||||
<p>
|
<p>
|
||||||
|
|
@ -6915,7 +6915,7 @@ test "async fn pointer in a struct field" {
|
||||||
fn func(y: *i32) void {
|
fn func(y: *i32) void {
|
||||||
defer y.* += 2;
|
defer y.* += 2;
|
||||||
y.* += 1;
|
y.* += 1;
|
||||||
suspend;
|
suspend {}
|
||||||
}
|
}
|
||||||
{#code_end#}
|
{#code_end#}
|
||||||
{#header_close#}
|
{#header_close#}
|
||||||
|
|
@ -7498,13 +7498,13 @@ test "main" {
|
||||||
{#header_close#}
|
{#header_close#}
|
||||||
|
|
||||||
{#header_open|@export#}
|
{#header_open|@export#}
|
||||||
<pre>{#syntax#}@export(target: anytype, comptime options: std.builtin.ExportOptions) void{#endsyntax#}</pre>
|
<pre>{#syntax#}@export(identifier, comptime options: std.builtin.ExportOptions) void{#endsyntax#}</pre>
|
||||||
<p>
|
<p>
|
||||||
Creates a symbol in the output object file.
|
Creates a symbol in the output object file.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
This function can be called from a {#link|comptime#} block to conditionally export symbols.
|
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
|
{#syntax#}options.linkage{#endsyntax#} is {#syntax#}Strong{#endsyntax#}, this is equivalent to
|
||||||
the {#syntax#}export{#endsyntax#} keyword used on a function:
|
the {#syntax#}export{#endsyntax#} keyword used on a function:
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -7531,6 +7531,14 @@ export fn @"A function name that is a complete sentence."() void {}
|
||||||
{#see_also|Exporting a C Library#}
|
{#see_also|Exporting a C Library#}
|
||||||
{#header_close#}
|
{#header_close#}
|
||||||
|
|
||||||
|
{#header_open|@extern#}
|
||||||
|
<pre>{#syntax#}@extern(T: type, comptime options: std.builtin.ExternOptions) *T{#endsyntax#}</pre>
|
||||||
|
<p>
|
||||||
|
Creates a reference to an external symbol in the output object file.
|
||||||
|
</p>
|
||||||
|
{#see_also|@export#}
|
||||||
|
{#header_close#}
|
||||||
|
|
||||||
{#header_open|@fence#}
|
{#header_open|@fence#}
|
||||||
<pre>{#syntax#}@fence(order: AtomicOrder){#endsyntax#}</pre>
|
<pre>{#syntax#}@fence(order: AtomicOrder){#endsyntax#}</pre>
|
||||||
<p>
|
<p>
|
||||||
|
|
@ -7640,7 +7648,7 @@ test "heap allocated frame" {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn func() void {
|
fn func() void {
|
||||||
suspend;
|
suspend {}
|
||||||
}
|
}
|
||||||
{#code_end#}
|
{#code_end#}
|
||||||
{#header_close#}
|
{#header_close#}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ pub const Bool = extern struct {
|
||||||
return @atomicRmw(bool, &self.unprotected_value, .Xchg, operand, ordering);
|
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) {
|
switch (ordering) {
|
||||||
.Unordered, .Monotonic, .Acquire, .SeqCst => {},
|
.Unordered, .Monotonic, .Acquire, .SeqCst => {},
|
||||||
else => @compileError("Invalid ordering '" ++ @tagName(ordering) ++ "' for a load operation"),
|
else => @compileError("Invalid ordering '" ++ @tagName(ordering) ++ "' for a load operation"),
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ pub fn Int(comptime T: type) type {
|
||||||
return @atomicRmw(T, &self.unprotected_value, op, operand, ordering);
|
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) {
|
switch (ordering) {
|
||||||
.Unordered, .Monotonic, .Acquire, .SeqCst => {},
|
.Unordered, .Monotonic, .Acquire, .SeqCst => {},
|
||||||
else => @compileError("Invalid ordering '" ++ @tagName(ordering) ++ "' for a load operation"),
|
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);
|
return self.rmw(.Sub, 1, .SeqCst);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(self: *Self) T {
|
pub fn get(self: *const Self) T {
|
||||||
return self.load(.SeqCst);
|
return self.load(.SeqCst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1386,6 +1386,8 @@ pub const LibExeObjStep = struct {
|
||||||
/// safely garbage-collected during the linking phase.
|
/// safely garbage-collected during the linking phase.
|
||||||
link_function_sections: bool = false,
|
link_function_sections: bool = false,
|
||||||
|
|
||||||
|
linker_allow_shlib_undefined: ?bool = null,
|
||||||
|
|
||||||
/// Uses system Wine installation to run cross compiled Windows build artifacts.
|
/// Uses system Wine installation to run cross compiled Windows build artifacts.
|
||||||
enable_wine: bool = false,
|
enable_wine: bool = false,
|
||||||
|
|
||||||
|
|
@ -2338,6 +2340,9 @@ pub const LibExeObjStep = struct {
|
||||||
if (self.link_function_sections) {
|
if (self.link_function_sections) {
|
||||||
try zig_args.append("-ffunction-sections");
|
try zig_args.append("-ffunction-sections");
|
||||||
}
|
}
|
||||||
|
if (self.linker_allow_shlib_undefined) |x| {
|
||||||
|
try zig_args.append(if (x) "-fallow-shlib-undefined" else "-fno-allow-shlib-undefined");
|
||||||
|
}
|
||||||
if (self.single_threaded) {
|
if (self.single_threaded) {
|
||||||
try zig_args.append("--single-threaded");
|
try zig_args.append("--single-threaded");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -154,7 +154,7 @@ pub const random = &@import("crypto/tlcsprng.zig").interface;
|
||||||
|
|
||||||
const std = @import("std.zig");
|
const std = @import("std.zig");
|
||||||
|
|
||||||
pub const Error = @import("crypto/error.zig").Error;
|
pub const errors = @import("crypto/errors.zig");
|
||||||
|
|
||||||
test "crypto" {
|
test "crypto" {
|
||||||
const please_windows_dont_oom = std.Target.current.os.tag == .windows;
|
const please_windows_dont_oom = std.Target.current.os.tag == .windows;
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,11 @@
|
||||||
// The MIT license requires this copyright notice to be included in all copies
|
// The MIT license requires this copyright notice to be included in all copies
|
||||||
// and substantial portions of the software.
|
// and substantial portions of the software.
|
||||||
const std = @import("std");
|
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.
|
/// Group operations over Curve25519.
|
||||||
pub const Curve25519 = struct {
|
pub const Curve25519 = struct {
|
||||||
|
|
@ -29,12 +33,12 @@ pub const Curve25519 = struct {
|
||||||
pub const basePoint = Curve25519{ .x = Fe.curve25519BasePoint };
|
pub const basePoint = Curve25519{ .x = Fe.curve25519BasePoint };
|
||||||
|
|
||||||
/// Check that the encoding of a Curve25519 point is canonical.
|
/// 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);
|
return Fe.rejectNonCanonical(s, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reject the neutral element.
|
/// Reject the neutral element.
|
||||||
pub fn rejectIdentity(p: Curve25519) Error!void {
|
pub fn rejectIdentity(p: Curve25519) IdentityElementError!void {
|
||||||
if (p.x.isZero()) {
|
if (p.x.isZero()) {
|
||||||
return error.IdentityElement;
|
return error.IdentityElement;
|
||||||
}
|
}
|
||||||
|
|
@ -45,7 +49,7 @@ pub const Curve25519 = struct {
|
||||||
return p.dbl().dbl().dbl();
|
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 x1 = p.x;
|
||||||
var x2 = Fe.one;
|
var x2 = Fe.one;
|
||||||
var z2 = Fe.zero;
|
var z2 = Fe.zero;
|
||||||
|
|
@ -86,7 +90,7 @@ pub const Curve25519 = struct {
|
||||||
/// way to use Curve25519 for a DH operation.
|
/// way to use Curve25519 for a DH operation.
|
||||||
/// Return error.IdentityElement if the resulting point is
|
/// Return error.IdentityElement if the resulting point is
|
||||||
/// the identity element.
|
/// 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;
|
var t: [32]u8 = s;
|
||||||
scalar.clamp(&t);
|
scalar.clamp(&t);
|
||||||
return try ladder(p, t, 255);
|
return try ladder(p, t, 255);
|
||||||
|
|
@ -96,16 +100,16 @@ pub const Curve25519 = struct {
|
||||||
/// Return error.IdentityElement if the resulting point is
|
/// Return error.IdentityElement if the resulting point is
|
||||||
/// the identity element or error.WeakPublicKey if the public
|
/// the identity element or error.WeakPublicKey if the public
|
||||||
/// key is a low-order point.
|
/// 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;
|
const cofactor = [_]u8{8} ++ [_]u8{0} ** 31;
|
||||||
_ = ladder(p, cofactor, 4) catch return error.WeakPublicKey;
|
_ = ladder(p, cofactor, 4) catch return error.WeakPublicKey;
|
||||||
return try ladder(p, s, 256);
|
return try ladder(p, s, 256);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute the Curve25519 equivalent to an Edwards25519 point.
|
/// 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();
|
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)
|
const x = one.add(p.y).mul(one.sub(p.y).invert()); // xMont=(1+yEd)/(1-yEd)
|
||||||
return Curve25519{ .x = x };
|
return Curve25519{ .x = x };
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,15 @@ const crypto = std.crypto;
|
||||||
const debug = std.debug;
|
const debug = std.debug;
|
||||||
const fmt = std.fmt;
|
const fmt = std.fmt;
|
||||||
const mem = std.mem;
|
const mem = std.mem;
|
||||||
|
|
||||||
const Sha512 = crypto.hash.sha2.Sha512;
|
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.
|
/// Ed25519 (EdDSA) signatures.
|
||||||
pub const Ed25519 = struct {
|
pub const Ed25519 = struct {
|
||||||
|
|
@ -41,7 +48,7 @@ pub const Ed25519 = struct {
|
||||||
///
|
///
|
||||||
/// For this reason, an EdDSA secret key is commonly called a seed,
|
/// For this reason, an EdDSA secret key is commonly called a seed,
|
||||||
/// from which the actual secret is derived.
|
/// 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: {
|
const ss = seed orelse ss: {
|
||||||
var random_seed: [seed_length]u8 = undefined;
|
var random_seed: [seed_length]u8 = undefined;
|
||||||
crypto.random.bytes(&random_seed);
|
crypto.random.bytes(&random_seed);
|
||||||
|
|
@ -51,7 +58,7 @@ pub const Ed25519 = struct {
|
||||||
var h = Sha512.init(.{});
|
var h = Sha512.init(.{});
|
||||||
h.update(&ss);
|
h.update(&ss);
|
||||||
h.final(&az);
|
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;
|
var sk: [secret_length]u8 = undefined;
|
||||||
mem.copy(u8, &sk, &ss);
|
mem.copy(u8, &sk, &ss);
|
||||||
const pk = p.toBytes();
|
const pk = p.toBytes();
|
||||||
|
|
@ -72,7 +79,7 @@ pub const Ed25519 = struct {
|
||||||
/// Sign a message using a key pair, and optional random noise.
|
/// Sign a message using a key pair, and optional random noise.
|
||||||
/// Having noise creates non-standard, non-deterministic signatures,
|
/// Having noise creates non-standard, non-deterministic signatures,
|
||||||
/// but has been proven to increase resilience against fault attacks.
|
/// 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 seed = key_pair.secret_key[0..seed_length];
|
||||||
const public_key = key_pair.secret_key[seed_length..];
|
const public_key = key_pair.secret_key[seed_length..];
|
||||||
if (!mem.eql(u8, public_key, &key_pair.public_key)) {
|
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.
|
/// Verify an Ed25519 signature given a message and a public key.
|
||||||
/// Returns error.SignatureVerificationFailed is the signature verification failed.
|
/// 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 r = sig[0..32];
|
||||||
const s = sig[32..64];
|
const s = sig[32..64];
|
||||||
try Curve.scalar.rejectNonCanonical(s.*);
|
try Curve.scalar.rejectNonCanonical(s.*);
|
||||||
|
|
@ -122,6 +129,7 @@ pub const Ed25519 = struct {
|
||||||
try a.rejectIdentity();
|
try a.rejectIdentity();
|
||||||
try Curve.rejectNonCanonical(r.*);
|
try Curve.rejectNonCanonical(r.*);
|
||||||
const expected_r = try Curve.fromBytes(r.*);
|
const expected_r = try Curve.fromBytes(r.*);
|
||||||
|
try expected_r.rejectIdentity();
|
||||||
|
|
||||||
var h = Sha512.init(.{});
|
var h = Sha512.init(.{});
|
||||||
h.update(r);
|
h.update(r);
|
||||||
|
|
@ -131,8 +139,7 @@ pub const Ed25519 = struct {
|
||||||
h.final(&hram64);
|
h.final(&hram64);
|
||||||
const hram = Curve.scalar.reduce64(hram64);
|
const hram = Curve.scalar.reduce64(hram64);
|
||||||
|
|
||||||
const ah = try a.neg().mulPublic(hram);
|
const sb_ah = try Curve.basePoint.mulDoubleBasePublic(s.*, a.neg(), hram);
|
||||||
const sb_ah = (try Curve.basePoint.mulPublic(s.*)).add(ah);
|
|
||||||
if (expected_r.sub(sb_ah).clearCofactor().rejectIdentity()) |_| {
|
if (expected_r.sub(sb_ah).clearCofactor().rejectIdentity()) |_| {
|
||||||
return error.SignatureVerificationFailed;
|
return error.SignatureVerificationFailed;
|
||||||
} else |_| {}
|
} else |_| {}
|
||||||
|
|
@ -146,7 +153,7 @@ pub const Ed25519 = struct {
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Verify several signatures in a single operation, much faster than verifying signatures one-by-one
|
/// 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 r_batch: [count][32]u8 = undefined;
|
||||||
var s_batch: [count][32]u8 = undefined;
|
var s_batch: [count][32]u8 = undefined;
|
||||||
var a_batch: [count]Curve = undefined;
|
var a_batch: [count]Curve = undefined;
|
||||||
|
|
@ -161,6 +168,7 @@ pub const Ed25519 = struct {
|
||||||
try a.rejectIdentity();
|
try a.rejectIdentity();
|
||||||
try Curve.rejectNonCanonical(r.*);
|
try Curve.rejectNonCanonical(r.*);
|
||||||
const expected_r = try Curve.fromBytes(r.*);
|
const expected_r = try Curve.fromBytes(r.*);
|
||||||
|
try expected_r.rejectIdentity();
|
||||||
expected_r_batch[i] = expected_r;
|
expected_r_batch[i] = expected_r;
|
||||||
r_batch[i] = r.*;
|
r_batch[i] = r.*;
|
||||||
s_batch[i] = s.*;
|
s_batch[i] = s.*;
|
||||||
|
|
@ -180,7 +188,7 @@ pub const Ed25519 = struct {
|
||||||
|
|
||||||
var z_batch: [count]Curve.scalar.CompressedScalar = undefined;
|
var z_batch: [count]Curve.scalar.CompressedScalar = undefined;
|
||||||
for (z_batch) |*z| {
|
for (z_batch) |*z| {
|
||||||
std.crypto.random.bytes(z[0..16]);
|
crypto.random.bytes(z[0..16]);
|
||||||
mem.set(u8, z[16..], 0);
|
mem.set(u8, z[16..], 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -233,8 +241,8 @@ test "ed25519 batch verification" {
|
||||||
const key_pair = try Ed25519.KeyPair.create(null);
|
const key_pair = try Ed25519.KeyPair.create(null);
|
||||||
var msg1: [32]u8 = undefined;
|
var msg1: [32]u8 = undefined;
|
||||||
var msg2: [32]u8 = undefined;
|
var msg2: [32]u8 = undefined;
|
||||||
std.crypto.random.bytes(&msg1);
|
crypto.random.bytes(&msg1);
|
||||||
std.crypto.random.bytes(&msg2);
|
crypto.random.bytes(&msg2);
|
||||||
const sig1 = try Ed25519.sign(&msg1, key_pair, null);
|
const sig1 = try Ed25519.sign(&msg1, key_pair, null);
|
||||||
const sig2 = try Ed25519.sign(&msg2, key_pair, null);
|
const sig2 = try Ed25519.sign(&msg2, key_pair, null);
|
||||||
var signature_batch = [_]Ed25519.BatchElement{
|
var signature_batch = [_]Ed25519.BatchElement{
|
||||||
|
|
@ -317,13 +325,13 @@ test "ed25519 test vectors" {
|
||||||
.msg_hex = "9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41",
|
.msg_hex = "9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41",
|
||||||
.public_key_hex = "f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43",
|
.public_key_hex = "f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43",
|
||||||
.sig_hex = "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03be9678ac102edcd92b0210bb34d7428d12ffc5df5f37e359941266a4e35f0f",
|
.sig_hex = "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03be9678ac102edcd92b0210bb34d7428d12ffc5df5f37e359941266a4e35f0f",
|
||||||
.expected = error.SignatureVerificationFailed, // 8 - non-canonical R
|
.expected = error.IdentityElement, // 8 - non-canonical R
|
||||||
},
|
},
|
||||||
Vec{
|
Vec{
|
||||||
.msg_hex = "9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41",
|
.msg_hex = "9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41",
|
||||||
.public_key_hex = "f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43",
|
.public_key_hex = "f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43",
|
||||||
.sig_hex = "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffca8c5b64cd208982aa38d4936621a4775aa233aa0505711d8fdcfdaa943d4908",
|
.sig_hex = "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffca8c5b64cd208982aa38d4936621a4775aa233aa0505711d8fdcfdaa943d4908",
|
||||||
.expected = null, // 9 - non-canonical R
|
.expected = error.IdentityElement, // 9 - non-canonical R
|
||||||
},
|
},
|
||||||
Vec{
|
Vec{
|
||||||
.msg_hex = "e96b7021eb39c1a163b6da4e3093dcd3f21387da4cc4572be588fafae23c155b",
|
.msg_hex = "e96b7021eb39c1a163b6da4e3093dcd3f21387da4cc4572be588fafae23c155b",
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,16 @@
|
||||||
// The MIT license requires this copyright notice to be included in all copies
|
// The MIT license requires this copyright notice to be included in all copies
|
||||||
// and substantial portions of the software.
|
// and substantial portions of the software.
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const crypto = std.crypto;
|
||||||
const debug = std.debug;
|
const debug = std.debug;
|
||||||
const fmt = std.fmt;
|
const fmt = std.fmt;
|
||||||
const mem = std.mem;
|
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.
|
/// Group operations over Edwards25519.
|
||||||
pub const Edwards25519 = struct {
|
pub const Edwards25519 = struct {
|
||||||
|
|
@ -26,7 +32,7 @@ pub const Edwards25519 = struct {
|
||||||
is_base: bool = false,
|
is_base: bool = false,
|
||||||
|
|
||||||
/// Decode an Edwards25519 point from its compressed (Y+sign) coordinates.
|
/// 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 z = Fe.one;
|
||||||
const y = Fe.fromBytes(s);
|
const y = Fe.fromBytes(s);
|
||||||
var u = y.sq();
|
var u = y.sq();
|
||||||
|
|
@ -56,7 +62,7 @@ pub const Edwards25519 = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check that the encoding of a point is canonical.
|
/// 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);
|
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 };
|
const identityElement = Edwards25519{ .x = Fe.zero, .y = Fe.one, .z = Fe.one, .t = Fe.zero };
|
||||||
|
|
||||||
/// Reject the neutral element.
|
/// Reject the neutral element.
|
||||||
pub fn rejectIdentity(p: Edwards25519) Error!void {
|
pub fn rejectIdentity(p: Edwards25519) IdentityElementError!void {
|
||||||
if (p.x.isZero()) {
|
if (p.x.isZero()) {
|
||||||
return error.IdentityElement;
|
return error.IdentityElement;
|
||||||
}
|
}
|
||||||
|
|
@ -177,7 +183,7 @@ pub const Edwards25519 = struct {
|
||||||
// Based on real-world benchmarks, we only use this for multi-scalar multiplication.
|
// 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
|
// NAF could be useful to half the size of precomputation tables, but we intentionally
|
||||||
// avoid these to keep the standard library lightweight.
|
// 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);
|
std.debug.assert(vartime);
|
||||||
const e = nonAdjacentForm(s);
|
const e = nonAdjacentForm(s);
|
||||||
var q = Edwards25519.identityElement;
|
var q = Edwards25519.identityElement;
|
||||||
|
|
@ -197,7 +203,7 @@ pub const Edwards25519 = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scalar multiplication with a 4-bit window and the first 15 multiples.
|
// 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 q = Edwards25519.identityElement;
|
||||||
var pos: usize = 252;
|
var pos: usize = 252;
|
||||||
while (true) : (pos -= 4) {
|
while (true) : (pos -= 4) {
|
||||||
|
|
@ -232,10 +238,15 @@ pub const Edwards25519 = struct {
|
||||||
break :pc precompute(Edwards25519.basePoint, 15);
|
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.
|
/// Multiply an Edwards25519 point by a scalar without clamping it.
|
||||||
/// Return error.WeakPublicKey if the resulting point is
|
/// Return error.WeakPublicKey if the base generates a small-order group,
|
||||||
/// the identity element.
|
/// and error.IdentityElement if the result is the identity element.
|
||||||
pub fn mul(p: Edwards25519, s: [32]u8) Error!Edwards25519 {
|
pub fn mul(p: Edwards25519, s: [32]u8) (IdentityElementError || WeakPublicKeyError)!Edwards25519 {
|
||||||
const pc = if (p.is_base) basePointPc else pc: {
|
const pc = if (p.is_base) basePointPc else pc: {
|
||||||
const xpc = precompute(p, 15);
|
const xpc = precompute(p, 15);
|
||||||
xpc[4].rejectIdentity() catch return error.WeakPublicKey;
|
xpc[4].rejectIdentity() catch return error.WeakPublicKey;
|
||||||
|
|
@ -246,7 +257,7 @@ pub const Edwards25519 = struct {
|
||||||
|
|
||||||
/// Multiply an Edwards25519 point by a *PUBLIC* scalar *IN VARIABLE TIME*
|
/// Multiply an Edwards25519 point by a *PUBLIC* scalar *IN VARIABLE TIME*
|
||||||
/// This can be used for signature verification.
|
/// 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) {
|
if (p.is_base) {
|
||||||
return pcMul16(basePointPc, s, true);
|
return pcMul16(basePointPc, s, true);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -256,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
|
/// Multiscalar multiplication *IN VARIABLE TIME* for public data
|
||||||
/// Computes ps0*ss0 + ps1*ss1 + ps2*ss2... faster than doing many of these operations individually
|
/// 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;
|
var pcs: [count][9]Edwards25519 = undefined;
|
||||||
for (ps) |p, i| {
|
for (ps) |p, i| {
|
||||||
if (p.is_base) {
|
if (p.is_base) {
|
||||||
@setEvalBranchQuota(10000);
|
pcs[i] = basePointPc8;
|
||||||
pcs[i] = comptime precompute(Edwards25519.basePoint, 8);
|
|
||||||
} else {
|
} else {
|
||||||
pcs[i] = precompute(p, 8);
|
pcs[i] = precompute(p, 8);
|
||||||
pcs[i][4].rejectIdentity() catch return error.WeakPublicKey;
|
pcs[i][4].rejectIdentity() catch return error.WeakPublicKey;
|
||||||
|
|
@ -297,14 +344,14 @@ pub const Edwards25519 = struct {
|
||||||
/// This is strongly recommended for DH operations.
|
/// This is strongly recommended for DH operations.
|
||||||
/// Return error.WeakPublicKey if the resulting point is
|
/// Return error.WeakPublicKey if the resulting point is
|
||||||
/// the identity element.
|
/// 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;
|
var t: [32]u8 = s;
|
||||||
scalar.clamp(&t);
|
scalar.clamp(&t);
|
||||||
return mul(p, t);
|
return mul(p, t);
|
||||||
}
|
}
|
||||||
|
|
||||||
// montgomery -- recover y = sqrt(x^3 + A*x^2 + x)
|
// 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();
|
var x2 = x.sq();
|
||||||
const x3 = x.mul(x2);
|
const x3 = x.mul(x2);
|
||||||
x2 = x2.mul32(Fe.edwards25519a_32);
|
x2 = x2.mul32(Fe.edwards25519a_32);
|
||||||
|
|
@ -367,7 +414,7 @@ pub const Edwards25519 = struct {
|
||||||
|
|
||||||
fn stringToPoints(comptime n: usize, ctx: []const u8, s: []const u8) [n]Edwards25519 {
|
fn stringToPoints(comptime n: usize, ctx: []const u8, s: []const u8) [n]Edwards25519 {
|
||||||
debug.assert(n <= 2);
|
debug.assert(n <= 2);
|
||||||
const H = std.crypto.hash.sha2.Sha512;
|
const H = crypto.hash.sha2.Sha512;
|
||||||
const h_l: usize = 48;
|
const h_l: usize = 48;
|
||||||
var xctx = ctx;
|
var xctx = ctx;
|
||||||
var hctx: [H.digest_length]u8 = undefined;
|
var hctx: [H.digest_length]u8 = undefined;
|
||||||
|
|
@ -485,8 +532,8 @@ test "edwards25519 packing/unpacking" {
|
||||||
test "edwards25519 point addition/substraction" {
|
test "edwards25519 point addition/substraction" {
|
||||||
var s1: [32]u8 = undefined;
|
var s1: [32]u8 = undefined;
|
||||||
var s2: [32]u8 = undefined;
|
var s2: [32]u8 = undefined;
|
||||||
std.crypto.random.bytes(&s1);
|
crypto.random.bytes(&s1);
|
||||||
std.crypto.random.bytes(&s2);
|
crypto.random.bytes(&s2);
|
||||||
const p = try Edwards25519.basePoint.clampedMul(s1);
|
const p = try Edwards25519.basePoint.clampedMul(s1);
|
||||||
const q = try Edwards25519.basePoint.clampedMul(s2);
|
const q = try Edwards25519.basePoint.clampedMul(s2);
|
||||||
const r = p.add(q).add(q).sub(q).sub(q);
|
const r = p.add(q).add(q).sub(q).sub(q);
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,12 @@
|
||||||
// The MIT license requires this copyright notice to be included in all copies
|
// The MIT license requires this copyright notice to be included in all copies
|
||||||
// and substantial portions of the software.
|
// and substantial portions of the software.
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const crypto = std.crypto;
|
||||||
const readIntLittle = std.mem.readIntLittle;
|
const readIntLittle = std.mem.readIntLittle;
|
||||||
const writeIntLittle = std.mem.writeIntLittle;
|
const writeIntLittle = std.mem.writeIntLittle;
|
||||||
const Error = std.crypto.Error;
|
|
||||||
|
const NonCanonicalError = crypto.errors.NonCanonicalError;
|
||||||
|
const NotSquareError = crypto.errors.NotSquareError;
|
||||||
|
|
||||||
pub const Fe = struct {
|
pub const Fe = struct {
|
||||||
limbs: [5]u64,
|
limbs: [5]u64,
|
||||||
|
|
@ -113,7 +116,7 @@ pub const Fe = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reject non-canonical encodings of an element, possibly ignoring the top bit
|
/// 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;
|
var c: u16 = (s[31] & 0x7f) ^ 0x7f;
|
||||||
comptime var i = 30;
|
comptime var i = 30;
|
||||||
inline while (i > 0) : (i -= 1) {
|
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
|
/// 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;
|
var x2_copy = x2;
|
||||||
const x = x2.uncheckedSqrt();
|
const x = x2.uncheckedSqrt();
|
||||||
const check = x.sq().sub(x2_copy);
|
const check = x.sq().sub(x2_copy);
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,11 @@
|
||||||
// and substantial portions of the software.
|
// and substantial portions of the software.
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const fmt = std.fmt;
|
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.
|
/// Group operations over Edwards25519.
|
||||||
pub const Ristretto255 = struct {
|
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() };
|
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) {
|
if ((s[0] & 1) != 0) {
|
||||||
return error.NonCanonical;
|
return error.NonCanonical;
|
||||||
}
|
}
|
||||||
|
|
@ -43,7 +47,7 @@ pub const Ristretto255 = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reject the neutral element.
|
/// 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();
|
return p.p.rejectIdentity();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -51,7 +55,7 @@ pub const Ristretto255 = struct {
|
||||||
pub const basePoint = Ristretto255{ .p = Curve.basePoint };
|
pub const basePoint = Ristretto255{ .p = Curve.basePoint };
|
||||||
|
|
||||||
/// Decode a Ristretto255 representative.
|
/// 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);
|
try rejectNonCanonical(s);
|
||||||
const s_ = Fe.fromBytes(s);
|
const s_ = Fe.fromBytes(s);
|
||||||
const ss = s_.sq(); // s^2
|
const ss = s_.sq(); // s^2
|
||||||
|
|
@ -154,7 +158,7 @@ pub const Ristretto255 = struct {
|
||||||
/// Multiply a Ristretto255 element with a scalar.
|
/// Multiply a Ristretto255 element with a scalar.
|
||||||
/// Return error.WeakPublicKey if the resulting element is
|
/// Return error.WeakPublicKey if the resulting element is
|
||||||
/// the identity element.
|
/// 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) };
|
return Ristretto255{ .p = try p.p.mul(s) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,8 @@
|
||||||
// and substantial portions of the software.
|
// and substantial portions of the software.
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const mem = std.mem;
|
const mem = std.mem;
|
||||||
const Error = std.crypto.Error;
|
|
||||||
|
const NonCanonicalError = std.crypto.errors.NonCanonicalError;
|
||||||
|
|
||||||
/// 2^252 + 27742317777372353535851937790883648493
|
/// 2^252 + 27742317777372353535851937790883648493
|
||||||
pub const field_size = [32]u8{
|
pub const field_size = [32]u8{
|
||||||
|
|
@ -19,7 +20,7 @@ pub const CompressedScalar = [32]u8;
|
||||||
pub const zero = [_]u8{0} ** 32;
|
pub const zero = [_]u8{0} ** 32;
|
||||||
|
|
||||||
/// Reject a scalar whose encoding is not canonical.
|
/// 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 c: u8 = 0;
|
||||||
var n: u8 = 1;
|
var n: u8 = 1;
|
||||||
var i: usize = 31;
|
var i: usize = 31;
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,10 @@ const mem = std.mem;
|
||||||
const fmt = std.fmt;
|
const fmt = std.fmt;
|
||||||
|
|
||||||
const Sha512 = crypto.hash.sha2.Sha512;
|
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.
|
/// X25519 DH function.
|
||||||
pub const X25519 = struct {
|
pub const X25519 = struct {
|
||||||
|
|
@ -32,7 +35,7 @@ pub const X25519 = struct {
|
||||||
secret_key: [secret_length]u8,
|
secret_key: [secret_length]u8,
|
||||||
|
|
||||||
/// Create a new key pair using an optional seed.
|
/// 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: {
|
const sk = seed orelse sk: {
|
||||||
var random_seed: [seed_length]u8 = undefined;
|
var random_seed: [seed_length]u8 = undefined;
|
||||||
crypto.random.bytes(&random_seed);
|
crypto.random.bytes(&random_seed);
|
||||||
|
|
@ -45,7 +48,7 @@ pub const X25519 = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a key pair from an Ed25519 key pair
|
/// 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];
|
const seed = ed25519_key_pair.secret_key[0..32];
|
||||||
var az: [Sha512.digest_length]u8 = undefined;
|
var az: [Sha512.digest_length]u8 = undefined;
|
||||||
Sha512.hash(seed, &az, .{});
|
Sha512.hash(seed, &az, .{});
|
||||||
|
|
@ -60,13 +63,13 @@ pub const X25519 = struct {
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Compute the public key for a given private key.
|
/// 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);
|
const q = try Curve.basePoint.clampedMul(secret_key);
|
||||||
return q.toBytes();
|
return q.toBytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute the X25519 equivalent to an Ed25519 public eky.
|
/// 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_ed = try crypto.ecc.Edwards25519.fromBytes(ed25519_public_key);
|
||||||
const pk = try Curve.fromEdwards25519(pk_ed);
|
const pk = try Curve.fromEdwards25519(pk_ed);
|
||||||
return pk.toBytes();
|
return pk.toBytes();
|
||||||
|
|
@ -75,7 +78,7 @@ pub const X25519 = struct {
|
||||||
/// Compute the scalar product of a public key and a secret scalar.
|
/// 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
|
/// Note that the output should not be used as a shared secret without
|
||||||
/// hashing it first.
|
/// 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);
|
const q = try Curve.fromBytes(public_key).clampedMul(secret_key);
|
||||||
return q.toBytes();
|
return q.toBytes();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ const std = @import("std");
|
||||||
const mem = std.mem;
|
const mem = std.mem;
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
const AesBlock = std.crypto.core.aes.Block;
|
const AesBlock = std.crypto.core.aes.Block;
|
||||||
const Error = std.crypto.Error;
|
const AuthenticationError = std.crypto.errors.AuthenticationError;
|
||||||
|
|
||||||
const State128L = struct {
|
const State128L = struct {
|
||||||
blocks: [8]AesBlock,
|
blocks: [8]AesBlock,
|
||||||
|
|
@ -137,7 +137,7 @@ pub const Aegis128L = struct {
|
||||||
/// ad: Associated Data
|
/// ad: Associated Data
|
||||||
/// npub: public nonce
|
/// npub: public nonce
|
||||||
/// k: private key
|
/// 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);
|
assert(c.len == m.len);
|
||||||
var state = State128L.init(key, npub);
|
var state = State128L.init(key, npub);
|
||||||
var src: [32]u8 align(16) = undefined;
|
var src: [32]u8 align(16) = undefined;
|
||||||
|
|
@ -299,7 +299,7 @@ pub const Aegis256 = struct {
|
||||||
/// ad: Associated Data
|
/// ad: Associated Data
|
||||||
/// npub: public nonce
|
/// npub: public nonce
|
||||||
/// k: private key
|
/// 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);
|
assert(c.len == m.len);
|
||||||
var state = State256.init(key, npub);
|
var state = State256.init(key, npub);
|
||||||
var src: [16]u8 align(16) = undefined;
|
var src: [16]u8 align(16) = undefined;
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ const debug = std.debug;
|
||||||
const Ghash = std.crypto.onetimeauth.Ghash;
|
const Ghash = std.crypto.onetimeauth.Ghash;
|
||||||
const mem = std.mem;
|
const mem = std.mem;
|
||||||
const modes = crypto.core.modes;
|
const modes = crypto.core.modes;
|
||||||
const Error = crypto.Error;
|
const AuthenticationError = crypto.errors.AuthenticationError;
|
||||||
|
|
||||||
pub const Aes128Gcm = AesGcm(crypto.core.aes.Aes128);
|
pub const Aes128Gcm = AesGcm(crypto.core.aes.Aes128);
|
||||||
pub const Aes256Gcm = AesGcm(crypto.core.aes.Aes256);
|
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);
|
assert(c.len == m.len);
|
||||||
|
|
||||||
const aes = Aes.initEnc(key);
|
const aes = Aes.initEnc(key);
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ const aes = crypto.core.aes;
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
const math = std.math;
|
const math = std.math;
|
||||||
const mem = std.mem;
|
const mem = std.mem;
|
||||||
const Error = crypto.Error;
|
const AuthenticationError = crypto.errors.AuthenticationError;
|
||||||
|
|
||||||
pub const Aes128Ocb = AesOcb(aes.Aes128);
|
pub const Aes128Ocb = AesOcb(aes.Aes128);
|
||||||
pub const Aes256Ocb = AesOcb(aes.Aes256);
|
pub const Aes256Ocb = AesOcb(aes.Aes256);
|
||||||
|
|
@ -179,7 +179,7 @@ fn AesOcb(comptime Aes: anytype) type {
|
||||||
/// ad: Associated Data
|
/// ad: Associated Data
|
||||||
/// npub: public nonce
|
/// npub: public nonce
|
||||||
/// k: secret key
|
/// 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);
|
assert(c.len == m.len);
|
||||||
|
|
||||||
const aes_enc_ctx = Aes.initEnc(key);
|
const aes_enc_ctx = Aes.initEnc(key);
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,8 @@ const mem = std.mem;
|
||||||
const debug = std.debug;
|
const debug = std.debug;
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
const utils = crypto.utils;
|
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_length: usize = 16;
|
||||||
const salt_str_length: usize = 22;
|
const salt_str_length: usize = 22;
|
||||||
|
|
@ -179,7 +180,7 @@ const Codec = struct {
|
||||||
debug.assert(j == b64.len);
|
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 i: usize = 0;
|
||||||
var j: usize = 0;
|
var j: usize = 0;
|
||||||
while (j < bin.len) {
|
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 state = State{};
|
||||||
var password_buf: [73]u8 = undefined;
|
var password_buf: [73]u8 = undefined;
|
||||||
const trimmed_len = math.min(password.len, password_buf.len - 1);
|
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.
|
/// 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,
|
/// 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.
|
/// 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;
|
var salt: [salt_length]u8 = undefined;
|
||||||
crypto.random.bytes(&salt);
|
crypto.random.bytes(&salt);
|
||||||
return strHashInternal(password, rounds_log, salt);
|
return strHashInternal(password, rounds_log, salt);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify that a previously computed hash is valid for a given password.
|
/// 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 (!mem.eql(u8, "$2", h[0..2])) return error.InvalidEncoding;
|
||||||
if (h[3] != '$' or h[6] != '$') return error.InvalidEncoding;
|
if (h[3] != '$' or h[6] != '$') return error.InvalidEncoding;
|
||||||
const rounds_log_str = h[4..][0..2];
|
const rounds_log_str = h[4..][0..2];
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ const testing = std.testing;
|
||||||
const maxInt = math.maxInt;
|
const maxInt = math.maxInt;
|
||||||
const Vector = std.meta.Vector;
|
const Vector = std.meta.Vector;
|
||||||
const Poly1305 = std.crypto.onetimeauth.Poly1305;
|
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.
|
/// IETF-variant of the ChaCha20 stream cipher, as designed for TLS.
|
||||||
pub const ChaCha20IETF = ChaChaIETF(20);
|
pub const ChaCha20IETF = ChaChaIETF(20);
|
||||||
|
|
@ -521,7 +521,7 @@ fn ChaChaPoly1305(comptime rounds_nb: usize) type {
|
||||||
/// npub: public nonce
|
/// npub: public nonce
|
||||||
/// k: private key
|
/// k: private key
|
||||||
/// NOTE: the check of the authentication tag is currently not done in constant time
|
/// 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);
|
assert(c.len == m.len);
|
||||||
|
|
||||||
var polyKey = [_]u8{0} ** 32;
|
var polyKey = [_]u8{0} ** 32;
|
||||||
|
|
@ -583,7 +583,7 @@ fn XChaChaPoly1305(comptime rounds_nb: usize) type {
|
||||||
/// ad: Associated Data
|
/// ad: Associated Data
|
||||||
/// npub: public nonce
|
/// npub: public nonce
|
||||||
/// k: private key
|
/// 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);
|
const extended = extend(k, npub, rounds_nb);
|
||||||
return ChaChaPoly1305(rounds_nb).decrypt(m, c, tag, ad, extended.nonce, extended.key);
|
return ChaChaPoly1305(rounds_nb).decrypt(m, c, tag, ad, extended.nonce, extended.key);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
|
||||||
};
|
|
||||||
35
lib/std/crypto/errors.zig
Normal file
35
lib/std/crypto/errors.zig
Normal file
|
|
@ -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;
|
||||||
|
|
@ -20,7 +20,7 @@ const assert = std.debug.assert;
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
const htest = @import("test.zig");
|
const htest = @import("test.zig");
|
||||||
const Vector = std.meta.Vector;
|
const Vector = std.meta.Vector;
|
||||||
const Error = std.crypto.Error;
|
const AuthenticationError = std.crypto.errors.AuthenticationError;
|
||||||
|
|
||||||
pub const State = struct {
|
pub const State = struct {
|
||||||
pub const BLOCKBYTES = 48;
|
pub const BLOCKBYTES = 48;
|
||||||
|
|
@ -393,7 +393,7 @@ pub const Aead = struct {
|
||||||
/// npub: public nonce
|
/// npub: public nonce
|
||||||
/// k: private key
|
/// k: private key
|
||||||
/// NOTE: the check of the authentication tag is currently not done in constant time
|
/// 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);
|
assert(c.len == m.len);
|
||||||
|
|
||||||
var state = Aead.init(ad, npub, k);
|
var state = Aead.init(ad, npub, k);
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ const debug = std.debug;
|
||||||
const mem = std.mem;
|
const mem = std.mem;
|
||||||
const math = std.math;
|
const math = std.math;
|
||||||
const testing = std.testing;
|
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.
|
/// 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
|
/// 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);
|
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 computed_tag = mac(c, ad, npub, key);
|
||||||
var acc: u8 = 0;
|
var acc: u8 = 0;
|
||||||
for (computed_tag) |_, j| {
|
for (computed_tag) |_, j| {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,8 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const mem = std.mem;
|
const mem = std.mem;
|
||||||
const maxInt = std.math.maxInt;
|
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
|
// 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.
|
/// 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`.
|
/// 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;
|
if (rounds < 1) return error.WeakParameters;
|
||||||
|
|
||||||
const dk_len = dk.len;
|
const dk_len = dk.len;
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,10 @@ const Vector = std.meta.Vector;
|
||||||
const Poly1305 = crypto.onetimeauth.Poly1305;
|
const Poly1305 = crypto.onetimeauth.Poly1305;
|
||||||
const Blake2b = crypto.hash.blake2.Blake2b;
|
const Blake2b = crypto.hash.blake2.Blake2b;
|
||||||
const X25519 = crypto.dh.X25519;
|
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 Salsa20VecImpl = struct {
|
||||||
const Lane = Vector(4, u32);
|
const Lane = Vector(4, u32);
|
||||||
|
|
@ -399,7 +402,7 @@ pub const XSalsa20Poly1305 = struct {
|
||||||
/// ad: Associated Data
|
/// ad: Associated Data
|
||||||
/// npub: public nonce
|
/// npub: public nonce
|
||||||
/// k: private key
|
/// 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);
|
debug.assert(c.len == m.len);
|
||||||
const extended = extend(k, npub);
|
const extended = extend(k, npub);
|
||||||
var block0 = [_]u8{0} ** 64;
|
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`.
|
/// 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.
|
/// `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) {
|
if (c.len < tag_length) {
|
||||||
return error.AuthenticationFailed;
|
return error.AuthenticationFailed;
|
||||||
}
|
}
|
||||||
|
|
@ -482,20 +485,20 @@ pub const Box = struct {
|
||||||
pub const KeyPair = X25519.KeyPair;
|
pub const KeyPair = X25519.KeyPair;
|
||||||
|
|
||||||
/// Compute a secret suitable for `secretbox` given a recipent's public key and a sender's secret key.
|
/// 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 p = try X25519.scalarmult(secret_key, public_key);
|
||||||
const zero = [_]u8{0} ** 16;
|
const zero = [_]u8{0} ** 16;
|
||||||
return Salsa20Impl.hsalsa20(zero, p);
|
return Salsa20Impl.hsalsa20(zero, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encrypt and authenticate a message using a recipient's public key `public_key` and a sender's `secret_key`.
|
/// 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);
|
const shared_key = try createSharedSecret(public_key, secret_key);
|
||||||
return SecretBox.seal(c, m, npub, shared_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`.
|
/// 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);
|
const shared_key = try createSharedSecret(public_key, secret_key);
|
||||||
return SecretBox.open(m, c, npub, shared_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`.
|
/// 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.
|
/// `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);
|
debug.assert(c.len == m.len + seal_length);
|
||||||
var ekp = try KeyPair.create(null);
|
var ekp = try KeyPair.create(null);
|
||||||
const nonce = createNonce(ekp.public_key, public_key);
|
const nonce = createNonce(ekp.public_key, public_key);
|
||||||
|
|
@ -539,7 +542,7 @@ pub const SealedBox = struct {
|
||||||
|
|
||||||
/// Decrypt a message using a key pair.
|
/// Decrypt a message using a key pair.
|
||||||
/// `m` must be exactly `seal_length` bytes smaller than `c`, as `c` also includes metadata.
|
/// `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) {
|
if (c.len < seal_length) {
|
||||||
return error.AuthenticationFailed;
|
return error.AuthenticationFailed;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -264,7 +264,7 @@ var shared_test_data = [1]i32{0} ** 10;
|
||||||
var shared_test_index: usize = 0;
|
var shared_test_index: usize = 0;
|
||||||
var shared_count: usize = 0;
|
var shared_count: usize = 0;
|
||||||
fn writeRunner(lock: *RwLock) callconv(.Async) void {
|
fn writeRunner(lock: *RwLock) callconv(.Async) void {
|
||||||
suspend; // resumed by onNextTick
|
suspend {} // resumed by onNextTick
|
||||||
|
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
while (i < shared_test_data.len) : (i += 1) {
|
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 {
|
fn readRunner(lock: *RwLock) callconv(.Async) void {
|
||||||
suspend; // resumed by onNextTick
|
suspend {} // resumed by onNextTick
|
||||||
std.time.sleep(1);
|
std.time.sleep(1);
|
||||||
|
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
|
|
|
||||||
|
|
@ -1349,15 +1349,6 @@ pub fn boolMask(comptime MaskInt: type, value: bool) callconv(.Inline) MaskInt {
|
||||||
return @bitCast(i1, @as(u1, @boolToInt(value)));
|
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));
|
return -%@intCast(MaskInt, @boolToInt(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,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 op = value;
|
||||||
var res: T = 0;
|
var res: T = 0;
|
||||||
var one: T = 1 << (@typeInfo(T).Int.bits - 2);
|
var one: T = 1 << (@typeInfo(T).Int.bits - 2);
|
||||||
|
|
@ -57,11 +63,13 @@ fn sqrt_int(comptime T: type, value: T) std.meta.Int(.unsigned, @typeInfo(T).Int
|
||||||
one >>= 2;
|
one >>= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ResultType = std.meta.Int(.unsigned, @typeInfo(T).Int.bits / 2);
|
const ResultType = Sqrt(T);
|
||||||
return @intCast(ResultType, res);
|
return @intCast(ResultType, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "math.sqrt_int" {
|
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, 3) == 1);
|
||||||
expect(sqrt_int(u32, 4) == 2);
|
expect(sqrt_int(u32, 4) == 2);
|
||||||
expect(sqrt_int(u32, 5) == 2);
|
expect(sqrt_int(u32, 5) == 2);
|
||||||
|
|
@ -73,7 +81,13 @@ test "math.sqrt_int" {
|
||||||
/// Returns the return type `sqrt` will return given an operand of type `T`.
|
/// Returns the return type `sqrt` will return given an operand of type `T`.
|
||||||
pub fn Sqrt(comptime T: type) type {
|
pub fn Sqrt(comptime T: type) type {
|
||||||
return switch (@typeInfo(T)) {
|
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,
|
else => T,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -884,7 +884,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.
|
/// 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.
|
/// This is for translate-c and is not intended for general use.
|
||||||
pub fn cast(comptime DestType: type, target: anytype) DestType {
|
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);
|
const SourceType = @TypeOf(target);
|
||||||
switch (@typeInfo(DestType)) {
|
switch (@typeInfo(DestType)) {
|
||||||
.Pointer => {
|
.Pointer => {
|
||||||
|
|
@ -921,9 +921,10 @@ pub fn cast(comptime DestType: type, target: anytype) DestType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.Enum => {
|
.Enum => |enum_type| {
|
||||||
if (@typeInfo(SourceType) == .Int or @typeInfo(SourceType) == .ComptimeInt) {
|
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 => {
|
.Int => {
|
||||||
|
|
@ -1011,6 +1012,17 @@ test "std.meta.cast" {
|
||||||
testing.expectEqual(@intToPtr(*u8, 2), cast(*u8, @intToPtr(*volatile u8, 2)));
|
testing.expectEqual(@intToPtr(*u8, 2), cast(*u8, @intToPtr(*volatile u8, 2)));
|
||||||
|
|
||||||
testing.expectEqual(@intToPtr(?*c_void, 2), cast(?*c_void, @intToPtr(*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.
|
/// Given a value returns its size as C's sizeof operator would.
|
||||||
|
|
|
||||||
|
|
@ -557,18 +557,10 @@ pub const kernel_stat = extern struct {
|
||||||
size: off_t,
|
size: off_t,
|
||||||
blksize: blksize_t,
|
blksize: blksize_t,
|
||||||
blocks: blkcnt_t,
|
blocks: blkcnt_t,
|
||||||
__atim32: timespec32,
|
|
||||||
__mtim32: timespec32,
|
|
||||||
__ctim32: timespec32,
|
|
||||||
__unused: [2]u32,
|
|
||||||
atim: timespec,
|
atim: timespec,
|
||||||
mtim: timespec,
|
mtim: timespec,
|
||||||
ctim: timespec,
|
ctim: timespec,
|
||||||
|
__unused: [2]u32,
|
||||||
const timespec32 = extern struct {
|
|
||||||
tv_sec: i32,
|
|
||||||
tv_nsec: i32,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn atime(self: @This()) timespec {
|
pub fn atime(self: @This()) timespec {
|
||||||
return self.atim;
|
return self.atim;
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ pub fn getauxval(index: usize) usize {
|
||||||
// Some architectures (and some syscalls) require 64bit parameters to be passed
|
// Some architectures (and some syscalls) require 64bit parameters to be passed
|
||||||
// in a even-aligned register pair.
|
// in a even-aligned register pair.
|
||||||
const require_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.isMIPS() or
|
||||||
std.Target.current.cpu.arch.isARM() or
|
std.Target.current.cpu.arch.isARM() or
|
||||||
std.Target.current.cpu.arch.isThumb();
|
std.Target.current.cpu.arch.isThumb();
|
||||||
|
|
@ -633,7 +634,7 @@ pub fn tkill(tid: pid_t, sig: i32) usize {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tgkill(tgid: pid_t, 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 {
|
pub fn link(oldpath: [*:0]const u8, newpath: [*:0]const u8, flags: i32) usize {
|
||||||
|
|
@ -1386,6 +1387,53 @@ pub fn madvise(address: [*]u8, len: usize, advice: u32) usize {
|
||||||
return syscall3(.madvise, @ptrToInt(address), len, advice);
|
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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
test {
|
||||||
if (std.Target.current.os.tag == .linux) {
|
if (std.Target.current.os.tag == .linux) {
|
||||||
_ = @import("linux/test.zig");
|
_ = @import("linux/test.zig");
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
const magic = 0xeb9f;
|
const magic = 0xeb9f;
|
||||||
const version = 1;
|
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
|
/// All offsets are in bytes relative to the end of this header
|
||||||
pub const Header = packed struct {
|
pub const Header = packed struct {
|
||||||
|
|
|
||||||
|
|
@ -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 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 var pfnMessageBoxW: @TypeOf(MessageBoxW) = undefined;
|
||||||
pub fn messageBoxW(hWnd: ?HWND, lpText: [*:0]const u16, lpCaption: [*:0]const u16, uType: u32) !i32 {
|
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);
|
const value = function(hWnd, lpText, lpCaption, uType);
|
||||||
if (value != 0) return value;
|
if (value != 0) return value;
|
||||||
switch (GetLastError()) {
|
switch (GetLastError()) {
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ test "strncpy" {
|
||||||
var s1: [9:0]u8 = undefined;
|
var s1: [9:0]u8 = undefined;
|
||||||
|
|
||||||
s1[0] = 0;
|
s1[0] = 0;
|
||||||
_ = strncpy(&s1, "foobarbaz", 9);
|
_ = strncpy(&s1, "foobarbaz", @sizeOf(@TypeOf(s1)));
|
||||||
std.testing.expectEqualSlices(u8, "foobarbaz", std.mem.spanZ(&s1));
|
std.testing.expectEqualSlices(u8, "foobarbaz", std.mem.spanZ(&s1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -242,7 +242,7 @@ export fn memcmp(vl: ?[*]const u8, vr: ?[*]const u8, n: usize) callconv(.C) isiz
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
test "test_memcmp" {
|
test "memcmp" {
|
||||||
const base_arr = &[_]u8{ 1, 1, 1 };
|
const base_arr = &[_]u8{ 1, 1, 1 };
|
||||||
const arr1 = &[_]u8{ 1, 1, 1 };
|
const arr1 = &[_]u8{ 1, 1, 1 };
|
||||||
const arr2 = &[_]u8{ 1, 0, 1 };
|
const arr2 = &[_]u8{ 1, 0, 1 };
|
||||||
|
|
@ -266,7 +266,7 @@ export fn bcmp(vl: [*]allowzero const u8, vr: [*]allowzero const u8, n: usize) c
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
test "test_bcmp" {
|
test "bcmp" {
|
||||||
const base_arr = &[_]u8{ 1, 1, 1 };
|
const base_arr = &[_]u8{ 1, 1, 1 };
|
||||||
const arr1 = &[_]u8{ 1, 1, 1 };
|
const arr1 = &[_]u8{ 1, 1, 1 };
|
||||||
const arr2 = &[_]u8{ 1, 0, 1 };
|
const arr2 = &[_]u8{ 1, 0, 1 };
|
||||||
|
|
@ -862,6 +862,85 @@ fn generic_fmod(comptime T: type, x: T, y: T) T {
|
||||||
return @bitCast(T, ux);
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
// 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
|
// 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.
|
// potentially some edge cases remaining that are not handled in the same way.
|
||||||
|
|
@ -996,25 +1075,32 @@ export fn sqrt(x: f64) f64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
test "sqrt" {
|
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);
|
// Note that @sqrt will either generate the sqrt opcode (if supported by the
|
||||||
std.testing.expect(std.math.approxEqAbs(f64, sqrt(2.0), 1.414214, epsilon));
|
// target ISA) or a call to `sqrtf` otherwise.
|
||||||
std.testing.expect(std.math.approxEqAbs(f64, sqrt(3.6), 1.897367, epsilon));
|
for (V) |val|
|
||||||
std.testing.expect(sqrt(4.0) == 2.0);
|
std.testing.expectEqual(@sqrt(val), sqrt(val));
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test "sqrt special" {
|
test "sqrt special" {
|
||||||
std.testing.expect(std.math.isPositiveInf(sqrt(std.math.inf(f64))));
|
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(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(isNan(sqrt(-1.0)));
|
||||||
std.testing.expect(std.math.isNan(sqrt(std.math.nan(f64))));
|
std.testing.expect(isNan(sqrt(std.math.nan(f64))));
|
||||||
}
|
}
|
||||||
|
|
||||||
export fn sqrtf(x: f32) f32 {
|
export fn sqrtf(x: f32) f32 {
|
||||||
|
|
@ -1094,23 +1180,30 @@ export fn sqrtf(x: f32) f32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
test "sqrtf" {
|
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);
|
// Note that @sqrt will either generate the sqrt opcode (if supported by the
|
||||||
std.testing.expect(std.math.approxEqAbs(f32, sqrtf(2.0), 1.414214, epsilon));
|
// target ISA) or a call to `sqrtf` otherwise.
|
||||||
std.testing.expect(std.math.approxEqAbs(f32, sqrtf(3.6), 1.897367, epsilon));
|
for (V) |val|
|
||||||
std.testing.expect(sqrtf(4.0) == 2.0);
|
std.testing.expectEqual(@sqrt(val), sqrtf(val));
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test "sqrtf special" {
|
test "sqrtf special" {
|
||||||
std.testing.expect(std.math.isPositiveInf(sqrtf(std.math.inf(f32))));
|
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(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(isNan(sqrtf(-1.0)));
|
||||||
std.testing.expect(std.math.isNan(sqrtf(std.math.nan(f32))));
|
std.testing.expect(isNan(sqrtf(std.math.nan(f32))));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -116,9 +116,11 @@ comptime {
|
||||||
@export(@import("compiler_rt/extendXfYf2.zig").__extenddftf2, .{ .name = "__extenddftf2", .linkage = linkage });
|
@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").__extendsftf2, .{ .name = "__extendsftf2", .linkage = linkage });
|
||||||
@export(@import("compiler_rt/extendXfYf2.zig").__extendhfsf2, .{ .name = "__extendhfsf2", .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").__truncsfhf2, .{ .name = "__truncsfhf2", .linkage = linkage });
|
||||||
@export(@import("compiler_rt/truncXfYf2.zig").__truncdfhf2, .{ .name = "__truncdfhf2", .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").__trunctfdf2, .{ .name = "__trunctfdf2", .linkage = linkage });
|
||||||
@export(@import("compiler_rt/truncXfYf2.zig").__trunctfsf2, .{ .name = "__trunctfsf2", .linkage = linkage });
|
@export(@import("compiler_rt/truncXfYf2.zig").__trunctfsf2, .{ .name = "__trunctfsf2", .linkage = linkage });
|
||||||
|
|
||||||
|
|
@ -299,7 +301,7 @@ comptime {
|
||||||
@export(@import("compiler_rt/sparc.zig")._Qp_qtod, .{ .name = "_Qp_qtod", .linkage = linkage });
|
@export(@import("compiler_rt/sparc.zig")._Qp_qtod, .{ .name = "_Qp_qtod", .linkage = linkage });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (arch == .powerpc or arch.isPPC64()) {
|
if ((arch == .powerpc or arch.isPPC64()) and !is_test) {
|
||||||
@export(@import("compiler_rt/addXf3.zig").__addtf3, .{ .name = "__addkf3", .linkage = linkage });
|
@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/addXf3.zig").__subtf3, .{ .name = "__subkf3", .linkage = linkage });
|
||||||
@export(@import("compiler_rt/mulXf3.zig").__multf3, .{ .name = "__mulkf3", .linkage = linkage });
|
@export(@import("compiler_rt/mulXf3.zig").__multf3, .{ .name = "__mulkf3", .linkage = linkage });
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,10 @@ pub fn __extendhfsf2(a: u16) callconv(.C) f32 {
|
||||||
return @call(.{ .modifier = .always_inline }, extendXfYf2, .{ f32, f16, a });
|
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 {
|
pub fn __aeabi_h2f(arg: u16) callconv(.AAPCS) f32 {
|
||||||
@setRuntimeSafety(false);
|
@setRuntimeSafety(false);
|
||||||
return @call(.{ .modifier = .always_inline }, __extendhfsf2, .{arg});
|
return @call(.{ .modifier = .always_inline }, __extendhfsf2, .{arg});
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,10 @@
|
||||||
// The MIT license requires this copyright notice to be included in all copies
|
// The MIT license requires this copyright notice to be included in all copies
|
||||||
// and substantial portions of the software.
|
// and substantial portions of the software.
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
const __extenddftf2 = @import("extendXfYf2.zig").__extenddftf2;
|
|
||||||
const __extendhfsf2 = @import("extendXfYf2.zig").__extendhfsf2;
|
const __extendhfsf2 = @import("extendXfYf2.zig").__extendhfsf2;
|
||||||
|
const __extendhftf2 = @import("extendXfYf2.zig").__extendhftf2;
|
||||||
const __extendsftf2 = @import("extendXfYf2.zig").__extendsftf2;
|
const __extendsftf2 = @import("extendXfYf2.zig").__extendsftf2;
|
||||||
|
const __extenddftf2 = @import("extendXfYf2.zig").__extenddftf2;
|
||||||
|
|
||||||
fn test__extenddftf2(a: f64, expectedHi: u64, expectedLo: u64) void {
|
fn test__extenddftf2(a: f64, expectedHi: u64, expectedLo: u64) void {
|
||||||
const x = __extenddftf2(a);
|
const x = __extenddftf2(a);
|
||||||
|
|
@ -161,3 +162,49 @@ fn makeNaN32(rand: u32) f32 {
|
||||||
fn makeInf32() f32 {
|
fn makeInf32() f32 {
|
||||||
return @bitCast(f32, @as(u32, 0x7f800000));
|
return @bitCast(f32, @as(u32, 0x7f800000));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
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(0x7e00, 0x7fff800000000000, 0x0);
|
||||||
|
// NaN
|
||||||
|
test__extendhftf2(0x7d00, 0x7fff400000000000, 0x0);
|
||||||
|
// inf
|
||||||
|
test__extendhftf2(0x7c00, 0x7fff000000000000, 0x0);
|
||||||
|
test__extendhftf2(0xfc00, 0xffff000000000000, 0x0);
|
||||||
|
// zero
|
||||||
|
test__extendhftf2(0x0000, 0x0000000000000000, 0x0);
|
||||||
|
test__extendhftf2(0x8000, 0x8000000000000000, 0x0);
|
||||||
|
// denormal
|
||||||
|
test__extendhftf2(0x0010, 0x3feb000000000000, 0x0);
|
||||||
|
test__extendhftf2(0x0001, 0x3fe7000000000000, 0x0);
|
||||||
|
test__extendhftf2(0x8001, 0xbfe7000000000000, 0x0);
|
||||||
|
|
||||||
|
// pi
|
||||||
|
test__extendhftf2(0x4248, 0x4000920000000000, 0x0);
|
||||||
|
test__extendhftf2(0xc248, 0xc000920000000000, 0x0);
|
||||||
|
|
||||||
|
test__extendhftf2(0x508c, 0x4004230000000000, 0x0);
|
||||||
|
test__extendhftf2(0x1bb7, 0x3ff6edc000000000, 0x0);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,10 @@ pub fn __truncdfhf2(a: f64) callconv(.C) u16 {
|
||||||
return @bitCast(u16, @call(.{ .modifier = .always_inline }, truncXfYf2, .{ f16, f64, a }));
|
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 {
|
pub fn __trunctfsf2(a: f128) callconv(.C) f32 {
|
||||||
return @call(.{ .modifier = .always_inline }, truncXfYf2, .{ f32, f128, a });
|
return @call(.{ .modifier = .always_inline }, truncXfYf2, .{ f32, f128, a });
|
||||||
}
|
}
|
||||||
|
|
@ -122,7 +126,7 @@ fn truncXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t {
|
||||||
if (shift > srcSigBits) {
|
if (shift > srcSigBits) {
|
||||||
absResult = 0;
|
absResult = 0;
|
||||||
} else {
|
} 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;
|
const denormalizedSignificand: src_rep_t = significand >> @intCast(SrcShift, shift) | sticky;
|
||||||
absResult = @intCast(dst_rep_t, denormalizedSignificand >> (srcSigBits - dstSigBits));
|
absResult = @intCast(dst_rep_t, denormalizedSignificand >> (srcSigBits - dstSigBits));
|
||||||
const roundBits: src_rep_t = denormalizedSignificand & roundMask;
|
const roundBits: src_rep_t = denormalizedSignificand & roundMask;
|
||||||
|
|
|
||||||
|
|
@ -242,3 +242,59 @@ test "truncdfsf2" {
|
||||||
// huge number becomes inf
|
// huge number becomes inf
|
||||||
test__truncdfsf2(340282366920938463463374607431768211456.0, 0x7f800000);
|
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);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
pub fn isPPC64(arch: Arch) bool {
|
||||||
return switch (arch) {
|
return switch (arch) {
|
||||||
.powerpc64, .powerpc64le => true,
|
.powerpc64, .powerpc64le => true,
|
||||||
|
|
@ -1184,8 +1191,8 @@ pub const Target = struct {
|
||||||
.mips, .mipsel => &mips.cpu.mips32,
|
.mips, .mipsel => &mips.cpu.mips32,
|
||||||
.mips64, .mips64el => &mips.cpu.mips64,
|
.mips64, .mips64el => &mips.cpu.mips64,
|
||||||
.msp430 => &msp430.cpu.generic,
|
.msp430 => &msp430.cpu.generic,
|
||||||
.powerpc => &powerpc.cpu.ppc32,
|
.powerpc => &powerpc.cpu.ppc,
|
||||||
.powerpcle => &powerpc.cpu.ppc32,
|
.powerpcle => &powerpc.cpu.ppc,
|
||||||
.powerpc64 => &powerpc.cpu.ppc64,
|
.powerpc64 => &powerpc.cpu.ppc64,
|
||||||
.powerpc64le => &powerpc.cpu.ppc64le,
|
.powerpc64le => &powerpc.cpu.ppc64le,
|
||||||
.amdgcn => &amdgpu.cpu.generic,
|
.amdgcn => &amdgpu.cpu.generic,
|
||||||
|
|
|
||||||
|
|
@ -751,13 +751,6 @@ pub const cpu = struct {
|
||||||
.hard_float,
|
.hard_float,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
pub const ppc32 = CpuModel{
|
|
||||||
.name = "ppc32",
|
|
||||||
.llvm_name = "ppc32",
|
|
||||||
.features = featureSet(&[_]Feature{
|
|
||||||
.hard_float,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
pub const ppc64 = CpuModel{
|
pub const ppc64 = CpuModel{
|
||||||
.name = "ppc64",
|
.name = "ppc64",
|
||||||
.llvm_name = "ppc64",
|
.llvm_name = "ppc64",
|
||||||
|
|
|
||||||
|
|
@ -852,7 +852,7 @@ const Parser = struct {
|
||||||
/// <- KEYWORD_comptime? VarDecl
|
/// <- KEYWORD_comptime? VarDecl
|
||||||
/// / KEYWORD_comptime BlockExprStatement
|
/// / KEYWORD_comptime BlockExprStatement
|
||||||
/// / KEYWORD_nosuspend BlockExprStatement
|
/// / KEYWORD_nosuspend BlockExprStatement
|
||||||
/// / KEYWORD_suspend (SEMICOLON / BlockExprStatement)
|
/// / KEYWORD_suspend BlockExprStatement
|
||||||
/// / KEYWORD_defer BlockExprStatement
|
/// / KEYWORD_defer BlockExprStatement
|
||||||
/// / KEYWORD_errdefer Payload? BlockExprStatement
|
/// / KEYWORD_errdefer Payload? BlockExprStatement
|
||||||
/// / IfStatement
|
/// / IfStatement
|
||||||
|
|
@ -892,6 +892,7 @@ const Parser = struct {
|
||||||
},
|
},
|
||||||
.keyword_suspend => {
|
.keyword_suspend => {
|
||||||
const token = p.nextToken();
|
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)
|
const block_expr: Node.Index = if (p.eatToken(.semicolon) != null)
|
||||||
0
|
0
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -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" {
|
test "zig fmt: simple top level comptime block" {
|
||||||
try testCanonical(
|
try testCanonical(
|
||||||
\\// line comment
|
\\// line comment
|
||||||
|
|
@ -1315,6 +1330,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" {
|
test "zig fmt: pointer of unknown length" {
|
||||||
try testCanonical(
|
try testCanonical(
|
||||||
\\fn foo(ptr: [*]u8) void {}
|
\\fn foo(ptr: [*]u8) void {}
|
||||||
|
|
@ -3644,9 +3680,9 @@ test "zig fmt: async functions" {
|
||||||
\\fn simpleAsyncFn() void {
|
\\fn simpleAsyncFn() void {
|
||||||
\\ const a = async a.b();
|
\\ const a = async a.b();
|
||||||
\\ x += 1;
|
\\ x += 1;
|
||||||
\\ suspend;
|
\\ suspend {}
|
||||||
\\ x += 1;
|
\\ x += 1;
|
||||||
\\ suspend;
|
\\ suspend {}
|
||||||
\\ const p: anyframe->void = async simpleAsyncFn() catch unreachable;
|
\\ const p: anyframe->void = async simpleAsyncFn() catch unreachable;
|
||||||
\\ await p;
|
\\ await p;
|
||||||
\\}
|
\\}
|
||||||
|
|
@ -5001,6 +5037,21 @@ 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;
|
||||||
|
\\ nosuspend;
|
||||||
|
\\}
|
||||||
|
, &[_]Error{
|
||||||
|
.expected_block_or_expr,
|
||||||
|
.expected_block_or_expr,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
test "recovery: missing block after for/while loops" {
|
test "recovery: missing block after for/while loops" {
|
||||||
try testError(
|
try testError(
|
||||||
\\test "" { while (foo) }
|
\\test "" { while (foo) }
|
||||||
|
|
@ -5144,7 +5195,7 @@ fn testError(source: []const u8, expected_errors: []const Error) !void {
|
||||||
var tree = try std.zig.parse(std.testing.allocator, source);
|
var tree = try std.zig.parse(std.testing.allocator, source);
|
||||||
defer tree.deinit(std.testing.allocator);
|
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| {
|
for (expected_errors) |expected, i| {
|
||||||
std.testing.expectEqual(expected, tree.errors[i].tag);
|
std.testing.expectEqual(expected, tree.errors[i].tag);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -269,7 +269,12 @@ fn renderExpression(gpa: *Allocator, ais: *Ais, tree: ast.Tree, node: ast.Node.I
|
||||||
try renderToken(ais, tree, suspend_token, .space);
|
try renderToken(ais, tree, suspend_token, .space);
|
||||||
return renderExpression(gpa, ais, tree, body, space);
|
return renderExpression(gpa, ais, tree, body, space);
|
||||||
} else {
|
} else {
|
||||||
return renderToken(ais, tree, suspend_token, space);
|
// 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;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -2310,9 +2315,9 @@ fn renderComments(ais: *Ais, tree: ast.Tree, start: usize, end: usize) Error!boo
|
||||||
// to the underlying writer, fixing up invaild whitespace.
|
// to the underlying writer, fixing up invaild whitespace.
|
||||||
const disabled_source = tree.source[ais.disabled_offset.?..comment_start];
|
const disabled_source = tree.source[ais.disabled_offset.?..comment_start];
|
||||||
try writeFixingWhitespace(ais.underlying_writer, disabled_source);
|
try writeFixingWhitespace(ais.underlying_writer, disabled_source);
|
||||||
ais.disabled_offset = null;
|
|
||||||
// Write with the canonical single space.
|
// Write with the canonical single space.
|
||||||
try ais.writer().writeAll("// zig fmt: on\n");
|
try ais.underlying_writer.writeAll("// zig fmt: on\n");
|
||||||
|
ais.disabled_offset = null;
|
||||||
} else if (ais.disabled_offset == null and mem.eql(u8, comment_content, "zig fmt: off")) {
|
} else if (ais.disabled_offset == null and mem.eql(u8, comment_content, "zig fmt: off")) {
|
||||||
// Write with the canonical single space.
|
// Write with the canonical single space.
|
||||||
try ais.writer().writeAll("// zig fmt: off\n");
|
try ais.writer().writeAll("// zig fmt: off\n");
|
||||||
|
|
|
||||||
|
|
@ -2856,25 +2856,29 @@ pub fn addCCArgs(
|
||||||
try argv.append("-fPIC");
|
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| {
|
if (out_dep_path) |p| {
|
||||||
try argv.appendSlice(&[_][]const u8{ "-MD", "-MV", "-MF", 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) {
|
if (target.os.tag == .freestanding) {
|
||||||
try argv.append("-ffreestanding");
|
try argv.append("-ffreestanding");
|
||||||
|
|
|
||||||
|
|
@ -1247,7 +1247,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||||
},
|
},
|
||||||
.stack_offset => |off| {
|
.stack_offset => |off| {
|
||||||
log.debug("reusing stack offset {} => {*}", .{ off, inst });
|
log.debug("reusing stack offset {} => {*}", .{ off, inst });
|
||||||
return true;
|
|
||||||
},
|
},
|
||||||
else => return false,
|
else => return false,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -645,8 +645,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
|
||||||
break :blk true;
|
break :blk true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.base.options.link_libcpp or
|
if (self.base.options.output_mode == .Lib or
|
||||||
self.base.options.output_mode == .Lib or
|
|
||||||
self.base.options.linker_script != null)
|
self.base.options.linker_script != null)
|
||||||
{
|
{
|
||||||
// Fallback to LLD in this handful of cases on x86_64 only.
|
// Fallback to LLD in this handful of cases on x86_64 only.
|
||||||
|
|
|
||||||
|
|
@ -208,14 +208,13 @@ pub fn parseObject(self: Archive, offset: u32) !Object {
|
||||||
|
|
||||||
const object_name = try parseName(self.allocator, object_header, reader);
|
const object_name = try parseName(self.allocator, object_header, reader);
|
||||||
defer self.allocator.free(object_name);
|
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: {
|
const name = name: {
|
||||||
var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
|
var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
|
||||||
const path = try std.os.realpath(self.name.?, &buffer);
|
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);
|
var object = Object.init(self.allocator);
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,9 @@ symtab_cmd_index: ?u16 = null,
|
||||||
dysymtab_cmd_index: ?u16 = null,
|
dysymtab_cmd_index: ?u16 = null,
|
||||||
build_version_cmd_index: ?u16 = null,
|
build_version_cmd_index: ?u16 = null,
|
||||||
data_in_code_cmd_index: ?u16 = null,
|
data_in_code_cmd_index: ?u16 = null,
|
||||||
|
|
||||||
text_section_index: ?u16 = null,
|
text_section_index: ?u16 = null,
|
||||||
|
mod_init_func_section_index: ?u16 = null,
|
||||||
|
|
||||||
// __DWARF segment sections
|
// __DWARF segment sections
|
||||||
dwarf_debug_info_index: ?u16 = null,
|
dwarf_debug_info_index: ?u16 = null,
|
||||||
|
|
@ -49,6 +51,7 @@ stabs: std.ArrayListUnmanaged(Stab) = .{},
|
||||||
tu_path: ?[]const u8 = null,
|
tu_path: ?[]const u8 = null,
|
||||||
tu_mtime: ?u64 = null,
|
tu_mtime: ?u64 = null,
|
||||||
|
|
||||||
|
initializers: std.ArrayListUnmanaged(CppStatic) = .{},
|
||||||
data_in_code_entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},
|
data_in_code_entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},
|
||||||
|
|
||||||
pub const Section = struct {
|
pub const Section = struct {
|
||||||
|
|
@ -68,6 +71,11 @@ pub const Section = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const CppStatic = struct {
|
||||||
|
symbol: u32,
|
||||||
|
target_addr: u64,
|
||||||
|
};
|
||||||
|
|
||||||
const Stab = struct {
|
const Stab = struct {
|
||||||
tag: Tag,
|
tag: Tag,
|
||||||
symbol: u32,
|
symbol: u32,
|
||||||
|
|
@ -170,6 +178,7 @@ pub fn deinit(self: *Object) void {
|
||||||
self.strtab.deinit(self.allocator);
|
self.strtab.deinit(self.allocator);
|
||||||
self.stabs.deinit(self.allocator);
|
self.stabs.deinit(self.allocator);
|
||||||
self.data_in_code_entries.deinit(self.allocator);
|
self.data_in_code_entries.deinit(self.allocator);
|
||||||
|
self.initializers.deinit(self.allocator);
|
||||||
|
|
||||||
if (self.name) |n| {
|
if (self.name) |n| {
|
||||||
self.allocator.free(n);
|
self.allocator.free(n);
|
||||||
|
|
@ -216,6 +225,7 @@ pub fn parse(self: *Object) !void {
|
||||||
try self.parseSections();
|
try self.parseSections();
|
||||||
if (self.symtab_cmd_index != null) try self.parseSymtab();
|
if (self.symtab_cmd_index != null) try self.parseSymtab();
|
||||||
if (self.data_in_code_cmd_index != null) try self.readDataInCode();
|
if (self.data_in_code_cmd_index != null) try self.readDataInCode();
|
||||||
|
try self.parseInitializers();
|
||||||
try self.parseDebugInfo();
|
try self.parseDebugInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -250,6 +260,10 @@ pub fn readLoadCommands(self: *Object, reader: anytype) !void {
|
||||||
if (mem.eql(u8, sectname, "__text")) {
|
if (mem.eql(u8, sectname, "__text")) {
|
||||||
self.text_section_index = index;
|
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;
|
sect.offset += offset;
|
||||||
|
|
@ -298,28 +312,53 @@ pub fn parseSections(self: *Object) !void {
|
||||||
var section = Section{
|
var section = Section{
|
||||||
.inner = sect,
|
.inner = sect,
|
||||||
.code = code,
|
.code = code,
|
||||||
.relocs = undefined,
|
.relocs = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Parse relocations
|
// 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);
|
var raw_relocs = try self.allocator.alloc(u8, @sizeOf(macho.relocation_info) * sect.nreloc);
|
||||||
defer self.allocator.free(raw_relocs);
|
defer self.allocator.free(raw_relocs);
|
||||||
|
|
||||||
_ = try self.file.?.preadAll(raw_relocs, sect.reloff);
|
_ = try self.file.?.preadAll(raw_relocs, sect.reloff);
|
||||||
|
|
||||||
break :relocs try reloc.parse(
|
section.relocs = try reloc.parse(
|
||||||
self.allocator,
|
self.allocator,
|
||||||
self.arch.?,
|
self.arch.?,
|
||||||
section.code,
|
section.code,
|
||||||
mem.bytesAsSlice(macho.relocation_info, raw_relocs),
|
mem.bytesAsSlice(macho.relocation_info, raw_relocs),
|
||||||
);
|
);
|
||||||
} else null;
|
}
|
||||||
|
|
||||||
self.sections.appendAssumeCapacity(section);
|
self.sections.appendAssumeCapacity(section);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parseInitializers(self: *Object) !void {
|
||||||
|
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});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parseSymtab(self: *Object) !void {
|
pub fn parseSymtab(self: *Object) !void {
|
||||||
const symtab_cmd = self.load_commands.items[self.symtab_cmd_index.?].Symtab;
|
const symtab_cmd = self.load_commands.items[self.symtab_cmd_index.?].Symtab;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ pub fn isUndf(sym: macho.nlist_64) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn isWeakDef(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.
|
/// Symbol is local if it is defined and not an extern.
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,7 @@ tlv_bss_section_index: ?u16 = null,
|
||||||
la_symbol_ptr_section_index: ?u16 = null,
|
la_symbol_ptr_section_index: ?u16 = null,
|
||||||
data_section_index: ?u16 = null,
|
data_section_index: ?u16 = null,
|
||||||
bss_section_index: ?u16 = null,
|
bss_section_index: ?u16 = null,
|
||||||
|
common_section_index: ?u16 = null,
|
||||||
|
|
||||||
symtab: std.StringArrayHashMapUnmanaged(Symbol) = .{},
|
symtab: std.StringArrayHashMapUnmanaged(Symbol) = .{},
|
||||||
strtab: std.ArrayListUnmanaged(u8) = .{},
|
strtab: std.ArrayListUnmanaged(u8) = .{},
|
||||||
|
|
@ -224,6 +225,7 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void {
|
||||||
self.allocateLinkeditSegment();
|
self.allocateLinkeditSegment();
|
||||||
try self.allocateSymbols();
|
try self.allocateSymbols();
|
||||||
try self.allocateStubsAndGotEntries();
|
try self.allocateStubsAndGotEntries();
|
||||||
|
try self.allocateCppStatics();
|
||||||
try self.writeStubHelperCommon();
|
try self.writeStubHelperCommon();
|
||||||
try self.resolveRelocsAndWriteSections();
|
try self.resolveRelocsAndWriteSections();
|
||||||
try self.flush();
|
try self.flush();
|
||||||
|
|
@ -465,23 +467,43 @@ fn updateMetadata(self: *Zld) !void {
|
||||||
},
|
},
|
||||||
macho.S_ZEROFILL => {
|
macho.S_ZEROFILL => {
|
||||||
if (!mem.eql(u8, segname, "__DATA")) continue;
|
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);
|
self.common_section_index = @intCast(u16, data_seg.sections.items.len);
|
||||||
try data_seg.addSection(self.allocator, .{
|
try data_seg.addSection(self.allocator, .{
|
||||||
.sectname = makeStaticString("__bss"),
|
.sectname = makeStaticString("__common"),
|
||||||
.segname = makeStaticString("__DATA"),
|
.segname = makeStaticString("__DATA"),
|
||||||
.addr = 0,
|
.addr = 0,
|
||||||
.size = 0,
|
.size = 0,
|
||||||
.offset = 0,
|
.offset = 0,
|
||||||
.@"align" = 0,
|
.@"align" = 0,
|
||||||
.reloff = 0,
|
.reloff = 0,
|
||||||
.nreloc = 0,
|
.nreloc = 0,
|
||||||
.flags = macho.S_ZEROFILL,
|
.flags = macho.S_ZEROFILL,
|
||||||
.reserved1 = 0,
|
.reserved1 = 0,
|
||||||
.reserved2 = 0,
|
.reserved2 = 0,
|
||||||
.reserved3 = 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 => {
|
macho.S_THREAD_LOCAL_VARIABLES => {
|
||||||
if (!mem.eql(u8, segname, "__DATA")) continue;
|
if (!mem.eql(u8, segname, "__DATA")) continue;
|
||||||
|
|
@ -568,7 +590,9 @@ fn updateMetadata(self: *Zld) !void {
|
||||||
|
|
||||||
const segname = parseName(&source_sect.segname);
|
const segname = parseName(&source_sect.segname);
|
||||||
const sectname = parseName(&source_sect.sectname);
|
const sectname = parseName(&source_sect.sectname);
|
||||||
|
|
||||||
log.debug("section '{s}/{s}' will be unmapped", .{ segname, sectname });
|
log.debug("section '{s}/{s}' will be unmapped", .{ segname, sectname });
|
||||||
|
|
||||||
try self.unhandled_sections.putNoClobber(self.allocator, .{
|
try self.unhandled_sections.putNoClobber(self.allocator, .{
|
||||||
.object_id = object_id,
|
.object_id = object_id,
|
||||||
.source_sect_id = source_sect_id,
|
.source_sect_id = source_sect_id,
|
||||||
|
|
@ -585,6 +609,7 @@ const MatchingSection = struct {
|
||||||
fn getMatchingSection(self: *Zld, section: macho.section_64) ?MatchingSection {
|
fn getMatchingSection(self: *Zld, section: macho.section_64) ?MatchingSection {
|
||||||
const segname = parseName(§ion.segname);
|
const segname = parseName(§ion.segname);
|
||||||
const sectname = parseName(§ion.sectname);
|
const sectname = parseName(§ion.sectname);
|
||||||
|
|
||||||
const res: ?MatchingSection = blk: {
|
const res: ?MatchingSection = blk: {
|
||||||
switch (section.flags) {
|
switch (section.flags) {
|
||||||
macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => {
|
macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => {
|
||||||
|
|
@ -612,6 +637,12 @@ fn getMatchingSection(self: *Zld, section: macho.section_64) ?MatchingSection {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
macho.S_ZEROFILL => {
|
macho.S_ZEROFILL => {
|
||||||
|
if (mem.eql(u8, sectname, "__common")) {
|
||||||
|
break :blk .{
|
||||||
|
.seg = self.data_segment_cmd_index.?,
|
||||||
|
.sect = self.common_section_index.?,
|
||||||
|
};
|
||||||
|
}
|
||||||
break :blk .{
|
break :blk .{
|
||||||
.seg = self.data_segment_cmd_index.?,
|
.seg = self.data_segment_cmd_index.?,
|
||||||
.sect = self.bss_section_index.?,
|
.sect = self.bss_section_index.?,
|
||||||
|
|
@ -667,6 +698,7 @@ fn getMatchingSection(self: *Zld, section: macho.section_64) ?MatchingSection {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -737,11 +769,12 @@ fn sortSections(self: *Zld) !void {
|
||||||
// __DATA segment
|
// __DATA segment
|
||||||
const indices = &[_]*?u16{
|
const indices = &[_]*?u16{
|
||||||
&self.la_symbol_ptr_section_index,
|
&self.la_symbol_ptr_section_index,
|
||||||
&self.tlv_section_index,
|
|
||||||
&self.data_section_index,
|
&self.data_section_index,
|
||||||
|
&self.tlv_section_index,
|
||||||
&self.tlv_data_section_index,
|
&self.tlv_data_section_index,
|
||||||
&self.tlv_bss_section_index,
|
&self.tlv_bss_section_index,
|
||||||
&self.bss_section_index,
|
&self.bss_section_index,
|
||||||
|
&self.common_section_index,
|
||||||
};
|
};
|
||||||
for (indices) |maybe_index| {
|
for (indices) |maybe_index| {
|
||||||
const new_index: u16 = if (maybe_index.*) |index| blk: {
|
const new_index: u16 = if (maybe_index.*) |index| blk: {
|
||||||
|
|
@ -959,6 +992,21 @@ fn allocateStubsAndGotEntries(self: *Zld) !void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn writeStubHelperCommon(self: *Zld) !void {
|
fn writeStubHelperCommon(self: *Zld) !void {
|
||||||
const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
|
const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
|
||||||
const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?];
|
const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?];
|
||||||
|
|
@ -1236,11 +1284,12 @@ fn resolveSymbolsInObject(self: *Zld, object_id: u16) !void {
|
||||||
continue;
|
continue;
|
||||||
} else if (Symbol.isGlobal(sym)) {
|
} else if (Symbol.isGlobal(sym)) {
|
||||||
const sym_name = object.getString(sym.n_strx);
|
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 {
|
const global = self.symtab.getEntry(sym_name) orelse {
|
||||||
// Put new global symbol into the symbol table.
|
// Put new global symbol into the symbol table.
|
||||||
const name = try self.allocator.dupe(u8, sym_name);
|
const name = try self.allocator.dupe(u8, sym_name);
|
||||||
try self.symtab.putNoClobber(self.allocator, 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,
|
.name = name,
|
||||||
.address = 0,
|
.address = 0,
|
||||||
.section = 0,
|
.section = 0,
|
||||||
|
|
@ -1251,15 +1300,20 @@ fn resolveSymbolsInObject(self: *Zld, object_id: u16) !void {
|
||||||
};
|
};
|
||||||
|
|
||||||
switch (global.value.tag) {
|
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 => {
|
.strong => {
|
||||||
log.err("symbol '{s}' defined multiple times", .{sym_name});
|
if (!is_weak) {
|
||||||
return error.MultipleSymbolDefinitions;
|
log.debug("strong symbol '{s}' defined multiple times", .{sym_name});
|
||||||
|
return error.MultipleSymbolDefinitions;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
},
|
},
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
global.value.tag = .strong;
|
global.value.tag = if (is_weak) .weak else .strong;
|
||||||
global.value.file = object_id;
|
global.value.file = object_id;
|
||||||
global.value.index = @intCast(u32, sym_id);
|
global.value.index = @intCast(u32, sym_id);
|
||||||
} else if (Symbol.isUndef(sym)) {
|
} else if (Symbol.isUndef(sym)) {
|
||||||
|
|
@ -1340,6 +1394,21 @@ fn resolveSymbols(self: *Zld) !void {
|
||||||
.section = 0,
|
.section = 0,
|
||||||
.file = 0,
|
.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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolveStubsAndGotEntries(self: *Zld) !void {
|
fn resolveStubsAndGotEntries(self: *Zld) !void {
|
||||||
|
|
@ -1412,9 +1481,14 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void {
|
||||||
log.debug("relocating object {s}", .{object.name});
|
log.debug("relocating object {s}", .{object.name});
|
||||||
|
|
||||||
for (object.sections.items) |sect, source_sect_id| {
|
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 segname = parseName(§.inner.segname);
|
||||||
const sectname = parseName(§.inner.sectname);
|
const sectname = parseName(§.inner.sectname);
|
||||||
|
|
||||||
|
log.debug("relocating section '{s},{s}'", .{ segname, sectname });
|
||||||
|
|
||||||
// Get mapping
|
// Get mapping
|
||||||
const target_mapping = self.mappings.get(.{
|
const target_mapping = self.mappings.get(.{
|
||||||
.object_id = @intCast(u16, object_id),
|
.object_id = @intCast(u16, object_id),
|
||||||
|
|
@ -1532,6 +1606,7 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void {
|
||||||
target_sect_off,
|
target_sect_off,
|
||||||
target_sect_off + sect.code.len,
|
target_sect_off + sect.code.len,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Zero-out the space
|
// Zero-out the space
|
||||||
var zeroes = try self.allocator.alloc(u8, sect.code.len);
|
var zeroes = try self.allocator.alloc(u8, sect.code.len);
|
||||||
defer self.allocator.free(zeroes);
|
defer self.allocator.free(zeroes);
|
||||||
|
|
@ -1571,25 +1646,33 @@ fn relocTargetAddr(self: *Zld, object_id: u16, target: reloc.Relocation.Target)
|
||||||
const target_sect = target_seg.sections.items[target_mapping.target_sect_id];
|
const target_sect = target_seg.sections.items[target_mapping.target_sect_id];
|
||||||
const target_addr = target_sect.addr + target_mapping.offset;
|
const target_addr = target_sect.addr + target_mapping.offset;
|
||||||
break :blk sym.n_value - source_sect.addr + target_addr;
|
break :blk sym.n_value - source_sect.addr + target_addr;
|
||||||
} else {
|
} else if (self.symtab.get(sym_name)) |global| {
|
||||||
if (self.stubs.get(sym_name)) |index| {
|
switch (global.tag) {
|
||||||
log.debug(" | symbol stub '{s}'", .{sym_name});
|
.weak, .strong => {
|
||||||
const segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
|
log.debug(" | global symbol '{s}'", .{sym_name});
|
||||||
const stubs = segment.sections.items[self.stubs_section_index.?];
|
break :blk global.address;
|
||||||
break :blk stubs.addr + index * stubs.reserved2;
|
},
|
||||||
} else if (mem.eql(u8, sym_name, "__tlv_bootstrap")) {
|
.import => {
|
||||||
log.debug(" | symbol '__tlv_bootstrap'", .{});
|
if (self.stubs.get(sym_name)) |index| {
|
||||||
const segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
|
log.debug(" | symbol stub '{s}'", .{sym_name});
|
||||||
const tlv = segment.sections.items[self.tlv_section_index.?];
|
const segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
|
||||||
break :blk tlv.addr;
|
const stubs = segment.sections.items[self.stubs_section_index.?];
|
||||||
} else {
|
break :blk stubs.addr + index * stubs.reserved2;
|
||||||
const global = self.symtab.get(sym_name) orelse {
|
} else if (mem.eql(u8, sym_name, "__tlv_bootstrap")) {
|
||||||
log.err("failed to resolve symbol '{s}' as a relocation target", .{sym_name});
|
log.debug(" | symbol '__tlv_bootstrap'", .{});
|
||||||
return error.FailedToResolveRelocationTarget;
|
const segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
|
||||||
};
|
const tlv = segment.sections.items[self.tlv_section_index.?];
|
||||||
log.debug(" | global symbol '{s}'", .{sym_name});
|
break :blk tlv.addr;
|
||||||
break :blk global.address;
|
} else {
|
||||||
|
log.err("failed to resolve symbol '{s}' as a relocation target", .{sym_name});
|
||||||
|
return error.FailedToResolveRelocationTarget;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
else => unreachable,
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
log.err("failed to resolve symbol '{s}' as a relocation target", .{sym_name});
|
||||||
|
return error.FailedToResolveRelocationTarget;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.section => |sect_id| {
|
.section => |sect_id| {
|
||||||
|
|
@ -2008,6 +2091,12 @@ fn populateMetadata(self: *Zld) !void {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flush(self: *Zld) !void {
|
fn flush(self: *Zld) !void {
|
||||||
|
if (self.common_section_index) |index| {
|
||||||
|
const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
|
||||||
|
const sect = &seg.sections.items[index];
|
||||||
|
sect.offset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (self.bss_section_index) |index| {
|
if (self.bss_section_index) |index| {
|
||||||
const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
|
const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
|
||||||
const sect = &seg.sections.items[index];
|
const sect = &seg.sections.items[index];
|
||||||
|
|
@ -2040,6 +2129,24 @@ fn flush(self: *Zld) !void {
|
||||||
try self.file.?.pwriteAll(buffer, sect.offset);
|
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 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(mem.sliceAsBytes(initializers.items), sect.offset);
|
||||||
|
sect.size = @intCast(u32, initializers.items.len * @sizeOf(u64));
|
||||||
|
}
|
||||||
|
|
||||||
try self.writeGotEntries();
|
try self.writeGotEntries();
|
||||||
try self.setEntryPoint();
|
try self.setEntryPoint();
|
||||||
try self.writeRebaseInfoTable();
|
try self.writeRebaseInfoTable();
|
||||||
|
|
@ -2139,35 +2246,18 @@ fn writeRebaseInfoTable(self: *Zld) !void {
|
||||||
// TODO audit and investigate this.
|
// TODO audit and investigate this.
|
||||||
const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
|
const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
|
||||||
const sect = seg.sections.items[idx];
|
const sect = seg.sections.items[idx];
|
||||||
const npointers = sect.size * @sizeOf(u64);
|
|
||||||
const base_offset = sect.addr - seg.inner.vmaddr;
|
const base_offset = sect.addr - seg.inner.vmaddr;
|
||||||
const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?);
|
const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?);
|
||||||
|
|
||||||
try pointers.ensureCapacity(pointers.items.len + npointers);
|
var index: u64 = 0;
|
||||||
var i: usize = 0;
|
for (self.objects.items) |object| {
|
||||||
while (i < npointers) : (i += 1) {
|
for (object.initializers.items) |_| {
|
||||||
pointers.appendAssumeCapacity(.{
|
try pointers.append(.{
|
||||||
.offset = base_offset + i * @sizeOf(u64),
|
.offset = base_offset + index * @sizeOf(u64),
|
||||||
.segment_id = segment_id,
|
.segment_id = segment_id,
|
||||||
});
|
});
|
||||||
}
|
index += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
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 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),
|
|
||||||
.segment_id = segment_id,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2447,7 +2537,7 @@ fn writeDebugInfo(self: *Zld) !void {
|
||||||
.n_type = macho.N_OSO,
|
.n_type = macho.N_OSO,
|
||||||
.n_sect = 0,
|
.n_sect = 0,
|
||||||
.n_desc = 1,
|
.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| {
|
for (object.stabs.items) |stab| {
|
||||||
|
|
|
||||||
|
|
@ -226,7 +226,9 @@ pub const Parser = struct {
|
||||||
try parser.parseTlvpLoadPageOff(rel);
|
try parser.parseTlvpLoadPageOff(rel);
|
||||||
},
|
},
|
||||||
.ARM64_RELOC_POINTER_TO_GOT => {
|
.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", .{});
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -355,6 +355,8 @@ const usage_build_generic =
|
||||||
\\ -rpath [path] Add directory to the runtime library search path
|
\\ -rpath [path] Add directory to the runtime library search path
|
||||||
\\ -feach-lib-rpath Ensure adding rpath for each used dynamic library
|
\\ -feach-lib-rpath Ensure adding rpath for each used dynamic library
|
||||||
\\ -fno-each-lib-rpath Prevent 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
|
\\ --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
|
\\ --emit-relocs Enable output of relocation sections for post build tools
|
||||||
\\ -dynamic Force output to be dynamically linked
|
\\ -dynamic Force output to be dynamically linked
|
||||||
|
|
@ -988,6 +990,10 @@ fn buildOutputType(
|
||||||
link_eh_frame_hdr = true;
|
link_eh_frame_hdr = true;
|
||||||
} else if (mem.eql(u8, arg, "--emit-relocs")) {
|
} else if (mem.eql(u8, arg, "--emit-relocs")) {
|
||||||
link_emit_relocs = true;
|
link_emit_relocs = true;
|
||||||
|
} else if (mem.eql(u8, arg, "-fallow-shlib-undefined")) {
|
||||||
|
linker_allow_shlib_undefined = true;
|
||||||
|
} else if (mem.eql(u8, arg, "-fno-allow-shlib-undefined")) {
|
||||||
|
linker_allow_shlib_undefined = false;
|
||||||
} else if (mem.eql(u8, arg, "-Bsymbolic")) {
|
} else if (mem.eql(u8, arg, "-Bsymbolic")) {
|
||||||
linker_bind_global_refs_locally = true;
|
linker_bind_global_refs_locally = true;
|
||||||
} else if (mem.eql(u8, arg, "--verbose-link")) {
|
} else if (mem.eql(u8, arg, "--verbose-link")) {
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ pub fn RegisterManager(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn isTracked(reg: Register) bool {
|
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 {
|
fn markRegUsed(self: *Self, reg: Register) void {
|
||||||
|
|
@ -55,6 +55,7 @@ pub fn RegisterManager(
|
||||||
self.free_registers |= @as(FreeRegInt, 1) << shift;
|
self.free_registers |= @as(FreeRegInt, 1) << shift;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true when this register is not tracked
|
||||||
pub fn isRegFree(self: Self, reg: Register) bool {
|
pub fn isRegFree(self: Self, reg: Register) bool {
|
||||||
if (FreeRegInt == u0) return true;
|
if (FreeRegInt == u0) return true;
|
||||||
const index = reg.allocIndex() orelse 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
|
/// 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 {
|
pub fn isRegAllocated(self: Self, reg: Register) bool {
|
||||||
if (FreeRegInt == u0) return false;
|
if (FreeRegInt == u0) return false;
|
||||||
const index = reg.allocIndex() orelse 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;
|
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.
|
/// Before calling, must ensureCapacity + 1 on self.registers.
|
||||||
/// Returns `null` if all registers are allocated.
|
/// Returns `null` if all registers are allocated.
|
||||||
pub fn tryAllocReg(self: *Self, inst: *ir.Inst) ?Register {
|
pub fn tryAllocReg(self: *Self, inst: *ir.Inst) ?Register {
|
||||||
const free_index = @ctz(FreeRegInt, self.free_registers);
|
return if (tryAllocRegs(self, 1, .{inst})) |regs| regs[0] else null;
|
||||||
if (free_index >= callee_preserved_regs.len) {
|
}
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is necessary because the return type of @ctz is 1
|
/// Before calling, must ensureCapacity + count on self.registers.
|
||||||
// bit longer than ShiftInt if callee_preserved_regs.len
|
pub fn allocRegs(self: *Self, comptime count: comptime_int, insts: [count]*ir.Inst) ![count]Register {
|
||||||
// is a power of two. This int cast is always safe because
|
comptime assert(count > 0 and count <= callee_preserved_regs.len);
|
||||||
// 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;
|
|
||||||
|
|
||||||
const reg = callee_preserved_regs[free_index];
|
return self.tryAllocRegs(count, insts) orelse blk: {
|
||||||
self.registers.putAssumeCapacityNoClobber(reg, inst);
|
// We'll take over the first count registers. Spill
|
||||||
log.debug("alloc {} => {*}", .{ reg, inst });
|
// the instructions that were previously there to a
|
||||||
return reg;
|
// 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.
|
/// Before calling, must ensureCapacity + 1 on self.registers.
|
||||||
pub fn allocReg(self: *Self, inst: *ir.Inst) !Register {
|
pub fn allocReg(self: *Self, inst: *ir.Inst) !Register {
|
||||||
return self.tryAllocReg(inst) orelse b: {
|
return (try allocRegs(self, 1, .{inst}))[0];
|
||||||
// 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);
|
|
||||||
|
|
||||||
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.
|
/// Does not track the register.
|
||||||
/// Returns `null` if all registers are allocated.
|
/// Returns `null` if all registers are allocated.
|
||||||
pub fn findUnusedReg(self: *Self) ?Register {
|
pub fn tryAllocRegWithoutTracking(self: *Self) ?Register {
|
||||||
const free_index = @ctz(FreeRegInt, self.free_registers);
|
return if (tryAllocRegsWithoutTracking(self, 1)) |regs| regs[0] else null;
|
||||||
if (free_index >= callee_preserved_regs.len) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return callee_preserved_regs[free_index];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Does not track the register.
|
/// Does not track the register.
|
||||||
pub fn allocRegWithoutTracking(self: *Self) !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
|
// We'll take over the first register. Move the instruction that was previously
|
||||||
// there to a stack allocation.
|
// there to a stack allocation.
|
||||||
const reg = callee_preserved_regs[0];
|
const reg = callee_preserved_regs[0];
|
||||||
|
|
@ -190,7 +224,10 @@ pub fn RegisterManager(
|
||||||
}
|
}
|
||||||
|
|
||||||
const MockRegister = enum(u2) {
|
const MockRegister = enum(u2) {
|
||||||
r0, r1, r2, r3,
|
r0,
|
||||||
|
r1,
|
||||||
|
r2,
|
||||||
|
r3,
|
||||||
|
|
||||||
pub fn allocIndex(self: MockRegister) ?u2 {
|
pub fn allocIndex(self: MockRegister) ?u2 {
|
||||||
inline for (mock_callee_preserved_regs) |cpreg, i| {
|
inline for (mock_callee_preserved_regs) |cpreg, i| {
|
||||||
|
|
@ -213,7 +250,7 @@ const MockFunction = struct {
|
||||||
self.register_manager.deinit(self.allocator);
|
self.register_manager.deinit(self.allocator);
|
||||||
self.spilled.deinit(self.allocator);
|
self.spilled.deinit(self.allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn spillInstruction(self: *Self, src: LazySrcLoc, reg: MockRegister, inst: *ir.Inst) !void {
|
pub fn spillInstruction(self: *Self, src: LazySrcLoc, reg: MockRegister, inst: *ir.Inst) !void {
|
||||||
try self.spilled.append(self.allocator, reg);
|
try self.spilled.append(self.allocator, reg);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
#include "bigint.hpp"
|
#include "bigint.hpp"
|
||||||
#include "buffer.hpp"
|
#include "buffer.hpp"
|
||||||
#include "softfloat.hpp"
|
#include "softfloat.hpp"
|
||||||
|
#include "softfloat_ext.hpp"
|
||||||
#include "parse_f128.h"
|
#include "parse_f128.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
@ -60,9 +61,7 @@ void bigfloat_init_bigint(BigFloat *dest, const BigInt *op) {
|
||||||
|
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
if (op->is_negative) {
|
if (op->is_negative) {
|
||||||
float128_t zero_f128;
|
f128M_neg(&dest->value, &dest->value);
|
||||||
ui32_to_f128M(0, &zero_f128);
|
|
||||||
f128M_sub(&zero_f128, &dest->value, &dest->value);
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -89,9 +88,7 @@ void bigfloat_add(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void bigfloat_negate(BigFloat *dest, const BigFloat *op) {
|
void bigfloat_negate(BigFloat *dest, const BigFloat *op) {
|
||||||
float128_t zero_f128;
|
f128M_neg(&op->value, &dest->value);
|
||||||
ui32_to_f128M(0, &zero_f128);
|
|
||||||
f128M_sub(&zero_f128, &op->value, &dest->value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void bigfloat_sub(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) {
|
void bigfloat_sub(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) {
|
||||||
|
|
|
||||||
|
|
@ -1446,10 +1446,10 @@ void bigint_negate(BigInt *dest, const BigInt *op) {
|
||||||
bigint_normalize(dest);
|
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 zero;
|
||||||
bigint_init_unsigned(&zero, 0);
|
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) {
|
void bigint_not(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed) {
|
||||||
|
|
|
||||||
|
|
@ -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_shr(BigInt *dest, const BigInt *op1, const BigInt *op2);
|
||||||
|
|
||||||
void bigint_negate(BigInt *dest, const BigInt *op);
|
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_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);
|
void bigint_truncate(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7436,7 +7436,10 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n
|
||||||
case ZigTypeIdFloat:
|
case ZigTypeIdFloat:
|
||||||
switch (type_entry->data.floating.bit_count) {
|
switch (type_entry->data.floating.bit_count) {
|
||||||
case 16:
|
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:
|
case 32:
|
||||||
return LLVMConstReal(get_llvm_type(g, type_entry), const_val->data.x_f32);
|
return LLVMConstReal(get_llvm_type(g, type_entry), const_val->data.x_f32);
|
||||||
case 64:
|
case 64:
|
||||||
|
|
|
||||||
|
|
@ -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);
|
Scope *child_scope = create_nosuspend_scope(irb->codegen, node, parent_scope);
|
||||||
// purposefully pass null for result_loc and let EndExpr handle it
|
// 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) {
|
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);
|
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);
|
||||||
ScopeSuspend *suspend_scope = create_suspend_scope(irb->codegen, node, parent_scope);
|
Scope *child_scope = &suspend_scope->base;
|
||||||
Scope *child_scope = &suspend_scope->base;
|
IrInstSrc *susp_res = ir_gen_node(irb, node->data.suspend.block, child_scope);
|
||||||
IrInstSrc *susp_res = ir_gen_node(irb, node->data.suspend.block, child_scope);
|
if (susp_res == irb->codegen->invalid_inst_src)
|
||||||
if (susp_res == irb->codegen->invalid_inst_src)
|
return 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));
|
||||||
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));
|
return ir_mark_gen(ir_build_suspend_finish_src(irb, parent_scope, node, begin));
|
||||||
}
|
}
|
||||||
|
|
@ -11363,11 +11361,8 @@ static void float_negate(ZigValue *out_val, ZigValue *op) {
|
||||||
} else if (op->type->id == ZigTypeIdFloat) {
|
} else if (op->type->id == ZigTypeIdFloat) {
|
||||||
switch (op->type->data.floating.bit_count) {
|
switch (op->type->data.floating.bit_count) {
|
||||||
case 16:
|
case 16:
|
||||||
{
|
out_val->data.x_f16 = f16_neg(op->data.x_f16);
|
||||||
const float16_t zero = zig_double_to_f16(0);
|
return;
|
||||||
out_val->data.x_f16 = f16_sub(zero, op->data.x_f16);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case 32:
|
case 32:
|
||||||
out_val->data.x_f32 = -op->data.x_f32;
|
out_val->data.x_f32 = -op->data.x_f32;
|
||||||
return;
|
return;
|
||||||
|
|
@ -11375,9 +11370,7 @@ static void float_negate(ZigValue *out_val, ZigValue *op) {
|
||||||
out_val->data.x_f64 = -op->data.x_f64;
|
out_val->data.x_f64 = -op->data.x_f64;
|
||||||
return;
|
return;
|
||||||
case 128:
|
case 128:
|
||||||
float128_t zero_f128;
|
f128M_neg(&op->data.x_f128, &out_val->data.x_f128);
|
||||||
ui32_to_f128M(0, &zero_f128);
|
|
||||||
f128M_sub(&zero_f128, &op->data.x_f128, &out_val->data.x_f128);
|
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
zig_unreachable();
|
zig_unreachable();
|
||||||
|
|
@ -21665,8 +21658,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 is_float = (scalar_type->id == ZigTypeIdFloat || scalar_type->id == ZigTypeIdComptimeFloat);
|
||||||
|
|
||||||
bool ok_type = ((scalar_type->id == ZigTypeIdInt && scalar_type->data.integral.is_signed) ||
|
bool ok_type = scalar_type->id == ZigTypeIdInt || scalar_type->id == ZigTypeIdComptimeInt ||
|
||||||
scalar_type->id == ZigTypeIdComptimeInt || (is_float && !is_wrap_op));
|
(is_float && !is_wrap_op);
|
||||||
|
|
||||||
if (!ok_type) {
|
if (!ok_type) {
|
||||||
const char *fmt = is_wrap_op ? "invalid wrapping negation type: '%s'" : "invalid negation type: '%s'";
|
const char *fmt = is_wrap_op ? "invalid wrapping negation type: '%s'" : "invalid negation type: '%s'";
|
||||||
|
|
@ -21677,7 +21670,7 @@ static ErrorMsg *ir_eval_negation_scalar(IrAnalyze *ira, IrInst* source_instr, Z
|
||||||
float_negate(scalar_out_val, operand_val);
|
float_negate(scalar_out_val, operand_val);
|
||||||
} else if (is_wrap_op) {
|
} else if (is_wrap_op) {
|
||||||
bigint_negate_wrap(&scalar_out_val->data.x_bigint, &operand_val->data.x_bigint,
|
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 {
|
} else {
|
||||||
bigint_negate(&scalar_out_val->data.x_bigint, &operand_val->data.x_bigint);
|
bigint_negate(&scalar_out_val->data.x_bigint, &operand_val->data.x_bigint);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -946,10 +946,7 @@ static AstNode *ast_parse_statement(ParseContext *pc) {
|
||||||
|
|
||||||
Token *suspend = eat_token_if(pc, TokenIdKeywordSuspend);
|
Token *suspend = eat_token_if(pc, TokenIdKeywordSuspend);
|
||||||
if (suspend != nullptr) {
|
if (suspend != nullptr) {
|
||||||
AstNode *statement = nullptr;
|
AstNode *statement = ast_expect(pc, ast_parse_block_expr_statement);
|
||||||
if (eat_token_if(pc, TokenIdSemicolon) == nullptr)
|
|
||||||
statement = ast_expect(pc, ast_parse_block_expr_statement);
|
|
||||||
|
|
||||||
AstNode *res = ast_create_node(pc, NodeTypeSuspend, suspend);
|
AstNode *res = ast_create_node(pc, NodeTypeSuspend, suspend);
|
||||||
res->data.suspend.block = statement;
|
res->data.suspend.block = statement;
|
||||||
return res;
|
return res;
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,21 @@
|
||||||
#include "softfloat_ext.hpp"
|
#include "softfloat_ext.hpp"
|
||||||
|
#include "zigendian.h"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "softfloat.h"
|
#include "softfloat.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
void f128M_abs(const float128_t *aPtr, float128_t *zPtr) {
|
void f128M_abs(const float128_t *aPtr, float128_t *zPtr) {
|
||||||
float128_t zero_float;
|
// Clear the sign bit.
|
||||||
ui32_to_f128M(0, &zero_float);
|
#if ZIG_BYTE_ORDER == ZIG_LITTLE_ENDIAN
|
||||||
if (f128M_lt(aPtr, &zero_float)) {
|
zPtr->v[1] = aPtr->v[1] & ~(UINT64_C(1) << 63);
|
||||||
f128M_sub(&zero_float, aPtr, zPtr);
|
zPtr->v[0] = aPtr->v[0];
|
||||||
} else {
|
#elif ZIG_BYTE_ORDER == ZIG_BIG_ENDIAN
|
||||||
*zPtr = *aPtr;
|
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) {
|
void f128M_trunc(const float128_t *aPtr, float128_t *zPtr) {
|
||||||
|
|
@ -22,4 +26,24 @@ void f128M_trunc(const float128_t *aPtr, float128_t *zPtr) {
|
||||||
} else {
|
} else {
|
||||||
f128M_roundToInt(aPtr, softfloat_round_min, false, zPtr);
|
f128M_roundToInt(aPtr, softfloat_round_min, false, zPtr);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float16_t f16_neg(const float16_t a) {
|
||||||
|
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) {
|
||||||
|
// 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
|
||||||
}
|
}
|
||||||
|
|
@ -5,5 +5,8 @@
|
||||||
|
|
||||||
void f128M_abs(const float128_t *aPtr, float128_t *zPtr);
|
void f128M_abs(const float128_t *aPtr, float128_t *zPtr);
|
||||||
void f128M_trunc(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
|
#endif
|
||||||
|
|
@ -1353,10 +1353,14 @@ fn transCreatePointerArithmeticSignedOp(
|
||||||
|
|
||||||
const bitcast_node = try usizeCastForWrappingPtrArithmetic(c.arena, rhs_node);
|
const bitcast_node = try usizeCastForWrappingPtrArithmetic(c.arena, rhs_node);
|
||||||
|
|
||||||
const arith_args = .{ .lhs = lhs_node, .rhs = bitcast_node };
|
return transCreateNodeInfixOp(
|
||||||
const arith_node = try if (is_add) Tag.add.create(c.arena, arith_args) else Tag.sub.create(c.arena, arith_args);
|
c,
|
||||||
|
scope,
|
||||||
return maybeSuppressResult(c, scope, result_used, arith_node);
|
if (is_add) .add else .sub,
|
||||||
|
lhs_node,
|
||||||
|
bitcast_node,
|
||||||
|
result_used,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transBinaryOperator(
|
fn transBinaryOperator(
|
||||||
|
|
@ -2161,8 +2165,8 @@ fn transCCast(
|
||||||
return Tag.as.create(c.arena, .{ .lhs = dst_node, .rhs = bool_to_int });
|
return Tag.as.create(c.arena, .{ .lhs = dst_node, .rhs = bool_to_int });
|
||||||
}
|
}
|
||||||
if (cIsEnum(dst_type)) {
|
if (cIsEnum(dst_type)) {
|
||||||
// @intToEnum(dest_type, val)
|
// import("std").meta.cast(dest_type, val)
|
||||||
return Tag.int_to_enum.create(c.arena, .{ .lhs = dst_node, .rhs = expr });
|
return Tag.std_meta_cast.create(c.arena, .{ .lhs = dst_node, .rhs = expr });
|
||||||
}
|
}
|
||||||
if (cIsEnum(src_type) and !cIsEnum(dst_type)) {
|
if (cIsEnum(src_type) and !cIsEnum(dst_type)) {
|
||||||
// @enumToInt(val)
|
// @enumToInt(val)
|
||||||
|
|
|
||||||
|
|
@ -1665,7 +1665,7 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
|
||||||
},
|
},
|
||||||
.array_access => {
|
.array_access => {
|
||||||
const payload = node.castTag(.array_access).?.data;
|
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 l_bracket = try c.addToken(.l_bracket, "[");
|
||||||
const index_expr = try renderNode(c, payload.rhs);
|
const index_expr = try renderNode(c, payload.rhs);
|
||||||
_ = try c.addToken(.r_bracket, "]");
|
_ = try c.addToken(.r_bracket, "]");
|
||||||
|
|
@ -1728,7 +1728,7 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
|
||||||
},
|
},
|
||||||
.field_access => {
|
.field_access => {
|
||||||
const payload = node.castTag(.field_access).?.data;
|
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);
|
return renderFieldAccess(c, lhs, payload.field_name);
|
||||||
},
|
},
|
||||||
.@"struct", .@"union" => return renderRecord(c, node),
|
.@"struct", .@"union" => return renderRecord(c, node),
|
||||||
|
|
@ -2073,7 +2073,7 @@ fn renderNullSentinelArrayType(c: *Context, len: usize, elem_type: Node) !NodeIn
|
||||||
.main_token = l_bracket,
|
.main_token = l_bracket,
|
||||||
.data = .{
|
.data = .{
|
||||||
.lhs = len_expr,
|
.lhs = len_expr,
|
||||||
.rhs = try c.addExtra(std.zig.ast.Node.ArrayTypeSentinel {
|
.rhs = try c.addExtra(std.zig.ast.Node.ArrayTypeSentinel{
|
||||||
.sentinel = sentinel_expr,
|
.sentinel = sentinel_expr,
|
||||||
.elem_type = elem_type_expr,
|
.elem_type = elem_type_expr,
|
||||||
}),
|
}),
|
||||||
|
|
|
||||||
|
|
@ -1021,7 +1021,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||||
\\export fn entry() void {
|
\\export fn entry() void {
|
||||||
\\ nosuspend {
|
\\ nosuspend {
|
||||||
\\ const bar = async foo();
|
\\ const bar = async foo();
|
||||||
\\ suspend;
|
\\ suspend {}
|
||||||
\\ resume bar;
|
\\ resume bar;
|
||||||
\\ }
|
\\ }
|
||||||
\\}
|
\\}
|
||||||
|
|
@ -2120,7 +2120,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||||
\\ non_async_fn = func;
|
\\ non_async_fn = func;
|
||||||
\\}
|
\\}
|
||||||
\\fn func() void {
|
\\fn func() void {
|
||||||
\\ suspend;
|
\\ suspend {}
|
||||||
\\}
|
\\}
|
||||||
, &[_][]const u8{
|
, &[_][]const u8{
|
||||||
"tmp.zig:5:1: error: 'func' cannot be async",
|
"tmp.zig:5:1: error: 'func' cannot be async",
|
||||||
|
|
@ -2198,7 +2198,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||||
\\ var x: anyframe = &f;
|
\\ var x: anyframe = &f;
|
||||||
\\}
|
\\}
|
||||||
\\fn func() void {
|
\\fn func() void {
|
||||||
\\ suspend;
|
\\ suspend {}
|
||||||
\\}
|
\\}
|
||||||
, &[_][]const u8{
|
, &[_][]const u8{
|
||||||
"tmp.zig:3:12: error: expected type 'anyframe', found '*const @Frame(func)'",
|
"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();
|
\\ frame = async bar();
|
||||||
\\}
|
\\}
|
||||||
\\fn foo() void {
|
\\fn foo() void {
|
||||||
\\ suspend;
|
\\ suspend {}
|
||||||
\\}
|
\\}
|
||||||
\\fn bar() void {
|
\\fn bar() void {
|
||||||
\\ suspend;
|
\\ suspend {}
|
||||||
\\}
|
\\}
|
||||||
, &[_][]const u8{
|
, &[_][]const u8{
|
||||||
"tmp.zig:3:13: error: expected type '*@Frame(bar)', found '*@Frame(foo)'",
|
"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;
|
\\ var result = await frame;
|
||||||
\\}
|
\\}
|
||||||
\\fn func() void {
|
\\fn func() void {
|
||||||
\\ suspend;
|
\\ suspend {}
|
||||||
\\}
|
\\}
|
||||||
, &[_][]const u8{
|
, &[_][]const u8{
|
||||||
"tmp.zig:1:1: error: function with calling convention 'C' cannot be async",
|
"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();
|
\\ bar();
|
||||||
\\}
|
\\}
|
||||||
\\fn bar() void {
|
\\fn bar() void {
|
||||||
\\ suspend;
|
\\ suspend {}
|
||||||
\\}
|
\\}
|
||||||
, &[_][]const u8{
|
, &[_][]const u8{
|
||||||
"tmp.zig:1:1: error: function with calling convention 'C' cannot be async",
|
"tmp.zig:1:1: error: function with calling convention 'C' cannot be async",
|
||||||
|
|
|
||||||
|
|
@ -1453,4 +1453,27 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
|
||||||
\\ return 0;
|
\\ return 0;
|
||||||
\\}
|
\\}
|
||||||
, "");
|
, "");
|
||||||
|
|
||||||
|
cases.add("Cast to enum from larger integral type. Issue #6011",
|
||||||
|
\\#include <stdint.h>
|
||||||
|
\\#include <stdlib.h>
|
||||||
|
\\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;
|
||||||
|
\\}
|
||||||
|
, "");
|
||||||
|
|
||||||
|
cases.add("Render array LHS as grouped node if necessary",
|
||||||
|
\\#include <stdlib.h>
|
||||||
|
\\int main(void) {
|
||||||
|
\\ int arr[] = {40, 41, 42, 43};
|
||||||
|
\\ if ((arr + 1)[1] != 42) abort();
|
||||||
|
\\ return 0;
|
||||||
|
\\}
|
||||||
|
, "");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||||
|
|
||||||
cases.addRuntimeSafety("switch on corrupted enum value",
|
cases.addRuntimeSafety("switch on corrupted enum value",
|
||||||
\\const std = @import("std");
|
\\const std = @import("std");
|
||||||
++ check_panic_msg ++
|
++ check_panic_msg ++
|
||||||
\\const E = enum(u32) {
|
\\const E = enum(u32) {
|
||||||
\\ X = 1,
|
\\ X = 1,
|
||||||
\\};
|
\\};
|
||||||
|
|
@ -28,7 +28,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||||
|
|
||||||
cases.addRuntimeSafety("switch on corrupted union value",
|
cases.addRuntimeSafety("switch on corrupted union value",
|
||||||
\\const std = @import("std");
|
\\const std = @import("std");
|
||||||
++ check_panic_msg ++
|
++ check_panic_msg ++
|
||||||
\\const U = union(enum(u32)) {
|
\\const U = union(enum(u32)) {
|
||||||
\\ X: u8,
|
\\ X: u8,
|
||||||
\\};
|
\\};
|
||||||
|
|
@ -54,7 +54,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||||
|
|
||||||
cases.addRuntimeSafety("@tagName on corrupted enum value",
|
cases.addRuntimeSafety("@tagName on corrupted enum value",
|
||||||
\\const std = @import("std");
|
\\const std = @import("std");
|
||||||
++ check_panic_msg ++
|
++ check_panic_msg ++
|
||||||
\\const E = enum(u32) {
|
\\const E = enum(u32) {
|
||||||
\\ X = 1,
|
\\ X = 1,
|
||||||
\\};
|
\\};
|
||||||
|
|
@ -67,7 +67,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||||
|
|
||||||
cases.addRuntimeSafety("@tagName on corrupted union value",
|
cases.addRuntimeSafety("@tagName on corrupted union value",
|
||||||
\\const std = @import("std");
|
\\const std = @import("std");
|
||||||
++ check_panic_msg ++
|
++ check_panic_msg ++
|
||||||
\\const U = union(enum(u32)) {
|
\\const U = union(enum(u32)) {
|
||||||
\\ X: u8,
|
\\ X: u8,
|
||||||
\\};
|
\\};
|
||||||
|
|
@ -92,7 +92,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||||
|
|
||||||
cases.addRuntimeSafety("slicing operator with sentinel",
|
cases.addRuntimeSafety("slicing operator with sentinel",
|
||||||
\\const std = @import("std");
|
\\const std = @import("std");
|
||||||
++ check_panic_msg ++
|
++ check_panic_msg ++
|
||||||
\\pub fn main() void {
|
\\pub fn main() void {
|
||||||
\\ var buf = [4]u8{'a','b','c',0};
|
\\ var buf = [4]u8{'a','b','c',0};
|
||||||
\\ const slice = buf[0..4 :0];
|
\\ const slice = buf[0..4 :0];
|
||||||
|
|
@ -100,7 +100,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||||
);
|
);
|
||||||
cases.addRuntimeSafety("slicing operator with sentinel",
|
cases.addRuntimeSafety("slicing operator with sentinel",
|
||||||
\\const std = @import("std");
|
\\const std = @import("std");
|
||||||
++ check_panic_msg ++
|
++ check_panic_msg ++
|
||||||
\\pub fn main() void {
|
\\pub fn main() void {
|
||||||
\\ var buf = [4]u8{'a','b','c',0};
|
\\ var buf = [4]u8{'a','b','c',0};
|
||||||
\\ const slice = buf[0..:0];
|
\\ const slice = buf[0..:0];
|
||||||
|
|
@ -108,7 +108,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||||
);
|
);
|
||||||
cases.addRuntimeSafety("slicing operator with sentinel",
|
cases.addRuntimeSafety("slicing operator with sentinel",
|
||||||
\\const std = @import("std");
|
\\const std = @import("std");
|
||||||
++ check_panic_msg ++
|
++ check_panic_msg ++
|
||||||
\\pub fn main() void {
|
\\pub fn main() void {
|
||||||
\\ var buf_zero = [0]u8{};
|
\\ var buf_zero = [0]u8{};
|
||||||
\\ const slice = buf_zero[0..0 :0];
|
\\ const slice = buf_zero[0..0 :0];
|
||||||
|
|
@ -116,7 +116,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||||
);
|
);
|
||||||
cases.addRuntimeSafety("slicing operator with sentinel",
|
cases.addRuntimeSafety("slicing operator with sentinel",
|
||||||
\\const std = @import("std");
|
\\const std = @import("std");
|
||||||
++ check_panic_msg ++
|
++ check_panic_msg ++
|
||||||
\\pub fn main() void {
|
\\pub fn main() void {
|
||||||
\\ var buf_zero = [0]u8{};
|
\\ var buf_zero = [0]u8{};
|
||||||
\\ const slice = buf_zero[0..:0];
|
\\ const slice = buf_zero[0..:0];
|
||||||
|
|
@ -124,7 +124,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||||
);
|
);
|
||||||
cases.addRuntimeSafety("slicing operator with sentinel",
|
cases.addRuntimeSafety("slicing operator with sentinel",
|
||||||
\\const std = @import("std");
|
\\const std = @import("std");
|
||||||
++ check_panic_msg ++
|
++ check_panic_msg ++
|
||||||
\\pub fn main() void {
|
\\pub fn main() void {
|
||||||
\\ var buf_sentinel = [2:0]u8{'a','b'};
|
\\ var buf_sentinel = [2:0]u8{'a','b'};
|
||||||
\\ @ptrCast(*[3]u8, &buf_sentinel)[2] = 0;
|
\\ @ptrCast(*[3]u8, &buf_sentinel)[2] = 0;
|
||||||
|
|
@ -133,7 +133,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||||
);
|
);
|
||||||
cases.addRuntimeSafety("slicing operator with sentinel",
|
cases.addRuntimeSafety("slicing operator with sentinel",
|
||||||
\\const std = @import("std");
|
\\const std = @import("std");
|
||||||
++ check_panic_msg ++
|
++ check_panic_msg ++
|
||||||
\\pub fn main() void {
|
\\pub fn main() void {
|
||||||
\\ var buf_slice: []const u8 = &[3]u8{ 'a', 'b', 0 };
|
\\ var buf_slice: []const u8 = &[3]u8{ 'a', 'b', 0 };
|
||||||
\\ const slice = buf_slice[0..3 :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",
|
cases.addRuntimeSafety("slicing operator with sentinel",
|
||||||
\\const std = @import("std");
|
\\const std = @import("std");
|
||||||
++ check_panic_msg ++
|
++ check_panic_msg ++
|
||||||
\\pub fn main() void {
|
\\pub fn main() void {
|
||||||
\\ var buf_slice: []const u8 = &[3]u8{ 'a', 'b', 0 };
|
\\ var buf_slice: []const u8 = &[3]u8{ 'a', 'b', 0 };
|
||||||
\\ const slice = buf_slice[0.. :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 {
|
\\fn add(a: i32, b: i32) i32 {
|
||||||
\\ if (a > 100) {
|
\\ if (a > 100) {
|
||||||
\\ suspend;
|
\\ suspend {}
|
||||||
\\ }
|
\\ }
|
||||||
\\ return a + b;
|
\\ return a + b;
|
||||||
\\}
|
\\}
|
||||||
|
|
@ -407,7 +407,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||||
\\ var frame = @asyncCall(&bytes, {}, ptr, .{});
|
\\ var frame = @asyncCall(&bytes, {}, ptr, .{});
|
||||||
\\}
|
\\}
|
||||||
\\fn other() callconv(.Async) void {
|
\\fn other() callconv(.Async) void {
|
||||||
\\ suspend;
|
\\ suspend {}
|
||||||
\\}
|
\\}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -424,7 +424,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||||
\\ await frame;
|
\\ await frame;
|
||||||
\\}
|
\\}
|
||||||
\\fn other() void {
|
\\fn other() void {
|
||||||
\\ suspend;
|
\\ suspend {}
|
||||||
\\}
|
\\}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -440,7 +440,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||||
\\ other();
|
\\ other();
|
||||||
\\}
|
\\}
|
||||||
\\fn other() void {
|
\\fn other() void {
|
||||||
\\ suspend;
|
\\ suspend {}
|
||||||
\\}
|
\\}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -454,7 +454,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||||
\\ resume p; //bad
|
\\ resume p; //bad
|
||||||
\\}
|
\\}
|
||||||
\\fn suspendOnce() void {
|
\\fn suspendOnce() void {
|
||||||
\\ suspend;
|
\\ suspend {}
|
||||||
\\}
|
\\}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -1019,7 +1019,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||||
\\}
|
\\}
|
||||||
\\
|
\\
|
||||||
\\fn failing() anyerror!void {
|
\\fn failing() anyerror!void {
|
||||||
\\ suspend;
|
\\ suspend {}
|
||||||
\\ return second();
|
\\ return second();
|
||||||
\\}
|
\\}
|
||||||
\\
|
\\
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,9 @@ test "simple coroutine suspend and resume" {
|
||||||
}
|
}
|
||||||
fn simpleAsyncFn() void {
|
fn simpleAsyncFn() void {
|
||||||
global_x += 1;
|
global_x += 1;
|
||||||
suspend;
|
suspend {}
|
||||||
global_x += 1;
|
global_x += 1;
|
||||||
suspend;
|
suspend {}
|
||||||
global_x += 1;
|
global_x += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -34,7 +34,7 @@ test "pass parameter to coroutine" {
|
||||||
}
|
}
|
||||||
fn simpleAsyncFnWithArg(delta: i32) void {
|
fn simpleAsyncFnWithArg(delta: i32) void {
|
||||||
global_y += delta;
|
global_y += delta;
|
||||||
suspend;
|
suspend {}
|
||||||
global_y += delta;
|
global_y += delta;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -50,7 +50,7 @@ test "suspend at end of function" {
|
||||||
|
|
||||||
fn suspendAtEnd() void {
|
fn suspendAtEnd() void {
|
||||||
x += 1;
|
x += 1;
|
||||||
suspend;
|
suspend {}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
S.doTheTest();
|
S.doTheTest();
|
||||||
|
|
@ -74,11 +74,11 @@ test "local variable in async function" {
|
||||||
|
|
||||||
fn add(a: i32, b: i32) void {
|
fn add(a: i32, b: i32) void {
|
||||||
var accum: i32 = 0;
|
var accum: i32 = 0;
|
||||||
suspend;
|
suspend {}
|
||||||
accum += a;
|
accum += a;
|
||||||
suspend;
|
suspend {}
|
||||||
accum += b;
|
accum += b;
|
||||||
suspend;
|
suspend {}
|
||||||
x = accum;
|
x = accum;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -102,7 +102,7 @@ test "calling an inferred async function" {
|
||||||
}
|
}
|
||||||
fn other() void {
|
fn other() void {
|
||||||
other_frame = @frame();
|
other_frame = @frame();
|
||||||
suspend;
|
suspend {}
|
||||||
x += 1;
|
x += 1;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -129,7 +129,7 @@ test "@frameSize" {
|
||||||
}
|
}
|
||||||
fn other(param: i32) void {
|
fn other(param: i32) void {
|
||||||
var local: i32 = undefined;
|
var local: i32 = undefined;
|
||||||
suspend;
|
suspend {}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
S.doTheTest();
|
S.doTheTest();
|
||||||
|
|
@ -269,7 +269,7 @@ test "async function with dot syntax" {
|
||||||
var y: i32 = 1;
|
var y: i32 = 1;
|
||||||
fn foo() callconv(.Async) void {
|
fn foo() callconv(.Async) void {
|
||||||
y += 1;
|
y += 1;
|
||||||
suspend;
|
suspend {}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const p = async S.foo();
|
const p = async S.foo();
|
||||||
|
|
@ -298,7 +298,7 @@ fn doTheAwait(f: anyframe->void) void {
|
||||||
fn simpleAsyncFn2(y: *i32) callconv(.Async) void {
|
fn simpleAsyncFn2(y: *i32) callconv(.Async) void {
|
||||||
defer y.* += 2;
|
defer y.* += 2;
|
||||||
y.* += 1;
|
y.* += 1;
|
||||||
suspend;
|
suspend {}
|
||||||
}
|
}
|
||||||
|
|
||||||
test "@asyncCall with return type" {
|
test "@asyncCall with return type" {
|
||||||
|
|
@ -312,7 +312,7 @@ test "@asyncCall with return type" {
|
||||||
|
|
||||||
fn afunc() i32 {
|
fn afunc() i32 {
|
||||||
global_frame = @frame();
|
global_frame = @frame();
|
||||||
suspend;
|
suspend {}
|
||||||
return 1234;
|
return 1234;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -348,7 +348,7 @@ test "async fn with inferred error set" {
|
||||||
|
|
||||||
fn failing() !void {
|
fn failing() !void {
|
||||||
global_frame = @frame();
|
global_frame = @frame();
|
||||||
suspend;
|
suspend {}
|
||||||
return error.Fail;
|
return error.Fail;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -375,7 +375,7 @@ fn nonFailing() (anyframe->anyerror!void) {
|
||||||
return &Static.frame;
|
return &Static.frame;
|
||||||
}
|
}
|
||||||
fn suspendThenFail() callconv(.Async) anyerror!void {
|
fn suspendThenFail() callconv(.Async) anyerror!void {
|
||||||
suspend;
|
suspend {}
|
||||||
return error.Fail;
|
return error.Fail;
|
||||||
}
|
}
|
||||||
fn printTrace(p: anyframe->(anyerror!void)) callconv(.Async) void {
|
fn printTrace(p: anyframe->(anyerror!void)) callconv(.Async) void {
|
||||||
|
|
@ -400,7 +400,7 @@ fn testBreakFromSuspend(my_result: *i32) callconv(.Async) void {
|
||||||
resume @frame();
|
resume @frame();
|
||||||
}
|
}
|
||||||
my_result.* += 1;
|
my_result.* += 1;
|
||||||
suspend;
|
suspend {}
|
||||||
my_result.* += 1;
|
my_result.* += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -421,7 +421,7 @@ test "heap allocated async function frame" {
|
||||||
|
|
||||||
fn someFunc() void {
|
fn someFunc() void {
|
||||||
x += 1;
|
x += 1;
|
||||||
suspend;
|
suspend {}
|
||||||
x += 1;
|
x += 1;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -454,7 +454,7 @@ test "async function call return value" {
|
||||||
|
|
||||||
fn other(x: i32, y: i32) Point {
|
fn other(x: i32, y: i32) Point {
|
||||||
frame = @frame();
|
frame = @frame();
|
||||||
suspend;
|
suspend {}
|
||||||
return Point{
|
return Point{
|
||||||
.x = x,
|
.x = x,
|
||||||
.y = y,
|
.y = y,
|
||||||
|
|
@ -487,7 +487,7 @@ test "suspension points inside branching control flow" {
|
||||||
|
|
||||||
fn func(b: bool) void {
|
fn func(b: bool) void {
|
||||||
while (b) {
|
while (b) {
|
||||||
suspend;
|
suspend {}
|
||||||
result += 1;
|
result += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -541,7 +541,7 @@ test "pass string literal to async function" {
|
||||||
|
|
||||||
fn hello(msg: []const u8) void {
|
fn hello(msg: []const u8) void {
|
||||||
frame = @frame();
|
frame = @frame();
|
||||||
suspend;
|
suspend {}
|
||||||
expectEqualStrings("hello", msg);
|
expectEqualStrings("hello", msg);
|
||||||
ok = true;
|
ok = true;
|
||||||
}
|
}
|
||||||
|
|
@ -566,7 +566,7 @@ test "await inside an errdefer" {
|
||||||
|
|
||||||
fn func() void {
|
fn func() void {
|
||||||
frame = @frame();
|
frame = @frame();
|
||||||
suspend;
|
suspend {}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
S.doTheTest();
|
S.doTheTest();
|
||||||
|
|
@ -590,7 +590,7 @@ test "try in an async function with error union and non-zero-bit payload" {
|
||||||
|
|
||||||
fn theProblem() ![]u8 {
|
fn theProblem() ![]u8 {
|
||||||
frame = @frame();
|
frame = @frame();
|
||||||
suspend;
|
suspend {}
|
||||||
const result = try other();
|
const result = try other();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
@ -622,7 +622,7 @@ test "returning a const error from async function" {
|
||||||
|
|
||||||
fn fetchUrl(unused: i32, url: []const u8) ![]u8 {
|
fn fetchUrl(unused: i32, url: []const u8) ![]u8 {
|
||||||
frame = @frame();
|
frame = @frame();
|
||||||
suspend;
|
suspend {}
|
||||||
ok = true;
|
ok = true;
|
||||||
return error.OutOfMemory;
|
return error.OutOfMemory;
|
||||||
}
|
}
|
||||||
|
|
@ -967,7 +967,7 @@ test "@asyncCall with comptime-known function, but not awaited directly" {
|
||||||
|
|
||||||
fn failing() !void {
|
fn failing() !void {
|
||||||
global_frame = @frame();
|
global_frame = @frame();
|
||||||
suspend;
|
suspend {}
|
||||||
return error.Fail;
|
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" {
|
test "@asyncCall with actual frame instead of byte buffer" {
|
||||||
const S = struct {
|
const S = struct {
|
||||||
fn func() i32 {
|
fn func() i32 {
|
||||||
suspend;
|
suspend {}
|
||||||
return 1234;
|
return 1234;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -993,7 +993,7 @@ test "@asyncCall using the result location inside the frame" {
|
||||||
fn simple2(y: *i32) callconv(.Async) i32 {
|
fn simple2(y: *i32) callconv(.Async) i32 {
|
||||||
defer y.* += 2;
|
defer y.* += 2;
|
||||||
y.* += 1;
|
y.* += 1;
|
||||||
suspend;
|
suspend {}
|
||||||
return 1234;
|
return 1234;
|
||||||
}
|
}
|
||||||
fn getAnswer(f: anyframe->i32, out: *i32) void {
|
fn getAnswer(f: anyframe->i32, out: *i32) void {
|
||||||
|
|
@ -1095,7 +1095,7 @@ test "nosuspend function call" {
|
||||||
}
|
}
|
||||||
fn add(a: i32, b: i32) i32 {
|
fn add(a: i32, b: i32) i32 {
|
||||||
if (a > 100) {
|
if (a > 100) {
|
||||||
suspend;
|
suspend {}
|
||||||
}
|
}
|
||||||
return a + b;
|
return a + b;
|
||||||
}
|
}
|
||||||
|
|
@ -1170,7 +1170,7 @@ test "suspend in for loop" {
|
||||||
global_frame = @frame();
|
global_frame = @frame();
|
||||||
var sum: u32 = 0;
|
var sum: u32 = 0;
|
||||||
for (stuff) |x| {
|
for (stuff) |x| {
|
||||||
suspend;
|
suspend {}
|
||||||
sum += x;
|
sum += x;
|
||||||
}
|
}
|
||||||
global_frame = null;
|
global_frame = null;
|
||||||
|
|
@ -1197,7 +1197,7 @@ test "suspend in while loop" {
|
||||||
global_frame = @frame();
|
global_frame = @frame();
|
||||||
defer global_frame = null;
|
defer global_frame = null;
|
||||||
while (stuff) |val| {
|
while (stuff) |val| {
|
||||||
suspend;
|
suspend {}
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -1206,7 +1206,7 @@ test "suspend in while loop" {
|
||||||
global_frame = @frame();
|
global_frame = @frame();
|
||||||
defer global_frame = null;
|
defer global_frame = null;
|
||||||
while (stuff) |val| {
|
while (stuff) |val| {
|
||||||
suspend;
|
suspend {}
|
||||||
return val;
|
return val;
|
||||||
} else |err| {
|
} else |err| {
|
||||||
return 0;
|
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 {
|
fn bar(x: i32, args: anytype) anyerror!void {
|
||||||
global_frame = @frame();
|
global_frame = @frame();
|
||||||
suspend;
|
suspend {}
|
||||||
global_int = x;
|
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 {
|
fn bar(x: u64, args: anytype) anyerror!void {
|
||||||
expect(x == 10);
|
expect(x == 10);
|
||||||
global_frame = @frame();
|
global_frame = @frame();
|
||||||
suspend;
|
suspend {}
|
||||||
global_int = args[0];
|
global_int = args[0];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -1383,7 +1383,7 @@ test "async function call resolves target fn frame, comptime func" {
|
||||||
|
|
||||||
fn bar() anyerror!void {
|
fn bar() anyerror!void {
|
||||||
global_frame = @frame();
|
global_frame = @frame();
|
||||||
suspend;
|
suspend {}
|
||||||
global_int += 1;
|
global_int += 1;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -1406,7 +1406,7 @@ test "async function call resolves target fn frame, runtime func" {
|
||||||
|
|
||||||
fn bar() anyerror!void {
|
fn bar() anyerror!void {
|
||||||
global_frame = @frame();
|
global_frame = @frame();
|
||||||
suspend;
|
suspend {}
|
||||||
global_int += 1;
|
global_int += 1;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -1430,7 +1430,7 @@ test "properly spill optional payload capture value" {
|
||||||
|
|
||||||
fn bar() void {
|
fn bar() void {
|
||||||
global_frame = @frame();
|
global_frame = @frame();
|
||||||
suspend;
|
suspend {}
|
||||||
global_int += 1;
|
global_int += 1;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -1466,13 +1466,13 @@ test "handle defer interfering with return value spill" {
|
||||||
|
|
||||||
fn bar() anyerror!void {
|
fn bar() anyerror!void {
|
||||||
global_frame1 = @frame();
|
global_frame1 = @frame();
|
||||||
suspend;
|
suspend {}
|
||||||
return error.Bad;
|
return error.Bad;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn baz() void {
|
fn baz() void {
|
||||||
global_frame2 = @frame();
|
global_frame2 = @frame();
|
||||||
suspend;
|
suspend {}
|
||||||
baz_happened = true;
|
baz_happened = true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -1497,7 +1497,7 @@ test "take address of temporary async frame" {
|
||||||
|
|
||||||
fn foo(arg: i32) i32 {
|
fn foo(arg: i32) i32 {
|
||||||
global_frame = @frame();
|
global_frame = @frame();
|
||||||
suspend;
|
suspend {}
|
||||||
return arg + 1234;
|
return arg + 1234;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1520,7 +1520,7 @@ test "nosuspend await" {
|
||||||
|
|
||||||
fn foo(want_suspend: bool) i32 {
|
fn foo(want_suspend: bool) i32 {
|
||||||
if (want_suspend) {
|
if (want_suspend) {
|
||||||
suspend;
|
suspend {}
|
||||||
}
|
}
|
||||||
return 42;
|
return 42;
|
||||||
}
|
}
|
||||||
|
|
@ -1569,11 +1569,11 @@ test "nosuspend on async function calls" {
|
||||||
// };
|
// };
|
||||||
// const S1 = struct {
|
// const S1 = struct {
|
||||||
// fn c() S0 {
|
// fn c() S0 {
|
||||||
// suspend;
|
// suspend {}
|
||||||
// return S0{};
|
// return S0{};
|
||||||
// }
|
// }
|
||||||
// fn d() !S0 {
|
// fn d() !S0 {
|
||||||
// suspend;
|
// suspend {}
|
||||||
// return S0{};
|
// return S0{};
|
||||||
// }
|
// }
|
||||||
// };
|
// };
|
||||||
|
|
@ -1591,11 +1591,11 @@ test "nosuspend resume async function calls" {
|
||||||
};
|
};
|
||||||
const S1 = struct {
|
const S1 = struct {
|
||||||
fn c() S0 {
|
fn c() S0 {
|
||||||
suspend;
|
suspend {}
|
||||||
return S0{};
|
return S0{};
|
||||||
}
|
}
|
||||||
fn d() !S0 {
|
fn d() !S0 {
|
||||||
suspend;
|
suspend {}
|
||||||
return S0{};
|
return S0{};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -229,16 +229,26 @@ fn testSignedWrappingEval(x: i32) void {
|
||||||
expect(max_val == maxInt(i32));
|
expect(max_val == maxInt(i32));
|
||||||
}
|
}
|
||||||
|
|
||||||
test "negation wrapping" {
|
test "signed negation wrapping" {
|
||||||
testNegationWrappingEval(minInt(i16));
|
testSignedNegationWrappingEval(minInt(i16));
|
||||||
comptime testNegationWrappingEval(minInt(i16));
|
comptime testSignedNegationWrappingEval(minInt(i16));
|
||||||
}
|
}
|
||||||
fn testNegationWrappingEval(x: i16) void {
|
fn testSignedNegationWrappingEval(x: i16) void {
|
||||||
expect(x == -32768);
|
expect(x == -32768);
|
||||||
const neg = -%x;
|
const neg = -%x;
|
||||||
expect(neg == -32768);
|
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 "unsigned 64-bit division" {
|
||||||
test_u64_div();
|
test_u64_div();
|
||||||
comptime test_u64_div();
|
comptime test_u64_div();
|
||||||
|
|
@ -843,3 +853,20 @@ test "compare undefined literal with comptime_int" {
|
||||||
x = true;
|
x = true;
|
||||||
expect(x);
|
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();
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -212,6 +212,22 @@ const test_targets = blk: {
|
||||||
// .link_libc = true,
|
// .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{
|
TestTarget{
|
||||||
.target = .{
|
.target = .{
|
||||||
.cpu_arch = .riscv64,
|
.cpu_arch = .riscv64,
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,22 @@ const std = @import("std");
|
||||||
const CrossTarget = std.zig.CrossTarget;
|
const CrossTarget = std.zig.CrossTarget;
|
||||||
|
|
||||||
pub fn addCases(cases: *tests.TranslateCContext) void {
|
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",
|
cases.add("unnamed child types of typedef receive typedef's name",
|
||||||
\\typedef enum {
|
\\typedef enum {
|
||||||
\\ FooA,
|
\\ FooA,
|
||||||
|
|
@ -111,7 +127,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||||
\\ const A = @enumToInt(enum_Foo.A);
|
\\ const A = @enumToInt(enum_Foo.A);
|
||||||
\\ const B = @enumToInt(enum_Foo.B);
|
\\ const B = @enumToInt(enum_Foo.B);
|
||||||
\\ const C = @enumToInt(enum_Foo.C);
|
\\ 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) {
|
\\ const enum_Foo = extern enum(c_int) {
|
||||||
\\ A,
|
\\ A,
|
||||||
|
|
@ -122,7 +138,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||||
\\ const A_2 = @enumToInt(enum_Foo.A);
|
\\ const A_2 = @enumToInt(enum_Foo.A);
|
||||||
\\ const B_3 = @enumToInt(enum_Foo.B);
|
\\ const B_3 = @enumToInt(enum_Foo.B);
|
||||||
\\ const C_4 = @enumToInt(enum_Foo.C);
|
\\ 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 +1692,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||||
\\pub const e = @enumToInt(enum_unnamed_1.e);
|
\\pub const e = @enumToInt(enum_unnamed_1.e);
|
||||||
\\pub const f = @enumToInt(enum_unnamed_1.f);
|
\\pub const f = @enumToInt(enum_unnamed_1.f);
|
||||||
\\pub const g = @enumToInt(enum_unnamed_1.g);
|
\\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) {
|
\\const enum_unnamed_2 = extern enum(c_int) {
|
||||||
\\ i,
|
\\ i,
|
||||||
\\ j,
|
\\ j,
|
||||||
|
|
@ -2308,7 +2324,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||||
\\ var a = arg_a;
|
\\ var a = arg_a;
|
||||||
\\ var b = arg_b;
|
\\ var b = arg_b;
|
||||||
\\ var c = arg_c;
|
\\ 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 e: c_int = @boolToInt((a != 0) and (b != 0));
|
||||||
\\ var f: c_int = @boolToInt((b != 0) and (c != null));
|
\\ var f: c_int = @boolToInt((b != 0) and (c != null));
|
||||||
\\ var g: c_int = @boolToInt((a != 0) and (c != null));
|
\\ var g: c_int = @boolToInt((a != 0) and (c != null));
|
||||||
|
|
|
||||||
|
|
@ -663,6 +663,12 @@ const llvm_targets = [_]LlvmTarget{
|
||||||
.zig_name = "powerpc",
|
.zig_name = "powerpc",
|
||||||
.llvm_name = "PowerPC",
|
.llvm_name = "PowerPC",
|
||||||
.td_name = "PPC.td",
|
.td_name = "PPC.td",
|
||||||
|
.feature_overrides = &.{
|
||||||
|
.{
|
||||||
|
.llvm_name = "ppc32",
|
||||||
|
.omit = true,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
.{
|
.{
|
||||||
.zig_name = "riscv",
|
.zig_name = "riscv",
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue