| author | |
| committer | |
| log | df24ce52b1059232db66e9af425a02df558aa1ca |
| tree | bf314eace4c7acd533c5c9532b330ecd8ae65686 |
| parent | eb9c29eb817a24e5326b9a63ebf7265d3b2bad2c |
| parent | 55c58f226d06d3708a82878a67f3800e0ce5810b |
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 | 44 | |
| 45 | 45 | const fmt_build_zig = b.addFmt(&[_][]const u8{"build.zig"}); |
| 46 | 46 | |
| 47 | const skip_debug = b.option(bool, "skip-debug", "Main test suite skips debug builds") orelse false; | |
| 47 | 48 | const skip_release = b.option(bool, "skip-release", "Main test suite skips release builds") orelse false; |
| 48 | 49 | const skip_release_small = b.option(bool, "skip-release-small", "Main test suite skips release-small builds") orelse skip_release; |
| 49 | 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 | 241 | |
| 241 | 242 | var chosen_modes: [4]builtin.Mode = undefined; |
| 242 | 243 | var chosen_mode_index: usize = 0; |
| 243 | chosen_modes[chosen_mode_index] = builtin.Mode.Debug; | |
| 244 | chosen_mode_index += 1; | |
| 244 | if (!skip_debug) { | |
| 245 | chosen_modes[chosen_mode_index] = builtin.Mode.Debug; | |
| 246 | chosen_mode_index += 1; | |
| 247 | } | |
| 245 | 248 | if (!skip_release_safe) { |
| 246 | 249 | chosen_modes[chosen_mode_index] = builtin.Mode.ReleaseSafe; |
| 247 | 250 | chosen_mode_index += 1; |
ci/drone/drone.yml+33-2| ... | ... | @@ -6,7 +6,38 @@ platform: |
| 6 | 6 | arch: arm64 |
| 7 | 7 | |
| 8 | 8 | steps: |
| 9 | - name: build-and-test | |
| 9 | - name: build | |
| 10 | image: ziglang/static-base:llvm12-aarch64-1 | |
| 11 | commands: | |
| 12 | - ./ci/drone/linux_script_build | |
| 13 | ||
| 14 | - name: test-1 | |
| 15 | depends_on: | |
| 16 | - build | |
| 17 | image: ziglang/static-base:llvm12-aarch64-1 | |
| 18 | commands: | |
| 19 | - ./ci/drone/linux_script_test 1 | |
| 20 | ||
| 21 | - name: test-2 | |
| 22 | depends_on: | |
| 23 | - build | |
| 24 | image: ziglang/static-base:llvm12-aarch64-1 | |
| 25 | commands: | |
| 26 | - ./ci/drone/linux_script_test 2 | |
| 27 | ||
| 28 | - name: test-3 | |
| 29 | depends_on: | |
| 30 | - build | |
| 31 | image: ziglang/static-base:llvm12-aarch64-1 | |
| 32 | commands: | |
| 33 | - ./ci/drone/linux_script_test 3 | |
| 34 | ||
| 35 | - name: finalize | |
| 36 | depends_on: | |
| 37 | - build | |
| 38 | - test-1 | |
| 39 | - test-2 | |
| 40 | - test-3 | |
| 10 | 41 | image: ziglang/static-base:llvm12-aarch64-1 |
| 11 | 42 | environment: |
| 12 | 43 | SRHT_OAUTH_TOKEN: |
| ... | ... | @@ -16,4 +47,4 @@ steps: |
| 16 | 47 | AWS_SECRET_ACCESS_KEY: |
| 17 | 48 | from_secret: AWS_SECRET_ACCESS_KEY |
| 18 | 49 | commands: |
| 19 | - ./ci/drone/linux_script | |
| 50 | - ./ci/drone/linux_script_finalize |
ci/drone/linux_script deleted-65| ... | ... | @@ -1,65 +0,0 @@ |
| 1 | #!/bin/sh | |
| 2 | ||
| 3 | set -x | |
| 4 | set -e | |
| 5 | ||
| 6 | TRIPLEARCH="$(uname -m)" | |
| 7 | BUILDDIR="$(pwd)" | |
| 8 | DISTDIR="$(pwd)/dist" | |
| 9 | ||
| 10 | apk update | |
| 11 | apk add py3-pip xz perl-utils jq curl samurai | |
| 12 | pip3 install s3cmd | |
| 13 | ||
| 14 | # Make the `zig version` number consistent. | |
| 15 | # This will affect the cmake command below. | |
| 16 | git config core.abbrev 9 | |
| 17 | git fetch --unshallow || true | |
| 18 | git fetch --tags | |
| 19 | ||
| 20 | mkdir build | |
| 21 | cd build | |
| 22 | cmake .. -DCMAKE_BUILD_TYPE=Release "-DCMAKE_INSTALL_PREFIX=$DISTDIR" -DZIG_STATIC=ON -DCMAKE_PREFIX_PATH=/deps/local -GNinja | |
| 23 | ||
| 24 | samu 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 | ||
| 32 | if [ -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 | |
| 65 | fi |
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 | ||
| 16 | set -x | |
| 17 | set -e | |
| 18 | ||
| 19 | TRIPLEARCH="$(uname -m)" | |
| 20 | DISTDIR="$DRONE_WORKSPACE/dist" | |
| 21 | ||
| 22 | export 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 | ||
| 5 | apk update | |
| 6 | apk add samurai | |
| 7 | ||
| 8 | # Make the `zig version` number consistent. | |
| 9 | # This will affect the cmake command below. | |
| 10 | git config core.abbrev 9 | |
| 11 | git fetch --unshallow || true | |
| 12 | git fetch --tags | |
| 13 | ||
| 14 | mkdir build | |
| 15 | cd build | |
| 16 | cmake .. -DCMAKE_BUILD_TYPE=Release "-DCMAKE_INSTALL_PREFIX=$DISTDIR" -DZIG_STATIC=ON -DCMAKE_PREFIX_PATH=/deps/local -GNinja | |
| 17 | ||
| 18 | samu install |
ci/drone/linux_script_finalize created+46| ... | ... | @@ -0,0 +1,46 @@ |
| 1 | #!/bin/sh | |
| 2 | ||
| 3 | . ./ci/drone/linux_script_base | |
| 4 | ||
| 5 | if [ -n "$DRONE_PULL_REQUEST" ]; then | |
| 6 | exit 0 | |
| 7 | fi | |
| 8 | ||
| 9 | apk update | |
| 10 | apk add py3-pip xz perl-utils jq curl samurai | |
| 11 | pip3 install s3cmd | |
| 12 | ||
| 13 | cd build | |
| 14 | ||
| 15 | mv ../LICENSE "$DISTDIR/" | |
| 16 | # docs are disabled due to: https://github.com/ziglang/zig/issues/8597 | |
| 17 | #mv ../zig-cache/langref.html "$DISTDIR/" | |
| 18 | mv "$DISTDIR/bin/zig" "$DISTDIR/" | |
| 19 | rmdir "$DISTDIR/bin" | |
| 20 | ||
| 21 | GITBRANCH="$DRONE_BRANCH" | |
| 22 | VERSION="$("$DISTDIR/zig" version)" | |
| 23 | DIRNAME="zig-linux-$TRIPLEARCH-$VERSION" | |
| 24 | TARBALL="$DIRNAME.tar.xz" | |
| 25 | mv "$DISTDIR" "$DIRNAME" | |
| 26 | tar cfJ "$TARBALL" "$DIRNAME" | |
| 27 | ||
| 28 | s3cmd put -P --add-header="cache-control: public, max-age=31536000, immutable" "$TARBALL" s3://ziglang.org/builds/ | |
| 29 | ||
| 30 | SHASUM=$(shasum -a 256 $TARBALL | cut '-d ' -f1) | |
| 31 | BYTESIZE=$(wc -c < $TARBALL) | |
| 32 | ||
| 33 | JSONFILE="tarball.json" | |
| 34 | touch $JSONFILE | |
| 35 | echo "{\"tarball\": \"$TARBALL\"," >>$JSONFILE | |
| 36 | echo "\"shasum\": \"$SHASUM\"," >>$JSONFILE | |
| 37 | echo "\"size\": \"$BYTESIZE\"}" >>$JSONFILE | |
| 38 | ||
| 39 | s3cmd put -P "$JSONFILE" "s3://ziglang.org/builds/$TRIPLEARCH-linux-$VERSION.json" | |
| 40 | if [ "$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" | |
| 46 | fi |
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 | |
| 11 | case "$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 | ;; | |
| 37 | esac | |
| 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 | 199 | inner: Context, |
| 200 | 200 | }; |
| 201 | 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)).*; | |
| 203 | 204 | |
| 204 | 205 | switch (@typeInfo(@typeInfo(@TypeOf(startFn)).Fn.return_type.?)) { |
| 205 | 206 | .NoReturn => { |
| ... | ... | @@ -260,7 +261,8 @@ pub fn spawn(comptime startFn: anytype, context: SpawnContextType(@TypeOf(startF |
| 260 | 261 | |
| 261 | 262 | const MainFuncs = struct { |
| 262 | 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).*; | |
| 264 | 266 | |
| 265 | 267 | switch (@typeInfo(@typeInfo(@TypeOf(startFn)).Fn.return_type.?)) { |
| 266 | 268 | .NoReturn => { |
| ... | ... | @@ -292,7 +294,8 @@ pub fn spawn(comptime startFn: anytype, context: SpawnContextType(@TypeOf(startF |
| 292 | 294 | } |
| 293 | 295 | } |
| 294 | 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)).*; | |
| 296 | 299 | |
| 297 | 300 | switch (@typeInfo(@typeInfo(@TypeOf(startFn)).Fn.return_type.?)) { |
| 298 | 301 | .NoReturn => { |
lib/std/builtin.zig+1| ... | ... | @@ -166,6 +166,7 @@ pub const CallingConvention = enum { |
| 166 | 166 | APCS, |
| 167 | 167 | AAPCS, |
| 168 | 168 | AAPCSVFP, |
| 169 | SysV | |
| 169 | 170 | }; |
| 170 | 171 | |
| 171 | 172 | /// 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 | 69 | writer_count: i32 = 0, |
| 70 | 70 | waiters: [2]?*c_void = [_]?*c_void{ null, null }, |
| 71 | 71 | }; |
| 72 | ||
| 73 | pub 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 | ||
| 119 | pub const EAI_MAX = 15; |
lib/std/child_process.zig+1-1| ... | ... | @@ -264,7 +264,7 @@ pub const ChildProcess = struct { |
| 264 | 264 | |
| 265 | 265 | // TODO collect output in a deadlock-avoiding way on Windows. |
| 266 | 266 | // 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 | 268 | const stdout_in = child.stdout.?.reader(); |
| 269 | 269 | const stderr_in = child.stderr.?.reader(); |
| 270 | 270 |
lib/std/crypto/25519/edwards25519.zig+9-16| ... | ... | @@ -75,16 +75,8 @@ pub const Edwards25519 = struct { |
| 75 | 75 | .is_base = true, |
| 76 | 76 | }; |
| 77 | 77 | |
| 78 | /// The edwards25519 neutral element. | |
| 79 | pub const neutralElement = Edwards25519{ | |
| 80 | .x = Fe{ .limbs = .{ 2251799813685229, 2251799813685247, 2251799813685247, 2251799813685247, 2251799813685247 } }, | |
| 81 | .y = Fe{ .limbs = .{ 1507481815385608, 2223447444246085, 1083941587175919, 2059929906842505, 1581435440146976 } }, | |
| 82 | .z = Fe{ .limbs = .{ 1507481815385608, 2223447444246085, 1083941587175919, 2059929906842505, 1581435440146976 } }, | |
| 83 | .t = Fe{ .limbs = .{ 2251799813685229, 2251799813685247, 2251799813685247, 2251799813685247, 2251799813685247 } }, | |
| 84 | .is_base = false, | |
| 85 | }; | |
| 86 | ||
| 87 | const identityElement = Edwards25519{ .x = Fe.zero, .y = Fe.one, .z = Fe.one, .t = Fe.zero }; | |
| 78 | pub const neutralElement = @compileError("deprecated: use identityElement instead"); | |
| 79 | pub const identityElement = Edwards25519{ .x = Fe.zero, .y = Fe.one, .z = Fe.one, .t = Fe.zero }; | |
| 88 | 80 | |
| 89 | 81 | /// Reject the neutral element. |
| 90 | 82 | pub fn rejectIdentity(p: Edwards25519) IdentityElementError!void { |
| ... | ... | @@ -160,9 +152,10 @@ pub const Edwards25519 = struct { |
| 160 | 152 | return t; |
| 161 | 153 | } |
| 162 | 154 | |
| 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 | 157 | var e: [2 * 32]i8 = undefined; |
| 165 | for (s) |x, i| { | |
| 158 | for (reduced) |x, i| { | |
| 166 | 159 | e[i * 2 + 0] = @as(i8, @truncate(u4, x)); |
| 167 | 160 | e[i * 2 + 1] = @as(i8, @truncate(u4, x >> 4)); |
| 168 | 161 | } |
| ... | ... | @@ -185,7 +178,7 @@ pub const Edwards25519 = struct { |
| 185 | 178 | // avoid these to keep the standard library lightweight. |
| 186 | 179 | fn pcMul(pc: [9]Edwards25519, s: [32]u8, comptime vartime: bool) IdentityElementError!Edwards25519 { |
| 187 | 180 | std.debug.assert(vartime); |
| 188 | const e = nonAdjacentForm(s); | |
| 181 | const e = slide(s); | |
| 189 | 182 | var q = Edwards25519.identityElement; |
| 190 | 183 | var pos: usize = 2 * 32 - 1; |
| 191 | 184 | while (true) : (pos -= 1) { |
| ... | ... | @@ -280,8 +273,8 @@ pub const Edwards25519 = struct { |
| 280 | 273 | xpc[4].rejectIdentity() catch return error.WeakPublicKey; |
| 281 | 274 | break :pc xpc; |
| 282 | 275 | }; |
| 283 | const e1 = nonAdjacentForm(s1); | |
| 284 | const e2 = nonAdjacentForm(s2); | |
| 276 | const e1 = slide(s1); | |
| 277 | const e2 = slide(s2); | |
| 285 | 278 | var q = Edwards25519.identityElement; |
| 286 | 279 | var pos: usize = 2 * 32 - 1; |
| 287 | 280 | while (true) : (pos -= 1) { |
| ... | ... | @@ -318,7 +311,7 @@ pub const Edwards25519 = struct { |
| 318 | 311 | } |
| 319 | 312 | var es: [count][2 * 32]i8 = undefined; |
| 320 | 313 | for (ss) |s, i| { |
| 321 | es[i] = nonAdjacentForm(s); | |
| 314 | es[i] = slide(s); | |
| 322 | 315 | } |
| 323 | 316 | var q = Edwards25519.identityElement; |
| 324 | 317 | var pos: usize = 2 * 32 - 1; |
lib/std/crypto/25519/field.zig+1-1| ... | ... | @@ -355,7 +355,7 @@ pub const Fe = struct { |
| 355 | 355 | return fe; |
| 356 | 356 | } |
| 357 | 357 | |
| 358 | /// Compute the inverse of a field element | |
| 358 | /// Return the inverse of a field element, or 0 if a=0. | |
| 359 | 359 | pub fn invert(a: Fe) Fe { |
| 360 | 360 | var t0 = a.sq(); |
| 361 | 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 | 98 | return add(a, neg(b)); |
| 99 | 99 | } |
| 100 | 100 | |
| 101 | /// A scalar in unpacked reprentation | |
| 101 | /// A scalar in unpacked representation | |
| 102 | 102 | pub const Scalar = struct { |
| 103 | 103 | const Limbs = [5]u64; |
| 104 | 104 | limbs: Limbs = undefined, |
lib/std/crypto/utils.zig+52| ... | ... | @@ -1,7 +1,11 @@ |
| 1 | 1 | const std = @import("../std.zig"); |
| 2 | const debug = std.debug; | |
| 2 | 3 | const mem = std.mem; |
| 3 | 4 | const testing = std.testing; |
| 4 | 5 | |
| 6 | const Endian = std.builtin.Endian; | |
| 7 | const Order = std.math.Order; | |
| 8 | ||
| 5 | 9 | /// Compares two arrays in constant time (for a given length) and returns whether they are equal. |
| 6 | 10 | /// This function was designed to compare short cryptographic secrets (MACs, signatures). |
| 7 | 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 | 42 | } |
| 39 | 43 | } |
| 40 | 44 | |
| 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 | |
| 47 | pub 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 | 80 | /// Sets a slice to zeroes. |
| 42 | 81 | /// Prevents the store from being optimized out. |
| 43 | 82 | pub fn secureZero(comptime T: type, s: []T) void { |
| ... | ... | @@ -70,6 +109,19 @@ test "crypto.utils.timingSafeEql (vectors)" { |
| 70 | 109 | testing.expect(timingSafeEql(std.meta.Vector(100, u8), v1, v3)); |
| 71 | 110 | } |
| 72 | 111 | |
| 112 | test "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 | ||
| 73 | 125 | test "crypto.utils.secureZero" { |
| 74 | 126 | var a = [_]u8{0xfe} ** 8; |
| 75 | 127 | var b = [_]u8{0xfe} ** 8; |
lib/std/debug.zig+11-4| ... | ... | @@ -339,6 +339,13 @@ pub const StackIterator = struct { |
| 339 | 339 | fp: usize, |
| 340 | 340 | |
| 341 | 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 | 349 | return StackIterator{ |
| 343 | 350 | .first_address = first_address, |
| 344 | 351 | .fp = fp orelse @frameAddress(), |
| ... | ... | @@ -346,18 +353,18 @@ pub const StackIterator = struct { |
| 346 | 353 | } |
| 347 | 354 | |
| 348 | 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 | 357 | // On RISC-V the frame pointer points to the top of the saved register |
| 351 | 358 | // area, on pretty much every other architecture it points to the stack |
| 352 | 359 | // slot where the previous frame pointer is saved. |
| 353 | 360 | 2 * @sizeOf(usize) |
| 354 | else if (native_arch.isSPARC()) | |
| 361 | else if (comptime native_arch.isSPARC()) | |
| 355 | 362 | // On SPARC the previous frame pointer is stored at 14 slots past %fp+BIAS. |
| 356 | 363 | 14 * @sizeOf(usize) |
| 357 | 364 | else |
| 358 | 365 | 0; |
| 359 | 366 | |
| 360 | const fp_bias = if (native_arch.isSPARC()) | |
| 367 | const fp_bias = if (comptime native_arch.isSPARC()) | |
| 361 | 368 | // On SPARC frame pointers are biased by a constant. |
| 362 | 369 | 2047 |
| 363 | 370 | else |
| ... | ... | @@ -383,7 +390,7 @@ pub const StackIterator = struct { |
| 383 | 390 | } |
| 384 | 391 | |
| 385 | 392 | fn next_internal(self: *StackIterator) ?usize { |
| 386 | const fp = if (native_arch.isSPARC()) | |
| 393 | const fp = if (comptime native_arch.isSPARC()) | |
| 387 | 394 | // On SPARC the offset is positive. (!) |
| 388 | 395 | math.add(usize, self.fp, fp_offset) catch return null |
| 389 | 396 | else |
lib/std/fmt.zig+3-1| ... | ... | @@ -1523,9 +1523,11 @@ test "parseUnsigned" { |
| 1523 | 1523 | } |
| 1524 | 1524 | |
| 1525 | 1525 | pub const parseFloat = @import("fmt/parse_float.zig").parseFloat; |
| 1526 | pub const parseHexFloat = @import("fmt/parse_hex_float.zig").parseHexFloat; | |
| 1526 | 1527 | |
| 1527 | test "parseFloat" { | |
| 1528 | test { | |
| 1528 | 1529 | _ = @import("fmt/parse_float.zig"); |
| 1530 | _ = @import("fmt/parse_hex_float.zig"); | |
| 1529 | 1531 | } |
| 1530 | 1532 | |
| 1531 | 1533 | pub 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 | ||
| 10 | const std = @import("std"); | |
| 11 | const ascii = std.ascii; | |
| 12 | const fmt = std.fmt; | |
| 13 | const math = std.math; | |
| 14 | const testing = std.testing; | |
| 15 | ||
| 16 | const assert = std.debug.assert; | |
| 17 | ||
| 18 | pub 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 | ||
| 249 | test "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 | } | |
| 255 | test "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 | ||
| 263 | test "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 | } | |
| 285 | test "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 | } | |
| 309 | test "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 | } | |
| 331 | test "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 | 29 | fn err(s: []const u8) void { |
| 30 | 30 | testing.expect(!json.validate(s)); |
| 31 | 31 | |
| 32 | testNonStreaming(s) catch return; | |
| 33 | testing.expect(false); | |
| 32 | testing.expect(std.meta.isError(testNonStreaming(s))); | |
| 34 | 33 | } |
| 35 | 34 | |
| 36 | 35 | fn utf8Error(s: []const u8) void { |
| ... | ... | @@ -48,8 +47,7 @@ fn any(s: []const u8) void { |
| 48 | 47 | fn anyStreamingErrNonStreaming(s: []const u8) void { |
| 49 | 48 | _ = json.validate(s); |
| 50 | 49 | |
| 51 | testNonStreaming(s) catch return; | |
| 52 | testing.expect(false); | |
| 50 | testing.expect(std.meta.isError(testNonStreaming(s))); | |
| 53 | 51 | } |
| 54 | 52 | |
| 55 | 53 | fn roundTrip(s: []const u8) !void { |
lib/std/mem.zig+40-4| ... | ... | @@ -1877,7 +1877,11 @@ test "rotate" { |
| 1877 | 1877 | |
| 1878 | 1878 | /// Replace needle with replacement as many times as possible, writing to an output buffer which is assumed to be of |
| 1879 | 1879 | /// appropriate size. Use replacementSize to calculate an appropriate buffer size. |
| 1880 | /// The needle must not be empty. | |
| 1880 | 1881 | pub 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 | 1885 | var i: usize = 0; |
| 1882 | 1886 | var slide: usize = 0; |
| 1883 | 1887 | var replacements: usize = 0; |
| ... | ... | @@ -1900,22 +1904,48 @@ pub fn replace(comptime T: type, input: []const T, needle: []const T, replacemen |
| 1900 | 1904 | test "replace" { |
| 1901 | 1905 | var output: [29]u8 = undefined; |
| 1902 | 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 | 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]); | |
| 1905 | 1910 | |
| 1906 | 1911 | replacements = replace(u8, "Favor reading code over writing code.", "code", "", output[0..]); |
| 1912 | expected = "Favor reading over writing ."; | |
| 1907 | 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 | } |
| 1910 | 1934 | |
| 1911 | 1935 | /// Calculate the size needed in an output buffer to perform a replacement. |
| 1936 | /// The needle must not be empty. | |
| 1912 | 1937 | pub 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 | 1941 | var i: usize = 0; |
| 1914 | 1942 | var size: usize = input.len; |
| 1915 | while (i < input.len) : (i += 1) { | |
| 1943 | while (i < input.len) { | |
| 1916 | 1944 | if (mem.indexOf(T, input[i..], needle) == @as(usize, 0)) { |
| 1917 | 1945 | size = size - needle.len + replacement.len; |
| 1918 | 1946 | i += needle.len; |
| 1947 | } else { | |
| 1948 | i += 1; | |
| 1919 | 1949 | } |
| 1920 | 1950 | } |
| 1921 | 1951 | |
| ... | ... | @@ -1924,9 +1954,15 @@ pub fn replacementSize(comptime T: type, input: []const T, needle: []const T, re |
| 1924 | 1954 | |
| 1925 | 1955 | test "replacementSize" { |
| 1926 | 1956 | testing.expect(replacementSize(u8, "All your base are belong to us", "base", "Zig") == 29); |
| 1927 | testing.expect(replacementSize(u8, "", "", "") == 0); | |
| 1928 | 1957 | testing.expect(replacementSize(u8, "Favor reading code over writing code.", "code", "") == 29); |
| 1929 | 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 | } |
| 1931 | 1967 | |
| 1932 | 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 | 117 | testing.expect(bitCount(f32) == 32); |
| 118 | 118 | } |
| 119 | 119 | |
| 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. | |
| 120 | 125 | pub fn alignment(comptime T: type) comptime_int { |
| 121 | //@alignOf works on non-pointer types | |
| 122 | const P = if (comptime trait.is(.Pointer)(T)) T else *T; | |
| 123 | return @typeInfo(P).Pointer.alignment; | |
| 126 | return switch (@typeInfo(T)) { | |
| 127 | .Optional => |info| switch (@typeInfo(info.child)) { | |
| 128 | .Pointer, .Fn => alignment(info.child), | |
| 129 | else => @alignOf(T), | |
| 130 | }, | |
| 131 | .Pointer => |info| info.alignment, | |
| 132 | .Fn => |info| info.alignment, | |
| 133 | else => @alignOf(T), | |
| 134 | }; | |
| 124 | 135 | } |
| 125 | 136 | |
| 126 | 137 | test "std.meta.alignment" { |
| ... | ... | @@ -129,6 +140,8 @@ test "std.meta.alignment" { |
| 129 | 140 | testing.expect(alignment(*align(2) u8) == 2); |
| 130 | 141 | testing.expect(alignment([]align(1) u8) == 1); |
| 131 | 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 | } |
| 133 | 146 | |
| 134 | 147 | pub fn Child(comptime T: type) type { |
| ... | ... | @@ -1342,3 +1355,13 @@ test "shuffleVectorIndex" { |
| 1342 | 1355 | testing.expect(shuffleVectorIndex(6, vector_len) == -3); |
| 1343 | 1356 | testing.expect(shuffleVectorIndex(7, vector_len) == -4); |
| 1344 | 1357 | } |
| 1358 | ||
| 1359 | /// Returns whether `error_union` contains an error. | |
| 1360 | pub fn isError(error_union: anytype) bool { | |
| 1361 | return if (error_union) |_| false else |_| true; | |
| 1362 | } | |
| 1363 | ||
| 1364 | test "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 | 279 | pub const PROT_WRITE = 2; |
| 280 | 280 | pub const PROT_EXEC = 4; |
| 281 | 281 | |
| 282 | pub const CLOCK_REALTIME = 0; | |
| 283 | pub const CLOCK_VIRTUAL = 1; | |
| 284 | pub const CLOCK_PROF = 2; | |
| 285 | pub const CLOCK_MONOTONIC = 4; | |
| 286 | pub const CLOCK_UPTIME = 5; | |
| 287 | pub const CLOCK_UPTIME_PRECISE = 7; | |
| 288 | pub const CLOCK_UPTIME_FAST = 8; | |
| 289 | pub const CLOCK_REALTIME_PRECISE = 9; | |
| 290 | pub const CLOCK_REALTIME_FAST = 10; | |
| 291 | pub const CLOCK_MONOTONIC_PRECISE = 11; | |
| 292 | pub const CLOCK_MONOTONIC_FAST = 12; | |
| 293 | pub const CLOCK_SECOND = 13; | |
| 294 | pub const CLOCK_THREAD_CPUTIME_ID = 14; | |
| 295 | pub const CLOCK_PROCESS_CPUTIME_ID = 15; | |
| 282 | pub const CLOCK_MONOTONIC = 0; | |
| 283 | pub const CLOCK_REALTIME = -1; | |
| 284 | pub const CLOCK_PROCESS_CPUTIME_ID = -2; | |
| 285 | pub const CLOCK_THREAD_CPUTIME_ID = -3; | |
| 296 | 286 | |
| 297 | 287 | pub const MAP_FAILED = @intToPtr(*c_void, maxInt(usize)); |
| 298 | 288 | pub const MAP_SHARED = 0x0001; |
| ... | ... | @@ -310,58 +300,59 @@ pub const MAP_NOCORE = 0x00020000; |
| 310 | 300 | pub const MAP_PREFAULT_READ = 0x00040000; |
| 311 | 301 | pub const MAP_32BIT = 0x00080000; |
| 312 | 302 | |
| 313 | pub const WNOHANG = 1; | |
| 314 | pub const WUNTRACED = 2; | |
| 315 | pub const WSTOPPED = WUNTRACED; | |
| 316 | pub const WCONTINUED = 4; | |
| 317 | pub const WNOWAIT = 8; | |
| 318 | pub const WEXITED = 16; | |
| 319 | pub const WTRAPPED = 32; | |
| 320 | ||
| 321 | pub const SA_ONSTACK = 0x0001; | |
| 322 | pub const SA_RESTART = 0x0002; | |
| 323 | pub const SA_RESETHAND = 0x0004; | |
| 324 | pub const SA_NOCLDSTOP = 0x0008; | |
| 325 | pub const SA_NODEFER = 0x0010; | |
| 326 | pub const SA_NOCLDWAIT = 0x0020; | |
| 327 | pub const SA_SIGINFO = 0x0040; | |
| 303 | pub const WNOHANG = 0x1; | |
| 304 | pub const WUNTRACED = 0x2; | |
| 305 | pub const WSTOPPED = 0x10; | |
| 306 | pub const WCONTINUED = 0x4; | |
| 307 | pub const WNOWAIT = 0x20; | |
| 308 | pub const WEXITED = 0x08; | |
| 309 | ||
| 310 | pub const SA_ONSTACK = 0x20; | |
| 311 | pub const SA_RESTART = 0x10; | |
| 312 | pub const SA_RESETHAND = 0x04; | |
| 313 | pub const SA_NOCLDSTOP = 0x01; | |
| 314 | pub const SA_NODEFER = 0x08; | |
| 315 | pub const SA_NOCLDWAIT = 0x02; | |
| 316 | pub const SA_SIGINFO = 0x40; | |
| 317 | pub const SA_NOMASK = SA_NODEFER; | |
| 318 | pub const SA_STACK = SA_ONSTACK; | |
| 319 | pub const SA_ONESHOT = SA_RESETHAND; | |
| 328 | 320 | |
| 329 | 321 | pub const SIGHUP = 1; |
| 330 | 322 | pub const SIGINT = 2; |
| 331 | 323 | pub const SIGQUIT = 3; |
| 332 | 324 | pub const SIGILL = 4; |
| 333 | pub const SIGTRAP = 5; | |
| 325 | pub const SIGCHLD = 5; | |
| 334 | 326 | pub const SIGABRT = 6; |
| 335 | 327 | pub const SIGIOT = SIGABRT; |
| 336 | pub const SIGEMT = 7; | |
| 328 | pub const SIGPIPE = 7; | |
| 337 | 329 | pub const SIGFPE = 8; |
| 338 | 330 | pub const SIGKILL = 9; |
| 339 | pub const SIGBUS = 10; | |
| 331 | pub const SIGSTOP = 10; | |
| 340 | 332 | pub const SIGSEGV = 11; |
| 341 | pub const SIGSYS = 12; | |
| 342 | pub const SIGPIPE = 13; | |
| 333 | pub const SIGCONT = 12; | |
| 334 | pub const SIGTSTP = 13; | |
| 343 | 335 | pub const SIGALRM = 14; |
| 344 | 336 | pub const SIGTERM = 15; |
| 345 | pub const SIGURG = 16; | |
| 346 | pub const SIGSTOP = 17; | |
| 347 | pub const SIGTSTP = 18; | |
| 348 | pub const SIGCONT = 19; | |
| 349 | pub const SIGCHLD = 20; | |
| 350 | pub const SIGTTIN = 21; | |
| 351 | pub const SIGTTOU = 22; | |
| 352 | pub const SIGIO = 23; | |
| 353 | pub const SIGXCPU = 24; | |
| 354 | pub const SIGXFSZ = 25; | |
| 355 | pub const SIGVTALRM = 26; | |
| 356 | pub const SIGPROF = 27; | |
| 357 | pub const SIGWINCH = 28; | |
| 358 | pub const SIGINFO = 29; | |
| 359 | pub const SIGUSR1 = 30; | |
| 360 | pub const SIGUSR2 = 31; | |
| 361 | pub const SIGTHR = 32; | |
| 362 | pub const SIGLWP = SIGTHR; | |
| 363 | pub const SIGLIBRT = 33; | |
| 364 | ||
| 337 | pub const SIGTTIN = 16; | |
| 338 | pub const SIGTTOU = 17; | |
| 339 | pub const SIGUSR1 = 18; | |
| 340 | pub const SIGUSR2 = 19; | |
| 341 | pub const SIGWINCH = 20; | |
| 342 | pub const SIGKILLTHR = 21; | |
| 343 | pub const SIGTRAP = 22; | |
| 344 | pub const SIGPOLL = 23; | |
| 345 | pub const SIGPROF = 24; | |
| 346 | pub const SIGSYS = 25; | |
| 347 | pub const SIGURG = 26; | |
| 348 | pub const SIGVTALRM = 27; | |
| 349 | pub const SIGXCPU = 28; | |
| 350 | pub const SIGXFSZ = 29; | |
| 351 | pub const SIGBUS = 30; | |
| 352 | pub const SIGRESERVED1 = 31; | |
| 353 | pub const SIGRESERVED2 = 32; | |
| 354 | ||
| 355 | // TODO: check | |
| 365 | 356 | pub const SIGRTMIN = 65; |
| 366 | 357 | pub const SIGRTMAX = 126; |
| 367 | 358 | |
| ... | ... | @@ -645,135 +636,51 @@ pub const EVFILT_SENDFILE = -12; |
| 645 | 636 | |
| 646 | 637 | pub const EVFILT_EMPTY = -13; |
| 647 | 638 | |
| 648 | /// On input, NOTE_TRIGGER causes the event to be triggered for output. | |
| 649 | pub const NOTE_TRIGGER = 0x01000000; | |
| 650 | ||
| 651 | /// ignore input fflags | |
| 652 | pub const NOTE_FFNOP = 0x00000000; | |
| 653 | ||
| 654 | /// and fflags | |
| 655 | pub const NOTE_FFAND = 0x40000000; | |
| 656 | ||
| 657 | /// or fflags | |
| 658 | pub const NOTE_FFOR = 0x80000000; | |
| 659 | ||
| 660 | /// copy fflags | |
| 661 | pub const NOTE_FFCOPY = 0xc0000000; | |
| 662 | ||
| 663 | /// mask for operations | |
| 664 | pub const NOTE_FFCTRLMASK = 0xc0000000; | |
| 665 | pub const NOTE_FFLAGSMASK = 0x00ffffff; | |
| 666 | ||
| 667 | /// low water mark | |
| 668 | pub const NOTE_LOWAT = 0x00000001; | |
| 669 | ||
| 670 | /// behave like poll() | |
| 671 | pub const NOTE_FILE_POLL = 0x00000002; | |
| 672 | ||
| 673 | /// vnode was removed | |
| 674 | pub const NOTE_DELETE = 0x00000001; | |
| 675 | ||
| 676 | /// data contents changed | |
| 677 | pub const NOTE_WRITE = 0x00000002; | |
| 678 | ||
| 679 | /// size increased | |
| 680 | pub const NOTE_EXTEND = 0x00000004; | |
| 681 | ||
| 682 | /// attributes changed | |
| 683 | pub const NOTE_ATTRIB = 0x00000008; | |
| 684 | ||
| 685 | /// link count changed | |
| 686 | pub const NOTE_LINK = 0x00000010; | |
| 687 | ||
| 688 | /// vnode was renamed | |
| 689 | pub const NOTE_RENAME = 0x00000020; | |
| 690 | ||
| 691 | /// vnode access was revoked | |
| 692 | pub const NOTE_REVOKE = 0x00000040; | |
| 693 | ||
| 694 | /// vnode was opened | |
| 695 | pub const NOTE_OPEN = 0x00000080; | |
| 696 | ||
| 697 | /// file closed, fd did not allow write | |
| 698 | pub const NOTE_CLOSE = 0x00000100; | |
| 699 | ||
| 700 | /// file closed, fd did allow write | |
| 701 | pub const NOTE_CLOSE_WRITE = 0x00000200; | |
| 702 | ||
| 703 | /// file was read | |
| 704 | pub const NOTE_READ = 0x00000400; | |
| 705 | ||
| 706 | /// process exited | |
| 707 | pub const NOTE_EXIT = 0x80000000; | |
| 708 | ||
| 709 | /// process forked | |
| 710 | pub const NOTE_FORK = 0x40000000; | |
| 711 | ||
| 712 | /// process exec'd | |
| 713 | pub const NOTE_EXEC = 0x20000000; | |
| 714 | ||
| 715 | /// mask for signal & exit status | |
| 716 | pub const NOTE_PDATAMASK = 0x000fffff; | |
| 717 | pub const NOTE_PCTRLMASK = (~NOTE_PDATAMASK); | |
| 718 | ||
| 719 | /// data is seconds | |
| 720 | pub const NOTE_SECONDS = 0x00000001; | |
| 721 | ||
| 722 | /// data is milliseconds | |
| 723 | pub const NOTE_MSECONDS = 0x00000002; | |
| 724 | ||
| 725 | /// data is microseconds | |
| 726 | pub const NOTE_USECONDS = 0x00000004; | |
| 727 | ||
| 728 | /// data is nanoseconds | |
| 729 | pub const NOTE_NSECONDS = 0x00000008; | |
| 730 | ||
| 731 | /// timeout is absolute | |
| 732 | pub const NOTE_ABSTIME = 0x00000010; | |
| 733 | ||
| 734 | pub const TIOCEXCL = 0x2000740d; | |
| 735 | pub const TIOCNXCL = 0x2000740e; | |
| 736 | pub const TIOCSCTTY = 0x20007461; | |
| 737 | pub const TIOCGPGRP = 0x40047477; | |
| 738 | pub const TIOCSPGRP = 0x80047476; | |
| 739 | pub const TIOCOUTQ = 0x40047473; | |
| 740 | pub const TIOCSTI = 0x80017472; | |
| 741 | pub const TIOCGWINSZ = 0x40087468; | |
| 742 | pub const TIOCSWINSZ = 0x80087467; | |
| 743 | pub const TIOCMGET = 0x4004746a; | |
| 744 | pub const TIOCMBIS = 0x8004746c; | |
| 745 | pub const TIOCMBIC = 0x8004746b; | |
| 746 | pub const TIOCMSET = 0x8004746d; | |
| 747 | pub const FIONREAD = 0x4004667f; | |
| 748 | pub const TIOCCONS = 0x80047462; | |
| 749 | pub const TIOCPKT = 0x80047470; | |
| 750 | pub const FIONBIO = 0x8004667e; | |
| 751 | pub const TIOCNOTTY = 0x20007471; | |
| 752 | pub const TIOCSETD = 0x8004741b; | |
| 753 | pub const TIOCGETD = 0x4004741a; | |
| 754 | pub const TIOCSBRK = 0x2000747b; | |
| 755 | pub const TIOCCBRK = 0x2000747a; | |
| 756 | pub const TIOCGSID = 0x40047463; | |
| 757 | pub const TIOCGPTN = 0x4004740f; | |
| 758 | pub const TIOCSIG = 0x2004745f; | |
| 639 | pub const TCGETA = 0x8000; | |
| 640 | pub const TCSETA = 0x8001; | |
| 641 | pub const TCSETAW = 0x8004; | |
| 642 | pub const TCSETAF = 0x8003; | |
| 643 | pub const TCSBRK = 08005; | |
| 644 | pub const TCXONC = 0x8007; | |
| 645 | pub const TCFLSH = 0x8006; | |
| 646 | ||
| 647 | pub const TIOCSCTTY = 0x8017; | |
| 648 | pub const TIOCGPGRP = 0x8015; | |
| 649 | pub const TIOCSPGRP = 0x8016; | |
| 650 | pub const TIOCGWINSZ = 0x8012; | |
| 651 | pub const TIOCSWINSZ = 0x8013; | |
| 652 | pub const TIOCMGET = 0x8018; | |
| 653 | pub const TIOCMBIS = 0x8022; | |
| 654 | pub const TIOCMBIC = 0x8023; | |
| 655 | pub const TIOCMSET = 0x8019; | |
| 656 | pub const FIONREAD = 0xbe000001; | |
| 657 | pub const FIONBIO = 0xbe000000; | |
| 658 | pub const TIOCSBRK = 0x8020; | |
| 659 | pub const TIOCCBRK = 0x8021; | |
| 660 | pub const TIOCGSID = 0x8024; | |
| 759 | 661 | |
| 760 | 662 | pub fn WEXITSTATUS(s: u32) u32 { |
| 761 | return (s & 0xff00) >> 8; | |
| 663 | return (s & 0xff); | |
| 762 | 664 | } |
| 665 | ||
| 763 | 666 | pub fn WTERMSIG(s: u32) u32 { |
| 764 | return s & 0x7f; | |
| 667 | return (s >> 8) & 0xff; | |
| 765 | 668 | } |
| 669 | ||
| 766 | 670 | pub fn WSTOPSIG(s: u32) u32 { |
| 767 | 671 | return WEXITSTATUS(s); |
| 768 | 672 | } |
| 673 | ||
| 769 | 674 | pub fn WIFEXITED(s: u32) bool { |
| 770 | 675 | return WTERMSIG(s) == 0; |
| 771 | 676 | } |
| 677 | ||
| 772 | 678 | pub fn WIFSTOPPED(s: u32) bool { |
| 773 | return @intCast(u16, (((s & 0xffff) *% 0x10001) >> 8)) > 0x7f00; | |
| 679 | return ((s >> 16) & 0xff) != 0; | |
| 774 | 680 | } |
| 681 | ||
| 775 | 682 | pub fn WIFSIGNALED(s: u32) bool { |
| 776 | return (s & 0xffff) -% 1 < 0xff; | |
| 683 | return ((s >> 8) & 0xff) != 0; | |
| 777 | 684 | } |
| 778 | 685 | |
| 779 | 686 | pub const winsize = extern struct { |
| ... | ... | @@ -823,49 +730,47 @@ pub const sigset_t = extern struct { |
| 823 | 730 | __bits: [_SIG_WORDS]u32, |
| 824 | 731 | }; |
| 825 | 732 | |
| 826 | pub const EPERM = 1; // Operation not permitted | |
| 827 | pub const ENOENT = 2; // No such file or directory | |
| 828 | pub const ESRCH = 3; // No such process | |
| 829 | pub const EINTR = 4; // Interrupted system call | |
| 830 | pub const EIO = 5; // Input/output error | |
| 831 | pub const ENXIO = 6; // Device not configured | |
| 832 | pub const E2BIG = 7; // Argument list too long | |
| 833 | pub const ENOEXEC = 8; // Exec format error | |
| 834 | pub const EBADF = 9; // Bad file descriptor | |
| 835 | pub const ECHILD = 10; // No child processes | |
| 836 | pub const EDEADLK = 11; // Resource deadlock avoided | |
| 837 | // 11 was EAGAIN | |
| 838 | pub const ENOMEM = 12; // Cannot allocate memory | |
| 839 | pub const EACCES = 13; // Permission denied | |
| 840 | pub const EFAULT = 14; // Bad address | |
| 841 | pub const ENOTBLK = 15; // Block device required | |
| 842 | pub const EBUSY = 16; // Device busy | |
| 843 | pub const EEXIST = 17; // File exists | |
| 844 | pub const EXDEV = 18; // Cross-device link | |
| 845 | pub const ENODEV = 19; // Operation not supported by device | |
| 846 | pub const ENOTDIR = 20; // Not a directory | |
| 847 | pub const EISDIR = 21; // Is a directory | |
| 848 | pub const EINVAL = 22; // Invalid argument | |
| 849 | pub const ENFILE = 23; // Too many open files in system | |
| 850 | pub const EMFILE = 24; // Too many open files | |
| 851 | pub const ENOTTY = 25; // Inappropriate ioctl for device | |
| 852 | pub const ETXTBSY = 26; // Text file busy | |
| 853 | pub const EFBIG = 27; // File too large | |
| 854 | pub const ENOSPC = 28; // No space left on device | |
| 855 | pub const ESPIPE = 29; // Illegal seek | |
| 856 | pub const EROFS = 30; // Read-only filesystem | |
| 857 | pub const EMLINK = 31; // Too many links | |
| 858 | pub const EPIPE = 32; // Broken pipe | |
| 733 | pub const EPERM = -0x7ffffff1; // Operation not permitted | |
| 734 | pub const ENOENT = -0x7fff9ffd; // No such file or directory | |
| 735 | pub const ESRCH = -0x7fff8ff3; // No such process | |
| 736 | pub const EINTR = -0x7ffffff6; // Interrupted system call | |
| 737 | pub const EIO = -0x7fffffff; // Input/output error | |
| 738 | pub const ENXIO = -0x7fff8ff5; // Device not configured | |
| 739 | pub const E2BIG = -0x7fff8fff; // Argument list too long | |
| 740 | pub const ENOEXEC = -0x7fffecfe; // Exec format error | |
| 741 | pub const ECHILD = -0x7fff8ffe; // No child processes | |
| 742 | pub const EDEADLK = -0x7fff8ffd; // Resource deadlock avoided | |
| 743 | pub const ENOMEM = -0x80000000; // Cannot allocate memory | |
| 744 | pub const EACCES = -0x7ffffffe; // Permission denied | |
| 745 | pub const EFAULT = -0x7fffecff; // Bad address | |
| 746 | pub const EBUSY = -0x7ffffff2; // Device busy | |
| 747 | pub const EEXIST = -0x7fff9ffe; // File exists | |
| 748 | pub const EXDEV = -0x7fff9ff5; // Cross-device link | |
| 749 | pub const ENODEV = -0x7fff8ff9; // Operation not supported by device | |
| 750 | pub const ENOTDIR = -0x7fff9ffb; // Not a directory | |
| 751 | pub const EISDIR = -0x7fff9ff7; // Is a directory | |
| 752 | pub const EINVAL = -0x7ffffffb; // Invalid argument | |
| 753 | pub const ENFILE = -0x7fff8ffa; // Too many open files in system | |
| 754 | pub const EMFILE = -0x7fff9ff6; // Too many open files | |
| 755 | pub const ENOTTY = -0x7fff8ff6; // Inappropriate ioctl for device | |
| 756 | pub const ETXTBSY = -0x7fff8fc5; // Text file busy | |
| 757 | pub const EFBIG = -0x7fff8ffc; // File too large | |
| 758 | pub const ENOSPC = -0x7fff9ff9; // No space left on device | |
| 759 | pub const ESPIPE = -0x7fff8ff4; // Illegal seek | |
| 760 | pub const EROFS = -0x7fff9ff8; // Read-only filesystem | |
| 761 | pub const EMLINK = -0x7fff8ffb; // Too many links | |
| 762 | pub const EPIPE = -0x7fff9ff3; // Broken pipe | |
| 763 | pub const EBADF = -0x7fffa000; // Bad file descriptor | |
| 859 | 764 | |
| 860 | 765 | // math software |
| 861 | 766 | pub const EDOM = 33; // Numerical argument out of domain |
| 862 | 767 | pub const ERANGE = 34; // Result too large |
| 863 | 768 | |
| 864 | 769 | // non-blocking and interrupt i/o |
| 865 | pub const EAGAIN = 35; // Resource temporarily unavailable | |
| 866 | pub const EWOULDBLOCK = EAGAIN; // Operation would block | |
| 867 | pub const EINPROGRESS = 36; // Operation now in progress | |
| 868 | pub const EALREADY = 37; // Operation already in progress | |
| 770 | pub const EAGAIN = -0x7ffffff5; | |
| 771 | pub const EWOULDBLOCK = -0x7ffffff5; | |
| 772 | pub const EINPROGRESS = -0x7fff8fdc; | |
| 773 | pub const EALREADY = -0x7fff8fdb; | |
| 869 | 774 | |
| 870 | 775 | // ipc/network software -- argument errors |
| 871 | 776 | pub const ENOTSOCK = 38; // Socket operation on non-socket |
| ... | ... | @@ -1447,3 +1352,20 @@ pub const directory_which = enum(c_int) { |
| 1447 | 1352 | |
| 1448 | 1353 | _, |
| 1449 | 1354 | }; |
| 1355 | ||
| 1356 | pub const cc_t = u8; | |
| 1357 | pub const speed_t = u8; | |
| 1358 | pub const tcflag_t = u32; | |
| 1359 | ||
| 1360 | pub const NCCS = 32; | |
| 1361 | ||
| 1362 | pub 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 | 248 | tls_data = @intToPtr([*]u8, img_base + phdr.p_vaddr)[0..phdr.p_filesz]; |
| 249 | 249 | tls_data_alloc_size = phdr.p_memsz; |
| 250 | 250 | } else { |
| 251 | tls_align_factor = @alignOf(*usize); | |
| 251 | tls_align_factor = @alignOf(usize); | |
| 252 | 252 | tls_data = &[_]u8{}; |
| 253 | 253 | tls_data_alloc_size = 0; |
| 254 | 254 | } |
| ... | ... | @@ -308,7 +308,7 @@ fn initTLS() void { |
| 308 | 308 | } |
| 309 | 309 | |
| 310 | 310 | fn 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 | } |
| 313 | 313 | |
| 314 | 314 | /// 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 | 88 | pub const unicode = @import("unicode.zig"); |
| 89 | 89 | pub const valgrind = @import("valgrind.zig"); |
| 90 | 90 | pub const wasm = @import("wasm.zig"); |
| 91 | pub const x = @import("x.zig"); | |
| 91 | 92 | pub const zig = @import("zig.zig"); |
| 92 | 93 | pub const start = @import("start.zig"); |
| 93 | 94 |
lib/std/target.zig+4-3| ... | ... | @@ -211,8 +211,9 @@ pub const Target = struct { |
| 211 | 211 | /// If neither of these cases apply, a runtime check should be used to determine if the |
| 212 | 212 | /// target supports a given OS feature. |
| 213 | 213 | /// |
| 214 | /// Binaries built with a given maximum version will continue to function on newer operating system | |
| 215 | /// versions. However, such a binary may not take full advantage of the newer operating system APIs. | |
| 214 | /// Binaries built with a given maximum version will continue to function on newer | |
| 215 | /// operating system versions. However, such a binary may not take full advantage of the | |
| 216 | /// newer operating system APIs. | |
| 216 | 217 | /// |
| 217 | 218 | /// See `Os.isAtLeast`. |
| 218 | 219 | pub const VersionRange = union { |
| ... | ... | @@ -260,7 +261,7 @@ pub const Target = struct { |
| 260 | 261 | .freebsd => return .{ |
| 261 | 262 | .semver = Version.Range{ |
| 262 | 263 | .min = .{ .major = 12, .minor = 0 }, |
| 263 | .max = .{ .major = 12, .minor = 1 }, | |
| 264 | .max = .{ .major = 13, .minor = 0 }, | |
| 264 | 265 | }, |
| 265 | 266 | }, |
| 266 | 267 | .macos => return .{ |
lib/std/unicode.zig+1-1| ... | ... | @@ -206,7 +206,7 @@ pub fn utf8ValidateSlice(s: []const u8) bool { |
| 206 | 206 | return false; |
| 207 | 207 | } |
| 208 | 208 | |
| 209 | if (utf8Decode(s[i .. i + cp_len])) |_| {} else |_| { | |
| 209 | if (std.meta.isError(utf8Decode(s[i .. i + cp_len]))) { | |
| 210 | 210 | return false; |
| 211 | 211 | } |
| 212 | 212 | i += cp_len; |
lib/std/x.zig created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | pub const os = @import("x/os/os.zig"); |
lib/std/x/os/Socket.zig created+276| ... | ... | @@ -0,0 +1,276 @@ |
| 1 | const std = @import("../../std.zig"); | |
| 2 | ||
| 3 | const os = std.os; | |
| 4 | const mem = std.mem; | |
| 5 | const net = std.net; | |
| 6 | const time = std.time; | |
| 7 | const builtin = std.builtin; | |
| 8 | const testing = std.testing; | |
| 9 | ||
| 10 | const Socket = @This(); | |
| 11 | ||
| 12 | /// A socket-address pair. | |
| 13 | pub const Connection = struct { | |
| 14 | socket: Socket, | |
| 15 | address: net.Address, | |
| 16 | }; | |
| 17 | ||
| 18 | /// The underlying handle of a socket. | |
| 19 | fd: os.socket_t, | |
| 20 | ||
| 21 | /// Open a new socket. | |
| 22 | pub 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. | |
| 27 | pub 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. | |
| 32 | pub fn shutdown(self: Socket, how: os.ShutdownHow) !void { | |
| 33 | return os.shutdown(self.fd, how); | |
| 34 | } | |
| 35 | ||
| 36 | /// Binds the socket to an address. | |
| 37 | pub 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. | |
| 42 | pub 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. | |
| 47 | pub 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. | |
| 53 | pub 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. | |
| 67 | pub 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. | |
| 73 | pub 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. | |
| 79 | pub 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. | |
| 85 | pub 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. | |
| 91 | pub 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. | |
| 98 | pub 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. | |
| 103 | pub 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. | |
| 111 | pub fn getError(self: Socket) !void { | |
| 112 | return os.getsockoptError(self.fd); | |
| 113 | } | |
| 114 | ||
| 115 | /// Query the read buffer size of the socket. | |
| 116 | pub 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. | |
| 133 | pub 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. | |
| 151 | pub 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. | |
| 160 | pub 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. | |
| 169 | pub 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. | |
| 178 | pub 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. | |
| 187 | pub 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. | |
| 195 | pub 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. | |
| 200 | pub 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. | |
| 207 | pub 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. | |
| 220 | pub 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 | ||
| 229 | test { | |
| 230 | testing.refAllDecls(@This()); | |
| 231 | } | |
| 232 | ||
| 233 | test "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 | ||
| 257 | test "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 @@ |
| 1 | const std = @import("../../std.zig"); | |
| 2 | ||
| 3 | const testing = std.testing; | |
| 4 | ||
| 5 | pub const Socket = @import("Socket.zig"); | |
| 6 | ||
| 7 | test { | |
| 8 | testing.refAllDecls(@This()); | |
| 9 | } |
lib/std/zig/system.zig+10-5| ... | ... | @@ -15,6 +15,7 @@ const Target = std.Target; |
| 15 | 15 | const CrossTarget = std.zig.CrossTarget; |
| 16 | 16 | const macos = @import("system/macos.zig"); |
| 17 | 17 | const native_endian = std.Target.current.cpu.arch.endian(); |
| 18 | const linux = @import("system/linux.zig"); | |
| 18 | 19 | pub const windows = @import("system/windows.zig"); |
| 19 | 20 | |
| 20 | 21 | pub const getSDKPath = macos.getSDKPath; |
| ... | ... | @@ -912,15 +913,19 @@ pub const NativeTargetInfo = struct { |
| 912 | 913 | .x86_64, .i386 => { |
| 913 | 914 | return @import("system/x86.zig").detectNativeCpuAndFeatures(cpu_arch, os, cross_target); |
| 914 | 915 | }, |
| 915 | else => { | |
| 916 | // This architecture does not have CPU model & feature detection yet. | |
| 917 | // See https://github.com/ziglang/zig/issues/4591 | |
| 918 | return null; | |
| 919 | }, | |
| 916 | else => {}, | |
| 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 | }; |
| 923 | 927 | |
| 924 | 928 | test { |
| 925 | 929 | _ = @import("system/macos.zig"); |
| 930 | _ = @import("system/linux.zig"); | |
| 926 | 931 | } |
lib/std/zig/system/linux.zig created+199| ... | ... | @@ -0,0 +1,199 @@ |
| 1 | const std = @import("std"); | |
| 2 | const mem = std.mem; | |
| 3 | const io = std.io; | |
| 4 | const fs = std.fs; | |
| 5 | const fmt = std.fmt; | |
| 6 | const testing = std.testing; | |
| 7 | ||
| 8 | const Target = std.Target; | |
| 9 | const CrossTarget = std.zig.CrossTarget; | |
| 10 | ||
| 11 | const assert = std.debug.assert; | |
| 12 | ||
| 13 | const 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 | ||
| 65 | const SparcCpuinfoParser = CpuinfoParser(SparcCpuinfoImpl); | |
| 66 | ||
| 67 | test "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 | ||
| 76 | const 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 | ||
| 131 | const PowerpcCpuinfoParser = CpuinfoParser(PowerpcCpuinfoImpl); | |
| 132 | ||
| 133 | test "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 | ||
| 148 | fn 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. | |
| 160 | fn 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 | ||
| 181 | pub 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 | 104 | extern fn ZigClangAPFloat_toString(*const APFloat, precision: c_uint, maxPadding: c_uint, truncateZero: bool) [*:0]const u8; |
| 105 | 105 | }; |
| 106 | 106 | |
| 107 | pub const APFloatBaseSemantics = extern enum { | |
| 108 | IEEEhalf, | |
| 109 | BFloat, | |
| 110 | IEEEsingle, | |
| 111 | IEEEdouble, | |
| 112 | x86DoubleExtended, | |
| 113 | IEEEquad, | |
| 114 | PPCDoubleDouble, | |
| 115 | }; | |
| 116 | ||
| 107 | 117 | pub const APInt = opaque { |
| 108 | 118 | pub const getLimitedValue = ZigClangAPInt_getLimitedValue; |
| 109 | 119 | extern fn ZigClangAPInt_getLimitedValue(*const APInt, limit: u64) u64; |
| ... | ... | @@ -455,6 +465,12 @@ pub const FileID = opaque {}; |
| 455 | 465 | pub const FloatingLiteral = opaque { |
| 456 | 466 | pub const getValueAsApproximateDouble = ZigClangFloatingLiteral_getValueAsApproximateDouble; |
| 457 | 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 | }; |
| 459 | 475 | |
| 460 | 476 | pub const ForStmt = opaque { |
src/codegen.zig+29-17| ... | ... | @@ -449,7 +449,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 449 | 449 | .rbrace_src = src_data.rbrace_src, |
| 450 | 450 | .source = src_data.source, |
| 451 | 451 | }; |
| 452 | defer function.register_manager.deinit(bin_file.allocator); | |
| 453 | 452 | defer function.stack.deinit(bin_file.allocator); |
| 454 | 453 | defer function.exitlude_jump_relocs.deinit(bin_file.allocator); |
| 455 | 454 | |
| ... | ... | @@ -779,8 +778,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 779 | 778 | branch.inst_table.putAssumeCapacity(inst, .dead); |
| 780 | 779 | switch (prev_value) { |
| 781 | 780 | .register => |reg| { |
| 782 | const canon_reg = toCanonicalReg(reg); | |
| 783 | self.register_manager.freeReg(canon_reg); | |
| 781 | // TODO separate architectures with registers from | |
| 782 | // stack-based architectures (spu_2) | |
| 783 | if (callee_preserved_regs.len > 0) { | |
| 784 | const canon_reg = toCanonicalReg(reg); | |
| 785 | self.register_manager.freeReg(canon_reg); | |
| 786 | } | |
| 784 | 787 | }, |
| 785 | 788 | else => {}, // TODO process stack allocation death |
| 786 | 789 | } |
| ... | ... | @@ -920,9 +923,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 920 | 923 | const ptr_bits = arch.ptrBitWidth(); |
| 921 | 924 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); |
| 922 | 925 | if (abi_size <= ptr_bytes) { |
| 923 | try self.register_manager.registers.ensureCapacity(self.gpa, self.register_manager.registers.count() + 1); | |
| 924 | if (self.register_manager.tryAllocReg(inst)) |reg| { | |
| 925 | return MCValue{ .register = registerAlias(reg, abi_size) }; | |
| 926 | // TODO separate architectures with registers from | |
| 927 | // stack-based architectures (spu_2) | |
| 928 | if (callee_preserved_regs.len > 0) { | |
| 929 | if (self.register_manager.tryAllocReg(inst)) |reg| { | |
| 930 | return MCValue{ .register = registerAlias(reg, abi_size) }; | |
| 931 | } | |
| 926 | 932 | } |
| 927 | 933 | } |
| 928 | 934 | } |
| ... | ... | @@ -952,8 +958,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 952 | 958 | /// `reg_owner` is the instruction that gets associated with the register in the register table. |
| 953 | 959 | /// This can have a side effect of spilling instructions to the stack to free up a register. |
| 954 | 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 | 961 | const reg = try self.register_manager.allocReg(reg_owner); |
| 958 | 962 | try self.genSetReg(reg_owner.src, reg_owner.ty, reg, mcv); |
| 959 | 963 | return MCValue{ .register = reg }; |
| ... | ... | @@ -1240,10 +1244,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1240 | 1244 | .register => |reg| { |
| 1241 | 1245 | // If it's in the registers table, need to associate the register with the |
| 1242 | 1246 | // new instruction. |
| 1243 | if (self.register_manager.registers.getEntry(toCanonicalReg(reg))) |entry| { | |
| 1244 | entry.value = inst; | |
| 1247 | // TODO separate architectures with registers from | |
| 1248 | // stack-based architectures (spu_2) | |
| 1249 | if (callee_preserved_regs.len > 0) { | |
| 1250 | if (reg.allocIndex()) |index| { | |
| 1251 | if (!self.register_manager.isRegFree(reg)) { | |
| 1252 | self.register_manager.registers[index] = inst; | |
| 1253 | } | |
| 1254 | } | |
| 1255 | log.debug("reusing {} => {*}", .{ reg, inst }); | |
| 1245 | 1256 | } |
| 1246 | log.debug("reusing {} => {*}", .{ reg, inst }); | |
| 1247 | 1257 | }, |
| 1248 | 1258 | .stack_offset => |off| { |
| 1249 | 1259 | log.debug("reusing stack offset {} => {*}", .{ off, inst }); |
| ... | ... | @@ -1738,6 +1748,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1738 | 1748 | const arg_index = self.arg_index; |
| 1739 | 1749 | self.arg_index += 1; |
| 1740 | 1750 | |
| 1751 | // TODO separate architectures with registers from | |
| 1752 | // stack-based architectures (spu_2) | |
| 1741 | 1753 | if (callee_preserved_regs.len == 0) { |
| 1742 | 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 | 1781 | |
| 1770 | 1782 | switch (mcv) { |
| 1771 | 1783 | .register => |reg| { |
| 1772 | try self.register_manager.registers.ensureCapacity(self.gpa, self.register_manager.registers.count() + 1); | |
| 1773 | 1784 | self.register_manager.getRegAssumeFree(toCanonicalReg(reg), &inst.base); |
| 1774 | 1785 | }, |
| 1775 | 1786 | else => {}, |
| ... | ... | @@ -2075,7 +2086,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2075 | 2086 | switch (mc_arg) { |
| 2076 | 2087 | .none => continue, |
| 2077 | 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 | 2094 | try self.genSetReg(arg.src, arg.ty, reg, arg_mcv); |
| 2080 | 2095 | }, |
| 2081 | 2096 | .stack_offset => { |
| ... | ... | @@ -2397,8 +2412,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2397 | 2412 | const parent_free_registers = self.register_manager.free_registers; |
| 2398 | 2413 | var parent_stack = try self.stack.clone(self.gpa); |
| 2399 | 2414 | defer parent_stack.deinit(self.gpa); |
| 2400 | var parent_registers = try self.register_manager.registers.clone(self.gpa); | |
| 2401 | defer parent_registers.deinit(self.gpa); | |
| 2415 | const parent_registers = self.register_manager.registers; | |
| 2402 | 2416 | |
| 2403 | 2417 | try self.branch_stack.append(.{}); |
| 2404 | 2418 | |
| ... | ... | @@ -2414,9 +2428,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2414 | 2428 | var saved_then_branch = self.branch_stack.pop(); |
| 2415 | 2429 | defer saved_then_branch.deinit(self.gpa); |
| 2416 | 2430 | |
| 2417 | self.register_manager.registers.deinit(self.gpa); | |
| 2418 | 2431 | self.register_manager.registers = parent_registers; |
| 2419 | parent_registers = .{}; | |
| 2420 | 2432 | |
| 2421 | 2433 | self.stack.deinit(self.gpa); |
| 2422 | 2434 | self.stack = parent_stack; |
src/codegen/riscv64.zig+47-10| ... | ... | @@ -1,5 +1,7 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const DW = std.dwarf; |
| 3 | const assert = std.debug.assert; | |
| 4 | const testing = std.testing; | |
| 3 | 5 | |
| 4 | 6 | // TODO: this is only tagged to facilitate the monstrosity. |
| 5 | 7 | // Once packed structs work make it packed. |
| ... | ... | @@ -110,7 +112,7 @@ pub const Instruction = union(enum) { |
| 110 | 112 | // -- less burden on callsite, bonus semantic checking |
| 111 | 113 | fn bType(op: u7, fn3: u3, r1: Register, r2: Register, imm: i13) Instruction { |
| 112 | 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 | |
| 114 | 116 | |
| 115 | 117 | return Instruction{ |
| 116 | 118 | .B = .{ |
| ... | ... | @@ -140,15 +142,15 @@ pub const Instruction = union(enum) { |
| 140 | 142 | } |
| 141 | 143 | |
| 142 | 144 | fn jType(op: u7, rd: Register, imm: i21) Instruction { |
| 143 | const umm = @bitcast(u21, imm); | |
| 144 | if (umm % 2 != 0) @panic("Internal error: misaligned jump target"); | |
| 145 | const umm = @bitCast(u21, imm); | |
| 146 | assert(umm % 2 == 0); // misaligned jump target | |
| 145 | 147 | |
| 146 | 148 | return Instruction{ |
| 147 | 149 | .J = .{ |
| 148 | 150 | .opcode = op, |
| 149 | 151 | .rd = @enumToInt(rd), |
| 150 | 152 | .imm1_10 = @truncate(u10, umm >> 1), |
| 151 | .imm11 = @truncate(u1, umm >> 1), | |
| 153 | .imm11 = @truncate(u1, umm >> 11), | |
| 152 | 154 | .imm12_19 = @truncate(u8, umm >> 12), |
| 153 | 155 | .imm20 = @truncate(u1, umm >> 20), |
| 154 | 156 | }, |
| ... | ... | @@ -340,27 +342,27 @@ pub const Instruction = union(enum) { |
| 340 | 342 | |
| 341 | 343 | // Branch |
| 342 | 344 | |
| 343 | pub fn beq(r1: Register, r2: Register, offset: u13) Instruction { | |
| 345 | pub fn beq(r1: Register, r2: Register, offset: i13) Instruction { | |
| 344 | 346 | return bType(0b1100011, 0b000, r1, r2, offset); |
| 345 | 347 | } |
| 346 | 348 | |
| 347 | pub fn bne(r1: Register, r2: Register, offset: u13) Instruction { | |
| 349 | pub fn bne(r1: Register, r2: Register, offset: i13) Instruction { | |
| 348 | 350 | return bType(0b1100011, 0b001, r1, r2, offset); |
| 349 | 351 | } |
| 350 | 352 | |
| 351 | pub fn blt(r1: Register, r2: Register, offset: u13) Instruction { | |
| 353 | pub fn blt(r1: Register, r2: Register, offset: i13) Instruction { | |
| 352 | 354 | return bType(0b1100011, 0b100, r1, r2, offset); |
| 353 | 355 | } |
| 354 | 356 | |
| 355 | pub fn bge(r1: Register, r2: Register, offset: u13) Instruction { | |
| 357 | pub fn bge(r1: Register, r2: Register, offset: i13) Instruction { | |
| 356 | 358 | return bType(0b1100011, 0b101, r1, r2, offset); |
| 357 | 359 | } |
| 358 | 360 | |
| 359 | pub fn bltu(r1: Register, r2: Register, offset: u13) Instruction { | |
| 361 | pub fn bltu(r1: Register, r2: Register, offset: i13) Instruction { | |
| 360 | 362 | return bType(0b1100011, 0b110, r1, r2, offset); |
| 361 | 363 | } |
| 362 | 364 | |
| 363 | pub fn bgeu(r1: Register, r2: Register, offset: u13) Instruction { | |
| 365 | pub fn bgeu(r1: Register, r2: Register, offset: i13) Instruction { | |
| 364 | 366 | return bType(0b1100011, 0b111, r1, r2, offset); |
| 365 | 367 | } |
| 366 | 368 | |
| ... | ... | @@ -431,3 +433,38 @@ pub const Register = enum(u5) { |
| 431 | 433 | pub const callee_preserved_regs = [_]Register{ |
| 432 | 434 | .s0, .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11, |
| 433 | 435 | }; |
| 436 | ||
| 437 | test "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 | 9 | const is_darwin = Target.current.isDarwin(); |
| 10 | 10 | const is_windows = Target.current.os.tag == .windows; |
| 11 | 11 | const is_gnu = Target.current.isGnu(); |
| 12 | const is_haiku = Target.current.os.tag == .haiku; | |
| 12 | 13 | |
| 13 | 14 | const log = std.log.scoped(.libc_installation); |
| 14 | 15 | |
| ... | ... | @@ -279,8 +280,14 @@ pub const LibCInstallation = struct { |
| 279 | 280 | return error.CCompilerCannotFindHeaders; |
| 280 | 281 | } |
| 281 | 282 | |
| 282 | const include_dir_example_file = "stdlib.h"; | |
| 283 | const sys_include_dir_example_file = if (is_windows) "sys\\types.h" else "sys/errno.h"; | |
| 283 | const include_dir_example_file = if (is_haiku) "posix/stdlib.h" else "stdlib.h"; | |
| 284 | const sys_include_dir_example_file = if (is_windows) | |
| 285 | "sys\\types.h" | |
| 286 | else if (is_haiku) | |
| 287 | "posix/errno.h" | |
| 288 | else | |
| 289 | "sys/errno.h" | |
| 290 | ; | |
| 284 | 291 | |
| 285 | 292 | var path_i: usize = 0; |
| 286 | 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 | 16 | ) type { |
| 17 | 17 | return struct { |
| 18 | 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 | 20 | free_registers: FreeRegInt = math.maxInt(FreeRegInt), |
| 21 | 21 | /// Tracks all registers allocated in the course of this function |
| 22 | 22 | allocated_registers: FreeRegInt = 0, |
| ... | ... | @@ -31,14 +31,6 @@ pub fn RegisterManager( |
| 31 | 31 | return @fieldParentPtr(Function, "register_manager", self); |
| 32 | 32 | } |
| 33 | 33 | |
| 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 | 34 | fn markRegUsed(self: *Self, reg: Register) void { |
| 43 | 35 | if (FreeRegInt == u0) return; |
| 44 | 36 | const index = reg.allocIndex() orelse return; |
| ... | ... | @@ -73,13 +65,13 @@ pub fn RegisterManager( |
| 73 | 65 | return self.allocated_registers & @as(FreeRegInt, 1) << shift != 0; |
| 74 | 66 | } |
| 75 | 67 | |
| 76 | /// Before calling, must ensureCapacity + count on self.registers. | |
| 77 | 68 | /// Returns `null` if all registers are allocated. |
| 78 | 69 | pub fn tryAllocRegs(self: *Self, comptime count: comptime_int, insts: [count]*ir.Inst) ?[count]Register { |
| 79 | 70 | if (self.tryAllocRegsWithoutTracking(count)) |regs| { |
| 80 | 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 | 74 | self.markRegUsed(reg); |
| 82 | self.registers.putAssumeCapacityNoClobber(reg, insts[i]); | |
| 83 | 75 | } |
| 84 | 76 | |
| 85 | 77 | return regs; |
| ... | ... | @@ -88,13 +80,11 @@ pub fn RegisterManager( |
| 88 | 80 | } |
| 89 | 81 | } |
| 90 | 82 | |
| 91 | /// Before calling, must ensureCapacity + 1 on self.registers. | |
| 92 | 83 | /// Returns `null` if all registers are allocated. |
| 93 | 84 | pub fn tryAllocReg(self: *Self, inst: *ir.Inst) ?Register { |
| 94 | 85 | return if (tryAllocRegs(self, 1, .{inst})) |regs| regs[0] else null; |
| 95 | 86 | } |
| 96 | 87 | |
| 97 | /// Before calling, must ensureCapacity + count on self.registers. | |
| 98 | 88 | pub fn allocRegs(self: *Self, comptime count: comptime_int, insts: [count]*ir.Inst) ![count]Register { |
| 99 | 89 | comptime assert(count > 0 and count <= callee_preserved_regs.len); |
| 100 | 90 | |
| ... | ... | @@ -106,24 +96,22 @@ pub fn RegisterManager( |
| 106 | 96 | std.mem.copy(Register, &regs, callee_preserved_regs[0..count]); |
| 107 | 97 | |
| 108 | 98 | for (regs) |reg, i| { |
| 99 | const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null | |
| 109 | 100 | if (self.isRegFree(reg)) { |
| 110 | 101 | self.markRegUsed(reg); |
| 111 | self.registers.putAssumeCapacityNoClobber(reg, insts[i]); | |
| 112 | 102 | } else { |
| 113 | const regs_entry = self.registers.getEntry(reg).?; | |
| 114 | const spilled_inst = regs_entry.value; | |
| 115 | regs_entry.value = insts[i]; | |
| 103 | const spilled_inst = self.registers[index].?; | |
| 116 | 104 | try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); |
| 117 | 105 | } |
| 106 | self.registers[index] = insts[i]; | |
| 118 | 107 | } |
| 119 | 108 | |
| 120 | 109 | break :blk regs; |
| 121 | 110 | }; |
| 122 | 111 | } |
| 123 | 112 | |
| 124 | /// Before calling, must ensureCapacity + 1 on self.registers. | |
| 125 | 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 | } |
| 128 | 116 | |
| 129 | 117 | /// Does not track the registers. |
| ... | ... | @@ -150,37 +138,48 @@ pub fn RegisterManager( |
| 150 | 138 | /// Does not track the register. |
| 151 | 139 | /// Returns `null` if all registers are allocated. |
| 152 | 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 | } |
| 155 | 143 | |
| 156 | /// Does not track the register. | |
| 157 | pub fn allocRegWithoutTracking(self: *Self) !Register { | |
| 158 | return self.tryAllocRegWithoutTracking() orelse b: { | |
| 159 | // We'll take over the first register. Move the instruction that was previously | |
| 160 | // there to a stack allocation. | |
| 161 | const reg = callee_preserved_regs[0]; | |
| 162 | const regs_entry = self.registers.remove(reg).?; | |
| 163 | const spilled_inst = regs_entry.value; | |
| 164 | try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); | |
| 165 | self.markRegFree(reg); | |
| 144 | /// Does not track the registers | |
| 145 | pub fn allocRegsWithoutTracking(self: *Self, comptime count: comptime_int) ![count]Register { | |
| 146 | return self.tryAllocRegsWithoutTracking(count) orelse blk: { | |
| 147 | // We'll take over the first count registers. Spill | |
| 148 | // the instructions that were previously there to a | |
| 149 | // stack allocations. | |
| 150 | var regs: [count]Register = undefined; | |
| 151 | std.mem.copy(Register, &regs, callee_preserved_regs[0..count]); | |
| 166 | 152 | |
| 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 | } |
| 170 | 166 | |
| 167 | /// Does not track the register. | |
| 168 | pub fn allocRegWithoutTracking(self: *Self) !Register { | |
| 169 | return (try self.allocRegsWithoutTracking(1))[0]; | |
| 170 | } | |
| 171 | ||
| 171 | 172 | /// Allocates the specified register with the specified |
| 172 | 173 | /// instruction. Spills the register if it is currently |
| 173 | 174 | /// allocated. |
| 174 | /// Before calling, must ensureCapacity + 1 on self.registers. | |
| 175 | 175 | pub fn getReg(self: *Self, reg: Register, inst: *ir.Inst) !void { |
| 176 | if (!isTracked(reg)) return; | |
| 176 | const index = reg.allocIndex() orelse return; | |
| 177 | 177 | |
| 178 | 178 | if (!self.isRegFree(reg)) { |
| 179 | 179 | // Move the instruction that was previously there to a |
| 180 | 180 | // stack allocation. |
| 181 | const regs_entry = self.registers.getEntry(reg).?; | |
| 182 | const spilled_inst = regs_entry.value; | |
| 183 | regs_entry.value = inst; | |
| 181 | const spilled_inst = self.registers[index].?; | |
| 182 | self.registers[index] = inst; | |
| 184 | 183 | try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); |
| 185 | 184 | } else { |
| 186 | 185 | self.getRegAssumeFree(reg, inst); |
| ... | ... | @@ -190,34 +189,33 @@ pub fn RegisterManager( |
| 190 | 189 | /// Spills the register if it is currently allocated. |
| 191 | 190 | /// Does not track the register. |
| 192 | 191 | pub fn getRegWithoutTracking(self: *Self, reg: Register) !void { |
| 193 | if (!isTracked(reg)) return; | |
| 192 | const index = reg.allocIndex() orelse return; | |
| 194 | 193 | |
| 195 | 194 | if (!self.isRegFree(reg)) { |
| 196 | 195 | // Move the instruction that was previously there to a |
| 197 | 196 | // stack allocation. |
| 198 | const regs_entry = self.registers.remove(reg).?; | |
| 199 | const spilled_inst = regs_entry.value; | |
| 197 | const spilled_inst = self.registers[index].?; | |
| 200 | 198 | try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); |
| 201 | 199 | self.markRegFree(reg); |
| 202 | 200 | } |
| 203 | 201 | } |
| 204 | 202 | |
| 205 | 203 | /// Allocates the specified register with the specified |
| 206 | /// instruction. Assumes that the register is free and no | |
| 204 | /// instruction. Asserts that the register is free and no | |
| 207 | 205 | /// spilling is necessary. |
| 208 | /// Before calling, must ensureCapacity + 1 on self.registers. | |
| 209 | 206 | pub fn getRegAssumeFree(self: *Self, reg: Register, inst: *ir.Inst) void { |
| 210 | if (!isTracked(reg)) return; | |
| 207 | const index = reg.allocIndex() orelse return; | |
| 211 | 208 | |
| 212 | self.registers.putAssumeCapacityNoClobber(reg, inst); | |
| 209 | assert(self.registers[index] == null); | |
| 210 | self.registers[index] = inst; | |
| 213 | 211 | self.markRegUsed(reg); |
| 214 | 212 | } |
| 215 | 213 | |
| 216 | 214 | /// Marks the specified register as free |
| 217 | 215 | pub fn freeReg(self: *Self, reg: Register) void { |
| 218 | if (!isTracked(reg)) return; | |
| 216 | const index = reg.allocIndex() orelse return; | |
| 219 | 217 | |
| 220 | _ = self.registers.remove(reg); | |
| 218 | self.registers[index] = null; | |
| 221 | 219 | self.markRegFree(reg); |
| 222 | 220 | } |
| 223 | 221 | }; |
| ... | ... | @@ -247,7 +245,6 @@ const MockFunction = struct { |
| 247 | 245 | const Self = @This(); |
| 248 | 246 | |
| 249 | 247 | pub fn deinit(self: *Self) void { |
| 250 | self.register_manager.deinit(self.allocator); | |
| 251 | 248 | self.spilled.deinit(self.allocator); |
| 252 | 249 | } |
| 253 | 250 | |
| ... | ... | @@ -273,7 +270,6 @@ test "tryAllocReg: no spilling" { |
| 273 | 270 | std.testing.expect(!function.register_manager.isRegAllocated(.r2)); |
| 274 | 271 | std.testing.expect(!function.register_manager.isRegAllocated(.r3)); |
| 275 | 272 | |
| 276 | try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2); | |
| 277 | 273 | std.testing.expectEqual(@as(?MockRegister, .r2), function.register_manager.tryAllocReg(&mock_instruction)); |
| 278 | 274 | std.testing.expectEqual(@as(?MockRegister, .r3), function.register_manager.tryAllocReg(&mock_instruction)); |
| 279 | 275 | std.testing.expectEqual(@as(?MockRegister, null), function.register_manager.tryAllocReg(&mock_instruction)); |
| ... | ... | @@ -305,7 +301,6 @@ test "allocReg: spilling" { |
| 305 | 301 | std.testing.expect(!function.register_manager.isRegAllocated(.r2)); |
| 306 | 302 | std.testing.expect(!function.register_manager.isRegAllocated(.r3)); |
| 307 | 303 | |
| 308 | try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2); | |
| 309 | 304 | std.testing.expectEqual(@as(?MockRegister, .r2), try function.register_manager.allocReg(&mock_instruction)); |
| 310 | 305 | std.testing.expectEqual(@as(?MockRegister, .r3), try function.register_manager.allocReg(&mock_instruction)); |
| 311 | 306 | |
| ... | ... | @@ -336,14 +331,12 @@ test "getReg" { |
| 336 | 331 | std.testing.expect(!function.register_manager.isRegAllocated(.r2)); |
| 337 | 332 | std.testing.expect(!function.register_manager.isRegAllocated(.r3)); |
| 338 | 333 | |
| 339 | try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2); | |
| 340 | 334 | try function.register_manager.getReg(.r3, &mock_instruction); |
| 341 | 335 | |
| 342 | 336 | std.testing.expect(!function.register_manager.isRegAllocated(.r2)); |
| 343 | 337 | std.testing.expect(function.register_manager.isRegAllocated(.r3)); |
| 344 | 338 | |
| 345 | 339 | // Spill r3 |
| 346 | try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2); | |
| 347 | 340 | try function.register_manager.getReg(.r3, &mock_instruction); |
| 348 | 341 | |
| 349 | 342 | std.testing.expect(!function.register_manager.isRegAllocated(.r2)); |
src/stage1/all_types.hpp+1| ... | ... | @@ -84,6 +84,7 @@ enum CallingConvention { |
| 84 | 84 | CallingConventionAPCS, |
| 85 | 85 | CallingConventionAAPCS, |
| 86 | 86 | CallingConventionAAPCSVFP, |
| 87 | CallingConventionSysV | |
| 87 | 88 | }; |
| 88 | 89 | |
| 89 | 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 | 974 | case CallingConventionAAPCS: return "AAPCS"; |
| 975 | 975 | case CallingConventionAAPCSVFP: return "AAPCSVFP"; |
| 976 | 976 | case CallingConventionInline: return "Inline"; |
| 977 | case CallingConventionSysV: return "SysV"; | |
| 977 | 978 | } |
| 978 | 979 | zig_unreachable(); |
| 979 | 980 | } |
| ... | ... | @@ -995,6 +996,7 @@ bool calling_convention_allows_zig_types(CallingConvention cc) { |
| 995 | 996 | case CallingConventionAPCS: |
| 996 | 997 | case CallingConventionAAPCS: |
| 997 | 998 | case CallingConventionAAPCSVFP: |
| 999 | case CallingConventionSysV: | |
| 998 | 1000 | return false; |
| 999 | 1001 | } |
| 1000 | 1002 | zig_unreachable(); |
| ... | ... | @@ -1969,6 +1971,10 @@ Error emit_error_unless_callconv_allowed_for_target(CodeGen *g, AstNode *source_ |
| 1969 | 1971 | case CallingConventionAAPCSVFP: |
| 1970 | 1972 | if (!target_is_arm(g->zig_target)) |
| 1971 | 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 | 1979 | if (allowed_platforms != nullptr) { |
| 1974 | 1980 | add_node_error(g, source_node, buf_sprintf( |
| ... | ... | @@ -3805,6 +3811,7 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) { |
| 3805 | 3811 | case CallingConventionAPCS: |
| 3806 | 3812 | case CallingConventionAAPCS: |
| 3807 | 3813 | case CallingConventionAAPCSVFP: |
| 3814 | case CallingConventionSysV: | |
| 3808 | 3815 | add_fn_export(g, fn_table_entry, buf_ptr(&fn_table_entry->symbol_name), |
| 3809 | 3816 | GlobalLinkageIdStrong, fn_cc); |
| 3810 | 3817 | break; |
| ... | ... | @@ -4769,11 +4776,11 @@ Error type_is_nonnull_ptr2(CodeGen *g, ZigType *type, bool *result) { |
| 4769 | 4776 | return ErrorNone; |
| 4770 | 4777 | } |
| 4771 | 4778 | |
| 4772 | static uint32_t get_async_frame_align_bytes(CodeGen *g) { | |
| 4773 | uint32_t a = g->pointer_size_bytes * 2; | |
| 4774 | // promises have at least alignment 8 so that we can have 3 extra bits when doing atomicrmw | |
| 4775 | if (a < 8) a = 8; | |
| 4776 | return a; | |
| 4779 | uint32_t get_async_frame_align_bytes(CodeGen *g) { | |
| 4780 | // Due to how the frame structure is built the minimum alignment is the one | |
| 4781 | // of a usize (or pointer). | |
| 4782 | // label (grep this): [fn_frame_struct_layout] | |
| 4783 | return max(g->builtin_types.entry_usize->abi_align, target_fn_align(g->zig_target)); | |
| 4777 | 4784 | } |
| 4778 | 4785 | |
| 4779 | 4786 | uint32_t get_ptr_align(CodeGen *g, ZigType *type) { |
| ... | ... | @@ -4789,11 +4796,8 @@ uint32_t get_ptr_align(CodeGen *g, ZigType *type) { |
| 4789 | 4796 | return (ptr_type->data.pointer.explicit_alignment == 0) ? |
| 4790 | 4797 | get_abi_alignment(g, ptr_type->data.pointer.child_type) : ptr_type->data.pointer.explicit_alignment; |
| 4791 | 4798 | } else if (ptr_type->id == ZigTypeIdFn) { |
| 4792 | // I tried making this use LLVMABIAlignmentOfType but it trips this assertion in LLVM: | |
| 4793 | // "Cannot getTypeInfo() on a type that is unsized!" | |
| 4794 | // when getting the alignment of `?fn() callconv(.C) void`. | |
| 4795 | // See http://lists.llvm.org/pipermail/llvm-dev/2018-September/126142.html | |
| 4796 | return (ptr_type->data.fn.fn_type_id.alignment == 0) ? 1 : ptr_type->data.fn.fn_type_id.alignment; | |
| 4799 | return (ptr_type->data.fn.fn_type_id.alignment == 0) ? | |
| 4800 | target_fn_ptr_align(g->zig_target) : ptr_type->data.fn.fn_type_id.alignment; | |
| 4797 | 4801 | } else if (ptr_type->id == ZigTypeIdAnyFrame) { |
| 4798 | 4802 | return get_async_frame_align_bytes(g); |
| 4799 | 4803 | } else { |
src/stage1/analyze.hpp+1| ... | ... | @@ -47,6 +47,7 @@ ZigType *get_test_fn_type(CodeGen *g); |
| 47 | 47 | ZigType *get_any_frame_type(CodeGen *g, ZigType *result_type); |
| 48 | 48 | bool handle_is_ptr(CodeGen *g, ZigType *type_entry); |
| 49 | 49 | Error emit_error_unless_callconv_allowed_for_target(CodeGen *g, AstNode *source_node, CallingConvention cc); |
| 50 | uint32_t get_async_frame_align_bytes(CodeGen *g); | |
| 50 | 51 | |
| 51 | 52 | bool type_has_bits(CodeGen *g, ZigType *type_entry); |
| 52 | 53 | Error 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 | 204 | case CallingConventionSignal: |
| 205 | 205 | assert(g->zig_target->arch == ZigLLVM_avr); |
| 206 | 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 | 211 | zig_unreachable(); |
| 209 | 212 | } |
| ... | ... | @@ -348,6 +351,7 @@ static bool cc_want_sret_attr(CallingConvention cc) { |
| 348 | 351 | case CallingConventionAPCS: |
| 349 | 352 | case CallingConventionAAPCS: |
| 350 | 353 | case CallingConventionAAPCSVFP: |
| 354 | case CallingConventionSysV: | |
| 351 | 355 | return true; |
| 352 | 356 | case CallingConventionAsync: |
| 353 | 357 | case CallingConventionUnspecified: |
| ... | ... | @@ -9079,6 +9083,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { |
| 9079 | 9083 | static_assert(CallingConventionAPCS == 11, ""); |
| 9080 | 9084 | static_assert(CallingConventionAAPCS == 12, ""); |
| 9081 | 9085 | static_assert(CallingConventionAAPCSVFP == 13, ""); |
| 9086 | static_assert(CallingConventionSysV == 14, ""); | |
| 9082 | 9087 | |
| 9083 | 9088 | static_assert(BuiltinPtrSizeOne == 0, ""); |
| 9084 | 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 | 19216 | case CallingConventionAPCS: |
| 19217 | 19217 | case CallingConventionAAPCS: |
| 19218 | 19218 | case CallingConventionAAPCSVFP: |
| 19219 | case CallingConventionSysV: | |
| 19219 | 19220 | add_fn_export(ira->codegen, fn_entry, buf_ptr(symbol_name), global_linkage_id, cc); |
| 19220 | 19221 | fn_entry->section_name = section_name; |
| 19221 | 19222 | break; |
| ... | ... | @@ -20659,8 +20660,12 @@ static IrInstGen *analyze_casted_new_stack(IrAnalyze *ira, IrInst* source_instr, |
| 20659 | 20660 | get_fn_frame_type(ira->codegen, fn_entry), false); |
| 20660 | 20661 | return ir_implicit_cast(ira, new_stack, needed_frame_type); |
| 20661 | 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 | 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 | 20669 | ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr); |
| 20665 | 20670 | ira->codegen->need_frame_size_prefix_data = true; |
| 20666 | 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 | 26084 | fields[0]->special = ConstValSpecialStatic; |
| 26080 | 26085 | fields[0]->type = get_builtin_type(ira->codegen, "CallingConvention"); |
| 26081 | 26086 | bigint_init_unsigned(&fields[0]->data.x_enum_tag, type_entry->data.fn.fn_type_id.cc); |
| 26082 | // alignment: u29 | |
| 26087 | // alignment: comptime_int | |
| 26083 | 26088 | ensure_field_index(result->type, "alignment", 1); |
| 26084 | 26089 | fields[1]->special = ConstValSpecialStatic; |
| 26085 | 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 | 26092 | // is_generic: bool |
| 26088 | 26093 | ensure_field_index(result->type, "is_generic", 2); |
| 26089 | 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 | 30100 | fn_type_id.alignment = align_bytes; |
| 30096 | 30101 | result_type = get_fn_type(ira->codegen, &fn_type_id); |
| 30097 | 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 | 30104 | result_type = target_type; |
| 30100 | 30105 | } else { |
| 30101 | 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 | 1253 | target->arch == ZigLLVM_ppc64le; |
| 1254 | 1254 | } |
| 1255 | 1255 | |
| 1256 | // Returns the minimum alignment for every function pointer on the given | |
| 1257 | // architecture. | |
| 1258 | unsigned 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. | |
| 1256 | 1264 | unsigned 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 | 98 | void target_libc_enum(size_t index, ZigTarget *out_target); |
| 99 | 99 | bool target_libc_needs_crti_crtn(const ZigTarget *target); |
| 100 | 100 | |
| 101 | unsigned target_fn_ptr_align(const ZigTarget *target); | |
| 101 | 102 | unsigned target_fn_align(const ZigTarget *target); |
| 102 | 103 | |
| 103 | 104 | #endif |
src/translate_c.zig+27-12| ... | ... | @@ -8,6 +8,7 @@ const ctok = std.c.tokenizer; |
| 8 | 8 | const CToken = std.c.Token; |
| 9 | 9 | const mem = std.mem; |
| 10 | 10 | const math = std.math; |
| 11 | const meta = std.meta; | |
| 11 | 12 | const ast = @import("translate_c/ast.zig"); |
| 12 | 13 | const Node = ast.Node; |
| 13 | 14 | const Tag = Node.Tag; |
| ... | ... | @@ -1741,7 +1742,7 @@ fn transImplicitCastExpr( |
| 1741 | 1742 | } |
| 1742 | 1743 | |
| 1743 | 1744 | fn isBuiltinDefined(name: []const u8) bool { |
| 1744 | inline for (std.meta.declarations(c_builtins)) |decl| { | |
| 1745 | inline for (meta.declarations(c_builtins)) |decl| { | |
| 1745 | 1746 | if (std.mem.eql(u8, name, decl.name)) return true; |
| 1746 | 1747 | } |
| 1747 | 1748 | return false; |
| ... | ... | @@ -3157,7 +3158,7 @@ const ClangFunctionType = union(enum) { |
| 3157 | 3158 | NoProto: *const clang.FunctionType, |
| 3158 | 3159 | |
| 3159 | 3160 | fn getReturnType(self: @This()) clang.QualType { |
| 3160 | switch (@as(std.meta.Tag(@This()), self)) { | |
| 3161 | switch (@as(meta.Tag(@This()), self)) { | |
| 3161 | 3162 | .Proto => return self.Proto.getReturnType(), |
| 3162 | 3163 | .NoProto => return self.NoProto.getReturnType(), |
| 3163 | 3164 | } |
| ... | ... | @@ -3539,7 +3540,7 @@ fn transCPtrCast( |
| 3539 | 3540 | expr |
| 3540 | 3541 | else blk: { |
| 3541 | 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 | 3544 | const align_cast = try Tag.align_cast.create(c.arena, .{ .lhs = alignof, .rhs = expr }); |
| 3544 | 3545 | break :blk align_cast; |
| 3545 | 3546 | }; |
| ... | ... | @@ -3547,9 +3548,22 @@ fn transCPtrCast( |
| 3547 | 3548 | } |
| 3548 | 3549 | } |
| 3549 | 3550 | |
| 3550 | fn transFloatingLiteral(c: *Context, scope: *Scope, stmt: *const clang.FloatingLiteral, used: ResultUsed) TransError!Node { | |
| 3551 | fn 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 | 3565 | // TODO use something more accurate |
| 3552 | var dbl = stmt.getValueAsApproximateDouble(); | |
| 3566 | var dbl = expr.getValueAsApproximateDouble(); | |
| 3553 | 3567 | const is_negative = dbl < 0; |
| 3554 | 3568 | if (is_negative) dbl = -dbl; |
| 3555 | 3569 | const str = if (dbl == std.math.floor(dbl)) |
| ... | ... | @@ -4093,7 +4107,7 @@ fn transCreateNodeAPInt(c: *Context, int: *const clang.APSInt) !Node { |
| 4093 | 4107 | } |
| 4094 | 4108 | |
| 4095 | 4109 | fn 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 | 4111 | const str = try std.fmt.allocPrint(c.arena, fmt_s, .{num}); |
| 4098 | 4112 | if (num_kind == .float) |
| 4099 | 4113 | return Tag.float_literal.create(c.arena, str) |
| ... | ... | @@ -4402,6 +4416,7 @@ fn transCC( |
| 4402 | 4416 | .X86ThisCall => return CallingConvention.Thiscall, |
| 4403 | 4417 | .AAPCS => return CallingConvention.AAPCS, |
| 4404 | 4418 | .AAPCS_VFP => return CallingConvention.AAPCSVFP, |
| 4419 | .X86_64SysV => return CallingConvention.SysV, | |
| 4405 | 4420 | else => return fail( |
| 4406 | 4421 | c, |
| 4407 | 4422 | error.UnsupportedType, |
| ... | ... | @@ -4848,12 +4863,12 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!Node { |
| 4848 | 4863 | // make the output less noisy by skipping promoteIntLiteral where |
| 4849 | 4864 | // it's guaranteed to not be required because of C standard type constraints |
| 4850 | 4865 | const guaranteed_to_fit = switch (suffix) { |
| 4851 | .none => if (math.cast(i16, value)) |_| true else |_| false, | |
| 4852 | .u => if (math.cast(u16, value)) |_| true else |_| false, | |
| 4853 | .l => if (math.cast(i32, value)) |_| true else |_| false, | |
| 4854 | .lu => if (math.cast(u32, value)) |_| true else |_| false, | |
| 4855 | .ll => if (math.cast(i64, value)) |_| true else |_| false, | |
| 4856 | .llu => if (math.cast(u64, value)) |_| true else |_| false, | |
| 4866 | .none => !meta.isError(math.cast(i16, value)), | |
| 4867 | .u => !meta.isError(math.cast(u16, value)), | |
| 4868 | .l => !meta.isError(math.cast(i32, value)), | |
| 4869 | .lu => !meta.isError(math.cast(u32, value)), | |
| 4870 | .ll => !meta.isError(math.cast(i64, value)), | |
| 4871 | .llu => !meta.isError(math.cast(u64, value)), | |
| 4857 | 4872 | .f => unreachable, |
| 4858 | 4873 | }; |
| 4859 | 4874 |
src/translate_c/ast.zig+11-1| ... | ... | @@ -120,8 +120,11 @@ pub const Node = extern union { |
| 120 | 120 | std_math_Log2Int, |
| 121 | 121 | /// @intCast(lhs, rhs) |
| 122 | 122 | int_cast, |
| 123 | /// @rem(lhs, rhs) | |
| 123 | /// @import("std").meta.promoteIntLiteral(value, type, radix) | |
| 124 | 124 | std_meta_promoteIntLiteral, |
| 125 | /// @import("std").meta.alignment(value) | |
| 126 | std_meta_alignment, | |
| 127 | /// @rem(lhs, rhs) | |
| 125 | 128 | rem, |
| 126 | 129 | /// @divTrunc(lhs, rhs) |
| 127 | 130 | div_trunc, |
| ... | ... | @@ -260,6 +263,7 @@ pub const Node = extern union { |
| 260 | 263 | .switch_else, |
| 261 | 264 | .block_single, |
| 262 | 265 | .std_meta_sizeof, |
| 266 | .std_meta_alignment, | |
| 263 | 267 | .bool_to_int, |
| 264 | 268 | .sizeof, |
| 265 | 269 | .alignof, |
| ... | ... | @@ -876,6 +880,11 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { |
| 876 | 880 | const import_node = try renderStdImport(c, "meta", "promoteIntLiteral"); |
| 877 | 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 | 888 | .std_meta_sizeof => { |
| 880 | 889 | const payload = node.castTag(.std_meta_sizeof).?.data; |
| 881 | 890 | const import_node = try renderStdImport(c, "meta", "sizeof"); |
| ... | ... | @@ -2144,6 +2153,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { |
| 2144 | 2153 | .typeof, |
| 2145 | 2154 | .typeinfo, |
| 2146 | 2155 | .std_meta_sizeof, |
| 2156 | .std_meta_alignment, | |
| 2147 | 2157 | .std_meta_cast, |
| 2148 | 2158 | .std_meta_promoteIntLiteral, |
| 2149 | 2159 | .std_meta_vector, |
src/zig_clang.cpp+5| ... | ... | @@ -2528,6 +2528,11 @@ double ZigClangFloatingLiteral_getValueAsApproximateDouble(const ZigClangFloatin |
| 2528 | 2528 | return casted->getValueAsApproximateDouble(); |
| 2529 | 2529 | } |
| 2530 | 2530 | |
| 2531 | ZigClangAPFloatBase_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 | ||
| 2531 | 2536 | enum ZigClangStringLiteral_StringKind ZigClangStringLiteral_getKind(const struct ZigClangStringLiteral *self) { |
| 2532 | 2537 | auto casted = reinterpret_cast<const clang::StringLiteral *>(self); |
| 2533 | 2538 | return (ZigClangStringLiteral_StringKind)casted->getKind(); |
src/zig_clang.h+11| ... | ... | @@ -881,6 +881,16 @@ enum ZigClangAPFloat_roundingMode { |
| 881 | 881 | ZigClangAPFloat_roundingMode_Invalid = -1, |
| 882 | 882 | }; |
| 883 | 883 | |
| 884 | enum 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 | ||
| 884 | 894 | enum ZigClangStringLiteral_StringKind { |
| 885 | 895 | ZigClangStringLiteral_StringKind_Ascii, |
| 886 | 896 | ZigClangStringLiteral_StringKind_Wide, |
| ... | ... | @@ -1142,6 +1152,7 @@ ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangDeclStmt_getBeginLoc(const st |
| 1142 | 1152 | ZIG_EXTERN_C unsigned ZigClangAPFloat_convertToHexString(const struct ZigClangAPFloat *self, char *DST, |
| 1143 | 1153 | unsigned HexDigits, bool UpperCase, enum ZigClangAPFloat_roundingMode RM); |
| 1144 | 1154 | ZIG_EXTERN_C double ZigClangFloatingLiteral_getValueAsApproximateDouble(const ZigClangFloatingLiteral *self); |
| 1155 | ZIG_EXTERN_C ZigClangAPFloatBase_Semantics ZigClangFloatingLiteral_getRawSemantics(const ZigClangFloatingLiteral *self); | |
| 1145 | 1156 | |
| 1146 | 1157 | ZIG_EXTERN_C enum ZigClangStringLiteral_StringKind ZigClangStringLiteral_getKind(const struct ZigClangStringLiteral *self); |
| 1147 | 1158 | ZIG_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 | 2136 | \\} |
| 2137 | 2137 | \\fn func() callconv(.Async) void {} |
| 2138 | 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 | }); |
| 2141 | 2143 | |
| 2142 | 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 | 306 | fn testFunction() void { |
| 307 | 307 | const fn_info = @typeInfo(@TypeOf(foo)); |
| 308 | 308 | expect(fn_info == .Fn); |
| 309 | expect(fn_info.Fn.alignment == 0); | |
| 309 | expect(fn_info.Fn.alignment > 0); | |
| 310 | 310 | expect(fn_info.Fn.calling_convention == .C); |
| 311 | 311 | expect(!fn_info.Fn.is_generic); |
| 312 | 312 | expect(fn_info.Fn.args.len == 2); |
test/stage2/riscv64.zig created+45| ... | ... | @@ -0,0 +1,45 @@ |
| 1 | const std = @import("std"); | |
| 2 | const TestContext = @import("../../src/test.zig").TestContext; | |
| 3 | ||
| 4 | const linux_riscv64 = std.zig.CrossTarget{ | |
| 5 | .cpu_arch = .riscv64, | |
| 6 | .os_tag = .linux, | |
| 7 | }; | |
| 8 | ||
| 9 | pub 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 | .os_tag = .linux, |
| 12 | 12 | }; |
| 13 | 13 | |
| 14 | const linux_riscv64 = std.zig.CrossTarget{ | |
| 15 | .cpu_arch = .riscv64, | |
| 16 | .os_tag = .linux, | |
| 17 | }; | |
| 18 | ||
| 19 | 14 | pub fn addCases(ctx: *TestContext) !void { |
| 20 | 15 | try @import("cbe.zig").addCases(ctx); |
| 21 | 16 | try @import("spu-ii.zig").addCases(ctx); |
| ... | ... | @@ -24,6 +19,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 24 | 19 | try @import("llvm.zig").addCases(ctx); |
| 25 | 20 | try @import("wasm.zig").addCases(ctx); |
| 26 | 21 | try @import("darwin.zig").addCases(ctx); |
| 22 | try @import("riscv64.zig").addCases(ctx); | |
| 27 | 23 | |
| 28 | 24 | { |
| 29 | 25 | var case = ctx.exe("hello world with updates", linux_x64); |
| ... | ... | @@ -137,42 +133,6 @@ pub fn addCases(ctx: *TestContext) !void { |
| 137 | 133 | ); |
| 138 | 134 | } |
| 139 | 135 | |
| 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 | 137 | var case = ctx.exe("adding numbers at comptime", linux_x64); |
| 178 | 138 | case.addCompareOutput( |
test/translate_c.zig+9-11| ... | ... | @@ -1363,7 +1363,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 1363 | 1363 | , &[_][]const u8{ |
| 1364 | 1364 | \\pub export fn ptrcast() [*c]f32 { |
| 1365 | 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 | }); |
| 1369 | 1369 | |
| ... | ... | @@ -1387,16 +1387,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 1387 | 1387 | \\pub export fn test_ptr_cast() void { |
| 1388 | 1388 | \\ var p: ?*c_void = undefined; |
| 1389 | 1389 | \\ { |
| 1390 | \\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@alignOf(u8), p)); | |
| 1391 | \\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@alignOf(c_short), p)); | |
| 1392 | \\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@alignOf(c_int), p)); | |
| 1393 | \\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@alignOf(c_longlong), p)); | |
| 1390 | \\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@import("std").meta.alignment(u8), p)); | |
| 1391 | \\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@import("std").meta.alignment(c_short), p)); | |
| 1392 | \\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@import("std").meta.alignment(c_int), p)); | |
| 1393 | \\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@import("std").meta.alignment(c_longlong), p)); | |
| 1394 | 1394 | \\ } |
| 1395 | 1395 | \\ { |
| 1396 | \\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@alignOf(u8), p)); | |
| 1397 | \\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@alignOf(c_short), p)); | |
| 1398 | \\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@alignOf(c_int), p)); | |
| 1399 | \\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@alignOf(c_longlong), p)); | |
| 1396 | \\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@import("std").meta.alignment(u8), p)); | |
| 1397 | \\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@import("std").meta.alignment(c_short), p)); | |
| 1398 | \\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@import("std").meta.alignment(c_int), p)); | |
| 1399 | \\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@import("std").meta.alignment(c_longlong), p)); | |
| 1400 | 1400 | \\ } |
| 1401 | 1401 | \\} |
| 1402 | 1402 | }); |
| ... | ... | @@ -3028,7 +3028,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 3028 | 3028 | \\void call() { |
| 3029 | 3029 | \\ fn_int(3.0f); |
| 3030 | 3030 | \\ fn_int(3.0); |
| 3031 | \\ fn_int(3.0L); | |
| 3032 | 3031 | \\ fn_int('ABCD'); |
| 3033 | 3032 | \\ fn_f32(3); |
| 3034 | 3033 | \\ fn_f64(3); |
| ... | ... | @@ -3053,7 +3052,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 3053 | 3052 | \\pub export fn call() void { |
| 3054 | 3053 | \\ fn_int(@floatToInt(c_int, 3.0)); |
| 3055 | 3054 | \\ fn_int(@floatToInt(c_int, 3.0)); |
| 3056 | \\ fn_int(@floatToInt(c_int, 3.0)); | |
| 3057 | 3055 | \\ fn_int(@as(c_int, 1094861636)); |
| 3058 | 3056 | \\ fn_f32(@intToFloat(f32, @as(c_int, 3))); |
| 3059 | 3057 | \\ fn_f64(@intToFloat(f64, @as(c_int, 3))); |