Merge remote-tracking branch 'origin/master' into stage2-whole-file-astgen

In particular I wanted to take advantage of the new hex float parsing
code.
This commit is contained in:
Andrew Kelley 2021-04-28 14:53:50 -07:00
commit df24ce52b1
52 changed files with 1672 additions and 483 deletions

View File

@ -44,6 +44,7 @@ pub fn build(b: *Builder) !void {
const fmt_build_zig = b.addFmt(&[_][]const u8{"build.zig"});
const skip_debug = b.option(bool, "skip-debug", "Main test suite skips debug builds") orelse false;
const skip_release = b.option(bool, "skip-release", "Main test suite skips release builds") orelse false;
const skip_release_small = b.option(bool, "skip-release-small", "Main test suite skips release-small builds") orelse skip_release;
const skip_release_fast = b.option(bool, "skip-release-fast", "Main test suite skips release-fast builds") orelse skip_release;
@ -240,8 +241,10 @@ pub fn build(b: *Builder) !void {
var chosen_modes: [4]builtin.Mode = undefined;
var chosen_mode_index: usize = 0;
chosen_modes[chosen_mode_index] = builtin.Mode.Debug;
chosen_mode_index += 1;
if (!skip_debug) {
chosen_modes[chosen_mode_index] = builtin.Mode.Debug;
chosen_mode_index += 1;
}
if (!skip_release_safe) {
chosen_modes[chosen_mode_index] = builtin.Mode.ReleaseSafe;
chosen_mode_index += 1;

View File

@ -6,7 +6,38 @@ platform:
arch: arm64
steps:
- name: build-and-test
- name: build
image: ziglang/static-base:llvm12-aarch64-1
commands:
- ./ci/drone/linux_script_build
- name: test-1
depends_on:
- build
image: ziglang/static-base:llvm12-aarch64-1
commands:
- ./ci/drone/linux_script_test 1
- name: test-2
depends_on:
- build
image: ziglang/static-base:llvm12-aarch64-1
commands:
- ./ci/drone/linux_script_test 2
- name: test-3
depends_on:
- build
image: ziglang/static-base:llvm12-aarch64-1
commands:
- ./ci/drone/linux_script_test 3
- name: finalize
depends_on:
- build
- test-1
- test-2
- test-3
image: ziglang/static-base:llvm12-aarch64-1
environment:
SRHT_OAUTH_TOKEN:
@ -16,4 +47,4 @@ steps:
AWS_SECRET_ACCESS_KEY:
from_secret: AWS_SECRET_ACCESS_KEY
commands:
- ./ci/drone/linux_script
- ./ci/drone/linux_script_finalize

View File

@ -1,65 +0,0 @@
#!/bin/sh
set -x
set -e
TRIPLEARCH="$(uname -m)"
BUILDDIR="$(pwd)"
DISTDIR="$(pwd)/dist"
apk update
apk add py3-pip xz perl-utils jq curl samurai
pip3 install s3cmd
# Make the `zig version` number consistent.
# This will affect the cmake command below.
git config core.abbrev 9
git fetch --unshallow || true
git fetch --tags
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release "-DCMAKE_INSTALL_PREFIX=$DISTDIR" -DZIG_STATIC=ON -DCMAKE_PREFIX_PATH=/deps/local -GNinja
samu install
# run-translated-c tests are skipped due to: https://github.com/ziglang/zig/issues/8537
./zig build test \
-Dskip-release \
-Dskip-non-native \
-Dskip-compile-errors \
-Dskip-run-translated-c
if [ -z "$DRONE_PULL_REQUEST" ]; then
mv ../LICENSE "$DISTDIR/"
mv ../zig-cache/langref.html "$DISTDIR/"
mv "$DISTDIR/bin/zig" "$DISTDIR/"
rmdir "$DISTDIR/bin"
GITBRANCH="$DRONE_BRANCH"
VERSION="$("$DISTDIR/zig" version)"
DIRNAME="zig-linux-$TRIPLEARCH-$VERSION"
TARBALL="$DIRNAME.tar.xz"
mv "$DISTDIR" "$DIRNAME"
tar cfJ "$TARBALL" "$DIRNAME"
s3cmd put -P --add-header="cache-control: public, max-age=31536000, immutable" "$TARBALL" s3://ziglang.org/builds/
SHASUM=$(shasum -a 256 $TARBALL | cut '-d ' -f1)
BYTESIZE=$(wc -c < $TARBALL)
JSONFILE="$TRIPLEARCH-linux-$GITBRANCH.json"
touch $JSONFILE
echo "{\"tarball\": \"$TARBALL\"," >>$JSONFILE
echo "\"shasum\": \"$SHASUM\"," >>$JSONFILE
echo "\"size\": \"$BYTESIZE\"}" >>$JSONFILE
s3cmd put -P --add-header="Cache-Control: max-age=0, must-revalidate" "$JSONFILE" "s3://ziglang.org/builds/$JSONFILE"
s3cmd put -P "$JSONFILE" "s3://ziglang.org/builds/$TRIPLEARCH-linux-$VERSION.json"
if [ "$GITBRANCH" = "master" ]; then
# avoid leaking oauth token
set +x
cd "$BUILDDIR"
./ci/srht/on_master_success "$VERSION" "$SRHT_OAUTH_TOKEN"
fi
fi

22
ci/drone/linux_script_base Executable file
View File

@ -0,0 +1,22 @@
#!/bin/sh
# https://docs.drone.io/pipeline/docker/syntax/workspace/
#
# Drone automatically creates a temporary volume, known as your workspace,
# where it clones your repository. The workspace is the current working
# directory for each step in your pipeline.
#
# Because the workspace is a volume, filesystem changes are persisted between
# pipeline steps. In other words, individual steps can communicate and share
# state using the filesystem.
#
# Workspace volumes are ephemeral. They are created when the pipeline starts
# and destroyed after the pipeline completes.
set -x
set -e
TRIPLEARCH="$(uname -m)"
DISTDIR="$DRONE_WORKSPACE/dist"
export ZIG_GLOBAL_CACHE_DIR="$DRONE_WORKSPACE/zig-cache"

18
ci/drone/linux_script_build Executable file
View File

@ -0,0 +1,18 @@
#!/bin/sh
. ./ci/drone/linux_script_base
apk update
apk add samurai
# Make the `zig version` number consistent.
# This will affect the cmake command below.
git config core.abbrev 9
git fetch --unshallow || true
git fetch --tags
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release "-DCMAKE_INSTALL_PREFIX=$DISTDIR" -DZIG_STATIC=ON -DCMAKE_PREFIX_PATH=/deps/local -GNinja
samu install

46
ci/drone/linux_script_finalize Executable file
View File

@ -0,0 +1,46 @@
#!/bin/sh
. ./ci/drone/linux_script_base
if [ -n "$DRONE_PULL_REQUEST" ]; then
exit 0
fi
apk update
apk add py3-pip xz perl-utils jq curl samurai
pip3 install s3cmd
cd build
mv ../LICENSE "$DISTDIR/"
# docs are disabled due to: https://github.com/ziglang/zig/issues/8597
#mv ../zig-cache/langref.html "$DISTDIR/"
mv "$DISTDIR/bin/zig" "$DISTDIR/"
rmdir "$DISTDIR/bin"
GITBRANCH="$DRONE_BRANCH"
VERSION="$("$DISTDIR/zig" version)"
DIRNAME="zig-linux-$TRIPLEARCH-$VERSION"
TARBALL="$DIRNAME.tar.xz"
mv "$DISTDIR" "$DIRNAME"
tar cfJ "$TARBALL" "$DIRNAME"
s3cmd put -P --add-header="cache-control: public, max-age=31536000, immutable" "$TARBALL" s3://ziglang.org/builds/
SHASUM=$(shasum -a 256 $TARBALL | cut '-d ' -f1)
BYTESIZE=$(wc -c < $TARBALL)
JSONFILE="tarball.json"
touch $JSONFILE
echo "{\"tarball\": \"$TARBALL\"," >>$JSONFILE
echo "\"shasum\": \"$SHASUM\"," >>$JSONFILE
echo "\"size\": \"$BYTESIZE\"}" >>$JSONFILE
s3cmd put -P "$JSONFILE" "s3://ziglang.org/builds/$TRIPLEARCH-linux-$VERSION.json"
if [ "$GITBRANCH" = "master" ]; then
# avoid leaking oauth token
set +x
cd "$DRONE_WORKSPACE"
./ci/srht/on_master_success "$VERSION" "$SRHT_OAUTH_TOKEN"
fi

46
ci/drone/linux_script_test Executable file
View File

@ -0,0 +1,46 @@
#!/bin/sh
. ./ci/drone/linux_script_base
# only release-fast builds of test suite due to: https://github.com/ziglang/zig/issues/8597
#
# Some test suite components will be missing because they do not support
# forcing -OReleaseFast
#
# see `zig build --help` for the full list of test-* components
case "$1" in
1)
steps="\
test-stage2 \
test-fmt \
test-behavior"
;;
2)
steps="test-std"
;;
3)
steps="\
test-compiler-rt \
test-minilibc \
test-compare-output \
test-translate-c \
test-run-translated-c"
;;
'')
echo "error: expecting test group argument"
exit 1
;;
*)
echo "error: unknown test group: $1"
exit 1
;;
esac
# only release-fast builds of test suite due to: https://github.com/ziglang/zig/issues/8597
./build/zig build \
-Drelease \
-Dskip-debug \
-Dskip-release-small \
-Dskip-release-safe \
-Dskip-non-native \
$steps

View File

