mirror of
https://github.com/ziglang/zig.git
synced 2025-12-27 16:43:07 +00:00
Merge remote-tracking branch 'origin/master' into stage2-whole-file-astgen
In particular I wanted to take advantage of the new hex float parsing code.
This commit is contained in:
commit
df24ce52b1
@ -44,6 +44,7 @@ pub fn build(b: *Builder) !void {
|
||||
|
||||
const fmt_build_zig = b.addFmt(&[_][]const u8{"build.zig"});
|
||||
|
||||
const skip_debug = b.option(bool, "skip-debug", "Main test suite skips debug builds") orelse false;
|
||||
const skip_release = b.option(bool, "skip-release", "Main test suite skips release builds") orelse false;
|
||||
const skip_release_small = b.option(bool, "skip-release-small", "Main test suite skips release-small builds") orelse skip_release;
|
||||
const skip_release_fast = b.option(bool, "skip-release-fast", "Main test suite skips release-fast builds") orelse skip_release;
|
||||
@ -240,8 +241,10 @@ pub fn build(b: *Builder) !void {
|
||||
|
||||
var chosen_modes: [4]builtin.Mode = undefined;
|
||||
var chosen_mode_index: usize = 0;
|
||||
chosen_modes[chosen_mode_index] = builtin.Mode.Debug;
|
||||
chosen_mode_index += 1;
|
||||
if (!skip_debug) {
|
||||
chosen_modes[chosen_mode_index] = builtin.Mode.Debug;
|
||||
chosen_mode_index += 1;
|
||||
}
|
||||
if (!skip_release_safe) {
|
||||
chosen_modes[chosen_mode_index] = builtin.Mode.ReleaseSafe;
|
||||
chosen_mode_index += 1;
|
||||
|
||||
@ -6,7 +6,38 @@ platform:
|
||||
arch: arm64
|
||||
|
||||
steps:
|
||||
- name: build-and-test
|
||||
- name: build
|
||||
image: ziglang/static-base:llvm12-aarch64-1
|
||||
commands:
|
||||
- ./ci/drone/linux_script_build
|
||||
|
||||
- name: test-1
|
||||
depends_on:
|
||||
- build
|
||||
image: ziglang/static-base:llvm12-aarch64-1
|
||||
commands:
|
||||
- ./ci/drone/linux_script_test 1
|
||||
|
||||
- name: test-2
|
||||
depends_on:
|
||||
- build
|
||||
image: ziglang/static-base:llvm12-aarch64-1
|
||||
commands:
|
||||
- ./ci/drone/linux_script_test 2
|
||||
|
||||
- name: test-3
|
||||
depends_on:
|
||||
- build
|
||||
image: ziglang/static-base:llvm12-aarch64-1
|
||||
commands:
|
||||
- ./ci/drone/linux_script_test 3
|
||||
|
||||
- name: finalize
|
||||
depends_on:
|
||||
- build
|
||||
- test-1
|
||||
- test-2
|
||||
- test-3
|
||||
image: ziglang/static-base:llvm12-aarch64-1
|
||||
environment:
|
||||
SRHT_OAUTH_TOKEN:
|
||||
@ -16,4 +47,4 @@ steps:
|
||||
AWS_SECRET_ACCESS_KEY:
|
||||
from_secret: AWS_SECRET_ACCESS_KEY
|
||||
commands:
|
||||
- ./ci/drone/linux_script
|
||||
- ./ci/drone/linux_script_finalize
|
||||
|
||||
@ -1,65 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -x
|
||||
set -e
|
||||
|
||||
TRIPLEARCH="$(uname -m)"
|
||||
BUILDDIR="$(pwd)"
|
||||
DISTDIR="$(pwd)/dist"
|
||||
|
||||
apk update
|
||||
apk add py3-pip xz perl-utils jq curl samurai
|
||||
pip3 install s3cmd
|
||||
|
||||
# Make the `zig version` number consistent.
|
||||
# This will affect the cmake command below.
|
||||
git config core.abbrev 9
|
||||
git fetch --unshallow || true
|
||||
git fetch --tags
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release "-DCMAKE_INSTALL_PREFIX=$DISTDIR" -DZIG_STATIC=ON -DCMAKE_PREFIX_PATH=/deps/local -GNinja
|
||||
|
||||
samu install
|
||||
# run-translated-c tests are skipped due to: https://github.com/ziglang/zig/issues/8537
|
||||
./zig build test \
|
||||
-Dskip-release \
|
||||
-Dskip-non-native \
|
||||
-Dskip-compile-errors \
|
||||
-Dskip-run-translated-c
|
||||
|
||||
if [ -z "$DRONE_PULL_REQUEST" ]; then
|
||||
mv ../LICENSE "$DISTDIR/"
|
||||
mv ../zig-cache/langref.html "$DISTDIR/"
|
||||
mv "$DISTDIR/bin/zig" "$DISTDIR/"
|
||||
rmdir "$DISTDIR/bin"
|
||||
|
||||
GITBRANCH="$DRONE_BRANCH"
|
||||
VERSION="$("$DISTDIR/zig" version)"
|
||||
DIRNAME="zig-linux-$TRIPLEARCH-$VERSION"
|
||||
TARBALL="$DIRNAME.tar.xz"
|
||||
mv "$DISTDIR" "$DIRNAME"
|
||||
tar cfJ "$TARBALL" "$DIRNAME"
|
||||
|
||||
s3cmd put -P --add-header="cache-control: public, max-age=31536000, immutable" "$TARBALL" s3://ziglang.org/builds/
|
||||
|
||||
SHASUM=$(shasum -a 256 $TARBALL | cut '-d ' -f1)
|
||||
BYTESIZE=$(wc -c < $TARBALL)
|
||||
|
||||
JSONFILE="$TRIPLEARCH-linux-$GITBRANCH.json"
|
||||
touch $JSONFILE
|
||||
echo "{\"tarball\": \"$TARBALL\"," >>$JSONFILE
|
||||
echo "\"shasum\": \"$SHASUM\"," >>$JSONFILE
|
||||
echo "\"size\": \"$BYTESIZE\"}" >>$JSONFILE
|
||||
|
||||
s3cmd put -P --add-header="Cache-Control: max-age=0, must-revalidate" "$JSONFILE" "s3://ziglang.org/builds/$JSONFILE"
|
||||
s3cmd put -P "$JSONFILE" "s3://ziglang.org/builds/$TRIPLEARCH-linux-$VERSION.json"
|
||||
if [ "$GITBRANCH" = "master" ]; then
|
||||
# avoid leaking oauth token
|
||||
set +x
|
||||
|
||||
cd "$BUILDDIR"
|
||||
./ci/srht/on_master_success "$VERSION" "$SRHT_OAUTH_TOKEN"
|
||||
fi
|
||||
fi
|
||||
22
ci/drone/linux_script_base
Executable file
22
ci/drone/linux_script_base
Executable file
@ -0,0 +1,22 @@
|
||||
#!/bin/sh
|
||||
|
||||
# https://docs.drone.io/pipeline/docker/syntax/workspace/
|
||||
#
|
||||
# Drone automatically creates a temporary volume, known as your workspace,
|
||||
# where it clones your repository. The workspace is the current working
|
||||
# directory for each step in your pipeline.
|
||||
#
|
||||
# Because the workspace is a volume, filesystem changes are persisted between
|
||||
# pipeline steps. In other words, individual steps can communicate and share
|
||||
# state using the filesystem.
|
||||
#
|
||||
# Workspace volumes are ephemeral. They are created when the pipeline starts
|
||||
# and destroyed after the pipeline completes.
|
||||
|
||||
set -x
|
||||
set -e
|
||||
|
||||
TRIPLEARCH="$(uname -m)"
|
||||
DISTDIR="$DRONE_WORKSPACE/dist"
|
||||
|
||||
export ZIG_GLOBAL_CACHE_DIR="$DRONE_WORKSPACE/zig-cache"
|
||||
18
ci/drone/linux_script_build
Executable file
18
ci/drone/linux_script_build
Executable file
@ -0,0 +1,18 @@
|
||||
#!/bin/sh
|
||||
|
||||
. ./ci/drone/linux_script_base
|
||||
|
||||
apk update
|
||||
apk add samurai
|
||||
|
||||
# Make the `zig version` number consistent.
|
||||
# This will affect the cmake command below.
|
||||
git config core.abbrev 9
|
||||
git fetch --unshallow || true
|
||||
git fetch --tags
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release "-DCMAKE_INSTALL_PREFIX=$DISTDIR" -DZIG_STATIC=ON -DCMAKE_PREFIX_PATH=/deps/local -GNinja
|
||||
|
||||
samu install
|
||||
46
ci/drone/linux_script_finalize
Executable file
46
ci/drone/linux_script_finalize
Executable file
@ -0,0 +1,46 @@
|
||||
#!/bin/sh
|
||||
|
||||
. ./ci/drone/linux_script_base
|
||||
|
||||
if [ -n "$DRONE_PULL_REQUEST" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
apk update
|
||||
apk add py3-pip xz perl-utils jq curl samurai
|
||||
pip3 install s3cmd
|
||||
|
||||
cd build
|
||||
|
||||
mv ../LICENSE "$DISTDIR/"
|
||||
# docs are disabled due to: https://github.com/ziglang/zig/issues/8597
|
||||
#mv ../zig-cache/langref.html "$DISTDIR/"
|
||||
mv "$DISTDIR/bin/zig" "$DISTDIR/"
|
||||
rmdir "$DISTDIR/bin"
|
||||
|
||||
GITBRANCH="$DRONE_BRANCH"
|
||||
VERSION="$("$DISTDIR/zig" version)"
|
||||
DIRNAME="zig-linux-$TRIPLEARCH-$VERSION"
|
||||
TARBALL="$DIRNAME.tar.xz"
|
||||
mv "$DISTDIR" "$DIRNAME"
|
||||
tar cfJ "$TARBALL" "$DIRNAME"
|
||||
|
||||
s3cmd put -P --add-header="cache-control: public, max-age=31536000, immutable" "$TARBALL" s3://ziglang.org/builds/
|
||||
|
||||
SHASUM=$(shasum -a 256 $TARBALL | cut '-d ' -f1)
|
||||
BYTESIZE=$(wc -c < $TARBALL)
|
||||
|
||||
JSONFILE="tarball.json"
|
||||
touch $JSONFILE
|
||||
echo "{\"tarball\": \"$TARBALL\"," >>$JSONFILE
|
||||
echo "\"shasum\": \"$SHASUM\"," >>$JSONFILE
|
||||
echo "\"size\": \"$BYTESIZE\"}" >>$JSONFILE
|
||||
|
||||
s3cmd put -P "$JSONFILE" "s3://ziglang.org/builds/$TRIPLEARCH-linux-$VERSION.json"
|
||||
if [ "$GITBRANCH" = "master" ]; then
|
||||
# avoid leaking oauth token
|
||||
set +x
|
||||
|
||||
cd "$DRONE_WORKSPACE"
|
||||
./ci/srht/on_master_success "$VERSION" "$SRHT_OAUTH_TOKEN"
|
||||
fi
|
||||
46
ci/drone/linux_script_test
Executable file
46
ci/drone/linux_script_test
Executable file
@ -0,0 +1,46 @@
|
||||
#!/bin/sh
|
||||
|
||||
. ./ci/drone/linux_script_base
|
||||
|
||||
# only release-fast builds of test suite due to: https://github.com/ziglang/zig/issues/8597
|
||||
#
|
||||
# Some test suite components will be missing because they do not support
|
||||
# forcing -OReleaseFast
|
||||
#
|
||||
# see `zig build --help` for the full list of test-* components
|
||||
case "$1" in
|
||||
1)
|
||||
steps="\
|
||||
test-stage2 \
|
||||
test-fmt \
|
||||
test-behavior"
|
||||
;;
|
||||
2)
|
||||
steps="test-std"
|
||||
;;
|
||||
3)
|
||||
steps="\
|
||||
test-compiler-rt \
|
||||
test-minilibc \
|
||||
test-compare-output \
|
||||
test-translate-c \
|
||||
test-run-translated-c"
|
||||
;;
|
||||
'')
|
||||
echo "error: expecting test group argument"
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
echo "error: unknown test group: $1"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# only release-fast builds of test suite due to: https://github.com/ziglang/zig/issues/8597
|
||||
./build/zig build \
|
||||
-Drelease \
|
||||
-Dskip-debug \
|
||||
-Dskip-release-small \
|
||||
-Dskip-release-safe \
|
||||
-Dskip-non-native \
|
||||
$steps
|
||||
@ -199,7 +199,8 @@ pub fn spawn(comptime startFn: anytype, context: SpawnContextType(@TypeOf(startF
|
||||
inner: Context,
|
||||
};
|
||||
fn threadMain(raw_arg: windows.LPVOID) callconv(.C) windows.DWORD {
|
||||
const arg = if (@sizeOf(Context) == 0) {} else @ptrCast(*Context, @alignCast(@alignOf(Context), raw_arg)).*;
|
||||
const arg = if (@sizeOf(Context) == 0) undefined //
|
||||
else @ptrCast(*Context, @alignCast(@alignOf(Context), raw_arg)).*;
|
||||
|
||||
switch (@typeInfo(@typeInfo(@TypeOf(startFn)).Fn.return_type.?)) {
|
||||
.NoReturn => {
|
||||
@ -260,7 +261,8 @@ pub fn spawn(comptime startFn: anytype, context: SpawnContextType(@TypeOf(startF
|
||||
|
||||
const MainFuncs = struct {
|
||||
fn linuxThreadMain(ctx_addr: usize) callconv(.C) u8 {
|
||||
const arg = if (@sizeOf(Context) == 0) {} else @intToPtr(*const Context, ctx_addr).*;
|
||||
const arg = if (@sizeOf(Context) == 0) undefined //
|
||||
else @intToPtr(*Context, ctx_addr).*;
|
||||
|
||||
switch (@typeInfo(@typeInfo(@TypeOf(startFn)).Fn.return_type.?)) {
|
||||
.NoReturn => {
|
||||
@ -292,7 +294,8 @@ pub fn spawn(comptime startFn: anytype, context: SpawnContextType(@TypeOf(startF
|
||||
}
|
||||
}
|
||||
fn posixThreadMain(ctx: ?*c_void) callconv(.C) ?*c_void {
|
||||
const arg = if (@sizeOf(Context) == 0) {} else @ptrCast(*Context, @alignCast(@alignOf(Context), ctx)).*;
|
||||
const arg = if (@sizeOf(Context) == 0) undefined //
|
||||
else @ptrCast(*Context, @alignCast(@alignOf(Context), ctx)).*;
|
||||
|
||||
switch (@typeInfo(@typeInfo(@TypeOf(startFn)).Fn.return_type.?)) {
|
||||
.NoReturn => {
|
||||
|
||||
@ -166,6 +166,7 @@ pub const CallingConvention = enum {
|
||||
APCS,
|
||||
AAPCS,
|
||||
AAPCSVFP,
|
||||
SysV
|
||||
};
|
||||
|
||||
/// This data structure is used by the Zig language code generation and
|
||||
|
||||
@ -69,3 +69,51 @@ pub const pthread_rwlock_t = extern struct {
|
||||
writer_count: i32 = 0,
|
||||
waiters: [2]?*c_void = [_]?*c_void{ null, null },
|
||||
};
|
||||
|
||||
pub const EAI = extern enum(c_int) {
|
||||
/// address family for hostname not supported
|
||||
ADDRFAMILY = 1,
|
||||
|
||||
/// name could not be resolved at this time
|
||||
AGAIN = 2,
|
||||
|
||||
/// flags parameter had an invalid value
|
||||
BADFLAGS = 3,
|
||||
|
||||
/// non-recoverable failure in name resolution
|
||||
FAIL = 4,
|
||||
|
||||
/// address family not recognized
|
||||
FAMILY = 5,
|
||||
|
||||
/// memory allocation failure
|
||||
MEMORY = 6,
|
||||
|
||||
/// no address associated with hostname
|
||||
NODATA = 7,
|
||||
|
||||
/// name does not resolve
|
||||
NONAME = 8,
|
||||
|
||||
/// service not recognized for socket type
|
||||
SERVICE = 9,
|
||||
|
||||
/// intended socket type was not recognized
|
||||
SOCKTYPE = 10,
|
||||
|
||||
/// system error returned in errno
|
||||
SYSTEM = 11,
|
||||
|
||||
/// invalid value for hints
|
||||
BADHINTS = 12,
|
||||
|
||||
/// resolved protocol is unknown
|
||||
PROTOCOL = 13,
|
||||
|
||||
/// argument buffer overflow
|
||||
OVERFLOW = 14,
|
||||
|
||||
_,
|
||||
};
|
||||
|
||||
pub const EAI_MAX = 15;
|
||||
|
||||
@ -264,7 +264,7 @@ pub const ChildProcess = struct {
|
||||
|
||||
// TODO collect output in a deadlock-avoiding way on Windows.
|
||||
// https://github.com/ziglang/zig/issues/6343
|
||||
if (builtin.os.tag == .windows) {
|
||||
if (builtin.os.tag == .windows or builtin.os.tag == .haiku) {
|
||||
const stdout_in = child.stdout.?.reader();
|
||||
const stderr_in = child.stderr.?.reader();
|
||||
|
||||
|
||||
@ -75,16 +75,8 @@ pub const Edwards25519 = struct {
|
||||
.is_base = true,
|
||||
};
|
||||
|
||||
/// The edwards25519 neutral element.
|
||||
pub const neutralElement = Edwards25519{
|
||||
.x = Fe{ .limbs = .{ 2251799813685229, 2251799813685247, 2251799813685247, 2251799813685247, 2251799813685247 } },
|
||||
.y = Fe{ .limbs = .{ 1507481815385608, 2223447444246085, 1083941587175919, 2059929906842505, 1581435440146976 } },
|
||||
.z = Fe{ .limbs = .{ 1507481815385608, 2223447444246085, 1083941587175919, 2059929906842505, 1581435440146976 } },
|
||||
.t = Fe{ .limbs = .{ 2251799813685229, 2251799813685247, 2251799813685247, 2251799813685247, 2251799813685247 } },
|
||||
.is_base = false,
|
||||
};
|
||||
|
||||
const identityElement = Edwards25519{ .x = Fe.zero, .y = Fe.one, .z = Fe.one, .t = Fe.zero };
|
||||
pub const neutralElement = @compileError("deprecated: use identityElement instead");
|
||||
pub const identityElement = Edwards25519{ .x = Fe.zero, .y = Fe.one, .z = Fe.one, .t = Fe.zero };
|
||||
|
||||
/// Reject the neutral element.
|
||||
pub fn rejectIdentity(p: Edwards25519) IdentityElementError!void {
|
||||
@ -160,9 +152,10 @@ pub const Edwards25519 = struct {
|
||||
return t;
|
||||
}
|
||||
|
||||
fn nonAdjacentForm(s: [32]u8) [2 * 32]i8 {
|
||||
fn slide(s: [32]u8) [2 * 32]i8 {
|
||||
const reduced = if ((s[s.len - 1] & 0x80) != 0) s else scalar.reduce(s);
|
||||
var e: [2 * 32]i8 = undefined;
|
||||
for (s) |x, i| {
|
||||
for (reduced) |x, i| {
|
||||
e[i * 2 + 0] = @as(i8, @truncate(u4, x));
|
||||
e[i * 2 + 1] = @as(i8, @truncate(u4, x >> 4));
|
||||
}
|
||||
@ -185,7 +178,7 @@ pub const Edwards25519 = struct {
|
||||
// avoid these to keep the standard library lightweight.
|
||||
fn pcMul(pc: [9]Edwards25519, s: [32]u8, comptime vartime: bool) IdentityElementError!Edwards25519 {
|
||||
std.debug.assert(vartime);
|
||||
const e = nonAdjacentForm(s);
|
||||
const e = slide(s);
|
||||
var q = Edwards25519.identityElement;
|
||||
var pos: usize = 2 * 32 - 1;
|
||||
while (true) : (pos -= 1) {
|
||||
@ -280,8 +273,8 @@ pub const Edwards25519 = struct {
|
||||
xpc[4].rejectIdentity() catch return error.WeakPublicKey;
|
||||
break :pc xpc;
|
||||
};
|
||||
const e1 = nonAdjacentForm(s1);
|
||||
const e2 = nonAdjacentForm(s2);
|
||||
const e1 = slide(s1);
|
||||
const e2 = slide(s2);
|
||||
var q = Edwards25519.identityElement;
|
||||
var pos: usize = 2 * 32 - 1;
|
||||
while (true) : (pos -= 1) {
|
||||
@ -318,7 +311,7 @@ pub const Edwards25519 = struct {
|
||||
}
|
||||
var es: [count][2 * 32]i8 = undefined;
|
||||
for (ss) |s, i| {
|
||||
es[i] = nonAdjacentForm(s);
|
||||
es[i] = slide(s);
|
||||
}
|
||||
var q = Edwards25519.identityElement;
|
||||
var pos: usize = 2 * 32 - 1;
|
||||
|
||||
@ -355,7 +355,7 @@ pub const Fe = struct {
|
||||
return fe;
|
||||
}
|
||||
|
||||
/// Compute the inverse of a field element
|
||||
/// Return the inverse of a field element, or 0 if a=0.
|
||||
pub fn invert(a: Fe) Fe {
|
||||
var t0 = a.sq();
|
||||
var t1 = t0.sqn(2).mul(a);
|
||||
|
||||
@ -98,7 +98,7 @@ pub fn sub(a: [32]u8, b: [32]u8) [32]u8 {
|
||||
return add(a, neg(b));
|
||||
}
|
||||
|
||||
/// A scalar in unpacked reprentation
|
||||
/// A scalar in unpacked representation
|
||||
pub const Scalar = struct {
|
||||
const Limbs = [5]u64;
|
||||
limbs: Limbs = undefined,
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
const std = @import("../std.zig");
|
||||
const debug = std.debug;
|
||||
const mem = std.mem;
|
||||
const testing = std.testing;
|
||||
|
||||
const Endian = std.builtin.Endian;
|
||||
const Order = std.math.Order;
|
||||
|
||||
/// Compares two arrays in constant time (for a given length) and returns whether they are equal.
|
||||
/// This function was designed to compare short cryptographic secrets (MACs, signatures).
|
||||
/// For all other applications, use mem.eql() instead.
|
||||
@ -38,6 +42,41 @@ pub fn timingSafeEql(comptime T: type, a: T, b: T) bool {
|
||||
}
|
||||
}
|
||||
|
||||
/// Compare two integers serialized as arrays of the same size, in constant time.
|
||||
/// Returns .lt if a<b, .gt if a>b and .eq if a=b
|
||||
pub fn timingSafeCompare(comptime T: type, a: []const T, b: []const T, endian: Endian) Order {
|
||||
debug.assert(a.len == b.len);
|
||||
const bits = switch (@typeInfo(T)) {
|
||||
.Int => |cinfo| if (cinfo.signedness != .unsigned) @compileError("Elements to be compared must be unsigned") else cinfo.bits,
|
||||
else => @compileError("Elements to be compared must be integers"),
|
||||
};
|
||||
comptime const Cext = std.meta.Int(.unsigned, bits + 1);
|
||||
var gt: T = 0;
|
||||
var eq: T = 1;
|
||||
if (endian == .Little) {
|
||||
var i = a.len;
|
||||
while (i != 0) {
|
||||
i -= 1;
|
||||
const x1 = a[i];
|
||||
const x2 = b[i];
|
||||
gt |= @truncate(T, (@as(Cext, x2) -% @as(Cext, x1)) >> bits) & eq;
|
||||
eq &= @truncate(T, (@as(Cext, (x2 ^ x1)) -% 1) >> bits);
|
||||
}
|
||||
} else {
|
||||
for (a) |x1, i| {
|
||||
const x2 = b[i];
|
||||
gt |= @truncate(T, (@as(Cext, x2) -% @as(Cext, x1)) >> bits) & eq;
|
||||
eq &= @truncate(T, (@as(Cext, (x2 ^ x1)) -% 1) >> bits);
|
||||
}
|
||||
}
|
||||
if (gt != 0) {
|
||||
return Order.gt;
|
||||
} else if (eq != 0) {
|
||||
return Order.eq;
|
||||
}
|
||||
return Order.lt;
|
||||
}
|
||||
|
||||
/// Sets a slice to zeroes.
|
||||
/// Prevents the store from being optimized out.
|
||||
pub fn secureZero(comptime T: type, s: []T) void {
|
||||
@ -70,6 +109,19 @@ test "crypto.utils.timingSafeEql (vectors)" {
|
||||
testing.expect(timingSafeEql(std.meta.Vector(100, u8), v1, v3));
|
||||
}
|
||||
|
||||
test "crypto.utils.timingSafeCompare" {
|
||||
var a = [_]u8{10} ** 32;
|
||||
var b = [_]u8{10} ** 32;
|
||||
testing.expectEqual(timingSafeCompare(u8, &a, &b, .Big), .eq);
|
||||
testing.expectEqual(timingSafeCompare(u8, &a, &b, .Little), .eq);
|
||||
a[31] = 1;
|
||||
testing.expectEqual(timingSafeCompare(u8, &a, &b, .Big), .lt);
|
||||
testing.expectEqual(timingSafeCompare(u8, &a, &b, .Little), .lt);
|
||||
a[0] = 20;
|
||||
testing.expectEqual(timingSafeCompare(u8, &a, &b, .Big), .gt);
|
||||
testing.expectEqual(timingSafeCompare(u8, &a, &b, .Little), .lt);
|
||||
}
|
||||
|
||||
test "crypto.utils.secureZero" {
|
||||
var a = [_]u8{0xfe} ** 8;
|
||||
var b = [_]u8{0xfe} ** 8;
|
||||
|
||||
@ -339,6 +339,13 @@ pub const StackIterator = struct {
|
||||
fp: usize,
|
||||
|
||||
pub fn init(first_address: ?usize, fp: ?usize) StackIterator {
|
||||
if (native_arch == .sparcv9) {
|
||||
// Flush all the register windows on stack.
|
||||
asm volatile (
|
||||
\\ flushw
|
||||
::: "memory");
|
||||
}
|
||||
|
||||
return StackIterator{
|
||||
.first_address = first_address,
|
||||
.fp = fp orelse @frameAddress(),
|
||||
@ -346,18 +353,18 @@ pub const StackIterator = struct {
|
||||
}
|
||||
|
||||
// Offset of the saved BP wrt the frame pointer.
|
||||
const fp_offset = if (native_arch.isRISCV())
|
||||
const fp_offset = if (comptime native_arch.isRISCV())
|
||||
// On RISC-V the frame pointer points to the top of the saved register
|
||||
// area, on pretty much every other architecture it points to the stack
|
||||
// slot where the previous frame pointer is saved.
|
||||
2 * @sizeOf(usize)
|
||||
else if (native_arch.isSPARC())
|
||||
else if (comptime native_arch.isSPARC())
|
||||
// On SPARC the previous frame pointer is stored at 14 slots past %fp+BIAS.
|
||||
14 * @sizeOf(usize)
|
||||
else
|
||||
0;
|
||||
|
||||
const fp_bias = if (native_arch.isSPARC())
|
||||
const fp_bias = if (comptime native_arch.isSPARC())
|
||||
// On SPARC frame pointers are biased by a constant.
|
||||
2047
|
||||
else
|
||||
@ -383,7 +390,7 @@ pub const StackIterator = struct {
|
||||
}
|
||||
|
||||
fn next_internal(self: *StackIterator) ?usize {
|
||||
const fp = if (native_arch.isSPARC())
|
||||
const fp = if (comptime native_arch.isSPARC())
|
||||
// On SPARC the offset is positive. (!)
|
||||
math.add(usize, self.fp, fp_offset) catch return null
|
||||
else
|
||||
|
||||
@ -1523,9 +1523,11 @@ test "parseUnsigned" {
|
||||
}
|
||||
|
||||
pub const parseFloat = @import("fmt/parse_float.zig").parseFloat;
|
||||
pub const parseHexFloat = @import("fmt/parse_hex_float.zig").parseHexFloat;
|
||||
|
||||
test "parseFloat" {
|
||||
test {
|
||||
_ = @import("fmt/parse_float.zig");
|
||||
_ = @import("fmt/parse_hex_float.zig");
|
||||
}
|
||||
|
||||
pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) {
|
||||
|
||||
352
lib/std/fmt/parse_hex_float.zig
Normal file
352
lib/std/fmt/parse_hex_float.zig
Normal file
@ -0,0 +1,352 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2015-2021 Zig Contributors
|
||||
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
||||
// The MIT license requires this copyright notice to be included in all copies
|
||||
// and substantial portions of the software.const std = @import("std");
|
||||
//
|
||||
// The rounding logic is inspired by LLVM's APFloat and Go's atofHex
|
||||
// implementation.
|
||||
|
||||
const std = @import("std");
|
||||
const ascii = std.ascii;
|
||||
const fmt = std.fmt;
|
||||
const math = std.math;
|
||||
const testing = std.testing;
|
||||
|
||||
const assert = std.debug.assert;
|
||||
|
||||
pub fn parseHexFloat(comptime T: type, s: []const u8) !T {
|
||||
assert(@typeInfo(T) == .Float);
|
||||
|
||||
const IntT = std.meta.Int(.unsigned, @typeInfo(T).Float.bits);
|
||||
|
||||
const mantissa_bits = math.floatMantissaBits(T);
|
||||
const exponent_bits = math.floatExponentBits(T);
|
||||
|
||||
const sign_shift = mantissa_bits + exponent_bits;
|
||||
|
||||
const exponent_bias = (1 << (exponent_bits - 1)) - 1;
|
||||
const exponent_min = 1 - exponent_bias;
|
||||
const exponent_max = exponent_bias;
|
||||
|
||||
if (s.len == 0)
|
||||
return error.InvalidCharacter;
|
||||
|
||||
if (ascii.eqlIgnoreCase(s, "nan")) {
|
||||
return math.nan(T);
|
||||
} else if (ascii.eqlIgnoreCase(s, "inf") or ascii.eqlIgnoreCase(s, "+inf")) {
|
||||
return math.inf(T);
|
||||
} else if (ascii.eqlIgnoreCase(s, "-inf")) {
|
||||
return -math.inf(T);
|
||||
}
|
||||
|
||||
var negative: bool = false;
|
||||
var exp_negative: bool = false;
|
||||
|
||||
var mantissa: u128 = 0;
|
||||
var exponent: i16 = 0;
|
||||
var frac_scale: i16 = 0;
|
||||
|
||||
const State = enum {
|
||||
MaybeSign,
|
||||
Prefix,
|
||||
LeadingIntegerDigit,
|
||||
IntegerDigit,
|
||||
MaybeDot,
|
||||
LeadingFractionDigit,
|
||||
FractionDigit,
|
||||
ExpPrefix,
|
||||
MaybeExpSign,
|
||||
ExpDigit,
|
||||
};
|
||||
|
||||
var state = State.MaybeSign;
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < s.len) {
|
||||
const c = s[i];
|
||||
|
||||
switch (state) {
|
||||
.MaybeSign => {
|
||||
state = .Prefix;
|
||||
|
||||
if (c == '+') {
|
||||
i += 1;
|
||||
} else if (c == '-') {
|
||||
negative = true;
|
||||
i += 1;
|
||||
}
|
||||
},
|
||||
.Prefix => {
|
||||
state = .LeadingIntegerDigit;
|
||||
|
||||
// Match both 0x and 0X.
|
||||
if (i + 2 > s.len or s[i] != '0' or s[i + 1] | 32 != 'x')
|
||||
return error.InvalidCharacter;
|
||||
i += 2;
|
||||
},
|
||||
.LeadingIntegerDigit => {
|
||||
if (c == '0') {
|
||||
// Skip leading zeros.
|
||||
i += 1;
|
||||
} else if (c == '_') {
|
||||
return error.InvalidCharacter;
|
||||
} else {
|
||||
state = .IntegerDigit;
|
||||
}
|
||||
},
|
||||
.IntegerDigit => {
|
||||
if (ascii.isXDigit(c)) {
|
||||
if (mantissa >= math.maxInt(u128) / 16)
|
||||
return error.Overflow;
|
||||
mantissa *%= 16;
|
||||
mantissa += try fmt.charToDigit(c, 16);
|
||||
i += 1;
|
||||
} else if (c == '_') {
|
||||
i += 1;
|
||||
} else {
|
||||
state = .MaybeDot;
|
||||
}
|
||||
},
|
||||
.MaybeDot => {
|
||||
if (c == '.') {
|
||||
state = .LeadingFractionDigit;
|
||||
i += 1;
|
||||
} else state = .ExpPrefix;
|
||||
},
|
||||
.LeadingFractionDigit => {
|
||||
if (c == '_') {
|
||||
return error.InvalidCharacter;
|
||||
} else state = .FractionDigit;
|
||||
},
|
||||
.FractionDigit => {
|
||||
if (ascii.isXDigit(c)) {
|
||||
if (mantissa < math.maxInt(u128) / 16) {
|
||||
mantissa *%= 16;
|
||||
mantissa +%= try fmt.charToDigit(c, 16);
|
||||
frac_scale += 1;
|
||||
} else if (c != '0') {
|
||||
return error.Overflow;
|
||||
}
|
||||
i += 1;
|
||||
} else if (c == '_') {
|
||||
i += 1;
|
||||
} else {
|
||||
state = .ExpPrefix;
|
||||
}
|
||||
},
|
||||
.ExpPrefix => {
|
||||
state = .MaybeExpSign;
|
||||
// Match both p and P.
|
||||
if (c | 32 != 'p')
|
||||
return error.InvalidCharacter;
|
||||
i += 1;
|
||||
},
|
||||
.MaybeExpSign => {
|
||||
state = .ExpDigit;
|
||||
|
||||
if (c == '+') {
|
||||
i += 1;
|
||||
} else if (c == '-') {
|
||||
exp_negative = true;
|
||||
i += 1;
|
||||
}
|
||||
},
|
||||
.ExpDigit => {
|
||||
if (ascii.isXDigit(c)) {
|
||||
if (exponent >= math.maxInt(i16) / 10)
|
||||
return error.Overflow;
|
||||
exponent *%= 10;
|
||||
exponent +%= try fmt.charToDigit(c, 10);
|
||||
i += 1;
|
||||
} else if (c == '_') {
|
||||
i += 1;
|
||||
} else {
|
||||
return error.InvalidCharacter;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if (exp_negative)
|
||||
exponent *= -1;
|
||||
|
||||
// Bring the decimal part to the left side of the decimal dot.
|
||||
exponent -= frac_scale * 4;
|
||||
|
||||
if (mantissa == 0) {
|
||||
// Signed zero.
|
||||
return if (negative) -0.0 else 0.0;
|
||||
}
|
||||
|
||||
// Divide by 2^mantissa_bits to right-align the mantissa in the fractional
|
||||
// part.
|
||||
exponent += mantissa_bits;
|
||||
|
||||
// Keep around two extra bits to correctly round any value that doesn't fit
|
||||
// the available mantissa bits. The result LSB serves as Guard bit, the
|
||||
// following one is the Round bit and the last one is the Sticky bit,
|
||||
// computed by OR-ing all the dropped bits.
|
||||
|
||||
// Normalize by aligning the implicit one bit.
|
||||
while (mantissa >> (mantissa_bits + 2) == 0) {
|
||||
mantissa <<= 1;
|
||||
exponent -= 1;
|
||||
}
|
||||
|
||||
// Normalize again by dropping the excess precision.
|
||||
// Note that the discarded bits are folded into the Sticky bit.
|
||||
while (mantissa >> (mantissa_bits + 2 + 1) != 0) {
|
||||
mantissa = mantissa >> 1 | (mantissa & 1);
|
||||
exponent += 1;
|
||||
}
|
||||
|
||||
// Very small numbers can be possibly represented as denormals, reduce the
|
||||
// exponent as much as possible.
|
||||
while (mantissa != 0 and exponent < exponent_min - 2) {
|
||||
mantissa = mantissa >> 1 | (mantissa & 1);
|
||||
exponent += 1;
|
||||
}
|
||||
|
||||
// There are two cases to handle:
|
||||
// - We've truncated more than 0.5ULP (R=S=1), increase the mantissa.
|
||||
// - We've truncated exactly 0.5ULP (R=1 S=0), increase the mantissa if the
|
||||
// result is odd (G=1).
|
||||
// The two checks can be neatly folded as follows.
|
||||
mantissa |= @boolToInt(mantissa & 0b100 != 0);
|
||||
mantissa += 1;
|
||||
|
||||
mantissa >>= 2;
|
||||
exponent += 2;
|
||||
|
||||
if (mantissa & (1 << (mantissa_bits + 1)) != 0) {
|
||||
// Renormalize, if the exponent overflows we'll catch that below.
|
||||
mantissa >>= 1;
|
||||
exponent += 1;
|
||||
}
|
||||
|
||||
if (mantissa >> mantissa_bits == 0) {
|
||||
// This is a denormal number, the biased exponent is zero.
|
||||
exponent = -exponent_bias;
|
||||
}
|
||||
|
||||
if (exponent > exponent_max) {
|
||||
// Overflow, return +inf.
|
||||
return math.inf(T);
|
||||
}
|
||||
|
||||
// Remove the implicit bit.
|
||||
mantissa &= @as(u128, (1 << mantissa_bits) - 1);
|
||||
|
||||
const raw: IntT =
|
||||
(if (negative) @as(IntT, 1) << sign_shift else 0) |
|
||||
@as(IntT, @bitCast(u16, exponent + exponent_bias)) << mantissa_bits |
|
||||
@truncate(IntT, mantissa);
|
||||
|
||||
return @bitCast(T, raw);
|
||||
}
|
||||
|
||||
test "special" {
|
||||
testing.expect(math.isNan(try parseHexFloat(f32, "nAn")));
|
||||
testing.expect(math.isPositiveInf(try parseHexFloat(f32, "iNf")));
|
||||
testing.expect(math.isPositiveInf(try parseHexFloat(f32, "+Inf")));
|
||||
testing.expect(math.isNegativeInf(try parseHexFloat(f32, "-iNf")));
|
||||
}
|
||||
test "zero" {
|
||||
testing.expectEqual(@as(f32, 0.0), try parseHexFloat(f32, "0x0"));
|
||||
testing.expectEqual(@as(f32, 0.0), try parseHexFloat(f32, "-0x0"));
|
||||
testing.expectEqual(@as(f32, 0.0), try parseHexFloat(f32, "0x0p42"));
|
||||
testing.expectEqual(@as(f32, 0.0), try parseHexFloat(f32, "-0x0.00000p42"));
|
||||
testing.expectEqual(@as(f32, 0.0), try parseHexFloat(f32, "0x0.00000p666"));
|
||||
}
|
||||
|
||||
test "f16" {
|
||||
const Case = struct { s: []const u8, v: f16 };
|
||||
const cases: []const Case = &[_]Case{
|
||||
.{ .s = "0x1p0", .v = 1.0 },
|
||||
.{ .s = "-0x1p-1", .v = -0.5 },
|
||||
.{ .s = "0x10p+10", .v = 16384.0 },
|
||||
.{ .s = "0x10p-10", .v = 0.015625 },
|
||||
// Max normalized value.
|
||||
.{ .s = "0x1.ffcp+15", .v = math.f16_max },
|
||||
.{ .s = "-0x1.ffcp+15", .v = -math.f16_max },
|
||||
// Min normalized value.
|
||||
.{ .s = "0x1p-14", .v = math.f16_min },
|
||||
.{ .s = "-0x1p-14", .v = -math.f16_min },
|
||||
// Min denormal value.
|
||||
.{ .s = "0x1p-24", .v = math.f16_true_min },
|
||||
.{ .s = "-0x1p-24", .v = -math.f16_true_min },
|
||||
};
|
||||
|
||||
for (cases) |case| {
|
||||
testing.expectEqual(case.v, try parseHexFloat(f16, case.s));
|
||||
}
|
||||
}
|
||||
test "f32" {
|
||||
const Case = struct { s: []const u8, v: f32 };
|
||||
const cases: []const Case = &[_]Case{
|
||||
.{ .s = "0x1p0", .v = 1.0 },
|
||||
.{ .s = "-0x1p-1", .v = -0.5 },
|
||||
.{ .s = "0x10p+10", .v = 16384.0 },
|
||||
.{ .s = "0x10p-10", .v = 0.015625 },
|
||||
.{ .s = "0x0.ffffffp128", .v = 0x0.ffffffp128 },
|
||||
.{ .s = "0x0.1234570p-125", .v = 0x0.1234570p-125 },
|
||||
// Max normalized value.
|
||||
.{ .s = "0x1.fffffeP+127", .v = math.f32_max },
|
||||
.{ .s = "-0x1.fffffeP+127", .v = -math.f32_max },
|
||||
// Min normalized value.
|
||||
.{ .s = "0x1p-126", .v = math.f32_min },
|
||||
.{ .s = "-0x1p-126", .v = -math.f32_min },
|
||||
// Min denormal value.
|
||||
.{ .s = "0x1P-149", .v = math.f32_true_min },
|
||||
.{ .s = "-0x1P-149", .v = -math.f32_true_min },
|
||||
};
|
||||
|
||||
for (cases) |case| {
|
||||
testing.expectEqual(case.v, try parseHexFloat(f32, case.s));
|
||||
}
|
||||
}
|
||||
test "f64" {
|
||||
const Case = struct { s: []const u8, v: f64 };
|
||||
const cases: []const Case = &[_]Case{
|
||||
.{ .s = "0x1p0", .v = 1.0 },
|
||||
.{ .s = "-0x1p-1", .v = -0.5 },
|
||||
.{ .s = "0x10p+10", .v = 16384.0 },
|
||||
.{ .s = "0x10p-10", .v = 0.015625 },
|
||||
// Max normalized value.
|
||||
.{ .s = "0x1.fffffffffffffp+1023", .v = math.f64_max },
|
||||
.{ .s = "-0x1.fffffffffffffp1023", .v = -math.f64_max },
|
||||
// Min normalized value.
|
||||
.{ .s = "0x1p-1022", .v = math.f64_min },
|
||||
.{ .s = "-0x1p-1022", .v = -math.f64_min },
|
||||
// Min denormalized value.
|
||||
.{ .s = "0x1p-1074", .v = math.f64_true_min },
|
||||
.{ .s = "-0x1p-1074", .v = -math.f64_true_min },
|
||||
};
|
||||
|
||||
for (cases) |case| {
|
||||
testing.expectEqual(case.v, try parseHexFloat(f64, case.s));
|
||||
}
|
||||
}
|
||||
test "f128" {
|
||||
const Case = struct { s: []const u8, v: f128 };
|
||||
const cases: []const Case = &[_]Case{
|
||||
.{ .s = "0x1p0", .v = 1.0 },
|
||||
.{ .s = "-0x1p-1", .v = -0.5 },
|
||||
.{ .s = "0x10p+10", .v = 16384.0 },
|
||||
.{ .s = "0x10p-10", .v = 0.015625 },
|
||||
// Max normalized value.
|
||||
.{ .s = "0xf.fffffffffffffffffffffffffff8p+16380", .v = math.f128_max },
|
||||
.{ .s = "-0xf.fffffffffffffffffffffffffff8p+16380", .v = -math.f128_max },
|
||||
// Min normalized value.
|
||||
.{ .s = "0x1p-16382", .v = math.f128_min },
|
||||
.{ .s = "-0x1p-16382", .v = -math.f128_min },
|
||||
// // Min denormalized value.
|
||||
.{ .s = "0x1p-16494", .v = math.f128_true_min },
|
||||
.{ .s = "-0x1p-16494", .v = -math.f128_true_min },
|
||||
};
|
||||
|
||||
for (cases) |case| {
|
||||
testing.expectEqual(@bitCast(u128, case.v), @bitCast(u128, try parseHexFloat(f128, case.s)));
|
||||
}
|
||||
}
|
||||
@ -29,8 +29,7 @@ fn ok(s: []const u8) !void {
|
||||
fn err(s: []const u8) void {
|
||||
testing.expect(!json.validate(s));
|
||||
|
||||
testNonStreaming(s) catch return;
|
||||
testing.expect(false);
|
||||
testing.expect(std.meta.isError(testNonStreaming(s)));
|
||||
}
|
||||
|
||||
fn utf8Error(s: []const u8) void {
|
||||
@ -48,8 +47,7 @@ fn any(s: []const u8) void {
|
||||
fn anyStreamingErrNonStreaming(s: []const u8) void {
|
||||
_ = json.validate(s);
|
||||
|
||||
testNonStreaming(s) catch return;
|
||||
testing.expect(false);
|
||||
testing.expect(std.meta.isError(testNonStreaming(s)));
|
||||
}
|
||||
|
||||
fn roundTrip(s: []const u8) !void {
|
||||
|
||||
@ -1877,7 +1877,11 @@ test "rotate" {
|
||||
|
||||
/// Replace needle with replacement as many times as possible, writing to an output buffer which is assumed to be of
|
||||
/// appropriate size. Use replacementSize to calculate an appropriate buffer size.
|
||||
/// The needle must not be empty.
|
||||
pub fn replace(comptime T: type, input: []const T, needle: []const T, replacement: []const T, output: []T) usize {
|
||||
// Empty needle will loop until output buffer overflows.
|
||||
assert(needle.len > 0);
|
||||
|
||||
var i: usize = 0;
|
||||
var slide: usize = 0;
|
||||
var replacements: usize = 0;
|
||||
@ -1900,22 +1904,48 @@ pub fn replace(comptime T: type, input: []const T, needle: []const T, replacemen
|
||||
test "replace" {
|
||||
var output: [29]u8 = undefined;
|
||||
var replacements = replace(u8, "All your base are belong to us", "base", "Zig", output[0..]);
|
||||
var expected: []const u8 = "All your Zig are belong to us";
|
||||
testing.expect(replacements == 1);
|
||||
testing.expect(eql(u8, output[0..], "All your Zig are belong to us"));
|
||||
testing.expectEqualStrings(expected, output[0..expected.len]);
|
||||
|
||||
replacements = replace(u8, "Favor reading code over writing code.", "code", "", output[0..]);
|
||||
expected = "Favor reading over writing .";
|
||||
testing.expect(replacements == 2);
|
||||
testing.expect(eql(u8, output[0..], "Favor reading over writing ."));
|
||||
testing.expectEqualStrings(expected, output[0..expected.len]);
|
||||
|
||||
// Empty needle is not allowed but input may be empty.
|
||||
replacements = replace(u8, "", "x", "y", output[0..0]);
|
||||
expected = "";
|
||||
testing.expect(replacements == 0);
|
||||
testing.expectEqualStrings(expected, output[0..expected.len]);
|
||||
|
||||
// Adjacent replacements.
|
||||
|
||||
replacements = replace(u8, "\\n\\n", "\\n", "\n", output[0..]);
|
||||
expected = "\n\n";
|
||||
testing.expect(replacements == 2);
|
||||
testing.expectEqualStrings(expected, output[0..expected.len]);
|
||||
|
||||
replacements = replace(u8, "abbba", "b", "cd", output[0..]);
|
||||
expected = "acdcdcda";
|
||||
testing.expect(replacements == 3);
|
||||
testing.expectEqualStrings(expected, output[0..expected.len]);
|
||||
}
|
||||
|
||||
/// Calculate the size needed in an output buffer to perform a replacement.
|
||||
/// The needle must not be empty.
|
||||
pub fn replacementSize(comptime T: type, input: []const T, needle: []const T, replacement: []const T) usize {
|
||||
// Empty needle will loop forever.
|
||||
assert(needle.len > 0);
|
||||
|
||||
var i: usize = 0;
|
||||
var size: usize = input.len;
|
||||
while (i < input.len) : (i += 1) {
|
||||
while (i < input.len) {
|
||||
if (mem.indexOf(T, input[i..], needle) == @as(usize, 0)) {
|
||||
size = size - needle.len + replacement.len;
|
||||
i += needle.len;
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1924,9 +1954,15 @@ pub fn replacementSize(comptime T: type, input: []const T, needle: []const T, re
|
||||
|
||||
test "replacementSize" {
|
||||
testing.expect(replacementSize(u8, "All your base are belong to us", "base", "Zig") == 29);
|
||||
testing.expect(replacementSize(u8, "", "", "") == 0);
|
||||
testing.expect(replacementSize(u8, "Favor reading code over writing code.", "code", "") == 29);
|
||||
testing.expect(replacementSize(u8, "Only one obvious way to do things.", "things.", "things in Zig.") == 41);
|
||||
|
||||
// Empty needle is not allowed but input may be empty.
|
||||
testing.expect(replacementSize(u8, "", "x", "y") == 0);
|
||||
|
||||
// Adjacent replacements.
|
||||
testing.expect(replacementSize(u8, "\\n\\n", "\\n", "\n") == 2);
|
||||
testing.expect(replacementSize(u8, "abbba", "b", "cd") == 8);
|
||||
}
|
||||
|
||||
/// Perform a replacement on an allocated buffer of pre-determined size. Caller must free returned memory.
|
||||
|
||||
@ -117,10 +117,21 @@ test "std.meta.bitCount" {
|
||||
testing.expect(bitCount(f32) == 32);
|
||||
}
|
||||
|
||||
/// Returns the alignment of type T.
|
||||
/// Note that if T is a pointer or function type the result is different than
|
||||
/// the one returned by @alignOf(T).
|
||||
/// If T is a pointer type the alignment of the type it points to is returned.
|
||||
/// If T is a function type the alignment a target-dependent value is returned.
|
||||
pub fn alignment(comptime T: type) comptime_int {
|
||||
//@alignOf works on non-pointer types
|
||||
const P = if (comptime trait.is(.Pointer)(T)) T else *T;
|
||||
return @typeInfo(P).Pointer.alignment;
|
||||
return switch (@typeInfo(T)) {
|
||||
.Optional => |info| switch (@typeInfo(info.child)) {
|
||||
.Pointer, .Fn => alignment(info.child),
|
||||
else => @alignOf(T),
|
||||
},
|
||||
.Pointer => |info| info.alignment,
|
||||
.Fn => |info| info.alignment,
|
||||
else => @alignOf(T),
|
||||
};
|
||||
}
|
||||
|
||||
test "std.meta.alignment" {
|
||||
@ -129,6 +140,8 @@ test "std.meta.alignment" {
|
||||
testing.expect(alignment(*align(2) u8) == 2);
|
||||
testing.expect(alignment([]align(1) u8) == 1);
|
||||
testing.expect(alignment([]align(2) u8) == 2);
|
||||
testing.expect(alignment(fn () void) > 0);
|
||||
testing.expect(alignment(fn () align(128) void) == 128);
|
||||
}
|
||||
|
||||
pub fn Child(comptime T: type) type {
|
||||
@ -1342,3 +1355,13 @@ test "shuffleVectorIndex" {
|
||||
testing.expect(shuffleVectorIndex(6, vector_len) == -3);
|
||||
testing.expect(shuffleVectorIndex(7, vector_len) == -4);
|
||||
}
|
||||
|
||||
/// Returns whether `error_union` contains an error.
|
||||
pub fn isError(error_union: anytype) bool {
|
||||
return if (error_union) |_| false else |_| true;
|
||||
}
|
||||
|
||||
test "isError" {
|
||||
std.testing.expect(isError(math.absInt(@as(i8, -128))));
|
||||
std.testing.expect(!isError(math.absInt(@as(i8, -127))));
|
||||
}
|
||||
|
||||
@ -279,20 +279,10 @@ pub const PROT_READ = 1;
|
||||
pub const PROT_WRITE = 2;
|
||||
pub const PROT_EXEC = 4;
|
||||
|
||||
pub const CLOCK_REALTIME = 0;
|
||||
pub const CLOCK_VIRTUAL = 1;
|
||||
pub const CLOCK_PROF = 2;
|
||||
pub const CLOCK_MONOTONIC = 4;
|
||||
pub const CLOCK_UPTIME = 5;
|
||||
pub const CLOCK_UPTIME_PRECISE = 7;
|
||||
pub const CLOCK_UPTIME_FAST = 8;
|
||||
pub const CLOCK_REALTIME_PRECISE = 9;
|
||||
pub const CLOCK_REALTIME_FAST = 10;
|
||||
pub const CLOCK_MONOTONIC_PRECISE = 11;
|
||||
pub const CLOCK_MONOTONIC_FAST = 12;
|
||||
pub const CLOCK_SECOND = 13;
|
||||
pub const CLOCK_THREAD_CPUTIME_ID = 14;
|
||||
pub const CLOCK_PROCESS_CPUTIME_ID = 15;
|
||||
pub const CLOCK_MONOTONIC = 0;
|
||||
pub const CLOCK_REALTIME = -1;
|
||||
pub const CLOCK_PROCESS_CPUTIME_ID = -2;
|
||||
pub const CLOCK_THREAD_CPUTIME_ID = -3;
|
||||
|
||||
pub const MAP_FAILED = @intToPtr(*c_void, maxInt(usize));
|
||||
pub const MAP_SHARED = 0x0001;
|
||||
@ -310,58 +300,59 @@ pub const MAP_NOCORE = 0x00020000;
|
||||
pub const MAP_PREFAULT_READ = 0x00040000;
|
||||
pub const MAP_32BIT = 0x00080000;
|
||||
|
||||
pub const WNOHANG = 1;
|
||||
pub const WUNTRACED = 2;
|
||||
pub const WSTOPPED = WUNTRACED;
|
||||
pub const WCONTINUED = 4;
|
||||
pub const WNOWAIT = 8;
|
||||
pub const WEXITED = 16;
|
||||
pub const WTRAPPED = 32;
|
||||
pub const WNOHANG = 0x1;
|
||||
pub const WUNTRACED = 0x2;
|
||||
pub const WSTOPPED = 0x10;
|
||||
pub const WCONTINUED = 0x4;
|
||||
pub const WNOWAIT = 0x20;
|
||||
pub const WEXITED = 0x08;
|
||||
|
||||
pub const SA_ONSTACK = 0x0001;
|
||||
pub const SA_RESTART = 0x0002;
|
||||
pub const SA_RESETHAND = 0x0004;
|
||||
pub const SA_NOCLDSTOP = 0x0008;
|
||||
pub const SA_NODEFER = 0x0010;
|
||||
pub const SA_NOCLDWAIT = 0x0020;
|
||||
pub const SA_SIGINFO = 0x0040;
|
||||
pub const SA_ONSTACK = 0x20;
|
||||
pub const SA_RESTART = 0x10;
|
||||
pub const SA_RESETHAND = 0x04;
|
||||
pub const SA_NOCLDSTOP = 0x01;
|
||||
pub const SA_NODEFER = 0x08;
|
||||
pub const SA_NOCLDWAIT = 0x02;
|
||||
pub const SA_SIGINFO = 0x40;
|
||||
pub const SA_NOMASK = SA_NODEFER;
|
||||
pub const SA_STACK = SA_ONSTACK;
|
||||
pub const SA_ONESHOT = SA_RESETHAND;
|
||||
|
||||
pub const SIGHUP = 1;
|
||||
pub const SIGINT = 2;
|
||||
pub const SIGQUIT = 3;
|
||||
pub const SIGILL = 4;
|
||||
pub const SIGTRAP = 5;
|
||||
pub const SIGCHLD = 5;
|
||||
pub const SIGABRT = 6;
|
||||
pub const SIGIOT = SIGABRT;
|
||||
pub const SIGEMT = 7;
|
||||
pub const SIGPIPE = 7;
|
||||
pub const SIGFPE = 8;
|
||||
pub const SIGKILL = 9;
|
||||
pub const SIGBUS = 10;
|
||||
pub const SIGSTOP = 10;
|
||||
pub const SIGSEGV = 11;
|
||||
pub const SIGSYS = 12;
|
||||
pub const SIGPIPE = 13;
|
||||
pub const SIGCONT = 12;
|
||||
pub const SIGTSTP = 13;
|
||||
pub const SIGALRM = 14;
|
||||
pub const SIGTERM = 15;
|
||||
pub const SIGURG = 16;
|
||||
pub const SIGSTOP = 17;
|
||||
pub const SIGTSTP = 18;
|
||||
pub const SIGCONT = 19;
|
||||
pub const SIGCHLD = 20;
|
||||
pub const SIGTTIN = 21;
|
||||
pub const SIGTTOU = 22;
|
||||
pub const SIGIO = 23;
|
||||
pub const SIGXCPU = 24;
|
||||
pub const SIGXFSZ = 25;
|
||||
pub const SIGVTALRM = 26;
|
||||
pub const SIGPROF = 27;
|
||||
pub const SIGWINCH = 28;
|
||||
pub const SIGINFO = 29;
|
||||
pub const SIGUSR1 = 30;
|
||||
pub const SIGUSR2 = 31;
|
||||
pub const SIGTHR = 32;
|
||||
pub const SIGLWP = SIGTHR;
|
||||
pub const SIGLIBRT = 33;
|
||||
pub const SIGTTIN = 16;
|
||||
pub const SIGTTOU = 17;
|
||||
pub const SIGUSR1 = 18;
|
||||
pub const SIGUSR2 = 19;
|
||||
pub const SIGWINCH = 20;
|
||||
pub const SIGKILLTHR = 21;
|
||||
pub const SIGTRAP = 22;
|
||||
pub const SIGPOLL = 23;
|
||||
pub const SIGPROF = 24;
|
||||
pub const SIGSYS = 25;
|
||||
pub const SIGURG = 26;
|
||||
pub const SIGVTALRM = 27;
|
||||
pub const SIGXCPU = 28;
|
||||
pub const SIGXFSZ = 29;
|
||||
pub const SIGBUS = 30;
|
||||
pub const SIGRESERVED1 = 31;
|
||||
pub const SIGRESERVED2 = 32;
|
||||
|
||||
// TODO: check
|
||||
pub const SIGRTMIN = 65;
|
||||
pub const SIGRTMAX = 126;
|
||||
|
||||
@ -645,135 +636,51 @@ pub const EVFILT_SENDFILE = -12;
|
||||
|
||||
pub const EVFILT_EMPTY = -13;
|
||||
|
||||
/// On input, NOTE_TRIGGER causes the event to be triggered for output.
|
||||
pub const NOTE_TRIGGER = 0x01000000;
|
||||
pub const TCGETA = 0x8000;
|
||||
pub const TCSETA = 0x8001;
|
||||
pub const TCSETAW = 0x8004;
|
||||
pub const TCSETAF = 0x8003;
|
||||
pub const TCSBRK = 08005;
|
||||
pub const TCXONC = 0x8007;
|
||||
pub const TCFLSH = 0x8006;
|
||||
|
||||
/// ignore input fflags
|
||||
pub const NOTE_FFNOP = 0x00000000;
|
||||
|
||||
/// and fflags
|
||||
pub const NOTE_FFAND = 0x40000000;
|
||||
|
||||
/// or fflags
|
||||
pub const NOTE_FFOR = 0x80000000;
|
||||
|
||||
/// copy fflags
|
||||
pub const NOTE_FFCOPY = 0xc0000000;
|
||||
|
||||
/// mask for operations
|
||||
pub const NOTE_FFCTRLMASK = 0xc0000000;
|
||||
pub const NOTE_FFLAGSMASK = 0x00ffffff;
|
||||
|
||||
/// low water mark
|
||||
pub const NOTE_LOWAT = 0x00000001;
|
||||
|
||||
/// behave like poll()
|
||||
pub const NOTE_FILE_POLL = 0x00000002;
|
||||
|
||||
/// vnode was removed
|
||||
pub const NOTE_DELETE = 0x00000001;
|
||||
|
||||
/// data contents changed
|
||||
pub const NOTE_WRITE = 0x00000002;
|
||||
|
||||
/// size increased
|
||||
pub const NOTE_EXTEND = 0x00000004;
|
||||
|
||||
/// attributes changed
|
||||
pub const NOTE_ATTRIB = 0x00000008;
|
||||
|
||||
/// link count changed
|
||||
pub const NOTE_LINK = 0x00000010;
|
||||
|
||||
/// vnode was renamed
|
||||
pub const NOTE_RENAME = 0x00000020;
|
||||
|
||||
/// vnode access was revoked
|
||||
pub const NOTE_REVOKE = 0x00000040;
|
||||
|
||||
/// vnode was opened
|
||||
pub const NOTE_OPEN = 0x00000080;
|
||||
|
||||
/// file closed, fd did not allow write
|
||||
pub const NOTE_CLOSE = 0x00000100;
|
||||
|
||||
/// file closed, fd did allow write
|
||||
pub const NOTE_CLOSE_WRITE = 0x00000200;
|
||||
|
||||
/// file was read
|
||||
pub const NOTE_READ = 0x00000400;
|
||||
|
||||
/// process exited
|
||||
pub const NOTE_EXIT = 0x80000000;
|
||||
|
||||
/// process forked
|
||||
pub const NOTE_FORK = 0x40000000;
|
||||
|
||||
/// process exec'd
|
||||
pub const NOTE_EXEC = 0x20000000;
|
||||
|
||||
/// mask for signal & exit status
|
||||
pub const NOTE_PDATAMASK = 0x000fffff;
|
||||
pub const NOTE_PCTRLMASK = (~NOTE_PDATAMASK);
|
||||
|
||||
/// data is seconds
|
||||
pub const NOTE_SECONDS = 0x00000001;
|
||||
|
||||
/// data is milliseconds
|
||||
pub const NOTE_MSECONDS = 0x00000002;
|
||||
|
||||
/// data is microseconds
|
||||
pub const NOTE_USECONDS = 0x00000004;
|
||||
|
||||
/// data is nanoseconds
|
||||
pub const NOTE_NSECONDS = 0x00000008;
|
||||
|
||||
/// timeout is absolute
|
||||
pub const NOTE_ABSTIME = 0x00000010;
|
||||
|
||||
pub const TIOCEXCL = 0x2000740d;
|
||||
pub const TIOCNXCL = 0x2000740e;
|
||||
pub const TIOCSCTTY = 0x20007461;
|
||||
pub const TIOCGPGRP = 0x40047477;
|
||||
pub const TIOCSPGRP = 0x80047476;
|
||||
pub const TIOCOUTQ = 0x40047473;
|
||||
pub const TIOCSTI = 0x80017472;
|
||||
pub const TIOCGWINSZ = 0x40087468;
|
||||
pub const TIOCSWINSZ = 0x80087467;
|
||||
pub const TIOCMGET = 0x4004746a;
|
||||
pub const TIOCMBIS = 0x8004746c;
|
||||
pub const TIOCMBIC = 0x8004746b;
|
||||
pub const TIOCMSET = 0x8004746d;
|
||||
pub const FIONREAD = 0x4004667f;
|
||||
pub const TIOCCONS = 0x80047462;
|
||||
pub const TIOCPKT = 0x80047470;
|
||||
pub const FIONBIO = 0x8004667e;
|
||||
pub const TIOCNOTTY = 0x20007471;
|
||||
pub const TIOCSETD = 0x8004741b;
|
||||
pub const TIOCGETD = 0x4004741a;
|
||||
pub const TIOCSBRK = 0x2000747b;
|
||||
pub const TIOCCBRK = 0x2000747a;
|
||||
pub const TIOCGSID = 0x40047463;
|
||||
pub const TIOCGPTN = 0x4004740f;
|
||||
pub const TIOCSIG = 0x2004745f;
|
||||
pub const TIOCSCTTY = 0x8017;
|
||||
pub const TIOCGPGRP = 0x8015;
|
||||
pub const TIOCSPGRP = 0x8016;
|
||||
pub const TIOCGWINSZ = 0x8012;
|
||||
pub const TIOCSWINSZ = 0x8013;
|
||||
pub const TIOCMGET = 0x8018;
|
||||
pub const TIOCMBIS = 0x8022;
|
||||
pub const TIOCMBIC = 0x8023;
|
||||
pub const TIOCMSET = 0x8019;
|
||||
pub const FIONREAD = 0xbe000001;
|
||||
pub const FIONBIO = 0xbe000000;
|
||||
pub const TIOCSBRK = 0x8020;
|
||||
pub const TIOCCBRK = 0x8021;
|
||||
pub const TIOCGSID = 0x8024;
|
||||
|
||||
pub fn WEXITSTATUS(s: u32) u32 {
|
||||
return (s & 0xff00) >> 8;
|
||||
return (s & 0xff);
|
||||
}
|
||||
|
||||
pub fn WTERMSIG(s: u32) u32 {
|
||||
return s & 0x7f;
|
||||
return (s >> 8) & 0xff;
|
||||
}
|
||||
|
||||
pub fn WSTOPSIG(s: u32) u32 {
|
||||
return WEXITSTATUS(s);
|
||||
}
|
||||
|
||||
pub fn WIFEXITED(s: u32) bool {
|
||||
return WTERMSIG(s) == 0;
|
||||
}
|
||||
|
||||
pub fn WIFSTOPPED(s: u32) bool {
|
||||
return @intCast(u16, (((s & 0xffff) *% 0x10001) >> 8)) > 0x7f00;
|
||||
return ((s >> 16) & 0xff) != 0;
|
||||
}
|
||||
|
||||
pub fn WIFSIGNALED(s: u32) bool {
|
||||
return (s & 0xffff) -% 1 < 0xff;
|
||||
return ((s >> 8) & 0xff) != 0;
|
||||
}
|
||||
|
||||
pub const winsize = extern struct {
|
||||
@ -823,49 +730,47 @@ pub const sigset_t = extern struct {
|
||||
__bits: [_SIG_WORDS]u32,
|
||||
};
|
||||
|
||||
pub const EPERM = 1; // Operation not permitted
|
||||
pub const ENOENT = 2; // No such file or directory
|
||||
pub const ESRCH = 3; // No such process
|
||||
pub const EINTR = 4; // Interrupted system call
|
||||
pub const EIO = 5; // Input/output error
|
||||
pub const ENXIO = 6; // Device not configured
|
||||
pub const E2BIG = 7; // Argument list too long
|
||||
pub const ENOEXEC = 8; // Exec format error
|
||||
pub const EBADF = 9; // Bad file descriptor
|
||||
pub const ECHILD = 10; // No child processes
|
||||
pub const EDEADLK = 11; // Resource deadlock avoided
|
||||
// 11 was EAGAIN
|
||||
pub const ENOMEM = 12; // Cannot allocate memory
|
||||
pub const EACCES = 13; // Permission denied
|
||||
pub const EFAULT = 14; // Bad address
|
||||
pub const ENOTBLK = 15; // Block device required
|
||||
pub const EBUSY = 16; // Device busy
|
||||
pub const EEXIST = 17; // File exists
|
||||
pub const EXDEV = 18; // Cross-device link
|
||||
pub const ENODEV = 19; // Operation not supported by device
|
||||
pub const ENOTDIR = 20; // Not a directory
|
||||
pub const EISDIR = 21; // Is a directory
|
||||
pub const EINVAL = 22; // Invalid argument
|
||||
pub const ENFILE = 23; // Too many open files in system
|
||||
pub const EMFILE = 24; // Too many open files
|
||||
pub const ENOTTY = 25; // Inappropriate ioctl for device
|
||||
pub const ETXTBSY = 26; // Text file busy
|
||||
pub const EFBIG = 27; // File too large
|
||||
pub const ENOSPC = 28; // No space left on device
|
||||
pub const ESPIPE = 29; // Illegal seek
|
||||
pub const EROFS = 30; // Read-only filesystem
|
||||
pub const EMLINK = 31; // Too many links
|
||||
pub const EPIPE = 32; // Broken pipe
|
||||
pub const EPERM = -0x7ffffff1; // Operation not permitted
|
||||
pub const ENOENT = -0x7fff9ffd; // No such file or directory
|
||||
pub const ESRCH = -0x7fff8ff3; // No such process
|
||||
pub const EINTR = -0x7ffffff6; // Interrupted system call
|
||||
pub const EIO = -0x7fffffff; // Input/output error
|
||||
pub const ENXIO = -0x7fff8ff5; // Device not configured
|
||||
pub const E2BIG = -0x7fff8fff; // Argument list too long
|
||||
pub const ENOEXEC = -0x7fffecfe; // Exec format error
|
||||
pub const ECHILD = -0x7fff8ffe; // No child processes
|
||||
pub const EDEADLK = -0x7fff8ffd; // Resource deadlock avoided
|
||||
pub const ENOMEM = -0x80000000; // Cannot allocate memory
|
||||
pub const EACCES = -0x7ffffffe; // Permission denied
|
||||
pub const EFAULT = -0x7fffecff; // Bad address
|
||||
pub const EBUSY = -0x7ffffff2; // Device busy
|
||||
pub const EEXIST = -0x7fff9ffe; // File exists
|
||||
pub const EXDEV = -0x7fff9ff5; // Cross-device link
|
||||
pub const ENODEV = -0x7fff8ff9; // Operation not supported by device
|
||||
pub const ENOTDIR = -0x7fff9ffb; // Not a directory
|
||||
pub const EISDIR = -0x7fff9ff7; // Is a directory
|
||||
pub const EINVAL = -0x7ffffffb; // Invalid argument
|
||||
pub const ENFILE = -0x7fff8ffa; // Too many open files in system
|
||||
pub const EMFILE = -0x7fff9ff6; // Too many open files
|
||||
pub const ENOTTY = -0x7fff8ff6; // Inappropriate ioctl for device
|
||||
pub const ETXTBSY = -0x7fff8fc5; // Text file busy
|
||||
pub const EFBIG = -0x7fff8ffc; // File too large
|
||||
pub const ENOSPC = -0x7fff9ff9; // No space left on device
|
||||
pub const ESPIPE = -0x7fff8ff4; // Illegal seek
|
||||
pub const EROFS = -0x7fff9ff8; // Read-only filesystem
|
||||
pub const EMLINK = -0x7fff8ffb; // Too many links
|
||||
pub const EPIPE = -0x7fff9ff3; // Broken pipe
|
||||
pub const EBADF = -0x7fffa000; // Bad file descriptor
|
||||
|
||||
// math software
|
||||
pub const EDOM = 33; // Numerical argument out of domain
|
||||
pub const ERANGE = 34; // Result too large
|
||||
|
||||
// non-blocking and interrupt i/o
|
||||
pub const EAGAIN = 35; // Resource temporarily unavailable
|
||||
pub const EWOULDBLOCK = EAGAIN; // Operation would block
|
||||
pub const EINPROGRESS = 36; // Operation now in progress
|
||||
pub const EALREADY = 37; // Operation already in progress
|
||||
pub const EAGAIN = -0x7ffffff5;
|
||||
pub const EWOULDBLOCK = -0x7ffffff5;
|
||||
pub const EINPROGRESS = -0x7fff8fdc;
|
||||
pub const EALREADY = -0x7fff8fdb;
|
||||
|
||||
// ipc/network software -- argument errors
|
||||
pub const ENOTSOCK = 38; // Socket operation on non-socket
|
||||
@ -1447,3 +1352,20 @@ pub const directory_which = enum(c_int) {
|
||||
|
||||
_,
|
||||
};
|
||||
|
||||
pub const cc_t = u8;
|
||||
pub const speed_t = u8;
|
||||
pub const tcflag_t = u32;
|
||||
|
||||
pub const NCCS = 32;
|
||||
|
||||
pub const termios = extern struct {
|
||||
c_iflag: tcflag_t,
|
||||
c_oflag: tcflag_t,
|
||||
c_cflag: tcflag_t,
|
||||
c_lflag: tcflag_t,
|
||||
c_line: cc_t,
|
||||
c_ispeed: speed_t,
|
||||
c_ospeed: speed_t,
|
||||
cc_t: [NCCS]cc_t,
|
||||
};
|
||||
|
||||
@ -248,7 +248,7 @@ fn initTLS() void {
|
||||
tls_data = @intToPtr([*]u8, img_base + phdr.p_vaddr)[0..phdr.p_filesz];
|
||||
tls_data_alloc_size = phdr.p_memsz;
|
||||
} else {
|
||||
tls_align_factor = @alignOf(*usize);
|
||||
tls_align_factor = @alignOf(usize);
|
||||
tls_data = &[_]u8{};
|
||||
tls_data_alloc_size = 0;
|
||||
}
|
||||
@ -308,7 +308,7 @@ fn initTLS() void {
|
||||
}
|
||||
|
||||
fn alignPtrCast(comptime T: type, ptr: [*]u8) callconv(.Inline) *T {
|
||||
return @ptrCast(*T, @alignCast(@alignOf(*T), ptr));
|
||||
return @ptrCast(*T, @alignCast(@alignOf(T), ptr));
|
||||
}
|
||||
|
||||
/// Initializes all the fields of the static TLS area and returns the computed
|
||||
|
||||
@ -88,6 +88,7 @@ pub const time = @import("time.zig");
|
||||
pub const unicode = @import("unicode.zig");
|
||||
pub const valgrind = @import("valgrind.zig");
|
||||
pub const wasm = @import("wasm.zig");
|
||||
pub const x = @import("x.zig");
|
||||
pub const zig = @import("zig.zig");
|
||||
pub const start = @import("start.zig");
|
||||
|
||||
|
||||
@ -211,8 +211,9 @@ pub const Target = struct {
|
||||
/// If neither of these cases apply, a runtime check should be used to determine if the
|
||||
/// target supports a given OS feature.
|
||||
///
|
||||
/// Binaries built with a given maximum version will continue to function on newer operating system
|
||||
/// versions. However, such a binary may not take full advantage of the newer operating system APIs.
|
||||
/// Binaries built with a given maximum version will continue to function on newer
|
||||
/// operating system versions. However, such a binary may not take full advantage of the
|
||||
/// newer operating system APIs.
|
||||
///
|
||||
/// See `Os.isAtLeast`.
|
||||
pub const VersionRange = union {
|
||||
@ -260,7 +261,7 @@ pub const Target = struct {
|
||||
.freebsd => return .{
|
||||
.semver = Version.Range{
|
||||
.min = .{ .major = 12, .minor = 0 },
|
||||
.max = .{ .major = 12, .minor = 1 },
|
||||
.max = .{ .major = 13, .minor = 0 },
|
||||
},
|
||||
},
|
||||
.macos => return .{
|
||||
|
||||
@ -206,7 +206,7 @@ pub fn utf8ValidateSlice(s: []const u8) bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (utf8Decode(s[i .. i + cp_len])) |_| {} else |_| {
|
||||
if (std.meta.isError(utf8Decode(s[i .. i + cp_len]))) {
|
||||
return false;
|
||||
}
|
||||
i += cp_len;
|
||||
|
||||
1
lib/std/x.zig
Normal file
1
lib/std/x.zig
Normal file
@ -0,0 +1 @@
|
||||
pub const os = @import("x/os/os.zig");
|
||||
276
lib/std/x/os/Socket.zig
Normal file
276
lib/std/x/os/Socket.zig
Normal file
@ -0,0 +1,276 @@
|
||||
const std = @import("../../std.zig");
|
||||
|
||||
const os = std.os;
|
||||
const mem = std.mem;
|
||||
const net = std.net;
|
||||
const time = std.time;
|
||||
const builtin = std.builtin;
|
||||
const testing = std.testing;
|
||||
|
||||
const Socket = @This();
|
||||
|
||||
/// A socket-address pair.
|
||||
pub const Connection = struct {
|
||||
socket: Socket,
|
||||
address: net.Address,
|
||||
};
|
||||
|
||||
/// The underlying handle of a socket.
|
||||
fd: os.socket_t,
|
||||
|
||||
/// Open a new socket.
|
||||
pub fn init(domain: u32, socket_type: u32, protocol: u32) !Socket {
|
||||
return Socket{ .fd = try os.socket(domain, socket_type, protocol) };
|
||||
}
|
||||
|
||||
/// Closes the socket.
|
||||
pub fn deinit(self: Socket) void {
|
||||
os.closeSocket(self.fd);
|
||||
}
|
||||
|
||||
/// Shutdown either the read side, or write side, or the entirety of a socket.
|
||||
pub fn shutdown(self: Socket, how: os.ShutdownHow) !void {
|
||||
return os.shutdown(self.fd, how);
|
||||
}
|
||||
|
||||
/// Binds the socket to an address.
|
||||
pub fn bind(self: Socket, address: net.Address) !void {
|
||||
return os.bind(self.fd, &address.any, address.getOsSockLen());
|
||||
}
|
||||
|
||||
/// Start listening for incoming connections on the socket.
|
||||
pub fn listen(self: Socket, max_backlog_size: u31) !void {
|
||||
return os.listen(self.fd, max_backlog_size);
|
||||
}
|
||||
|
||||
/// Have the socket attempt to the connect to an address.
|
||||
pub fn connect(self: Socket, address: net.Address) !void {
|
||||
return os.connect(self.fd, &address.any, address.getOsSockLen());
|
||||
}
|
||||
|
||||
/// Accept a pending incoming connection queued to the kernel backlog
|
||||
/// of the socket.
|
||||
pub fn accept(self: Socket, flags: u32) !Socket.Connection {
|
||||
var address: os.sockaddr = undefined;
|
||||
var address_len: u32 = @sizeOf(os.sockaddr);
|
||||
|
||||
const fd = try os.accept(self.fd, &address, &address_len, flags);
|
||||
|
||||
return Connection{
|
||||
.socket = Socket{ .fd = fd },
|
||||
.address = net.Address.initPosix(@alignCast(4, &address)),
|
||||
};
|
||||
}
|
||||
|
||||
/// Read data from the socket into the buffer provided. It returns the
|
||||
/// number of bytes read into the buffer provided.
|
||||
pub fn read(self: Socket, buf: []u8) !usize {
|
||||
return os.read(self.fd, buf);
|
||||
}
|
||||
|
||||
/// Read data from the socket into the buffer provided with a set of flags
|
||||
/// specified. It returns the number of bytes read into the buffer provided.
|
||||
pub fn recv(self: Socket, buf: []u8, flags: u32) !usize {
|
||||
return os.recv(self.fd, buf, flags);
|
||||
}
|
||||
|
||||
/// Write a buffer of data provided to the socket. It returns the number
|
||||
/// of bytes that are written to the socket.
|
||||
pub fn write(self: Socket, buf: []const u8) !usize {
|
||||
return os.write(self.fd, buf);
|
||||
}
|
||||
|
||||
/// Writes multiple I/O vectors to the socket. It returns the number
|
||||
/// of bytes that are written to the socket.
|
||||
pub fn writev(self: Socket, buffers: []const os.iovec_const) !usize {
|
||||
return os.writev(self.fd, buffers);
|
||||
}
|
||||
|
||||
/// Write a buffer of data provided to the socket with a set of flags specified.
|
||||
/// It returns the number of bytes that are written to the socket.
|
||||
pub fn send(self: Socket, buf: []const u8, flags: u32) !usize {
|
||||
return os.send(self.fd, buf, flags);
|
||||
}
|
||||
|
||||
/// Writes multiple I/O vectors with a prepended message header to the socket
|
||||
/// with a set of flags specified. It returns the number of bytes that are
|
||||
/// written to the socket.
|
||||
pub fn sendmsg(self: Socket, msg: os.msghdr_const, flags: u32) !usize {
|
||||
return os.sendmsg(self.fd, msg, flags);
|
||||
}
|
||||
|
||||
/// Query the address that the socket is locally bounded to.
|
||||
pub fn getLocalAddress(self: Socket) !net.Address {
|
||||
var address: os.sockaddr = undefined;
|
||||
var address_len: u32 = @sizeOf(os.sockaddr);
|
||||
try os.getsockname(self.fd, &address, &address_len);
|
||||
return net.Address.initPosix(@alignCast(4, &address));
|
||||
}
|
||||
|
||||
/// Query and return the latest cached error on the socket.
|
||||
pub fn getError(self: Socket) !void {
|
||||
return os.getsockoptError(self.fd);
|
||||
}
|
||||
|
||||
/// Query the read buffer size of the socket.
|
||||
pub fn getReadBufferSize(self: Socket) !u32 {
|
||||
var value: u32 = undefined;
|
||||
var value_len: u32 = @sizeOf(u32);
|
||||
|
||||
const rc = os.system.getsockopt(self.fd, os.SOL_SOCKET, os.SO_RCVBUF, mem.asBytes(&value), &value_len);
|
||||
return switch (os.errno(rc)) {
|
||||
0 => value,
|
||||
os.EBADF => error.BadFileDescriptor,
|
||||
os.EFAULT => error.InvalidAddressSpace,
|
||||
os.EINVAL => error.InvalidSocketOption,
|
||||
os.ENOPROTOOPT => error.UnknownSocketOption,
|
||||
os.ENOTSOCK => error.NotASocket,
|
||||
else => |err| os.unexpectedErrno(err),
|
||||
};
|
||||
}
|
||||
|
||||
/// Query the write buffer size of the socket.
|
||||
pub fn getWriteBufferSize(self: Socket) !u32 {
|
||||
var value: u32 = undefined;
|
||||
var value_len: u32 = @sizeOf(u32);
|
||||
|
||||
const rc = os.system.getsockopt(self.fd, os.SOL_SOCKET, os.SO_SNDBUF, mem.asBytes(&value), &value_len);
|
||||
return switch (os.errno(rc)) {
|
||||
0 => value,
|
||||
os.EBADF => error.BadFileDescriptor,
|
||||
os.EFAULT => error.InvalidAddressSpace,
|
||||
os.EINVAL => error.InvalidSocketOption,
|
||||
os.ENOPROTOOPT => error.UnknownSocketOption,
|
||||
os.ENOTSOCK => error.NotASocket,
|
||||
else => |err| os.unexpectedErrno(err),
|
||||
};
|
||||
}
|
||||
|
||||
/// Allow multiple sockets on the same host to listen on the same address. It returns `error.UnsupportedSocketOption` if
|
||||
/// the host does not support sockets listening the same address.
|
||||
pub fn setReuseAddress(self: Socket, enabled: bool) !void {
|
||||
if (comptime @hasDecl(os, "SO_REUSEADDR")) {
|
||||
return os.setsockopt(self.fd, os.SOL_SOCKET, os.SO_REUSEADDR, mem.asBytes(&@as(usize, @boolToInt(enabled))));
|
||||
}
|
||||
return error.UnsupportedSocketOption;
|
||||
}
|
||||
|
||||
/// Allow multiple sockets on the same host to listen on the same port. It returns `error.UnsupportedSocketOption` if
|
||||
/// the host does not supports sockets listening on the same port.
|
||||
pub fn setReusePort(self: Socket, enabled: bool) !void {
|
||||
if (comptime @hasDecl(os, "SO_REUSEPORT")) {
|
||||
return os.setsockopt(self.fd, os.SOL_SOCKET, os.SO_REUSEPORT, mem.asBytes(&@as(usize, @boolToInt(enabled))));
|
||||
}
|
||||
return error.UnsupportedSocketOption;
|
||||
}
|
||||
|
||||
/// Disable Nagle's algorithm on a TCP socket. It returns `error.UnsupportedSocketOption` if the host does not support
|
||||
/// sockets disabling Nagle's algorithm.
|
||||
pub fn setNoDelay(self: Socket, enabled: bool) !void {
|
||||
if (comptime @hasDecl(os, "TCP_NODELAY")) {
|
||||
return os.setsockopt(self.fd, os.IPPROTO_TCP, os.TCP_NODELAY, mem.asBytes(&@as(usize, @boolToInt(enabled))));
|
||||
}
|
||||
return error.UnsupportedSocketOption;
|
||||
}
|
||||
|
||||
/// Enables TCP Fast Open (RFC 7413) on a TCP socket. It returns `error.UnsupportedSocketOption` if the host does not
|
||||
/// support TCP Fast Open.
|
||||
pub fn setFastOpen(self: Socket, enabled: bool) !void {
|
||||
if (comptime @hasDecl(os, "TCP_FASTOPEN")) {
|
||||
return os.setsockopt(self.fd, os.IPPROTO_TCP, os.TCP_FASTOPEN, mem.asBytes(&@as(usize, @boolToInt(enabled))));
|
||||
}
|
||||
return error.UnsupportedSocketOption;
|
||||
}
|
||||
|
||||
/// Enables TCP Quick ACK on a TCP socket to immediately send rather than delay ACKs when necessary. It returns
|
||||
/// `error.UnsupportedSocketOption` if the host does not support TCP Quick ACK.
|
||||
pub fn setQuickACK(self: Socket, enabled: bool) !void {
|
||||
if (comptime @hasDecl(os, "TCP_QUICKACK")) {
|
||||
return os.setsockopt(self.fd, os.IPPROTO_TCP, os.TCP_QUICKACK, mem.asBytes(&@as(usize, @boolToInt(enabled))));
|
||||
}
|
||||
return error.UnsupportedSocketOption;
|
||||
}
|
||||
|
||||
/// Set the write buffer size of the socket.
|
||||
pub fn setWriteBufferSize(self: Socket, size: u32) !void {
|
||||
return os.setsockopt(self.fd, os.SOL_SOCKET, os.SO_SNDBUF, mem.asBytes(&size));
|
||||
}
|
||||
|
||||
/// Set the read buffer size of the socket.
|
||||
pub fn setReadBufferSize(self: Socket, size: u32) !void {
|
||||
return os.setsockopt(self.fd, os.SOL_SOCKET, os.SO_RCVBUF, mem.asBytes(&size));
|
||||
}
|
||||
|
||||
/// Set a timeout on the socket that is to occur if no messages are successfully written
|
||||
/// to its bound destination after a specified number of milliseconds. A subsequent write
|
||||
/// to the socket will thereafter return `error.WouldBlock` should the timeout be exceeded.
|
||||
pub fn setWriteTimeout(self: Socket, milliseconds: usize) !void {
|
||||
const timeout = os.timeval{
|
||||
.tv_sec = @intCast(isize, milliseconds / time.ms_per_s),
|
||||
.tv_usec = @intCast(isize, (milliseconds % time.ms_per_s) * time.us_per_ms),
|
||||
};
|
||||
|
||||
return os.setsockopt(self.fd, os.SOL_SOCKET, os.SO_SNDTIMEO, mem.asBytes(&timeout));
|
||||
}
|
||||
|
||||
/// Set a timeout on the socket that is to occur if no messages are successfully read
|
||||
/// from its bound destination after a specified number of milliseconds. A subsequent
|
||||
/// read from the socket will thereafter return `error.WouldBlock` should the timeout be
|
||||
/// exceeded.
|
||||
pub fn setReadTimeout(self: Socket, milliseconds: usize) !void {
|
||||
const timeout = os.timeval{
|
||||
.tv_sec = @intCast(isize, milliseconds / time.ms_per_s),
|
||||
.tv_usec = @intCast(isize, (milliseconds % time.ms_per_s) * time.us_per_ms),
|
||||
};
|
||||
|
||||
return os.setsockopt(self.fd, os.SOL_SOCKET, os.SO_RCVTIMEO, mem.asBytes(&timeout));
|
||||
}
|
||||
|
||||
test {
|
||||
testing.refAllDecls(@This());
|
||||
}
|
||||
|
||||
test "socket/linux: set read timeout of 1 millisecond on blocking socket" {
|
||||
if (builtin.os.tag != .linux) return error.SkipZigTest;
|
||||
|
||||
const a = try Socket.init(os.AF_INET, os.SOCK_STREAM | os.SOCK_CLOEXEC, os.IPPROTO_TCP);
|
||||
defer a.deinit();
|
||||
|
||||
try a.bind(net.Address.initIp4([_]u8{ 0, 0, 0, 0 }, 0));
|
||||
try a.listen(128);
|
||||
|
||||
const binded_address = try a.getLocalAddress();
|
||||
|
||||
const b = try Socket.init(os.AF_INET, os.SOCK_STREAM | os.SOCK_CLOEXEC, os.IPPROTO_TCP);
|
||||
defer b.deinit();
|
||||
|
||||
try b.connect(binded_address);
|
||||
try b.setReadTimeout(1);
|
||||
|
||||
const ab = try a.accept(os.SOCK_CLOEXEC);
|
||||
defer ab.socket.deinit();
|
||||
|
||||
var buf: [1]u8 = undefined;
|
||||
testing.expectError(error.WouldBlock, b.read(&buf));
|
||||
}
|
||||
|
||||
test "socket/linux: create non-blocking socket pair" {
|
||||
if (builtin.os.tag != .linux) return error.SkipZigTest;
|
||||
|
||||
const a = try Socket.init(os.AF_INET, os.SOCK_STREAM | os.SOCK_NONBLOCK | os.SOCK_CLOEXEC, os.IPPROTO_TCP);
|
||||
defer a.deinit();
|
||||
|
||||
try a.bind(net.Address.initIp4([_]u8{ 0, 0, 0, 0 }, 0));
|
||||
try a.listen(128);
|
||||
|
||||
const binded_address = try a.getLocalAddress();
|
||||
|
||||
const b = try Socket.init(os.AF_INET, os.SOCK_STREAM | os.SOCK_NONBLOCK | os.SOCK_CLOEXEC, os.IPPROTO_TCP);
|
||||
defer b.deinit();
|
||||
|
||||
testing.expectError(error.WouldBlock, b.connect(binded_address));
|
||||
try b.getError();
|
||||
|
||||
const ab = try a.accept(os.SOCK_NONBLOCK | os.SOCK_CLOEXEC);
|
||||
defer ab.socket.deinit();
|
||||
}
|
||||
9
lib/std/x/os/os.zig
Normal file
9
lib/std/x/os/os.zig
Normal file
@ -0,0 +1,9 @@
|
||||
const std = @import("../../std.zig");
|
||||
|
||||
const testing = std.testing;
|
||||
|
||||
pub const Socket = @import("Socket.zig");
|
||||
|
||||
test {
|
||||
testing.refAllDecls(@This());
|
||||
}
|
||||
@ -15,6 +15,7 @@ const Target = std.Target;
|
||||
const CrossTarget = std.zig.CrossTarget;
|
||||
const macos = @import("system/macos.zig");
|
||||
const native_endian = std.Target.current.cpu.arch.endian();
|
||||
const linux = @import("system/linux.zig");
|
||||
pub const windows = @import("system/windows.zig");
|
||||
|
||||
pub const getSDKPath = macos.getSDKPath;
|
||||
@ -912,15 +913,19 @@ pub const NativeTargetInfo = struct {
|
||||
.x86_64, .i386 => {
|
||||
return @import("system/x86.zig").detectNativeCpuAndFeatures(cpu_arch, os, cross_target);
|
||||
},
|
||||
else => {
|
||||
// This architecture does not have CPU model & feature detection yet.
|
||||
// See https://github.com/ziglang/zig/issues/4591
|
||||
return null;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
|
||||
// This architecture does not have CPU model & feature detection yet.
|
||||
// See https://github.com/ziglang/zig/issues/4591
|
||||
if (std.Target.current.os.tag != .linux)
|
||||
return null;
|
||||
|
||||
return linux.detectNativeCpuAndFeatures();
|
||||
}
|
||||
};
|
||||
|
||||
test {
|
||||
_ = @import("system/macos.zig");
|
||||
_ = @import("system/linux.zig");
|
||||
}
|
||||
|
||||
199
lib/std/zig/system/linux.zig
Normal file
199
lib/std/zig/system/linux.zig
Normal file
@ -0,0 +1,199 @@
|
||||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
const io = std.io;
|
||||
const fs = std.fs;
|
||||
const fmt = std.fmt;
|
||||
const testing = std.testing;
|
||||
|
||||
const Target = std.Target;
|
||||
const CrossTarget = std.zig.CrossTarget;
|
||||
|
||||
const assert = std.debug.assert;
|
||||
|
||||
const SparcCpuinfoImpl = struct {
|
||||
model: ?*const Target.Cpu.Model = null,
|
||||
is_64bit: bool = false,
|
||||
|
||||
const cpu_names = .{
|
||||
.{ "SuperSparc", &Target.sparc.cpu.supersparc },
|
||||
.{ "HyperSparc", &Target.sparc.cpu.hypersparc },
|
||||
.{ "SpitFire", &Target.sparc.cpu.ultrasparc },
|
||||
.{ "BlackBird", &Target.sparc.cpu.ultrasparc },
|
||||
.{ "Sabre", &Target.sparc.cpu.ultrasparc },
|
||||
.{ "Hummingbird", &Target.sparc.cpu.ultrasparc },
|
||||
.{ "Cheetah", &Target.sparc.cpu.ultrasparc3 },
|
||||
.{ "Jalapeno", &Target.sparc.cpu.ultrasparc3 },
|
||||
.{ "Jaguar", &Target.sparc.cpu.ultrasparc3 },
|
||||
.{ "Panther", &Target.sparc.cpu.ultrasparc3 },
|
||||
.{ "Serrano", &Target.sparc.cpu.ultrasparc3 },
|
||||
.{ "UltraSparc T1", &Target.sparc.cpu.niagara },
|
||||
.{ "UltraSparc T2", &Target.sparc.cpu.niagara2 },
|
||||
.{ "UltraSparc T3", &Target.sparc.cpu.niagara3 },
|
||||
.{ "UltraSparc T4", &Target.sparc.cpu.niagara4 },
|
||||
.{ "UltraSparc T5", &Target.sparc.cpu.niagara4 },
|
||||
.{ "LEON", &Target.sparc.cpu.leon3 },
|
||||
};
|
||||
|
||||
fn line_hook(self: *SparcCpuinfoImpl, key: []const u8, value: []const u8) !bool {
|
||||
if (mem.eql(u8, key, "cpu")) {
|
||||
inline for (cpu_names) |pair| {
|
||||
if (mem.indexOfPos(u8, value, 0, pair[0]) != null) {
|
||||
self.model = pair[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (mem.eql(u8, key, "type")) {
|
||||
self.is_64bit = mem.eql(u8, value, "sun4u") or mem.eql(u8, value, "sun4v");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
fn finalize(self: *const SparcCpuinfoImpl, arch: Target.Cpu.Arch) ?Target.Cpu {
|
||||
// At the moment we only support 64bit SPARC systems.
|
||||
assert(self.is_64bit);
|
||||
|
||||
const model = self.model orelse Target.Cpu.Model.generic(arch);
|
||||
return Target.Cpu{
|
||||
.arch = arch,
|
||||
.model = model,
|
||||
.features = model.features,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const SparcCpuinfoParser = CpuinfoParser(SparcCpuinfoImpl);
|
||||
|
||||
test "cpuinfo: SPARC" {
|
||||
try testParser(SparcCpuinfoParser, &Target.sparc.cpu.niagara2,
|
||||
\\cpu : UltraSparc T2 (Niagara2)
|
||||
\\fpu : UltraSparc T2 integrated FPU
|
||||
\\pmu : niagara2
|
||||
\\type : sun4v
|
||||
);
|
||||
}
|
||||
|
||||
const PowerpcCpuinfoImpl = struct {
|
||||
model: ?*const Target.Cpu.Model = null,
|
||||
|
||||
const cpu_names = .{
|
||||
.{ "604e", &Target.powerpc.cpu.@"604e" },
|
||||
.{ "604", &Target.powerpc.cpu.@"604" },
|
||||
.{ "7400", &Target.powerpc.cpu.@"7400" },
|
||||
.{ "7410", &Target.powerpc.cpu.@"7400" },
|
||||
.{ "7447", &Target.powerpc.cpu.@"7400" },
|
||||
.{ "7455", &Target.powerpc.cpu.@"7450" },
|
||||
.{ "G4", &Target.powerpc.cpu.@"g4" },
|
||||
.{ "POWER4", &Target.powerpc.cpu.@"970" },
|
||||
.{ "PPC970FX", &Target.powerpc.cpu.@"970" },
|
||||
.{ "PPC970MP", &Target.powerpc.cpu.@"970" },
|
||||
.{ "G5", &Target.powerpc.cpu.@"g5" },
|
||||
.{ "POWER5", &Target.powerpc.cpu.@"g5" },
|
||||
.{ "A2", &Target.powerpc.cpu.@"a2" },
|
||||
.{ "POWER6", &Target.powerpc.cpu.@"pwr6" },
|
||||
.{ "POWER7", &Target.powerpc.cpu.@"pwr7" },
|
||||
.{ "POWER8", &Target.powerpc.cpu.@"pwr8" },
|
||||
.{ "POWER8E", &Target.powerpc.cpu.@"pwr8" },
|
||||
.{ "POWER8NVL", &Target.powerpc.cpu.@"pwr8" },
|
||||
.{ "POWER9", &Target.powerpc.cpu.@"pwr9" },
|
||||
.{ "POWER10", &Target.powerpc.cpu.@"pwr10" },
|
||||
};
|
||||
|
||||
fn line_hook(self: *PowerpcCpuinfoImpl, key: []const u8, value: []const u8) !bool {
|
||||
if (mem.eql(u8, key, "cpu")) {
|
||||
// The model name is often followed by a comma or space and extra
|
||||
// info.
|
||||
inline for (cpu_names) |pair| {
|
||||
const end_index = mem.indexOfAny(u8, value, ", ") orelse value.len;
|
||||
if (mem.eql(u8, value[0..end_index], pair[0])) {
|
||||
self.model = pair[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Stop the detection once we've seen the first core.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
fn finalize(self: *const PowerpcCpuinfoImpl, arch: Target.Cpu.Arch) ?Target.Cpu {
|
||||
const model = self.model orelse Target.Cpu.Model.generic(arch);
|
||||
return Target.Cpu{
|
||||
.arch = arch,
|
||||
.model = model,
|
||||
.features = model.features,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const PowerpcCpuinfoParser = CpuinfoParser(PowerpcCpuinfoImpl);
|
||||
|
||||
test "cpuinfo: PowerPC" {
|
||||
try testParser(PowerpcCpuinfoParser, &Target.powerpc.cpu.@"970",
|
||||
\\processor : 0
|
||||
\\cpu : PPC970MP, altivec supported
|
||||
\\clock : 1250.000000MHz
|
||||
\\revision : 1.1 (pvr 0044 0101)
|
||||
);
|
||||
try testParser(PowerpcCpuinfoParser, &Target.powerpc.cpu.pwr8,
|
||||
\\processor : 0
|
||||
\\cpu : POWER8 (raw), altivec supported
|
||||
\\clock : 2926.000000MHz
|
||||
\\revision : 2.0 (pvr 004d 0200)
|
||||
);
|
||||
}
|
||||
|
||||
fn testParser(parser: anytype, expected_model: *const Target.Cpu.Model, input: []const u8) !void {
|
||||
var fbs = io.fixedBufferStream(input);
|
||||
const result = try parser.parse(.powerpc, fbs.reader());
|
||||
testing.expectEqual(expected_model, result.?.model);
|
||||
testing.expect(expected_model.features.eql(result.?.features));
|
||||
}
|
||||
|
||||
// The generic implementation of a /proc/cpuinfo parser.
|
||||
// For every line it invokes the line_hook method with the key and value strings
|
||||
// as first and second parameters. Returning false from the hook function stops
|
||||
// the iteration without raising an error.
|
||||
// When all the lines have been analyzed the finalize method is called.
|
||||
fn CpuinfoParser(comptime impl: anytype) type {
|
||||
return struct {
|
||||
fn parse(arch: Target.Cpu.Arch, reader: anytype) anyerror!?Target.Cpu {
|
||||
var line_buf: [1024]u8 = undefined;
|
||||
var obj: impl = .{};
|
||||
|
||||
while (true) {
|
||||
const line = (try reader.readUntilDelimiterOrEof(&line_buf, '\n')) orelse break;
|
||||
const colon_pos = mem.indexOfScalar(u8, line, ':') orelse continue;
|
||||
const key = mem.trimRight(u8, line[0..colon_pos], " \t");
|
||||
const value = mem.trimLeft(u8, line[colon_pos + 1 ..], " \t");
|
||||
|
||||
if (!try obj.line_hook(key, value))
|
||||
break;
|
||||
}
|
||||
|
||||
return obj.finalize(arch);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn detectNativeCpuAndFeatures() ?Target.Cpu {
|
||||
var f = fs.openFileAbsolute("/proc/cpuinfo", .{ .intended_io_mode = .blocking }) catch |err| switch (err) {
|
||||
else => return null,
|
||||
};
|
||||
defer f.close();
|
||||
|
||||
const current_arch = std.Target.current.cpu.arch;
|
||||
switch (current_arch) {
|
||||
.sparcv9 => {
|
||||
return SparcCpuinfoParser.parse(current_arch, f.reader()) catch null;
|
||||
},
|
||||
.powerpc, .powerpcle, .powerpc64, .powerpc64le => {
|
||||
return PowerpcCpuinfoParser.parse(current_arch, f.reader()) catch null;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
@ -104,6 +104,16 @@ pub const APFloat = opaque {
|
||||
extern fn ZigClangAPFloat_toString(*const APFloat, precision: c_uint, maxPadding: c_uint, truncateZero: bool) [*:0]const u8;
|
||||
};
|
||||
|
||||
pub const APFloatBaseSemantics = extern enum {
|
||||
IEEEhalf,
|
||||
BFloat,
|
||||
IEEEsingle,
|
||||
IEEEdouble,
|
||||
x86DoubleExtended,
|
||||
IEEEquad,
|
||||
PPCDoubleDouble,
|
||||
};
|
||||
|
||||
pub const APInt = opaque {
|
||||
pub const getLimitedValue = ZigClangAPInt_getLimitedValue;
|
||||
extern fn ZigClangAPInt_getLimitedValue(*const APInt, limit: u64) u64;
|
||||
@ -455,6 +465,12 @@ pub const FileID = opaque {};
|
||||
pub const FloatingLiteral = opaque {
|
||||
pub const getValueAsApproximateDouble = ZigClangFloatingLiteral_getValueAsApproximateDouble;
|
||||
extern fn ZigClangFloatingLiteral_getValueAsApproximateDouble(*const FloatingLiteral) f64;
|
||||
|
||||
pub const getBeginLoc = ZigClangIntegerLiteral_getBeginLoc;
|
||||
extern fn ZigClangIntegerLiteral_getBeginLoc(*const FloatingLiteral) SourceLocation;
|
||||
|
||||
pub const getRawSemantics = ZigClangFloatingLiteral_getRawSemantics;
|
||||
extern fn ZigClangFloatingLiteral_getRawSemantics(*const FloatingLiteral) APFloatBaseSemantics;
|
||||
};
|
||||
|
||||
pub const ForStmt = opaque {
|
||||
|
||||
@ -449,7 +449,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
.rbrace_src = src_data.rbrace_src,
|
||||
.source = src_data.source,
|
||||
};
|
||||
defer function.register_manager.deinit(bin_file.allocator);
|
||||
defer function.stack.deinit(bin_file.allocator);
|
||||
defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
|
||||
|
||||
@ -779,8 +778,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
branch.inst_table.putAssumeCapacity(inst, .dead);
|
||||
switch (prev_value) {
|
||||
.register => |reg| {
|
||||
const canon_reg = toCanonicalReg(reg);
|
||||
self.register_manager.freeReg(canon_reg);
|
||||
// TODO separate architectures with registers from
|
||||
// stack-based architectures (spu_2)
|
||||
if (callee_preserved_regs.len > 0) {
|
||||
const canon_reg = toCanonicalReg(reg);
|
||||
self.register_manager.freeReg(canon_reg);
|
||||
}
|
||||
},
|
||||
else => {}, // TODO process stack allocation death
|
||||
}
|
||||
@ -920,9 +923,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
const ptr_bits = arch.ptrBitWidth();
|
||||
const ptr_bytes: u64 = @divExact(ptr_bits, 8);
|
||||
if (abi_size <= ptr_bytes) {
|
||||
try self.register_manager.registers.ensureCapacity(self.gpa, self.register_manager.registers.count() + 1);
|
||||
if (self.register_manager.tryAllocReg(inst)) |reg| {
|
||||
return MCValue{ .register = registerAlias(reg, abi_size) };
|
||||
// TODO separate architectures with registers from
|
||||
// stack-based architectures (spu_2)
|
||||
if (callee_preserved_regs.len > 0) {
|
||||
if (self.register_manager.tryAllocReg(inst)) |reg| {
|
||||
return MCValue{ .register = registerAlias(reg, abi_size) };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -952,8 +958,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
/// `reg_owner` is the instruction that gets associated with the register in the register table.
|
||||
/// This can have a side effect of spilling instructions to the stack to free up a register.
|
||||
fn copyToNewRegister(self: *Self, reg_owner: *ir.Inst, mcv: MCValue) !MCValue {
|
||||
try self.register_manager.registers.ensureCapacity(self.gpa, @intCast(u32, self.register_manager.registers.count() + 1));
|
||||
|
||||
const reg = try self.register_manager.allocReg(reg_owner);
|
||||
try self.genSetReg(reg_owner.src, reg_owner.ty, reg, mcv);
|
||||
return MCValue{ .register = reg };
|
||||
@ -1240,10 +1244,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
.register => |reg| {
|
||||
// If it's in the registers table, need to associate the register with the
|
||||
// new instruction.
|
||||
if (self.register_manager.registers.getEntry(toCanonicalReg(reg))) |entry| {
|
||||
entry.value = inst;
|
||||
// TODO separate architectures with registers from
|
||||
// stack-based architectures (spu_2)
|
||||
if (callee_preserved_regs.len > 0) {
|
||||
if (reg.allocIndex()) |index| {
|
||||
if (!self.register_manager.isRegFree(reg)) {
|
||||
self.register_manager.registers[index] = inst;
|
||||
}
|
||||
}
|
||||
log.debug("reusing {} => {*}", .{ reg, inst });
|
||||
}
|
||||
log.debug("reusing {} => {*}", .{ reg, inst });
|
||||
},
|
||||
.stack_offset => |off| {
|
||||
log.debug("reusing stack offset {} => {*}", .{ off, inst });
|
||||
@ -1738,6 +1748,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
const arg_index = self.arg_index;
|
||||
self.arg_index += 1;
|
||||
|
||||
// TODO separate architectures with registers from
|
||||
// stack-based architectures (spu_2)
|
||||
if (callee_preserved_regs.len == 0) {
|
||||
return self.fail(inst.base.src, "TODO implement Register enum for {}", .{self.target.cpu.arch});
|
||||
}
|
||||
@ -1769,7 +1781,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
|
||||
switch (mcv) {
|
||||
.register => |reg| {
|
||||
try self.register_manager.registers.ensureCapacity(self.gpa, self.register_manager.registers.count() + 1);
|
||||
self.register_manager.getRegAssumeFree(toCanonicalReg(reg), &inst.base);
|
||||
},
|
||||
else => {},
|
||||
@ -2075,7 +2086,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
switch (mc_arg) {
|
||||
.none => continue,
|
||||
.register => |reg| {
|
||||
try self.register_manager.getRegWithoutTracking(reg);
|
||||
// TODO prevent this macho if block to be generated for all archs
|
||||
switch (arch) {
|
||||
.x86_64, .aarch64 => try self.register_manager.getRegWithoutTracking(reg),
|
||||
else => unreachable,
|
||||
}
|
||||
try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
|
||||
},
|
||||
.stack_offset => {
|
||||
@ -2397,8 +2412,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
const parent_free_registers = self.register_manager.free_registers;
|
||||
var parent_stack = try self.stack.clone(self.gpa);
|
||||
defer parent_stack.deinit(self.gpa);
|
||||
var parent_registers = try self.register_manager.registers.clone(self.gpa);
|
||||
defer parent_registers.deinit(self.gpa);
|
||||
const parent_registers = self.register_manager.registers;
|
||||
|
||||
try self.branch_stack.append(.{});
|
||||
|
||||
@ -2414,9 +2428,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
var saved_then_branch = self.branch_stack.pop();
|
||||
defer saved_then_branch.deinit(self.gpa);
|
||||
|
||||
self.register_manager.registers.deinit(self.gpa);
|
||||
self.register_manager.registers = parent_registers;
|
||||
parent_registers = .{};
|
||||
|
||||
self.stack.deinit(self.gpa);
|
||||
self.stack = parent_stack;
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
const std = @import("std");
|
||||
const DW = std.dwarf;
|
||||
const assert = std.debug.assert;
|
||||
const testing = std.testing;
|
||||
|
||||
// TODO: this is only tagged to facilitate the monstrosity.
|
||||
// Once packed structs work make it packed.
|
||||
@ -110,7 +112,7 @@ pub const Instruction = union(enum) {
|
||||
// -- less burden on callsite, bonus semantic checking
|
||||
fn bType(op: u7, fn3: u3, r1: Register, r2: Register, imm: i13) Instruction {
|
||||
const umm = @bitCast(u13, imm);
|
||||
if (umm % 2 != 0) @panic("Internal error: misaligned branch target");
|
||||
assert(umm % 2 == 0); // misaligned branch target
|
||||
|
||||
return Instruction{
|
||||
.B = .{
|
||||
@ -140,15 +142,15 @@ pub const Instruction = union(enum) {
|
||||
}
|
||||
|
||||
fn jType(op: u7, rd: Register, imm: i21) Instruction {
|
||||
const umm = @bitcast(u21, imm);
|
||||
if (umm % 2 != 0) @panic("Internal error: misaligned jump target");
|
||||
const umm = @bitCast(u21, imm);
|
||||
assert(umm % 2 == 0); // misaligned jump target
|
||||
|
||||
return Instruction{
|
||||
.J = .{
|
||||
.opcode = op,
|
||||
.rd = @enumToInt(rd),
|
||||
.imm1_10 = @truncate(u10, umm >> 1),
|
||||
.imm11 = @truncate(u1, umm >> 1),
|
||||
.imm11 = @truncate(u1, umm >> 11),
|
||||
.imm12_19 = @truncate(u8, umm >> 12),
|
||||
.imm20 = @truncate(u1, umm >> 20),
|
||||
},
|
||||
@ -340,27 +342,27 @@ pub const Instruction = union(enum) {
|
||||
|
||||
// Branch
|
||||
|
||||
pub fn beq(r1: Register, r2: Register, offset: u13) Instruction {
|
||||
pub fn beq(r1: Register, r2: Register, offset: i13) Instruction {
|
||||
return bType(0b1100011, 0b000, r1, r2, offset);
|
||||
}
|
||||
|
||||
pub fn bne(r1: Register, r2: Register, offset: u13) Instruction {
|
||||
pub fn bne(r1: Register, r2: Register, offset: i13) Instruction {
|
||||
return bType(0b1100011, 0b001, r1, r2, offset);
|
||||
}
|
||||
|
||||
pub fn blt(r1: Register, r2: Register, offset: u13) Instruction {
|
||||
pub fn blt(r1: Register, r2: Register, offset: i13) Instruction {
|
||||
return bType(0b1100011, 0b100, r1, r2, offset);
|
||||
}
|
||||
|
||||
pub fn bge(r1: Register, r2: Register, offset: u13) Instruction {
|
||||
pub fn bge(r1: Register, r2: Register, offset: i13) Instruction {
|
||||
return bType(0b1100011, 0b101, r1, r2, offset);
|
||||
}
|
||||
|
||||
pub fn bltu(r1: Register, r2: Register, offset: u13) Instruction {
|
||||
pub fn bltu(r1: Register, r2: Register, offset: i13) Instruction {
|
||||
return bType(0b1100011, 0b110, r1, r2, offset);
|
||||
}
|
||||
|
||||
pub fn bgeu(r1: Register, r2: Register, offset: u13) Instruction {
|
||||
pub fn bgeu(r1: Register, r2: Register, offset: i13) Instruction {
|
||||
return bType(0b1100011, 0b111, r1, r2, offset);
|
||||
}
|
||||
|
||||
@ -431,3 +433,38 @@ pub const Register = enum(u5) {
|
||||
pub const callee_preserved_regs = [_]Register{
|
||||
.s0, .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11,
|
||||
};
|
||||
|
||||
test "serialize instructions" {
|
||||
const Testcase = struct {
|
||||
inst: Instruction,
|
||||
expected: u32,
|
||||
};
|
||||
|
||||
const testcases = [_]Testcase{
|
||||
.{ // add t6, zero, zero
|
||||
.inst = Instruction.add(.t6, .zero, .zero),
|
||||
.expected = 0b0000000_00000_00000_000_11111_0110011,
|
||||
},
|
||||
.{ // sd s0, 0x7f(s0)
|
||||
.inst = Instruction.sd(.s0, 0x7f, .s0),
|
||||
.expected = 0b0000011_01000_01000_011_11111_0100011,
|
||||
},
|
||||
.{ // bne s0, s1, 0x42
|
||||
.inst = Instruction.bne(.s0, .s1, 0x42),
|
||||
.expected = 0b0_000010_01001_01000_001_0001_0_1100011,
|
||||
},
|
||||
.{ // j 0x1a
|
||||
.inst = Instruction.jal(.zero, 0x1a),
|
||||
.expected = 0b0_0000001101_0_00000000_00000_1101111,
|
||||
},
|
||||
.{ // ebreak
|
||||
.inst = Instruction.ebreak,
|
||||
.expected = 0b000000000001_00000_000_00000_1110011,
|
||||
},
|
||||
};
|
||||
|
||||
for (testcases) |case| {
|
||||
const actual = case.inst.toU32();
|
||||
testing.expectEqual(case.expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ const build_options = @import("build_options");
|
||||
const is_darwin = Target.current.isDarwin();
|
||||
const is_windows = Target.current.os.tag == .windows;
|
||||
const is_gnu = Target.current.isGnu();
|
||||
const is_haiku = Target.current.os.tag == .haiku;
|
||||
|
||||
const log = std.log.scoped(.libc_installation);
|
||||
|
||||
@ -279,8 +280,14 @@ pub const LibCInstallation = struct {
|
||||
return error.CCompilerCannotFindHeaders;
|
||||
}
|
||||
|
||||
const include_dir_example_file = "stdlib.h";
|
||||
const sys_include_dir_example_file = if (is_windows) "sys\\types.h" else "sys/errno.h";
|
||||
const include_dir_example_file = if (is_haiku) "posix/stdlib.h" else "stdlib.h";
|
||||
const sys_include_dir_example_file = if (is_windows)
|
||||
"sys\\types.h"
|
||||
else if (is_haiku)
|
||||
"posix/errno.h"
|
||||
else
|
||||
"sys/errno.h"
|
||||
;
|
||||
|
||||
var path_i: usize = 0;
|
||||
while (path_i < search_paths.items.len) : (path_i += 1) {
|
||||
|
||||
@ -16,7 +16,7 @@ pub fn RegisterManager(
|
||||
) type {
|
||||
return struct {
|
||||
/// The key must be canonical register.
|
||||
registers: std.AutoHashMapUnmanaged(Register, *ir.Inst) = .{},
|
||||
registers: [callee_preserved_regs.len]?*ir.Inst = [_]?*ir.Inst{null} ** callee_preserved_regs.len,
|
||||
free_registers: FreeRegInt = math.maxInt(FreeRegInt),
|
||||
/// Tracks all registers allocated in the course of this function
|
||||
allocated_registers: FreeRegInt = 0,
|
||||
@ -31,14 +31,6 @@ pub fn RegisterManager(
|
||||
return @fieldParentPtr(Function, "register_manager", self);
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self, allocator: *Allocator) void {
|
||||
self.registers.deinit(allocator);
|
||||
}
|
||||
|
||||
fn isTracked(reg: Register) bool {
|
||||
return reg.allocIndex() != null;
|
||||
}
|
||||
|
||||
fn markRegUsed(self: *Self, reg: Register) void {
|
||||
if (FreeRegInt == u0) return;
|
||||
const index = reg.allocIndex() orelse return;
|
||||
@ -73,13 +65,13 @@ pub fn RegisterManager(
|
||||
return self.allocated_registers & @as(FreeRegInt, 1) << shift != 0;
|
||||
}
|
||||
|
||||
/// Before calling, must ensureCapacity + count on self.registers.
|
||||
/// Returns `null` if all registers are allocated.
|
||||
pub fn tryAllocRegs(self: *Self, comptime count: comptime_int, insts: [count]*ir.Inst) ?[count]Register {
|
||||
if (self.tryAllocRegsWithoutTracking(count)) |regs| {
|
||||
for (regs) |reg, i| {
|
||||
const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null
|
||||
self.registers[index] = insts[i];
|
||||
self.markRegUsed(reg);
|
||||
self.registers.putAssumeCapacityNoClobber(reg, insts[i]);
|
||||
}
|
||||
|
||||
return regs;
|
||||
@ -88,13 +80,11 @@ pub fn RegisterManager(
|
||||
}
|
||||
}
|
||||
|
||||
/// Before calling, must ensureCapacity + 1 on self.registers.
|
||||
/// Returns `null` if all registers are allocated.
|
||||
pub fn tryAllocReg(self: *Self, inst: *ir.Inst) ?Register {
|
||||
return if (tryAllocRegs(self, 1, .{inst})) |regs| regs[0] else null;
|
||||
}
|
||||
|
||||
/// Before calling, must ensureCapacity + count on self.registers.
|
||||
pub fn allocRegs(self: *Self, comptime count: comptime_int, insts: [count]*ir.Inst) ![count]Register {
|
||||
comptime assert(count > 0 and count <= callee_preserved_regs.len);
|
||||
|
||||
@ -106,24 +96,22 @@ pub fn RegisterManager(
|
||||
std.mem.copy(Register, ®s, callee_preserved_regs[0..count]);
|
||||
|
||||
for (regs) |reg, i| {
|
||||
const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null
|
||||
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];
|
||||
const spilled_inst = self.registers[index].?;
|
||||
try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);
|
||||
}
|
||||
self.registers[index] = insts[i];
|
||||
}
|
||||
|
||||
break :blk regs;
|
||||
};
|
||||
}
|
||||
|
||||
/// Before calling, must ensureCapacity + 1 on self.registers.
|
||||
pub fn allocReg(self: *Self, inst: *ir.Inst) !Register {
|
||||
return (try allocRegs(self, 1, .{inst}))[0];
|
||||
return (try self.allocRegs(1, .{inst}))[0];
|
||||
}
|
||||
|
||||
/// Does not track the registers.
|
||||
@ -150,37 +138,48 @@ pub fn RegisterManager(
|
||||
/// Does not track the register.
|
||||
/// Returns `null` if all registers are allocated.
|
||||
pub fn tryAllocRegWithoutTracking(self: *Self) ?Register {
|
||||
return if (tryAllocRegsWithoutTracking(self, 1)) |regs| regs[0] else null;
|
||||
return if (self.tryAllocRegsWithoutTracking(1)) |regs| regs[0] else null;
|
||||
}
|
||||
|
||||
/// Does not track the registers
|
||||
pub fn allocRegsWithoutTracking(self: *Self, comptime count: comptime_int) ![count]Register {
|
||||
return self.tryAllocRegsWithoutTracking(count) orelse blk: {
|
||||
// We'll take over the first count registers. Spill
|
||||
// the instructions that were previously there to a
|
||||
// stack allocations.
|
||||
var regs: [count]Register = undefined;
|
||||
std.mem.copy(Register, ®s, callee_preserved_regs[0..count]);
|
||||
|
||||
for (regs) |reg, i| {
|
||||
const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null
|
||||
if (!self.isRegFree(reg)) {
|
||||
const spilled_inst = self.registers[index].?;
|
||||
try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);
|
||||
self.registers[index] = null;
|
||||
self.markRegFree(reg);
|
||||
}
|
||||
}
|
||||
|
||||
break :blk regs;
|
||||
};
|
||||
}
|
||||
|
||||
/// Does not track the register.
|
||||
pub fn allocRegWithoutTracking(self: *Self) !Register {
|
||||
return self.tryAllocRegWithoutTracking() orelse b: {
|
||||
// We'll take over the first register. Move the instruction that was previously
|
||||
// there to a stack allocation.
|
||||
const reg = callee_preserved_regs[0];
|
||||
const regs_entry = self.registers.remove(reg).?;
|
||||
const spilled_inst = regs_entry.value;
|
||||
try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);
|
||||
self.markRegFree(reg);
|
||||
|
||||
break :b reg;
|
||||
};
|
||||
return (try self.allocRegsWithoutTracking(1))[0];
|
||||
}
|
||||
|
||||
/// Allocates the specified register with the specified
|
||||
/// instruction. Spills the register if it is currently
|
||||
/// allocated.
|
||||
/// Before calling, must ensureCapacity + 1 on self.registers.
|
||||
pub fn getReg(self: *Self, reg: Register, inst: *ir.Inst) !void {
|
||||
if (!isTracked(reg)) return;
|
||||
const index = reg.allocIndex() orelse return;
|
||||
|
||||
if (!self.isRegFree(reg)) {
|
||||
// Move the instruction that was previously there to a
|
||||
// stack allocation.
|
||||
const regs_entry = self.registers.getEntry(reg).?;
|
||||
const spilled_inst = regs_entry.value;
|
||||
regs_entry.value = inst;
|
||||
const spilled_inst = self.registers[index].?;
|
||||
self.registers[index] = inst;
|
||||
try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);
|
||||
} else {
|
||||
self.getRegAssumeFree(reg, inst);
|
||||
@ -190,34 +189,33 @@ pub fn RegisterManager(
|
||||
/// Spills the register if it is currently allocated.
|
||||
/// Does not track the register.
|
||||
pub fn getRegWithoutTracking(self: *Self, reg: Register) !void {
|
||||
if (!isTracked(reg)) return;
|
||||
const index = reg.allocIndex() orelse return;
|
||||
|
||||
if (!self.isRegFree(reg)) {
|
||||
// Move the instruction that was previously there to a
|
||||
// stack allocation.
|
||||
const regs_entry = self.registers.remove(reg).?;
|
||||
const spilled_inst = regs_entry.value;
|
||||
const spilled_inst = self.registers[index].?;
|
||||
try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);
|
||||
self.markRegFree(reg);
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocates the specified register with the specified
|
||||
/// instruction. Assumes that the register is free and no
|
||||
/// instruction. Asserts that the register is free and no
|
||||
/// spilling is necessary.
|
||||
/// Before calling, must ensureCapacity + 1 on self.registers.
|
||||
pub fn getRegAssumeFree(self: *Self, reg: Register, inst: *ir.Inst) void {
|
||||
if (!isTracked(reg)) return;
|
||||
const index = reg.allocIndex() orelse return;
|
||||
|
||||
self.registers.putAssumeCapacityNoClobber(reg, inst);
|
||||
assert(self.registers[index] == null);
|
||||
self.registers[index] = inst;
|
||||
self.markRegUsed(reg);
|
||||
}
|
||||
|
||||
/// Marks the specified register as free
|
||||
pub fn freeReg(self: *Self, reg: Register) void {
|
||||
if (!isTracked(reg)) return;
|
||||
const index = reg.allocIndex() orelse return;
|
||||
|
||||
_ = self.registers.remove(reg);
|
||||
self.registers[index] = null;
|
||||
self.markRegFree(reg);
|
||||
}
|
||||
};
|
||||
@ -247,7 +245,6 @@ const MockFunction = struct {
|
||||
const Self = @This();
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
self.register_manager.deinit(self.allocator);
|
||||
self.spilled.deinit(self.allocator);
|
||||
}
|
||||
|
||||
@ -273,7 +270,6 @@ test "tryAllocReg: no spilling" {
|
||||
std.testing.expect(!function.register_manager.isRegAllocated(.r2));
|
||||
std.testing.expect(!function.register_manager.isRegAllocated(.r3));
|
||||
|
||||
try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2);
|
||||
std.testing.expectEqual(@as(?MockRegister, .r2), function.register_manager.tryAllocReg(&mock_instruction));
|
||||
std.testing.expectEqual(@as(?MockRegister, .r3), function.register_manager.tryAllocReg(&mock_instruction));
|
||||
std.testing.expectEqual(@as(?MockRegister, null), function.register_manager.tryAllocReg(&mock_instruction));
|
||||
@ -305,7 +301,6 @@ test "allocReg: spilling" {
|
||||
std.testing.expect(!function.register_manager.isRegAllocated(.r2));
|
||||
std.testing.expect(!function.register_manager.isRegAllocated(.r3));
|
||||
|
||||
try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2);
|
||||
std.testing.expectEqual(@as(?MockRegister, .r2), try function.register_manager.allocReg(&mock_instruction));
|
||||
std.testing.expectEqual(@as(?MockRegister, .r3), try function.register_manager.allocReg(&mock_instruction));
|
||||
|
||||
@ -336,14 +331,12 @@ test "getReg" {
|
||||
std.testing.expect(!function.register_manager.isRegAllocated(.r2));
|
||||
std.testing.expect(!function.register_manager.isRegAllocated(.r3));
|
||||
|
||||
try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2);
|
||||
try function.register_manager.getReg(.r3, &mock_instruction);
|
||||
|
||||
std.testing.expect(!function.register_manager.isRegAllocated(.r2));
|
||||
std.testing.expect(function.register_manager.isRegAllocated(.r3));
|
||||
|
||||
// Spill r3
|
||||
try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2);
|
||||
try function.register_manager.getReg(.r3, &mock_instruction);
|
||||
|
||||
std.testing.expect(!function.register_manager.isRegAllocated(.r2));
|
||||
|
||||
@ -84,6 +84,7 @@ enum CallingConvention {
|
||||
CallingConventionAPCS,
|
||||
CallingConventionAAPCS,
|
||||
CallingConventionAAPCSVFP,
|
||||
CallingConventionSysV
|
||||
};
|
||||
|
||||
// This one corresponds to the builtin.zig enum.
|
||||
|
||||
@ -974,6 +974,7 @@ const char *calling_convention_name(CallingConvention cc) {
|
||||
case CallingConventionAAPCS: return "AAPCS";
|
||||
case CallingConventionAAPCSVFP: return "AAPCSVFP";
|
||||
case CallingConventionInline: return "Inline";
|
||||
case CallingConventionSysV: return "SysV";
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
@ -995,6 +996,7 @@ bool calling_convention_allows_zig_types(CallingConvention cc) {
|
||||
case CallingConventionAPCS:
|
||||
case CallingConventionAAPCS:
|
||||
case CallingConventionAAPCSVFP:
|
||||
case CallingConventionSysV:
|
||||
return false;
|
||||
}
|
||||
zig_unreachable();
|
||||
@ -1969,6 +1971,10 @@ Error emit_error_unless_callconv_allowed_for_target(CodeGen *g, AstNode *source_
|
||||
case CallingConventionAAPCSVFP:
|
||||
if (!target_is_arm(g->zig_target))
|
||||
allowed_platforms = "ARM";
|
||||
break;
|
||||
case CallingConventionSysV:
|
||||
if (g->zig_target->arch != ZigLLVM_x86_64)
|
||||
allowed_platforms = "x86_64";
|
||||
}
|
||||
if (allowed_platforms != nullptr) {
|
||||
add_node_error(g, source_node, buf_sprintf(
|
||||
@ -3805,6 +3811,7 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
|
||||
case CallingConventionAPCS:
|
||||
case CallingConventionAAPCS:
|
||||
case CallingConventionAAPCSVFP:
|
||||
case CallingConventionSysV:
|
||||
add_fn_export(g, fn_table_entry, buf_ptr(&fn_table_entry->symbol_name),
|
||||
GlobalLinkageIdStrong, fn_cc);
|
||||
break;
|
||||
@ -4769,11 +4776,11 @@ Error type_is_nonnull_ptr2(CodeGen *g, ZigType *type, bool *result) {
|
||||
return ErrorNone;
|
||||
}
|
||||
|
||||
static uint32_t get_async_frame_align_bytes(CodeGen *g) {
|
||||
uint32_t a = g->pointer_size_bytes * 2;
|
||||
// promises have at least alignment 8 so that we can have 3 extra bits when doing atomicrmw
|
||||
if (a < 8) a = 8;
|
||||
return a;
|
||||
uint32_t get_async_frame_align_bytes(CodeGen *g) {
|
||||
// Due to how the frame structure is built the minimum alignment is the one
|
||||
// of a usize (or pointer).
|
||||
// label (grep this): [fn_frame_struct_layout]
|
||||
return max(g->builtin_types.entry_usize->abi_align, target_fn_align(g->zig_target));
|
||||
}
|
||||
|
||||
uint32_t get_ptr_align(CodeGen *g, ZigType *type) {
|
||||
@ -4789,11 +4796,8 @@ uint32_t get_ptr_align(CodeGen *g, ZigType *type) {
|
||||
return (ptr_type->data.pointer.explicit_alignment == 0) ?
|
||||
get_abi_alignment(g, ptr_type->data.pointer.child_type) : ptr_type->data.pointer.explicit_alignment;
|
||||
} else if (ptr_type->id == ZigTypeIdFn) {
|
||||
// I tried making this use LLVMABIAlignmentOfType but it trips this assertion in LLVM:
|
||||
// "Cannot getTypeInfo() on a type that is unsized!"
|
||||
// when getting the alignment of `?fn() callconv(.C) void`.
|
||||
// See http://lists.llvm.org/pipermail/llvm-dev/2018-September/126142.html
|
||||
return (ptr_type->data.fn.fn_type_id.alignment == 0) ? 1 : ptr_type->data.fn.fn_type_id.alignment;
|
||||
return (ptr_type->data.fn.fn_type_id.alignment == 0) ?
|
||||
target_fn_ptr_align(g->zig_target) : ptr_type->data.fn.fn_type_id.alignment;
|
||||
} else if (ptr_type->id == ZigTypeIdAnyFrame) {
|
||||
return get_async_frame_align_bytes(g);
|
||||
} else {
|
||||
|
||||
@ -47,6 +47,7 @@ ZigType *get_test_fn_type(CodeGen *g);
|
||||
ZigType *get_any_frame_type(CodeGen *g, ZigType *result_type);
|
||||
bool handle_is_ptr(CodeGen *g, ZigType *type_entry);
|
||||
Error emit_error_unless_callconv_allowed_for_target(CodeGen *g, AstNode *source_node, CallingConvention cc);
|
||||
uint32_t get_async_frame_align_bytes(CodeGen *g);
|
||||
|
||||
bool type_has_bits(CodeGen *g, ZigType *type_entry);
|
||||
Error type_has_bits2(CodeGen *g, ZigType *type_entry, bool *result);
|
||||
|
||||
@ -204,6 +204,9 @@ static ZigLLVM_CallingConv get_llvm_cc(CodeGen *g, CallingConvention cc) {
|
||||
case CallingConventionSignal:
|
||||
assert(g->zig_target->arch == ZigLLVM_avr);
|
||||
return ZigLLVM_AVR_SIGNAL;
|
||||
case CallingConventionSysV:
|
||||
assert(g->zig_target->arch == ZigLLVM_x86_64);
|
||||
return ZigLLVM_X86_64_SysV;
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
@ -348,6 +351,7 @@ static bool cc_want_sret_attr(CallingConvention cc) {
|
||||
case CallingConventionAPCS:
|
||||
case CallingConventionAAPCS:
|
||||
case CallingConventionAAPCSVFP:
|
||||
case CallingConventionSysV:
|
||||
return true;
|
||||
case CallingConventionAsync:
|
||||
case CallingConventionUnspecified:
|
||||
@ -9079,6 +9083,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
|
||||
static_assert(CallingConventionAPCS == 11, "");
|
||||
static_assert(CallingConventionAAPCS == 12, "");
|
||||
static_assert(CallingConventionAAPCSVFP == 13, "");
|
||||
static_assert(CallingConventionSysV == 14, "");
|
||||
|
||||
static_assert(BuiltinPtrSizeOne == 0, "");
|
||||
static_assert(BuiltinPtrSizeMany == 1, "");
|
||||
|
||||
@ -19216,6 +19216,7 @@ static IrInstGen *ir_analyze_instruction_export(IrAnalyze *ira, IrInstSrcExport
|
||||
case CallingConventionAPCS:
|
||||
case CallingConventionAAPCS:
|
||||
case CallingConventionAAPCSVFP:
|
||||
case CallingConventionSysV:
|
||||
add_fn_export(ira->codegen, fn_entry, buf_ptr(symbol_name), global_linkage_id, cc);
|
||||
fn_entry->section_name = section_name;
|
||||
break;
|
||||
@ -20659,8 +20660,12 @@ static IrInstGen *analyze_casted_new_stack(IrAnalyze *ira, IrInst* source_instr,
|
||||
get_fn_frame_type(ira->codegen, fn_entry), false);
|
||||
return ir_implicit_cast(ira, new_stack, needed_frame_type);
|
||||
} else {
|
||||
// XXX The stack alignment is hardcoded to 16 here and in
|
||||
// std.Target.stack_align.
|
||||
const uint32_t required_align = is_async_call_builtin ?
|
||||
get_async_frame_align_bytes(ira->codegen) : 16;
|
||||
ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8,
|
||||
false, false, PtrLenUnknown, target_fn_align(ira->codegen->zig_target), 0, 0, false);
|
||||
false, false, PtrLenUnknown, required_align, 0, 0, false);
|
||||
ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr);
|
||||
ira->codegen->need_frame_size_prefix_data = true;
|
||||
return ir_implicit_cast2(ira, new_stack_src, new_stack, u8_slice);
|
||||
@ -26079,11 +26084,11 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
|
||||
fields[0]->special = ConstValSpecialStatic;
|
||||
fields[0]->type = get_builtin_type(ira->codegen, "CallingConvention");
|
||||
bigint_init_unsigned(&fields[0]->data.x_enum_tag, type_entry->data.fn.fn_type_id.cc);
|
||||
// alignment: u29
|
||||
// alignment: comptime_int
|
||||
ensure_field_index(result->type, "alignment", 1);
|
||||
fields[1]->special = ConstValSpecialStatic;
|
||||
fields[1]->type = ira->codegen->builtin_types.entry_num_lit_int;
|
||||
bigint_init_unsigned(&fields[1]->data.x_bigint, type_entry->data.fn.fn_type_id.alignment);
|
||||
bigint_init_unsigned(&fields[1]->data.x_bigint, get_ptr_align(ira->codegen, type_entry));
|
||||
// is_generic: bool
|
||||
ensure_field_index(result->type, "is_generic", 2);
|
||||
bool is_generic = type_entry->data.fn.is_generic;
|
||||
@ -30095,7 +30100,7 @@ static IrInstGen *ir_align_cast(IrAnalyze *ira, IrInstGen *target, uint32_t alig
|
||||
fn_type_id.alignment = align_bytes;
|
||||
result_type = get_fn_type(ira->codegen, &fn_type_id);
|
||||
} else if (target_type->id == ZigTypeIdAnyFrame) {
|
||||
if (align_bytes >= target_fn_align(ira->codegen->zig_target)) {
|
||||
if (align_bytes >= get_async_frame_align_bytes(ira->codegen)) {
|
||||
result_type = target_type;
|
||||
} else {
|
||||
ir_add_error(ira, &target->base, buf_sprintf("sub-aligned anyframe not allowed"));
|
||||
|
||||
@ -1253,6 +1253,37 @@ bool target_is_ppc(const ZigTarget *target) {
|
||||
target->arch == ZigLLVM_ppc64le;
|
||||
}
|
||||
|
||||
unsigned target_fn_align(const ZigTarget *target) {
|
||||
return 16;
|
||||
// Returns the minimum alignment for every function pointer on the given
|
||||
// architecture.
|
||||
unsigned target_fn_ptr_align(const ZigTarget *target) {
|
||||
// TODO This is a pessimization but is always correct.
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Returns the minimum alignment for every function on the given architecture.
|
||||
unsigned target_fn_align(const ZigTarget *target) {
|
||||
switch (target->arch) {
|
||||
case ZigLLVM_riscv32:
|
||||
case ZigLLVM_riscv64:
|
||||
// TODO If the C extension is not present the value is 4.
|
||||
return 2;
|
||||
case ZigLLVM_ppc:
|
||||
case ZigLLVM_ppcle:
|
||||
case ZigLLVM_ppc64:
|
||||
case ZigLLVM_ppc64le:
|
||||
case ZigLLVM_aarch64:
|
||||
case ZigLLVM_aarch64_be:
|
||||
case ZigLLVM_aarch64_32:
|
||||
case ZigLLVM_sparc:
|
||||
case ZigLLVM_sparcel:
|
||||
case ZigLLVM_sparcv9:
|
||||
case ZigLLVM_mips:
|
||||
case ZigLLVM_mipsel:
|
||||
case ZigLLVM_mips64:
|
||||
case ZigLLVM_mips64el:
|
||||
return 4;
|
||||
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,6 +98,7 @@ size_t target_libc_count(void);
|
||||
void target_libc_enum(size_t index, ZigTarget *out_target);
|
||||
bool target_libc_needs_crti_crtn(const ZigTarget *target);
|
||||
|
||||
unsigned target_fn_ptr_align(const ZigTarget *target);
|
||||
unsigned target_fn_align(const ZigTarget *target);
|
||||
|
||||
#endif
|
||||
|
||||
@ -8,6 +8,7 @@ const ctok = std.c.tokenizer;
|
||||
const CToken = std.c.Token;
|
||||
const mem = std.mem;
|
||||
const math = std.math;
|
||||
const meta = std.meta;
|
||||
const ast = @import("translate_c/ast.zig");
|
||||
const Node = ast.Node;
|
||||
const Tag = Node.Tag;
|
||||
@ -1741,7 +1742,7 @@ fn transImplicitCastExpr(
|
||||
}
|
||||
|
||||
fn isBuiltinDefined(name: []const u8) bool {
|
||||
inline for (std.meta.declarations(c_builtins)) |decl| {
|
||||
inline for (meta.declarations(c_builtins)) |decl| {
|
||||
if (std.mem.eql(u8, name, decl.name)) return true;
|
||||
}
|
||||
return false;
|
||||
@ -3157,7 +3158,7 @@ const ClangFunctionType = union(enum) {
|
||||
NoProto: *const clang.FunctionType,
|
||||
|
||||
fn getReturnType(self: @This()) clang.QualType {
|
||||
switch (@as(std.meta.Tag(@This()), self)) {
|
||||
switch (@as(meta.Tag(@This()), self)) {
|
||||
.Proto => return self.Proto.getReturnType(),
|
||||
.NoProto => return self.NoProto.getReturnType(),
|
||||
}
|
||||
@ -3539,7 +3540,7 @@ fn transCPtrCast(
|
||||
expr
|
||||
else blk: {
|
||||
const child_type_node = try transQualType(c, scope, child_type, loc);
|
||||
const alignof = try Tag.alignof.create(c.arena, child_type_node);
|
||||
const alignof = try Tag.std_meta_alignment.create(c.arena, child_type_node);
|
||||
const align_cast = try Tag.align_cast.create(c.arena, .{ .lhs = alignof, .rhs = expr });
|
||||
break :blk align_cast;
|
||||
};
|
||||
@ -3547,9 +3548,22 @@ fn transCPtrCast(
|
||||
}
|
||||
}
|
||||
|
||||
fn transFloatingLiteral(c: *Context, scope: *Scope, stmt: *const clang.FloatingLiteral, used: ResultUsed) TransError!Node {
|
||||
fn transFloatingLiteral(c: *Context, scope: *Scope, expr: *const clang.FloatingLiteral, used: ResultUsed) TransError!Node {
|
||||
switch (expr.getRawSemantics()) {
|
||||
.IEEEhalf, // f16
|
||||
.IEEEsingle, // f32
|
||||
.IEEEdouble, // f64
|
||||
=> {},
|
||||
else => |format| return fail(
|
||||
c,
|
||||
error.UnsupportedTranslation,
|
||||
expr.getBeginLoc(),
|
||||
"unsupported floating point constant format {}",
|
||||
.{format},
|
||||
),
|
||||
}
|
||||
// TODO use something more accurate
|
||||
var dbl = stmt.getValueAsApproximateDouble();
|
||||
var dbl = expr.getValueAsApproximateDouble();
|
||||
const is_negative = dbl < 0;
|
||||
if (is_negative) dbl = -dbl;
|
||||
const str = if (dbl == std.math.floor(dbl))
|
||||
@ -4093,7 +4107,7 @@ fn transCreateNodeAPInt(c: *Context, int: *const clang.APSInt) !Node {
|
||||
}
|
||||
|
||||
fn transCreateNodeNumber(c: *Context, num: anytype, num_kind: enum { int, float }) !Node {
|
||||
const fmt_s = if (comptime std.meta.trait.isNumber(@TypeOf(num))) "{d}" else "{s}";
|
||||
const fmt_s = if (comptime meta.trait.isNumber(@TypeOf(num))) "{d}" else "{s}";
|
||||
const str = try std.fmt.allocPrint(c.arena, fmt_s, .{num});
|
||||
if (num_kind == .float)
|
||||
return Tag.float_literal.create(c.arena, str)
|
||||
@ -4402,6 +4416,7 @@ fn transCC(
|
||||
.X86ThisCall => return CallingConvention.Thiscall,
|
||||
.AAPCS => return CallingConvention.AAPCS,
|
||||
.AAPCS_VFP => return CallingConvention.AAPCSVFP,
|
||||
.X86_64SysV => return CallingConvention.SysV,
|
||||
else => return fail(
|
||||
c,
|
||||
error.UnsupportedType,
|
||||
@ -4848,12 +4863,12 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!Node {
|
||||
// make the output less noisy by skipping promoteIntLiteral where
|
||||
// it's guaranteed to not be required because of C standard type constraints
|
||||
const guaranteed_to_fit = switch (suffix) {
|
||||
.none => if (math.cast(i16, value)) |_| true else |_| false,
|
||||
.u => if (math.cast(u16, value)) |_| true else |_| false,
|
||||
.l => if (math.cast(i32, value)) |_| true else |_| false,
|
||||
.lu => if (math.cast(u32, value)) |_| true else |_| false,
|
||||
.ll => if (math.cast(i64, value)) |_| true else |_| false,
|
||||
.llu => if (math.cast(u64, value)) |_| true else |_| false,
|
||||
.none => !meta.isError(math.cast(i16, value)),
|
||||
.u => !meta.isError(math.cast(u16, value)),
|
||||
.l => !meta.isError(math.cast(i32, value)),
|
||||
.lu => !meta.isError(math.cast(u32, value)),
|
||||
.ll => !meta.isError(math.cast(i64, value)),
|
||||
.llu => !meta.isError(math.cast(u64, value)),
|
||||
.f => unreachable,
|
||||
};
|
||||
|
||||
|
||||
@ -120,8 +120,11 @@ pub const Node = extern union {
|
||||
std_math_Log2Int,
|
||||
/// @intCast(lhs, rhs)
|
||||
int_cast,
|
||||
/// @rem(lhs, rhs)
|
||||
/// @import("std").meta.promoteIntLiteral(value, type, radix)
|
||||
std_meta_promoteIntLiteral,
|
||||
/// @import("std").meta.alignment(value)
|
||||
std_meta_alignment,
|
||||
/// @rem(lhs, rhs)
|
||||
rem,
|
||||
/// @divTrunc(lhs, rhs)
|
||||
div_trunc,
|
||||
@ -260,6 +263,7 @@ pub const Node = extern union {
|
||||
.switch_else,
|
||||
.block_single,
|
||||
.std_meta_sizeof,
|
||||
.std_meta_alignment,
|
||||
.bool_to_int,
|
||||
.sizeof,
|
||||
.alignof,
|
||||
@ -876,6 +880,11 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
|
||||
const import_node = try renderStdImport(c, "meta", "promoteIntLiteral");
|
||||
return renderCall(c, import_node, &.{ payload.type, payload.value, payload.radix });
|
||||
},
|
||||
.std_meta_alignment => {
|
||||
const payload = node.castTag(.std_meta_alignment).?.data;
|
||||
const import_node = try renderStdImport(c, "meta", "alignment");
|
||||
return renderCall(c, import_node, &.{payload});
|
||||
},
|
||||
.std_meta_sizeof => {
|
||||
const payload = node.castTag(.std_meta_sizeof).?.data;
|
||||
const import_node = try renderStdImport(c, "meta", "sizeof");
|
||||
@ -2144,6 +2153,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
|
||||
.typeof,
|
||||
.typeinfo,
|
||||
.std_meta_sizeof,
|
||||
.std_meta_alignment,
|
||||
.std_meta_cast,
|
||||
.std_meta_promoteIntLiteral,
|
||||
.std_meta_vector,
|
||||
|
||||
@ -2528,6 +2528,11 @@ double ZigClangFloatingLiteral_getValueAsApproximateDouble(const ZigClangFloatin
|
||||
return casted->getValueAsApproximateDouble();
|
||||
}
|
||||
|
||||
ZigClangAPFloatBase_Semantics ZigClangFloatingLiteral_getRawSemantics(const ZigClangFloatingLiteral *self) {
|
||||
auto casted = reinterpret_cast<const clang::FloatingLiteral *>(self);
|
||||
return static_cast<ZigClangAPFloatBase_Semantics>(casted->getRawSemantics());
|
||||
}
|
||||
|
||||
enum ZigClangStringLiteral_StringKind ZigClangStringLiteral_getKind(const struct ZigClangStringLiteral *self) {
|
||||
auto casted = reinterpret_cast<const clang::StringLiteral *>(self);
|
||||
return (ZigClangStringLiteral_StringKind)casted->getKind();
|
||||
|
||||
@ -881,6 +881,16 @@ enum ZigClangAPFloat_roundingMode {
|
||||
ZigClangAPFloat_roundingMode_Invalid = -1,
|
||||
};
|
||||
|
||||
enum ZigClangAPFloatBase_Semantics {
|
||||
ZigClangAPFloatBase_Semantics_IEEEhalf,
|
||||
ZigClangAPFloatBase_Semantics_BFloat,
|
||||
ZigClangAPFloatBase_Semantics_IEEEsingle,
|
||||
ZigClangAPFloatBase_Semantics_IEEEdouble,
|
||||
ZigClangAPFloatBase_Semantics_x87DoubleExtended,
|
||||
ZigClangAPFloatBase_Semantics_IEEEquad,
|
||||
ZigClangAPFloatBase_Semantics_PPCDoubleDouble,
|
||||
};
|
||||
|
||||
enum ZigClangStringLiteral_StringKind {
|
||||
ZigClangStringLiteral_StringKind_Ascii,
|
||||
ZigClangStringLiteral_StringKind_Wide,
|
||||
@ -1142,6 +1152,7 @@ ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangDeclStmt_getBeginLoc(const st
|
||||
ZIG_EXTERN_C unsigned ZigClangAPFloat_convertToHexString(const struct ZigClangAPFloat *self, char *DST,
|
||||
unsigned HexDigits, bool UpperCase, enum ZigClangAPFloat_roundingMode RM);
|
||||
ZIG_EXTERN_C double ZigClangFloatingLiteral_getValueAsApproximateDouble(const ZigClangFloatingLiteral *self);
|
||||
ZIG_EXTERN_C ZigClangAPFloatBase_Semantics ZigClangFloatingLiteral_getRawSemantics(const ZigClangFloatingLiteral *self);
|
||||
|
||||
ZIG_EXTERN_C enum ZigClangStringLiteral_StringKind ZigClangStringLiteral_getKind(const struct ZigClangStringLiteral *self);
|
||||
ZIG_EXTERN_C uint32_t ZigClangStringLiteral_getCodeUnit(const struct ZigClangStringLiteral *self, size_t i);
|
||||
|
||||
@ -2136,7 +2136,9 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||
\\}
|
||||
\\fn func() callconv(.Async) void {}
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:4:21: error: expected type '[]align(16) u8', found '*[64]u8'",
|
||||
// Split the check in two as the alignment value is target dependent.
|
||||
"tmp.zig:4:21: error: expected type '[]align(",
|
||||
") u8', found '*[64]u8'",
|
||||
});
|
||||
|
||||
cases.add("atomic orderings of fence Acquire or stricter",
|
||||
|
||||
@ -306,7 +306,7 @@ test "type info: function type info" {
|
||||
fn testFunction() void {
|
||||
const fn_info = @typeInfo(@TypeOf(foo));
|
||||
expect(fn_info == .Fn);
|
||||
expect(fn_info.Fn.alignment == 0);
|
||||
expect(fn_info.Fn.alignment > 0);
|
||||
expect(fn_info.Fn.calling_convention == .C);
|
||||
expect(!fn_info.Fn.is_generic);
|
||||
expect(fn_info.Fn.args.len == 2);
|
||||
|
||||
45
test/stage2/riscv64.zig
Normal file
45
test/stage2/riscv64.zig
Normal file
@ -0,0 +1,45 @@
|
||||
const std = @import("std");
|
||||
const TestContext = @import("../../src/test.zig").TestContext;
|
||||
|
||||
const linux_riscv64 = std.zig.CrossTarget{
|
||||
.cpu_arch = .riscv64,
|
||||
.os_tag = .linux,
|
||||
};
|
||||
|
||||
pub fn addCases(ctx: *TestContext) !void {
|
||||
{
|
||||
var case = ctx.exe("riscv64 hello world", linux_riscv64);
|
||||
// Regular old hello world
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ print();
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn print() void {
|
||||
\\ asm volatile ("ecall"
|
||||
\\ :
|
||||
\\ : [number] "{a7}" (64),
|
||||
\\ [arg1] "{a0}" (1),
|
||||
\\ [arg2] "{a1}" (@ptrToInt("Hello, World!\n")),
|
||||
\\ [arg3] "{a2}" ("Hello, World!\n".len)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ return;
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("ecall"
|
||||
\\ :
|
||||
\\ : [number] "{a7}" (94),
|
||||
\\ [arg1] "{a0}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"Hello, World!\n",
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -11,11 +11,6 @@ const linux_x64 = std.zig.CrossTarget{
|
||||
.os_tag = .linux,
|
||||
};
|
||||
|
||||
const linux_riscv64 = std.zig.CrossTarget{
|
||||
.cpu_arch = .riscv64,
|
||||
.os_tag = .linux,
|
||||
};
|
||||
|
||||
pub fn addCases(ctx: *TestContext) !void {
|
||||
try @import("cbe.zig").addCases(ctx);
|
||||
try @import("spu-ii.zig").addCases(ctx);
|
||||
@ -24,6 +19,7 @@ pub fn addCases(ctx: *TestContext) !void {
|
||||
try @import("llvm.zig").addCases(ctx);
|
||||
try @import("wasm.zig").addCases(ctx);
|
||||
try @import("darwin.zig").addCases(ctx);
|
||||
try @import("riscv64.zig").addCases(ctx);
|
||||
|
||||
{
|
||||
var case = ctx.exe("hello world with updates", linux_x64);
|
||||
@ -137,42 +133,6 @@ pub fn addCases(ctx: *TestContext) !void {
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
var case = ctx.exe("riscv64 hello world", linux_riscv64);
|
||||
// Regular old hello world
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ print();
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn print() void {
|
||||
\\ asm volatile ("ecall"
|
||||
\\ :
|
||||
\\ : [number] "{a7}" (64),
|
||||
\\ [arg1] "{a0}" (1),
|
||||
\\ [arg2] "{a1}" (@ptrToInt("Hello, World!\n")),
|
||||
\\ [arg3] "{a2}" ("Hello, World!\n".len)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ return;
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("ecall"
|
||||
\\ :
|
||||
\\ : [number] "{a7}" (94),
|
||||
\\ [arg1] "{a0}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"Hello, World!\n",
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
var case = ctx.exe("adding numbers at comptime", linux_x64);
|
||||
case.addCompareOutput(
|
||||
|
||||
@ -1363,7 +1363,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
, &[_][]const u8{
|
||||
\\pub export fn ptrcast() [*c]f32 {
|
||||
\\ var a: [*c]c_int = undefined;
|
||||
\\ return @ptrCast([*c]f32, @alignCast(@alignOf(f32), a));
|
||||
\\ return @ptrCast([*c]f32, @alignCast(@import("std").meta.alignment(f32), a));
|
||||
\\}
|
||||
});
|
||||
|
||||
@ -1387,16 +1387,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
\\pub export fn test_ptr_cast() void {
|
||||
\\ var p: ?*c_void = undefined;
|
||||
\\ {
|
||||
\\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@alignOf(u8), p));
|
||||
\\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@alignOf(c_short), p));
|
||||
\\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@alignOf(c_int), p));
|
||||
\\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@alignOf(c_longlong), p));
|
||||
\\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@import("std").meta.alignment(u8), p));
|
||||
\\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@import("std").meta.alignment(c_short), p));
|
||||
\\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@import("std").meta.alignment(c_int), p));
|
||||
\\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@import("std").meta.alignment(c_longlong), p));
|
||||
\\ }
|
||||
\\ {
|
||||
\\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@alignOf(u8), p));
|
||||
\\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@alignOf(c_short), p));
|
||||
\\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@alignOf(c_int), p));
|
||||
\\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@alignOf(c_longlong), p));
|
||||
\\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@import("std").meta.alignment(u8), p));
|
||||
\\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@import("std").meta.alignment(c_short), p));
|
||||
\\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@import("std").meta.alignment(c_int), p));
|
||||
\\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@import("std").meta.alignment(c_longlong), p));
|
||||
\\ }
|
||||
\\}
|
||||
});
|
||||
@ -3028,7 +3028,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
\\void call() {
|
||||
\\ fn_int(3.0f);
|
||||
\\ fn_int(3.0);
|
||||
\\ fn_int(3.0L);
|
||||
\\ fn_int('ABCD');
|
||||
\\ fn_f32(3);
|
||||
\\ fn_f64(3);
|
||||
@ -3053,7 +3052,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
\\pub export fn call() void {
|
||||
\\ fn_int(@floatToInt(c_int, 3.0));
|
||||
\\ fn_int(@floatToInt(c_int, 3.0));
|
||||
\\ fn_int(@floatToInt(c_int, 3.0));
|
||||
\\ fn_int(@as(c_int, 1094861636));
|
||||
\\ fn_f32(@intToFloat(f32, @as(c_int, 3)));
|
||||
\\ fn_f64(@intToFloat(f64, @as(c_int, 3)));
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user