authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-28 14:53:50-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-28 14:57:38-07:00
logdf24ce52b1059232db66e9af425a02df558aa1ca
treebf314eace4c7acd533c5c9532b330ecd8ae65686
parenteb9c29eb817a24e5326b9a63ebf7265d3b2bad2c
parent55c58f226d06d3708a82878a67f3800e0ce5810b

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.

52 files changed, 1674 insertions(+), 485 deletions(-)

build.zig+5-2
......@@ -44,6 +44,7 @@ pub fn build(b: *Builder) !void {
4444
4545 const fmt_build_zig = b.addFmt(&[_][]const u8{"build.zig"});
4646
47 const skip_debug = b.option(bool, "skip-debug", "Main test suite skips debug builds") orelse false;
4748 const skip_release = b.option(bool, "skip-release", "Main test suite skips release builds") orelse false;
4849 const skip_release_small = b.option(bool, "skip-release-small", "Main test suite skips release-small builds") orelse skip_release;
4950 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 {
240241
241242 var chosen_modes: [4]builtin.Mode = undefined;
242243 var chosen_mode_index: usize = 0;
243 chosen_modes[chosen_mode_index] = builtin.Mode.Debug;
244 chosen_mode_index += 1;
244 if (!skip_debug) {
245 chosen_modes[chosen_mode_index] = builtin.Mode.Debug;
246 chosen_mode_index += 1;
247 }
245248 if (!skip_release_safe) {
246249 chosen_modes[chosen_mode_index] = builtin.Mode.ReleaseSafe;
247250 chosen_mode_index += 1;
ci/drone/drone.yml+33-2
......@@ -6,7 +6,38 @@ platform:
66 arch: arm64
77
88steps:
9- name: build-and-test
9- name: build
10 image: ziglang/static-base:llvm12-aarch64-1
11 commands:
12 - ./ci/drone/linux_script_build
13
14- name: test-1
15 depends_on:
16 - build
17 image: ziglang/static-base:llvm12-aarch64-1
18 commands:
19 - ./ci/drone/linux_script_test 1
20
21- name: test-2
22 depends_on:
23 - build
24 image: ziglang/static-base:llvm12-aarch64-1
25 commands:
26 - ./ci/drone/linux_script_test 2
27
28- name: test-3
29 depends_on:
30 - build
31 image: ziglang/static-base:llvm12-aarch64-1
32 commands:
33 - ./ci/drone/linux_script_test 3
34
35- name: finalize
36 depends_on:
37 - build
38 - test-1
39 - test-2
40 - test-3
1041 image: ziglang/static-base:llvm12-aarch64-1
1142 environment:
1243 SRHT_OAUTH_TOKEN:
......@@ -16,4 +47,4 @@ steps:
1647 AWS_SECRET_ACCESS_KEY:
1748 from_secret: AWS_SECRET_ACCESS_KEY
1849 commands:
19 - ./ci/drone/linux_script
50 - ./ci/drone/linux_script_finalize
ci/drone/linux_script deleted-65
......@@ -1,65 +0,0 @@
1#!/bin/sh
2
3set -x
4set -e
5
6TRIPLEARCH="$(uname -m)"
7BUILDDIR="$(pwd)"
8DISTDIR="$(pwd)/dist"
9
10apk update
11apk add py3-pip xz perl-utils jq curl samurai
12pip3 install s3cmd
13
14# Make the `zig version` number consistent.
15# This will affect the cmake command below.
16git config core.abbrev 9
17git fetch --unshallow || true
18git fetch --tags
19
20mkdir build
21cd build
22cmake .. -DCMAKE_BUILD_TYPE=Release "-DCMAKE_INSTALL_PREFIX=$DISTDIR" -DZIG_STATIC=ON -DCMAKE_PREFIX_PATH=/deps/local -GNinja
23
24samu install
25# run-translated-c tests are skipped due to: https://github.com/ziglang/zig/issues/8537
26./zig build test \
27 -Dskip-release \
28 -Dskip-non-native \
29 -Dskip-compile-errors \
30 -Dskip-run-translated-c
31
32if [ -z "$DRONE_PULL_REQUEST" ]; then
33 mv ../LICENSE "$DISTDIR/"
34 mv ../zig-cache/langref.html "$DISTDIR/"
35 mv "$DISTDIR/bin/zig" "$DISTDIR/"
36 rmdir "$DISTDIR/bin"
37
38 GITBRANCH="$DRONE_BRANCH"
39 VERSION="$("$DISTDIR/zig" version)"
40 DIRNAME="zig-linux-$TRIPLEARCH-$VERSION"
41 TARBALL="$DIRNAME.tar.xz"
42 mv "$DISTDIR" "$DIRNAME"
43 tar cfJ "$TARBALL" "$DIRNAME"
44
45 s3cmd put -P --add-header="cache-control: public, max-age=31536000, immutable" "$TARBALL" s3://ziglang.org/builds/
46
47 SHASUM=$(shasum -a 256 $TARBALL | cut '-d ' -f1)
48 BYTESIZE=$(wc -c < $TARBALL)
49
50 JSONFILE="$TRIPLEARCH-linux-$GITBRANCH.json"
51 touch $JSONFILE
52 echo "{\"tarball\": \"$TARBALL\"," >>$JSONFILE
53 echo "\"shasum\": \"$SHASUM\"," >>$JSONFILE
54 echo "\"size\": \"$BYTESIZE\"}" >>$JSONFILE
55
56 s3cmd put -P --add-header="Cache-Control: max-age=0, must-revalidate" "$JSONFILE" "s3://ziglang.org/builds/$JSONFILE"
57 s3cmd put -P "$JSONFILE" "s3://ziglang.org/builds/$TRIPLEARCH-linux-$VERSION.json"
58 if [ "$GITBRANCH" = "master" ]; then
59 # avoid leaking oauth token
60 set +x
61
62 cd "$BUILDDIR"
63 ./ci/srht/on_master_success "$VERSION" "$SRHT_OAUTH_TOKEN"
64 fi
65fi
ci/drone/linux_script_base created+22
......@@ -0,0 +1,22 @@
1#!/bin/sh
2
3# https://docs.drone.io/pipeline/docker/syntax/workspace/
4#
5# Drone automatically creates a temporary volume, known as your workspace,
6# where it clones your repository. The workspace is the current working
7# directory for each step in your pipeline.
8#
9# Because the workspace is a volume, filesystem changes are persisted between
10# pipeline steps. In other words, individual steps can communicate and share
11# state using the filesystem.
12#
13# Workspace volumes are ephemeral. They are created when the pipeline starts
14# and destroyed after the pipeline completes.
15
16set -x
17set -e
18
19TRIPLEARCH="$(uname -m)"
20DISTDIR="$DRONE_WORKSPACE/dist"
21
22export ZIG_GLOBAL_CACHE_DIR="$DRONE_WORKSPACE/zig-cache"
ci/drone/linux_script_build created+18
......@@ -0,0 +1,18 @@
1#!/bin/sh
2
3. ./ci/drone/linux_script_base
4
5apk update
6apk add samurai
7
8# Make the `zig version` number consistent.
9# This will affect the cmake command below.
10git config core.abbrev 9
11git fetch --unshallow || true
12git fetch --tags
13
14mkdir build
15cd build
16cmake .. -DCMAKE_BUILD_TYPE=Release "-DCMAKE_INSTALL_PREFIX=$DISTDIR" -DZIG_STATIC=ON -DCMAKE_PREFIX_PATH=/deps/local -GNinja
17
18samu install
ci/drone/linux_script_finalize created+46
......@@ -0,0 +1,46 @@
1#!/bin/sh
2
3. ./ci/drone/linux_script_base
4
5if [ -n "$DRONE_PULL_REQUEST" ]; then
6 exit 0
7fi
8
9apk update
10apk add py3-pip xz perl-utils jq curl samurai
11pip3 install s3cmd
12
13cd build
14
15mv ../LICENSE "$DISTDIR/"
16# docs are disabled due to: https://github.com/ziglang/zig/issues/8597
17#mv ../zig-cache/langref.html "$DISTDIR/"
18mv "$DISTDIR/bin/zig" "$DISTDIR/"
19rmdir "$DISTDIR/bin"
20
21GITBRANCH="$DRONE_BRANCH"
22VERSION="$("$DISTDIR/zig" version)"
23DIRNAME="zig-linux-$TRIPLEARCH-$VERSION"
24TARBALL="$DIRNAME.tar.xz"
25mv "$DISTDIR" "$DIRNAME"
26tar cfJ "$TARBALL" "$DIRNAME"
27
28s3cmd put -P --add-header="cache-control: public, max-age=31536000, immutable" "$TARBALL" s3://ziglang.org/builds/
29
30SHASUM=$(shasum -a 256 $TARBALL | cut '-d ' -f1)
31BYTESIZE=$(wc -c < $TARBALL)
32
33JSONFILE="tarball.json"
34touch $JSONFILE
35echo "{\"tarball\": \"$TARBALL\"," >>$JSONFILE
36echo "\"shasum\": \"$SHASUM\"," >>$JSONFILE
37echo "\"size\": \"$BYTESIZE\"}" >>$JSONFILE
38
39s3cmd put -P "$JSONFILE" "s3://ziglang.org/builds/$TRIPLEARCH-linux-$VERSION.json"
40if [ "$GITBRANCH" = "master" ]; then
41 # avoid leaking oauth token
42 set +x
43
44 cd "$DRONE_WORKSPACE"
45 ./ci/srht/on_master_success "$VERSION" "$SRHT_OAUTH_TOKEN"
46fi
ci/drone/linux_script_test created+46
......@@ -0,0 +1,46 @@
1#!/bin/sh
2
3. ./ci/drone/linux_script_base
4
5# only release-fast builds of test suite due to: https://github.com/ziglang/zig/issues/8597
6#
7# Some test suite components will be missing because they do not support
8# forcing -OReleaseFast
9#
10# see `zig build --help` for the full list of test-* components
11case "$1" in
12 1)
13 steps="\
14 test-stage2 \
15 test-fmt \
16 test-behavior"
17 ;;
18 2)
19 steps="test-std"
20 ;;
21 3)
22 steps="\
23 test-compiler-rt \
24 test-minilibc \
25 test-compare-output \
26 test-translate-c \
27 test-run-translated-c"
28 ;;
29 '')
30 echo "error: expecting test group argument"
31 exit 1
32 ;;
33 *)
34 echo "error: unknown test group: $1"
35 exit 1
36 ;;
37esac
38
39# only release-fast builds of test suite due to: https://github.com/ziglang/zig/issues/8597
40./build/zig build \
41 -Drelease \
42 -Dskip-debug \
43 -Dskip-release-small \
44 -Dskip-release-safe \
45 -Dskip-non-native \
46 $steps
lib/std/Thread.zig+6-3
......@@ -199,7 +199,8 @@ pub fn spawn(comptime startFn: anytype, context: SpawnContextType(@TypeOf(startF
199199 inner: Context,
200200 };
201201 fn threadMain(raw_arg: windows.LPVOID) callconv(.C) windows.DWORD {
202 const arg = if (@sizeOf(Context) == 0) {} else @ptrCast(*Context, @alignCast(@alignOf(Context), raw_arg)).*;
202 const arg = if (@sizeOf(Context) == 0) undefined //
203 else @ptrCast(*Context, @alignCast(@alignOf(Context), raw_arg)).*;
203204
204205 switch (@typeInfo(@typeInfo(@TypeOf(startFn)).Fn.return_type.?)) {
205206 .NoReturn => {
......@@ -260,7 +261,8 @@ pub fn spawn(comptime startFn: anytype, context: SpawnContextType(@TypeOf(startF
260261
261262 const MainFuncs = struct {
262263 fn linuxThreadMain(ctx_addr: usize) callconv(.C) u8 {
263 const arg = if (@sizeOf(Context) == 0) {} else @intToPtr(*const Context, ctx_addr).*;
264 const arg = if (@sizeOf(Context) == 0) undefined //
265 else @intToPtr(*Context, ctx_addr).*;
264266
265267 switch (@typeInfo(@typeInfo(@TypeOf(startFn)).Fn.return_type.?)) {
266268 .NoReturn => {
......@@ -292,7 +294,8 @@ pub fn spawn(comptime startFn: anytype, context: SpawnContextType(@TypeOf(startF
292294 }
293295 }
294296 fn posixThreadMain(ctx: ?*c_void) callconv(.C) ?*c_void {
295 const arg = if (@sizeOf(Context) == 0) {} else @ptrCast(*Context, @alignCast(@alignOf(Context), ctx)).*;
297 const arg = if (@sizeOf(Context) == 0) undefined //
298 else @ptrCast(*Context, @alignCast(@alignOf(Context), ctx)).*;
296299
297300 switch (@typeInfo(@typeInfo(@TypeOf(startFn)).Fn.return_type.?)) {
298301 .NoReturn => {
lib/std/builtin.zig+1
......@@ -166,6 +166,7 @@ pub const CallingConvention = enum {
166166 APCS,
167167 AAPCS,
168168 AAPCSVFP,
169 SysV
169170};
170171
171172/// This data structure is used by the Zig language code generation and
lib/std/c/haiku.zig+48
......@@ -69,3 +69,51 @@ pub const pthread_rwlock_t = extern struct {
6969 writer_count: i32 = 0,
7070 waiters: [2]?*c_void = [_]?*c_void{ null, null },
7171};
72
73pub const EAI = extern enum(c_int) {
74 /// address family for hostname not supported
75 ADDRFAMILY = 1,
76
77 /// name could not be resolved at this time
78 AGAIN = 2,
79
80 /// flags parameter had an invalid value
81 BADFLAGS = 3,
82
83 /// non-recoverable failure in name resolution
84 FAIL = 4,
85
86 /// address family not recognized
87 FAMILY = 5,
88
89 /// memory allocation failure
90 MEMORY = 6,
91
92 /// no address associated with hostname
93 NODATA = 7,
94
95 /// name does not resolve
96 NONAME = 8,
97
98 /// service not recognized for socket type
99 SERVICE = 9,
100
101 /// intended socket type was not recognized
102 SOCKTYPE = 10,
103
104 /// system error returned in errno
105 SYSTEM = 11,
106
107 /// invalid value for hints
108 BADHINTS = 12,
109
110 /// resolved protocol is unknown
111 PROTOCOL = 13,
112
113 /// argument buffer overflow
114 OVERFLOW = 14,
115
116 _,
117};
118
119pub const EAI_MAX = 15;
lib/std/child_process.zig+1-1
......@@ -264,7 +264,7 @@ pub const ChildProcess = struct {
264264
265265 // TODO collect output in a deadlock-avoiding way on Windows.
266266 // https://github.com/ziglang/zig/issues/6343
267 if (builtin.os.tag == .windows) {
267 if (builtin.os.tag == .windows or builtin.os.tag == .haiku) {
268268 const stdout_in = child.stdout.?.reader();
269269 const stderr_in = child.stderr.?.reader();
270270
lib/std/crypto/25519/edwards25519.zig+9-16
......@@ -75,16 +75,8 @@ pub const Edwards25519 = struct {
7575 .is_base = true,
7676 };
7777
78 /// The edwards25519 neutral element.
79 pub const neutralElement = Edwards25519{
80 .x = Fe{ .limbs = .{ 2251799813685229, 2251799813685247, 2251799813685247, 2251799813685247, 2251799813685247 } },
81 .y = Fe{ .limbs = .{ 1507481815385608, 2223447444246085, 1083941587175919, 2059929906842505, 1581435440146976 } },
82 .z = Fe{ .limbs = .{ 1507481815385608, 2223447444246085, 1083941587175919, 2059929906842505, 1581435440146976 } },
83 .t = Fe{ .limbs = .{ 2251799813685229, 2251799813685247, 2251799813685247, 2251799813685247, 2251799813685247 } },
84 .is_base = false,
85 };
86
87 const identityElement = Edwards25519{ .x = Fe.zero, .y = Fe.one, .z = Fe.one, .t = Fe.zero };
78 pub const neutralElement = @compileError("deprecated: use identityElement instead");
79 pub const identityElement = Edwards25519{ .x = Fe.zero, .y = Fe.one, .z = Fe.one, .t = Fe.zero };
8880
8981 /// Reject the neutral element.
9082 pub fn rejectIdentity(p: Edwards25519) IdentityElementError!void {
......@@ -160,9 +152,10 @@ pub const Edwards25519 = struct {
160152 return t;
161153 }
162154
163 fn nonAdjacentForm(s: [32]u8) [2 * 32]i8 {
155 fn slide(s: [32]u8) [2 * 32]i8 {
156 const reduced = if ((s[s.len - 1] & 0x80) != 0) s else scalar.reduce(s);
164157 var e: [2 * 32]i8 = undefined;
165 for (s) |x, i| {
158 for (reduced) |x, i| {
166159 e[i * 2 + 0] = @as(i8, @truncate(u4, x));
167160 e[i * 2 + 1] = @as(i8, @truncate(u4, x >> 4));
168161 }
......@@ -185,7 +178,7 @@ pub const Edwards25519 = struct {
185178 // avoid these to keep the standard library lightweight.
186179 fn pcMul(pc: [9]Edwards25519, s: [32]u8, comptime vartime: bool) IdentityElementError!Edwards25519 {
187180 std.debug.assert(vartime);
188 const e = nonAdjacentForm(s);
181 const e = slide(s);
189182 var q = Edwards25519.identityElement;
190183 var pos: usize = 2 * 32 - 1;
191184 while (true) : (pos -= 1) {
......@@ -280,8 +273,8 @@ pub const Edwards25519 = struct {
280273 xpc[4].rejectIdentity() catch return error.WeakPublicKey;
281274 break :pc xpc;
282275 };
283 const e1 = nonAdjacentForm(s1);
284 const e2 = nonAdjacentForm(s2);
276 const e1 = slide(s1);
277 const e2 = slide(s2);
285278 var q = Edwards25519.identityElement;
286279 var pos: usize = 2 * 32 - 1;
287280 while (true) : (pos -= 1) {
......@@ -318,7 +311,7 @@ pub const Edwards25519 = struct {
318311 }
319312 var es: [count][2 * 32]i8 = undefined;
320313 for (ss) |s, i| {
321 es[i] = nonAdjacentForm(s);
314 es[i] = slide(s);
322315 }
323316 var q = Edwards25519.identityElement;
324317 var pos: usize = 2 * 32 - 1;
lib/std/crypto/25519/field.zig+1-1
......@@ -355,7 +355,7 @@ pub const Fe = struct {
355355 return fe;
356356 }
357357
358 /// Compute the inverse of a field element
358 /// Return the inverse of a field element, or 0 if a=0.
359359 pub fn invert(a: Fe) Fe {
360360 var t0 = a.sq();
361361 var t1 = t0.sqn(2).mul(a);
lib/std/crypto/25519/scalar.zig+1-1
......@@ -98,7 +98,7 @@ pub fn sub(a: [32]u8, b: [32]u8) [32]u8 {
9898 return add(a, neg(b));
9999}
100100
101/// A scalar in unpacked reprentation
101/// A scalar in unpacked representation
102102pub const Scalar = struct {
103103 const Limbs = [5]u64;
104104 limbs: Limbs = undefined,
lib/std/crypto/utils.zig+52
......@@ -1,7 +1,11 @@
11const std = @import("../std.zig");
2const debug = std.debug;
23const mem = std.mem;
34const testing = std.testing;
45
6const Endian = std.builtin.Endian;
7const Order = std.math.Order;
8
59/// Compares two arrays in constant time (for a given length) and returns whether they are equal.
610/// This function was designed to compare short cryptographic secrets (MACs, signatures).
711/// For all other applications, use mem.eql() instead.
......@@ -38,6 +42,41 @@ pub fn timingSafeEql(comptime T: type, a: T, b: T) bool {
3842 }
3943}
4044
45/// Compare two integers serialized as arrays of the same size, in constant time.
46/// Returns .lt if a<b, .gt if a>b and .eq if a=b
47pub fn timingSafeCompare(comptime T: type, a: []const T, b: []const T, endian: Endian) Order {
48 debug.assert(a.len == b.len);
49 const bits = switch (@typeInfo(T)) {
50 .Int => |cinfo| if (cinfo.signedness != .unsigned) @compileError("Elements to be compared must be unsigned") else cinfo.bits,
51 else => @compileError("Elements to be compared must be integers"),
52 };
53 comptime const Cext = std.meta.Int(.unsigned, bits + 1);
54 var gt: T = 0;
55 var eq: T = 1;
56 if (endian == .Little) {
57 var i = a.len;
58 while (i != 0) {
59 i -= 1;
60 const x1 = a[i];
61 const x2 = b[i];
62 gt |= @truncate(T, (@as(Cext, x2) -% @as(Cext, x1)) >> bits) & eq;
63 eq &= @truncate(T, (@as(Cext, (x2 ^ x1)) -% 1) >> bits);
64 }
65 } else {
66 for (a) |x1, i| {
67 const x2 = b[i];
68 gt |= @truncate(T, (@as(Cext, x2) -% @as(Cext, x1)) >> bits) & eq;
69 eq &= @truncate(T, (@as(Cext, (x2 ^ x1)) -% 1) >> bits);
70 }
71 }
72 if (gt != 0) {
73 return Order.gt;
74 } else if (eq != 0) {
75 return Order.eq;
76 }
77 return Order.lt;
78}
79
4180/// Sets a slice to zeroes.
4281/// Prevents the store from being optimized out.
4382pub fn secureZero(comptime T: type, s: []T) void {
......@@ -70,6 +109,19 @@ test "crypto.utils.timingSafeEql (vectors)" {
70109 testing.expect(timingSafeEql(std.meta.Vector(100, u8), v1, v3));
71110}
72111
112test "crypto.utils.timingSafeCompare" {
113 var a = [_]u8{10} ** 32;
114 var b = [_]u8{10} ** 32;
115 testing.expectEqual(timingSafeCompare(u8, &a, &b, .Big), .eq);
116 testing.expectEqual(timingSafeCompare(u8, &a, &b, .Little), .eq);
117 a[31] = 1;
118 testing.expectEqual(timingSafeCompare(u8, &a, &b, .Big), .lt);
119 testing.expectEqual(timingSafeCompare(u8, &a, &b, .Little), .lt);
120 a[0] = 20;
121 testing.expectEqual(timingSafeCompare(u8, &a, &b, .Big), .gt);
122 testing.expectEqual(timingSafeCompare(u8, &a, &b, .Little), .lt);
123}
124
73125test "crypto.utils.secureZero" {
74126 var a = [_]u8{0xfe} ** 8;
75127 var b = [_]u8{0xfe} ** 8;
lib/std/debug.zig+11-4
......@@ -339,6 +339,13 @@ pub const StackIterator = struct {
339339 fp: usize,
340340
341341 pub fn init(first_address: ?usize, fp: ?usize) StackIterator {
342 if (native_arch == .sparcv9) {
343 // Flush all the register windows on stack.
344 asm volatile (
345 \\ flushw
346 ::: "memory");
347 }
348
342349 return StackIterator{
343350 .first_address = first_address,
344351 .fp = fp orelse @frameAddress(),
......@@ -346,18 +353,18 @@ pub const StackIterator = struct {
346353 }
347354
348355 // Offset of the saved BP wrt the frame pointer.
349 const fp_offset = if (native_arch.isRISCV())
356 const fp_offset = if (comptime native_arch.isRISCV())
350357 // On RISC-V the frame pointer points to the top of the saved register
351358 // area, on pretty much every other architecture it points to the stack
352359 // slot where the previous frame pointer is saved.
353360 2 * @sizeOf(usize)
354 else if (native_arch.isSPARC())
361 else if (comptime native_arch.isSPARC())
355362 // On SPARC the previous frame pointer is stored at 14 slots past %fp+BIAS.
356363 14 * @sizeOf(usize)
357364 else
358365 0;
359366
360 const fp_bias = if (native_arch.isSPARC())
367 const fp_bias = if (comptime native_arch.isSPARC())
361368 // On SPARC frame pointers are biased by a constant.
362369 2047
363370 else
......@@ -383,7 +390,7 @@ pub const StackIterator = struct {
383390 }
384391
385392 fn next_internal(self: *StackIterator) ?usize {
386 const fp = if (native_arch.isSPARC())
393 const fp = if (comptime native_arch.isSPARC())
387394 // On SPARC the offset is positive. (!)
388395 math.add(usize, self.fp, fp_offset) catch return null
389396 else
lib/std/fmt.zig+3-1
......@@ -1523,9 +1523,11 @@ test "parseUnsigned" {
15231523}
15241524
15251525pub const parseFloat = @import("fmt/parse_float.zig").parseFloat;
1526pub const parseHexFloat = @import("fmt/parse_hex_float.zig").parseHexFloat;
15261527
1527test "parseFloat" {
1528test {
15281529 _ = @import("fmt/parse_float.zig");
1530 _ = @import("fmt/parse_hex_float.zig");
15291531}
15301532
15311533pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) {
lib/std/fmt/parse_hex_float.zig created+352
......@@ -0,0 +1,352 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2021 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.const std = @import("std");
6//
7// The rounding logic is inspired by LLVM's APFloat and Go's atofHex
8// implementation.
9
10const std = @import("std");
11const ascii = std.ascii;
12const fmt = std.fmt;
13const math = std.math;
14const testing = std.testing;
15
16const assert = std.debug.assert;
17
18pub fn parseHexFloat(comptime T: type, s: []const u8) !T {
19 assert(@typeInfo(T) == .Float);
20
21 const IntT = std.meta.Int(.unsigned, @typeInfo(T).Float.bits);
22
23 const mantissa_bits = math.floatMantissaBits(T);
24 const exponent_bits = math.floatExponentBits(T);
25
26 const sign_shift = mantissa_bits + exponent_bits;
27
28 const exponent_bias = (1 << (exponent_bits - 1)) - 1;
29 const exponent_min = 1 - exponent_bias;
30 const exponent_max = exponent_bias;
31
32 if (s.len == 0)
33 return error.InvalidCharacter;
34
35 if (ascii.eqlIgnoreCase(s, "nan")) {
36 return math.nan(T);
37 } else if (ascii.eqlIgnoreCase(s, "inf") or ascii.eqlIgnoreCase(s, "+inf")) {
38 return math.inf(T);
39 } else if (ascii.eqlIgnoreCase(s, "-inf")) {
40 return -math.inf(T);
41 }
42
43 var negative: bool = false;
44 var exp_negative: bool = false;
45
46 var mantissa: u128 = 0;
47 var exponent: i16 = 0;
48 var frac_scale: i16 = 0;
49
50 const State = enum {
51 MaybeSign,
52 Prefix,
53 LeadingIntegerDigit,
54 IntegerDigit,
55 MaybeDot,
56 LeadingFractionDigit,
57 FractionDigit,
58 ExpPrefix,
59 MaybeExpSign,
60 ExpDigit,
61 };
62
63 var state = State.MaybeSign;
64
65 var i: usize = 0;
66 while (i < s.len) {
67 const c = s[i];
68
69 switch (state) {
70 .MaybeSign => {
71 state = .Prefix;
72
73 if (c == '+') {
74 i += 1;
75 } else if (c == '-') {
76 negative = true;
77 i += 1;
78 }
79 },
80 .Prefix => {
81 state = .LeadingIntegerDigit;
82
83 // Match both 0x and 0X.
84 if (i + 2 > s.len or s[i] != '0' or s[i + 1] | 32 != 'x')
85 return error.InvalidCharacter;
86 i += 2;
87 },
88 .LeadingIntegerDigit => {
89 if (c == '0') {
90 // Skip leading zeros.
91 i += 1;
92 } else if (c == '_') {
93 return error.InvalidCharacter;
94 } else {
95 state = .IntegerDigit;
96 }
97 },
98 .IntegerDigit => {
99 if (ascii.isXDigit(c)) {
100 if (mantissa >= math.maxInt(u128) / 16)
101 return error.Overflow;
102 mantissa *%= 16;
103 mantissa += try fmt.charToDigit(c, 16);
104 i += 1;
105 } else if (c == '_') {
106 i += 1;
107 } else {
108 state = .MaybeDot;
109 }
110 },
111 .MaybeDot => {
112 if (c == '.') {
113 state = .LeadingFractionDigit;
114 i += 1;
115 } else state = .ExpPrefix;
116 },
117 .LeadingFractionDigit => {
118 if (c == '_') {
119 return error.InvalidCharacter;
120 } else state = .FractionDigit;
121 },
122 .FractionDigit => {
123 if (ascii.isXDigit(c)) {
124 if (mantissa < math.maxInt(u128) / 16) {
125 mantissa *%= 16;
126 mantissa +%= try fmt.charToDigit(c, 16);
127 frac_scale += 1;
128 } else if (c != '0') {
129 return error.Overflow;
130 }
131 i += 1;
132 } else if (c == '_') {
133 i += 1;
134 } else {
135 state = .ExpPrefix;
136 }
137 },
138 .ExpPrefix => {
139 state = .MaybeExpSign;
140 // Match both p and P.
141 if (c | 32 != 'p')
142 return error.InvalidCharacter;
143 i += 1;
144 },
145 .MaybeExpSign => {
146 state = .ExpDigit;
147
148 if (c == '+') {
149 i += 1;
150 } else if (c == '-') {
151 exp_negative = true;
152 i += 1;
153 }
154 },
155 .ExpDigit => {
156 if (ascii.isXDigit(c)) {
157 if (exponent >= math.maxInt(i16) / 10)
158 return error.Overflow;
159 exponent *%= 10;
160 exponent +%= try fmt.charToDigit(c, 10);
161 i += 1;
162 } else if (c == '_') {
163 i += 1;
164 } else {
165 return error.InvalidCharacter;
166 }
167 },
168 }
169 }
170
171 if (exp_negative)
172 exponent *= -1;
173
174 // Bring the decimal part to the left side of the decimal dot.
175 exponent -= frac_scale * 4;
176
177 if (mantissa == 0) {
178 // Signed zero.
179 return if (negative) -0.0 else 0.0;
180 }
181
182 // Divide by 2^mantissa_bits to right-align the mantissa in the fractional
183 // part.
184 exponent += mantissa_bits;
185
186 // Keep around two extra bits to correctly round any value that doesn't fit
187 // the available mantissa bits. The result LSB serves as Guard bit, the
188 // following one is the Round bit and the last one is the Sticky bit,
189 // computed by OR-ing all the dropped bits.
190
191 // Normalize by aligning the implicit one bit.
192 while (mantissa >> (mantissa_bits + 2) == 0) {
193 mantissa <<= 1;
194 exponent -= 1;
195 }
196
197 // Normalize again by dropping the excess precision.
198 // Note that the discarded bits are folded into the Sticky bit.
199 while (mantissa >> (mantissa_bits + 2 + 1) != 0) {
200 mantissa = mantissa >> 1 | (mantissa & 1);
201 exponent += 1;
202 }
203
204 // Very small numbers can be possibly represented as denormals, reduce the
205 // exponent as much as possible.
206 while (mantissa != 0 and exponent < exponent_min - 2) {
207 mantissa = mantissa >> 1 | (mantissa & 1);
208 exponent += 1;
209 }
210
211 // There are two cases to handle:
212 // - We've truncated more than 0.5ULP (R=S=1), increase the mantissa.
213 // - We've truncated exactly 0.5ULP (R=1 S=0), increase the mantissa if the
214 // result is odd (G=1).
215 // The two checks can be neatly folded as follows.
216 mantissa |= @boolToInt(mantissa & 0b100 != 0);
217 mantissa += 1;
218
219 mantissa >>= 2;
220 exponent += 2;
221
222 if (mantissa & (1 << (mantissa_bits + 1)) != 0) {
223 // Renormalize, if the exponent overflows we'll catch that below.
224 mantissa >>= 1;
225 exponent += 1;
226 }
227
228 if (mantissa >> mantissa_bits == 0) {
229 // This is a denormal number, the biased exponent is zero.
230 exponent = -exponent_bias;
231 }
232
233 if (exponent > exponent_max) {
234 // Overflow, return +inf.
235 return math.inf(T);
236 }
237
238 // Remove the implicit bit.
239 mantissa &= @as(u128, (1 << mantissa_bits) - 1);
240
241 const raw: IntT =
242 (if (negative) @as(IntT, 1) << sign_shift else 0) |
243 @as(IntT, @bitCast(u16, exponent + exponent_bias)) << mantissa_bits |
244 @truncate(IntT, mantissa);
245
246 return @bitCast(T, raw);
247}
248
249test "special" {
250 testing.expect(math.isNan(try parseHexFloat(f32, "nAn")));
251 testing.expect(math.isPositiveInf(try parseHexFloat(f32, "iNf")));
252 testing.expect(math.isPositiveInf(try parseHexFloat(f32, "+Inf")));
253 testing.expect(math.isNegativeInf(try parseHexFloat(f32, "-iNf")));
254}
255test "zero" {
256 testing.expectEqual(@as(f32, 0.0), try parseHexFloat(f32, "0x0"));
257 testing.expectEqual(@as(f32, 0.0), try parseHexFloat(f32, "-0x0"));
258 testing.expectEqual(@as(f32, 0.0), try parseHexFloat(f32, "0x0p42"));
259 testing.expectEqual(@as(f32, 0.0), try parseHexFloat(f32, "-0x0.00000p42"));
260 testing.expectEqual(@as(f32, 0.0), try parseHexFloat(f32, "0x0.00000p666"));
261}
262
263test "f16" {
264 const Case = struct { s: []const u8, v: f16 };
265 const cases: []const Case = &[_]Case{
266 .{ .s = "0x1p0", .v = 1.0 },
267 .{ .s = "-0x1p-1", .v = -0.5 },
268 .{ .s = "0x10p+10", .v = 16384.0 },
269 .{ .s = "0x10p-10", .v = 0.015625 },
270 // Max normalized value.
271 .{ .s = "0x1.ffcp+15", .v = math.f16_max },
272 .{ .s = "-0x1.ffcp+15", .v = -math.f16_max },
273 // Min normalized value.
274 .{ .s = "0x1p-14", .v = math.f16_min },
275 .{ .s = "-0x1p-14", .v = -math.f16_min },
276 // Min denormal value.
277 .{ .s = "0x1p-24", .v = math.f16_true_min },
278 .{ .s = "-0x1p-24", .v = -math.f16_true_min },
279 };
280
281 for (cases) |case| {
282 testing.expectEqual(case.v, try parseHexFloat(f16, case.s));
283 }
284}
285test "f32" {
286 const Case = struct { s: []const u8, v: f32 };
287 const cases: []const Case = &[_]Case{
288 .{ .s = "0x1p0", .v = 1.0 },
289 .{ .s = "-0x1p-1", .v = -0.5 },
290 .{ .s = "0x10p+10", .v = 16384.0 },
291 .{ .s = "0x10p-10", .v = 0.015625 },
292 .{ .s = "0x0.ffffffp128", .v = 0x0.ffffffp128 },
293 .{ .s = "0x0.1234570p-125", .v = 0x0.1234570p-125 },
294 // Max normalized value.
295 .{ .s = "0x1.fffffeP+127", .v = math.f32_max },
296 .{ .s = "-0x1.fffffeP+127", .v = -math.f32_max },
297 // Min normalized value.
298 .{ .s = "0x1p-126", .v = math.f32_min },
299 .{ .s = "-0x1p-126", .v = -math.f32_min },
300 // Min denormal value.
301 .{ .s = "0x1P-149", .v = math.f32_true_min },
302 .{ .s = "-0x1P-149", .v = -math.f32_true_min },
303 };
304
305 for (cases) |case| {
306 testing.expectEqual(case.v, try parseHexFloat(f32, case.s));
307 }
308}
309test "f64" {
310 const Case = struct { s: []const u8, v: f64 };
311 const cases: []const Case = &[_]Case{
312 .{ .s = "0x1p0", .v = 1.0 },
313 .{ .s = "-0x1p-1", .v = -0.5 },
314 .{ .s = "0x10p+10", .v = 16384.0 },
315 .{ .s = "0x10p-10", .v = 0.015625 },
316 // Max normalized value.
317 .{ .s = "0x1.fffffffffffffp+1023", .v = math.f64_max },
318 .{ .s = "-0x1.fffffffffffffp1023", .v = -math.f64_max },
319 // Min normalized value.
320 .{ .s = "0x1p-1022", .v = math.f64_min },
321 .{ .s = "-0x1p-1022", .v = -math.f64_min },
322 // Min denormalized value.
323 .{ .s = "0x1p-1074", .v = math.f64_true_min },
324 .{ .s = "-0x1p-1074", .v = -math.f64_true_min },
325 };
326
327 for (cases) |case| {
328 testing.expectEqual(case.v, try parseHexFloat(f64, case.s));
329 }
330}
331test "f128" {
332 const Case = struct { s: []const u8, v: f128 };
333 const cases: []const Case = &[_]Case{
334 .{ .s = "0x1p0", .v = 1.0 },
335 .{ .s = "-0x1p-1", .v = -0.5 },
336 .{ .s = "0x10p+10", .v = 16384.0 },
337 .{ .s = "0x10p-10", .v = 0.015625 },
338 // Max normalized value.
339 .{ .s = "0xf.fffffffffffffffffffffffffff8p+16380", .v = math.f128_max },
340 .{ .s = "-0xf.fffffffffffffffffffffffffff8p+16380", .v = -math.f128_max },
341 // Min normalized value.
342 .{ .s = "0x1p-16382", .v = math.f128_min },
343 .{ .s = "-0x1p-16382", .v = -math.f128_min },
344 // // Min denormalized value.
345 .{ .s = "0x1p-16494", .v = math.f128_true_min },
346 .{ .s = "-0x1p-16494", .v = -math.f128_true_min },
347 };
348
349 for (cases) |case| {
350 testing.expectEqual(@bitCast(u128, case.v), @bitCast(u128, try parseHexFloat(f128, case.s)));
351 }
352}
lib/std/json/test.zig+2-4
......@@ -29,8 +29,7 @@ fn ok(s: []const u8) !void {
2929fn err(s: []const u8) void {
3030 testing.expect(!json.validate(s));
3131
32 testNonStreaming(s) catch return;
33 testing.expect(false);
32 testing.expect(std.meta.isError(testNonStreaming(s)));
3433}
3534
3635fn utf8Error(s: []const u8) void {
......@@ -48,8 +47,7 @@ fn any(s: []const u8) void {
4847fn anyStreamingErrNonStreaming(s: []const u8) void {
4948 _ = json.validate(s);
5049
51 testNonStreaming(s) catch return;
52 testing.expect(false);
50 testing.expect(std.meta.isError(testNonStreaming(s)));
5351}
5452
5553fn roundTrip(s: []const u8) !void {
lib/std/mem.zig+40-4
......@@ -1877,7 +1877,11 @@ test "rotate" {
18771877
18781878/// Replace needle with replacement as many times as possible, writing to an output buffer which is assumed to be of
18791879/// appropriate size. Use replacementSize to calculate an appropriate buffer size.
1880/// The needle must not be empty.
18801881pub fn replace(comptime T: type, input: []const T, needle: []const T, replacement: []const T, output: []T) usize {
1882 // Empty needle will loop until output buffer overflows.
1883 assert(needle.len > 0);
1884
18811885 var i: usize = 0;
18821886 var slide: usize = 0;
18831887 var replacements: usize = 0;
......@@ -1900,22 +1904,48 @@ pub fn replace(comptime T: type, input: []const T, needle: []const T, replacemen
19001904test "replace" {
19011905 var output: [29]u8 = undefined;
19021906 var replacements = replace(u8, "All your base are belong to us", "base", "Zig", output[0..]);
1907 var expected: []const u8 = "All your Zig are belong to us";
19031908 testing.expect(replacements == 1);
1904 testing.expect(eql(u8, output[0..], "All your Zig are belong to us"));
1909 testing.expectEqualStrings(expected, output[0..expected.len]);
19051910
19061911 replacements = replace(u8, "Favor reading code over writing code.", "code", "", output[0..]);
1912 expected = "Favor reading over writing .";
19071913 testing.expect(replacements == 2);
1908 testing.expect(eql(u8, output[0..], "Favor reading over writing ."));
1914 testing.expectEqualStrings(expected, output[0..expected.len]);
1915
1916 // Empty needle is not allowed but input may be empty.
1917 replacements = replace(u8, "", "x", "y", output[0..0]);
1918 expected = "";
1919 testing.expect(replacements == 0);
1920 testing.expectEqualStrings(expected, output[0..expected.len]);
1921
1922 // Adjacent replacements.
1923
1924 replacements = replace(u8, "\\n\\n", "\\n", "\n", output[0..]);
1925 expected = "\n\n";
1926 testing.expect(replacements == 2);
1927 testing.expectEqualStrings(expected, output[0..expected.len]);
1928
1929 replacements = replace(u8, "abbba", "b", "cd", output[0..]);
1930 expected = "acdcdcda";
1931 testing.expect(replacements == 3);
1932 testing.expectEqualStrings(expected, output[0..expected.len]);
19091933}
19101934
19111935/// Calculate the size needed in an output buffer to perform a replacement.
1936/// The needle must not be empty.
19121937pub fn replacementSize(comptime T: type, input: []const T, needle: []const T, replacement: []const T) usize {
1938 // Empty needle will loop forever.
1939 assert(needle.len > 0);
1940
19131941 var i: usize = 0;
19141942 var size: usize = input.len;
1915 while (i < input.len) : (i += 1) {
1943 while (i < input.len) {
19161944 if (mem.indexOf(T, input[i..], needle) == @as(usize, 0)) {
19171945 size = size - needle.len + replacement.len;
19181946 i += needle.len;
1947 } else {
1948 i += 1;
19191949 }
19201950 }
19211951
......@@ -1924,9 +1954,15 @@ pub fn replacementSize(comptime T: type, input: []const T, needle: []const T, re
19241954
19251955test "replacementSize" {
19261956 testing.expect(replacementSize(u8, "All your base are belong to us", "base", "Zig") == 29);
1927 testing.expect(replacementSize(u8, "", "", "") == 0);
19281957 testing.expect(replacementSize(u8, "Favor reading code over writing code.", "code", "") == 29);
19291958 testing.expect(replacementSize(u8, "Only one obvious way to do things.", "things.", "things in Zig.") == 41);
1959
1960 // Empty needle is not allowed but input may be empty.
1961 testing.expect(replacementSize(u8, "", "x", "y") == 0);
1962
1963 // Adjacent replacements.
1964 testing.expect(replacementSize(u8, "\\n\\n", "\\n", "\n") == 2);
1965 testing.expect(replacementSize(u8, "abbba", "b", "cd") == 8);
19301966}
19311967
19321968/// Perform a replacement on an allocated buffer of pre-determined size. Caller must free returned memory.
lib/std/meta.zig+26-3
......@@ -117,10 +117,21 @@ test "std.meta.bitCount" {
117117 testing.expect(bitCount(f32) == 32);
118118}
119119
120/// Returns the alignment of type T.
121/// Note that if T is a pointer or function type the result is different than
122/// the one returned by @alignOf(T).
123/// If T is a pointer type the alignment of the type it points to is returned.
124/// If T is a function type the alignment a target-dependent value is returned.
120125pub fn alignment(comptime T: type) comptime_int {
121 //@alignOf works on non-pointer types
122 const P = if (comptime trait.is(.Pointer)(T)) T else *T;
123 return @typeInfo(P).Pointer.alignment;
126 return switch (@typeInfo(T)) {
127 .Optional => |info| switch (@typeInfo(info.child)) {
128 .Pointer, .Fn => alignment(info.child),
129 else => @alignOf(T),
130 },
131 .Pointer => |info| info.alignment,
132 .Fn => |info| info.alignment,
133 else => @alignOf(T),
134 };
124135}
125136
126137test "std.meta.alignment" {
......@@ -129,6 +140,8 @@ test "std.meta.alignment" {
129140 testing.expect(alignment(*align(2) u8) == 2);
130141 testing.expect(alignment([]align(1) u8) == 1);
131142 testing.expect(alignment([]align(2) u8) == 2);
143 testing.expect(alignment(fn () void) > 0);
144 testing.expect(alignment(fn () align(128) void) == 128);
132145}
133146
134147pub fn Child(comptime T: type) type {
......@@ -1342,3 +1355,13 @@ test "shuffleVectorIndex" {
13421355 testing.expect(shuffleVectorIndex(6, vector_len) == -3);
13431356 testing.expect(shuffleVectorIndex(7, vector_len) == -4);
13441357}
1358
1359/// Returns whether `error_union` contains an error.
1360pub fn isError(error_union: anytype) bool {
1361 return if (error_union) |_| false else |_| true;
1362}
1363
1364test "isError" {
1365 std.testing.expect(isError(math.absInt(@as(i8, -128))));
1366 std.testing.expect(!isError(math.absInt(@as(i8, -127))));
1367}
lib/std/os/bits/haiku.zig+128-206
......@@ -279,20 +279,10 @@ pub const PROT_READ = 1;
279279pub const PROT_WRITE = 2;
280280pub const PROT_EXEC = 4;
281281
282pub const CLOCK_REALTIME = 0;
283pub const CLOCK_VIRTUAL = 1;
284pub const CLOCK_PROF = 2;
285pub const CLOCK_MONOTONIC = 4;
286pub const CLOCK_UPTIME = 5;
287pub const CLOCK_UPTIME_PRECISE = 7;
288pub const CLOCK_UPTIME_FAST = 8;
289pub const CLOCK_REALTIME_PRECISE = 9;
290pub const CLOCK_REALTIME_FAST = 10;
291pub const CLOCK_MONOTONIC_PRECISE = 11;
292pub const CLOCK_MONOTONIC_FAST = 12;
293pub const CLOCK_SECOND = 13;
294pub const CLOCK_THREAD_CPUTIME_ID = 14;
295pub const CLOCK_PROCESS_CPUTIME_ID = 15;
282pub const CLOCK_MONOTONIC = 0;
283pub const CLOCK_REALTIME = -1;
284pub const CLOCK_PROCESS_CPUTIME_ID = -2;
285pub const CLOCK_THREAD_CPUTIME_ID = -3;
296286
297287pub const MAP_FAILED = @intToPtr(*c_void, maxInt(usize));
298288pub const MAP_SHARED = 0x0001;
......@@ -310,58 +300,59 @@ pub const MAP_NOCORE = 0x00020000;
310300pub const MAP_PREFAULT_READ = 0x00040000;
311301pub const MAP_32BIT = 0x00080000;
312302
313pub const WNOHANG = 1;
314pub const WUNTRACED = 2;
315pub const WSTOPPED = WUNTRACED;
316pub const WCONTINUED = 4;
317pub const WNOWAIT = 8;
318pub const WEXITED = 16;
319pub const WTRAPPED = 32;
320
321pub const SA_ONSTACK = 0x0001;
322pub const SA_RESTART = 0x0002;
323pub const SA_RESETHAND = 0x0004;
324pub const SA_NOCLDSTOP = 0x0008;
325pub const SA_NODEFER = 0x0010;
326pub const SA_NOCLDWAIT = 0x0020;
327pub const SA_SIGINFO = 0x0040;
303pub const WNOHANG = 0x1;
304pub const WUNTRACED = 0x2;
305pub const WSTOPPED = 0x10;
306pub const WCONTINUED = 0x4;
307pub const WNOWAIT = 0x20;
308pub const WEXITED = 0x08;
309
310pub const SA_ONSTACK = 0x20;
311pub const SA_RESTART = 0x10;
312pub const SA_RESETHAND = 0x04;
313pub const SA_NOCLDSTOP = 0x01;
314pub const SA_NODEFER = 0x08;
315pub const SA_NOCLDWAIT = 0x02;
316pub const SA_SIGINFO = 0x40;
317pub const SA_NOMASK = SA_NODEFER;
318pub const SA_STACK = SA_ONSTACK;
319pub const SA_ONESHOT = SA_RESETHAND;
328320
329321pub const SIGHUP = 1;
330322pub const SIGINT = 2;
331323pub const SIGQUIT = 3;
332324pub const SIGILL = 4;
333pub const SIGTRAP = 5;
325pub const SIGCHLD = 5;
334326pub const SIGABRT = 6;
335327pub const SIGIOT = SIGABRT;
336pub const SIGEMT = 7;
328pub const SIGPIPE = 7;
337329pub const SIGFPE = 8;
338330pub const SIGKILL = 9;
339pub const SIGBUS = 10;
331pub const SIGSTOP = 10;
340332pub const SIGSEGV = 11;
341pub const SIGSYS = 12;
342pub const SIGPIPE = 13;
333pub const SIGCONT = 12;
334pub const SIGTSTP = 13;
343335pub const SIGALRM = 14;
344336pub const SIGTERM = 15;
345pub const SIGURG = 16;
346pub const SIGSTOP = 17;
347pub const SIGTSTP = 18;
348pub const SIGCONT = 19;
349pub const SIGCHLD = 20;
350pub const SIGTTIN = 21;
351pub const SIGTTOU = 22;
352pub const SIGIO = 23;
353pub const SIGXCPU = 24;
354pub const SIGXFSZ = 25;
355pub const SIGVTALRM = 26;
356pub const SIGPROF = 27;
357pub const SIGWINCH = 28;
358pub const SIGINFO = 29;
359pub const SIGUSR1 = 30;
360pub const SIGUSR2 = 31;
361pub const SIGTHR = 32;
362pub const SIGLWP = SIGTHR;
363pub const SIGLIBRT = 33;
364
337pub const SIGTTIN = 16;
338pub const SIGTTOU = 17;
339pub const SIGUSR1 = 18;
340pub const SIGUSR2 = 19;
341pub const SIGWINCH = 20;
342pub const SIGKILLTHR = 21;
343pub const SIGTRAP = 22;
344pub const SIGPOLL = 23;
345pub const SIGPROF = 24;
346pub const SIGSYS = 25;
347pub const SIGURG = 26;
348pub const SIGVTALRM = 27;
349pub const SIGXCPU = 28;
350pub const SIGXFSZ = 29;
351pub const SIGBUS = 30;
352pub const SIGRESERVED1 = 31;
353pub const SIGRESERVED2 = 32;
354
355// TODO: check
365356pub const SIGRTMIN = 65;
366357pub const SIGRTMAX = 126;
367358
......@@ -645,135 +636,51 @@ pub const EVFILT_SENDFILE = -12;
645636
646637pub const EVFILT_EMPTY = -13;
647638
648/// On input, NOTE_TRIGGER causes the event to be triggered for output.
649pub const NOTE_TRIGGER = 0x01000000;
650
651/// ignore input fflags
652pub const NOTE_FFNOP = 0x00000000;
653
654/// and fflags
655pub const NOTE_FFAND = 0x40000000;
656
657/// or fflags
658pub const NOTE_FFOR = 0x80000000;
659
660/// copy fflags
661pub const NOTE_FFCOPY = 0xc0000000;
662
663/// mask for operations
664pub const NOTE_FFCTRLMASK = 0xc0000000;
665pub const NOTE_FFLAGSMASK = 0x00ffffff;
666
667/// low water mark
668pub const NOTE_LOWAT = 0x00000001;
669
670/// behave like poll()
671pub const NOTE_FILE_POLL = 0x00000002;
672
673/// vnode was removed
674pub const NOTE_DELETE = 0x00000001;
675
676/// data contents changed
677pub const NOTE_WRITE = 0x00000002;
678
679/// size increased
680pub const NOTE_EXTEND = 0x00000004;
681
682/// attributes changed
683pub const NOTE_ATTRIB = 0x00000008;
684
685/// link count changed
686pub const NOTE_LINK = 0x00000010;
687
688/// vnode was renamed
689pub const NOTE_RENAME = 0x00000020;
690
691/// vnode access was revoked
692pub const NOTE_REVOKE = 0x00000040;
693
694/// vnode was opened
695pub const NOTE_OPEN = 0x00000080;
696
697/// file closed, fd did not allow write
698pub const NOTE_CLOSE = 0x00000100;
699
700/// file closed, fd did allow write
701pub const NOTE_CLOSE_WRITE = 0x00000200;
702
703/// file was read
704pub const NOTE_READ = 0x00000400;
705
706/// process exited
707pub const NOTE_EXIT = 0x80000000;
708
709/// process forked
710pub const NOTE_FORK = 0x40000000;
711
712/// process exec'd
713pub const NOTE_EXEC = 0x20000000;
714
715/// mask for signal & exit status
716pub const NOTE_PDATAMASK = 0x000fffff;
717pub const NOTE_PCTRLMASK = (~NOTE_PDATAMASK);
718
719/// data is seconds
720pub const NOTE_SECONDS = 0x00000001;
721
722/// data is milliseconds
723pub const NOTE_MSECONDS = 0x00000002;
724
725/// data is microseconds
726pub const NOTE_USECONDS = 0x00000004;
727
728/// data is nanoseconds
729pub const NOTE_NSECONDS = 0x00000008;
730
731/// timeout is absolute
732pub const NOTE_ABSTIME = 0x00000010;
733
734pub const TIOCEXCL = 0x2000740d;
735pub const TIOCNXCL = 0x2000740e;
736pub const TIOCSCTTY = 0x20007461;
737pub const TIOCGPGRP = 0x40047477;
738pub const TIOCSPGRP = 0x80047476;
739pub const TIOCOUTQ = 0x40047473;
740pub const TIOCSTI = 0x80017472;
741pub const TIOCGWINSZ = 0x40087468;
742pub const TIOCSWINSZ = 0x80087467;
743pub const TIOCMGET = 0x4004746a;
744pub const TIOCMBIS = 0x8004746c;
745pub const TIOCMBIC = 0x8004746b;
746pub const TIOCMSET = 0x8004746d;
747pub const FIONREAD = 0x4004667f;
748pub const TIOCCONS = 0x80047462;
749pub const TIOCPKT = 0x80047470;
750pub const FIONBIO = 0x8004667e;
751pub const TIOCNOTTY = 0x20007471;
752pub const TIOCSETD = 0x8004741b;
753pub const TIOCGETD = 0x4004741a;
754pub const TIOCSBRK = 0x2000747b;
755pub const TIOCCBRK = 0x2000747a;
756pub const TIOCGSID = 0x40047463;
757pub const TIOCGPTN = 0x4004740f;
758pub const TIOCSIG = 0x2004745f;
639pub const TCGETA = 0x8000;
640pub const TCSETA = 0x8001;
641pub const TCSETAW = 0x8004;
642pub const TCSETAF = 0x8003;
643pub const TCSBRK = 08005;
644pub const TCXONC = 0x8007;
645pub const TCFLSH = 0x8006;
646
647pub const TIOCSCTTY = 0x8017;
648pub const TIOCGPGRP = 0x8015;
649pub const TIOCSPGRP = 0x8016;
650pub const TIOCGWINSZ = 0x8012;
651pub const TIOCSWINSZ = 0x8013;
652pub const TIOCMGET = 0x8018;
653pub const TIOCMBIS = 0x8022;
654pub const TIOCMBIC = 0x8023;
655pub const TIOCMSET = 0x8019;
656pub const FIONREAD = 0xbe000001;
657pub const FIONBIO = 0xbe000000;
658pub const TIOCSBRK = 0x8020;
659pub const TIOCCBRK = 0x8021;
660pub const TIOCGSID = 0x8024;
759661
760662pub fn WEXITSTATUS(s: u32) u32 {
761 return (s & 0xff00) >> 8;
663 return (s & 0xff);
762664}
665
763666pub fn WTERMSIG(s: u32) u32 {
764 return s & 0x7f;
667 return (s >> 8) & 0xff;
765668}
669
766670pub fn WSTOPSIG(s: u32) u32 {
767671 return WEXITSTATUS(s);
768672}
673
769674pub fn WIFEXITED(s: u32) bool {
770675 return WTERMSIG(s) == 0;
771676}
677
772678pub fn WIFSTOPPED(s: u32) bool {
773 return @intCast(u16, (((s & 0xffff) *% 0x10001) >> 8)) > 0x7f00;
679 return ((s >> 16) & 0xff) != 0;
774680}
681
775682pub fn WIFSIGNALED(s: u32) bool {
776 return (s & 0xffff) -% 1 < 0xff;
683 return ((s >> 8) & 0xff) != 0;
777684}
778685
779686pub const winsize = extern struct {
......@@ -823,49 +730,47 @@ pub const sigset_t = extern struct {
823730 __bits: [_SIG_WORDS]u32,
824731};
825732
826pub const EPERM = 1; // Operation not permitted
827pub const ENOENT = 2; // No such file or directory
828pub const ESRCH = 3; // No such process
829pub const EINTR = 4; // Interrupted system call
830pub const EIO = 5; // Input/output error
831pub const ENXIO = 6; // Device not configured
832pub const E2BIG = 7; // Argument list too long
833pub const ENOEXEC = 8; // Exec format error
834pub const EBADF = 9; // Bad file descriptor
835pub const ECHILD = 10; // No child processes
836pub const EDEADLK = 11; // Resource deadlock avoided
837// 11 was EAGAIN
838pub const ENOMEM = 12; // Cannot allocate memory
839pub const EACCES = 13; // Permission denied
840pub const EFAULT = 14; // Bad address
841pub const ENOTBLK = 15; // Block device required
842pub const EBUSY = 16; // Device busy
843pub const EEXIST = 17; // File exists
844pub const EXDEV = 18; // Cross-device link
845pub const ENODEV = 19; // Operation not supported by device
846pub const ENOTDIR = 20; // Not a directory
847pub const EISDIR = 21; // Is a directory
848pub const EINVAL = 22; // Invalid argument
849pub const ENFILE = 23; // Too many open files in system
850pub const EMFILE = 24; // Too many open files
851pub const ENOTTY = 25; // Inappropriate ioctl for device
852pub const ETXTBSY = 26; // Text file busy
853pub const EFBIG = 27; // File too large
854pub const ENOSPC = 28; // No space left on device
855pub const ESPIPE = 29; // Illegal seek
856pub const EROFS = 30; // Read-only filesystem
857pub const EMLINK = 31; // Too many links
858pub const EPIPE = 32; // Broken pipe
733pub const EPERM = -0x7ffffff1; // Operation not permitted
734pub const ENOENT = -0x7fff9ffd; // No such file or directory
735pub const ESRCH = -0x7fff8ff3; // No such process
736pub const EINTR = -0x7ffffff6; // Interrupted system call
737pub const EIO = -0x7fffffff; // Input/output error
738pub const ENXIO = -0x7fff8ff5; // Device not configured
739pub const E2BIG = -0x7fff8fff; // Argument list too long
740pub const ENOEXEC = -0x7fffecfe; // Exec format error
741pub const ECHILD = -0x7fff8ffe; // No child processes
742pub const EDEADLK = -0x7fff8ffd; // Resource deadlock avoided
743pub const ENOMEM = -0x80000000; // Cannot allocate memory
744pub const EACCES = -0x7ffffffe; // Permission denied
745pub const EFAULT = -0x7fffecff; // Bad address
746pub const EBUSY = -0x7ffffff2; // Device busy
747pub const EEXIST = -0x7fff9ffe; // File exists
748pub const EXDEV = -0x7fff9ff5; // Cross-device link
749pub const ENODEV = -0x7fff8ff9; // Operation not supported by device
750pub const ENOTDIR = -0x7fff9ffb; // Not a directory
751pub const EISDIR = -0x7fff9ff7; // Is a directory
752pub const EINVAL = -0x7ffffffb; // Invalid argument
753pub const ENFILE = -0x7fff8ffa; // Too many open files in system
754pub const EMFILE = -0x7fff9ff6; // Too many open files
755pub const ENOTTY = -0x7fff8ff6; // Inappropriate ioctl for device
756pub const ETXTBSY = -0x7fff8fc5; // Text file busy
757pub const EFBIG = -0x7fff8ffc; // File too large
758pub const ENOSPC = -0x7fff9ff9; // No space left on device
759pub const ESPIPE = -0x7fff8ff4; // Illegal seek
760pub const EROFS = -0x7fff9ff8; // Read-only filesystem
761pub const EMLINK = -0x7fff8ffb; // Too many links
762pub const EPIPE = -0x7fff9ff3; // Broken pipe
763pub const EBADF = -0x7fffa000; // Bad file descriptor
859764
860765// math software
861766pub const EDOM = 33; // Numerical argument out of domain
862767pub const ERANGE = 34; // Result too large
863768
864769// non-blocking and interrupt i/o
865pub const EAGAIN = 35; // Resource temporarily unavailable
866pub const EWOULDBLOCK = EAGAIN; // Operation would block
867pub const EINPROGRESS = 36; // Operation now in progress
868pub const EALREADY = 37; // Operation already in progress
770pub const EAGAIN = -0x7ffffff5;
771pub const EWOULDBLOCK = -0x7ffffff5;
772pub const EINPROGRESS = -0x7fff8fdc;
773pub const EALREADY = -0x7fff8fdb;
869774
870775// ipc/network software -- argument errors
871776pub const ENOTSOCK = 38; // Socket operation on non-socket
......@@ -1447,3 +1352,20 @@ pub const directory_which = enum(c_int) {
14471352
14481353 _,
14491354};
1355
1356pub const cc_t = u8;
1357pub const speed_t = u8;
1358pub const tcflag_t = u32;
1359
1360pub const NCCS = 32;
1361
1362pub const termios = extern struct {
1363 c_iflag: tcflag_t,
1364 c_oflag: tcflag_t,
1365 c_cflag: tcflag_t,
1366 c_lflag: tcflag_t,
1367 c_line: cc_t,
1368 c_ispeed: speed_t,
1369 c_ospeed: speed_t,
1370 cc_t: [NCCS]cc_t,
1371};
lib/std/os/linux/tls.zig+2-2
......@@ -248,7 +248,7 @@ fn initTLS() void {
248248 tls_data = @intToPtr([*]u8, img_base + phdr.p_vaddr)[0..phdr.p_filesz];
249249 tls_data_alloc_size = phdr.p_memsz;
250250 } else {
251 tls_align_factor = @alignOf(*usize);
251 tls_align_factor = @alignOf(usize);
252252 tls_data = &[_]u8{};
253253 tls_data_alloc_size = 0;
254254 }
......@@ -308,7 +308,7 @@ fn initTLS() void {
308308}
309309
310310fn alignPtrCast(comptime T: type, ptr: [*]u8) callconv(.Inline) *T {
311 return @ptrCast(*T, @alignCast(@alignOf(*T), ptr));
311 return @ptrCast(*T, @alignCast(@alignOf(T), ptr));
312312}
313313
314314/// Initializes all the fields of the static TLS area and returns the computed
lib/std/std.zig+1
......@@ -88,6 +88,7 @@ pub const time = @import("time.zig");
8888pub const unicode = @import("unicode.zig");
8989pub const valgrind = @import("valgrind.zig");
9090pub const wasm = @import("wasm.zig");
91pub const x = @import("x.zig");
9192pub const zig = @import("zig.zig");
9293pub const start = @import("start.zig");
9394
lib/std/target.zig+4-3
......@@ -211,8 +211,9 @@ pub const Target = struct {
211211 /// If neither of these cases apply, a runtime check should be used to determine if the
212212 /// target supports a given OS feature.
213213 ///
214 /// Binaries built with a given maximum version will continue to function on newer operating system
215 /// versions. However, such a binary may not take full advantage of the newer operating system APIs.
214 /// Binaries built with a given maximum version will continue to function on newer
215 /// operating system versions. However, such a binary may not take full advantage of the
216 /// newer operating system APIs.
216217 ///
217218 /// See `Os.isAtLeast`.
218219 pub const VersionRange = union {
......@@ -260,7 +261,7 @@ pub const Target = struct {
260261 .freebsd => return .{
261262 .semver = Version.Range{
262263 .min = .{ .major = 12, .minor = 0 },
263 .max = .{ .major = 12, .minor = 1 },
264 .max = .{ .major = 13, .minor = 0 },
264265 },
265266 },
266267 .macos => return .{
lib/std/unicode.zig+1-1
......@@ -206,7 +206,7 @@ pub fn utf8ValidateSlice(s: []const u8) bool {
206206 return false;
207207 }
208208
209 if (utf8Decode(s[i .. i + cp_len])) |_| {} else |_| {
209 if (std.meta.isError(utf8Decode(s[i .. i + cp_len]))) {
210210 return false;
211211 }
212212 i += cp_len;
lib/std/x.zig created+1
......@@ -0,0 +1 @@
1pub const os = @import("x/os/os.zig");
lib/std/x/os/Socket.zig created+276
......@@ -0,0 +1,276 @@
1const std = @import("../../std.zig");
2
3const os = std.os;
4const mem = std.mem;
5const net = std.net;
6const time = std.time;
7const builtin = std.builtin;
8const testing = std.testing;
9
10const Socket = @This();
11
12/// A socket-address pair.
13pub const Connection = struct {
14 socket: Socket,
15 address: net.Address,
16};
17
18/// The underlying handle of a socket.
19fd: os.socket_t,
20
21/// Open a new socket.
22pub fn init(domain: u32, socket_type: u32, protocol: u32) !Socket {
23 return Socket{ .fd = try os.socket(domain, socket_type, protocol) };
24}
25
26/// Closes the socket.
27pub fn deinit(self: Socket) void {
28 os.closeSocket(self.fd);
29}
30
31/// Shutdown either the read side, or write side, or the entirety of a socket.
32pub fn shutdown(self: Socket, how: os.ShutdownHow) !void {
33 return os.shutdown(self.fd, how);
34}
35
36/// Binds the socket to an address.
37pub fn bind(self: Socket, address: net.Address) !void {
38 return os.bind(self.fd, &address.any, address.getOsSockLen());
39}
40
41/// Start listening for incoming connections on the socket.
42pub fn listen(self: Socket, max_backlog_size: u31) !void {
43 return os.listen(self.fd, max_backlog_size);
44}
45
46/// Have the socket attempt to the connect to an address.
47pub fn connect(self: Socket, address: net.Address) !void {
48 return os.connect(self.fd, &address.any, address.getOsSockLen());
49}
50
51/// Accept a pending incoming connection queued to the kernel backlog
52/// of the socket.
53pub fn accept(self: Socket, flags: u32) !Socket.Connection {
54 var address: os.sockaddr = undefined;
55 var address_len: u32 = @sizeOf(os.sockaddr);
56
57 const fd = try os.accept(self.fd, &address, &address_len, flags);
58
59 return Connection{
60 .socket = Socket{ .fd = fd },
61 .address = net.Address.initPosix(@alignCast(4, &address)),
62 };
63}
64
65/// Read data from the socket into the buffer provided. It returns the
66/// number of bytes read into the buffer provided.
67pub fn read(self: Socket, buf: []u8) !usize {
68 return os.read(self.fd, buf);
69}
70
71/// Read data from the socket into the buffer provided with a set of flags
72/// specified. It returns the number of bytes read into the buffer provided.
73pub fn recv(self: Socket, buf: []u8, flags: u32) !usize {
74 return os.recv(self.fd, buf, flags);
75}
76
77/// Write a buffer of data provided to the socket. It returns the number
78/// of bytes that are written to the socket.
79pub fn write(self: Socket, buf: []const u8) !usize {
80 return os.write(self.fd, buf);
81}
82
83/// Writes multiple I/O vectors to the socket. It returns the number
84/// of bytes that are written to the socket.
85pub fn writev(self: Socket, buffers: []const os.iovec_const) !usize {
86 return os.writev(self.fd, buffers);
87}
88
89/// Write a buffer of data provided to the socket with a set of flags specified.
90/// It returns the number of bytes that are written to the socket.
91pub fn send(self: Socket, buf: []const u8, flags: u32) !usize {
92 return os.send(self.fd, buf, flags);
93}
94
95/// Writes multiple I/O vectors with a prepended message header to the socket
96/// with a set of flags specified. It returns the number of bytes that are
97/// written to the socket.
98pub fn sendmsg(self: Socket, msg: os.msghdr_const, flags: u32) !usize {
99 return os.sendmsg(self.fd, msg, flags);
100}
101
102/// Query the address that the socket is locally bounded to.
103pub fn getLocalAddress(self: Socket) !net.Address {
104 var address: os.sockaddr = undefined;
105 var address_len: u32 = @sizeOf(os.sockaddr);
106 try os.getsockname(self.fd, &address, &address_len);
107 return net.Address.initPosix(@alignCast(4, &address));
108}
109
110/// Query and return the latest cached error on the socket.
111pub fn getError(self: Socket) !void {
112 return os.getsockoptError(self.fd);
113}
114
115/// Query the read buffer size of the socket.
116pub fn getReadBufferSize(self: Socket) !u32 {
117 var value: u32 = undefined;
118 var value_len: u32 = @sizeOf(u32);
119
120 const rc = os.system.getsockopt(self.fd, os.SOL_SOCKET, os.SO_RCVBUF, mem.asBytes(&value), &value_len);
121 return switch (os.errno(rc)) {
122 0 => value,
123 os.EBADF => error.BadFileDescriptor,
124 os.EFAULT => error.InvalidAddressSpace,
125 os.EINVAL => error.InvalidSocketOption,
126 os.ENOPROTOOPT => error.UnknownSocketOption,
127 os.ENOTSOCK => error.NotASocket,
128 else => |err| os.unexpectedErrno(err),
129 };
130}
131
132/// Query the write buffer size of the socket.
133pub fn getWriteBufferSize(self: Socket) !u32 {
134 var value: u32 = undefined;
135 var value_len: u32 = @sizeOf(u32);
136
137 const rc = os.system.getsockopt(self.fd, os.SOL_SOCKET, os.SO_SNDBUF, mem.asBytes(&value), &value_len);
138 return switch (os.errno(rc)) {
139 0 => value,
140 os.EBADF => error.BadFileDescriptor,
141 os.EFAULT => error.InvalidAddressSpace,
142 os.EINVAL => error.InvalidSocketOption,
143 os.ENOPROTOOPT => error.UnknownSocketOption,
144 os.ENOTSOCK => error.NotASocket,
145 else => |err| os.unexpectedErrno(err),
146 };
147}
148
149/// Allow multiple sockets on the same host to listen on the same address. It returns `error.UnsupportedSocketOption` if
150/// the host does not support sockets listening the same address.
151pub fn setReuseAddress(self: Socket, enabled: bool) !void {
152 if (comptime @hasDecl(os, "SO_REUSEADDR")) {
153 return os.setsockopt(self.fd, os.SOL_SOCKET, os.SO_REUSEADDR, mem.asBytes(&@as(usize, @boolToInt(enabled))));
154 }
155 return error.UnsupportedSocketOption;
156}
157
158/// Allow multiple sockets on the same host to listen on the same port. It returns `error.UnsupportedSocketOption` if
159/// the host does not supports sockets listening on the same port.
160pub fn setReusePort(self: Socket, enabled: bool) !void {
161 if (comptime @hasDecl(os, "SO_REUSEPORT")) {
162 return os.setsockopt(self.fd, os.SOL_SOCKET, os.SO_REUSEPORT, mem.asBytes(&@as(usize, @boolToInt(enabled))));
163 }
164 return error.UnsupportedSocketOption;
165}
166
167/// Disable Nagle's algorithm on a TCP socket. It returns `error.UnsupportedSocketOption` if the host does not support
168/// sockets disabling Nagle's algorithm.
169pub fn setNoDelay(self: Socket, enabled: bool) !void {
170 if (comptime @hasDecl(os, "TCP_NODELAY")) {
171 return os.setsockopt(self.fd, os.IPPROTO_TCP, os.TCP_NODELAY, mem.asBytes(&@as(usize, @boolToInt(enabled))));
172 }
173 return error.UnsupportedSocketOption;
174}
175
176/// Enables TCP Fast Open (RFC 7413) on a TCP socket. It returns `error.UnsupportedSocketOption` if the host does not
177/// support TCP Fast Open.
178pub fn setFastOpen(self: Socket, enabled: bool) !void {
179 if (comptime @hasDecl(os, "TCP_FASTOPEN")) {
180 return os.setsockopt(self.fd, os.IPPROTO_TCP, os.TCP_FASTOPEN, mem.asBytes(&@as(usize, @boolToInt(enabled))));
181 }
182 return error.UnsupportedSocketOption;
183}
184
185/// Enables TCP Quick ACK on a TCP socket to immediately send rather than delay ACKs when necessary. It returns
186/// `error.UnsupportedSocketOption` if the host does not support TCP Quick ACK.
187pub fn setQuickACK(self: Socket, enabled: bool) !void {
188 if (comptime @hasDecl(os, "TCP_QUICKACK")) {
189 return os.setsockopt(self.fd, os.IPPROTO_TCP, os.TCP_QUICKACK, mem.asBytes(&@as(usize, @boolToInt(enabled))));
190 }
191 return error.UnsupportedSocketOption;
192}
193
194/// Set the write buffer size of the socket.
195pub fn setWriteBufferSize(self: Socket, size: u32) !void {
196 return os.setsockopt(self.fd, os.SOL_SOCKET, os.SO_SNDBUF, mem.asBytes(&size));
197}
198
199/// Set the read buffer size of the socket.
200pub fn setReadBufferSize(self: Socket, size: u32) !void {
201 return os.setsockopt(self.fd, os.SOL_SOCKET, os.SO_RCVBUF, mem.asBytes(&size));
202}
203
204/// Set a timeout on the socket that is to occur if no messages are successfully written
205/// to its bound destination after a specified number of milliseconds. A subsequent write
206/// to the socket will thereafter return `error.WouldBlock` should the timeout be exceeded.
207pub fn setWriteTimeout(self: Socket, milliseconds: usize) !void {
208 const timeout = os.timeval{
209 .tv_sec = @intCast(isize, milliseconds / time.ms_per_s),
210 .tv_usec = @intCast(isize, (milliseconds % time.ms_per_s) * time.us_per_ms),
211 };
212
213 return os.setsockopt(self.fd, os.SOL_SOCKET, os.SO_SNDTIMEO, mem.asBytes(&timeout));
214}
215
216/// Set a timeout on the socket that is to occur if no messages are successfully read
217/// from its bound destination after a specified number of milliseconds. A subsequent
218/// read from the socket will thereafter return `error.WouldBlock` should the timeout be
219/// exceeded.
220pub fn setReadTimeout(self: Socket, milliseconds: usize) !void {
221 const timeout = os.timeval{
222 .tv_sec = @intCast(isize, milliseconds / time.ms_per_s),
223 .tv_usec = @intCast(isize, (milliseconds % time.ms_per_s) * time.us_per_ms),
224 };
225
226 return os.setsockopt(self.fd, os.SOL_SOCKET, os.SO_RCVTIMEO, mem.asBytes(&timeout));
227}
228
229test {
230 testing.refAllDecls(@This());
231}
232
233test "socket/linux: set read timeout of 1 millisecond on blocking socket" {
234 if (builtin.os.tag != .linux) return error.SkipZigTest;
235
236 const a = try Socket.init(os.AF_INET, os.SOCK_STREAM | os.SOCK_CLOEXEC, os.IPPROTO_TCP);
237 defer a.deinit();
238
239 try a.bind(net.Address.initIp4([_]u8{ 0, 0, 0, 0 }, 0));
240 try a.listen(128);
241
242 const binded_address = try a.getLocalAddress();
243
244 const b = try Socket.init(os.AF_INET, os.SOCK_STREAM | os.SOCK_CLOEXEC, os.IPPROTO_TCP);
245 defer b.deinit();
246
247 try b.connect(binded_address);
248 try b.setReadTimeout(1);
249
250 const ab = try a.accept(os.SOCK_CLOEXEC);
251 defer ab.socket.deinit();
252
253 var buf: [1]u8 = undefined;
254 testing.expectError(error.WouldBlock, b.read(&buf));
255}
256
257test "socket/linux: create non-blocking socket pair" {
258 if (builtin.os.tag != .linux) return error.SkipZigTest;
259
260 const a = try Socket.init(os.AF_INET, os.SOCK_STREAM | os.SOCK_NONBLOCK | os.SOCK_CLOEXEC, os.IPPROTO_TCP);
261 defer a.deinit();
262
263 try a.bind(net.Address.initIp4([_]u8{ 0, 0, 0, 0 }, 0));
264 try a.listen(128);
265
266 const binded_address = try a.getLocalAddress();
267
268 const b = try Socket.init(os.AF_INET, os.SOCK_STREAM | os.SOCK_NONBLOCK | os.SOCK_CLOEXEC, os.IPPROTO_TCP);
269 defer b.deinit();
270
271 testing.expectError(error.WouldBlock, b.connect(binded_address));
272 try b.getError();
273
274 const ab = try a.accept(os.SOCK_NONBLOCK | os.SOCK_CLOEXEC);
275 defer ab.socket.deinit();
276}
lib/std/x/os/os.zig created+9
......@@ -0,0 +1,9 @@
1const std = @import("../../std.zig");
2
3const testing = std.testing;
4
5pub const Socket = @import("Socket.zig");
6
7test {
8 testing.refAllDecls(@This());
9}
lib/std/zig/system.zig+10-5
......@@ -15,6 +15,7 @@ const Target = std.Target;
1515const CrossTarget = std.zig.CrossTarget;
1616const macos = @import("system/macos.zig");
1717const native_endian = std.Target.current.cpu.arch.endian();
18const linux = @import("system/linux.zig");
1819pub const windows = @import("system/windows.zig");
1920
2021pub const getSDKPath = macos.getSDKPath;
......@@ -912,15 +913,19 @@ pub const NativeTargetInfo = struct {
912913 .x86_64, .i386 => {
913914 return @import("system/x86.zig").detectNativeCpuAndFeatures(cpu_arch, os, cross_target);
914915 },
915 else => {
916 // This architecture does not have CPU model & feature detection yet.
917 // See https://github.com/ziglang/zig/issues/4591
918 return null;
919 },
916 else => {},
920917 }
918
919 // This architecture does not have CPU model & feature detection yet.
920 // See https://github.com/ziglang/zig/issues/4591
921 if (std.Target.current.os.tag != .linux)
922 return null;
923
924 return linux.detectNativeCpuAndFeatures();
921925 }
922926};
923927
924928test {
925929 _ = @import("system/macos.zig");
930 _ = @import("system/linux.zig");
926931}
lib/std/zig/system/linux.zig created+199
......@@ -0,0 +1,199 @@
1const std = @import("std");
2const mem = std.mem;
3const io = std.io;
4const fs = std.fs;
5const fmt = std.fmt;
6const testing = std.testing;
7
8const Target = std.Target;
9const CrossTarget = std.zig.CrossTarget;
10
11const assert = std.debug.assert;
12
13const SparcCpuinfoImpl = struct {
14 model: ?*const Target.Cpu.Model = null,
15 is_64bit: bool = false,
16
17 const cpu_names = .{
18 .{ "SuperSparc", &Target.sparc.cpu.supersparc },
19 .{ "HyperSparc", &Target.sparc.cpu.hypersparc },
20 .{ "SpitFire", &Target.sparc.cpu.ultrasparc },
21 .{ "BlackBird", &Target.sparc.cpu.ultrasparc },
22 .{ "Sabre", &Target.sparc.cpu.ultrasparc },
23 .{ "Hummingbird", &Target.sparc.cpu.ultrasparc },
24 .{ "Cheetah", &Target.sparc.cpu.ultrasparc3 },
25 .{ "Jalapeno", &Target.sparc.cpu.ultrasparc3 },
26 .{ "Jaguar", &Target.sparc.cpu.ultrasparc3 },
27 .{ "Panther", &Target.sparc.cpu.ultrasparc3 },
28 .{ "Serrano", &Target.sparc.cpu.ultrasparc3 },
29 .{ "UltraSparc T1", &Target.sparc.cpu.niagara },
30 .{ "UltraSparc T2", &Target.sparc.cpu.niagara2 },
31 .{ "UltraSparc T3", &Target.sparc.cpu.niagara3 },
32 .{ "UltraSparc T4", &Target.sparc.cpu.niagara4 },
33 .{ "UltraSparc T5", &Target.sparc.cpu.niagara4 },
34 .{ "LEON", &Target.sparc.cpu.leon3 },
35 };
36
37 fn line_hook(self: *SparcCpuinfoImpl, key: []const u8, value: []const u8) !bool {
38 if (mem.eql(u8, key, "cpu")) {
39 inline for (cpu_names) |pair| {
40 if (mem.indexOfPos(u8, value, 0, pair[0]) != null) {
41 self.model = pair[1];
42 break;
43 }
44 }
45 } else if (mem.eql(u8, key, "type")) {
46 self.is_64bit = mem.eql(u8, value, "sun4u") or mem.eql(u8, value, "sun4v");
47 }
48
49 return true;
50 }
51
52 fn finalize(self: *const SparcCpuinfoImpl, arch: Target.Cpu.Arch) ?Target.Cpu {
53 // At the moment we only support 64bit SPARC systems.
54 assert(self.is_64bit);
55
56 const model = self.model orelse Target.Cpu.Model.generic(arch);
57 return Target.Cpu{
58 .arch = arch,
59 .model = model,
60 .features = model.features,
61 };
62 }
63};
64
65const SparcCpuinfoParser = CpuinfoParser(SparcCpuinfoImpl);
66
67test "cpuinfo: SPARC" {
68 try testParser(SparcCpuinfoParser, &Target.sparc.cpu.niagara2,
69 \\cpu : UltraSparc T2 (Niagara2)
70 \\fpu : UltraSparc T2 integrated FPU
71 \\pmu : niagara2
72 \\type : sun4v
73 );
74}
75
76const PowerpcCpuinfoImpl = struct {
77 model: ?*const Target.Cpu.Model = null,
78
79 const cpu_names = .{
80 .{ "604e", &Target.powerpc.cpu.@"604e" },
81 .{ "604", &Target.powerpc.cpu.@"604" },
82 .{ "7400", &Target.powerpc.cpu.@"7400" },
83 .{ "7410", &Target.powerpc.cpu.@"7400" },
84 .{ "7447", &Target.powerpc.cpu.@"7400" },
85 .{ "7455", &Target.powerpc.cpu.@"7450" },
86 .{ "G4", &Target.powerpc.cpu.@"g4" },
87 .{ "POWER4", &Target.powerpc.cpu.@"970" },
88 .{ "PPC970FX", &Target.powerpc.cpu.@"970" },
89 .{ "PPC970MP", &Target.powerpc.cpu.@"970" },
90 .{ "G5", &Target.powerpc.cpu.@"g5" },
91 .{ "POWER5", &Target.powerpc.cpu.@"g5" },
92 .{ "A2", &Target.powerpc.cpu.@"a2" },
93 .{ "POWER6", &Target.powerpc.cpu.@"pwr6" },
94 .{ "POWER7", &Target.powerpc.cpu.@"pwr7" },
95 .{ "POWER8", &Target.powerpc.cpu.@"pwr8" },
96 .{ "POWER8E", &Target.powerpc.cpu.@"pwr8" },
97 .{ "POWER8NVL", &Target.powerpc.cpu.@"pwr8" },
98 .{ "POWER9", &Target.powerpc.cpu.@"pwr9" },
99 .{ "POWER10", &Target.powerpc.cpu.@"pwr10" },
100 };
101
102 fn line_hook(self: *PowerpcCpuinfoImpl, key: []const u8, value: []const u8) !bool {
103 if (mem.eql(u8, key, "cpu")) {
104 // The model name is often followed by a comma or space and extra
105 // info.
106 inline for (cpu_names) |pair| {
107 const end_index = mem.indexOfAny(u8, value, ", ") orelse value.len;
108 if (mem.eql(u8, value[0..end_index], pair[0])) {
109 self.model = pair[1];
110 break;
111 }
112 }
113
114 // Stop the detection once we've seen the first core.
115 return false;
116 }
117
118 return true;
119 }
120
121 fn finalize(self: *const PowerpcCpuinfoImpl, arch: Target.Cpu.Arch) ?Target.Cpu {
122 const model = self.model orelse Target.Cpu.Model.generic(arch);
123 return Target.Cpu{
124 .arch = arch,
125 .model = model,
126 .features = model.features,
127 };
128 }
129};
130
131const PowerpcCpuinfoParser = CpuinfoParser(PowerpcCpuinfoImpl);
132
133test "cpuinfo: PowerPC" {
134 try testParser(PowerpcCpuinfoParser, &Target.powerpc.cpu.@"970",
135 \\processor : 0
136 \\cpu : PPC970MP, altivec supported
137 \\clock : 1250.000000MHz
138 \\revision : 1.1 (pvr 0044 0101)
139 );
140 try testParser(PowerpcCpuinfoParser, &Target.powerpc.cpu.pwr8,
141 \\processor : 0
142 \\cpu : POWER8 (raw), altivec supported
143 \\clock : 2926.000000MHz
144 \\revision : 2.0 (pvr 004d 0200)
145 );
146}
147
148fn testParser(parser: anytype, expected_model: *const Target.Cpu.Model, input: []const u8) !void {
149 var fbs = io.fixedBufferStream(input);
150 const result = try parser.parse(.powerpc, fbs.reader());
151 testing.expectEqual(expected_model, result.?.model);
152 testing.expect(expected_model.features.eql(result.?.features));
153}
154
155// The generic implementation of a /proc/cpuinfo parser.
156// For every line it invokes the line_hook method with the key and value strings
157// as first and second parameters. Returning false from the hook function stops
158// the iteration without raising an error.
159// When all the lines have been analyzed the finalize method is called.
160fn CpuinfoParser(comptime impl: anytype) type {
161 return struct {
162 fn parse(arch: Target.Cpu.Arch, reader: anytype) anyerror!?Target.Cpu {
163 var line_buf: [1024]u8 = undefined;
164 var obj: impl = .{};
165
166 while (true) {
167 const line = (try reader.readUntilDelimiterOrEof(&line_buf, '\n')) orelse break;
168 const colon_pos = mem.indexOfScalar(u8, line, ':') orelse continue;
169 const key = mem.trimRight(u8, line[0..colon_pos], " \t");
170 const value = mem.trimLeft(u8, line[colon_pos + 1 ..], " \t");
171
172 if (!try obj.line_hook(key, value))
173 break;
174 }
175
176 return obj.finalize(arch);
177 }
178 };
179}
180
181pub fn detectNativeCpuAndFeatures() ?Target.Cpu {
182 var f = fs.openFileAbsolute("/proc/cpuinfo", .{ .intended_io_mode = .blocking }) catch |err| switch (err) {
183 else => return null,
184 };
185 defer f.close();
186
187 const current_arch = std.Target.current.cpu.arch;
188 switch (current_arch) {
189 .sparcv9 => {
190 return SparcCpuinfoParser.parse(current_arch, f.reader()) catch null;
191 },
192 .powerpc, .powerpcle, .powerpc64, .powerpc64le => {
193 return PowerpcCpuinfoParser.parse(current_arch, f.reader()) catch null;
194 },
195 else => {},
196 }
197
198 return null;
199}
src/clang.zig+16
......@@ -104,6 +104,16 @@ pub const APFloat = opaque {
104104 extern fn ZigClangAPFloat_toString(*const APFloat, precision: c_uint, maxPadding: c_uint, truncateZero: bool) [*:0]const u8;
105105};
106106
107pub const APFloatBaseSemantics = extern enum {
108 IEEEhalf,
109 BFloat,
110 IEEEsingle,
111 IEEEdouble,
112 x86DoubleExtended,
113 IEEEquad,
114 PPCDoubleDouble,
115};
116
107117pub const APInt = opaque {
108118 pub const getLimitedValue = ZigClangAPInt_getLimitedValue;
109119 extern fn ZigClangAPInt_getLimitedValue(*const APInt, limit: u64) u64;
......@@ -455,6 +465,12 @@ pub const FileID = opaque {};
455465pub const FloatingLiteral = opaque {
456466 pub const getValueAsApproximateDouble = ZigClangFloatingLiteral_getValueAsApproximateDouble;
457467 extern fn ZigClangFloatingLiteral_getValueAsApproximateDouble(*const FloatingLiteral) f64;
468
469 pub const getBeginLoc = ZigClangIntegerLiteral_getBeginLoc;
470 extern fn ZigClangIntegerLiteral_getBeginLoc(*const FloatingLiteral) SourceLocation;
471
472 pub const getRawSemantics = ZigClangFloatingLiteral_getRawSemantics;
473 extern fn ZigClangFloatingLiteral_getRawSemantics(*const FloatingLiteral) APFloatBaseSemantics;
458474};
459475
460476pub const ForStmt = opaque {
src/codegen.zig+29-17
......@@ -449,7 +449,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
449449 .rbrace_src = src_data.rbrace_src,
450450 .source = src_data.source,
451451 };
452 defer function.register_manager.deinit(bin_file.allocator);
453452 defer function.stack.deinit(bin_file.allocator);
454453 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
455454
......@@ -779,8 +778,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
779778 branch.inst_table.putAssumeCapacity(inst, .dead);
780779 switch (prev_value) {
781780 .register => |reg| {
782 const canon_reg = toCanonicalReg(reg);
783 self.register_manager.freeReg(canon_reg);
781 // TODO separate architectures with registers from
782 // stack-based architectures (spu_2)
783 if (callee_preserved_regs.len > 0) {
784 const canon_reg = toCanonicalReg(reg);
785 self.register_manager.freeReg(canon_reg);
786 }
784787 },
785788 else => {}, // TODO process stack allocation death
786789 }
......@@ -920,9 +923,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
920923 const ptr_bits = arch.ptrBitWidth();
921924 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
922925 if (abi_size <= ptr_bytes) {
923 try self.register_manager.registers.ensureCapacity(self.gpa, self.register_manager.registers.count() + 1);
924 if (self.register_manager.tryAllocReg(inst)) |reg| {
925 return MCValue{ .register = registerAlias(reg, abi_size) };
926 // TODO separate architectures with registers from
927 // stack-based architectures (spu_2)
928 if (callee_preserved_regs.len > 0) {
929 if (self.register_manager.tryAllocReg(inst)) |reg| {
930 return MCValue{ .register = registerAlias(reg, abi_size) };
931 }
926932 }
927933 }
928934 }
......@@ -952,8 +958,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
952958 /// `reg_owner` is the instruction that gets associated with the register in the register table.
953959 /// This can have a side effect of spilling instructions to the stack to free up a register.
954960 fn copyToNewRegister(self: *Self, reg_owner: *ir.Inst, mcv: MCValue) !MCValue {
955 try self.register_manager.registers.ensureCapacity(self.gpa, @intCast(u32, self.register_manager.registers.count() + 1));
956
957961 const reg = try self.register_manager.allocReg(reg_owner);
958962 try self.genSetReg(reg_owner.src, reg_owner.ty, reg, mcv);
959963 return MCValue{ .register = reg };
......@@ -1240,10 +1244,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
12401244 .register => |reg| {
12411245 // If it's in the registers table, need to associate the register with the
12421246 // new instruction.
1243 if (self.register_manager.registers.getEntry(toCanonicalReg(reg))) |entry| {
1244 entry.value = inst;
1247 // TODO separate architectures with registers from
1248 // stack-based architectures (spu_2)
1249 if (callee_preserved_regs.len > 0) {
1250 if (reg.allocIndex()) |index| {
1251 if (!self.register_manager.isRegFree(reg)) {
1252 self.register_manager.registers[index] = inst;
1253 }
1254 }
1255 log.debug("reusing {} => {*}", .{ reg, inst });
12451256 }
1246 log.debug("reusing {} => {*}", .{ reg, inst });
12471257 },
12481258 .stack_offset => |off| {
12491259 log.debug("reusing stack offset {} => {*}", .{ off, inst });
......@@ -1738,6 +1748,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
17381748 const arg_index = self.arg_index;
17391749 self.arg_index += 1;
17401750
1751 // TODO separate architectures with registers from
1752 // stack-based architectures (spu_2)
17411753 if (callee_preserved_regs.len == 0) {
17421754 return self.fail(inst.base.src, "TODO implement Register enum for {}", .{self.target.cpu.arch});
17431755 }
......@@ -1769,7 +1781,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
17691781
17701782 switch (mcv) {
17711783 .register => |reg| {
1772 try self.register_manager.registers.ensureCapacity(self.gpa, self.register_manager.registers.count() + 1);
17731784 self.register_manager.getRegAssumeFree(toCanonicalReg(reg), &inst.base);
17741785 },
17751786 else => {},
......@@ -2075,7 +2086,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
20752086 switch (mc_arg) {
20762087 .none => continue,
20772088 .register => |reg| {
2078 try self.register_manager.getRegWithoutTracking(reg);
2089 // TODO prevent this macho if block to be generated for all archs
2090 switch (arch) {
2091 .x86_64, .aarch64 => try self.register_manager.getRegWithoutTracking(reg),
2092 else => unreachable,
2093 }
20792094 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
20802095 },
20812096 .stack_offset => {
......@@ -2397,8 +2412,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
23972412 const parent_free_registers = self.register_manager.free_registers;
23982413 var parent_stack = try self.stack.clone(self.gpa);
23992414 defer parent_stack.deinit(self.gpa);
2400 var parent_registers = try self.register_manager.registers.clone(self.gpa);
2401 defer parent_registers.deinit(self.gpa);
2415 const parent_registers = self.register_manager.registers;
24022416
24032417 try self.branch_stack.append(.{});
24042418
......@@ -2414,9 +2428,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
24142428 var saved_then_branch = self.branch_stack.pop();
24152429 defer saved_then_branch.deinit(self.gpa);
24162430
2417 self.register_manager.registers.deinit(self.gpa);
24182431 self.register_manager.registers = parent_registers;
2419 parent_registers = .{};
24202432
24212433 self.stack.deinit(self.gpa);
24222434 self.stack = parent_stack;
src/codegen/riscv64.zig+47-10
......@@ -1,5 +1,7 @@
11const std = @import("std");
22const DW = std.dwarf;
3const assert = std.debug.assert;
4const testing = std.testing;
35
46// TODO: this is only tagged to facilitate the monstrosity.
57// Once packed structs work make it packed.
......@@ -110,7 +112,7 @@ pub const Instruction = union(enum) {
110112 // -- less burden on callsite, bonus semantic checking
111113 fn bType(op: u7, fn3: u3, r1: Register, r2: Register, imm: i13) Instruction {
112114 const umm = @bitCast(u13, imm);
113 if (umm % 2 != 0) @panic("Internal error: misaligned branch target");
115 assert(umm % 2 == 0); // misaligned branch target
114116
115117 return Instruction{
116118 .B = .{
......@@ -140,15 +142,15 @@ pub const Instruction = union(enum) {
140142 }
141143
142144 fn jType(op: u7, rd: Register, imm: i21) Instruction {
143 const umm = @bitcast(u21, imm);
144 if (umm % 2 != 0) @panic("Internal error: misaligned jump target");
145 const umm = @bitCast(u21, imm);
146 assert(umm % 2 == 0); // misaligned jump target
145147
146148 return Instruction{
147149 .J = .{
148150 .opcode = op,
149151 .rd = @enumToInt(rd),
150152 .imm1_10 = @truncate(u10, umm >> 1),
151 .imm11 = @truncate(u1, umm >> 1),
153 .imm11 = @truncate(u1, umm >> 11),
152154 .imm12_19 = @truncate(u8, umm >> 12),
153155 .imm20 = @truncate(u1, umm >> 20),
154156 },
......@@ -340,27 +342,27 @@ pub const Instruction = union(enum) {
340342
341343 // Branch
342344
343 pub fn beq(r1: Register, r2: Register, offset: u13) Instruction {
345 pub fn beq(r1: Register, r2: Register, offset: i13) Instruction {
344346 return bType(0b1100011, 0b000, r1, r2, offset);
345347 }
346348
347 pub fn bne(r1: Register, r2: Register, offset: u13) Instruction {
349 pub fn bne(r1: Register, r2: Register, offset: i13) Instruction {
348350 return bType(0b1100011, 0b001, r1, r2, offset);
349351 }
350352
351 pub fn blt(r1: Register, r2: Register, offset: u13) Instruction {
353 pub fn blt(r1: Register, r2: Register, offset: i13) Instruction {
352354 return bType(0b1100011, 0b100, r1, r2, offset);
353355 }
354356
355 pub fn bge(r1: Register, r2: Register, offset: u13) Instruction {
357 pub fn bge(r1: Register, r2: Register, offset: i13) Instruction {
356358 return bType(0b1100011, 0b101, r1, r2, offset);
357359 }
358360
359 pub fn bltu(r1: Register, r2: Register, offset: u13) Instruction {
361 pub fn bltu(r1: Register, r2: Register, offset: i13) Instruction {
360362 return bType(0b1100011, 0b110, r1, r2, offset);
361363 }
362364
363 pub fn bgeu(r1: Register, r2: Register, offset: u13) Instruction {
365 pub fn bgeu(r1: Register, r2: Register, offset: i13) Instruction {
364366 return bType(0b1100011, 0b111, r1, r2, offset);
365367 }
366368
......@@ -431,3 +433,38 @@ pub const Register = enum(u5) {
431433pub const callee_preserved_regs = [_]Register{
432434 .s0, .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11,
433435};
436
437test "serialize instructions" {
438 const Testcase = struct {
439 inst: Instruction,
440 expected: u32,
441 };
442
443 const testcases = [_]Testcase{
444 .{ // add t6, zero, zero
445 .inst = Instruction.add(.t6, .zero, .zero),
446 .expected = 0b0000000_00000_00000_000_11111_0110011,
447 },
448 .{ // sd s0, 0x7f(s0)
449 .inst = Instruction.sd(.s0, 0x7f, .s0),
450 .expected = 0b0000011_01000_01000_011_11111_0100011,
451 },
452 .{ // bne s0, s1, 0x42
453 .inst = Instruction.bne(.s0, .s1, 0x42),
454 .expected = 0b0_000010_01001_01000_001_0001_0_1100011,
455 },
456 .{ // j 0x1a
457 .inst = Instruction.jal(.zero, 0x1a),
458 .expected = 0b0_0000001101_0_00000000_00000_1101111,
459 },
460 .{ // ebreak
461 .inst = Instruction.ebreak,
462 .expected = 0b000000000001_00000_000_00000_1110011,
463 },
464 };
465
466 for (testcases) |case| {
467 const actual = case.inst.toU32();
468 testing.expectEqual(case.expected, actual);
469 }
470}
src/libc_installation.zig+9-2
......@@ -9,6 +9,7 @@ const build_options = @import("build_options");
99const is_darwin = Target.current.isDarwin();
1010const is_windows = Target.current.os.tag == .windows;
1111const is_gnu = Target.current.isGnu();
12const is_haiku = Target.current.os.tag == .haiku;
1213
1314const log = std.log.scoped(.libc_installation);
1415
......@@ -279,8 +280,14 @@ pub const LibCInstallation = struct {
279280 return error.CCompilerCannotFindHeaders;
280281 }
281282
282 const include_dir_example_file = "stdlib.h";
283 const sys_include_dir_example_file = if (is_windows) "sys\\types.h" else "sys/errno.h";
283 const include_dir_example_file = if (is_haiku) "posix/stdlib.h" else "stdlib.h";
284 const sys_include_dir_example_file = if (is_windows)
285 "sys\\types.h"
286 else if (is_haiku)
287 "posix/errno.h"
288 else
289 "sys/errno.h"
290 ;
284291
285292 var path_i: usize = 0;
286293 while (path_i < search_paths.items.len) : (path_i += 1) {
src/register_manager.zig+43-50
......@@ -16,7 +16,7 @@ pub fn RegisterManager(
1616) type {
1717 return struct {
1818 /// The key must be canonical register.
19 registers: std.AutoHashMapUnmanaged(Register, *ir.Inst) = .{},
19 registers: [callee_preserved_regs.len]?*ir.Inst = [_]?*ir.Inst{null} ** callee_preserved_regs.len,
2020 free_registers: FreeRegInt = math.maxInt(FreeRegInt),
2121 /// Tracks all registers allocated in the course of this function
2222 allocated_registers: FreeRegInt = 0,
......@@ -31,14 +31,6 @@ pub fn RegisterManager(
3131 return @fieldParentPtr(Function, "register_manager", self);
3232 }
3333
34 pub fn deinit(self: *Self, allocator: *Allocator) void {
35 self.registers.deinit(allocator);
36 }
37
38 fn isTracked(reg: Register) bool {
39 return reg.allocIndex() != null;
40 }
41
4234 fn markRegUsed(self: *Self, reg: Register) void {
4335 if (FreeRegInt == u0) return;
4436 const index = reg.allocIndex() orelse return;
......@@ -73,13 +65,13 @@ pub fn RegisterManager(
7365 return self.allocated_registers & @as(FreeRegInt, 1) << shift != 0;
7466 }
7567
76 /// Before calling, must ensureCapacity + count on self.registers.
7768 /// Returns `null` if all registers are allocated.
7869 pub fn tryAllocRegs(self: *Self, comptime count: comptime_int, insts: [count]*ir.Inst) ?[count]Register {
7970 if (self.tryAllocRegsWithoutTracking(count)) |regs| {
8071 for (regs) |reg, i| {
72 const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null
73 self.registers[index] = insts[i];
8174 self.markRegUsed(reg);
82 self.registers.putAssumeCapacityNoClobber(reg, insts[i]);
8375 }
8476
8577 return regs;
......@@ -88,13 +80,11 @@ pub fn RegisterManager(
8880 }
8981 }
9082
91 /// Before calling, must ensureCapacity + 1 on self.registers.
9283 /// Returns `null` if all registers are allocated.
9384 pub fn tryAllocReg(self: *Self, inst: *ir.Inst) ?Register {
9485 return if (tryAllocRegs(self, 1, .{inst})) |regs| regs[0] else null;
9586 }
9687
97 /// Before calling, must ensureCapacity + count on self.registers.
9888 pub fn allocRegs(self: *Self, comptime count: comptime_int, insts: [count]*ir.Inst) ![count]Register {
9989 comptime assert(count > 0 and count <= callee_preserved_regs.len);
10090
......@@ -106,24 +96,22 @@ pub fn RegisterManager(
10696 std.mem.copy(Register, &regs, callee_preserved_regs[0..count]);
10797
10898 for (regs) |reg, i| {
99 const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null
109100 if (self.isRegFree(reg)) {
110101 self.markRegUsed(reg);
111 self.registers.putAssumeCapacityNoClobber(reg, insts[i]);
112102 } else {
113 const regs_entry = self.registers.getEntry(reg).?;
114 const spilled_inst = regs_entry.value;
115 regs_entry.value = insts[i];
103 const spilled_inst = self.registers[index].?;
116104 try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);
117105 }
106 self.registers[index] = insts[i];
118107 }
119108
120109 break :blk regs;
121110 };
122111 }
123112
124 /// Before calling, must ensureCapacity + 1 on self.registers.
125113 pub fn allocReg(self: *Self, inst: *ir.Inst) !Register {
126 return (try allocRegs(self, 1, .{inst}))[0];
114 return (try self.allocRegs(1, .{inst}))[0];
127115 }
128116
129117 /// Does not track the registers.
......@@ -150,37 +138,48 @@ pub fn RegisterManager(
150138 /// Does not track the register.
151139 /// Returns `null` if all registers are allocated.
152140 pub fn tryAllocRegWithoutTracking(self: *Self) ?Register {
153 return if (tryAllocRegsWithoutTracking(self, 1)) |regs| regs[0] else null;
141 return if (self.tryAllocRegsWithoutTracking(1)) |regs| regs[0] else null;
154142 }
155143
156 /// Does not track the register.
157 pub fn allocRegWithoutTracking(self: *Self) !Register {
158 return self.tryAllocRegWithoutTracking() orelse b: {
159 // We'll take over the first register. Move the instruction that was previously
160 // there to a stack allocation.
161 const reg = callee_preserved_regs[0];
162 const regs_entry = self.registers.remove(reg).?;
163 const spilled_inst = regs_entry.value;
164 try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);
165 self.markRegFree(reg);
144 /// Does not track the registers
145 pub fn allocRegsWithoutTracking(self: *Self, comptime count: comptime_int) ![count]Register {
146 return self.tryAllocRegsWithoutTracking(count) orelse blk: {
147 // We'll take over the first count registers. Spill
148 // the instructions that were previously there to a
149 // stack allocations.
150 var regs: [count]Register = undefined;
151 std.mem.copy(Register, &regs, callee_preserved_regs[0..count]);
166152
167 break :b reg;
153 for (regs) |reg, i| {
154 const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null
155 if (!self.isRegFree(reg)) {
156 const spilled_inst = self.registers[index].?;
157 try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);
158 self.registers[index] = null;
159 self.markRegFree(reg);
160 }
161 }
162
163 break :blk regs;
168164 };
169165 }
170166
167 /// Does not track the register.
168 pub fn allocRegWithoutTracking(self: *Self) !Register {
169 return (try self.allocRegsWithoutTracking(1))[0];
170 }
171
171172 /// Allocates the specified register with the specified
172173 /// instruction. Spills the register if it is currently
173174 /// allocated.
174 /// Before calling, must ensureCapacity + 1 on self.registers.
175175 pub fn getReg(self: *Self, reg: Register, inst: *ir.Inst) !void {
176 if (!isTracked(reg)) return;
176 const index = reg.allocIndex() orelse return;
177177
178178 if (!self.isRegFree(reg)) {
179179 // Move the instruction that was previously there to a
180180 // stack allocation.
181 const regs_entry = self.registers.getEntry(reg).?;
182 const spilled_inst = regs_entry.value;
183 regs_entry.value = inst;
181 const spilled_inst = self.registers[index].?;
182 self.registers[index] = inst;
184183 try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);
185184 } else {
186185 self.getRegAssumeFree(reg, inst);
......@@ -190,34 +189,33 @@ pub fn RegisterManager(
190189 /// Spills the register if it is currently allocated.
191190 /// Does not track the register.
192191 pub fn getRegWithoutTracking(self: *Self, reg: Register) !void {
193 if (!isTracked(reg)) return;
192 const index = reg.allocIndex() orelse return;
194193
195194 if (!self.isRegFree(reg)) {
196195 // Move the instruction that was previously there to a
197196 // stack allocation.
198 const regs_entry = self.registers.remove(reg).?;
199 const spilled_inst = regs_entry.value;
197 const spilled_inst = self.registers[index].?;
200198 try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);
201199 self.markRegFree(reg);
202200 }
203201 }
204202
205203 /// Allocates the specified register with the specified
206 /// instruction. Assumes that the register is free and no
204 /// instruction. Asserts that the register is free and no
207205 /// spilling is necessary.
208 /// Before calling, must ensureCapacity + 1 on self.registers.
209206 pub fn getRegAssumeFree(self: *Self, reg: Register, inst: *ir.Inst) void {
210 if (!isTracked(reg)) return;
207 const index = reg.allocIndex() orelse return;
211208
212 self.registers.putAssumeCapacityNoClobber(reg, inst);
209 assert(self.registers[index] == null);
210 self.registers[index] = inst;
213211 self.markRegUsed(reg);
214212 }
215213
216214 /// Marks the specified register as free
217215 pub fn freeReg(self: *Self, reg: Register) void {
218 if (!isTracked(reg)) return;
216 const index = reg.allocIndex() orelse return;
219217
220 _ = self.registers.remove(reg);
218 self.registers[index] = null;
221219 self.markRegFree(reg);
222220 }
223221 };
......@@ -247,7 +245,6 @@ const MockFunction = struct {
247245 const Self = @This();
248246
249247 pub fn deinit(self: *Self) void {
250 self.register_manager.deinit(self.allocator);
251248 self.spilled.deinit(self.allocator);
252249 }
253250
......@@ -273,7 +270,6 @@ test "tryAllocReg: no spilling" {
273270 std.testing.expect(!function.register_manager.isRegAllocated(.r2));
274271 std.testing.expect(!function.register_manager.isRegAllocated(.r3));
275272
276 try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2);
277273 std.testing.expectEqual(@as(?MockRegister, .r2), function.register_manager.tryAllocReg(&mock_instruction));
278274 std.testing.expectEqual(@as(?MockRegister, .r3), function.register_manager.tryAllocReg(&mock_instruction));
279275 std.testing.expectEqual(@as(?MockRegister, null), function.register_manager.tryAllocReg(&mock_instruction));
......@@ -305,7 +301,6 @@ test "allocReg: spilling" {
305301 std.testing.expect(!function.register_manager.isRegAllocated(.r2));
306302 std.testing.expect(!function.register_manager.isRegAllocated(.r3));
307303
308 try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2);
309304 std.testing.expectEqual(@as(?MockRegister, .r2), try function.register_manager.allocReg(&mock_instruction));
310305 std.testing.expectEqual(@as(?MockRegister, .r3), try function.register_manager.allocReg(&mock_instruction));
311306
......@@ -336,14 +331,12 @@ test "getReg" {
336331 std.testing.expect(!function.register_manager.isRegAllocated(.r2));
337332 std.testing.expect(!function.register_manager.isRegAllocated(.r3));
338333
339 try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2);
340334 try function.register_manager.getReg(.r3, &mock_instruction);
341335
342336 std.testing.expect(!function.register_manager.isRegAllocated(.r2));
343337 std.testing.expect(function.register_manager.isRegAllocated(.r3));
344338
345339 // Spill r3
346 try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2);
347340 try function.register_manager.getReg(.r3, &mock_instruction);
348341
349342 std.testing.expect(!function.register_manager.isRegAllocated(.r2));
src/stage1/all_types.hpp+1
......@@ -84,6 +84,7 @@ enum CallingConvention {
8484 CallingConventionAPCS,
8585 CallingConventionAAPCS,
8686 CallingConventionAAPCSVFP,
87 CallingConventionSysV
8788};
8889
8990// This one corresponds to the builtin.zig enum.
src/stage1/analyze.cpp+14-10
......@@ -974,6 +974,7 @@ const char *calling_convention_name(CallingConvention cc) {
974974 case CallingConventionAAPCS: return "AAPCS";
975975 case CallingConventionAAPCSVFP: return "AAPCSVFP";
976976 case CallingConventionInline: return "Inline";
977 case CallingConventionSysV: return "SysV";
977978 }
978979 zig_unreachable();
979980}
......@@ -995,6 +996,7 @@ bool calling_convention_allows_zig_types(CallingConvention cc) {
995996 case CallingConventionAPCS:
996997 case CallingConventionAAPCS:
997998 case CallingConventionAAPCSVFP:
999 case CallingConventionSysV:
9981000 return false;
9991001 }
10001002 zig_unreachable();
......@@ -1969,6 +1971,10 @@ Error emit_error_unless_callconv_allowed_for_target(CodeGen *g, AstNode *source_
19691971 case CallingConventionAAPCSVFP:
19701972 if (!target_is_arm(g->zig_target))
19711973 allowed_platforms = "ARM";
1974 break;
1975 case CallingConventionSysV:
1976 if (g->zig_target->arch != ZigLLVM_x86_64)
1977 allowed_platforms = "x86_64";
19721978 }
19731979 if (allowed_platforms != nullptr) {
19741980 add_node_error(g, source_node, buf_sprintf(
......@@ -3805,6 +3811,7 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
38053811 case CallingConventionAPCS:
38063812 case CallingConventionAAPCS:
38073813 case CallingConventionAAPCSVFP:
3814 case CallingConventionSysV:
38083815 add_fn_export(g, fn_table_entry, buf_ptr(&fn_table_entry->symbol_name),
38093816 GlobalLinkageIdStrong, fn_cc);
38103817 break;
......@@ -4769,11 +4776,11 @@ Error type_is_nonnull_ptr2(CodeGen *g, ZigType *type, bool *result) {
47694776 return ErrorNone;
47704777}
47714778
4772static uint32_t get_async_frame_align_bytes(CodeGen *g) {
4773 uint32_t a = g->pointer_size_bytes * 2;
4774 // promises have at least alignment 8 so that we can have 3 extra bits when doing atomicrmw
4775 if (a < 8) a = 8;
4776 return a;
4779uint32_t get_async_frame_align_bytes(CodeGen *g) {
4780 // Due to how the frame structure is built the minimum alignment is the one
4781 // of a usize (or pointer).
4782 // label (grep this): [fn_frame_struct_layout]
4783 return max(g->builtin_types.entry_usize->abi_align, target_fn_align(g->zig_target));
47774784}
47784785
47794786uint32_t get_ptr_align(CodeGen *g, ZigType *type) {
......@@ -4789,11 +4796,8 @@ uint32_t get_ptr_align(CodeGen *g, ZigType *type) {
47894796 return (ptr_type->data.pointer.explicit_alignment == 0) ?
47904797 get_abi_alignment(g, ptr_type->data.pointer.child_type) : ptr_type->data.pointer.explicit_alignment;
47914798 } else if (ptr_type->id == ZigTypeIdFn) {
4792 // I tried making this use LLVMABIAlignmentOfType but it trips this assertion in LLVM:
4793 // "Cannot getTypeInfo() on a type that is unsized!"
4794 // when getting the alignment of `?fn() callconv(.C) void`.
4795 // See http://lists.llvm.org/pipermail/llvm-dev/2018-September/126142.html
4796 return (ptr_type->data.fn.fn_type_id.alignment == 0) ? 1 : ptr_type->data.fn.fn_type_id.alignment;
4799 return (ptr_type->data.fn.fn_type_id.alignment == 0) ?
4800 target_fn_ptr_align(g->zig_target) : ptr_type->data.fn.fn_type_id.alignment;
47974801 } else if (ptr_type->id == ZigTypeIdAnyFrame) {
47984802 return get_async_frame_align_bytes(g);
47994803 } else {
src/stage1/analyze.hpp+1
......@@ -47,6 +47,7 @@ ZigType *get_test_fn_type(CodeGen *g);
4747ZigType *get_any_frame_type(CodeGen *g, ZigType *result_type);
4848bool handle_is_ptr(CodeGen *g, ZigType *type_entry);
4949Error emit_error_unless_callconv_allowed_for_target(CodeGen *g, AstNode *source_node, CallingConvention cc);
50uint32_t get_async_frame_align_bytes(CodeGen *g);
5051
5152bool type_has_bits(CodeGen *g, ZigType *type_entry);
5253Error type_has_bits2(CodeGen *g, ZigType *type_entry, bool *result);
src/stage1/codegen.cpp+5
......@@ -204,6 +204,9 @@ static ZigLLVM_CallingConv get_llvm_cc(CodeGen *g, CallingConvention cc) {
204204 case CallingConventionSignal:
205205 assert(g->zig_target->arch == ZigLLVM_avr);
206206 return ZigLLVM_AVR_SIGNAL;
207 case CallingConventionSysV:
208 assert(g->zig_target->arch == ZigLLVM_x86_64);
209 return ZigLLVM_X86_64_SysV;
207210 }
208211 zig_unreachable();
209212}
......@@ -348,6 +351,7 @@ static bool cc_want_sret_attr(CallingConvention cc) {
348351 case CallingConventionAPCS:
349352 case CallingConventionAAPCS:
350353 case CallingConventionAAPCSVFP:
354 case CallingConventionSysV:
351355 return true;
352356 case CallingConventionAsync:
353357 case CallingConventionUnspecified:
......@@ -9079,6 +9083,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
90799083 static_assert(CallingConventionAPCS == 11, "");
90809084 static_assert(CallingConventionAAPCS == 12, "");
90819085 static_assert(CallingConventionAAPCSVFP == 13, "");
9086 static_assert(CallingConventionSysV == 14, "");
90829087
90839088 static_assert(BuiltinPtrSizeOne == 0, "");
90849089 static_assert(BuiltinPtrSizeMany == 1, "");
src/stage1/ir.cpp+9-4
......@@ -19216,6 +19216,7 @@ static IrInstGen *ir_analyze_instruction_export(IrAnalyze *ira, IrInstSrcExport
1921619216 case CallingConventionAPCS:
1921719217 case CallingConventionAAPCS:
1921819218 case CallingConventionAAPCSVFP:
19219 case CallingConventionSysV:
1921919220 add_fn_export(ira->codegen, fn_entry, buf_ptr(symbol_name), global_linkage_id, cc);
1922019221 fn_entry->section_name = section_name;
1922119222 break;
......@@ -20659,8 +20660,12 @@ static IrInstGen *analyze_casted_new_stack(IrAnalyze *ira, IrInst* source_instr,
2065920660 get_fn_frame_type(ira->codegen, fn_entry), false);
2066020661 return ir_implicit_cast(ira, new_stack, needed_frame_type);
2066120662 } else {
20663 // XXX The stack alignment is hardcoded to 16 here and in
20664 // std.Target.stack_align.
20665 const uint32_t required_align = is_async_call_builtin ?
20666 get_async_frame_align_bytes(ira->codegen) : 16;
2066220667 ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8,
20663 false, false, PtrLenUnknown, target_fn_align(ira->codegen->zig_target), 0, 0, false);
20668 false, false, PtrLenUnknown, required_align, 0, 0, false);
2066420669 ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr);
2066520670 ira->codegen->need_frame_size_prefix_data = true;
2066620671 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
2607926084 fields[0]->special = ConstValSpecialStatic;
2608026085 fields[0]->type = get_builtin_type(ira->codegen, "CallingConvention");
2608126086 bigint_init_unsigned(&fields[0]->data.x_enum_tag, type_entry->data.fn.fn_type_id.cc);
26082 // alignment: u29
26087 // alignment: comptime_int
2608326088 ensure_field_index(result->type, "alignment", 1);
2608426089 fields[1]->special = ConstValSpecialStatic;
2608526090 fields[1]->type = ira->codegen->builtin_types.entry_num_lit_int;
26086 bigint_init_unsigned(&fields[1]->data.x_bigint, type_entry->data.fn.fn_type_id.alignment);
26091 bigint_init_unsigned(&fields[1]->data.x_bigint, get_ptr_align(ira->codegen, type_entry));
2608726092 // is_generic: bool
2608826093 ensure_field_index(result->type, "is_generic", 2);
2608926094 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
3009530100 fn_type_id.alignment = align_bytes;
3009630101 result_type = get_fn_type(ira->codegen, &fn_type_id);
3009730102 } else if (target_type->id == ZigTypeIdAnyFrame) {
30098 if (align_bytes >= target_fn_align(ira->codegen->zig_target)) {
30103 if (align_bytes >= get_async_frame_align_bytes(ira->codegen)) {
3009930104 result_type = target_type;
3010030105 } else {
3010130106 ir_add_error(ira, &target->base, buf_sprintf("sub-aligned anyframe not allowed"));
src/stage1/target.cpp+32-1
......@@ -1253,6 +1253,37 @@ bool target_is_ppc(const ZigTarget *target) {
12531253 target->arch == ZigLLVM_ppc64le;
12541254}
12551255
1256// Returns the minimum alignment for every function pointer on the given
1257// architecture.
1258unsigned target_fn_ptr_align(const ZigTarget *target) {
1259 // TODO This is a pessimization but is always correct.
1260 return 1;
1261}
1262
1263// Returns the minimum alignment for every function on the given architecture.
12561264unsigned target_fn_align(const ZigTarget *target) {
1257 return 16;
1265 switch (target->arch) {
1266 case ZigLLVM_riscv32:
1267 case ZigLLVM_riscv64:
1268 // TODO If the C extension is not present the value is 4.
1269 return 2;
1270 case ZigLLVM_ppc:
1271 case ZigLLVM_ppcle:
1272 case ZigLLVM_ppc64:
1273 case ZigLLVM_ppc64le:
1274 case ZigLLVM_aarch64:
1275 case ZigLLVM_aarch64_be:
1276 case ZigLLVM_aarch64_32:
1277 case ZigLLVM_sparc:
1278 case ZigLLVM_sparcel:
1279 case ZigLLVM_sparcv9:
1280 case ZigLLVM_mips:
1281 case ZigLLVM_mipsel:
1282 case ZigLLVM_mips64:
1283 case ZigLLVM_mips64el:
1284 return 4;
1285
1286 default:
1287 return 1;
1288 }
12581289}
src/stage1/target.hpp+1
......@@ -98,6 +98,7 @@ size_t target_libc_count(void);
9898void target_libc_enum(size_t index, ZigTarget *out_target);
9999bool target_libc_needs_crti_crtn(const ZigTarget *target);
100100
101unsigned target_fn_ptr_align(const ZigTarget *target);
101102unsigned target_fn_align(const ZigTarget *target);
102103
103104#endif
src/translate_c.zig+27-12
......@@ -8,6 +8,7 @@ const ctok = std.c.tokenizer;
88const CToken = std.c.Token;
99const mem = std.mem;
1010const math = std.math;
11const meta = std.meta;
1112const ast = @import("translate_c/ast.zig");
1213const Node = ast.Node;
1314const Tag = Node.Tag;
......@@ -1741,7 +1742,7 @@ fn transImplicitCastExpr(
17411742}
17421743
17431744fn isBuiltinDefined(name: []const u8) bool {
1744 inline for (std.meta.declarations(c_builtins)) |decl| {
1745 inline for (meta.declarations(c_builtins)) |decl| {
17451746 if (std.mem.eql(u8, name, decl.name)) return true;
17461747 }
17471748 return false;
......@@ -3157,7 +3158,7 @@ const ClangFunctionType = union(enum) {
31573158 NoProto: *const clang.FunctionType,
31583159
31593160 fn getReturnType(self: @This()) clang.QualType {
3160 switch (@as(std.meta.Tag(@This()), self)) {
3161 switch (@as(meta.Tag(@This()), self)) {
31613162 .Proto => return self.Proto.getReturnType(),
31623163 .NoProto => return self.NoProto.getReturnType(),
31633164 }
......@@ -3539,7 +3540,7 @@ fn transCPtrCast(
35393540 expr
35403541 else blk: {
35413542 const child_type_node = try transQualType(c, scope, child_type, loc);
3542 const alignof = try Tag.alignof.create(c.arena, child_type_node);
3543 const alignof = try Tag.std_meta_alignment.create(c.arena, child_type_node);
35433544 const align_cast = try Tag.align_cast.create(c.arena, .{ .lhs = alignof, .rhs = expr });
35443545 break :blk align_cast;
35453546 };
......@@ -3547,9 +3548,22 @@ fn transCPtrCast(
35473548 }
35483549}
35493550
3550fn transFloatingLiteral(c: *Context, scope: *Scope, stmt: *const clang.FloatingLiteral, used: ResultUsed) TransError!Node {
3551fn transFloatingLiteral(c: *Context, scope: *Scope, expr: *const clang.FloatingLiteral, used: ResultUsed) TransError!Node {
3552 switch (expr.getRawSemantics()) {
3553 .IEEEhalf, // f16
3554 .IEEEsingle, // f32
3555 .IEEEdouble, // f64
3556 => {},
3557 else => |format| return fail(
3558 c,
3559 error.UnsupportedTranslation,
3560 expr.getBeginLoc(),
3561 "unsupported floating point constant format {}",
3562 .{format},
3563 ),
3564 }
35513565 // TODO use something more accurate
3552 var dbl = stmt.getValueAsApproximateDouble();
3566 var dbl = expr.getValueAsApproximateDouble();
35533567 const is_negative = dbl < 0;
35543568 if (is_negative) dbl = -dbl;
35553569 const str = if (dbl == std.math.floor(dbl))
......@@ -4093,7 +4107,7 @@ fn transCreateNodeAPInt(c: *Context, int: *const clang.APSInt) !Node {
40934107}
40944108
40954109fn transCreateNodeNumber(c: *Context, num: anytype, num_kind: enum { int, float }) !Node {
4096 const fmt_s = if (comptime std.meta.trait.isNumber(@TypeOf(num))) "{d}" else "{s}";
4110 const fmt_s = if (comptime meta.trait.isNumber(@TypeOf(num))) "{d}" else "{s}";
40974111 const str = try std.fmt.allocPrint(c.arena, fmt_s, .{num});
40984112 if (num_kind == .float)
40994113 return Tag.float_literal.create(c.arena, str)
......@@ -4402,6 +4416,7 @@ fn transCC(
44024416 .X86ThisCall => return CallingConvention.Thiscall,
44034417 .AAPCS => return CallingConvention.AAPCS,
44044418 .AAPCS_VFP => return CallingConvention.AAPCSVFP,
4419 .X86_64SysV => return CallingConvention.SysV,
44054420 else => return fail(
44064421 c,
44074422 error.UnsupportedType,
......@@ -4848,12 +4863,12 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!Node {
48484863 // make the output less noisy by skipping promoteIntLiteral where
48494864 // it's guaranteed to not be required because of C standard type constraints
48504865 const guaranteed_to_fit = switch (suffix) {
4851 .none => if (math.cast(i16, value)) |_| true else |_| false,
4852 .u => if (math.cast(u16, value)) |_| true else |_| false,
4853 .l => if (math.cast(i32, value)) |_| true else |_| false,
4854 .lu => if (math.cast(u32, value)) |_| true else |_| false,
4855 .ll => if (math.cast(i64, value)) |_| true else |_| false,
4856 .llu => if (math.cast(u64, value)) |_| true else |_| false,
4866 .none => !meta.isError(math.cast(i16, value)),
4867 .u => !meta.isError(math.cast(u16, value)),
4868 .l => !meta.isError(math.cast(i32, value)),
4869 .lu => !meta.isError(math.cast(u32, value)),
4870 .ll => !meta.isError(math.cast(i64, value)),
4871 .llu => !meta.isError(math.cast(u64, value)),
48574872 .f => unreachable,
48584873 };
48594874
src/translate_c/ast.zig+11-1
......@@ -120,8 +120,11 @@ pub const Node = extern union {
120120 std_math_Log2Int,
121121 /// @intCast(lhs, rhs)
122122 int_cast,
123 /// @rem(lhs, rhs)
123 /// @import("std").meta.promoteIntLiteral(value, type, radix)
124124 std_meta_promoteIntLiteral,
125 /// @import("std").meta.alignment(value)
126 std_meta_alignment,
127 /// @rem(lhs, rhs)
125128 rem,
126129 /// @divTrunc(lhs, rhs)
127130 div_trunc,
......@@ -260,6 +263,7 @@ pub const Node = extern union {
260263 .switch_else,
261264 .block_single,
262265 .std_meta_sizeof,
266 .std_meta_alignment,
263267 .bool_to_int,
264268 .sizeof,
265269 .alignof,
......@@ -876,6 +880,11 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
876880 const import_node = try renderStdImport(c, "meta", "promoteIntLiteral");
877881 return renderCall(c, import_node, &.{ payload.type, payload.value, payload.radix });
878882 },
883 .std_meta_alignment => {
884 const payload = node.castTag(.std_meta_alignment).?.data;
885 const import_node = try renderStdImport(c, "meta", "alignment");
886 return renderCall(c, import_node, &.{payload});
887 },
879888 .std_meta_sizeof => {
880889 const payload = node.castTag(.std_meta_sizeof).?.data;
881890 const import_node = try renderStdImport(c, "meta", "sizeof");
......@@ -2144,6 +2153,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
21442153 .typeof,
21452154 .typeinfo,
21462155 .std_meta_sizeof,
2156 .std_meta_alignment,
21472157 .std_meta_cast,
21482158 .std_meta_promoteIntLiteral,
21492159 .std_meta_vector,
src/zig_clang.cpp+5
......@@ -2528,6 +2528,11 @@ double ZigClangFloatingLiteral_getValueAsApproximateDouble(const ZigClangFloatin
25282528 return casted->getValueAsApproximateDouble();
25292529}
25302530
2531ZigClangAPFloatBase_Semantics ZigClangFloatingLiteral_getRawSemantics(const ZigClangFloatingLiteral *self) {
2532 auto casted = reinterpret_cast<const clang::FloatingLiteral *>(self);
2533 return static_cast<ZigClangAPFloatBase_Semantics>(casted->getRawSemantics());
2534}
2535
25312536enum ZigClangStringLiteral_StringKind ZigClangStringLiteral_getKind(const struct ZigClangStringLiteral *self) {
25322537 auto casted = reinterpret_cast<const clang::StringLiteral *>(self);
25332538 return (ZigClangStringLiteral_StringKind)casted->getKind();
src/zig_clang.h+11
......@@ -881,6 +881,16 @@ enum ZigClangAPFloat_roundingMode {
881881 ZigClangAPFloat_roundingMode_Invalid = -1,
882882};
883883
884enum ZigClangAPFloatBase_Semantics {
885 ZigClangAPFloatBase_Semantics_IEEEhalf,
886 ZigClangAPFloatBase_Semantics_BFloat,
887 ZigClangAPFloatBase_Semantics_IEEEsingle,
888 ZigClangAPFloatBase_Semantics_IEEEdouble,
889 ZigClangAPFloatBase_Semantics_x87DoubleExtended,
890 ZigClangAPFloatBase_Semantics_IEEEquad,
891 ZigClangAPFloatBase_Semantics_PPCDoubleDouble,
892};
893
884894enum ZigClangStringLiteral_StringKind {
885895 ZigClangStringLiteral_StringKind_Ascii,
886896 ZigClangStringLiteral_StringKind_Wide,
......@@ -1142,6 +1152,7 @@ ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangDeclStmt_getBeginLoc(const st
11421152ZIG_EXTERN_C unsigned ZigClangAPFloat_convertToHexString(const struct ZigClangAPFloat *self, char *DST,
11431153 unsigned HexDigits, bool UpperCase, enum ZigClangAPFloat_roundingMode RM);
11441154ZIG_EXTERN_C double ZigClangFloatingLiteral_getValueAsApproximateDouble(const ZigClangFloatingLiteral *self);
1155ZIG_EXTERN_C ZigClangAPFloatBase_Semantics ZigClangFloatingLiteral_getRawSemantics(const ZigClangFloatingLiteral *self);
11451156
11461157ZIG_EXTERN_C enum ZigClangStringLiteral_StringKind ZigClangStringLiteral_getKind(const struct ZigClangStringLiteral *self);
11471158ZIG_EXTERN_C uint32_t ZigClangStringLiteral_getCodeUnit(const struct ZigClangStringLiteral *self, size_t i);
test/compile_errors.zig+3-1
......@@ -2136,7 +2136,9 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
21362136 \\}
21372137 \\fn func() callconv(.Async) void {}
21382138 , &[_][]const u8{
2139 "tmp.zig:4:21: error: expected type '[]align(16) u8', found '*[64]u8'",
2139 // Split the check in two as the alignment value is target dependent.
2140 "tmp.zig:4:21: error: expected type '[]align(",
2141 ") u8', found '*[64]u8'",
21402142 });
21412143
21422144 cases.add("atomic orderings of fence Acquire or stricter",
test/stage1/behavior/type_info.zig+1-1
......@@ -306,7 +306,7 @@ test "type info: function type info" {
306306fn testFunction() void {
307307 const fn_info = @typeInfo(@TypeOf(foo));
308308 expect(fn_info == .Fn);
309 expect(fn_info.Fn.alignment == 0);
309 expect(fn_info.Fn.alignment > 0);
310310 expect(fn_info.Fn.calling_convention == .C);
311311 expect(!fn_info.Fn.is_generic);
312312 expect(fn_info.Fn.args.len == 2);
test/stage2/riscv64.zig created+45
......@@ -0,0 +1,45 @@
1const std = @import("std");
2const TestContext = @import("../../src/test.zig").TestContext;
3
4const linux_riscv64 = std.zig.CrossTarget{
5 .cpu_arch = .riscv64,
6 .os_tag = .linux,
7};
8
9pub fn addCases(ctx: *TestContext) !void {
10 {
11 var case = ctx.exe("riscv64 hello world", linux_riscv64);
12 // Regular old hello world
13 case.addCompareOutput(
14 \\export fn _start() noreturn {
15 \\ print();
16 \\
17 \\ exit();
18 \\}
19 \\
20 \\fn print() void {
21 \\ asm volatile ("ecall"
22 \\ :
23 \\ : [number] "{a7}" (64),
24 \\ [arg1] "{a0}" (1),
25 \\ [arg2] "{a1}" (@ptrToInt("Hello, World!\n")),
26 \\ [arg3] "{a2}" ("Hello, World!\n".len)
27 \\ : "rcx", "r11", "memory"
28 \\ );
29 \\ return;
30 \\}
31 \\
32 \\fn exit() noreturn {
33 \\ asm volatile ("ecall"
34 \\ :
35 \\ : [number] "{a7}" (94),
36 \\ [arg1] "{a0}" (0)
37 \\ : "rcx", "r11", "memory"
38 \\ );
39 \\ unreachable;
40 \\}
41 ,
42 "Hello, World!\n",
43 );
44 }
45}
test/stage2/test.zig+1-41
......@@ -11,11 +11,6 @@ const linux_x64 = std.zig.CrossTarget{
1111 .os_tag = .linux,
1212};
1313
14const linux_riscv64 = std.zig.CrossTarget{
15 .cpu_arch = .riscv64,
16 .os_tag = .linux,
17};
18
1914pub fn addCases(ctx: *TestContext) !void {
2015 try @import("cbe.zig").addCases(ctx);
2116 try @import("spu-ii.zig").addCases(ctx);
......@@ -24,6 +19,7 @@ pub fn addCases(ctx: *TestContext) !void {
2419 try @import("llvm.zig").addCases(ctx);
2520 try @import("wasm.zig").addCases(ctx);
2621 try @import("darwin.zig").addCases(ctx);
22 try @import("riscv64.zig").addCases(ctx);
2723
2824 {
2925 var case = ctx.exe("hello world with updates", linux_x64);
......@@ -137,42 +133,6 @@ pub fn addCases(ctx: *TestContext) !void {
137133 );
138134 }
139135
140 {
141 var case = ctx.exe("riscv64 hello world", linux_riscv64);
142 // Regular old hello world
143 case.addCompareOutput(
144 \\export fn _start() noreturn {
145 \\ print();
146 \\
147 \\ exit();
148 \\}
149 \\
150 \\fn print() void {
151 \\ asm volatile ("ecall"
152 \\ :
153 \\ : [number] "{a7}" (64),
154 \\ [arg1] "{a0}" (1),
155 \\ [arg2] "{a1}" (@ptrToInt("Hello, World!\n")),
156 \\ [arg3] "{a2}" ("Hello, World!\n".len)
157 \\ : "rcx", "r11", "memory"
158 \\ );
159 \\ return;
160 \\}
161 \\
162 \\fn exit() noreturn {
163 \\ asm volatile ("ecall"
164 \\ :
165 \\ : [number] "{a7}" (94),
166 \\ [arg1] "{a0}" (0)
167 \\ : "rcx", "r11", "memory"
168 \\ );
169 \\ unreachable;
170 \\}
171 ,
172 "Hello, World!\n",
173 );
174 }
175
176136 {
177137 var case = ctx.exe("adding numbers at comptime", linux_x64);
178138 case.addCompareOutput(
test/translate_c.zig+9-11
......@@ -1363,7 +1363,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
13631363 , &[_][]const u8{
13641364 \\pub export fn ptrcast() [*c]f32 {
13651365 \\ var a: [*c]c_int = undefined;
1366 \\ return @ptrCast([*c]f32, @alignCast(@alignOf(f32), a));
1366 \\ return @ptrCast([*c]f32, @alignCast(@import("std").meta.alignment(f32), a));
13671367 \\}
13681368 });
13691369
......@@ -1387,16 +1387,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
13871387 \\pub export fn test_ptr_cast() void {
13881388 \\ var p: ?*c_void = undefined;
13891389 \\ {
1390 \\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@alignOf(u8), p));
1391 \\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@alignOf(c_short), p));
1392 \\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@alignOf(c_int), p));
1393 \\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@alignOf(c_longlong), p));
1390 \\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@import("std").meta.alignment(u8), p));
1391 \\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@import("std").meta.alignment(c_short), p));
1392 \\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@import("std").meta.alignment(c_int), p));
1393 \\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@import("std").meta.alignment(c_longlong), p));
13941394 \\ }
13951395 \\ {
1396 \\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@alignOf(u8), p));
1397 \\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@alignOf(c_short), p));
1398 \\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@alignOf(c_int), p));
1399 \\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@alignOf(c_longlong), p));
1396 \\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@import("std").meta.alignment(u8), p));
1397 \\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@import("std").meta.alignment(c_short), p));
1398 \\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@import("std").meta.alignment(c_int), p));
1399 \\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@import("std").meta.alignment(c_longlong), p));
14001400 \\ }
14011401 \\}
14021402 });
......@@ -3028,7 +3028,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
30283028 \\void call() {
30293029 \\ fn_int(3.0f);
30303030 \\ fn_int(3.0);
3031 \\ fn_int(3.0L);
30323031 \\ fn_int('ABCD');
30333032 \\ fn_f32(3);
30343033 \\ fn_f64(3);
......@@ -3053,7 +3052,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
30533052 \\pub export fn call() void {
30543053 \\ fn_int(@floatToInt(c_int, 3.0));
30553054 \\ fn_int(@floatToInt(c_int, 3.0));
3056 \\ fn_int(@floatToInt(c_int, 3.0));
30573055 \\ fn_int(@as(c_int, 1094861636));
30583056 \\ fn_f32(@intToFloat(f32, @as(c_int, 3)));
30593057 \\ fn_f64(@intToFloat(f64, @as(c_int, 3)));