@ -199,7 +199,8 @@ pub fn spawn(comptime startFn: anytype, context: SpawnContextType(@TypeOf(startF
inner: Context,
};
fn threadMain(raw_arg: windows.LPVOID) callconv(.C) windows.DWORD {
const arg = if (@sizeOf(Context) == 0) {} else @ptrCast(*Context, @alignCast(@alignOf(Context), raw_arg)).*;
const arg = if (@sizeOf(Context) == 0) undefined //
else @ptrCast(*Context, @alignCast(@alignOf(Context), raw_arg)).*;
switch (@typeInfo(@typeInfo(@TypeOf(startFn)).Fn.return_type.?)) {
.NoReturn => {
@ -260,7 +261,8 @@ pub fn spawn(comptime startFn: anytype, context: SpawnContextType(@TypeOf(startF
const MainFuncs = struct {
fn linuxThreadMain(ctx_addr: usize) callconv(.C) u8 {
const arg = if (@sizeOf(Context) == 0) {} else @intToPtr(*const Context, ctx_addr).*;
const arg = if (@sizeOf(Context) == 0) undefined //
else @intToPtr(*Context, ctx_addr).*;
switch (@typeInfo(@typeInfo(@TypeOf(startFn)).Fn.return_type.?)) {
.NoReturn => {
@ -292,7 +294,8 @@ pub fn spawn(comptime startFn: anytype, context: SpawnContextType(@TypeOf(startF
}
}
fn posixThreadMain(ctx: ?*c_void) callconv(.C) ?*c_void {
const arg = if (@sizeOf(Context) == 0) {} else @ptrCast(*Context, @alignCast(@alignOf(Context), ctx)).*;
const arg = if (@sizeOf(Context) == 0) undefined //
else @ptrCast(*Context, @alignCast(@alignOf(Context), ctx)).*;
switch (@typeInfo(@typeInfo(@TypeOf(startFn)).Fn.return_type.?)) {
.NoReturn => {

View File

@ -166,6 +166,7 @@ pub const CallingConvention = enum {
APCS,
AAPCS,
AAPCSVFP,
SysV
};
/// This data structure is used by the Zig language code generation and

View File

@ -69,3 +69,51 @@ pub const pthread_rwlock_t = extern struct {
writer_count: i32 = 0,
waiters: [2]?*c_void = [_]?*c_void{ null, null },
};
pub const EAI = extern enum(c_int) {
/// address family for hostname not supported
ADDRFAMILY = 1,
/// name could not be resolved at this time
AGAIN = 2,
/// flags parameter had an invalid value
BADFLAGS = 3,
/// non-recoverable failure in name resolution
FAIL = 4,
/// address family not recognized
FAMILY = 5,
/// memory allocation failure
MEMORY = 6,
/// no address associated with hostname
NODATA = 7,
/// name does not resolve
NONAME = 8,
/// service not recognized for socket type
SERVICE = 9,
/// intended socket type was not recognized
SOCKTYPE = 10,
/// system error returned in errno
SYSTEM = 11,
/// invalid value for hints
BADHINTS = 12,
/// resolved protocol is unknown
PROTOCOL = 13,
/// argument buffer overflow
OVERFLOW = 14,
_,
};
pub const EAI_MAX = 15;

View File

@ -264,7 +264,7 @@ pub const ChildProcess = struct {
// TODO collect output in a deadlock-avoiding way on Windows.
// https://github.com/ziglang/zig/issues/6343
if (builtin.os.tag == .windows) {
if (builtin.os.tag == .windows or builtin.os.tag == .haiku) {
const stdout_in = child.stdout.?.reader();
const stderr_in = child.stderr.?.reader();

View File

@ -75,16 +75,8 @@ pub const Edwards25519 = struct {
.is_base = true,
};
/// The edwards25519 neutral element.
pub const neutralElement = Edwards25519{
.x = Fe{ .limbs = .{ 2251799813685229, 2251799813685247, 2251799813685247, 2251799813685247, 2251799813685247 } },
.y = Fe{ .limbs = .{ 1507481815385608, 2223447444246085, 1083941587175919, 2059929906842505, 1581435440146976 } },
.z = Fe{ .limbs = .{ 1507481815385608, 2223447444246085, 1083941587175919, 2059929906842505, 1581435440146976 } },
.t = Fe{ .limbs = .{ 2251799813685229, 2251799813685247, 2251799813685247, 2251799813685247, 2251799813685247 } },
.is_base = false,
};
const identityElement = Edwards25519{ .x = Fe.zero, .y = Fe.one, .z = Fe.one, .t = Fe.zero };
pub const neutralElement = @compileError("deprecated: use identityElement instead");
pub const identityElement = Edwards25519{ .x = Fe.zero, .y = Fe.one, .z = Fe.one, .t = Fe.zero };
/// Reject the neutral element.
pub fn rejectIdentity(p: Edwards25519) IdentityElementError!void {
@ -160,9 +152,10 @@ pub const Edwards25519 = struct {
return t;
}
fn nonAdjacentForm(s: [32]u8) [2 * 32]i8 {
fn slide(s: [32]u8) [2 * 32]i8 {
const reduced = if ((s[s.len - 1] & 0x80) != 0) s else scalar.reduce(s);
var e: [2 * 32]i8 = undefined;
for (s) |x, i| {
for (reduced) |x, i| {
e[i * 2 + 0] = @as(i8, @truncate(u4, x));
e[i * 2 + 1] = @as(i8, @truncate(u4, x >> 4));
}
@ -185,7 +178,7 @@ pub const Edwards25519 = struct {
// avoid these to keep the standard library lightweight.
fn pcMul(pc: [9]Edwards25519, s: [32]u8, comptime vartime: bool) IdentityElementError!Edwards25519 {
std.debug.assert(vartime);
const e = nonAdjacentForm(s);
const e = slide(s);
var q = Edwards25519.identityElement;
var pos: usize = 2 * 32 - 1;
while (true) : (pos -= 1) {
@ -280,8 +273,8 @@ pub const Edwards25519 = struct {
xpc[4].rejectIdentity() catch return error.WeakPublicKey;
break :pc xpc;
};
const e1 = nonAdjacentForm(s1);
const e2 = nonAdjacentForm(s2);
const e1 = slide(s1);
const e2 = slide(s2);
var q = Edwards25519.identityElement;
var pos: usize = 2 * 32 - 1;
while (true) : (pos -= 1) {
@ -318,7 +311,7 @@ pub const Edwards25519 = struct {
}
var es: [count][2 * 32]i8 = undefined;
for (ss) |s, i| {
es[i] = nonAdjacentForm(s);
es[i] = slide(s);
}
var q = Edwards25519.identityElement;
var pos: usize = 2 * 32 - 1;

View File

@ -355,7 +355,7 @@ pub const Fe = struct {
return fe;
}
/// Compute the inverse of a field element
/// Return the inverse of a field element, or 0 if a=0.
pub fn invert(a: Fe) Fe {
var t0 = a.sq();
var t1 = t0.sqn(2).mul(a);

View File

@ -98,7 +98,7 @@ pub fn sub(a: [32]u8, b: [32]u8) [32]u8 {
return add(a, neg(b));
}
/// A scalar in unpacked reprentation
/// A scalar in unpacked representation
pub const Scalar = struct {
const Limbs = [5]u64;
limbs: Limbs = undefined,

View File

@ -1,7 +1,11 @@
const std = @import("../std.zig");
const debug = std.debug;
const mem = std.mem;
const testing = std.testing;
const Endian = std.builtin.Endian;
const Order = std.math.Order;
/// Compares two arrays in constant time (for a given length) and returns whether they are equal.
/// This function was designed to compare short cryptographic secrets (MACs, signatures).
/// For all other applications, use mem.eql() instead.
@ -38,6 +42,41 @@ pub fn timingSafeEql(comptime T: type, a: T, b: T) bool {
}
}
/// Compare two integers serialized as arrays of the same size, in constant time.
/// Returns .lt if a<b, .gt if a>b and .eq if a=b
pub fn timingSafeCompare(comptime T: type, a: []const T, b: []const T, endian: Endian) Order {
debug.assert(a.len == b.len);
const bits = switch (@typeInfo(T)) {
.Int => |cinfo| if (cinfo.signedness != .unsigned) @compileError("Elements to be compared must be unsigned") else cinfo.bits,
else => @compileError("Elements to be compared must be integers"),
};
comptime const Cext = std.meta.Int(.unsigned, bits + 1);
var gt: T = 0;
var eq: T = 1;
if (endian == .Little) {
var i = a.len;
while (i != 0) {
i -= 1;
const x1 = a[i];
const x2 = b[i];
gt |= @truncate(T, (@as(Cext, x2) -% @as(Cext, x1)) >> bits) & eq;
eq &= @truncate(T, (@as(Cext, (x2 ^ x1)) -% 1) >> bits);
}
} else {
for (a) |x1, i| {
const x2 = b[i];
gt |= @truncate(T, (@as(Cext, x2) -% @as(Cext, x1)) >> bits) & eq;
eq &= @truncate(T, (@as(Cext, (x2 ^ x1)) -% 1) >> bits);
}
}
if (gt != 0) {
return Order.gt;
} else if (eq != 0) {
return Order.eq;
}
return Order.lt;
}
/// Sets a slice to zeroes.
/// Prevents the store from being optimized out.
pub fn secureZero(comptime T: type, s: []T) void {
@ -70,6 +109,19 @@ test "crypto.utils.timingSafeEql (vectors)" {
testing.expect(timingSafeEql(std.meta.Vector(100, u8), v1, v3));
}
test "crypto.utils.timingSafeCompare" {
var a = [_]u8{10} ** 32;
var b = [_]u8{10} ** 32;
testing.expectEqual(timingSafeCompare(u8, &a, &b, .Big), .eq);
testing.expectEqual(timingSafeCompare(u8, &a, &b, .Little), .eq);
a[31] = 1;
testing.expectEqual(timingSafeCompare(u8, &a, &b, .Big), .lt);
testing.expectEqual(timingSafeCompare(u8, &a, &b, .Little), .lt);
a[0] = 20;
testing.expectEqual(timingSafeCompare(u8, &a, &b, .Big), .gt);
testing.expectEqual(timingSafeCompare(u8, &a, &b, .Little), .lt);
}
test "crypto.utils.secureZero" {
var a = [_]u8{0xfe} ** 8;
var b = [_]u8{0xfe} ** 8;

View File

@ -339,6 +339,13 @@ pub const StackIterator = struct {
fp: usize,
pub fn init(first_address: ?usize, fp: ?usize) StackIterator {
if (native_arch == .sparcv9) {
// Flush all the register windows on stack.
asm volatile (
\\ flushw
::: "memory");
}
return StackIterator{
.first_address = first_address,
.fp = fp orelse @frameAddress(),
@ -346,18 +353,18 @@ pub const StackIterator = struct {
}
// Offset of the saved BP wrt the frame pointer.
const fp_offset = if (native_arch.isRISCV())
const fp_offset = if (comptime native_arch.isRISCV())
// On RISC-V the frame pointer points to the top of the saved register
// area, on pretty much every other architecture it points to the stack
// slot where the previous frame pointer is saved.
2 * @sizeOf(usize)
else if (native_arch.isSPARC())
else if (comptime native_arch.isSPARC())
// On SPARC the previous frame pointer is stored at 14 slots past %fp+BIAS.
14 * @sizeOf(usize)
else
0;
const fp_bias = if (native_arch.isSPARC())
const fp_bias = if (comptime native_arch.isSPARC())
// On SPARC frame pointers are biased by a constant.
2047
else
@ -383,7 +390,7 @@ pub const StackIterator = struct {
}
fn next_internal(self: *StackIterator) ?usize {
const fp = if (native_arch.isSPARC())
const fp = if (comptime native_arch.isSPARC())
// On SPARC the offset is positive. (!)
math.add(usize, self.fp, fp_offset) catch return null
else

View File

@ -1523,9 +1523,11 @@ test "parseUnsigned" {
}
pub const parseFloat = @import("fmt/parse_float.zig").parseFloat;
pub const parseHexFloat = @import("fmt/parse_hex_float.zig").parseHexFloat;
test "parseFloat" {
test {
_ = @import("fmt/parse_float.zig");
_ = @import("fmt/parse_hex_float.zig");
}
pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) {

View File

@ -0,0 +1,352 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2015-2021 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.const std = @import("std");
//
// The rounding logic is inspired by LLVM's APFloat and Go's atofHex
// implementation.
const std = @import("std");
const ascii = std.ascii;
const fmt = std.fmt;
const math = std.math;
const testing = std.testing;
const assert = std.debug.assert;
pub fn parseHexFloat(comptime T: type, s: []const u8) !T {
assert(@typeInfo(T) == .Float);
const IntT = std.meta.Int(.unsigned, @typeInfo(T).Float.bits);
const mantissa_bits = math.floatMantissaBits(T);
const exponent_bits = math.floatExponentBits(T);
const sign_shift = mantissa_bits + exponent_bits;
const exponent_bias = (1 << (exponent_bits - 1)) - 1;
const exponent_min = 1 - exponent_bias;
const exponent_max = exponent_bias;
if (s.len == 0)
return error.InvalidCharacter;
if (ascii.eqlIgnoreCase(s, "nan")) {
return math.nan(T);
} else if (ascii.eqlIgnoreCase(s, "inf") or ascii.eqlIgnoreCase(s, "+inf")) {
return math.inf(T);
} else if (ascii.eqlIgnoreCase(s, "-inf")) {
return -math.inf(T);
}
var negative: bool = false;
var exp_negative: bool = false;
var mantissa: u128 = 0;
var exponent: i16 = 0;
var frac_scale: i16 = 0;
const State = enum {
MaybeSign,
Prefix,
LeadingIntegerDigit,
IntegerDigit,
MaybeDot,
LeadingFractionDigit,
FractionDigit,
ExpPrefix,
MaybeExpSign,
ExpDigit,
};
var state = State.MaybeSign;
var i: usize = 0;
while (i < s.len) {
const c = s[i];
switch (state) {
.MaybeSign => {
state = .Prefix;
if (c == '+') {
i += 1;
} else if (c == '-') {
negative = true;
i += 1;
}
},
.Prefix => {
state = .LeadingIntegerDigit;
// Match both 0x and 0X.
if (i + 2 > s.len or s[i] != '0' or s[i + 1] | 32 != 'x')
return error.InvalidCharacter;
i += 2;
},
.LeadingIntegerDigit => {
if (c == '0') {
// Skip leading zeros.
i += 1;
} else if (c == '_') {
return error.InvalidCharacter;
} else {
state = .IntegerDigit;
}
},
.IntegerDigit => {
if (ascii.isXDigit(c)) {
if (mantissa >= math.maxInt(u128) / 16)
return error.Overflow;
mantissa *%= 16;
mantissa += try fmt.charToDigit(c, 16);
i += 1;
} else if (c == '_') {
i += 1;
} else {
state = .MaybeDot;
}
},
.MaybeDot => {
if (c == '.') {
state = .LeadingFractionDigit;
i += 1;
} else state = .ExpPrefix;
},
.LeadingFractionDigit => {
if (c == '_') {
return error.InvalidCharacter;
} else state = .FractionDigit;
},
.FractionDigit => {
if (ascii.isXDigit(c)) {
if (mantissa < math.maxInt(u128) / 16) {
mantissa *%= 16;
mantissa +%= try fmt.charToDigit(c, 16);
frac_scale += 1;
} else if (c != '0') {
return error.Overflow;
}
i += 1;
} else if (c == '_') {
i += 1;
} else {
state = .ExpPrefix;
}
},
.ExpPrefix => {
state = .MaybeExpSign;
// Match both p and P.
if (c | 32 != 'p')
return error.InvalidCharacter;
i += 1;
},
.MaybeExpSign => {
state = .ExpDigit;
if (c == '+') {
i += 1;
} else if (c == '-') {
exp_negative = true;
i += 1;
}
},
.ExpDigit => {
if (ascii.isXDigit(c)) {
if (exponent >= math.maxInt(i16) / 10)
return error.Overflow;
exponent *%= 10;
exponent +%= try fmt.charToDigit(c, 10);
i += 1;
} else if (c == '_') {
i += 1;
} else {
return error.InvalidCharacter;
}
},
}
}
if (exp_negative)
exponent *= -1;
// Bring the decimal part to the left side of the decimal dot.
exponent -= frac_scale * 4;
if (mantissa == 0) {
// Signed zero.
return if (negative) -0.0 else 0.0;
}
// Divide by 2^mantissa_bits to right-align the mantissa in the fractional
// part.
exponent += mantissa_bits;
// Keep around two extra bits to correctly round any value that doesn't fit
// the available mantissa bits. The result LSB serves as Guard bit, the
// following one is the Round bit and the last one is the Sticky bit,
// computed by OR-ing all the dropped bits.
// Normalize by aligning the implicit one bit.
while (mantissa >> (mantissa_bits + 2) == 0) {
mantissa <<= 1;
exponent -= 1;
}
// Normalize again by dropping the excess precision.
// Note that the discarded bits are folded into the Sticky bit.
while (mantissa >> (mantissa_bits + 2 + 1) != 0) {
mantissa = mantissa >> 1 | (mantissa & 1);
exponent += 1;
}
// Very small numbers can be possibly represented as denormals, reduce the
// exponent as much as possible.
while (mantissa != 0 and exponent < exponent_min - 2) {
mantissa = mantissa >> 1 | (mantissa & 1);
exponent += 1;
}
// There are two cases to handle:
// - We've truncated more than 0.5ULP (R=S=1), increase the mantissa.
// - We've truncated exactly 0.5ULP (R=1 S=0), increase the mantissa if the
// result is odd (G=1).
// The two checks can be neatly folded as follows.
mantissa |= @boolToInt(mantissa & 0b100 != 0);
mantissa += 1;
mantissa >>= 2;
exponent += 2;
if (mantissa & (1 << (mantissa_bits + 1)) != 0) {
// Renormalize, if the exponent overflows we'll catch that below.
mantissa >>= 1;
exponent += 1;
}
if (mantissa >> mantissa_bits == 0) {
// This is a denormal number, the biased exponent is zero.
exponent = -exponent_bias;
}
if (exponent > exponent_max) {
// Overflow, return +inf.
return math.inf(T);
}
// Remove the implicit bit.
mantissa &= @as(u128, (1 << mantissa_bits) - 1);
const raw: IntT =
(if (negative) @as(IntT, 1) << sign_shift else 0) |
@as(IntT, @bitCast(u16, exponent + exponent_bias)) << mantissa_bits |
@truncate(IntT, mantissa);
return @bitCast(T, raw);
}
test "special" {
testing.expect(math.isNan(try parseHexFloat(f32, "nAn")));
testing.expect(math.isPositiveInf(try parseHexFloat(f32, "iNf")));
testing.expect(math.isPositiveInf(try parseHexFloat(f32, "+Inf")));
testing.expect(math.isNegativeInf(try parseHexFloat(f32, "-iNf")));
}
test "zero" {
testing.expectEqual(@as(f32, 0.0), try parseHexFloat(f32, "0x0"));
testing.expectEqual(@as(f32, 0.0), try parseHexFloat(f32, "-0x0"));
testing.expectEqual(@as(f32, 0.0), try parseHexFloat(f32, "0x0p42"));
testing.expectEqual(@as(f32, 0.0), try parseHexFloat(f32, "-0x0.00000p42"));
testing.expectEqual(@as(f32, 0.0), try parseHexFloat(f32, "0x0.00000p666"));
}
test "f16" {
const Case = struct { s: []const u8, v: f16 };
const cases: []const Case = &[_]Case{
.{ .s = "0x1p0", .v = 1.0 },
.{ .s = "-0x1p-1", .v = -0.5 },
.{ .s = "0x10p+10", .v = 16384.0 },
.{ .s = "0x10p-10", .v = 0.015625 },
// Max normalized value.
.{ .s = "0x1.ffcp+15", .v = math.f16_max },
.{ .s = "-0x1.ffcp+15", .v = -math.f16_max },
// Min normalized value.
.{ .s = "0x1p-14", .v = math.f16_min },
.{ .s = "-0x1p-14", .v = -math.f16_min },
// Min denormal value.
.{ .s = "0x1p-24", .v = math.f16_true_min },
.{ .s = "-0x1p-24", .v = -math.f16_true_min },
};
for (cases) |case| {
testing.expectEqual(case.v, try parseHexFloat(f16, case.s));
}
}
test "f32" {
const Case = struct { s: []const u8, v: f32 };
const cases: []const Case = &[_]Case{
.{ .s = "0x1p0", .v = 1.0 },
.{ .s = "-0x1p-1", .v = -0.5 },
.{ .s = "0x10p+10", .v = 16384.0 },
.{ .s = "0x10p-10", .v = 0.015625 },
.{ .s = "0x0.ffffffp128", .v = 0x0.ffffffp128 },
.{ .s = "0x0.1234570p-125", .v = 0x0.1234570p-125 },
// Max normalized value.
.{ .s = "0x1.fffffeP+127", .v = math.f32_max },
.{ .s = "-0x1.fffffeP+127", .v = -math.f32_max },
// Min normalized value.
.{ .s = "0x1p-126", .v = math.f32_min },
.{ .s = "-0x1p-126", .v = -math.f32_min },
// Min denormal value.
.{ .s = "0x1P-149", .v = math.f32_true_min },
.{ .s = "-0x1P-149", .v = -math.f32_true_min },
};
for (cases) |case| {
testing.expectEqual(case.v, try parseHexFloat(f32, case.s));
}
}
test "f64" {
const Case = struct { s: []const u8, v: f64 };
const cases: []const Case = &[_]Case{
.{ .s = "0x1p0", .v = 1.0 },
.{ .s = "-0x1p-1", .v = -0.5 },
.{ .s = "0x10p+10", .v = 16384.0 },
.{ .s = "0x10p-10", .v = 0.015625 },
// Max normalized value.
.{ .s = "0x1.fffffffffffffp+1023", .v = math.f64_max },
.{ .s = "-0x1.fffffffffffffp1023", .v = -math.f64_max },
// Min normalized value.
.{ .s = "0x1p-1022", .v = math.f64_min },
.{ .s = "-0x1p-1022", .v = -math.f64_min },
// Min denormalized value.
.{ .s = "0x1p-1074", .v = math.f64_true_min },
.{ .s = "-0x1p-1074", .v = -math.f64_true_min },
};
for (cases) |case| {
testing.expectEqual(case.v, try parseHexFloat(f64, case.s));
}
}
test "f128" {
const Case = struct { s: []const u8, v: f128 };
const cases: []const Case = &[_]Case{
.{ .s = "0x1p0", .v = 1.0 },
.{ .s = "-0x1p-1", .v = -0.5 },
.{ .s = "0x10p+10", .v = 16384.0 },
.{ .s = "0x10p-10", .v = 0.015625 },
// Max normalized value.
.{ .s = "0xf.fffffffffffffffffffffffffff8p+16380", .v = math.f128_max },
.{ .s = "-0xf.fffffffffffffffffffffffffff8p+16380", .v = -math.f128_max },
// Min normalized value.
.{ .s = "0x1p-16382", .v = math.f128_min },
.{ .s = "-0x1p-16382", .v = -math.f128_min },
// // Min denormalized value.
.{ .s = "0x1p-16494", .v = math.f128_true_min },
.{ .s = "-0x1p-16494", .v = -math.f128_true_min },
};
for (cases) |case| {
testing.expectEqual(@bitCast(u128, case.v), @bitCast(u128, try parseHexFloat(f128, case.s)));
}
}

View File

@ -29,8 +29,7 @@ fn ok(s: []const u8) !void {
fn err(s: []const u8) void {
testing.expect(!json.validate(s));
testNonStreaming(s) catch return;
testing.expect(false);
testing.expect(std.meta.isError(testNonStreaming(s)));
}
fn utf8Error(s: []const u8) void {
@ -48,8 +47,7 @@ fn any(s: []const u8) void {
fn anyStreamingErrNonStreaming(s: []const u8) void {
_ = json.validate(s);
testNonStreaming(s) catch return;
testing.expect(false);
testing.expect(std.meta.isError(testNonStreaming(s)));
}
fn roundTrip(s: []const u8) !void {

View File

@ -1877,7 +1877,11 @@ test "rotate" {
/// Replace needle with replacement as many times as possible, writing to an output buffer which is assumed to be of
/// appropriate size. Use replacementSize to calculate an appropriate buffer size.
/// The needle must not be empty.
pub fn replace(comptime T: type, input: []const T, needle: []const T, replacement: []const T, output: []T) usize {
// Empty needle will loop until output buffer overflows.
assert(needle.len > 0);
var i: usize = 0;
var slide: usize = 0;
var replacements: usize = 0;
@ -1900,22 +1904,48 @@ pub fn replace(comptime T: type, input: []const T, needle: []const T, replacemen
test "replace" {
var output: [29]u8 = undefined;
var replacements = replace(u8, "All your base are belong to us", "base", "Zig", output[0..]);
var expected: []const u8 = "All your Zig are belong to us";
testing.expect(replacements == 1);
testing.expect(eql(u8, output[0..], "All your Zig are belong to us"));
testing.expectEqualStrings(expected, output[0..expected.len]);
replacements = replace(u8, "Favor reading code over writing code.", "code", "", output[0..]);
expected = "Favor reading over writing .";
testing.expect(replacements == 2);
testing.expect(eql(u8, output[0..], "Favor reading over writing ."));
testing.expectEqualStrings(expected, output[0..expected.len]);
// Empty needle is not allowed but input may be empty.
replacements = replace(u8, "", "x", "y", output[0..0]);
expected = "";
testing.expect(replacements == 0);
testing.expectEqualStrings(expected, output[0..expected.len]);
// Adjacent replacements.
replacements = replace(u8, "\\n\\n", "\\n", "\n", output[0..]);
expected = "\n\n";
testing.expect(replacements == 2);
testing.expectEqualStrings(expected, output[0..expected.len]);
replacements = replace(u8, "abbba", "b", "cd", output[0..]);
expected = "acdcdcda";
testing.expect(replacements == 3);
testing.expectEqualStrings(expected, output[0..expected.len]);
}
/// Calculate the size needed in an output buffer to perform a replacement.
/// The needle must not be empty.
pub fn replacementSize(comptime T: type, input: []const T, needle: []const T, replacement: []const T) usize {
// Empty needle will loop forever.
assert(needle.len > 0);
var i: usize = 0;
var size: usize = input.len;
while (i < input.len) : (i += 1) {
while (i < input.len) {
if (mem.indexOf(T, input[i..], needle) == @as(usize, 0)) {
size = size - needle.len + replacement.len;
i += needle.len;
} else {
i += 1;
}
}
@ -1924,9 +1954,15 @@ pub fn replacementSize(comptime T: type, input: []const T, needle: []const T, re
test "replacementSize" {
testing.expect(replacementSize(u8, "All your base are belong to us", "base", "Zig") == 29);
testing.expect(replacementSize(u8, "", "", "") == 0);
testing.expect(replacementSize(u8, "Favor reading code over writing code.", "code", "") == 29);
testing.expect(replacementSize(u8, "Only one obvious way to do things.", "things.", "things in Zig.") == 41);
// Empty needle is not allowed but input may be empty.
testing.expect(replacementSize(u8, "", "x", "y") == 0);
// Adjacent replacements.
testing.expect(replacementSize(u8, "\\n\\n", "\\n", "\n") == 2);
testing.expect(replacementSize(u8, "abbba", "b", "cd") == 8);
}
/// Perform a replacement on an allocated buffer of pre-determined size. Caller must free returned memory.

View File

@ -117,10 +117,21 @@ test "std.meta.bitCount" {
testing.expect(bitCount(f32) == 32);
}
/// Returns the alignment of type T.
/// Note that if T is a pointer or function type the result is different than
/// the one returned by @alignOf(T).
/// If T is a pointer type the alignment of the type it points to is returned.
/// If T is a function type the alignment a target-dependent value is returned.
pub fn alignment(comptime T: type) comptime_int {
//@alignOf works on non-pointer types
const P = if (comptime trait.is(.Pointer)(T)) T else *T;
return @typeInfo(P).Pointer.alignment;
return switch (@typeInfo(T)) {
.Optional => |info| switch (@typeInfo(info.child)) {
.Pointer, .Fn => alignment(info.child),
else => @alignOf(T),
},
.Pointer => |info| info.alignment,
.Fn => |info| info.alignment,
else => @alignOf(T),
};
}
test "std.meta.alignment" {
@ -129,6 +140,8 @@ test "std.meta.alignment" {
testing.expect(alignment(*align(2) u8) == 2);
testing.expect(alignment([]align(1) u8) == 1);
testing.expect(alignment([]align(2) u8) == 2);
testing.expect(alignment(fn () void) > 0);
testing.expect(alignment(fn () align(128) void) == 128);
}
pub fn Child(comptime T: type) type {
@ -1342,3 +1355,13 @@ test "shuffleVectorIndex" {
testing.expect(shuffleVectorIndex(6, vector_len) == -3);
testing.expect(shuffleVectorIndex(7, vector_len) == -4);
}
/// Returns whether `error_union` contains an error.
pub fn isError(error_union: anytype) bool {
return if (error_union) |_| false else |_| true;
}
test "isError" {
std.testing.expect(isError(math.absInt(@as(i8, -128))));
std.testing.expect(!isError(math.absInt(@as(i8, -127))));
}

View File

@ -279,20 +279,10 @@ pub const PROT_READ = 1;
pub const PROT_WRITE = 2;
pub const PROT_EXEC = 4;
pub const CLOCK_REALTIME = 0;
pub const CLOCK_VIRTUAL = 1;
pub const CLOCK_PROF = 2;
pub const CLOCK_MONOTONIC = 4;
pub const CLOCK_UPTIME = 5;
pub const CLOCK_UPTIME_PRECISE = 7;
pub const CLOCK_UPTIME_FAST = 8;
pub const CLOCK_REALTIME_PRECISE = 9;
pub const CLOCK_REALTIME_FAST = 10;
pub const CLOCK_MONOTONIC_PRECISE = 11;
pub const CLOCK_MONOTONIC_FAST = 12;
pub const CLOCK_SECOND = 13;
pub const CLOCK_THREAD_CPUTIME_ID = 14;
pub const CLOCK_PROCESS_CPUTIME_ID = 15;
pub const CLOCK_MONOTONIC = 0;
pub const CLOCK_REALTIME = -1;
pub const CLOCK_PROCESS_CPUTIME_ID = -2;
pub const CLOCK_THREAD_CPUTIME_ID = -3;
pub const MAP_FAILED = @intToPtr(*c_void, maxInt(usize));
pub const MAP_SHARED = 0x0001;
@ -310,58 +300,59 @@ pub const MAP_NOCORE = 0x00020000;
pub const MAP_PREFAULT_READ = 0x00040000;
pub const MAP_32BIT = 0x00080000;
pub const WNOHANG = 1;
pub const WUNTRACED = 2;
pub const WSTOPPED = WUNTRACED;
pub const WCONTINUED = 4;
pub const WNOWAIT = 8;
pub const WEXITED = 16;
pub const WTRAPPED = 32;
pub const WNOHANG = 0x1;
pub const WUNTRACED = 0x2;
pub const WSTOPPED = 0x10;
pub const WCONTINUED = 0x4;
pub const WNOWAIT = 0x20;
pub const WEXITED = 0x08;
pub const SA_ONSTACK = 0x0001;
pub const SA_RESTART = 0x0002;
pub const SA_RESETHAND = 0x0004;
pub const SA_NOCLDSTOP = 0x0008;
pub const SA_NODEFER = 0x0010;
pub const SA_NOCLDWAIT = 0x0020;
pub const SA_SIGINFO = 0x0040;
pub const SA_ONSTACK = 0x20;
pub const SA_RESTART = 0x10;
pub const SA_RESETHAND = 0x04;
pub const SA_NOCLDSTOP = 0x01;
pub const SA_NODEFER = 0x08;
pub const SA_NOCLDWAIT = 0x02;
pub const SA_SIGINFO = 0x40;
pub const SA_NOMASK = SA_NODEFER;
pub const SA_STACK = SA_ONSTACK;
pub const SA_ONESHOT = SA_RESETHAND;
pub const SIGHUP = 1;
pub const SIGINT = 2;
pub const SIGQUIT = 3;
pub const SIGILL = 4;
pub const SIGTRAP = 5;
pub const SIGCHLD = 5;
pub const SIGABRT = 6;
pub const SIGIOT = SIGABRT;
pub const SIGEMT = 7;
pub const SIGPIPE = 7;
pub const SIGFPE = 8;
pub const SIGKILL = 9;
pub const SIGBUS = 10;
pub const SIGSTOP = 10;
pub const SIGSEGV = 11;
pub const SIGSYS = 12;
pub const SIGPIPE = 13;
pub const SIGCONT = 12;
pub const SIGTSTP = 13;
pub const SIGALRM = 14;
pub const SIGTERM = 15;
pub const SIGURG = 16;
pub const SIGSTOP = 17;
pub const SIGTSTP = 18;
pub const SIGCONT = 19;
pub const SIGCHLD = 20;
pub const SIGTTIN = 21;
pub const SIGTTOU = 22;
pub const SIGIO = 23;
pub const SIGXCPU = 24;
pub const SIGXFSZ = 25;
pub const SIGVTALRM = 26;
pub const SIGPROF = 27;
pub const SIGWINCH = 28;
pub const SIGINFO = 29;
pub const SIGUSR1 = 30;
pub const SIGUSR2 = 31;
pub const SIGTHR = 32;
pub const SIGLWP = SIGTHR;
pub const SIGLIBRT = 33;
pub const SIGTTIN = 16;
pub const SIGTTOU = 17;
pub const SIGUSR1 = 18;
pub const SIGUSR2 = 19;
pub const SIGWINCH = 20;
pub const SIGKILLTHR = 21;
pub const SIGTRAP = 22;
pub const SIGPOLL = 23;
pub const SIGPROF = 24;
pub const SIGSYS = 25;
pub const SIGURG = 26;
pub const SIGVTALRM = 27;
pub const SIGXCPU = 28;
pub const SIGXFSZ = 29;
pub const SIGBUS = 30;
pub const SIGRESERVED1 = 31;
pub const SIGRESERVED2 = 32;
// TODO: check
pub const SIGRTMIN = 65;
pub const SIGRTMAX = 126;
@ -645,135 +636,51 @@ pub const EVFILT_SENDFILE = -12;
pub const EVFILT_EMPTY = -13;
/// On input, NOTE_TRIGGER causes the event to be triggered for output.
pub const NOTE_TRIGGER = 0x01000000;
pub const TCGETA = 0x8000;
pub const TCSETA = 0x8001;
pub const TCSETAW = 0x8004;
pub const TCSETAF = 0x8003;
pub const TCSBRK = 08005;
pub const TCXONC = 0x8007;
pub const TCFLSH = 0x8006;
/// ignore input fflags
pub const NOTE_FFNOP = 0x00000000;
/// and fflags
pub const NOTE_FFAND = 0x40000000;
/// or fflags
pub const NOTE_FFOR = 0x80000000;
/// copy fflags
pub const NOTE_FFCOPY = 0xc0000000;
/// mask for operations
pub const NOTE_FFCTRLMASK = 0xc0000000;
pub const NOTE_FFLAGSMASK = 0x00ffffff;
/// low water mark
pub const NOTE_LOWAT = 0x00000001;
/// behave like poll()
pub const NOTE_FILE_POLL = 0x00000002;
/// vnode was removed
pub const NOTE_DELETE = 0x00000001;
/// data contents changed
pub const NOTE_WRITE = 0x00000002;
/// size increased
pub const NOTE_EXTEND = 0x00000004;
/// attributes changed
pub const NOTE_ATTRIB = 0x00000008;
/// link count changed
pub const NOTE_LINK = 0x00000010;
/// vnode was renamed
pub const NOTE_RENAME = 0x00000020;
/// vnode access was revoked
pub const NOTE_REVOKE = 0x00000040;
/// vnode was opened
pub const NOTE_OPEN = 0x00000080;
/// file closed, fd did not allow write
pub const NOTE_CLOSE = 0x00000100;
/// file closed, fd did allow write
pub const NOTE_CLOSE_WRITE = 0x00000200;
/// file was read
pub const NOTE_READ = 0x00000400;
/// process exited
pub const NOTE_EXIT = 0x80000000;
/// process forked
pub const NOTE_FORK = 0x40000000;
/// process exec'd
pub const NOTE_EXEC = 0x20000000;
/// mask for signal & exit status
pub const NOTE_PDATAMASK = 0x000fffff;
pub const NOTE_PCTRLMASK = (~NOTE_PDATAMASK);
/// data is seconds
pub const NOTE_SECONDS = 0x00000001;
/// data is milliseconds
pub const NOTE_MSECONDS = 0x00000002;
/// data is microseconds
pub const NOTE_USECONDS = 0x00000004;
/// data is nanoseconds
pub const NOTE_NSECONDS = 0x00000008;
/// timeout is absolute
pub const NOTE_ABSTIME = 0x00000010;
pub const TIOCEXCL = 0x2000740d;
pub const TIOCNXCL = 0x2000740e;
pub const TIOCSCTTY = 0x20007461;
pub const TIOCGPGRP = 0x40047477;
pub const TIOCSPGRP = 0x80047476;
pub const TIOCOUTQ = 0x40047473;
pub const TIOCSTI = 0x80017472;
pub const TIOCGWINSZ = 0x40087468;
pub const TIOCSWINSZ = 0x80087467;
pub const TIOCMGET = 0x4004746a;
pub const TIOCMBIS = 0x8004746c;
pub const TIOCMBIC = 0x8004746b;
pub const TIOCMSET = 0x8004746d;
pub const FIONREAD = 0x4004667f;
pub const TIOCCONS = 0x80047462;
pub const TIOCPKT = 0x80047470;
pub const FIONBIO = 0x8004667e;
pub const TIOCNOTTY = 0x20007471;
pub const TIOCSETD = 0x8004741b;
pub const TIOCGETD = 0x4004741a;
pub const TIOCSBRK = 0x2000747b;
pub const TIOCCBRK = 0x2000747a;
pub const TIOCGSID = 0x40047463;
pub const TIOCGPTN = 0x4004740f;
pub const TIOCSIG = 0x2004745f;
pub const TIOCSCTTY = 0x8017;
pub const TIOCGPGRP = 0x8015;
pub const TIOCSPGRP = 0x8016;
pub const TIOCGWINSZ = 0x8012;
pub const TIOCSWINSZ = 0x8013;
pub const TIOCMGET = 0x8018;
pub const TIOCMBIS = 0x8022;
pub const TIOCMBIC = 0x8023;
pub const TIOCMSET = 0x8019;
pub const FIONREAD = 0xbe000001;
pub const FIONBIO = 0xbe000000;
pub const TIOCSBRK = 0x8020;
pub const TIOCCBRK = 0x8021;
pub const TIOCGSID = 0x8024;
pub fn WEXITSTATUS(s: u32) u32 {
return (s & 0xff00) >> 8;
return (s & 0xff);
}
pub fn WTERMSIG(s: u32) u32 {
return s & 0x7f;
return (s >> 8) & 0xff;
}
pub fn WSTOPSIG(s: u32) u32 {
return WEXITSTATUS(s);
}
pub fn WIFEXITED(s: u32) bool {
return WTERMSIG(s) == 0;
}
pub fn WIFSTOPPED(s: u32) bool {
return @intCast(u16, (((s & 0xffff) *% 0x10001) >> 8)) > 0x7f00;
return ((s >> 16) & 0xff) != 0;
}
pub fn WIFSIGNALED(s: u32) bool {
return (s & 0xffff) -% 1 < 0xff;
return ((s >> 8) & 0xff) != 0;
}
pub const winsize = extern struct {
@ -823,49 +730,47 @@ pub const sigset_t = extern struct {
__bits: [_SIG_WORDS]u32,
};
pub const EPERM = 1; // Operation not permitted
pub const ENOENT = 2; // No such file or directory
pub const ESRCH = 3; // No such process
pub const EINTR = 4; // Interrupted system call
pub const EIO = 5; // Input/output error
pub const ENXIO = 6; // Device not configured
pub const E2BIG = 7; // Argument list too long
pub const ENOEXEC = 8; // Exec format error
pub const EBADF = 9; // Bad file descriptor
pub const ECHILD = 10; // No child processes
pub const EDEADLK = 11; // Resource deadlock avoided
// 11 was EAGAIN
pub const ENOMEM = 12; // Cannot allocate memory
pub const EACCES = 13; // Permission denied
pub const EFAULT = 14; // Bad address
pub const ENOTBLK = 15; // Block device required
pub const EBUSY = 16; // Device busy
pub const EEXIST = 17; // File exists
pub const EXDEV = 18; // Cross-device link
pub const ENODEV = 19; // Operation not supported by device
pub const ENOTDIR = 20; // Not a directory
pub const EISDIR = 21; // Is a directory
pub const EINVAL = 22; // Invalid argument
pub const ENFILE = 23; // Too many open files in system
pub const EMFILE = 24; // Too many open files
pub const ENOTTY = 25; // Inappropriate ioctl for device
pub const ETXTBSY = 26; // Text file busy
pub const EFBIG = 27; // File too large
pub const ENOSPC = 28; // No space left on device
pub const ESPIPE = 29; // Illegal seek
pub const EROFS = 30; // Read-only filesystem
pub const EMLINK = 31; // Too many links
pub const EPIPE = 32; // Broken pipe
pub const EPERM = -0x7ffffff1; // Operation not permitted
pub const ENOENT = -0x7fff9ffd; // No such file or directory
pub const ESRCH = -0x7fff8ff3; // No such process
pub const EINTR = -0x7ffffff6; // Interrupted system call
pub const EIO = -0x7fffffff; // Input/output error
pub const ENXIO = -0x7fff8ff5; // Device not configured
pub const E2BIG = -0x7fff8fff; // Argument list too long
pub const ENOEXEC = -0x7fffecfe; // Exec format error
pub const ECHILD = -0x7fff8ffe; // No child processes
pub const EDEADLK = -0x7fff8ffd; // Resource deadlock avoided
pub const ENOMEM = -0x80000000; // Cannot allocate memory
pub const EACCES = -0x7ffffffe; // Permission denied
pub const EFAULT = -0x7fffecff; // Bad address
pub const EBUSY = -0x7ffffff2; // Device busy
pub const EEXIST = -0x7fff9ffe; // File exists
pub const EXDEV = -0x7fff9ff5; // Cross-device link
pub const ENODEV = -0x7fff8ff9; // Operation not supported by device
pub const ENOTDIR = -0x7fff9ffb; // Not a directory
pub const EISDIR = -0x7fff9ff7; // Is a directory
pub const EINVAL = -0x7ffffffb; // Invalid argument
pub const ENFILE = -0x7fff8ffa; // Too many open files in system
pub const EMFILE = -0x7fff9ff6; // Too many open files
pub const ENOTTY = -0x7fff8ff6; // Inappropriate ioctl for device
pub const ETXTBSY = -0x7fff8fc5; // Text file busy
pub const EFBIG = -0x7fff8ffc; // File too large
pub const ENOSPC = -0x7fff9ff9; // No space left on device
pub const ESPIPE = -0x7fff8ff4; // Illegal seek
pub const EROFS = -0x7fff9ff8; // Read-only filesystem
pub const EMLINK = -0x7fff8ffb; // Too many links
pub const EPIPE = -0x7fff9ff3; // Broken pipe
pub const EBADF = -0x7fffa000; // Bad file descriptor
// math software
pub const EDOM = 33; // Numerical argument out of domain
pub const ERANGE = 34; // Result too large
// non-blocking and interrupt i/o
pub const EAGAIN = 35; // Resource temporarily unavailable
pub const EWOULDBLOCK = EAGAIN; // Operation would block
pub const EINPROGRESS = 36; // Operation now in progress
pub const EALREADY = 37; // Operation already in progress
pub const EAGAIN = -0x7ffffff5;
pub const EWOULDBLOCK = -0x7ffffff5;
pub const EINPROGRESS = -0x7fff8fdc;
pub const EALREADY = -0x7fff8fdb;
// ipc/network software -- argument errors
pub const ENOTSOCK = 38; // Socket operation on non-socket
@ -1447,3 +1352,20 @@ pub const directory_which = enum(c_int) {
_,
};
pub const cc_t = u8;
pub const speed_t = u8;
pub const tcflag_t = u32;
pub const NCCS = 32;
pub const termios = extern struct {
c_iflag: tcflag_t,
c_oflag: tcflag_t,
c_cflag: tcflag_t,
c_lflag: tcflag_t,
c_line: cc_t,
c_ispeed: speed_t,
c_ospeed: speed_t,
cc_t: [NCCS]cc_t,
};

View File

@ -248,7 +248,7 @@ fn initTLS() void {
tls_data = @intToPtr([*]u8, img_base + phdr.p_vaddr)[0..phdr.p_filesz];
tls_data_alloc_size = phdr.p_memsz;
} else {
tls_align_factor = @alignOf(*usize);
tls_align_factor = @alignOf(usize);
tls_data = &[_]u8{};
tls_data_alloc_size = 0;
}
@ -308,7 +308,7 @@ fn initTLS() void {
}
fn alignPtrCast(comptime T: type, ptr: [*]u8) callconv(.Inline) *T {
return @ptrCast(*T, @alignCast(@alignOf(*T), ptr));
return @ptrCast(*T, @alignCast(@alignOf(T), ptr));
}
/// Initializes all the fields of the static TLS area and returns the computed

View File

@ -88,6 +88,7 @@ pub const time = @import("time.zig");
pub const unicode = @import("unicode.zig");
pub const valgrind = @import("valgrind.zig");
pub const wasm = @import("wasm.zig");
pub const x = @import("x.zig");
pub const zig = @import("zig.zig");
pub const start = @import("start.zig");

View File

@ -211,8 +211,9 @@ pub const Target = struct {
/// If neither of these cases apply, a runtime check should be used to determine if the
/// target supports a given OS feature.
///
/// Binaries built with a given maximum version will continue to function on newer operating system
/// versions. However, such a binary may not take full advantage of the newer operating system APIs.
/// Binaries built with a given maximum version will continue to function on newer
/// operating system versions. However, such a binary may not take full advantage of the
/// newer operating system APIs.
///
/// See `Os.isAtLeast`.
pub const VersionRange = union {
@ -260,7 +261,7 @@ pub const Target = struct {
.freebsd => return .{
.semver = Version.Range{
.min = .{ .major = 12, .minor = 0 },
.max = .{ .major = 12, .minor = 1 },
.max = .{ .major = 13, .minor = 0 },
},
},
.macos => return .{

View File

@ -206,7 +206,7 @@ pub fn utf8ValidateSlice(s: []const u8) bool {
return false;
}
if (utf8Decode(s[i .. i + cp_len])) |_| {} else |_| {
if (std.meta.isError(utf8Decode(s[i .. i + cp_len]))) {
return false;
}
i += cp_len;

1
lib/std/x.zig Normal file
View File

@ -0,0 +1 @@
pub const os = @import("x/os/os.zig");

276
lib/std/x/os/Socket.zig Normal file
View File

@ -0,0 +1,276 @@
const std = @import("../../std.zig");
const os = std.os;
const mem = std.mem;
const net = std.net;
const time = std.time;
const builtin = std.builtin;
const testing = std.testing;
const Socket = @This();
/// A socket-address pair.
pub const Connection = struct {
socket: Socket,
address: net.Address,
};
/// The underlying handle of a socket.
fd: os.socket_t,
/// Open a new socket.
pub fn init(domain: u32, socket_type: u32, protocol: u32) !Socket {
return Socket{ .fd = try os.socket(domain, socket_type, protocol) };
}
/// Closes the socket.
pub fn deinit(self: Socket) void {
os.closeSocket(self.fd);
}
/// Shutdown either the read side, or write side, or the entirety of a socket.
pub fn shutdown(self: Socket, how: os.ShutdownHow) !void {
return os.shutdown(self.fd, how);
}
/// Binds the socket to an address.
pub fn bind(self: Socket, address: net.Address) !void {
return os.bind(self.fd, &address.any, address.getOsSockLen());
}
/// Start listening for incoming connections on the socket.
pub fn listen(self: Socket, max_backlog_size: u31) !void {
return os.listen(self.fd, max_backlog_size);
}
/// Have the socket attempt to the connect to an address.
pub fn connect(self: Socket, address: net.Address) !void {
return os.connect(self.fd, &address.any, address.getOsSockLen());
}
/// Accept a pending incoming connection queued to the kernel backlog
/// of the socket.
pub fn accept(self: Socket, flags: u32) !Socket.Connection {
var address: os.sockaddr = undefined;
var address_len: u32 = @sizeOf(os.sockaddr);
const fd = try os.accept(self.fd, &address, &address_len, flags);
return Connection{
.socket = Socket{ .fd = fd },
.address = net.Address.initPosix(@alignCast(4, &address)),
};
}
/// Read data from the socket into the buffer provided. It returns the
/// number of bytes read into the buffer provided.
pub fn read(self: Socket, buf: []u8) !usize {
return os.read(self.fd, buf);
}
/// Read data from the socket into the buffer provided with a set of flags
/// specified. It returns the number of bytes read into the buffer provided.
pub fn recv(self: Socket, buf: []u8, flags: u32) !usize {
return os.recv(self.fd, buf, flags);
}
/// Write a buffer of data provided to the socket. It returns the number
/// of bytes that are written to the socket.
pub fn write(self: Socket, buf: []const u8) !usize {
return os.write(self.fd, buf);
}
/// Writes multiple I/O vectors to the socket. It returns the number
/// of bytes that are written to the socket.
pub fn writev(self: Socket, buffers: []const os.iovec_const) !usize {
return os.writev(self.fd, buffers);
}
/// Write a buffer of data provided to the socket with a set of flags specified.
/// It returns the number of bytes that are written to the socket.
pub fn send(self: Socket, buf: []const u8, flags: u32) !usize {
return os.send(self.fd, buf, flags);
}
/// Writes multiple I/O vectors with a prepended message header to the socket
/// with a set of flags specified. It returns the number of bytes that are
/// written to the socket.
pub fn sendmsg(self: Socket, msg: os.msghdr_const, flags: u32) !usize {
return os.sendmsg(self.fd, msg, flags);
}
/// Query the address that the socket is locally bounded to.
pub fn getLocalAddress(self: Socket) !net.Address {
var address: os.sockaddr = undefined;
var address_len: u32 = @sizeOf(os.sockaddr);
try os.getsockname(self.fd, &address, &address_len);
return net.Address.initPosix(@alignCast(4, &address));
}
/// Query and return the latest cached error on the socket.
pub fn getError(self: Socket) !void {
return os.getsockoptError(self.fd);
}
/// Query the read buffer size of the socket.
pub fn getReadBufferSize(self: Socket) !u32 {
var value: u32 = undefined;
var value_len: u32 = @sizeOf(u32);
const rc = os.system.getsockopt(self.fd, os.SOL_SOCKET, os.SO_RCVBUF, mem.asBytes(&value), &value_len);
return switch (os.errno(rc)) {
0 => value,
os.EBADF => error.BadFileDescriptor,
os.EFAULT => error.InvalidAddressSpace,
os.EINVAL => error.InvalidSocketOption,
os.ENOPROTOOPT => error.UnknownSocketOption,
os.ENOTSOCK => error.NotASocket,
else => |err| os.unexpectedErrno(err),
};
}
/// Query the write buffer size of the socket.
pub fn getWriteBufferSize(self: Socket) !u32 {
var value: u32 = undefined;
var value_len: u32 = @sizeOf(u32);
const rc = os.system.getsockopt(self.fd, os.SOL_SOCKET, os.SO_SNDBUF, mem.asBytes(&value), &value_len);
return switch (os.errno(rc)) {
0 => value,
os.EBADF => error.BadFileDescriptor,
os.EFAULT => error.InvalidAddressSpace,
os.EINVAL => error.InvalidSocketOption,
os.ENOPROTOOPT => error.UnknownSocketOption,
os.ENOTSOCK => error.NotASocket,
else => |err| os.unexpectedErrno(err),
};
}
/// Allow multiple sockets on the same host to listen on the same address. It returns `error.UnsupportedSocketOption` if
/// the host does not support sockets listening the same address.
pub fn setReuseAddress(self: Socket, enabled: bool) !void {
if (comptime @hasDecl(os, "SO_REUSEADDR")) {
return os.setsockopt(self.fd, os.SOL_SOCKET, os.SO_REUSEADDR, mem.asBytes(&@as(usize, @boolToInt(enabled))));
}
return error.UnsupportedSocketOption;
}
/// Allow multiple sockets on the same host to listen on the same port. It returns `error.UnsupportedSocketOption` if
/// the host does not supports sockets listening on the same port.
pub fn setReusePort(self: Socket, enabled: bool) !void {
if (comptime @hasDecl(os, "SO_REUSEPORT")) {
return os.setsockopt(self.fd, os.SOL_SOCKET, os.SO_REUSEPORT, mem.asBytes(&@as(usize, @boolToInt(enabled))));
}
return error.UnsupportedSocketOption;
}
/// Disable Nagle's algorithm on a TCP socket. It returns `error.UnsupportedSocketOption` if the host does not support
/// sockets disabling Nagle's algorithm.
pub fn setNoDelay(self: Socket, enabled: bool) !void {
if (comptime @hasDecl(os, "TCP_NODELAY")) {
return os.setsockopt(self.fd, os.IPPROTO_TCP, os.TCP_NODELAY, mem.asBytes(&@as(usize, @boolToInt(enabled))));
}
return error.UnsupportedSocketOption;
}
/// Enables TCP Fast Open (RFC 7413) on a TCP socket. It returns `error.UnsupportedSocketOption` if the host does not
/// support TCP Fast Open.
pub fn setFastOpen(self: Socket, enabled: bool) !void {
if (comptime @hasDecl(os, "TCP_FASTOPEN")) {
return os.setsockopt(self.fd, os.IPPROTO_TCP, os.TCP_FASTOPEN, mem.asBytes(&@as(usize, @boolToInt(enabled))));
}
return error.UnsupportedSocketOption;
}
/// Enables TCP Quick ACK on a TCP socket to immediately send rather than delay ACKs when necessary. It returns
/// `error.UnsupportedSocketOption` if the host does not support TCP Quick ACK.
pub fn setQuickACK(self: Socket, enabled: bool) !void {
if (comptime @hasDecl(os, "TCP_QUICKACK")) {
return os.setsockopt(self.fd, os.IPPROTO_TCP, os.TCP_QUICKACK, mem.asBytes(&@as(usize, @boolToInt(enabled))));
}
return error.UnsupportedSocketOption;
}
/// Set the write buffer size of the socket.
pub fn setWriteBufferSize(self: Socket, size: u32) !void {
return os.setsockopt(self.fd, os.SOL_SOCKET, os.SO_SNDBUF, mem.asBytes(&size));
}
/// Set the read buffer size of the socket.
pub fn setReadBufferSize(self: Socket, size: u32) !void {
return os.setsockopt(self.fd, os.SOL_SOCKET, os.SO_RCVBUF, mem.asBytes(&size));
}
/// Set a timeout on the socket that is to occur if no messages are successfully written
/// to its bound destination after a specified number of milliseconds. A subsequent write
/// to the socket will thereafter return `error.WouldBlock` should the timeout be exceeded.
pub fn setWriteTimeout(self: Socket, milliseconds: usize) !void {
const timeout = os.timeval{
.tv_sec = @intCast(isize, milliseconds / time.ms_per_s),
.tv_usec = @intCast(isize, (milliseconds % time.ms_per_s) * time.us_per_ms),
};
return os.setsockopt(self.fd, os.SOL_SOCKET, os.SO_SNDTIMEO, mem.asBytes(&timeout));
}
/// Set a timeout on the socket that is to occur if no messages are successfully read
/// from its bound destination after a specified number of milliseconds. A subsequent
/// read from the socket will thereafter return `error.WouldBlock` should the timeout be
/// exceeded.
pub fn setReadTimeout(self: Socket, milliseconds: usize) !void {
const timeout = os.timeval{
.tv_sec = @intCast(isize, milliseconds / time.ms_per_s),
.tv_usec = @intCast(isize, (milliseconds % time.ms_per_s) * time.us_per_ms),
};
return os.setsockopt(self.fd, os.SOL_SOCKET, os.SO_RCVTIMEO, mem.asBytes(&timeout));
}
test {
testing.refAllDecls(@This());
}
test "socket/linux: set read timeout of 1 millisecond on blocking socket" {
if (builtin.os.tag != .linux) return error.SkipZigTest;
const a = try Socket.init(os.AF_INET, os.SOCK_STREAM | os.SOCK_CLOEXEC, os.IPPROTO_TCP);
defer a.deinit();
try a.bind(net.Address.initIp4([_]u8{ 0, 0, 0, 0 }, 0));
try a.listen(128);
const binded_address = try a.getLocalAddress();
const b = try Socket.init(os.AF_INET, os.SOCK_STREAM | os.SOCK_CLOEXEC, os.IPPROTO_TCP);
defer b.deinit();
try b.connect(binded_address);
try b.setReadTimeout(1);
const ab = try a.accept(os.SOCK_CLOEXEC);
defer ab.socket.deinit();
var buf: [1]u8 = undefined;
testing.expectError(error.WouldBlock, b.read(&buf));
}
test "socket/linux: create non-blocking socket pair" {
if (builtin.os.tag != .linux) return error.SkipZigTest;
const a = try Socket.init(os.AF_INET, os.SOCK_STREAM | os.SOCK_NONBLOCK | os.SOCK_CLOEXEC, os.IPPROTO_TCP);
defer a.deinit();
try a.bind(net.Address.initIp4([_]u8{ 0, 0, 0, 0 }, 0));
try a.listen(128);
const binded_address = try a.getLocalAddress();
const b = try Socket.init(os.AF_INET, os.SOCK_STREAM | os.SOCK_NONBLOCK | os.SOCK_CLOEXEC, os.IPPROTO_TCP);
defer b.deinit();
testing.expectError(error.WouldBlock, b.connect(binded_address));
try b.getError();
const ab = try a.accept(os.SOCK_NONBLOCK | os.SOCK_CLOEXEC);
defer ab.socket.deinit();
}

9
lib/std/x/os/os.zig Normal file
View File

@ -0,0 +1,9 @@
const std = @import("../../std.zig");
const testing = std.testing;
pub const Socket = @import("Socket.zig");
test {
testing.refAllDecls(@This());
}

View File

@ -15,6 +15,7 @@ const Target = std.Target;
const CrossTarget = std.zig.CrossTarget;
const macos = @import("system/macos.zig");
const native_endian = std.Target.current.cpu.arch.endian();
const linux = @import("system/linux.zig");
pub const windows = @import("system/windows.zig");
pub const getSDKPath = macos.getSDKPath;
@ -912,15 +913,19 @@ pub const NativeTargetInfo = struct {
.x86_64, .i386 => {
return @import("system/x86.zig").detectNativeCpuAndFeatures(cpu_arch, os, cross_target);
},
else => {
// This architecture does not have CPU model & feature detection yet.
// See https://github.com/ziglang/zig/issues/4591
return null;
},
else => {},
}
// This architecture does not have CPU model & feature detection yet.
// See https://github.com/ziglang/zig/issues/4591
if (std.Target.current.os.tag != .linux)
return null;
return linux.detectNativeCpuAndFeatures();
}
};
test {
_ = @import("system/macos.zig");
_ = @import("system/linux.zig");
}

View File

@ -0,0 +1,199 @@
const std = @import("std");
const mem = std.mem;
const io = std.io;
const fs = std.fs;
const fmt = std.fmt;
const testing = std.testing;
const Target = std.Target;
const CrossTarget = std.zig.CrossTarget;
const assert = std.debug.assert;
const SparcCpuinfoImpl = struct {
model: ?*const Target.Cpu.Model = null,
is_64bit: bool = false,
const cpu_names = .{
.{ "SuperSparc", &Target.sparc.cpu.supersparc },
.{ "HyperSparc", &Target.sparc.cpu.hypersparc },
.{ "SpitFire", &Target.sparc.cpu.ultrasparc },
.{ "BlackBird", &Target.sparc.cpu.ultrasparc },
.{ "Sabre", &Target.sparc.cpu.ultrasparc },
.{ "Hummingbird", &Target.sparc.cpu.ultrasparc },
.{ "Cheetah", &Target.sparc.cpu.ultrasparc3 },
.{ "Jalapeno", &Target.sparc.cpu.ultrasparc3 },
.{ "Jaguar", &Target.sparc.cpu.ultrasparc3 },
.{ "Panther", &Target.sparc.cpu.ultrasparc3 },
.{ "Serrano", &Target.sparc.cpu.ultrasparc3 },
.{ "UltraSparc T1", &Target.sparc.cpu.niagara },
.{ "UltraSparc T2", &Target.sparc.cpu.niagara2 },
.{ "UltraSparc T3", &Target.sparc.cpu.niagara3 },
.{ "UltraSparc T4", &Target.sparc.cpu.niagara4 },
.{ "UltraSparc T5", &Target.sparc.cpu.niagara4 },
.{ "LEON", &Target.sparc.cpu.leon3 },
};
fn line_hook(self: *SparcCpuinfoImpl, key: []const u8, value: []const u8) !bool {
if (mem.eql(u8, key, "cpu")) {
inline for (cpu_names) |pair| {
if (mem.indexOfPos(u8, value, 0, pair[0]) != null) {
self.model = pair[1];
break;
}
}
} else if (mem.eql(u8, key, "type")) {
self.is_64bit = mem.eql(u8, value, "sun4u") or mem.eql(u8, value, "sun4v");
}
return true;
}
fn finalize(self: *const SparcCpuinfoImpl, arch: Target.Cpu.Arch) ?Target.Cpu {
// At the moment we only support 64bit SPARC systems.
assert(self.is_64bit);
const model = self.model orelse Target.Cpu.Model.generic(arch);
return Target.Cpu{
.arch = arch,
.model = model,
.features = model.features,
};
}
};
const SparcCpuinfoParser = CpuinfoParser(SparcCpuinfoImpl);
test "cpuinfo: SPARC" {
try testParser(SparcCpuinfoParser, &Target.sparc.cpu.niagara2,
\\cpu : UltraSparc T2 (Niagara2)
\\fpu : UltraSparc T2 integrated FPU
\\pmu : niagara2
\\type : sun4v
);
}
const PowerpcCpuinfoImpl = struct {
model: ?*const Target.Cpu.Model = null,
const cpu_names = .{
.{ "604e", &Target.powerpc.cpu.@"604e" },
.{ "604", &Target.powerpc.cpu.@"604" },
.{ "7400", &Target.powerpc.cpu.@"7400" },
.{ "7410", &Target.powerpc.cpu.@"7400" },
.{ "7447", &Target.powerpc.cpu.@"7400" },
.{ "7455", &Target.powerpc.cpu.@"7450" },
.{ "G4", &Target.powerpc.cpu.@"g4" },
.{ "POWER4", &Target.powerpc.cpu.@"970" },
.{ "PPC970FX", &Target.powerpc.cpu.@"970" },
.{ "PPC970MP", &Target.powerpc.cpu.@"970" },
.{ "G5", &Target.powerpc.cpu.@"g5" },
.{ "POWER5", &Target.powerpc.cpu.@"g5" },
.{ "A2", &Target.powerpc.cpu.@"a2" },
.{ "POWER6", &Target.powerpc.cpu.@"pwr6" },
.{ "POWER7", &Target.powerpc.cpu.@"pwr7" },
.{ "POWER8", &Target.powerpc.cpu.@"pwr8" },
.{ "POWER8E", &Target.powerpc.cpu.@"pwr8" },
.{ "POWER8NVL", &Target.powerpc.cpu.@"pwr8" },
.{ "POWER9", &Target.powerpc.cpu.@"pwr9" },
.{ "POWER10", &Target.powerpc.cpu.@"pwr10" },
};
fn line_hook(self: *PowerpcCpuinfoImpl, key: []const u8, value: []const u8) !bool {
if (mem.eql(u8, key, "cpu")) {
// The model name is often followed by a comma or space and extra
// info.
inline for (cpu_names) |pair| {
const end_index = mem.indexOfAny(u8, value, ", ") orelse value.len;
if (mem.eql(u8, value[0..end_index], pair[0])) {
self.model = pair[1];
break;
}
}
// Stop the detection once we've seen the first core.
return false;
}
return true;
}
fn finalize(self: *const PowerpcCpuinfoImpl, arch: Target.Cpu.Arch) ?Target.Cpu {
const model = self.model orelse Target.Cpu.Model.generic(arch);
return Target.Cpu{
.arch = arch,
.model = model,
.features = model.features,
};
}
};
const PowerpcCpuinfoParser = CpuinfoParser(PowerpcCpuinfoImpl);
test "cpuinfo: PowerPC" {
try testParser(PowerpcCpuinfoParser, &Target.powerpc.cpu.@"970",
\\processor : 0
\\cpu : PPC970MP, altivec supported
\\clock : 1250.000000MHz
\\revision : 1.1 (pvr 0044 0101)
);
try testParser(PowerpcCpuinfoParser, &Target.powerpc.cpu.pwr8,
\\processor : 0
\\cpu : POWER8 (raw), altivec supported
\\clock : 2926.000000MHz
\\revision : 2.0 (pvr 004d 0200)
);
}
fn testParser(parser: anytype, expected_model: *const Target.Cpu.Model, input: []const u8) !void {
var fbs = io.fixedBufferStream(input);
const result = try parser.parse(.powerpc, fbs.reader());
testing.expectEqual(expected_model, result.?.model);
testing.expect(expected_model.features.eql(result.?.features));
}
// The generic implementation of a /proc/cpuinfo parser.
// For every line it invokes the line_hook method with the key and value strings
// as first and second parameters. Returning false from the hook function stops
// the iteration without raising an error.
// When all the lines have been analyzed the finalize method is called.
fn CpuinfoParser(comptime impl: anytype) type {
return struct {
fn parse(arch: Target.Cpu.Arch, reader: anytype) anyerror!?Target.Cpu {
var line_buf: [1024]u8 = undefined;
var obj: impl = .{};
while (true) {
const line = (try reader.readUntilDelimiterOrEof(&line_buf, '\n')) orelse break;
const colon_pos = mem.indexOfScalar(u8, line, ':') orelse continue;
const key = mem.trimRight(u8, line[0..colon_pos], " \t");
const value = mem.trimLeft(u8, line[colon_pos + 1 ..], " \t");
if (!try obj.line_hook(key, value))
break;
}
return obj.finalize(arch);
}
};
}
pub fn detectNativeCpuAndFeatures() ?Target.Cpu {
var f = fs.openFileAbsolute("/proc/cpuinfo", .{ .intended_io_mode = .blocking }) catch |err| switch (err) {
else => return null,
};
defer f.close();
const current_arch = std.Target.current.cpu.arch;
switch (current_arch) {
.sparcv9 => {
return SparcCpuinfoParser.parse(current_arch, f.reader()) catch null;
},
.powerpc, .powerpcle, .powerpc64, .powerpc64le => {
return PowerpcCpuinfoParser.parse(current_arch, f.reader()) catch null;
},
else => {},
}
return null;
}

View File

@ -104,6 +104,16 @@ pub const APFloat = opaque {
extern fn ZigClangAPFloat_toString(*const APFloat, precision: c_uint, maxPadding: c_uint, truncateZero: bool) [*:0]const u8;
};
pub const APFloatBaseSemantics = extern enum {
IEEEhalf,
BFloat,
IEEEsingle,
IEEEdouble,
x86DoubleExtended,
IEEEquad,
PPCDoubleDouble,
};
pub const APInt = opaque {
pub const getLimitedValue = ZigClangAPInt_getLimitedValue;
extern fn ZigClangAPInt_getLimitedValue(*const APInt, limit: u64) u64;
@ -455,6 +465,12 @@ pub const FileID = opaque {};
pub const FloatingLiteral = opaque {
pub const getValueAsApproximateDouble = ZigClangFloatingLiteral_getValueAsApproximateDouble;
extern fn ZigClangFloatingLiteral_getValueAsApproximateDouble(*const FloatingLiteral) f64;
pub const getBeginLoc = ZigClangIntegerLiteral_getBeginLoc;
extern fn ZigClangIntegerLiteral_getBeginLoc(*const FloatingLiteral) SourceLocation;
pub const getRawSemantics = ZigClangFloatingLiteral_getRawSemantics;
extern fn ZigClangFloatingLiteral_getRawSemantics(*const FloatingLiteral) APFloatBaseSemantics;
};
pub const ForStmt = opaque {

View File

@ -449,7 +449,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
.rbrace_src = src_data.rbrace_src,
.source = src_data.source,
};
defer function.register_manager.deinit(bin_file.allocator);
defer function.stack.deinit(bin_file.allocator);
defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
@ -779,8 +778,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
branch.inst_table.putAssumeCapacity(inst, .dead);
switch (prev_value) {
.register => |reg| {
const canon_reg = toCanonicalReg(reg);
self.register_manager.freeReg(canon_reg);
// TODO separate architectures with registers from
// stack-based architectures (spu_2)
if (callee_preserved_regs.len > 0) {
const canon_reg = toCanonicalReg(reg);
self.register_manager.freeReg(canon_reg);
}
},
else => {}, // TODO process stack allocation death
}
@ -920,9 +923,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
const ptr_bits = arch.ptrBitWidth();
const ptr_bytes: u64 = @divExact(ptr_bits, 8);
if (abi_size <= ptr_bytes) {
try self.register_manager.registers.ensureCapacity(self.gpa, self.register_manager.registers.count() + 1);
if (self.register_manager.tryAllocReg(inst)) |reg| {
return MCValue{ .register = registerAlias(reg, abi_size) };
// TODO separate architectures with registers from
// stack-based architectures (spu_2)
if (callee_preserved_regs.len > 0) {
if (self.register_manager.tryAllocReg(inst)) |reg| {
return MCValue{ .register = registerAlias(reg, abi_size) };
}
}
}
}
@ -952,8 +958,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
/// `reg_owner` is the instruction that gets associated with the register in the register table.
/// This can have a side effect of spilling instructions to the stack to free up a register.
fn copyToNewRegister(self: *Self, reg_owner: *ir.Inst, mcv: MCValue) !MCValue {
try self.register_manager.registers.ensureCapacity(self.gpa, @intCast(u32, self.register_manager.registers.count() + 1));
const reg = try self.register_manager.allocReg(reg_owner);
try self.genSetReg(reg_owner.src, reg_owner.ty, reg, mcv);
return MCValue{ .register = reg };
@ -1240,10 +1244,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
.register => |reg| {
// If it's in the registers table, need to associate the register with the
// new instruction.
if (self.register_manager.registers.getEntry(toCanonicalReg(reg))) |entry| {
entry.value = inst;
// TODO separate architectures with registers from
// stack-based architectures (spu_2)
if (callee_preserved_regs.len > 0) {
if (reg.allocIndex()) |index| {
if (!self.register_manager.isRegFree(reg)) {
self.register_manager.registers[index] = inst;
}
}
log.debug("reusing {} => {*}", .{ reg, inst });
}
log.debug("reusing {} => {*}", .{ reg, inst });
},
.stack_offset => |off| {
log.debug("reusing stack offset {} => {*}", .{ off, inst });
@ -1738,6 +1748,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
const arg_index = self.arg_index;
self.arg_index += 1;
// TODO separate architectures with registers from
// stack-based architectures (spu_2)
if (callee_preserved_regs.len == 0) {
return self.fail(inst.base.src, "TODO implement Register enum for {}", .{self.target.cpu.arch});
}
@ -1769,7 +1781,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
switch (mcv) {
.register => |reg| {
try self.register_manager.registers.ensureCapacity(self.gpa, self.register_manager.registers.count() + 1);
self.register_manager.getRegAssumeFree(toCanonicalReg(reg), &inst.base);
},
else => {},
@ -2075,7 +2086,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
switch (mc_arg) {
.none => continue,
.register => |reg| {
try self.register_manager.getRegWithoutTracking(reg);
// TODO prevent this macho if block to be generated for all archs
switch (arch) {
.x86_64, .aarch64 => try self.register_manager.getRegWithoutTracking(reg),
else => unreachable,
}
try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
},
.stack_offset => {
@ -2397,8 +2412,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
const parent_free_registers = self.register_manager.free_registers;
var parent_stack = try self.stack.clone(self.gpa);
defer parent_stack.deinit(self.gpa);
var parent_registers = try self.register_manager.registers.clone(self.gpa);
defer parent_registers.deinit(self.gpa);
const parent_registers = self.register_manager.registers;
try self.branch_stack.append(.{});
@ -2414,9 +2428,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
var saved_then_branch = self.branch_stack.pop();
defer saved_then_branch.deinit(self.gpa);
self.register_manager.registers.deinit(self.gpa);
self.register_manager.registers = parent_registers;
parent_registers = .{};
self.stack.deinit(self.gpa);
self.stack = parent_stack;

View File

@ -1,5 +1,7 @@
const std = @import("std");
const DW = std.dwarf;
const assert = std.debug.assert;
const testing = std.testing;
// TODO: this is only tagged to facilitate the monstrosity.
// Once packed structs work make it packed.
@ -110,7 +112,7 @@ pub const Instruction = union(enum) {
// -- less burden on callsite, bonus semantic checking
fn bType(op: u7, fn3: u3, r1: Register, r2: Register, imm: i13) Instruction {
const umm = @bitCast(u13, imm);
if (umm % 2 != 0) @panic("Internal error: misaligned branch target");
assert(umm % 2 == 0); // misaligned branch target
return Instruction{
.B = .{
@ -140,15 +142,15 @@ pub const Instruction = union(enum) {
}
fn jType(op: u7, rd: Register, imm: i21) Instruction {
const umm = @bitcast(u21, imm);
if (umm % 2 != 0) @panic("Internal error: misaligned jump target");
const umm = @bitCast(u21, imm);
assert(umm % 2 == 0); // misaligned jump target
return Instruction{
.J = .{
.opcode = op,
.rd = @enumToInt(rd),
.imm1_10 = @truncate(u10, umm >> 1),
.imm11 = @truncate(u1, umm >> 1),
.imm11 = @truncate(u1, umm >> 11),
.imm12_19 = @truncate(u8, umm >> 12),
.imm20 = @truncate(u1, umm >> 20),
},
@ -340,27 +342,27 @@ pub const Instruction = union(enum) {
// Branch
pub fn beq(r1: Register, r2: Register, offset: u13) Instruction {
pub fn beq(r1: Register, r2: Register, offset: i13) Instruction {
return bType(0b1100011, 0b000, r1, r2, offset);
}
pub fn bne(r1: Register, r2: Register, offset: u13) Instruction {
pub fn bne(r1: Register, r2: Register, offset: i13) Instruction {
return bType(0b1100011, 0b001, r1, r2, offset);
}
pub fn blt(r1: Register, r2: Register, offset: u13) Instruction {
pub fn blt(r1: Register, r2: Register, offset: i13) Instruction {
return bType(0b1100011, 0b100, r1, r2, offset);
}
pub fn bge(r1: Register, r2: Register, offset: u13) Instruction {
pub fn bge(r1: Register, r2: Register, offset: i13) Instruction {
return bType(0b1100011, 0b101, r1, r2, offset);
}
pub fn bltu(r1: Register, r2: Register, offset: u13) Instruction {
pub fn bltu(r1: Register, r2: Register, offset: i13) Instruction {
return bType(0b1100011, 0b110, r1, r2, offset);
}
pub fn bgeu(r1: Register, r2: Register, offset: u13) Instruction {
pub fn bgeu(r1: Register, r2: Register, offset: i13) Instruction {
return bType(0b1100011, 0b111, r1, r2, offset);
}
@ -431,3 +433,38 @@ pub const Register = enum(u5) {
pub const callee_preserved_regs = [_]Register{
.s0, .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11,
};
test "serialize instructions" {
const Testcase = struct {
inst: Instruction,
expected: u32,
};
const testcases = [_]Testcase{
.{ // add t6, zero, zero
.inst = Instruction.add(.t6, .zero, .zero),
.expected = 0b0000000_00000_00000_000_11111_0110011,
},
.{ // sd s0, 0x7f(s0)
.inst = Instruction.sd(.s0, 0x7f, .s0),
.expected = 0b0000011_01000_01000_011_11111_0100011,
},
.{ // bne s0, s1, 0x42
.inst = Instruction.bne(.s0, .s1, 0x42),
.expected = 0b0_000010_01001_01000_001_0001_0_1100011,
},
.{ // j 0x1a
.inst = Instruction.jal(.zero, 0x1a),
.expected = 0b0_0000001101_0_00000000_00000_1101111,
},
.{ // ebreak
.inst = Instruction.ebreak,
.expected = 0b000000000001_00000_000_00000_1110011,
},
};
for (testcases) |case| {
const actual = case.inst.toU32();
testing.expectEqual(case.expected, actual);
}
}

View File

@ -9,6 +9,7 @@ const build_options = @import("build_options");
const is_darwin = Target.current.isDarwin();
const is_windows = Target.current.os.tag == .windows;
const is_gnu = Target.current.isGnu();
const is_haiku = Target.current.os.tag == .haiku;
const log = std.log.scoped(.libc_installation);
@ -279,8 +280,14 @@ pub const LibCInstallation = struct {
return error.CCompilerCannotFindHeaders;
}
const include_dir_example_file = "stdlib.h";
const sys_include_dir_example_file = if (is_windows) "sys\\types.h" else "sys/errno.h";
const include_dir_example_file = if (is_haiku) "posix/stdlib.h" else "stdlib.h";
const sys_include_dir_example_file = if (is_windows)
"sys\\types.h"
else if (is_haiku)
"posix/errno.h"
else
"sys/errno.h"
;
var path_i: usize = 0;
while (path_i < search_paths.items.len) : (path_i += 1) {

View File

@ -16,7 +16,7 @@ pub fn RegisterManager(
) type {
return struct {
/// The key must be canonical register.
registers: std.AutoHashMapUnmanaged(Register, *ir.Inst) = .{},
registers: [callee_preserved_regs.len]?*ir.Inst = [_]?*ir.Inst{null} ** callee_preserved_regs.len,
free_registers: FreeRegInt = math.maxInt(FreeRegInt),
/// Tracks all registers allocated in the course of this function
allocated_registers: FreeRegInt = 0,
@ -31,14 +31,6 @@ pub fn RegisterManager(
return @fieldParentPtr(Function, "register_manager", self);
}
pub fn deinit(self: *Self, allocator: *Allocator) void {
self.registers.deinit(allocator);
}
fn isTracked(reg: Register) bool {
return reg.allocIndex() != null;
}
fn markRegUsed(self: *Self, reg: Register) void {
if (FreeRegInt == u0) return;
const index = reg.allocIndex() orelse return;
@ -73,13 +65,13 @@ pub fn RegisterManager(
return self.allocated_registers & @as(FreeRegInt, 1) << shift != 0;
}
/// Before calling, must ensureCapacity + count on self.registers.
/// Returns `null` if all registers are allocated.
pub fn tryAllocRegs(self: *Self, comptime count: comptime_int, insts: [count]*ir.Inst) ?[count]Register {
if (self.tryAllocRegsWithoutTracking(count)) |regs| {
for (regs) |reg, i| {
const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null
self.registers[index] = insts[i];
self.markRegUsed(reg);
self.registers.putAssumeCapacityNoClobber(reg, insts[i]);
}
return regs;
@ -88,13 +80,11 @@ pub fn RegisterManager(
}
}
/// Before calling, must ensureCapacity + 1 on self.registers.
/// Returns `null` if all registers are allocated.
pub fn tryAllocReg(self: *Self, inst: *ir.Inst) ?Register {
return if (tryAllocRegs(self, 1, .{inst})) |regs| regs[0] else null;
}
/// Before calling, must ensureCapacity + count on self.registers.
pub fn allocRegs(self: *Self, comptime count: comptime_int, insts: [count]*ir.Inst) ![count]Register {
comptime assert(count > 0 and count <= callee_preserved_regs.len);
@ -106,24 +96,22 @@ pub fn RegisterManager(
std.mem.copy(Register, &regs, callee_preserved_regs[0..count]);
for (regs) |reg, i| {
const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null
if (self.isRegFree(reg)) {
self.markRegUsed(reg);
self.registers.putAssumeCapacityNoClobber(reg, insts[i]);
} else {
const regs_entry = self.registers.getEntry(reg).?;
const spilled_inst = regs_entry.value;
regs_entry.value = insts[i];
const spilled_inst = self.registers[index].?;
try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);
}
self.registers[index] = insts[i];
}
break :blk regs;
};
}
/// Before calling, must ensureCapacity + 1 on self.registers.
pub fn allocReg(self: *Self, inst: *ir.Inst) !Register {
return (try allocRegs(self, 1, .{inst}))[0];
return (try self.allocRegs(1, .{inst}))[0];
}
/// Does not track the registers.
@ -150,37 +138,48 @@ pub fn RegisterManager(
/// Does not track the register.
/// Returns `null` if all registers are allocated.
pub fn tryAllocRegWithoutTracking(self: *Self) ?Register {
return if (tryAllocRegsWithoutTracking(self, 1)) |regs| regs[0] else null;
return if (self.tryAllocRegsWithoutTracking(1)) |regs| regs[0] else null;
}
/// Does not track the registers
pub fn allocRegsWithoutTracking(self: *Self, comptime count: comptime_int) ![count]Register {
return self.tryAllocRegsWithoutTracking(count) orelse blk: {
// We'll take over the first count registers. Spill
// the instructions that were previously there to a
// stack allocations.
var regs: [count]Register = undefined;
std.mem.copy(Register, &regs, callee_preserved_regs[0..count]);
for (regs) |reg, i| {
const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null
if (!self.isRegFree(reg)) {
const spilled_inst = self.registers[index].?;
try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);
self.registers[index] = null;
self.markRegFree(reg);
}
}
break :blk regs;
};
}
/// Does not track the register.
pub fn allocRegWithoutTracking(self: *Self) !Register {
return self.tryAllocRegWithoutTracking() orelse b: {
// We'll take over the first register. Move the instruction that was previously
// there to a stack allocation.
const reg = callee_preserved_regs[0];
const regs_entry = self.registers.remove(reg).?;
const spilled_inst = regs_entry.value;
try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);
self.markRegFree(reg);
break :b reg;
};
return (try self.allocRegsWithoutTracking(1))[0];
}
/// Allocates the specified register with the specified
/// instruction. Spills the register if it is currently
/// allocated.
/// Before calling, must ensureCapacity + 1 on self.registers.
pub fn getReg(self: *Self, reg: Register, inst: *ir.Inst) !void {
if (!isTracked(reg)) return;
const index = reg.allocIndex() orelse return;
if (!self.isRegFree(reg)) {
// Move the instruction that was previously there to a
// stack allocation.
const regs_entry = self.registers.getEntry(reg).?;
const spilled_inst = regs_entry.value;
regs_entry.value = inst;
const spilled_inst = self.registers[index].?;
self.registers[index] = inst;
try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);
} else {
self.getRegAssumeFree(reg, inst);
@ -190,34 +189,33 @@ pub fn RegisterManager(
/// Spills the register if it is currently allocated.
/// Does not track the register.
pub fn getRegWithoutTracking(self: *Self, reg: Register) !void {
if (!isTracked(reg)) return;
const index = reg.allocIndex() orelse return;
if (!self.isRegFree(reg)) {
// Move the instruction that was previously there to a
// stack allocation.
const regs_entry = self.registers.remove(reg).?;
const spilled_inst = regs_entry.value;
const spilled_inst = self.registers[index].?;
try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);
self.markRegFree(reg);
}
}
/// Allocates the specified register with the specified
/// instruction. Assumes that the register is free and no
/// instruction. Asserts that the register is free and no
/// spilling is necessary.
/// Before calling, must ensureCapacity + 1 on self.registers.
pub fn getRegAssumeFree(self: *Self, reg: Register, inst: *ir.Inst) void {
if (!isTracked(reg)) return;
const index = reg.allocIndex() orelse return;
self.registers.putAssumeCapacityNoClobber(reg, inst);
assert(self.registers[index] == null);
self.registers[index] = inst;
self.markRegUsed(reg);
}
/// Marks the specified register as free
pub fn freeReg(self: *Self, reg: Register) void {
if (!isTracked(reg)) return;
const index = reg.allocIndex() orelse return;
_ = self.registers.remove(reg);
self.registers[index] = null;
self.markRegFree(reg);
}
};
@ -247,7 +245,6 @@ const MockFunction = struct {
const Self = @This();
pub fn deinit(self: *Self) void {
self.register_manager.deinit(self.allocator);
self.spilled.deinit(self.allocator);
}
@ -273,7 +270,6 @@ test "tryAllocReg: no spilling" {
std.testing.expect(!function.register_manager.isRegAllocated(.r2));
std.testing.expect(!function.register_manager.isRegAllocated(.r3));
try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2);
std.testing.expectEqual(@as(?MockRegister, .r2), function.register_manager.tryAllocReg(&mock_instruction));
std.testing.expectEqual(@as(?MockRegister, .r3), function.register_manager.tryAllocReg(&mock_instruction));
std.testing.expectEqual(@as(?MockRegister, null), function.register_manager.tryAllocReg(&mock_instruction));
@ -305,7 +301,6 @@ test "allocReg: spilling" {
std.testing.expect(!function.register_manager.isRegAllocated(.r2));
std.testing.expect(!function.register_manager.isRegAllocated(.r3));
try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2);
std.testing.expectEqual(@as(?MockRegister, .r2), try function.register_manager.allocReg(&mock_instruction));
std.testing.expectEqual(@as(?MockRegister, .r3), try function.register_manager.allocReg(&mock_instruction));
@ -336,14 +331,12 @@ test "getReg" {
std.testing.expect(!function.register_manager.isRegAllocated(.r2));
std.testing.expect(!function.register_manager.isRegAllocated(.r3));
try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2);
try function.register_manager.getReg(.r3, &mock_instruction);
std.testing.expect(!function.register_manager.isRegAllocated(.r2));
std.testing.expect(function.register_manager.isRegAllocated(.r3));
// Spill r3
try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2);
try function.register_manager.getReg(.r3, &mock_instruction);
std.testing.expect(!function.register_manager.isRegAllocated(.r2));

View File

@ -84,6 +84,7 @@ enum CallingConvention {
CallingConventionAPCS,
CallingConventionAAPCS,
CallingConventionAAPCSVFP,
CallingConventionSysV
};
// This one corresponds to the builtin.zig enum.

View File

@ -974,6 +974,7 @@ const char *calling_convention_name(CallingConvention cc) {
case CallingConventionAAPCS: return "AAPCS";
case CallingConventionAAPCSVFP: return "AAPCSVFP";
case CallingConventionInline: return "Inline";
case CallingConventionSysV: return "SysV";
}
zig_unreachable();
}
@ -995,6 +996,7 @@ bool calling_convention_allows_zig_types(CallingConvention cc) {
case CallingConventionAPCS:
case CallingConventionAAPCS:
case CallingConventionAAPCSVFP:
case CallingConventionSysV:
return false;
}
zig_unreachable();
@ -1969,6 +1971,10 @@ Error emit_error_unless_callconv_allowed_for_target(CodeGen *g, AstNode *source_
case CallingConventionAAPCSVFP:
if (!target_is_arm(g->zig_target))
allowed_platforms = "ARM";
break;
case CallingConventionSysV:
if (g->zig_target->arch != ZigLLVM_x86_64)
allowed_platforms = "x86_64";
}
if (allowed_platforms != nullptr) {
add_node_error(g, source_node, buf_sprintf(
@ -3805,6 +3811,7 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
case CallingConventionAPCS:
case CallingConventionAAPCS:
case CallingConventionAAPCSVFP:
case CallingConventionSysV:
add_fn_export(g, fn_table_entry, buf_ptr(&fn_table_entry->symbol_name),
GlobalLinkageIdStrong, fn_cc);
break;
@ -4769,11 +4776,11 @@ Error type_is_nonnull_ptr2(CodeGen *g, ZigType *type, bool *result) {
return ErrorNone;
}
static uint32_t get_async_frame_align_bytes(CodeGen *g) {
uint32_t a = g->pointer_size_bytes * 2;
// promises have at least alignment 8 so that we can have 3 extra bits when doing atomicrmw
if (a < 8) a = 8;
return a;
uint32_t get_async_frame_align_bytes(CodeGen *g) {
// Due to how the frame structure is built the minimum alignment is the one
// of a usize (or pointer).
// label (grep this): [fn_frame_struct_layout]
return max(g->builtin_types.entry_usize->abi_align, target_fn_align(g->zig_target));
}
uint32_t get_ptr_align(CodeGen *g, ZigType *type) {
@ -4789,11 +4796,8 @@ uint32_t get_ptr_align(CodeGen *g, ZigType *type) {
return (ptr_type->data.pointer.explicit_alignment == 0) ?
get_abi_alignment(g, ptr_type->data.pointer.child_type) : ptr_type->data.pointer.explicit_alignment;
} else if (ptr_type->id == ZigTypeIdFn) {
// I tried making this use LLVMABIAlignmentOfType but it trips this assertion in LLVM:
// "Cannot getTypeInfo() on a type that is unsized!"
// when getting the alignment of `?fn() callconv(.C) void`.
// See http://lists.llvm.org/pipermail/llvm-dev/2018-September/126142.html
return (ptr_type->data.fn.fn_type_id.alignment == 0) ? 1 : ptr_type->data.fn.fn_type_id.alignment;
return (ptr_type->data.fn.fn_type_id.alignment == 0) ?
target_fn_ptr_align(g->zig_target) : ptr_type->data.fn.fn_type_id.alignment;
} else if (ptr_type->id == ZigTypeIdAnyFrame) {
return get_async_frame_align_bytes(g);
} else {

View File

@ -47,6 +47,7 @@ ZigType *get_test_fn_type(CodeGen *g);
ZigType *get_any_frame_type(CodeGen *g, ZigType *result_type);
bool handle_is_ptr(CodeGen *g, ZigType *type_entry);
Error emit_error_unless_callconv_allowed_for_target(CodeGen *g, AstNode *source_node, CallingConvention cc);
uint32_t get_async_frame_align_bytes(CodeGen *g);
bool type_has_bits(CodeGen *g, ZigType *type_entry);
Error type_has_bits2(CodeGen *g, ZigType *type_entry, bool *result);

View File

@ -204,6 +204,9 @@ static ZigLLVM_CallingConv get_llvm_cc(CodeGen *g, CallingConvention cc) {
case CallingConventionSignal:
assert(g->zig_target->arch == ZigLLVM_avr);
return ZigLLVM_AVR_SIGNAL;
case CallingConventionSysV:
assert(g->zig_target->arch == ZigLLVM_x86_64);
return ZigLLVM_X86_64_SysV;
}
zig_unreachable();
}
@ -348,6 +351,7 @@ static bool cc_want_sret_attr(CallingConvention cc) {
case CallingConventionAPCS:
case CallingConventionAAPCS:
case CallingConventionAAPCSVFP:
case CallingConventionSysV:
return true;
case CallingConventionAsync:
case CallingConventionUnspecified:
@ -9079,6 +9083,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
static_assert(CallingConventionAPCS == 11, "");
static_assert(CallingConventionAAPCS == 12, "");
static_assert(CallingConventionAAPCSVFP == 13, "");
static_assert(CallingConventionSysV == 14, "");
static_assert(BuiltinPtrSizeOne == 0, "");
static_assert(BuiltinPtrSizeMany == 1, "");

View File

@ -19216,6 +19216,7 @@ static IrInstGen *ir_analyze_instruction_export(IrAnalyze *ira, IrInstSrcExport
case CallingConventionAPCS:
case CallingConventionAAPCS:
case CallingConventionAAPCSVFP:
case CallingConventionSysV:
add_fn_export(ira->codegen, fn_entry, buf_ptr(symbol_name), global_linkage_id, cc);
fn_entry->section_name = section_name;
break;
@ -20659,8 +20660,12 @@ static IrInstGen *analyze_casted_new_stack(IrAnalyze *ira, IrInst* source_instr,
get_fn_frame_type(ira->codegen, fn_entry), false);
return ir_implicit_cast(ira, new_stack, needed_frame_type);
} else {
// XXX The stack alignment is hardcoded to 16 here and in
// std.Target.stack_align.
const uint32_t required_align = is_async_call_builtin ?
get_async_frame_align_bytes(ira->codegen) : 16;
ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8,
false, false, PtrLenUnknown, target_fn_align(ira->codegen->zig_target), 0, 0, false);
false, false, PtrLenUnknown, required_align, 0, 0, false);
ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr);
ira->codegen->need_frame_size_prefix_data = true;
return ir_implicit_cast2(ira, new_stack_src, new_stack, u8_slice);
@ -26079,11 +26084,11 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
fields[0]->special = ConstValSpecialStatic;
fields[0]->type = get_builtin_type(ira->codegen, "CallingConvention");
bigint_init_unsigned(&fields[0]->data.x_enum_tag, type_entry->data.fn.fn_type_id.cc);
// alignment: u29
// alignment: comptime_int
ensure_field_index(result->type, "alignment", 1);
fields[1]->special = ConstValSpecialStatic;
fields[1]->type = ira->codegen->builtin_types.entry_num_lit_int;
bigint_init_unsigned(&fields[1]->data.x_bigint, type_entry->data.fn.fn_type_id.alignment);
bigint_init_unsigned(&fields[1]->data.x_bigint, get_ptr_align(ira->codegen, type_entry));
// is_generic: bool
ensure_field_index(result->type, "is_generic", 2);
bool is_generic = type_entry->data.fn.is_generic;
@ -30095,7 +30100,7 @@ static IrInstGen *ir_align_cast(IrAnalyze *ira, IrInstGen *target, uint32_t alig
fn_type_id.alignment = align_bytes;
result_type = get_fn_type(ira->codegen, &fn_type_id);
} else if (target_type->id == ZigTypeIdAnyFrame) {
if (align_bytes >= target_fn_align(ira->codegen->zig_target)) {
if (align_bytes >= get_async_frame_align_bytes(ira->codegen)) {
result_type = target_type;
} else {
ir_add_error(ira, &target->base, buf_sprintf("sub-aligned anyframe not allowed"));

View File

@ -1253,6 +1253,37 @@ bool target_is_ppc(const ZigTarget *target) {
target->arch == ZigLLVM_ppc64le;
}
unsigned target_fn_align(const ZigTarget *target) {
return 16;
// Returns the minimum alignment for every function pointer on the given
// architecture.
unsigned target_fn_ptr_align(const ZigTarget *target) {
// TODO This is a pessimization but is always correct.
return 1;
}
// Returns the minimum alignment for every function on the given architecture.
unsigned target_fn_align(const ZigTarget *target) {
switch (target->arch) {
case ZigLLVM_riscv32:
case ZigLLVM_riscv64:
// TODO If the C extension is not present the value is 4.
return 2;
case ZigLLVM_ppc:
case ZigLLVM_ppcle:
case ZigLLVM_ppc64:
case ZigLLVM_ppc64le:
case ZigLLVM_aarch64:
case ZigLLVM_aarch64_be:
case ZigLLVM_aarch64_32:
case ZigLLVM_sparc:
case ZigLLVM_sparcel:
case ZigLLVM_sparcv9:
case ZigLLVM_mips:
case ZigLLVM_mipsel:
case ZigLLVM_mips64:
case ZigLLVM_mips64el:
return 4;
default:
return 1;
}
}

View File

@ -98,6 +98,7 @@ size_t target_libc_count(void);
void target_libc_enum(size_t index, ZigTarget *out_target);
bool target_libc_needs_crti_crtn(const ZigTarget *target);
unsigned target_fn_ptr_align(const ZigTarget *target);
unsigned target_fn_align(const ZigTarget *target);
#endif

View File

@ -8,6 +8,7 @@ const ctok = std.c.tokenizer;
const CToken = std.c.Token;
const mem = std.mem;
const math = std.math;
const meta = std.meta;
const ast = @import("translate_c/ast.zig");
const Node = ast.Node;
const Tag = Node.Tag;
@ -1741,7 +1742,7 @@ fn transImplicitCastExpr(
}
fn isBuiltinDefined(name: []const u8) bool {
inline for (std.meta.declarations(c_builtins)) |decl| {
inline for (meta.declarations(c_builtins)) |decl| {
if (std.mem.eql(u8, name, decl.name)) return true;
}
return false;
@ -3157,7 +3158,7 @@ const ClangFunctionType = union(enum) {
NoProto: *const clang.FunctionType,
fn getReturnType(self: @This()) clang.QualType {
switch (@as(std.meta.Tag(@This()), self)) {
switch (@as(meta.Tag(@This()), self)) {
.Proto => return self.Proto.getReturnType(),
.NoProto => return self.NoProto.getReturnType(),
}
@ -3539,7 +3540,7 @@ fn transCPtrCast(
expr
else blk: {
const child_type_node = try transQualType(c, scope, child_type, loc);
const alignof = try Tag.alignof.create(c.arena, child_type_node);
const alignof = try Tag.std_meta_alignment.create(c.arena, child_type_node);
const align_cast = try Tag.align_cast.create(c.arena, .{ .lhs = alignof, .rhs = expr });
break :blk align_cast;
};
@ -3547,9 +3548,22 @@ fn transCPtrCast(
}
}
fn transFloatingLiteral(c: *Context, scope: *Scope, stmt: *const clang.FloatingLiteral, used: ResultUsed) TransError!Node {
fn transFloatingLiteral(c: *Context, scope: *Scope, expr: *const clang.FloatingLiteral, used: ResultUsed) TransError!Node {
switch (expr.getRawSemantics()) {
.IEEEhalf, // f16
.IEEEsingle, // f32
.IEEEdouble, // f64
=> {},
else => |format| return fail(
c,
error.UnsupportedTranslation,
expr.getBeginLoc(),
"unsupported floating point constant format {}",
.{format},
),
}
// TODO use something more accurate
var dbl = stmt.getValueAsApproximateDouble();
var dbl = expr.getValueAsApproximateDouble();
const is_negative = dbl < 0;
if (is_negative) dbl = -dbl;
const str = if (dbl == std.math.floor(dbl))
@ -4093,7 +4107,7 @@ fn transCreateNodeAPInt(c: *Context, int: *const clang.APSInt) !Node {
}
fn transCreateNodeNumber(c: *Context, num: anytype, num_kind: enum { int, float }) !Node {
const fmt_s = if (comptime std.meta.trait.isNumber(@TypeOf(num))) "{d}" else "{s}";
const fmt_s = if (comptime meta.trait.isNumber(@TypeOf(num))) "{d}" else "{s}";
const str = try std.fmt.allocPrint(c.arena, fmt_s, .{num});
if (num_kind == .float)
return Tag.float_literal.create(c.arena, str)
@ -4402,6 +4416,7 @@ fn transCC(
.X86ThisCall => return CallingConvention.Thiscall,
.AAPCS => return CallingConvention.AAPCS,
.AAPCS_VFP => return CallingConvention.AAPCSVFP,
.X86_64SysV => return CallingConvention.SysV,
else => return fail(
c,
error.UnsupportedType,
@ -4848,12 +4863,12 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!Node {
// make the output less noisy by skipping promoteIntLiteral where
// it's guaranteed to not be required because of C standard type constraints
const guaranteed_to_fit = switch (suffix) {
.none => if (math.cast(i16, value)) |_| true else |_| false,
.u => if (math.cast(u16, value)) |_| true else |_| false,
.l => if (math.cast(i32, value)) |_| true else |_| false,
.lu => if (math.cast(u32, value)) |_| true else |_| false,
.ll => if (math.cast(i64, value)) |_| true else |_| false,
.llu => if (math.cast(u64, value)) |_| true else |_| false,
.none => !meta.isError(math.cast(i16, value)),
.u => !meta.isError(math.cast(u16, value)),
.l => !meta.isError(math.cast(i32, value)),
.lu => !meta.isError(math.cast(u32, value)),
.ll => !meta.isError(math.cast(i64, value)),
.llu => !meta.isError(math.cast(u64, value)),
.f => unreachable,
};

View File

@ -120,8 +120,11 @@ pub const Node = extern union {
std_math_Log2Int,
/// @intCast(lhs, rhs)
int_cast,
/// @rem(lhs, rhs)
/// @import("std").meta.promoteIntLiteral(value, type, radix)
std_meta_promoteIntLiteral,
/// @import("std").meta.alignment(value)
std_meta_alignment,
/// @rem(lhs, rhs)
rem,
/// @divTrunc(lhs, rhs)
div_trunc,
@ -260,6 +263,7 @@ pub const Node = extern union {
.switch_else,
.block_single,
.std_meta_sizeof,
.std_meta_alignment,
.bool_to_int,
.sizeof,
.alignof,
@ -876,6 +880,11 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
const import_node = try renderStdImport(c, "meta", "promoteIntLiteral");
return renderCall(c, import_node, &.{ payload.type, payload.value, payload.radix });
},
.std_meta_alignment => {
const payload = node.castTag(.std_meta_alignment).?.data;
const import_node = try renderStdImport(c, "meta", "alignment");
return renderCall(c, import_node, &.{payload});
},
.std_meta_sizeof => {
const payload = node.castTag(.std_meta_sizeof).?.data;
const import_node = try renderStdImport(c, "meta", "sizeof");
@ -2144,6 +2153,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
.typeof,
.typeinfo,
.std_meta_sizeof,
.std_meta_alignment,
.std_meta_cast,
.std_meta_promoteIntLiteral,
.std_meta_vector,

View File

@ -2528,6 +2528,11 @@ double ZigClangFloatingLiteral_getValueAsApproximateDouble(const ZigClangFloatin
return casted->getValueAsApproximateDouble();
}
ZigClangAPFloatBase_Semantics ZigClangFloatingLiteral_getRawSemantics(const ZigClangFloatingLiteral *self) {
auto casted = reinterpret_cast<const clang::FloatingLiteral *>(self);
return static_cast<ZigClangAPFloatBase_Semantics>(casted->getRawSemantics());
}
enum ZigClangStringLiteral_StringKind ZigClangStringLiteral_getKind(const struct ZigClangStringLiteral *self) {
auto casted = reinterpret_cast<const clang::StringLiteral *>(self);
return (ZigClangStringLiteral_StringKind)casted->getKind();

View File

@ -881,6 +881,16 @@ enum ZigClangAPFloat_roundingMode {
ZigClangAPFloat_roundingMode_Invalid = -1,
};
enum ZigClangAPFloatBase_Semantics {
ZigClangAPFloatBase_Semantics_IEEEhalf,
ZigClangAPFloatBase_Semantics_BFloat,
ZigClangAPFloatBase_Semantics_IEEEsingle,
ZigClangAPFloatBase_Semantics_IEEEdouble,
ZigClangAPFloatBase_Semantics_x87DoubleExtended,
ZigClangAPFloatBase_Semantics_IEEEquad,
ZigClangAPFloatBase_Semantics_PPCDoubleDouble,
};
enum ZigClangStringLiteral_StringKind {
ZigClangStringLiteral_StringKind_Ascii,
ZigClangStringLiteral_StringKind_Wide,
@ -1142,6 +1152,7 @@ ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangDeclStmt_getBeginLoc(const st
ZIG_EXTERN_C unsigned ZigClangAPFloat_convertToHexString(const struct ZigClangAPFloat *self, char *DST,
unsigned HexDigits, bool UpperCase, enum ZigClangAPFloat_roundingMode RM);
ZIG_EXTERN_C double ZigClangFloatingLiteral_getValueAsApproximateDouble(const ZigClangFloatingLiteral *self);
ZIG_EXTERN_C ZigClangAPFloatBase_Semantics ZigClangFloatingLiteral_getRawSemantics(const ZigClangFloatingLiteral *self);
ZIG_EXTERN_C enum ZigClangStringLiteral_StringKind ZigClangStringLiteral_getKind(const struct ZigClangStringLiteral *self);
ZIG_EXTERN_C uint32_t ZigClangStringLiteral_getCodeUnit(const struct ZigClangStringLiteral *self, size_t i);

View File

@ -2136,7 +2136,9 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\}
\\fn func() callconv(.Async) void {}
, &[_][]const u8{
"tmp.zig:4:21: error: expected type '[]align(16) u8', found '*[64]u8'",
// Split the check in two as the alignment value is target dependent.
"tmp.zig:4:21: error: expected type '[]align(",
") u8', found '*[64]u8'",
});
cases.add("atomic orderings of fence Acquire or stricter",

View File

@ -306,7 +306,7 @@ test "type info: function type info" {
fn testFunction() void {
const fn_info = @typeInfo(@TypeOf(foo));
expect(fn_info == .Fn);
expect(fn_info.Fn.alignment == 0);
expect(fn_info.Fn.alignment > 0);
expect(fn_info.Fn.calling_convention == .C);
expect(!fn_info.Fn.is_generic);
expect(fn_info.Fn.args.len == 2);

45
test/stage2/riscv64.zig Normal file
View File

@ -0,0 +1,45 @@
const std = @import("std");
const TestContext = @import("../../src/test.zig").TestContext;
const linux_riscv64 = std.zig.CrossTarget{
.cpu_arch = .riscv64,
.os_tag = .linux,
};
pub fn addCases(ctx: *TestContext) !void {
{
var case = ctx.exe("riscv64 hello world", linux_riscv64);
// Regular old hello world
case.addCompareOutput(
\\export fn _start() noreturn {
\\ print();
\\
\\ exit();
\\}
\\
\\fn print() void {
\\ asm volatile ("ecall"
\\ :
\\ : [number] "{a7}" (64),
\\ [arg1] "{a0}" (1),
\\ [arg2] "{a1}" (@ptrToInt("Hello, World!\n")),
\\ [arg3] "{a2}" ("Hello, World!\n".len)
\\ : "rcx", "r11", "memory"
\\ );
\\ return;
\\}
\\
\\fn exit() noreturn {
\\ asm volatile ("ecall"
\\ :
\\ : [number] "{a7}" (94),
\\ [arg1] "{a0}" (0)
\\ : "rcx", "r11", "memory"
\\ );
\\ unreachable;
\\}
,
"Hello, World!\n",
);
}
}

View File

@ -11,11 +11,6 @@ const linux_x64 = std.zig.CrossTarget{
.os_tag = .linux,
};
const linux_riscv64 = std.zig.CrossTarget{
.cpu_arch = .riscv64,
.os_tag = .linux,
};
pub fn addCases(ctx: *TestContext) !void {
try @import("cbe.zig").addCases(ctx);
try @import("spu-ii.zig").addCases(ctx);
@ -24,6 +19,7 @@ pub fn addCases(ctx: *TestContext) !void {
try @import("llvm.zig").addCases(ctx);
try @import("wasm.zig").addCases(ctx);
try @import("darwin.zig").addCases(ctx);
try @import("riscv64.zig").addCases(ctx);
{
var case = ctx.exe("hello world with updates", linux_x64);
@ -137,42 +133,6 @@ pub fn addCases(ctx: *TestContext) !void {
);
}
{
var case = ctx.exe("riscv64 hello world", linux_riscv64);
// Regular old hello world
case.addCompareOutput(
\\export fn _start() noreturn {
\\ print();
\\
\\ exit();
\\}
\\
\\fn print() void {
\\ asm volatile ("ecall"
\\ :
\\ : [number] "{a7}" (64),
\\ [arg1] "{a0}" (1),
\\ [arg2] "{a1}" (@ptrToInt("Hello, World!\n")),
\\ [arg3] "{a2}" ("Hello, World!\n".len)
\\ : "rcx", "r11", "memory"
\\ );
\\ return;
\\}
\\
\\fn exit() noreturn {
\\ asm volatile ("ecall"
\\ :
\\ : [number] "{a7}" (94),
\\ [arg1] "{a0}" (0)
\\ : "rcx", "r11", "memory"
\\ );
\\ unreachable;
\\}
,
"Hello, World!\n",
);
}
{
var case = ctx.exe("adding numbers at comptime", linux_x64);
case.addCompareOutput(

View File

@ -1363,7 +1363,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
, &[_][]const u8{
\\pub export fn ptrcast() [*c]f32 {
\\ var a: [*c]c_int = undefined;
\\ return @ptrCast([*c]f32, @alignCast(@alignOf(f32), a));
\\ return @ptrCast([*c]f32, @alignCast(@import("std").meta.alignment(f32), a));
\\}
});
@ -1387,16 +1387,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\pub export fn test_ptr_cast() void {
\\ var p: ?*c_void = undefined;
\\ {
\\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@alignOf(u8), p));
\\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@alignOf(c_short), p));
\\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@alignOf(c_int), p));
\\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@alignOf(c_longlong), p));
\\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@import("std").meta.alignment(u8), p));
\\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@import("std").meta.alignment(c_short), p));
\\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@import("std").meta.alignment(c_int), p));
\\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@import("std").meta.alignment(c_longlong), p));
\\ }
\\ {
\\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@alignOf(u8), p));
\\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@alignOf(c_short), p));
\\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@alignOf(c_int), p));
\\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@alignOf(c_longlong), p));
\\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@import("std").meta.alignment(u8), p));
\\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@import("std").meta.alignment(c_short), p));
\\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@import("std").meta.alignment(c_int), p));
\\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@import("std").meta.alignment(c_longlong), p));
\\ }
\\}
});
@ -3028,7 +3028,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\void call() {
\\ fn_int(3.0f);
\\ fn_int(3.0);
\\ fn_int(3.0L);
\\ fn_int('ABCD');
\\ fn_f32(3);
\\ fn_f64(3);
@ -3053,7 +3052,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\pub export fn call() void {
\\ fn_int(@floatToInt(c_int, 3.0));
\\ fn_int(@floatToInt(c_int, 3.0));
\\ fn_int(@floatToInt(c_int, 3.0));
\\ fn_int(@as(c_int, 1094861636));
\\ fn_f32(@intToFloat(f32, @as(c_int, 3)));
\\ fn_f64(@intToFloat(f64, @as(c_int, 3)));