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 {...@@ -44,6 +44,7 @@ pub fn build(b: *Builder) !void {
4444
45 const fmt_build_zig = b.addFmt(&[_][]const u8{"build.zig"});45 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;
47 const skip_release = b.option(bool, "skip-release", "Main test suite skips release builds") orelse false;48 const skip_release = b.option(bool, "skip-release", "Main test suite skips release builds") orelse false;
48 const skip_release_small = b.option(bool, "skip-release-small", "Main test suite skips release-small builds") orelse skip_release;49 const skip_release_small = b.option(bool, "skip-release-small", "Main test suite skips release-small builds") orelse skip_release;
49 const skip_release_fast = b.option(bool, "skip-release-fast", "Main test suite skips release-fast builds") orelse skip_release;50 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 {...@@ -240,8 +241,10 @@ pub fn build(b: *Builder) !void {
240241
241 var chosen_modes: [4]builtin.Mode = undefined;242 var chosen_modes: [4]builtin.Mode = undefined;
242 var chosen_mode_index: usize = 0;243 var chosen_mode_index: usize = 0;
243 chosen_modes[chosen_mode_index] = builtin.Mode.Debug;244 if (!skip_debug) {
244 chosen_mode_index += 1;245 chosen_modes[chosen_mode_index] = builtin.Mode.Debug;
246 chosen_mode_index += 1;
247 }
245 if (!skip_release_safe) {248 if (!skip_release_safe) {
246 chosen_modes[chosen_mode_index] = builtin.Mode.ReleaseSafe;249 chosen_modes[chosen_mode_index] = builtin.Mode.ReleaseSafe;
247 chosen_mode_index += 1;250 chosen_mode_index += 1;
ci/drone/drone.yml+33-2
...@@ -6,7 +6,38 @@ platform:...@@ -6,7 +6,38 @@ platform:
6 arch: arm646 arch: arm64
77
8steps:8steps:
9- name: build-and-test9- 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
10 image: ziglang/static-base:llvm12-aarch64-141 image: ziglang/static-base:llvm12-aarch64-1
11 environment:42 environment:
12 SRHT_OAUTH_TOKEN:43 SRHT_OAUTH_TOKEN:
...@@ -16,4 +47,4 @@ steps:...@@ -16,4 +47,4 @@ steps:
16 AWS_SECRET_ACCESS_KEY:47 AWS_SECRET_ACCESS_KEY:
17 from_secret: AWS_SECRET_ACCESS_KEY48 from_secret: AWS_SECRET_ACCESS_KEY
18 commands:49 commands:
19 - ./ci/drone/linux_script50 - ./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...@@ -199,7 +199,8 @@ pub fn spawn(comptime startFn: anytype, context: SpawnContextType(@TypeOf(startF
199 inner: Context,199 inner: Context,
200 };200 };
201 fn threadMain(raw_arg: windows.LPVOID) callconv(.C) windows.DWORD {201 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
204 switch (@typeInfo(@typeInfo(@TypeOf(startFn)).Fn.return_type.?)) {205 switch (@typeInfo(@typeInfo(@TypeOf(startFn)).Fn.return_type.?)) {
205 .NoReturn => {206 .NoReturn => {
...@@ -260,7 +261,8 @@ pub fn spawn(comptime startFn: anytype, context: SpawnContextType(@TypeOf(startF...@@ -260,7 +261,8 @@ pub fn spawn(comptime startFn: anytype, context: SpawnContextType(@TypeOf(startF
260261
261 const MainFuncs = struct {262 const MainFuncs = struct {
262 fn linuxThreadMain(ctx_addr: usize) callconv(.C) u8 {263 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
265 switch (@typeInfo(@typeInfo(@TypeOf(startFn)).Fn.return_type.?)) {267 switch (@typeInfo(@typeInfo(@TypeOf(startFn)).Fn.return_type.?)) {
266 .NoReturn => {268 .NoReturn => {
...@@ -292,7 +294,8 @@ pub fn spawn(comptime startFn: anytype, context: SpawnContextType(@TypeOf(startF...@@ -292,7 +294,8 @@ pub fn spawn(comptime startFn: anytype, context: SpawnContextType(@TypeOf(startF
292 }294 }
293 }295 }
294 fn posixThreadMain(ctx: ?*c_void) callconv(.C) ?*c_void {296 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
297 switch (@typeInfo(@typeInfo(@TypeOf(startFn)).Fn.return_type.?)) {300 switch (@typeInfo(@typeInfo(@TypeOf(startFn)).Fn.return_type.?)) {
298 .NoReturn => {301 .NoReturn => {
lib/std/builtin.zig+1
...@@ -166,6 +166,7 @@ pub const CallingConvention = enum {...@@ -166,6 +166,7 @@ pub const CallingConvention = enum {
166 APCS,166 APCS,
167 AAPCS,167 AAPCS,
168 AAPCSVFP,168 AAPCSVFP,
169 SysV
169};170};
170171
171/// This data structure is used by the Zig language code generation and172/// 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 {...@@ -69,3 +69,51 @@ pub const pthread_rwlock_t = extern struct {
69 writer_count: i32 = 0,69 writer_count: i32 = 0,
70 waiters: [2]?*c_void = [_]?*c_void{ null, null },70 waiters: [2]?*c_void = [_]?*c_void{ null, null },
71};71};
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 {...@@ -264,7 +264,7 @@ pub const ChildProcess = struct {
264264
265 // TODO collect output in a deadlock-avoiding way on Windows.265 // TODO collect output in a deadlock-avoiding way on Windows.
266 // https://github.com/ziglang/zig/issues/6343266 // https://github.com/ziglang/zig/issues/6343
267 if (builtin.os.tag == .windows) {267 if (builtin.os.tag == .windows or builtin.os.tag == .haiku) {
268 const stdout_in = child.stdout.?.reader();268 const stdout_in = child.stdout.?.reader();
269 const stderr_in = child.stderr.?.reader();269 const stderr_in = child.stderr.?.reader();
270270
lib/std/crypto/25519/edwards25519.zig+9-16
...@@ -75,16 +75,8 @@ pub const Edwards25519 = struct {...@@ -75,16 +75,8 @@ pub const Edwards25519 = struct {
75 .is_base = true,75 .is_base = true,
76 };76 };
7777
78 /// The edwards25519 neutral element.78 pub const neutralElement = @compileError("deprecated: use identityElement instead");
79 pub const neutralElement = Edwards25519{79 pub const identityElement = Edwards25519{ .x = Fe.zero, .y = Fe.one, .z = Fe.one, .t = Fe.zero };
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 };
8880
89 /// Reject the neutral element.81 /// Reject the neutral element.
90 pub fn rejectIdentity(p: Edwards25519) IdentityElementError!void {82 pub fn rejectIdentity(p: Edwards25519) IdentityElementError!void {
...@@ -160,9 +152,10 @@ pub const Edwards25519 = struct {...@@ -160,9 +152,10 @@ pub const Edwards25519 = struct {
160 return t;152 return t;
161 }153 }
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);
164 var e: [2 * 32]i8 = undefined;157 var e: [2 * 32]i8 = undefined;
165 for (s) |x, i| {158 for (reduced) |x, i| {
166 e[i * 2 + 0] = @as(i8, @truncate(u4, x));159 e[i * 2 + 0] = @as(i8, @truncate(u4, x));
167 e[i * 2 + 1] = @as(i8, @truncate(u4, x >> 4));160 e[i * 2 + 1] = @as(i8, @truncate(u4, x >> 4));
168 }161 }
...@@ -185,7 +178,7 @@ pub const Edwards25519 = struct {...@@ -185,7 +178,7 @@ pub const Edwards25519 = struct {
185 // avoid these to keep the standard library lightweight.178 // avoid these to keep the standard library lightweight.
186 fn pcMul(pc: [9]Edwards25519, s: [32]u8, comptime vartime: bool) IdentityElementError!Edwards25519 {179 fn pcMul(pc: [9]Edwards25519, s: [32]u8, comptime vartime: bool) IdentityElementError!Edwards25519 {
187 std.debug.assert(vartime);180 std.debug.assert(vartime);
188 const e = nonAdjacentForm(s);181 const e = slide(s);
189 var q = Edwards25519.identityElement;182 var q = Edwards25519.identityElement;
190 var pos: usize = 2 * 32 - 1;183 var pos: usize = 2 * 32 - 1;
191 while (true) : (pos -= 1) {184 while (true) : (pos -= 1) {
...@@ -280,8 +273,8 @@ pub const Edwards25519 = struct {...@@ -280,8 +273,8 @@ pub const Edwards25519 = struct {
280 xpc[4].rejectIdentity() catch return error.WeakPublicKey;273 xpc[4].rejectIdentity() catch return error.WeakPublicKey;
281 break :pc xpc;274 break :pc xpc;
282 };275 };
283 const e1 = nonAdjacentForm(s1);276 const e1 = slide(s1);
284 const e2 = nonAdjacentForm(s2);277 const e2 = slide(s2);
285 var q = Edwards25519.identityElement;278 var q = Edwards25519.identityElement;
286 var pos: usize = 2 * 32 - 1;279 var pos: usize = 2 * 32 - 1;
287 while (true) : (pos -= 1) {280 while (true) : (pos -= 1) {
...@@ -318,7 +311,7 @@ pub const Edwards25519 = struct {...@@ -318,7 +311,7 @@ pub const Edwards25519 = struct {
318 }311 }
319 var es: [count][2 * 32]i8 = undefined;312 var es: [count][2 * 32]i8 = undefined;
320 for (ss) |s, i| {313 for (ss) |s, i| {
321 es[i] = nonAdjacentForm(s);314 es[i] = slide(s);
322 }315 }
323 var q = Edwards25519.identityElement;316 var q = Edwards25519.identityElement;
324 var pos: usize = 2 * 32 - 1;317 var pos: usize = 2 * 32 - 1;
lib/std/crypto/25519/field.zig+1-1
...@@ -355,7 +355,7 @@ pub const Fe = struct {...@@ -355,7 +355,7 @@ pub const Fe = struct {
355 return fe;355 return fe;
356 }356 }
357357
358 /// Compute the inverse of a field element358 /// Return the inverse of a field element, or 0 if a=0.
359 pub fn invert(a: Fe) Fe {359 pub fn invert(a: Fe) Fe {
360 var t0 = a.sq();360 var t0 = a.sq();
361 var t1 = t0.sqn(2).mul(a);361 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 {...@@ -98,7 +98,7 @@ pub fn sub(a: [32]u8, b: [32]u8) [32]u8 {
98 return add(a, neg(b));98 return add(a, neg(b));
99}99}
100100
101/// A scalar in unpacked reprentation101/// A scalar in unpacked representation
102pub const Scalar = struct {102pub const Scalar = struct {
103 const Limbs = [5]u64;103 const Limbs = [5]u64;
104 limbs: Limbs = undefined,104 limbs: Limbs = undefined,
lib/std/crypto/utils.zig+52
...@@ -1,7 +1,11 @@...@@ -1,7 +1,11 @@
1const std = @import("../std.zig");1const std = @import("../std.zig");
2const debug = std.debug;
2const mem = std.mem;3const mem = std.mem;
3const testing = std.testing;4const testing = std.testing;
45
6const Endian = std.builtin.Endian;
7const Order = std.math.Order;
8
5/// Compares two arrays in constant time (for a given length) and returns whether they are equal.9/// Compares two arrays in constant time (for a given length) and returns whether they are equal.
6/// This function was designed to compare short cryptographic secrets (MACs, signatures).10/// This function was designed to compare short cryptographic secrets (MACs, signatures).
7/// For all other applications, use mem.eql() instead.11/// For all other applications, use mem.eql() instead.
...@@ -38,6 +42,41 @@ pub fn timingSafeEql(comptime T: type, a: T, b: T) bool {...@@ -38,6 +42,41 @@ pub fn timingSafeEql(comptime T: type, a: T, b: T) bool {
38 }42 }
39}43}
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
41/// Sets a slice to zeroes.80/// Sets a slice to zeroes.
42/// Prevents the store from being optimized out.81/// Prevents the store from being optimized out.
43pub fn secureZero(comptime T: type, s: []T) void {82pub fn secureZero(comptime T: type, s: []T) void {
...@@ -70,6 +109,19 @@ test "crypto.utils.timingSafeEql (vectors)" {...@@ -70,6 +109,19 @@ test "crypto.utils.timingSafeEql (vectors)" {
70 testing.expect(timingSafeEql(std.meta.Vector(100, u8), v1, v3));109 testing.expect(timingSafeEql(std.meta.Vector(100, u8), v1, v3));
71}110}
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
73test "crypto.utils.secureZero" {125test "crypto.utils.secureZero" {
74 var a = [_]u8{0xfe} ** 8;126 var a = [_]u8{0xfe} ** 8;
75 var b = [_]u8{0xfe} ** 8;127 var b = [_]u8{0xfe} ** 8;
lib/std/debug.zig+11-4
...@@ -339,6 +339,13 @@ pub const StackIterator = struct {...@@ -339,6 +339,13 @@ pub const StackIterator = struct {
339 fp: usize,339 fp: usize,
340340
341 pub fn init(first_address: ?usize, fp: ?usize) StackIterator {341 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
342 return StackIterator{349 return StackIterator{
343 .first_address = first_address,350 .first_address = first_address,
344 .fp = fp orelse @frameAddress(),351 .fp = fp orelse @frameAddress(),
...@@ -346,18 +353,18 @@ pub const StackIterator = struct {...@@ -346,18 +353,18 @@ pub const StackIterator = struct {
346 }353 }
347354
348 // Offset of the saved BP wrt the frame pointer.355 // 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())
350 // On RISC-V the frame pointer points to the top of the saved register357 // On RISC-V the frame pointer points to the top of the saved register
351 // area, on pretty much every other architecture it points to the stack358 // area, on pretty much every other architecture it points to the stack
352 // slot where the previous frame pointer is saved.359 // slot where the previous frame pointer is saved.
353 2 * @sizeOf(usize)360 2 * @sizeOf(usize)
354 else if (native_arch.isSPARC())361 else if (comptime native_arch.isSPARC())
355 // On SPARC the previous frame pointer is stored at 14 slots past %fp+BIAS.362 // On SPARC the previous frame pointer is stored at 14 slots past %fp+BIAS.
356 14 * @sizeOf(usize)363 14 * @sizeOf(usize)
357 else364 else
358 0;365 0;
359366
360 const fp_bias = if (native_arch.isSPARC())367 const fp_bias = if (comptime native_arch.isSPARC())
361 // On SPARC frame pointers are biased by a constant.368 // On SPARC frame pointers are biased by a constant.
362 2047369 2047
363 else370 else
...@@ -383,7 +390,7 @@ pub const StackIterator = struct {...@@ -383,7 +390,7 @@ pub const StackIterator = struct {
383 }390 }
384391
385 fn next_internal(self: *StackIterator) ?usize {392 fn next_internal(self: *StackIterator) ?usize {
386 const fp = if (native_arch.isSPARC())393 const fp = if (comptime native_arch.isSPARC())
387 // On SPARC the offset is positive. (!)394 // On SPARC the offset is positive. (!)
388 math.add(usize, self.fp, fp_offset) catch return null395 math.add(usize, self.fp, fp_offset) catch return null
389 else396 else
lib/std/fmt.zig+3-1
...@@ -1523,9 +1523,11 @@ test "parseUnsigned" {...@@ -1523,9 +1523,11 @@ test "parseUnsigned" {
1523}1523}
15241524
1525pub const parseFloat = @import("fmt/parse_float.zig").parseFloat;1525pub const parseFloat = @import("fmt/parse_float.zig").parseFloat;
1526pub const parseHexFloat = @import("fmt/parse_hex_float.zig").parseHexFloat;
15261527
1527test "parseFloat" {1528test {
1528 _ = @import("fmt/parse_float.zig");1529 _ = @import("fmt/parse_float.zig");
1530 _ = @import("fmt/parse_hex_float.zig");
1529}1531}
15301532
1531pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) {1533pub 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 {...@@ -29,8 +29,7 @@ fn ok(s: []const u8) !void {
29fn err(s: []const u8) void {29fn err(s: []const u8) void {
30 testing.expect(!json.validate(s));30 testing.expect(!json.validate(s));
3131
32 testNonStreaming(s) catch return;32 testing.expect(std.meta.isError(testNonStreaming(s)));
33 testing.expect(false);
34}33}
3534
36fn utf8Error(s: []const u8) void {35fn utf8Error(s: []const u8) void {
...@@ -48,8 +47,7 @@ fn any(s: []const u8) void {...@@ -48,8 +47,7 @@ fn any(s: []const u8) void {
48fn anyStreamingErrNonStreaming(s: []const u8) void {47fn anyStreamingErrNonStreaming(s: []const u8) void {
49 _ = json.validate(s);48 _ = json.validate(s);
5049
51 testNonStreaming(s) catch return;50 testing.expect(std.meta.isError(testNonStreaming(s)));
52 testing.expect(false);
53}51}
5452
55fn roundTrip(s: []const u8) !void {53fn roundTrip(s: []const u8) !void {
lib/std/mem.zig+40-4
...@@ -1877,7 +1877,11 @@ test "rotate" {...@@ -1877,7 +1877,11 @@ test "rotate" {
18771877
1878/// Replace needle with replacement as many times as possible, writing to an output buffer which is assumed to be of1878/// Replace needle with replacement as many times as possible, writing to an output buffer which is assumed to be of
1879/// appropriate size. Use replacementSize to calculate an appropriate buffer size.1879/// appropriate size. Use replacementSize to calculate an appropriate buffer size.
1880/// The needle must not be empty.
1880pub fn replace(comptime T: type, input: []const T, needle: []const T, replacement: []const T, output: []T) usize {1881pub 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
1881 var i: usize = 0;1885 var i: usize = 0;
1882 var slide: usize = 0;1886 var slide: usize = 0;
1883 var replacements: usize = 0;1887 var replacements: usize = 0;
...@@ -1900,22 +1904,48 @@ pub fn replace(comptime T: type, input: []const T, needle: []const T, replacemen...@@ -1900,22 +1904,48 @@ pub fn replace(comptime T: type, input: []const T, needle: []const T, replacemen
1900test "replace" {1904test "replace" {
1901 var output: [29]u8 = undefined;1905 var output: [29]u8 = undefined;
1902 var replacements = replace(u8, "All your base are belong to us", "base", "Zig", output[0..]);1906 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";
1903 testing.expect(replacements == 1);1908 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
1906 replacements = replace(u8, "Favor reading code over writing code.", "code", "", output[0..]);1911 replacements = replace(u8, "Favor reading code over writing code.", "code", "", output[0..]);
1912 expected = "Favor reading over writing .";
1907 testing.expect(replacements == 2);1913 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]);
1909}1933}
19101934
1911/// Calculate the size needed in an output buffer to perform a replacement.1935/// Calculate the size needed in an output buffer to perform a replacement.
1936/// The needle must not be empty.
1912pub fn replacementSize(comptime T: type, input: []const T, needle: []const T, replacement: []const T) usize {1937pub 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
1913 var i: usize = 0;1941 var i: usize = 0;
1914 var size: usize = input.len;1942 var size: usize = input.len;
1915 while (i < input.len) : (i += 1) {1943 while (i < input.len) {
1916 if (mem.indexOf(T, input[i..], needle) == @as(usize, 0)) {1944 if (mem.indexOf(T, input[i..], needle) == @as(usize, 0)) {
1917 size = size - needle.len + replacement.len;1945 size = size - needle.len + replacement.len;
1918 i += needle.len;1946 i += needle.len;
1947 } else {
1948 i += 1;
1919 }1949 }
1920 }1950 }
19211951
...@@ -1924,9 +1954,15 @@ pub fn replacementSize(comptime T: type, input: []const T, needle: []const T, re...@@ -1924,9 +1954,15 @@ pub fn replacementSize(comptime T: type, input: []const T, needle: []const T, re
19241954
1925test "replacementSize" {1955test "replacementSize" {
1926 testing.expect(replacementSize(u8, "All your base are belong to us", "base", "Zig") == 29);1956 testing.expect(replacementSize(u8, "All your base are belong to us", "base", "Zig") == 29);
1927 testing.expect(replacementSize(u8, "", "", "") == 0);
1928 testing.expect(replacementSize(u8, "Favor reading code over writing code.", "code", "") == 29);1957 testing.expect(replacementSize(u8, "Favor reading code over writing code.", "code", "") == 29);
1929 testing.expect(replacementSize(u8, "Only one obvious way to do things.", "things.", "things in Zig.") == 41);1958 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);
1930}1966}
19311967
1932/// Perform a replacement on an allocated buffer of pre-determined size. Caller must free returned memory.1968/// 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" {...@@ -117,10 +117,21 @@ test "std.meta.bitCount" {
117 testing.expect(bitCount(f32) == 32);117 testing.expect(bitCount(f32) == 32);
118}118}
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.
120pub fn alignment(comptime T: type) comptime_int {125pub fn alignment(comptime T: type) comptime_int {
121 //@alignOf works on non-pointer types126 return switch (@typeInfo(T)) {
122 const P = if (comptime trait.is(.Pointer)(T)) T else *T;127 .Optional => |info| switch (@typeInfo(info.child)) {
123 return @typeInfo(P).Pointer.alignment;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 };
124}135}
125136
126test "std.meta.alignment" {137test "std.meta.alignment" {
...@@ -129,6 +140,8 @@ test "std.meta.alignment" {...@@ -129,6 +140,8 @@ test "std.meta.alignment" {
129 testing.expect(alignment(*align(2) u8) == 2);140 testing.expect(alignment(*align(2) u8) == 2);
130 testing.expect(alignment([]align(1) u8) == 1);141 testing.expect(alignment([]align(1) u8) == 1);
131 testing.expect(alignment([]align(2) u8) == 2);142 testing.expect(alignment([]align(2) u8) == 2);
143 testing.expect(alignment(fn () void) > 0);
144 testing.expect(alignment(fn () align(128) void) == 128);
132}145}
133146
134pub fn Child(comptime T: type) type {147pub fn Child(comptime T: type) type {
...@@ -1342,3 +1355,13 @@ test "shuffleVectorIndex" {...@@ -1342,3 +1355,13 @@ test "shuffleVectorIndex" {
1342 testing.expect(shuffleVectorIndex(6, vector_len) == -3);1355 testing.expect(shuffleVectorIndex(6, vector_len) == -3);
1343 testing.expect(shuffleVectorIndex(7, vector_len) == -4);1356 testing.expect(shuffleVectorIndex(7, vector_len) == -4);
1344}1357}
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;...@@ -279,20 +279,10 @@ pub const PROT_READ = 1;
279pub const PROT_WRITE = 2;279pub const PROT_WRITE = 2;
280pub const PROT_EXEC = 4;280pub const PROT_EXEC = 4;
281281
282pub const CLOCK_REALTIME = 0;282pub const CLOCK_MONOTONIC = 0;
283pub const CLOCK_VIRTUAL = 1;283pub const CLOCK_REALTIME = -1;
284pub const CLOCK_PROF = 2;284pub const CLOCK_PROCESS_CPUTIME_ID = -2;
285pub const CLOCK_MONOTONIC = 4;285pub const CLOCK_THREAD_CPUTIME_ID = -3;
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;
296286
297pub const MAP_FAILED = @intToPtr(*c_void, maxInt(usize));287pub const MAP_FAILED = @intToPtr(*c_void, maxInt(usize));
298pub const MAP_SHARED = 0x0001;288pub const MAP_SHARED = 0x0001;
...@@ -310,58 +300,59 @@ pub const MAP_NOCORE = 0x00020000;...@@ -310,58 +300,59 @@ pub const MAP_NOCORE = 0x00020000;
310pub const MAP_PREFAULT_READ = 0x00040000;300pub const MAP_PREFAULT_READ = 0x00040000;
311pub const MAP_32BIT = 0x00080000;301pub const MAP_32BIT = 0x00080000;
312302
313pub const WNOHANG = 1;303pub const WNOHANG = 0x1;
314pub const WUNTRACED = 2;304pub const WUNTRACED = 0x2;
315pub const WSTOPPED = WUNTRACED;305pub const WSTOPPED = 0x10;
316pub const WCONTINUED = 4;306pub const WCONTINUED = 0x4;
317pub const WNOWAIT = 8;307pub const WNOWAIT = 0x20;
318pub const WEXITED = 16;308pub const WEXITED = 0x08;
319pub const WTRAPPED = 32;309
320310pub const SA_ONSTACK = 0x20;
321pub const SA_ONSTACK = 0x0001;311pub const SA_RESTART = 0x10;
322pub const SA_RESTART = 0x0002;312pub const SA_RESETHAND = 0x04;
323pub const SA_RESETHAND = 0x0004;313pub const SA_NOCLDSTOP = 0x01;
324pub const SA_NOCLDSTOP = 0x0008;314pub const SA_NODEFER = 0x08;
325pub const SA_NODEFER = 0x0010;315pub const SA_NOCLDWAIT = 0x02;
326pub const SA_NOCLDWAIT = 0x0020;316pub const SA_SIGINFO = 0x40;
327pub const SA_SIGINFO = 0x0040;317pub const SA_NOMASK = SA_NODEFER;
318pub const SA_STACK = SA_ONSTACK;
319pub const SA_ONESHOT = SA_RESETHAND;
328320
329pub const SIGHUP = 1;321pub const SIGHUP = 1;
330pub const SIGINT = 2;322pub const SIGINT = 2;
331pub const SIGQUIT = 3;323pub const SIGQUIT = 3;
332pub const SIGILL = 4;324pub const SIGILL = 4;
333pub const SIGTRAP = 5;325pub const SIGCHLD = 5;
334pub const SIGABRT = 6;326pub const SIGABRT = 6;
335pub const SIGIOT = SIGABRT;327pub const SIGIOT = SIGABRT;
336pub const SIGEMT = 7;328pub const SIGPIPE = 7;
337pub const SIGFPE = 8;329pub const SIGFPE = 8;
338pub const SIGKILL = 9;330pub const SIGKILL = 9;
339pub const SIGBUS = 10;331pub const SIGSTOP = 10;
340pub const SIGSEGV = 11;332pub const SIGSEGV = 11;
341pub const SIGSYS = 12;333pub const SIGCONT = 12;
342pub const SIGPIPE = 13;334pub const SIGTSTP = 13;
343pub const SIGALRM = 14;335pub const SIGALRM = 14;
344pub const SIGTERM = 15;336pub const SIGTERM = 15;
345pub const SIGURG = 16;337pub const SIGTTIN = 16;
346pub const SIGSTOP = 17;338pub const SIGTTOU = 17;
347pub const SIGTSTP = 18;339pub const SIGUSR1 = 18;
348pub const SIGCONT = 19;340pub const SIGUSR2 = 19;
349pub const SIGCHLD = 20;341pub const SIGWINCH = 20;
350pub const SIGTTIN = 21;342pub const SIGKILLTHR = 21;
351pub const SIGTTOU = 22;343pub const SIGTRAP = 22;
352pub const SIGIO = 23;344pub const SIGPOLL = 23;
353pub const SIGXCPU = 24;345pub const SIGPROF = 24;
354pub const SIGXFSZ = 25;346pub const SIGSYS = 25;
355pub const SIGVTALRM = 26;347pub const SIGURG = 26;
356pub const SIGPROF = 27;348pub const SIGVTALRM = 27;
357pub const SIGWINCH = 28;349pub const SIGXCPU = 28;
358pub const SIGINFO = 29;350pub const SIGXFSZ = 29;
359pub const SIGUSR1 = 30;351pub const SIGBUS = 30;
360pub const SIGUSR2 = 31;352pub const SIGRESERVED1 = 31;
361pub const SIGTHR = 32;353pub const SIGRESERVED2 = 32;
362pub const SIGLWP = SIGTHR;354
363pub const SIGLIBRT = 33;355// TODO: check
364
365pub const SIGRTMIN = 65;356pub const SIGRTMIN = 65;
366pub const SIGRTMAX = 126;357pub const SIGRTMAX = 126;
367358
...@@ -645,135 +636,51 @@ pub const EVFILT_SENDFILE = -12;...@@ -645,135 +636,51 @@ pub const EVFILT_SENDFILE = -12;
645636
646pub const EVFILT_EMPTY = -13;637pub const EVFILT_EMPTY = -13;
647638
648/// On input, NOTE_TRIGGER causes the event to be triggered for output.639pub const TCGETA = 0x8000;
649pub const NOTE_TRIGGER = 0x01000000;640pub const TCSETA = 0x8001;
650641pub const TCSETAW = 0x8004;
651/// ignore input fflags642pub const TCSETAF = 0x8003;
652pub const NOTE_FFNOP = 0x00000000;643pub const TCSBRK = 08005;
653644pub const TCXONC = 0x8007;
654/// and fflags645pub const TCFLSH = 0x8006;
655pub const NOTE_FFAND = 0x40000000;646
656647pub const TIOCSCTTY = 0x8017;
657/// or fflags648pub const TIOCGPGRP = 0x8015;
658pub const NOTE_FFOR = 0x80000000;649pub const TIOCSPGRP = 0x8016;
659650pub const TIOCGWINSZ = 0x8012;
660/// copy fflags651pub const TIOCSWINSZ = 0x8013;
661pub const NOTE_FFCOPY = 0xc0000000;652pub const TIOCMGET = 0x8018;
662653pub const TIOCMBIS = 0x8022;
663/// mask for operations654pub const TIOCMBIC = 0x8023;
664pub const NOTE_FFCTRLMASK = 0xc0000000;655pub const TIOCMSET = 0x8019;
665pub const NOTE_FFLAGSMASK = 0x00ffffff;656pub const FIONREAD = 0xbe000001;
666657pub const FIONBIO = 0xbe000000;
667/// low water mark658pub const TIOCSBRK = 0x8020;
668pub const NOTE_LOWAT = 0x00000001;659pub const TIOCCBRK = 0x8021;
669660pub const TIOCGSID = 0x8024;
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;
759661
760pub fn WEXITSTATUS(s: u32) u32 {662pub fn WEXITSTATUS(s: u32) u32 {
761 return (s & 0xff00) >> 8;663 return (s & 0xff);
762}664}
665
763pub fn WTERMSIG(s: u32) u32 {666pub fn WTERMSIG(s: u32) u32 {
764 return s & 0x7f;667 return (s >> 8) & 0xff;
765}668}
669
766pub fn WSTOPSIG(s: u32) u32 {670pub fn WSTOPSIG(s: u32) u32 {
767 return WEXITSTATUS(s);671 return WEXITSTATUS(s);
768}672}
673
769pub fn WIFEXITED(s: u32) bool {674pub fn WIFEXITED(s: u32) bool {
770 return WTERMSIG(s) == 0;675 return WTERMSIG(s) == 0;
771}676}
677
772pub fn WIFSTOPPED(s: u32) bool {678pub fn WIFSTOPPED(s: u32) bool {
773 return @intCast(u16, (((s & 0xffff) *% 0x10001) >> 8)) > 0x7f00;679 return ((s >> 16) & 0xff) != 0;
774}680}
681
775pub fn WIFSIGNALED(s: u32) bool {682pub fn WIFSIGNALED(s: u32) bool {
776 return (s & 0xffff) -% 1 < 0xff;683 return ((s >> 8) & 0xff) != 0;
777}684}
778685
779pub const winsize = extern struct {686pub const winsize = extern struct {
...@@ -823,49 +730,47 @@ pub const sigset_t = extern struct {...@@ -823,49 +730,47 @@ pub const sigset_t = extern struct {
823 __bits: [_SIG_WORDS]u32,730 __bits: [_SIG_WORDS]u32,
824};731};
825732
826pub const EPERM = 1; // Operation not permitted733pub const EPERM = -0x7ffffff1; // Operation not permitted
827pub const ENOENT = 2; // No such file or directory734pub const ENOENT = -0x7fff9ffd; // No such file or directory
828pub const ESRCH = 3; // No such process735pub const ESRCH = -0x7fff8ff3; // No such process
829pub const EINTR = 4; // Interrupted system call736pub const EINTR = -0x7ffffff6; // Interrupted system call
830pub const EIO = 5; // Input/output error737pub const EIO = -0x7fffffff; // Input/output error
831pub const ENXIO = 6; // Device not configured738pub const ENXIO = -0x7fff8ff5; // Device not configured
832pub const E2BIG = 7; // Argument list too long739pub const E2BIG = -0x7fff8fff; // Argument list too long
833pub const ENOEXEC = 8; // Exec format error740pub const ENOEXEC = -0x7fffecfe; // Exec format error
834pub const EBADF = 9; // Bad file descriptor741pub const ECHILD = -0x7fff8ffe; // No child processes
835pub const ECHILD = 10; // No child processes742pub const EDEADLK = -0x7fff8ffd; // Resource deadlock avoided
836pub const EDEADLK = 11; // Resource deadlock avoided743pub const ENOMEM = -0x80000000; // Cannot allocate memory
837// 11 was EAGAIN744pub const EACCES = -0x7ffffffe; // Permission denied
838pub const ENOMEM = 12; // Cannot allocate memory745pub const EFAULT = -0x7fffecff; // Bad address
839pub const EACCES = 13; // Permission denied746pub const EBUSY = -0x7ffffff2; // Device busy
840pub const EFAULT = 14; // Bad address747pub const EEXIST = -0x7fff9ffe; // File exists
841pub const ENOTBLK = 15; // Block device required748pub const EXDEV = -0x7fff9ff5; // Cross-device link
842pub const EBUSY = 16; // Device busy749pub const ENODEV = -0x7fff8ff9; // Operation not supported by device
843pub const EEXIST = 17; // File exists750pub const ENOTDIR = -0x7fff9ffb; // Not a directory
844pub const EXDEV = 18; // Cross-device link751pub const EISDIR = -0x7fff9ff7; // Is a directory
845pub const ENODEV = 19; // Operation not supported by device752pub const EINVAL = -0x7ffffffb; // Invalid argument
846pub const ENOTDIR = 20; // Not a directory753pub const ENFILE = -0x7fff8ffa; // Too many open files in system
847pub const EISDIR = 21; // Is a directory754pub const EMFILE = -0x7fff9ff6; // Too many open files
848pub const EINVAL = 22; // Invalid argument755pub const ENOTTY = -0x7fff8ff6; // Inappropriate ioctl for device
849pub const ENFILE = 23; // Too many open files in system756pub const ETXTBSY = -0x7fff8fc5; // Text file busy
850pub const EMFILE = 24; // Too many open files757pub const EFBIG = -0x7fff8ffc; // File too large
851pub const ENOTTY = 25; // Inappropriate ioctl for device758pub const ENOSPC = -0x7fff9ff9; // No space left on device
852pub const ETXTBSY = 26; // Text file busy759pub const ESPIPE = -0x7fff8ff4; // Illegal seek
853pub const EFBIG = 27; // File too large760pub const EROFS = -0x7fff9ff8; // Read-only filesystem
854pub const ENOSPC = 28; // No space left on device761pub const EMLINK = -0x7fff8ffb; // Too many links
855pub const ESPIPE = 29; // Illegal seek762pub const EPIPE = -0x7fff9ff3; // Broken pipe
856pub const EROFS = 30; // Read-only filesystem763pub const EBADF = -0x7fffa000; // Bad file descriptor
857pub const EMLINK = 31; // Too many links
858pub const EPIPE = 32; // Broken pipe
859764
860// math software765// math software
861pub const EDOM = 33; // Numerical argument out of domain766pub const EDOM = 33; // Numerical argument out of domain
862pub const ERANGE = 34; // Result too large767pub const ERANGE = 34; // Result too large
863768
864// non-blocking and interrupt i/o769// non-blocking and interrupt i/o
865pub const EAGAIN = 35; // Resource temporarily unavailable770pub const EAGAIN = -0x7ffffff5;
866pub const EWOULDBLOCK = EAGAIN; // Operation would block771pub const EWOULDBLOCK = -0x7ffffff5;
867pub const EINPROGRESS = 36; // Operation now in progress772pub const EINPROGRESS = -0x7fff8fdc;
868pub const EALREADY = 37; // Operation already in progress773pub const EALREADY = -0x7fff8fdb;
869774
870// ipc/network software -- argument errors775// ipc/network software -- argument errors
871pub const ENOTSOCK = 38; // Socket operation on non-socket776pub const ENOTSOCK = 38; // Socket operation on non-socket
...@@ -1447,3 +1352,20 @@ pub const directory_which = enum(c_int) {...@@ -1447,3 +1352,20 @@ pub const directory_which = enum(c_int) {
14471352
1448 _,1353 _,
1449};1354};
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 {...@@ -248,7 +248,7 @@ fn initTLS() void {
248 tls_data = @intToPtr([*]u8, img_base + phdr.p_vaddr)[0..phdr.p_filesz];248 tls_data = @intToPtr([*]u8, img_base + phdr.p_vaddr)[0..phdr.p_filesz];
249 tls_data_alloc_size = phdr.p_memsz;249 tls_data_alloc_size = phdr.p_memsz;
250 } else {250 } else {
251 tls_align_factor = @alignOf(*usize);251 tls_align_factor = @alignOf(usize);
252 tls_data = &[_]u8{};252 tls_data = &[_]u8{};
253 tls_data_alloc_size = 0;253 tls_data_alloc_size = 0;
254 }254 }
...@@ -308,7 +308,7 @@ fn initTLS() void {...@@ -308,7 +308,7 @@ fn initTLS() void {
308}308}
309309
310fn alignPtrCast(comptime T: type, ptr: [*]u8) callconv(.Inline) *T {310fn 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));
312}312}
313313
314/// Initializes all the fields of the static TLS area and returns the computed314/// 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");...@@ -88,6 +88,7 @@ pub const time = @import("time.zig");
88pub const unicode = @import("unicode.zig");88pub const unicode = @import("unicode.zig");
89pub const valgrind = @import("valgrind.zig");89pub const valgrind = @import("valgrind.zig");
90pub const wasm = @import("wasm.zig");90pub const wasm = @import("wasm.zig");
91pub const x = @import("x.zig");
91pub const zig = @import("zig.zig");92pub const zig = @import("zig.zig");
92pub const start = @import("start.zig");93pub const start = @import("start.zig");
9394
lib/std/target.zig+4-3
...@@ -211,8 +211,9 @@ pub const Target = struct {...@@ -211,8 +211,9 @@ pub const Target = struct {
211 /// If neither of these cases apply, a runtime check should be used to determine if the211 /// If neither of these cases apply, a runtime check should be used to determine if the
212 /// target supports a given OS feature.212 /// target supports a given OS feature.
213 ///213 ///
214 /// Binaries built with a given maximum version will continue to function on newer operating system214 /// Binaries built with a given maximum version will continue to function on newer
215 /// versions. However, such a binary may not take full advantage of the newer operating system APIs.215 /// operating system versions. However, such a binary may not take full advantage of the
216 /// newer operating system APIs.
216 ///217 ///
217 /// See `Os.isAtLeast`.218 /// See `Os.isAtLeast`.
218 pub const VersionRange = union {219 pub const VersionRange = union {
...@@ -260,7 +261,7 @@ pub const Target = struct {...@@ -260,7 +261,7 @@ pub const Target = struct {
260 .freebsd => return .{261 .freebsd => return .{
261 .semver = Version.Range{262 .semver = Version.Range{
262 .min = .{ .major = 12, .minor = 0 },263 .min = .{ .major = 12, .minor = 0 },
263 .max = .{ .major = 12, .minor = 1 },264 .max = .{ .major = 13, .minor = 0 },
264 },265 },
265 },266 },
266 .macos => return .{267 .macos => return .{
lib/std/unicode.zig+1-1
...@@ -206,7 +206,7 @@ pub fn utf8ValidateSlice(s: []const u8) bool {...@@ -206,7 +206,7 @@ pub fn utf8ValidateSlice(s: []const u8) bool {
206 return false;206 return false;
207 }207 }
208208
209 if (utf8Decode(s[i .. i + cp_len])) |_| {} else |_| {209 if (std.meta.isError(utf8Decode(s[i .. i + cp_len]))) {
210 return false;210 return false;
211 }211 }
212 i += cp_len;212 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;...@@ -15,6 +15,7 @@ const Target = std.Target;
15const CrossTarget = std.zig.CrossTarget;15const CrossTarget = std.zig.CrossTarget;
16const macos = @import("system/macos.zig");16const macos = @import("system/macos.zig");
17const native_endian = std.Target.current.cpu.arch.endian();17const native_endian = std.Target.current.cpu.arch.endian();
18const linux = @import("system/linux.zig");
18pub const windows = @import("system/windows.zig");19pub const windows = @import("system/windows.zig");
1920
20pub const getSDKPath = macos.getSDKPath;21pub const getSDKPath = macos.getSDKPath;
...@@ -912,15 +913,19 @@ pub const NativeTargetInfo = struct {...@@ -912,15 +913,19 @@ pub const NativeTargetInfo = struct {
912 .x86_64, .i386 => {913 .x86_64, .i386 => {
913 return @import("system/x86.zig").detectNativeCpuAndFeatures(cpu_arch, os, cross_target);914 return @import("system/x86.zig").detectNativeCpuAndFeatures(cpu_arch, os, cross_target);
914 },915 },
915 else => {916 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 },
920 }917 }
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();
921 }925 }
922};926};
923927
924test {928test {
925 _ = @import("system/macos.zig");929 _ = @import("system/macos.zig");
930 _ = @import("system/linux.zig");
926}931}
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 {...@@ -104,6 +104,16 @@ pub const APFloat = opaque {
104 extern fn ZigClangAPFloat_toString(*const APFloat, precision: c_uint, maxPadding: c_uint, truncateZero: bool) [*:0]const u8;104 extern fn ZigClangAPFloat_toString(*const APFloat, precision: c_uint, maxPadding: c_uint, truncateZero: bool) [*:0]const u8;
105};105};
106106
107pub const APFloatBaseSemantics = extern enum {
108 IEEEhalf,
109 BFloat,
110 IEEEsingle,
111 IEEEdouble,
112 x86DoubleExtended,
113 IEEEquad,
114 PPCDoubleDouble,
115};
116
107pub const APInt = opaque {117pub const APInt = opaque {
108 pub const getLimitedValue = ZigClangAPInt_getLimitedValue;118 pub const getLimitedValue = ZigClangAPInt_getLimitedValue;
109 extern fn ZigClangAPInt_getLimitedValue(*const APInt, limit: u64) u64;119 extern fn ZigClangAPInt_getLimitedValue(*const APInt, limit: u64) u64;
...@@ -455,6 +465,12 @@ pub const FileID = opaque {};...@@ -455,6 +465,12 @@ pub const FileID = opaque {};
455pub const FloatingLiteral = opaque {465pub const FloatingLiteral = opaque {
456 pub const getValueAsApproximateDouble = ZigClangFloatingLiteral_getValueAsApproximateDouble;466 pub const getValueAsApproximateDouble = ZigClangFloatingLiteral_getValueAsApproximateDouble;
457 extern fn ZigClangFloatingLiteral_getValueAsApproximateDouble(*const FloatingLiteral) f64;467 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;
458};474};
459475
460pub const ForStmt = opaque {476pub const ForStmt = opaque {
src/codegen.zig+29-17
...@@ -449,7 +449,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -449,7 +449,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
449 .rbrace_src = src_data.rbrace_src,449 .rbrace_src = src_data.rbrace_src,
450 .source = src_data.source,450 .source = src_data.source,
451 };451 };
452 defer function.register_manager.deinit(bin_file.allocator);
453 defer function.stack.deinit(bin_file.allocator);452 defer function.stack.deinit(bin_file.allocator);
454 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);453 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
455454
...@@ -779,8 +778,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -779,8 +778,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
779 branch.inst_table.putAssumeCapacity(inst, .dead);778 branch.inst_table.putAssumeCapacity(inst, .dead);
780 switch (prev_value) {779 switch (prev_value) {
781 .register => |reg| {780 .register => |reg| {
782 const canon_reg = toCanonicalReg(reg);781 // TODO separate architectures with registers from
783 self.register_manager.freeReg(canon_reg);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 }
784 },787 },
785 else => {}, // TODO process stack allocation death788 else => {}, // TODO process stack allocation death
786 }789 }
...@@ -920,9 +923,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -920,9 +923,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
920 const ptr_bits = arch.ptrBitWidth();923 const ptr_bits = arch.ptrBitWidth();
921 const ptr_bytes: u64 = @divExact(ptr_bits, 8);924 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
922 if (abi_size <= ptr_bytes) {925 if (abi_size <= ptr_bytes) {
923 try self.register_manager.registers.ensureCapacity(self.gpa, self.register_manager.registers.count() + 1);926 // TODO separate architectures with registers from
924 if (self.register_manager.tryAllocReg(inst)) |reg| {927 // stack-based architectures (spu_2)
925 return MCValue{ .register = registerAlias(reg, abi_size) };928 if (callee_preserved_regs.len > 0) {
929 if (self.register_manager.tryAllocReg(inst)) |reg| {
930 return MCValue{ .register = registerAlias(reg, abi_size) };
931 }
926 }932 }
927 }933 }
928 }934 }
...@@ -952,8 +958,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -952,8 +958,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
952 /// `reg_owner` is the instruction that gets associated with the register in the register table.958 /// `reg_owner` is the instruction that gets associated with the register in the register table.
953 /// This can have a side effect of spilling instructions to the stack to free up a register.959 /// This can have a side effect of spilling instructions to the stack to free up a register.
954 fn copyToNewRegister(self: *Self, reg_owner: *ir.Inst, mcv: MCValue) !MCValue {960 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
957 const reg = try self.register_manager.allocReg(reg_owner);961 const reg = try self.register_manager.allocReg(reg_owner);
958 try self.genSetReg(reg_owner.src, reg_owner.ty, reg, mcv);962 try self.genSetReg(reg_owner.src, reg_owner.ty, reg, mcv);
959 return MCValue{ .register = reg };963 return MCValue{ .register = reg };
...@@ -1240,10 +1244,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1240,10 +1244,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1240 .register => |reg| {1244 .register => |reg| {
1241 // If it's in the registers table, need to associate the register with the1245 // If it's in the registers table, need to associate the register with the
1242 // new instruction.1246 // new instruction.
1243 if (self.register_manager.registers.getEntry(toCanonicalReg(reg))) |entry| {1247 // TODO separate architectures with registers from
1244 entry.value = inst;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 });
1245 }1256 }
1246 log.debug("reusing {} => {*}", .{ reg, inst });
1247 },1257 },
1248 .stack_offset => |off| {1258 .stack_offset => |off| {
1249 log.debug("reusing stack offset {} => {*}", .{ off, inst });1259 log.debug("reusing stack offset {} => {*}", .{ off, inst });
...@@ -1738,6 +1748,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1738,6 +1748,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1738 const arg_index = self.arg_index;1748 const arg_index = self.arg_index;
1739 self.arg_index += 1;1749 self.arg_index += 1;
17401750
1751 // TODO separate architectures with registers from
1752 // stack-based architectures (spu_2)
1741 if (callee_preserved_regs.len == 0) {1753 if (callee_preserved_regs.len == 0) {
1742 return self.fail(inst.base.src, "TODO implement Register enum for {}", .{self.target.cpu.arch});1754 return self.fail(inst.base.src, "TODO implement Register enum for {}", .{self.target.cpu.arch});
1743 }1755 }
...@@ -1769,7 +1781,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1769,7 +1781,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
17691781
1770 switch (mcv) {1782 switch (mcv) {
1771 .register => |reg| {1783 .register => |reg| {
1772 try self.register_manager.registers.ensureCapacity(self.gpa, self.register_manager.registers.count() + 1);
1773 self.register_manager.getRegAssumeFree(toCanonicalReg(reg), &inst.base);1784 self.register_manager.getRegAssumeFree(toCanonicalReg(reg), &inst.base);
1774 },1785 },
1775 else => {},1786 else => {},
...@@ -2075,7 +2086,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2075,7 +2086,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2075 switch (mc_arg) {2086 switch (mc_arg) {
2076 .none => continue,2087 .none => continue,
2077 .register => |reg| {2088 .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 }
2079 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);2094 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
2080 },2095 },
2081 .stack_offset => {2096 .stack_offset => {
...@@ -2397,8 +2412,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2397,8 +2412,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2397 const parent_free_registers = self.register_manager.free_registers;2412 const parent_free_registers = self.register_manager.free_registers;
2398 var parent_stack = try self.stack.clone(self.gpa);2413 var parent_stack = try self.stack.clone(self.gpa);
2399 defer parent_stack.deinit(self.gpa);2414 defer parent_stack.deinit(self.gpa);
2400 var parent_registers = try self.register_manager.registers.clone(self.gpa);2415 const parent_registers = self.register_manager.registers;
2401 defer parent_registers.deinit(self.gpa);
24022416
2403 try self.branch_stack.append(.{});2417 try self.branch_stack.append(.{});
24042418
...@@ -2414,9 +2428,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2414,9 +2428,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2414 var saved_then_branch = self.branch_stack.pop();2428 var saved_then_branch = self.branch_stack.pop();
2415 defer saved_then_branch.deinit(self.gpa);2429 defer saved_then_branch.deinit(self.gpa);
24162430
2417 self.register_manager.registers.deinit(self.gpa);
2418 self.register_manager.registers = parent_registers;2431 self.register_manager.registers = parent_registers;
2419 parent_registers = .{};
24202432
2421 self.stack.deinit(self.gpa);2433 self.stack.deinit(self.gpa);
2422 self.stack = parent_stack;2434 self.stack = parent_stack;
src/codegen/riscv64.zig+47-10
...@@ -1,5 +1,7 @@...@@ -1,5 +1,7 @@
1const std = @import("std");1const std = @import("std");
2const DW = std.dwarf;2const DW = std.dwarf;
3const assert = std.debug.assert;
4const testing = std.testing;
35
4// TODO: this is only tagged to facilitate the monstrosity.6// TODO: this is only tagged to facilitate the monstrosity.
5// Once packed structs work make it packed.7// Once packed structs work make it packed.
...@@ -110,7 +112,7 @@ pub const Instruction = union(enum) {...@@ -110,7 +112,7 @@ pub const Instruction = union(enum) {
110 // -- less burden on callsite, bonus semantic checking112 // -- less burden on callsite, bonus semantic checking
111 fn bType(op: u7, fn3: u3, r1: Register, r2: Register, imm: i13) Instruction {113 fn bType(op: u7, fn3: u3, r1: Register, r2: Register, imm: i13) Instruction {
112 const umm = @bitCast(u13, imm);114 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
115 return Instruction{117 return Instruction{
116 .B = .{118 .B = .{
...@@ -140,15 +142,15 @@ pub const Instruction = union(enum) {...@@ -140,15 +142,15 @@ pub const Instruction = union(enum) {
140 }142 }
141143
142 fn jType(op: u7, rd: Register, imm: i21) Instruction {144 fn jType(op: u7, rd: Register, imm: i21) Instruction {
143 const umm = @bitcast(u21, imm);145 const umm = @bitCast(u21, imm);
144 if (umm % 2 != 0) @panic("Internal error: misaligned jump target");146 assert(umm % 2 == 0); // misaligned jump target
145147
146 return Instruction{148 return Instruction{
147 .J = .{149 .J = .{
148 .opcode = op,150 .opcode = op,
149 .rd = @enumToInt(rd),151 .rd = @enumToInt(rd),
150 .imm1_10 = @truncate(u10, umm >> 1),152 .imm1_10 = @truncate(u10, umm >> 1),
151 .imm11 = @truncate(u1, umm >> 1),153 .imm11 = @truncate(u1, umm >> 11),
152 .imm12_19 = @truncate(u8, umm >> 12),154 .imm12_19 = @truncate(u8, umm >> 12),
153 .imm20 = @truncate(u1, umm >> 20),155 .imm20 = @truncate(u1, umm >> 20),
154 },156 },
...@@ -340,27 +342,27 @@ pub const Instruction = union(enum) {...@@ -340,27 +342,27 @@ pub const Instruction = union(enum) {
340342
341 // Branch343 // Branch
342344
343 pub fn beq(r1: Register, r2: Register, offset: u13) Instruction {345 pub fn beq(r1: Register, r2: Register, offset: i13) Instruction {
344 return bType(0b1100011, 0b000, r1, r2, offset);346 return bType(0b1100011, 0b000, r1, r2, offset);
345 }347 }
346348
347 pub fn bne(r1: Register, r2: Register, offset: u13) Instruction {349 pub fn bne(r1: Register, r2: Register, offset: i13) Instruction {
348 return bType(0b1100011, 0b001, r1, r2, offset);350 return bType(0b1100011, 0b001, r1, r2, offset);
349 }351 }
350352
351 pub fn blt(r1: Register, r2: Register, offset: u13) Instruction {353 pub fn blt(r1: Register, r2: Register, offset: i13) Instruction {
352 return bType(0b1100011, 0b100, r1, r2, offset);354 return bType(0b1100011, 0b100, r1, r2, offset);
353 }355 }
354356
355 pub fn bge(r1: Register, r2: Register, offset: u13) Instruction {357 pub fn bge(r1: Register, r2: Register, offset: i13) Instruction {
356 return bType(0b1100011, 0b101, r1, r2, offset);358 return bType(0b1100011, 0b101, r1, r2, offset);
357 }359 }
358360
359 pub fn bltu(r1: Register, r2: Register, offset: u13) Instruction {361 pub fn bltu(r1: Register, r2: Register, offset: i13) Instruction {
360 return bType(0b1100011, 0b110, r1, r2, offset);362 return bType(0b1100011, 0b110, r1, r2, offset);
361 }363 }
362364
363 pub fn bgeu(r1: Register, r2: Register, offset: u13) Instruction {365 pub fn bgeu(r1: Register, r2: Register, offset: i13) Instruction {
364 return bType(0b1100011, 0b111, r1, r2, offset);366 return bType(0b1100011, 0b111, r1, r2, offset);
365 }367 }
366368
...@@ -431,3 +433,38 @@ pub const Register = enum(u5) {...@@ -431,3 +433,38 @@ pub const Register = enum(u5) {
431pub const callee_preserved_regs = [_]Register{433pub const callee_preserved_regs = [_]Register{
432 .s0, .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11,434 .s0, .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11,
433};435};
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");...@@ -9,6 +9,7 @@ const build_options = @import("build_options");
9const is_darwin = Target.current.isDarwin();9const is_darwin = Target.current.isDarwin();
10const is_windows = Target.current.os.tag == .windows;10const is_windows = Target.current.os.tag == .windows;
11const is_gnu = Target.current.isGnu();11const is_gnu = Target.current.isGnu();
12const is_haiku = Target.current.os.tag == .haiku;
1213
13const log = std.log.scoped(.libc_installation);14const log = std.log.scoped(.libc_installation);
1415
...@@ -279,8 +280,14 @@ pub const LibCInstallation = struct {...@@ -279,8 +280,14 @@ pub const LibCInstallation = struct {
279 return error.CCompilerCannotFindHeaders;280 return error.CCompilerCannotFindHeaders;
280 }281 }
281282
282 const include_dir_example_file = "stdlib.h";283 const include_dir_example_file = if (is_haiku) "posix/stdlib.h" else "stdlib.h";
283 const sys_include_dir_example_file = if (is_windows) "sys\\types.h" else "sys/errno.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
285 var path_i: usize = 0;292 var path_i: usize = 0;
286 while (path_i < search_paths.items.len) : (path_i += 1) {293 while (path_i < search_paths.items.len) : (path_i += 1) {
src/register_manager.zig+43-50
...@@ -16,7 +16,7 @@ pub fn RegisterManager(...@@ -16,7 +16,7 @@ pub fn RegisterManager(
16) type {16) type {
17 return struct {17 return struct {
18 /// The key must be canonical register.18 /// 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,
20 free_registers: FreeRegInt = math.maxInt(FreeRegInt),20 free_registers: FreeRegInt = math.maxInt(FreeRegInt),
21 /// Tracks all registers allocated in the course of this function21 /// Tracks all registers allocated in the course of this function
22 allocated_registers: FreeRegInt = 0,22 allocated_registers: FreeRegInt = 0,
...@@ -31,14 +31,6 @@ pub fn RegisterManager(...@@ -31,14 +31,6 @@ pub fn RegisterManager(
31 return @fieldParentPtr(Function, "register_manager", self);31 return @fieldParentPtr(Function, "register_manager", self);
32 }32 }
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
42 fn markRegUsed(self: *Self, reg: Register) void {34 fn markRegUsed(self: *Self, reg: Register) void {
43 if (FreeRegInt == u0) return;35 if (FreeRegInt == u0) return;
44 const index = reg.allocIndex() orelse return;36 const index = reg.allocIndex() orelse return;
...@@ -73,13 +65,13 @@ pub fn RegisterManager(...@@ -73,13 +65,13 @@ pub fn RegisterManager(
73 return self.allocated_registers & @as(FreeRegInt, 1) << shift != 0;65 return self.allocated_registers & @as(FreeRegInt, 1) << shift != 0;
74 }66 }
7567
76 /// Before calling, must ensureCapacity + count on self.registers.
77 /// Returns `null` if all registers are allocated.68 /// Returns `null` if all registers are allocated.
78 pub fn tryAllocRegs(self: *Self, comptime count: comptime_int, insts: [count]*ir.Inst) ?[count]Register {69 pub fn tryAllocRegs(self: *Self, comptime count: comptime_int, insts: [count]*ir.Inst) ?[count]Register {
79 if (self.tryAllocRegsWithoutTracking(count)) |regs| {70 if (self.tryAllocRegsWithoutTracking(count)) |regs| {
80 for (regs) |reg, i| {71 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];
81 self.markRegUsed(reg);74 self.markRegUsed(reg);
82 self.registers.putAssumeCapacityNoClobber(reg, insts[i]);
83 }75 }
8476
85 return regs;77 return regs;
...@@ -88,13 +80,11 @@ pub fn RegisterManager(...@@ -88,13 +80,11 @@ pub fn RegisterManager(
88 }80 }
89 }81 }
9082
91 /// Before calling, must ensureCapacity + 1 on self.registers.
92 /// Returns `null` if all registers are allocated.83 /// Returns `null` if all registers are allocated.
93 pub fn tryAllocReg(self: *Self, inst: *ir.Inst) ?Register {84 pub fn tryAllocReg(self: *Self, inst: *ir.Inst) ?Register {
94 return if (tryAllocRegs(self, 1, .{inst})) |regs| regs[0] else null;85 return if (tryAllocRegs(self, 1, .{inst})) |regs| regs[0] else null;
95 }86 }
9687
97 /// Before calling, must ensureCapacity + count on self.registers.
98 pub fn allocRegs(self: *Self, comptime count: comptime_int, insts: [count]*ir.Inst) ![count]Register {88 pub fn allocRegs(self: *Self, comptime count: comptime_int, insts: [count]*ir.Inst) ![count]Register {
99 comptime assert(count > 0 and count <= callee_preserved_regs.len);89 comptime assert(count > 0 and count <= callee_preserved_regs.len);
10090
...@@ -106,24 +96,22 @@ pub fn RegisterManager(...@@ -106,24 +96,22 @@ pub fn RegisterManager(
106 std.mem.copy(Register, &regs, callee_preserved_regs[0..count]);96 std.mem.copy(Register, &regs, callee_preserved_regs[0..count]);
10797
108 for (regs) |reg, i| {98 for (regs) |reg, i| {
99 const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null
109 if (self.isRegFree(reg)) {100 if (self.isRegFree(reg)) {
110 self.markRegUsed(reg);101 self.markRegUsed(reg);
111 self.registers.putAssumeCapacityNoClobber(reg, insts[i]);
112 } else {102 } else {
113 const regs_entry = self.registers.getEntry(reg).?;103 const spilled_inst = self.registers[index].?;
114 const spilled_inst = regs_entry.value;
115 regs_entry.value = insts[i];
116 try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);104 try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);
117 }105 }
106 self.registers[index] = insts[i];
118 }107 }
119108
120 break :blk regs;109 break :blk regs;
121 };110 };
122 }111 }
123112
124 /// Before calling, must ensureCapacity + 1 on self.registers.
125 pub fn allocReg(self: *Self, inst: *ir.Inst) !Register {113 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];
127 }115 }
128116
129 /// Does not track the registers.117 /// Does not track the registers.
...@@ -150,37 +138,48 @@ pub fn RegisterManager(...@@ -150,37 +138,48 @@ pub fn RegisterManager(
150 /// Does not track the register.138 /// Does not track the register.
151 /// Returns `null` if all registers are allocated.139 /// Returns `null` if all registers are allocated.
152 pub fn tryAllocRegWithoutTracking(self: *Self) ?Register {140 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;
154 }142 }
155143
156 /// Does not track the register.144 /// Does not track the registers
157 pub fn allocRegWithoutTracking(self: *Self) !Register {145 pub fn allocRegsWithoutTracking(self: *Self, comptime count: comptime_int) ![count]Register {
158 return self.tryAllocRegWithoutTracking() orelse b: {146 return self.tryAllocRegsWithoutTracking(count) orelse blk: {
159 // We'll take over the first register. Move the instruction that was previously147 // We'll take over the first count registers. Spill
160 // there to a stack allocation.148 // the instructions that were previously there to a
161 const reg = callee_preserved_regs[0];149 // stack allocations.
162 const regs_entry = self.registers.remove(reg).?;150 var regs: [count]Register = undefined;
163 const spilled_inst = regs_entry.value;151 std.mem.copy(Register, &regs, callee_preserved_regs[0..count]);
164 try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);
165 self.markRegFree(reg);
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;
168 };164 };
169 }165 }
170166
167 /// Does not track the register.
168 pub fn allocRegWithoutTracking(self: *Self) !Register {
169 return (try self.allocRegsWithoutTracking(1))[0];
170 }
171
171 /// Allocates the specified register with the specified172 /// Allocates the specified register with the specified
172 /// instruction. Spills the register if it is currently173 /// instruction. Spills the register if it is currently
173 /// allocated.174 /// allocated.
174 /// Before calling, must ensureCapacity + 1 on self.registers.
175 pub fn getReg(self: *Self, reg: Register, inst: *ir.Inst) !void {175 pub fn getReg(self: *Self, reg: Register, inst: *ir.Inst) !void {
176 if (!isTracked(reg)) return;176 const index = reg.allocIndex() orelse return;
177177
178 if (!self.isRegFree(reg)) {178 if (!self.isRegFree(reg)) {
179 // Move the instruction that was previously there to a179 // Move the instruction that was previously there to a
180 // stack allocation.180 // stack allocation.
181 const regs_entry = self.registers.getEntry(reg).?;181 const spilled_inst = self.registers[index].?;
182 const spilled_inst = regs_entry.value;182 self.registers[index] = inst;
183 regs_entry.value = inst;
184 try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);183 try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);
185 } else {184 } else {
186 self.getRegAssumeFree(reg, inst);185 self.getRegAssumeFree(reg, inst);
...@@ -190,34 +189,33 @@ pub fn RegisterManager(...@@ -190,34 +189,33 @@ pub fn RegisterManager(
190 /// Spills the register if it is currently allocated.189 /// Spills the register if it is currently allocated.
191 /// Does not track the register.190 /// Does not track the register.
192 pub fn getRegWithoutTracking(self: *Self, reg: Register) !void {191 pub fn getRegWithoutTracking(self: *Self, reg: Register) !void {
193 if (!isTracked(reg)) return;192 const index = reg.allocIndex() orelse return;
194193
195 if (!self.isRegFree(reg)) {194 if (!self.isRegFree(reg)) {
196 // Move the instruction that was previously there to a195 // Move the instruction that was previously there to a
197 // stack allocation.196 // stack allocation.
198 const regs_entry = self.registers.remove(reg).?;197 const spilled_inst = self.registers[index].?;
199 const spilled_inst = regs_entry.value;
200 try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);198 try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);
201 self.markRegFree(reg);199 self.markRegFree(reg);
202 }200 }
203 }201 }
204202
205 /// Allocates the specified register with the specified203 /// Allocates the specified register with the specified
206 /// instruction. Assumes that the register is free and no204 /// instruction. Asserts that the register is free and no
207 /// spilling is necessary.205 /// spilling is necessary.
208 /// Before calling, must ensureCapacity + 1 on self.registers.
209 pub fn getRegAssumeFree(self: *Self, reg: Register, inst: *ir.Inst) void {206 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;
213 self.markRegUsed(reg);211 self.markRegUsed(reg);
214 }212 }
215213
216 /// Marks the specified register as free214 /// Marks the specified register as free
217 pub fn freeReg(self: *Self, reg: Register) void {215 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;
221 self.markRegFree(reg);219 self.markRegFree(reg);
222 }220 }
223 };221 };
...@@ -247,7 +245,6 @@ const MockFunction = struct {...@@ -247,7 +245,6 @@ const MockFunction = struct {
247 const Self = @This();245 const Self = @This();
248246
249 pub fn deinit(self: *Self) void {247 pub fn deinit(self: *Self) void {
250 self.register_manager.deinit(self.allocator);
251 self.spilled.deinit(self.allocator);248 self.spilled.deinit(self.allocator);
252 }249 }
253250
...@@ -273,7 +270,6 @@ test "tryAllocReg: no spilling" {...@@ -273,7 +270,6 @@ test "tryAllocReg: no spilling" {
273 std.testing.expect(!function.register_manager.isRegAllocated(.r2));270 std.testing.expect(!function.register_manager.isRegAllocated(.r2));
274 std.testing.expect(!function.register_manager.isRegAllocated(.r3));271 std.testing.expect(!function.register_manager.isRegAllocated(.r3));
275272
276 try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2);
277 std.testing.expectEqual(@as(?MockRegister, .r2), function.register_manager.tryAllocReg(&mock_instruction));273 std.testing.expectEqual(@as(?MockRegister, .r2), function.register_manager.tryAllocReg(&mock_instruction));
278 std.testing.expectEqual(@as(?MockRegister, .r3), function.register_manager.tryAllocReg(&mock_instruction));274 std.testing.expectEqual(@as(?MockRegister, .r3), function.register_manager.tryAllocReg(&mock_instruction));
279 std.testing.expectEqual(@as(?MockRegister, null), function.register_manager.tryAllocReg(&mock_instruction));275 std.testing.expectEqual(@as(?MockRegister, null), function.register_manager.tryAllocReg(&mock_instruction));
...@@ -305,7 +301,6 @@ test "allocReg: spilling" {...@@ -305,7 +301,6 @@ test "allocReg: spilling" {
305 std.testing.expect(!function.register_manager.isRegAllocated(.r2));301 std.testing.expect(!function.register_manager.isRegAllocated(.r2));
306 std.testing.expect(!function.register_manager.isRegAllocated(.r3));302 std.testing.expect(!function.register_manager.isRegAllocated(.r3));
307303
308 try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2);
309 std.testing.expectEqual(@as(?MockRegister, .r2), try function.register_manager.allocReg(&mock_instruction));304 std.testing.expectEqual(@as(?MockRegister, .r2), try function.register_manager.allocReg(&mock_instruction));
310 std.testing.expectEqual(@as(?MockRegister, .r3), try function.register_manager.allocReg(&mock_instruction));305 std.testing.expectEqual(@as(?MockRegister, .r3), try function.register_manager.allocReg(&mock_instruction));
311306
...@@ -336,14 +331,12 @@ test "getReg" {...@@ -336,14 +331,12 @@ test "getReg" {
336 std.testing.expect(!function.register_manager.isRegAllocated(.r2));331 std.testing.expect(!function.register_manager.isRegAllocated(.r2));
337 std.testing.expect(!function.register_manager.isRegAllocated(.r3));332 std.testing.expect(!function.register_manager.isRegAllocated(.r3));
338333
339 try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2);
340 try function.register_manager.getReg(.r3, &mock_instruction);334 try function.register_manager.getReg(.r3, &mock_instruction);
341335
342 std.testing.expect(!function.register_manager.isRegAllocated(.r2));336 std.testing.expect(!function.register_manager.isRegAllocated(.r2));
343 std.testing.expect(function.register_manager.isRegAllocated(.r3));337 std.testing.expect(function.register_manager.isRegAllocated(.r3));
344338
345 // Spill r3339 // Spill r3
346 try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2);
347 try function.register_manager.getReg(.r3, &mock_instruction);340 try function.register_manager.getReg(.r3, &mock_instruction);
348341
349 std.testing.expect(!function.register_manager.isRegAllocated(.r2));342 std.testing.expect(!function.register_manager.isRegAllocated(.r2));
src/stage1/all_types.hpp+1
...@@ -84,6 +84,7 @@ enum CallingConvention {...@@ -84,6 +84,7 @@ enum CallingConvention {
84 CallingConventionAPCS,84 CallingConventionAPCS,
85 CallingConventionAAPCS,85 CallingConventionAAPCS,
86 CallingConventionAAPCSVFP,86 CallingConventionAAPCSVFP,
87 CallingConventionSysV
87};88};
8889
89// This one corresponds to the builtin.zig enum.90// 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) {...@@ -974,6 +974,7 @@ const char *calling_convention_name(CallingConvention cc) {
974 case CallingConventionAAPCS: return "AAPCS";974 case CallingConventionAAPCS: return "AAPCS";
975 case CallingConventionAAPCSVFP: return "AAPCSVFP";975 case CallingConventionAAPCSVFP: return "AAPCSVFP";
976 case CallingConventionInline: return "Inline";976 case CallingConventionInline: return "Inline";
977 case CallingConventionSysV: return "SysV";
977 }978 }
978 zig_unreachable();979 zig_unreachable();
979}980}
...@@ -995,6 +996,7 @@ bool calling_convention_allows_zig_types(CallingConvention cc) {...@@ -995,6 +996,7 @@ bool calling_convention_allows_zig_types(CallingConvention cc) {
995 case CallingConventionAPCS:996 case CallingConventionAPCS:
996 case CallingConventionAAPCS:997 case CallingConventionAAPCS:
997 case CallingConventionAAPCSVFP:998 case CallingConventionAAPCSVFP:
999 case CallingConventionSysV:
998 return false;1000 return false;
999 }1001 }
1000 zig_unreachable();1002 zig_unreachable();
...@@ -1969,6 +1971,10 @@ Error emit_error_unless_callconv_allowed_for_target(CodeGen *g, AstNode *source_...@@ -1969,6 +1971,10 @@ Error emit_error_unless_callconv_allowed_for_target(CodeGen *g, AstNode *source_
1969 case CallingConventionAAPCSVFP:1971 case CallingConventionAAPCSVFP:
1970 if (!target_is_arm(g->zig_target))1972 if (!target_is_arm(g->zig_target))
1971 allowed_platforms = "ARM";1973 allowed_platforms = "ARM";
1974 break;
1975 case CallingConventionSysV:
1976 if (g->zig_target->arch != ZigLLVM_x86_64)
1977 allowed_platforms = "x86_64";
1972 }1978 }
1973 if (allowed_platforms != nullptr) {1979 if (allowed_platforms != nullptr) {
1974 add_node_error(g, source_node, buf_sprintf(1980 add_node_error(g, source_node, buf_sprintf(
...@@ -3805,6 +3811,7 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {...@@ -3805,6 +3811,7 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
3805 case CallingConventionAPCS:3811 case CallingConventionAPCS:
3806 case CallingConventionAAPCS:3812 case CallingConventionAAPCS:
3807 case CallingConventionAAPCSVFP:3813 case CallingConventionAAPCSVFP:
3814 case CallingConventionSysV:
3808 add_fn_export(g, fn_table_entry, buf_ptr(&fn_table_entry->symbol_name),3815 add_fn_export(g, fn_table_entry, buf_ptr(&fn_table_entry->symbol_name),
3809 GlobalLinkageIdStrong, fn_cc);3816 GlobalLinkageIdStrong, fn_cc);
3810 break;3817 break;
...@@ -4769,11 +4776,11 @@ Error type_is_nonnull_ptr2(CodeGen *g, ZigType *type, bool *result) {...@@ -4769,11 +4776,11 @@ Error type_is_nonnull_ptr2(CodeGen *g, ZigType *type, bool *result) {
4769 return ErrorNone;4776 return ErrorNone;
4770}4777}
47714778
4772static uint32_t get_async_frame_align_bytes(CodeGen *g) {4779uint32_t get_async_frame_align_bytes(CodeGen *g) {
4773 uint32_t a = g->pointer_size_bytes * 2;4780 // Due to how the frame structure is built the minimum alignment is the one
4774 // promises have at least alignment 8 so that we can have 3 extra bits when doing atomicrmw4781 // of a usize (or pointer).
4775 if (a < 8) a = 8;4782 // label (grep this): [fn_frame_struct_layout]
4776 return a;4783 return max(g->builtin_types.entry_usize->abi_align, target_fn_align(g->zig_target));
4777}4784}
47784785
4779uint32_t get_ptr_align(CodeGen *g, ZigType *type) {4786uint32_t get_ptr_align(CodeGen *g, ZigType *type) {
...@@ -4789,11 +4796,8 @@ uint32_t get_ptr_align(CodeGen *g, ZigType *type) {...@@ -4789,11 +4796,8 @@ uint32_t get_ptr_align(CodeGen *g, ZigType *type) {
4789 return (ptr_type->data.pointer.explicit_alignment == 0) ?4796 return (ptr_type->data.pointer.explicit_alignment == 0) ?
4790 get_abi_alignment(g, ptr_type->data.pointer.child_type) : ptr_type->data.pointer.explicit_alignment;4797 get_abi_alignment(g, ptr_type->data.pointer.child_type) : ptr_type->data.pointer.explicit_alignment;
4791 } else if (ptr_type->id == ZigTypeIdFn) {4798 } else if (ptr_type->id == ZigTypeIdFn) {
4792 // I tried making this use LLVMABIAlignmentOfType but it trips this assertion in LLVM:4799 return (ptr_type->data.fn.fn_type_id.alignment == 0) ?
4793 // "Cannot getTypeInfo() on a type that is unsized!"4800 target_fn_ptr_align(g->zig_target) : ptr_type->data.fn.fn_type_id.alignment;
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;
4797 } else if (ptr_type->id == ZigTypeIdAnyFrame) {4801 } else if (ptr_type->id == ZigTypeIdAnyFrame) {
4798 return get_async_frame_align_bytes(g);4802 return get_async_frame_align_bytes(g);
4799 } else {4803 } else {
src/stage1/analyze.hpp+1
...@@ -47,6 +47,7 @@ ZigType *get_test_fn_type(CodeGen *g);...@@ -47,6 +47,7 @@ ZigType *get_test_fn_type(CodeGen *g);
47ZigType *get_any_frame_type(CodeGen *g, ZigType *result_type);47ZigType *get_any_frame_type(CodeGen *g, ZigType *result_type);
48bool handle_is_ptr(CodeGen *g, ZigType *type_entry);48bool handle_is_ptr(CodeGen *g, ZigType *type_entry);
49Error emit_error_unless_callconv_allowed_for_target(CodeGen *g, AstNode *source_node, CallingConvention cc);49Error emit_error_unless_callconv_allowed_for_target(CodeGen *g, AstNode *source_node, CallingConvention cc);
50uint32_t get_async_frame_align_bytes(CodeGen *g);
5051
51bool type_has_bits(CodeGen *g, ZigType *type_entry);52bool type_has_bits(CodeGen *g, ZigType *type_entry);
52Error type_has_bits2(CodeGen *g, ZigType *type_entry, bool *result);53Error 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) {...@@ -204,6 +204,9 @@ static ZigLLVM_CallingConv get_llvm_cc(CodeGen *g, CallingConvention cc) {
204 case CallingConventionSignal:204 case CallingConventionSignal:
205 assert(g->zig_target->arch == ZigLLVM_avr);205 assert(g->zig_target->arch == ZigLLVM_avr);
206 return ZigLLVM_AVR_SIGNAL;206 return ZigLLVM_AVR_SIGNAL;
207 case CallingConventionSysV:
208 assert(g->zig_target->arch == ZigLLVM_x86_64);
209 return ZigLLVM_X86_64_SysV;
207 }210 }
208 zig_unreachable();211 zig_unreachable();
209}212}
...@@ -348,6 +351,7 @@ static bool cc_want_sret_attr(CallingConvention cc) {...@@ -348,6 +351,7 @@ static bool cc_want_sret_attr(CallingConvention cc) {
348 case CallingConventionAPCS:351 case CallingConventionAPCS:
349 case CallingConventionAAPCS:352 case CallingConventionAAPCS:
350 case CallingConventionAAPCSVFP:353 case CallingConventionAAPCSVFP:
354 case CallingConventionSysV:
351 return true;355 return true;
352 case CallingConventionAsync:356 case CallingConventionAsync:
353 case CallingConventionUnspecified:357 case CallingConventionUnspecified:
...@@ -9079,6 +9083,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {...@@ -9079,6 +9083,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
9079 static_assert(CallingConventionAPCS == 11, "");9083 static_assert(CallingConventionAPCS == 11, "");
9080 static_assert(CallingConventionAAPCS == 12, "");9084 static_assert(CallingConventionAAPCS == 12, "");
9081 static_assert(CallingConventionAAPCSVFP == 13, "");9085 static_assert(CallingConventionAAPCSVFP == 13, "");
9086 static_assert(CallingConventionSysV == 14, "");
90829087
9083 static_assert(BuiltinPtrSizeOne == 0, "");9088 static_assert(BuiltinPtrSizeOne == 0, "");
9084 static_assert(BuiltinPtrSizeMany == 1, "");9089 static_assert(BuiltinPtrSizeMany == 1, "");
src/stage1/ir.cpp+9-4
...@@ -19216,6 +19216,7 @@ static IrInstGen *ir_analyze_instruction_export(IrAnalyze *ira, IrInstSrcExport...@@ -19216,6 +19216,7 @@ static IrInstGen *ir_analyze_instruction_export(IrAnalyze *ira, IrInstSrcExport
19216 case CallingConventionAPCS:19216 case CallingConventionAPCS:
19217 case CallingConventionAAPCS:19217 case CallingConventionAAPCS:
19218 case CallingConventionAAPCSVFP:19218 case CallingConventionAAPCSVFP:
19219 case CallingConventionSysV:
19219 add_fn_export(ira->codegen, fn_entry, buf_ptr(symbol_name), global_linkage_id, cc);19220 add_fn_export(ira->codegen, fn_entry, buf_ptr(symbol_name), global_linkage_id, cc);
19220 fn_entry->section_name = section_name;19221 fn_entry->section_name = section_name;
19221 break;19222 break;
...@@ -20659,8 +20660,12 @@ static IrInstGen *analyze_casted_new_stack(IrAnalyze *ira, IrInst* source_instr,...@@ -20659,8 +20660,12 @@ static IrInstGen *analyze_casted_new_stack(IrAnalyze *ira, IrInst* source_instr,
20659 get_fn_frame_type(ira->codegen, fn_entry), false);20660 get_fn_frame_type(ira->codegen, fn_entry), false);
20660 return ir_implicit_cast(ira, new_stack, needed_frame_type);20661 return ir_implicit_cast(ira, new_stack, needed_frame_type);
20661 } else {20662 } 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;
20662 ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8,20667 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);
20664 ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr);20669 ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr);
20665 ira->codegen->need_frame_size_prefix_data = true;20670 ira->codegen->need_frame_size_prefix_data = true;
20666 return ir_implicit_cast2(ira, new_stack_src, new_stack, u8_slice);20671 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...@@ -26079,11 +26084,11 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
26079 fields[0]->special = ConstValSpecialStatic;26084 fields[0]->special = ConstValSpecialStatic;
26080 fields[0]->type = get_builtin_type(ira->codegen, "CallingConvention");26085 fields[0]->type = get_builtin_type(ira->codegen, "CallingConvention");
26081 bigint_init_unsigned(&fields[0]->data.x_enum_tag, type_entry->data.fn.fn_type_id.cc);26086 bigint_init_unsigned(&fields[0]->data.x_enum_tag, type_entry->data.fn.fn_type_id.cc);
26082 // alignment: u2926087 // alignment: comptime_int
26083 ensure_field_index(result->type, "alignment", 1);26088 ensure_field_index(result->type, "alignment", 1);
26084 fields[1]->special = ConstValSpecialStatic;26089 fields[1]->special = ConstValSpecialStatic;
26085 fields[1]->type = ira->codegen->builtin_types.entry_num_lit_int;26090 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));
26087 // is_generic: bool26092 // is_generic: bool
26088 ensure_field_index(result->type, "is_generic", 2);26093 ensure_field_index(result->type, "is_generic", 2);
26089 bool is_generic = type_entry->data.fn.is_generic;26094 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...@@ -30095,7 +30100,7 @@ static IrInstGen *ir_align_cast(IrAnalyze *ira, IrInstGen *target, uint32_t alig
30095 fn_type_id.alignment = align_bytes;30100 fn_type_id.alignment = align_bytes;
30096 result_type = get_fn_type(ira->codegen, &fn_type_id);30101 result_type = get_fn_type(ira->codegen, &fn_type_id);
30097 } else if (target_type->id == ZigTypeIdAnyFrame) {30102 } 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)) {
30099 result_type = target_type;30104 result_type = target_type;
30100 } else {30105 } else {
30101 ir_add_error(ira, &target->base, buf_sprintf("sub-aligned anyframe not allowed"));30106 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) {...@@ -1253,6 +1253,37 @@ bool target_is_ppc(const ZigTarget *target) {
1253 target->arch == ZigLLVM_ppc64le;1253 target->arch == ZigLLVM_ppc64le;
1254}1254}
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.
1256unsigned target_fn_align(const ZigTarget *target) {1264unsigned 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 }
1258}1289}
src/stage1/target.hpp+1
...@@ -98,6 +98,7 @@ size_t target_libc_count(void);...@@ -98,6 +98,7 @@ size_t target_libc_count(void);
98void target_libc_enum(size_t index, ZigTarget *out_target);98void target_libc_enum(size_t index, ZigTarget *out_target);
99bool target_libc_needs_crti_crtn(const ZigTarget *target);99bool target_libc_needs_crti_crtn(const ZigTarget *target);
100100
101unsigned target_fn_ptr_align(const ZigTarget *target);
101unsigned target_fn_align(const ZigTarget *target);102unsigned target_fn_align(const ZigTarget *target);
102103
103#endif104#endif
src/translate_c.zig+27-12
...@@ -8,6 +8,7 @@ const ctok = std.c.tokenizer;...@@ -8,6 +8,7 @@ const ctok = std.c.tokenizer;
8const CToken = std.c.Token;8const CToken = std.c.Token;
9const mem = std.mem;9const mem = std.mem;
10const math = std.math;10const math = std.math;
11const meta = std.meta;
11const ast = @import("translate_c/ast.zig");12const ast = @import("translate_c/ast.zig");
12const Node = ast.Node;13const Node = ast.Node;
13const Tag = Node.Tag;14const Tag = Node.Tag;
...@@ -1741,7 +1742,7 @@ fn transImplicitCastExpr(...@@ -1741,7 +1742,7 @@ fn transImplicitCastExpr(
1741}1742}
17421743
1743fn isBuiltinDefined(name: []const u8) bool {1744fn isBuiltinDefined(name: []const u8) bool {
1744 inline for (std.meta.declarations(c_builtins)) |decl| {1745 inline for (meta.declarations(c_builtins)) |decl| {
1745 if (std.mem.eql(u8, name, decl.name)) return true;1746 if (std.mem.eql(u8, name, decl.name)) return true;
1746 }1747 }
1747 return false;1748 return false;
...@@ -3157,7 +3158,7 @@ const ClangFunctionType = union(enum) {...@@ -3157,7 +3158,7 @@ const ClangFunctionType = union(enum) {
3157 NoProto: *const clang.FunctionType,3158 NoProto: *const clang.FunctionType,
31583159
3159 fn getReturnType(self: @This()) clang.QualType {3160 fn getReturnType(self: @This()) clang.QualType {
3160 switch (@as(std.meta.Tag(@This()), self)) {3161 switch (@as(meta.Tag(@This()), self)) {
3161 .Proto => return self.Proto.getReturnType(),3162 .Proto => return self.Proto.getReturnType(),
3162 .NoProto => return self.NoProto.getReturnType(),3163 .NoProto => return self.NoProto.getReturnType(),
3163 }3164 }
...@@ -3539,7 +3540,7 @@ fn transCPtrCast(...@@ -3539,7 +3540,7 @@ fn transCPtrCast(
3539 expr3540 expr
3540 else blk: {3541 else blk: {
3541 const child_type_node = try transQualType(c, scope, child_type, loc);3542 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);
3543 const align_cast = try Tag.align_cast.create(c.arena, .{ .lhs = alignof, .rhs = expr });3544 const align_cast = try Tag.align_cast.create(c.arena, .{ .lhs = alignof, .rhs = expr });
3544 break :blk align_cast;3545 break :blk align_cast;
3545 };3546 };
...@@ -3547,9 +3548,22 @@ fn transCPtrCast(...@@ -3547,9 +3548,22 @@ fn transCPtrCast(
3547 }3548 }
3548}3549}
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 }
3551 // TODO use something more accurate3565 // TODO use something more accurate
3552 var dbl = stmt.getValueAsApproximateDouble();3566 var dbl = expr.getValueAsApproximateDouble();
3553 const is_negative = dbl < 0;3567 const is_negative = dbl < 0;
3554 if (is_negative) dbl = -dbl;3568 if (is_negative) dbl = -dbl;
3555 const str = if (dbl == std.math.floor(dbl))3569 const str = if (dbl == std.math.floor(dbl))
...@@ -4093,7 +4107,7 @@ fn transCreateNodeAPInt(c: *Context, int: *const clang.APSInt) !Node {...@@ -4093,7 +4107,7 @@ fn transCreateNodeAPInt(c: *Context, int: *const clang.APSInt) !Node {
4093}4107}
40944108
4095fn transCreateNodeNumber(c: *Context, num: anytype, num_kind: enum { int, float }) !Node {4109fn 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}";
4097 const str = try std.fmt.allocPrint(c.arena, fmt_s, .{num});4111 const str = try std.fmt.allocPrint(c.arena, fmt_s, .{num});
4098 if (num_kind == .float)4112 if (num_kind == .float)
4099 return Tag.float_literal.create(c.arena, str)4113 return Tag.float_literal.create(c.arena, str)
...@@ -4402,6 +4416,7 @@ fn transCC(...@@ -4402,6 +4416,7 @@ fn transCC(
4402 .X86ThisCall => return CallingConvention.Thiscall,4416 .X86ThisCall => return CallingConvention.Thiscall,
4403 .AAPCS => return CallingConvention.AAPCS,4417 .AAPCS => return CallingConvention.AAPCS,
4404 .AAPCS_VFP => return CallingConvention.AAPCSVFP,4418 .AAPCS_VFP => return CallingConvention.AAPCSVFP,
4419 .X86_64SysV => return CallingConvention.SysV,
4405 else => return fail(4420 else => return fail(
4406 c,4421 c,
4407 error.UnsupportedType,4422 error.UnsupportedType,
...@@ -4848,12 +4863,12 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!Node {...@@ -4848,12 +4863,12 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!Node {
4848 // make the output less noisy by skipping promoteIntLiteral where4863 // make the output less noisy by skipping promoteIntLiteral where
4849 // it's guaranteed to not be required because of C standard type constraints4864 // it's guaranteed to not be required because of C standard type constraints
4850 const guaranteed_to_fit = switch (suffix) {4865 const guaranteed_to_fit = switch (suffix) {
4851 .none => if (math.cast(i16, value)) |_| true else |_| false,4866 .none => !meta.isError(math.cast(i16, value)),
4852 .u => if (math.cast(u16, value)) |_| true else |_| false,4867 .u => !meta.isError(math.cast(u16, value)),
4853 .l => if (math.cast(i32, value)) |_| true else |_| false,4868 .l => !meta.isError(math.cast(i32, value)),
4854 .lu => if (math.cast(u32, value)) |_| true else |_| false,4869 .lu => !meta.isError(math.cast(u32, value)),
4855 .ll => if (math.cast(i64, value)) |_| true else |_| false,4870 .ll => !meta.isError(math.cast(i64, value)),
4856 .llu => if (math.cast(u64, value)) |_| true else |_| false,4871 .llu => !meta.isError(math.cast(u64, value)),
4857 .f => unreachable,4872 .f => unreachable,
4858 };4873 };
48594874
src/translate_c/ast.zig+11-1
...@@ -120,8 +120,11 @@ pub const Node = extern union {...@@ -120,8 +120,11 @@ pub const Node = extern union {
120 std_math_Log2Int,120 std_math_Log2Int,
121 /// @intCast(lhs, rhs)121 /// @intCast(lhs, rhs)
122 int_cast,122 int_cast,
123 /// @rem(lhs, rhs)123 /// @import("std").meta.promoteIntLiteral(value, type, radix)
124 std_meta_promoteIntLiteral,124 std_meta_promoteIntLiteral,
125 /// @import("std").meta.alignment(value)
126 std_meta_alignment,
127 /// @rem(lhs, rhs)
125 rem,128 rem,
126 /// @divTrunc(lhs, rhs)129 /// @divTrunc(lhs, rhs)
127 div_trunc,130 div_trunc,
...@@ -260,6 +263,7 @@ pub const Node = extern union {...@@ -260,6 +263,7 @@ pub const Node = extern union {
260 .switch_else,263 .switch_else,
261 .block_single,264 .block_single,
262 .std_meta_sizeof,265 .std_meta_sizeof,
266 .std_meta_alignment,
263 .bool_to_int,267 .bool_to_int,
264 .sizeof,268 .sizeof,
265 .alignof,269 .alignof,
...@@ -876,6 +880,11 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {...@@ -876,6 +880,11 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
876 const import_node = try renderStdImport(c, "meta", "promoteIntLiteral");880 const import_node = try renderStdImport(c, "meta", "promoteIntLiteral");
877 return renderCall(c, import_node, &.{ payload.type, payload.value, payload.radix });881 return renderCall(c, import_node, &.{ payload.type, payload.value, payload.radix });
878 },882 },
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 },
879 .std_meta_sizeof => {888 .std_meta_sizeof => {
880 const payload = node.castTag(.std_meta_sizeof).?.data;889 const payload = node.castTag(.std_meta_sizeof).?.data;
881 const import_node = try renderStdImport(c, "meta", "sizeof");890 const import_node = try renderStdImport(c, "meta", "sizeof");
...@@ -2144,6 +2153,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {...@@ -2144,6 +2153,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
2144 .typeof,2153 .typeof,
2145 .typeinfo,2154 .typeinfo,
2146 .std_meta_sizeof,2155 .std_meta_sizeof,
2156 .std_meta_alignment,
2147 .std_meta_cast,2157 .std_meta_cast,
2148 .std_meta_promoteIntLiteral,2158 .std_meta_promoteIntLiteral,
2149 .std_meta_vector,2159 .std_meta_vector,
src/zig_clang.cpp+5
...@@ -2528,6 +2528,11 @@ double ZigClangFloatingLiteral_getValueAsApproximateDouble(const ZigClangFloatin...@@ -2528,6 +2528,11 @@ double ZigClangFloatingLiteral_getValueAsApproximateDouble(const ZigClangFloatin
2528 return casted->getValueAsApproximateDouble();2528 return casted->getValueAsApproximateDouble();
2529}2529}
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
2531enum ZigClangStringLiteral_StringKind ZigClangStringLiteral_getKind(const struct ZigClangStringLiteral *self) {2536enum ZigClangStringLiteral_StringKind ZigClangStringLiteral_getKind(const struct ZigClangStringLiteral *self) {
2532 auto casted = reinterpret_cast<const clang::StringLiteral *>(self);2537 auto casted = reinterpret_cast<const clang::StringLiteral *>(self);
2533 return (ZigClangStringLiteral_StringKind)casted->getKind();2538 return (ZigClangStringLiteral_StringKind)casted->getKind();
src/zig_clang.h+11
...@@ -881,6 +881,16 @@ enum ZigClangAPFloat_roundingMode {...@@ -881,6 +881,16 @@ enum ZigClangAPFloat_roundingMode {
881 ZigClangAPFloat_roundingMode_Invalid = -1,881 ZigClangAPFloat_roundingMode_Invalid = -1,
882};882};
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
884enum ZigClangStringLiteral_StringKind {894enum ZigClangStringLiteral_StringKind {
885 ZigClangStringLiteral_StringKind_Ascii,895 ZigClangStringLiteral_StringKind_Ascii,
886 ZigClangStringLiteral_StringKind_Wide,896 ZigClangStringLiteral_StringKind_Wide,
...@@ -1142,6 +1152,7 @@ ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangDeclStmt_getBeginLoc(const st...@@ -1142,6 +1152,7 @@ ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangDeclStmt_getBeginLoc(const st
1142ZIG_EXTERN_C unsigned ZigClangAPFloat_convertToHexString(const struct ZigClangAPFloat *self, char *DST,1152ZIG_EXTERN_C unsigned ZigClangAPFloat_convertToHexString(const struct ZigClangAPFloat *self, char *DST,
1143 unsigned HexDigits, bool UpperCase, enum ZigClangAPFloat_roundingMode RM);1153 unsigned HexDigits, bool UpperCase, enum ZigClangAPFloat_roundingMode RM);
1144ZIG_EXTERN_C double ZigClangFloatingLiteral_getValueAsApproximateDouble(const ZigClangFloatingLiteral *self);1154ZIG_EXTERN_C double ZigClangFloatingLiteral_getValueAsApproximateDouble(const ZigClangFloatingLiteral *self);
1155ZIG_EXTERN_C ZigClangAPFloatBase_Semantics ZigClangFloatingLiteral_getRawSemantics(const ZigClangFloatingLiteral *self);
11451156
1146ZIG_EXTERN_C enum ZigClangStringLiteral_StringKind ZigClangStringLiteral_getKind(const struct ZigClangStringLiteral *self);1157ZIG_EXTERN_C enum ZigClangStringLiteral_StringKind ZigClangStringLiteral_getKind(const struct ZigClangStringLiteral *self);
1147ZIG_EXTERN_C uint32_t ZigClangStringLiteral_getCodeUnit(const struct ZigClangStringLiteral *self, size_t i);1158ZIG_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 {...@@ -2136,7 +2136,9 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
2136 \\}2136 \\}
2137 \\fn func() callconv(.Async) void {}2137 \\fn func() callconv(.Async) void {}
2138 , &[_][]const u8{2138 , &[_][]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'",
2140 });2142 });
21412143
2142 cases.add("atomic orderings of fence Acquire or stricter",2144 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" {...@@ -306,7 +306,7 @@ test "type info: function type info" {
306fn testFunction() void {306fn testFunction() void {
307 const fn_info = @typeInfo(@TypeOf(foo));307 const fn_info = @typeInfo(@TypeOf(foo));
308 expect(fn_info == .Fn);308 expect(fn_info == .Fn);
309 expect(fn_info.Fn.alignment == 0);309 expect(fn_info.Fn.alignment > 0);
310 expect(fn_info.Fn.calling_convention == .C);310 expect(fn_info.Fn.calling_convention == .C);
311 expect(!fn_info.Fn.is_generic);311 expect(!fn_info.Fn.is_generic);
312 expect(fn_info.Fn.args.len == 2);312 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{...@@ -11,11 +11,6 @@ const linux_x64 = std.zig.CrossTarget{
11 .os_tag = .linux,11 .os_tag = .linux,
12};12};
1313
14const linux_riscv64 = std.zig.CrossTarget{
15 .cpu_arch = .riscv64,
16 .os_tag = .linux,
17};
18
19pub fn addCases(ctx: *TestContext) !void {14pub fn addCases(ctx: *TestContext) !void {
20 try @import("cbe.zig").addCases(ctx);15 try @import("cbe.zig").addCases(ctx);
21 try @import("spu-ii.zig").addCases(ctx);16 try @import("spu-ii.zig").addCases(ctx);
...@@ -24,6 +19,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -24,6 +19,7 @@ pub fn addCases(ctx: *TestContext) !void {
24 try @import("llvm.zig").addCases(ctx);19 try @import("llvm.zig").addCases(ctx);
25 try @import("wasm.zig").addCases(ctx);20 try @import("wasm.zig").addCases(ctx);
26 try @import("darwin.zig").addCases(ctx);21 try @import("darwin.zig").addCases(ctx);
22 try @import("riscv64.zig").addCases(ctx);
2723
28 {24 {
29 var case = ctx.exe("hello world with updates", linux_x64);25 var case = ctx.exe("hello world with updates", linux_x64);
...@@ -137,42 +133,6 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -137,42 +133,6 @@ pub fn addCases(ctx: *TestContext) !void {
137 );133 );
138 }134 }
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
176 {136 {
177 var case = ctx.exe("adding numbers at comptime", linux_x64);137 var case = ctx.exe("adding numbers at comptime", linux_x64);
178 case.addCompareOutput(138 case.addCompareOutput(
test/translate_c.zig+9-11
...@@ -1363,7 +1363,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1363,7 +1363,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1363 , &[_][]const u8{1363 , &[_][]const u8{
1364 \\pub export fn ptrcast() [*c]f32 {1364 \\pub export fn ptrcast() [*c]f32 {
1365 \\ var a: [*c]c_int = undefined;1365 \\ 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));
1367 \\}1367 \\}
1368 });1368 });
13691369
...@@ -1387,16 +1387,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1387,16 +1387,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1387 \\pub export fn test_ptr_cast() void {1387 \\pub export fn test_ptr_cast() void {
1388 \\ var p: ?*c_void = undefined;1388 \\ var p: ?*c_void = undefined;
1389 \\ {1389 \\ {
1390 \\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@alignOf(u8), 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(@alignOf(c_short), 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(@alignOf(c_int), 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(@alignOf(c_longlong), p));1393 \\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@import("std").meta.alignment(c_longlong), p));
1394 \\ }1394 \\ }
1395 \\ {1395 \\ {
1396 \\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@alignOf(u8), 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(@alignOf(c_short), 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(@alignOf(c_int), 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(@alignOf(c_longlong), p));1399 \\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@import("std").meta.alignment(c_longlong), p));
1400 \\ }1400 \\ }
1401 \\}1401 \\}
1402 });1402 });
...@@ -3028,7 +3028,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -3028,7 +3028,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
3028 \\void call() {3028 \\void call() {
3029 \\ fn_int(3.0f);3029 \\ fn_int(3.0f);
3030 \\ fn_int(3.0);3030 \\ fn_int(3.0);
3031 \\ fn_int(3.0L);
3032 \\ fn_int('ABCD');3031 \\ fn_int('ABCD');
3033 \\ fn_f32(3);3032 \\ fn_f32(3);
3034 \\ fn_f64(3);3033 \\ fn_f64(3);
...@@ -3053,7 +3052,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -3053,7 +3052,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
3053 \\pub export fn call() void {3052 \\pub export fn call() void {
3054 \\ fn_int(@floatToInt(c_int, 3.0));3053 \\ fn_int(@floatToInt(c_int, 3.0));
3055 \\ fn_int(@floatToInt(c_int, 3.0));3054 \\ fn_int(@floatToInt(c_int, 3.0));
3056 \\ fn_int(@floatToInt(c_int, 3.0));
3057 \\ fn_int(@as(c_int, 1094861636));3055 \\ fn_int(@as(c_int, 1094861636));
3058 \\ fn_f32(@intToFloat(f32, @as(c_int, 3)));3056 \\ fn_f32(@intToFloat(f32, @as(c_int, 3)));
3059 \\ fn_f64(@intToFloat(f64, @as(c_int, 3)));3057 \\ fn_f64(@intToFloat(f64, @as(c_int, 3)));