Merge remote-tracking branch 'origin/master' into llvm16

This commit is contained in:
Andrew Kelley 2023-02-18 09:33:27 -07:00
commit efdc94c107
119 changed files with 4631 additions and 1911 deletions

View File

@ -216,6 +216,9 @@ set(ZIG_STAGE2_SOURCES
"${CMAKE_SOURCE_DIR}/lib/std/atomic/stack.zig"
"${CMAKE_SOURCE_DIR}/lib/std/base64.zig"
"${CMAKE_SOURCE_DIR}/lib/std/buf_map.zig"
"${CMAKE_SOURCE_DIR}/lib/std/Build.zig"
"${CMAKE_SOURCE_DIR}/lib/std/Build/Cache.zig"
"${CMAKE_SOURCE_DIR}/lib/std/Build/Cache/DepTokenizer.zig"
"${CMAKE_SOURCE_DIR}/lib/std/builtin.zig"
"${CMAKE_SOURCE_DIR}/lib/std/c.zig"
"${CMAKE_SOURCE_DIR}/lib/std/c/linux.zig"
@ -523,9 +526,7 @@ set(ZIG_STAGE2_SOURCES
"${CMAKE_SOURCE_DIR}/lib/std/zig/tokenizer.zig"
"${CMAKE_SOURCE_DIR}/src/Air.zig"
"${CMAKE_SOURCE_DIR}/src/AstGen.zig"
"${CMAKE_SOURCE_DIR}/src/Cache.zig"
"${CMAKE_SOURCE_DIR}/src/Compilation.zig"
"${CMAKE_SOURCE_DIR}/src/DepTokenizer.zig"
"${CMAKE_SOURCE_DIR}/src/Liveness.zig"
"${CMAKE_SOURCE_DIR}/src/Module.zig"
"${CMAKE_SOURCE_DIR}/src/Package.zig"

View File

@ -40,15 +40,11 @@ pub fn build(b: *std.Build) !void {
});
docgen_exe.single_threaded = single_threaded;
const rel_zig_exe = try fs.path.relative(b.allocator, b.build_root, b.zig_exe);
const langref_out_path = fs.path.join(
b.allocator,
&[_][]const u8{ b.cache_root, "langref.html" },
) catch unreachable;
const langref_out_path = try b.cache_root.join(b.allocator, &.{"langref.html"});
const docgen_cmd = docgen_exe.run();
docgen_cmd.addArgs(&[_][]const u8{
"--zig",
rel_zig_exe,
b.zig_exe,
"doc" ++ fs.path.sep_str ++ "langref.html.in",
langref_out_path,
});
@ -133,6 +129,8 @@ pub fn build(b: *std.Build) !void {
"compress-gettysburg.txt",
"compress-pi.txt",
"rfc1951.txt",
// exclude files from lib/std/compress/lzma/testdata
".lzma",
// exclude files from lib/std/compress/xz/testdata
".xz",
// exclude files from lib/std/tz/
@ -219,7 +217,7 @@ pub fn build(b: *std.Build) !void {
var code: u8 = undefined;
const git_describe_untrimmed = b.execAllowFail(&[_][]const u8{
"git", "-C", b.build_root, "describe", "--match", "*.*.*", "--tags",
"git", "-C", b.build_root.path orelse ".", "describe", "--match", "*.*.*", "--tags",
}, &code, .Ignore) catch {
break :v version_string;
};

View File

@ -5393,7 +5393,6 @@ pub fn parseU64(buf: []const u8, radix: u8) !u64 {
// x *= radix
var ov = @mulWithOverflow(x, radix);
if (ov[1] != 0) return error.OverFlow;
// x += digit
ov = @addWithOverflow(ov[0], digit);
@ -6067,7 +6066,7 @@ struct Foo *do_a_thing(void) {
<p>Zig code</p>
{#syntax_block|zig|call_malloc_from_zig.zig#}
// malloc prototype included for reference
extern fn malloc(size: size_t) ?*u8;
extern fn malloc(size: usize) ?*u8;
fn doAThing() ?*Foo {
const ptr = malloc(1234) orelse return null;
@ -7479,64 +7478,66 @@ pub fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) usize {
<p>
Dissecting the syntax:
</p>
{#syntax_block|zig|Assembly Syntax Explained#}
// Inline assembly is an expression which returns a value.
// the `asm` keyword begins the expression.
_ = asm
// `volatile` is an optional modifier that tells Zig this
// inline assembly expression has side-effects. Without
// `volatile`, Zig is allowed to delete the inline assembly
// code if the result is unused.
volatile (
// Next is a comptime string which is the assembly code.
// Inside this string one may use `%[ret]`, `%[number]`,
// or `%[arg1]` where a register is expected, to specify
// the register that Zig uses for the argument or return value,
// if the register constraint strings are used. However in
// the below code, this is not used. A literal `%` can be
// obtained by escaping it with a double percent: `%%`.
// Often multiline string syntax comes in handy here.
{#code_begin|syntax|Assembly Syntax Explained#}
pub fn syscall1(number: usize, arg1: usize) usize {
// Inline assembly is an expression which returns a value.
// the `asm` keyword begins the expression.
return asm
// `volatile` is an optional modifier that tells Zig this
// inline assembly expression has side-effects. Without
// `volatile`, Zig is allowed to delete the inline assembly
// code if the result is unused.
volatile (
// Next is a comptime string which is the assembly code.
// Inside this string one may use `%[ret]`, `%[number]`,
// or `%[arg1]` where a register is expected, to specify
// the register that Zig uses for the argument or return value,
// if the register constraint strings are used. However in
// the below code, this is not used. A literal `%` can be
// obtained by escaping it with a double percent: `%%`.
// Often multiline string syntax comes in handy here.
\\syscall
// Next is the output. It is possible in the future Zig will
// support multiple outputs, depending on how
// https://github.com/ziglang/zig/issues/215 is resolved.
// It is allowed for there to be no outputs, in which case
// this colon would be directly followed by the colon for the inputs.
:
// This specifies the name to be used in `%[ret]` syntax in
// the above assembly string. This example does not use it,
// but the syntax is mandatory.
[ret]
// Next is the output constraint string. This feature is still
// considered unstable in Zig, and so LLVM/GCC documentation
// must be used to understand the semantics.
// http://releases.llvm.org/10.0.0/docs/LangRef.html#inline-asm-constraint-string
// https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html
// In this example, the constraint string means "the result value of
// this inline assembly instruction is whatever is in $rax".
"={rax}"
// Next is either a value binding, or `->` and then a type. The
// type is the result type of the inline assembly expression.
// If it is a value binding, then `%[ret]` syntax would be used
// to refer to the register bound to the value.
(-> usize)
// Next is the list of inputs.
// The constraint for these inputs means, "when the assembly code is
// executed, $rax shall have the value of `number` and $rdi shall have
// the value of `arg1`". Any number of input parameters is allowed,
// including none.
: [number] "{rax}" (number),
[arg1] "{rdi}" (arg1)
// Next is the list of clobbers. These declare a set of registers whose
// values will not be preserved by the execution of this assembly code.
// These do not include output or input registers. The special clobber
// value of "memory" means that the assembly writes to arbitrary undeclared
// memory locations - not only the memory pointed to by a declared indirect
// output. In this example we list $rcx and $r11 because it is known the
// kernel syscall does not preserve these registers.
: "rcx", "r11"
);
{#end_syntax_block#}
// Next is the output. It is possible in the future Zig will
// support multiple outputs, depending on how
// https://github.com/ziglang/zig/issues/215 is resolved.
// It is allowed for there to be no outputs, in which case
// this colon would be directly followed by the colon for the inputs.
:
// This specifies the name to be used in `%[ret]` syntax in
// the above assembly string. This example does not use it,
// but the syntax is mandatory.
[ret]
// Next is the output constraint string. This feature is still
// considered unstable in Zig, and so LLVM/GCC documentation
// must be used to understand the semantics.
// http://releases.llvm.org/10.0.0/docs/LangRef.html#inline-asm-constraint-string
// https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html
// In this example, the constraint string means "the result value of
// this inline assembly instruction is whatever is in $rax".
"={rax}"
// Next is either a value binding, or `->` and then a type. The
// type is the result type of the inline assembly expression.
// If it is a value binding, then `%[ret]` syntax would be used
// to refer to the register bound to the value.
(-> usize)
// Next is the list of inputs.
// The constraint for these inputs means, "when the assembly code is
// executed, $rax shall have the value of `number` and $rdi shall have
// the value of `arg1`". Any number of input parameters is allowed,
// including none.
: [number] "{rax}" (number),
[arg1] "{rdi}" (arg1)
// Next is the list of clobbers. These declare a set of registers whose
// values will not be preserved by the execution of this assembly code.
// These do not include output or input registers. The special clobber
// value of "memory" means that the assembly writes to arbitrary undeclared
// memory locations - not only the memory pointed to by a declared indirect
// output. In this example we list $rcx and $r11 because it is known the
// kernel syscall does not preserve these registers.
: "rcx", "r11"
);
}
{#code_end#}
<p>
For x86 and x86_64 targets, the syntax is AT&amp;T syntax, rather than the more
popular Intel syntax. This is due to technical constraints; assembly parsing is
@ -7892,7 +7893,7 @@ fn add(a: i32, b: i32) i32 {
{#syntax#}@call{#endsyntax#} allows more flexibility than normal function call syntax does. The
{#syntax#}CallModifier{#endsyntax#} enum is reproduced here:
</p>
{#syntax_block|zig|builtin.CallModifier struct#}
{#code_begin|syntax|builtin.CallModifier struct#}
pub const CallModifier = enum {
/// Equivalent to function call syntax.
auto,
@ -7926,7 +7927,7 @@ pub const CallModifier = enum {
/// compile-time, a compile error is emitted instead.
compile_time,
};
{#end_syntax_block#}
{#code_end#}
{#header_close#}
{#header_open|@cDefine#}
@ -8128,6 +8129,13 @@ test "main" {
{#code_end#}
{#header_close#}
{#header_open|@constCast#}
<pre>{#syntax#}@constCast(value: anytype) DestType{#endsyntax#}</pre>
<p>
Remove {#syntax#}const{#endsyntax#} qualifier from a pointer.
</p>
{#header_close#}
{#header_open|@ctz#}
<pre>{#syntax#}@ctz(operand: anytype){#endsyntax#}</pre>
<p>{#syntax#}@TypeOf(operand){#endsyntax#} must be an integer type or an integer vector type.</p>
@ -8813,7 +8821,8 @@ pub const PrefetchOptions = struct {
{#syntax#}@ptrCast{#endsyntax#} cannot be used for:
</p>
<ul>
<li>Removing {#syntax#}const{#endsyntax#} or {#syntax#}volatile{#endsyntax#} qualifier, use {#link|@qualCast#}.</li>
<li>Removing {#syntax#}const{#endsyntax#} qualifier, use {#link|@constCast#}.</li>
<li>Removing {#syntax#}volatile{#endsyntax#} qualifier, use {#link|@volatileCast#}.</li>
<li>Changing pointer address space, use {#link|@addrSpaceCast#}.</li>
<li>Increasing pointer alignment, use {#link|@alignCast#}.</li>
<li>Casting a non-slice pointer to a slice, use slicing syntax {#syntax#}ptr[start..end]{#endsyntax#}.</li>
@ -8830,13 +8839,6 @@ pub const PrefetchOptions = struct {
{#header_close#}
{#header_open|@qualCast#}
<pre>{#syntax#}@qualCast(comptime DestType: type, value: anytype) DestType{#endsyntax#}</pre>
<p>
Remove {#syntax#}const{#endsyntax#} or {#syntax#}volatile{#endsyntax#} qualifier from a pointer.
</p>
{#header_close#}
{#header_open|@rem#}
<pre>{#syntax#}@rem(numerator: T, denominator: T) T{#endsyntax#}</pre>
<p>
@ -9524,6 +9526,13 @@ fn foo(comptime T: type, ptr: *T) T {
<pre>{#syntax#}@Vector(len: comptime_int, Element: type) type{#endsyntax#}</pre>
<p>Creates {#link|Vectors#}.</p>
{#header_close#}
{#header_open|@volatileCast#}
<pre>{#syntax#}@volatileCast(value: anytype) DestType{#endsyntax#}</pre>
<p>
Remove {#syntax#}volatile{#endsyntax#} qualifier from a pointer.
</p>
{#header_close#}
{#header_close#}
{#header_open|Build Mode#}
@ -9604,7 +9613,7 @@ pub fn build(b: *std.Build) void {
{#header_close#}
{#header_open|Single Threaded Builds#}
<p>Zig has a compile option <kbd>--single-threaded</kbd> which has the following effects:</p>
<p>Zig has a compile option <kbd>-fsingle-threaded</kbd> which has the following effects:</p>
<ul>
<li>All {#link|Thread Local Variables#} are treated as regular {#link|Container Level Variables#}.</li>
<li>The overhead of {#link|Async Functions#} becomes equivalent to function call overhead.</li>

View File

@ -43,13 +43,40 @@ pub fn main() !void {
const host = try std.zig.system.NativeTargetInfo.detect(.{});
const build_root_directory: std.Build.Cache.Directory = .{
.path = build_root,
.handle = try std.fs.cwd().openDir(build_root, .{}),
};
const local_cache_directory: std.Build.Cache.Directory = .{
.path = cache_root,
.handle = try std.fs.cwd().makeOpenPath(cache_root, .{}),
};
const global_cache_directory: std.Build.Cache.Directory = .{
.path = global_cache_root,
.handle = try std.fs.cwd().makeOpenPath(global_cache_root, .{}),
};
var cache: std.Build.Cache = .{
.gpa = allocator,
.manifest_dir = try local_cache_directory.handle.makeOpenPath("h", .{}),
};
cache.addPrefix(.{ .path = null, .handle = std.fs.cwd() });
cache.addPrefix(build_root_directory);
cache.addPrefix(local_cache_directory);
cache.addPrefix(global_cache_directory);
//cache.hash.addBytes(builtin.zig_version);
const builder = try std.Build.create(
allocator,
zig_exe,
build_root,
cache_root,
global_cache_root,
build_root_directory,
local_cache_directory,
global_cache_directory,
host,
&cache,
);
defer builder.destroy();
@ -93,6 +120,8 @@ pub fn main() !void {
std.debug.print("Expected argument after {s}\n\n", .{arg});
return usageAndErr(builder, false, stderr_stream);
};
} else if (mem.eql(u8, arg, "-l") or mem.eql(u8, arg, "--list-steps")) {
return steps(builder, false, stdout_stream);
} else if (mem.eql(u8, arg, "--prefix-lib-dir")) {
dir_list.lib_dir = nextArg(args, &arg_idx) orelse {
std.debug.print("Expected argument after {s}\n\n", .{arg});
@ -136,7 +165,7 @@ pub fn main() !void {
return usageAndErr(builder, false, stderr_stream);
};
} else if (mem.eql(u8, arg, "--zig-lib-dir")) {
builder.override_lib_dir = nextArg(args, &arg_idx) orelse {
builder.zig_lib_dir = nextArg(args, &arg_idx) orelse {
std.debug.print("Expected argument after --zig-lib-dir\n\n", .{});
return usageAndErr(builder, false, stderr_stream);
};
@ -232,20 +261,13 @@ pub fn main() !void {
};
}
fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !void {
fn steps(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !void {
// run the build script to collect the options
if (!already_ran_build) {
builder.resolveInstallPrefix(null, .{});
try builder.runBuild(root);
}
try out_stream.print(
\\Usage: {s} build [steps] [options]
\\
\\Steps:
\\
, .{builder.zig_exe});
const allocator = builder.allocator;
for (builder.top_level_steps.items) |top_level_step| {
const name = if (&top_level_step.step == builder.default_step)
@ -254,6 +276,23 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi
top_level_step.step.name;
try out_stream.print(" {s:<28} {s}\n", .{ name, top_level_step.description });
}
}
fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !void {
// run the build script to collect the options
if (!already_ran_build) {
builder.resolveInstallPrefix(null, .{});
try builder.runBuild(root);
}
try out_stream.print(
\\
\\Usage: {s} build [steps] [options]
\\
\\Steps:
\\
, .{builder.zig_exe});
try steps(builder, true, out_stream);
try out_stream.writeAll(
\\
@ -284,6 +323,7 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi
\\ Windows programs on Linux hosts. (default: no)
\\
\\ -h, --help Print this help and exit
\\ -l, --list-steps Print available steps
\\ --verbose Print commands before executing them
\\ --color [auto|off|on] Enable or disable colored error messages
\\ --prominent-compile-errors Output compile errors formatted for a human to read
@ -292,6 +332,7 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi
\\
);
const allocator = builder.allocator;
if (builder.available_options_list.items.len == 0) {
try out_stream.print(" (none)\n", .{});
} else {

View File

@ -23,6 +23,7 @@ comptime {
_ = @import("compiler_rt/multf3.zig");
_ = @import("compiler_rt/mulxf3.zig");
_ = @import("compiler_rt/powiXf2.zig");
_ = @import("compiler_rt/mulc3.zig");
_ = @import("compiler_rt/mulhc3.zig");
_ = @import("compiler_rt/mulsc3.zig");

View File

@ -77,15 +77,15 @@ Integer and Float Operations
| ✓ | __ucmpdi2 | u64 | u64 | i32 | .. |
| ✓ | __ucmpti2 | u128 | u128 | i32 | .. |
| | | | | | **Integer Arithmetic** |
| | __ashlsi3 | i32 | i32 | i32 | `a << b` [^unused_rl78] |
| | __ashlsi3 | i32 | i32 | i32 | `a << b` [^unused_rl78] |
| ✓ | __ashldi3 | i64 | i32 | i64 | .. |
| ✓ | __ashlti3 | i128 | i32 | i128 | .. |
| ✓ | __aeabi_llsl | i32 | i32 | i32 | .. ARM |
| | __ashrsi3 | i32 | i32 | i32 | `a >> b` arithmetic (sign fill) [^unused_rl78] |
| | __ashrsi3 | i32 | i32 | i32 | `a >> b` arithmetic (sign fill) [^unused_rl78] |
| ✓ | __ashrdi3 | i64 | i32 | i64 | .. |
| ✓ | __ashrti3 | i128 | i32 | i128 | .. |
| ✓ | __aeabi_lasr | i64 | i32 | i64 | .. ARM |
| | __lshrsi3 | i32 | i32 | i32 | `a >> b` logical (zero fill) [^unused_rl78] |
| | __lshrsi3 | i32 | i32 | i32 | `a >> b` logical (zero fill) [^unused_rl78] |
| ✓ | __lshrdi3 | i64 | i32 | i64 | .. |
| ✓ | __lshrti3 | i128 | i32 | i128 | .. |
| ✓ | __aeabi_llsr | i64 | i32 | i64 | .. ARM |
@ -114,7 +114,7 @@ Integer and Float Operations
| ✓ | __udivmodti4 | u128 | u128 | u128 | .. |
| ✓ | __divmodsi4 | i32 | i32 | i32 | `a / b, rem.* = a % b` |
| ✓ | __divmoddi4 | i64 | i64 | i64 | .. |
| | __divmodti4 | i128 | i128 | i128 | .. [^libgcc_compat] |
| | __divmodti4 | i128 | i128 | i128 | .. [^libgcc_compat] |
| | | | | | **Integer Arithmetic with Trapping Overflow**|
| ✓ | __absvsi2 | i32 | i32 | i32 | abs(a) |
| ✓ | __absvdi2 | i64 | i64 | i64 | .. |
@ -328,11 +328,11 @@ Integer and Float Operations
| ✓ | __negtf2 | f128 | ∅ | f128 | .. |
| ✓ | __negxf2 | f80 | ∅ | f80 | .. |
| | | | | | **Floating point raised to integer power** |
| ✗ | __powihf2 | f16 | f16 | f16 | `a ^ b` |
| ✗ | __powisf2 | f32 | f32 | f32 | .. |
| ✗ | __powidf2 | f64 | f64 | f64 | .. |
| ✗ | __powitf2 | f128 | f128 | f128 | .. |
| ✗ | __powixf2 | f80 | f80 | f80 | .. |
| ✓ | __powihf2 | f16 | i32 | f16 | `a ^ b` |
| ✓ | __powisf2 | f32 | i32 | f32 | .. |
| ✓ | __powidf2 | f64 | i32 | f64 | .. |
| ✓ | __powitf2 | f128 | i32 | f128 | .. |
| ✓ | __powixf2 | f80 | i32 | f80 | .. |
| ✓ | __mulhc3 | all4 | f16 | f16 | `(a+ib) * (c+id)` |
| ✓ | __mulsc3 | all4 | f32 | f32 | .. |
| ✓ | __muldc3 | all4 | f64 | f64 | .. |
@ -552,20 +552,156 @@ TODO brief explanation + implementation
| ------ | ------------- | --------- | --------- | --------- | -------------------------- |
| | | | | | **Fixed-Point Fractional** |
Further content:
Math functions according to C99 with gnu extension sincos. f16, f80 and f128 functions
are additionally supported by Zig, but not part of C standard. Alphabetically sorted.
| Done | Name | a | b | Out | Comment |
| ---- | ------- | --------- | --------- | --------- | -------------------------- |
| ✓ | __ceilh | f16 | ∅ | f16 |smallest integer value not less than a|
| ✓ | ceilf | f32 | ∅ | f32 |If a is integer, +-0, +-NaN, or +-infinite, a itself is returned.|
| ✓ | ceil | f64 | ∅ | f64 | .. |
| ✓ | __ceilx | f80 | ∅ | f80 | |
| ✓ | ceilf128 | f128 | ∅ | f128 | .. PPC |
| ✓ | ceilq | f128 | ∅ | f128 | .. |
| ✓ | ceill |long double| ∅ |long double| .. |
| ✓ | __cosh | f16 | ∅ | f16 | `cos(a)=(e^(ia)+e^(-ia))/2`|
| ✓ | cosf | f32 | ∅ | f32 | .. |
| ✓ | cos | f64 | ∅ | f64 | .. |
| ✓ | __cosx | f80 | ∅ | f80 | .. |
| ✓ | cosf128 | f128 | ∅ | f128 | .. |
| ✓ | cosq | f128 | ∅ | f128 | .. PPC |
| ✓ | cosl |long double| ∅ |long double| .. |
| ✓ | __exph | f16 | ∅ | f16 | `e^a` with e base of natural logarithms|
| ✓ | expf | f32 | ∅ | f32 | .. |
| ✓ | exp | f64 | ∅ | f64 | .. |
| ✓ | __expx | f80 | ∅ | f80 | .. |
| ✓ | expf128 | f128 | ∅ | f128 | .. |
| ✓ | expq | f128 | ∅ | f128 | .. PPC |
| ✓ | expl |long double| ∅ |long double| .. |
| ✓ | __exp2h | f16 | ∅ | f16 | `2^a` |
| ✓ | exp2f | f32 | ∅ | f32 | .. |
| ✓ | exp2 | f64 | ∅ | f64 | .. |
| ✓ | __exp2x | f80 | ∅ | f80 | .. |
| ✓ | exp2f128 | f128 | ∅ | f128 | .. |
| ✓ | exp2q | f128 | ∅ | f128 | .. PPC |
| ✓ | exp2l |long double| ∅ |long double| .. |
| ✓ | __fabsh | f16 | ∅ | f16 | absolute value of a |
| ✓ | fabsf | f32 | ∅ | f32 | .. |
| ✓ | fabs | f64 | ∅ | f64 | .. |
| ✓ | __fabsx | f80 | ∅ | f80 | .. |
| ✓ | fabsf128 | f128 | ∅ | f128 | .. |
| ✓ | fabsq | f128 | ∅ | f128 | .. PPC |
| ✓ | fabsl |long double| ∅ |long double| .. |
| ✓ | __floorh | f16 | ∅ | f16 |largest integer value not greater than a|
| ✓ | floorf | f32 | ∅ | f32 |If a is integer, +-0, +-NaN, or +-infinite, a itself is returned.|
| ✓ | floor | f64 | ∅ | f64 | .. |
| ✓ | __floorx | f80 | ∅ | f80 | .. |
| ✓ | floorf128 | f128 | ∅ | f128 | .. |
| ✓ | floorq | f128 | ∅ | f128 | .. PPC |
| ✓ | floorl |long double| ∅ |long double| .. |
| ✓ | __fmah | f16 | 2xf16 | f16 | args a,b,c result `(a*b)+c`|
| ✓ | fmaf | f32 | 2xf32 | f32 |Fused multiply-add for hardware acceleration|
| ✓ | fma | f64 | 2xf64 | f64 | .. |
| ✓ | __fmax | f80 | 2xf80 | f80 | .. |
| ✓ | fmaf128 | f128 | 2xf128 | f128 | .. |
| ✓ | fmaq | f128 | 2xf128 | f128 | .. PPC |
| ✓ | fmal |long double|2xlong double|long double| .. |
| ✓ | __fmaxh | f16 | f16 | f16 | larger value of a,b |
| ✓ | fmaxf | f32 | f32 | f32 | .. |
| ✓ | fmax | f64 | f64 | f64 | .. |
| ✓ | __fmaxx | f80 | f80 | f80 | .. |
| ✓ | fmaxf128 | f128 | f128 | f128 | .. |
| ✓ | fmaxq | f128 | f128 | f128 | .. PPC |
| ✓ | fmaxl |long double|long double|long double| .. |
| ✓ | __fminh | f16 | f16 | f16 | smaller value of a,b |
| ✓ | fminf | f32 | f32 | f32 | .. |
| ✓ | fmin | f64 | f64 | f64 | .. |
| ✓ | __fminx | f80 | f80 | f80 | .. |
| ✓ | fminf128 | f128 | f128 | f128 | .. |
| ✓ | fminq | f128 | f128 | f128 | .. PPC |
| ✓ | fminl |long double|long double|long double| .. |
| ✓ | __fmodh | f16 | f16 | f16 |floating-point remainder of division a/b|
| ✓ | fmodf | f32 | f32 | f32 | .. |
| ✓ | fmod | f64 | f64 | f64 | .. |
| ✓ | __fmodx | f80 | f80 | f80 | .. |
| ✓ | fmodf128 | f128 | f128 | f128 | .. |
| ✓ | fmodq | f128 | f128 | f128 | .. PPC |
| ✓ | fmodl |long double|long double|long double| .. |
| ✓ | __logh | f16 | ∅ | f16 |natural (base-e) logarithm of a|
| ✓ | logf | f32 | ∅ | f32 | .. |
| ✓ | log | f64 | ∅ | f64 | .. |
| ✓ | __logx | f80 | ∅ | f80 | .. |
| ✓ | logf128 | f128 | ∅ | f128 | .. |
| ✓ | logq | f128 | ∅ | f128 | .. PPC |
| ✓ | logl |long double| ∅ |long double| .. |
| ✓ | __log10h | f16 | ∅ | f16 |common (base-10) logarithm of a|
| ✓ | log10f | f32 | ∅ | f32 | .. |
| ✓ | log10 | f64 | ∅ | f64 | .. |
| ✓ | __log10x | f80 | ∅ | f80 | .. |
| ✓ | log10f128 | f128 | ∅ | f128 | .. |
| ✓ | log10q | f128 | ∅ | f128 | .. PPC |
| ✓ | log10l |long double| ∅ |long double| .. |
| ✓ | __log2h | f16 | ∅ | f16 | base-2 logarithm of a |
| ✓ | log2f | f32 | ∅ | f32 | .. |
| ✓ | log2 | f64 | ∅ | f64 | .. |
| ✓ | __log2x | f80 | ∅ | f80 | .. |
| ✓ | log2f128 | f128 | ∅ | f128 | .. |
| ✓ | log2q | f128 | ∅ | f128 | .. PPC |
| ✓ | log2l |long double| ∅ |long double| .. |
| ✓ | __roundh | f16 | ∅ | f16 | a rounded to next int away from zero|
| ✓ | roundf | f32 | ∅ | f32 | .. |
| ✓ | round | f64 | ∅ | f64 | .. |
| ✓ | __roundx | f80 | ∅ | f80 | .. |
| ✓ | roundf128 | f128 | ∅ | f128 | .. |
| ✓ | roundq | f128 | ∅ | f128 | .. PPC |
| ✓ | roundl |long double| ∅ |long double| .. |
| ✓ | __sinh | f16 | ∅ | f16 | `sin(a)=(e^(ia)-e^(-ia))/2`|
| ✓ | sinf | f32 | ∅ | f32 | .. |
| ✓ | sin | f64 | ∅ | f64 | .. |
| ✓ | __sinx | f80 | ∅ | f80 | .. |
| ✓ | sinf128 | f128 | ∅ | f128 | .. |
| ✓ | sinq | f128 | ∅ | f128 | .. PPC |
| ✓ | sinl |long double| ∅ |long double| .. |
| ✓ | __sincosh | f16 | 2x *f16 | ∅ |sin and cos of the same angle a|
| ✓ | sincosf | f32 | 2x *f32 | ∅ |args a,*b,*c, `b.*=sin(x),c.*=cos(x)`|
| ✓ | sincos | f64 | 2x *f64 | ∅ | .. |
| ✓ | __sincosx | f80 | 2x *f80 | ∅ | .. |
| ✓ | sincosf128 | f128 | 2x *f128 | ∅ | .. |
| ✓ | sincosq | f128 | 2x *f128 | ∅ | .. PPC |
| ✓ | sincosl |long double| 2x *long double|∅ | .. |
| ✓ | __sqrth | f16 | ∅ | f16 | square root of a (find `r st. a=r^2`)|
| ✓ | sqrtf | f32 | ∅ | f32 | .. |
| ✓ | sqrt | f64 | ∅ | f64 | .. |
| ✓ | __sqrtx | f80 | ∅ | f80 | .. |
| ✓ | sqrtf128 | f128 | ∅ | f128 | .. |
| ✓ | sqrtq | f128 | ∅ | f128 | .. PPC |
| ✓ | sqrtl |long double| ∅ |long double| .. |
| ✓ | __tanh | f16 | ∅ | f16 | `tan(x)=sin(x)/cos(x) |
| ✓ | tanf | f32 | ∅ | f32 | .. |
| ✓ | tan | f64 | ∅ | f64 | .. |
| ✓ | __tanx | f80 | ∅ | f80 | .. |
| ✓ | tanf128 | f128 | ∅ | f128 | .. |
| ✓ | tanq | f128 | ∅ | f128 | .. PPC |
| ✓ | tanl |long double| ∅ |long double| .. |
| ✓ | __trunch | f16 | ∅ | f16 | a rounded to next int towards zero|
| ✓ | truncf | f32 | ∅ | f32 | .. |
| ✓ | trunc | f64 | ∅ | f64 | .. |
| ✓ | __truncx | f80 | ∅ | f80 | .. |
| ✓ | truncf128 | f128 | ∅ | f128 | .. |
| ✓ | truncq | f128 | ∅ | f128 | .. PPC |
| ✓ | truncl |long double| ∅ |long double| .. |
Further content (conditionally) exported with C abi:
- aarch64 outline atomics
- atomics
- msvc things like _alldiv, _aulldiv, _allrem
- clear cache
- tls emulation
- math routines (cos, sin, tan, ceil, floor, exp, exp2, fabs, log, log10, log2, sincos, sqrt)
- bcmp
- ieee float routines (fma, fmax, fmin, fmod, fabs, float rounding, )
- arm routines (memory routines + memclr [setting to 0], divmod routines and stubs for unwind_cpp)
- atomics
- bcmp
- clear cache
- memory routines (memcmp, memcpy, memset, memmove)
- msvc things like _alldiv, _aulldiv, _allrem
- objective-c __isPlatformVersionAtLeast check
- stack probe routines
- tls emulation
Future work
Arbitrary length integer library routines
Future work:
- Arbitrary length integer library routines

View File

@ -1,32 +0,0 @@
const __ashldi3 = @import("shift.zig").__ashldi3;
const testing = @import("std").testing;
fn test__ashldi3(a: i64, b: i32, expected: u64) !void {
const x = __ashldi3(a, b);
try testing.expectEqual(@bitCast(i64, expected), x);
}
test "ashldi3" {
try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 0, 0x123456789ABCDEF);
try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 1, 0x2468ACF13579BDE);
try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 2, 0x48D159E26AF37BC);
try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 3, 0x91A2B3C4D5E6F78);
try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 4, 0x123456789ABCDEF0);
try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 28, 0x789ABCDEF0000000);
try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 29, 0xF13579BDE0000000);
try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 30, 0xE26AF37BC0000000);
try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 31, 0xC4D5E6F780000000);
try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 32, 0x89ABCDEF00000000);
try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 33, 0x13579BDE00000000);
try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 34, 0x26AF37BC00000000);
try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 35, 0x4D5E6F7800000000);
try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 36, 0x9ABCDEF000000000);
try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 60, 0xF000000000000000);
try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 61, 0xE000000000000000);
try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 62, 0xC000000000000000);
try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 63, 0x8000000000000000);
}

View File

@ -1,46 +0,0 @@
const __ashlti3 = @import("shift.zig").__ashlti3;
const testing = @import("std").testing;
fn test__ashlti3(a: i128, b: i32, expected: i128) !void {
const x = __ashlti3(a, b);
try testing.expectEqual(expected, x);
}
test "ashlti3" {
try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 0, @bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)));
try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 1, @bitCast(i128, @intCast(u128, 0xFDB97530ECA8642BFDB97530ECA8642A)));
try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 2, @bitCast(i128, @intCast(u128, 0xFB72EA61D950C857FB72EA61D950C854)));
try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 3, @bitCast(i128, @intCast(u128, 0xF6E5D4C3B2A190AFF6E5D4C3B2A190A8)));
try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 4, @bitCast(i128, @intCast(u128, 0xEDCBA9876543215FEDCBA98765432150)));
try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 28, @bitCast(i128, @intCast(u128, 0x876543215FEDCBA98765432150000000)));
try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 29, @bitCast(i128, @intCast(u128, 0x0ECA8642BFDB97530ECA8642A0000000)));
try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 30, @bitCast(i128, @intCast(u128, 0x1D950C857FB72EA61D950C8540000000)));
try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 31, @bitCast(i128, @intCast(u128, 0x3B2A190AFF6E5D4C3B2A190A80000000)));
try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 32, @bitCast(i128, @intCast(u128, 0x76543215FEDCBA987654321500000000)));
try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 33, @bitCast(i128, @intCast(u128, 0xECA8642BFDB97530ECA8642A00000000)));
try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 34, @bitCast(i128, @intCast(u128, 0xD950C857FB72EA61D950C85400000000)));
try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 35, @bitCast(i128, @intCast(u128, 0xB2A190AFF6E5D4C3B2A190A800000000)));
try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 36, @bitCast(i128, @intCast(u128, 0x6543215FEDCBA9876543215000000000)));
try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 60, @bitCast(i128, @intCast(u128, 0x5FEDCBA9876543215000000000000000)));
try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 61, @bitCast(i128, @intCast(u128, 0xBFDB97530ECA8642A000000000000000)));
try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 62, @bitCast(i128, @intCast(u128, 0x7FB72EA61D950C854000000000000000)));
try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 63, @bitCast(i128, @intCast(u128, 0xFF6E5D4C3B2A190A8000000000000000)));
try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 64, @bitCast(i128, @intCast(u128, 0xFEDCBA98765432150000000000000000)));
try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 65, @bitCast(i128, @intCast(u128, 0xFDB97530ECA8642A0000000000000000)));
try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 66, @bitCast(i128, @intCast(u128, 0xFB72EA61D950C8540000000000000000)));
try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 67, @bitCast(i128, @intCast(u128, 0xF6E5D4C3B2A190A80000000000000000)));
try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 68, @bitCast(i128, @intCast(u128, 0xEDCBA987654321500000000000000000)));
try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 92, @bitCast(i128, @intCast(u128, 0x87654321500000000000000000000000)));
try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 93, @bitCast(i128, @intCast(u128, 0x0ECA8642A00000000000000000000000)));
try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 94, @bitCast(i128, @intCast(u128, 0x1D950C85400000000000000000000000)));
try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 95, @bitCast(i128, @intCast(u128, 0x3B2A190A800000000000000000000000)));
try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 96, @bitCast(i128, @intCast(u128, 0x76543215000000000000000000000000)));
try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 97, @bitCast(i128, @intCast(u128, 0xECA8642A000000000000000000000000)));
try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 98, @bitCast(i128, @intCast(u128, 0xD950C854000000000000000000000000)));
try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 99, @bitCast(i128, @intCast(u128, 0xB2A190A8000000000000000000000000)));
try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 100, @bitCast(i128, @intCast(u128, 0x65432150000000000000000000000000)));
try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 124, @bitCast(i128, @intCast(u128, 0x50000000000000000000000000000000)));
try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 125, @bitCast(i128, @intCast(u128, 0xA0000000000000000000000000000000)));
try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 126, @bitCast(i128, @intCast(u128, 0x40000000000000000000000000000000)));
try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 127, @bitCast(i128, @intCast(u128, 0x80000000000000000000000000000000)));
}

View File

@ -1,55 +0,0 @@
const __ashrdi3 = @import("shift.zig").__ashrdi3;
const testing = @import("std").testing;
fn test__ashrdi3(a: i64, b: i32, expected: u64) !void {
const x = __ashrdi3(a, b);
try testing.expectEqual(@bitCast(i64, expected), x);
}
test "ashrdi3" {
try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 0, 0x123456789ABCDEF);
try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 1, 0x91A2B3C4D5E6F7);
try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 2, 0x48D159E26AF37B);
try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 3, 0x2468ACF13579BD);
try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 4, 0x123456789ABCDE);
try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 28, 0x12345678);
try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 29, 0x91A2B3C);
try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 30, 0x48D159E);
try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 31, 0x2468ACF);
try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 32, 0x1234567);
try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 33, 0x91A2B3);
try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 34, 0x48D159);
try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 35, 0x2468AC);
try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 36, 0x123456);
try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 60, 0);
try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 61, 0);
try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 62, 0);
try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 63, 0);
try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 0, 0xFEDCBA9876543210);
try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 1, 0xFF6E5D4C3B2A1908);
try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 2, 0xFFB72EA61D950C84);
try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 3, 0xFFDB97530ECA8642);
try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 4, 0xFFEDCBA987654321);
try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 28, 0xFFFFFFFFEDCBA987);
try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 29, 0xFFFFFFFFF6E5D4C3);
try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 30, 0xFFFFFFFFFB72EA61);
try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 31, 0xFFFFFFFFFDB97530);
try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 32, 0xFFFFFFFFFEDCBA98);
try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 33, 0xFFFFFFFFFF6E5D4C);
try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 34, 0xFFFFFFFFFFB72EA6);
try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 35, 0xFFFFFFFFFFDB9753);
try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 36, 0xFFFFFFFFFFEDCBA9);
try test__ashrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 60, 0xFFFFFFFFFFFFFFFA);
try test__ashrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 61, 0xFFFFFFFFFFFFFFFD);
try test__ashrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 62, 0xFFFFFFFFFFFFFFFE);
try test__ashrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 63, 0xFFFFFFFFFFFFFFFF);
}

View File

@ -1,56 +0,0 @@
const __ashrti3 = @import("shift.zig").__ashrti3;
const testing = @import("std").testing;
fn test__ashrti3(a: i128, b: i32, expected: i128) !void {
const x = __ashrti3(a, b);
try testing.expectEqual(expected, x);
}
test "ashrti3" {
try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 0, @bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)));
try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 1, @bitCast(i128, @intCast(u128, 0xFF6E5D4C3B2A190AFF6E5D4C3B2A190A)));
try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 2, @bitCast(i128, @intCast(u128, 0xFFB72EA61D950C857FB72EA61D950C85)));
try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 3, @bitCast(i128, @intCast(u128, 0xFFDB97530ECA8642BFDB97530ECA8642)));
try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 4, @bitCast(i128, @intCast(u128, 0xFFEDCBA9876543215FEDCBA987654321)));
try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 28, @bitCast(i128, @intCast(u128, 0xFFFFFFFFEDCBA9876543215FEDCBA987)));
try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 29, @bitCast(i128, @intCast(u128, 0xFFFFFFFFF6E5D4C3B2A190AFF6E5D4C3)));
try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 30, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFB72EA61D950C857FB72EA61)));
try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 31, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFDB97530ECA8642BFDB97530)));
try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 32, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFEDCBA9876543215FEDCBA98)));
try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 33, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFF6E5D4C3B2A190AFF6E5D4C)));
try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 34, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFB72EA61D950C857FB72EA6)));
try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 35, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFDB97530ECA8642BFDB9753)));
try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 36, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFEDCBA9876543215FEDCBA9)));
try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 60, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFEDCBA9876543215F)));
try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 61, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFF6E5D4C3B2A190AF)));
try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 62, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFB72EA61D950C857)));
try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 63, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFDB97530ECA8642B)));
try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 64, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFEDCBA9876543215)));
try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 65, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFF6E5D4C3B2A190A)));
try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 66, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFB72EA61D950C85)));
try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 67, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFDB97530ECA8642)));
try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 68, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFEDCBA987654321)));
try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 92, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFEDCBA987)));
try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 93, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFF6E5D4C3)));
try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 94, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFB72EA61)));
try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 95, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFDB97530)));
try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 96, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFEDCBA98)));
try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 97, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFF6E5D4C)));
try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 98, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFB72EA6)));
try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 99, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFDB9753)));
try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 100, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFEDCBA9)));
try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 124, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)));
try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 125, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)));
try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 126, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)));
try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 127, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)));
}

View File

@ -9,10 +9,12 @@ const arch = builtin.cpu.arch;
const is_test = builtin.is_test;
const common = @import("common.zig");
const udivmod = @import("udivmod.zig").udivmod;
const __divti3 = @import("divti3.zig").__divti3;
pub const panic = common.panic;
comptime {
@export(__divmodti4, .{ .name = "__divmodti4", .linkage = common.linkage, .visibility = common.visibility });
@export(__udivmoddi4, .{ .name = "__udivmoddi4", .linkage = common.linkage, .visibility = common.visibility });
@export(__mulsi3, .{ .name = "__mulsi3", .linkage = common.linkage, .visibility = common.visibility });
@export(__divmoddi4, .{ .name = "__divmoddi4", .linkage = common.linkage, .visibility = common.visibility });
@ -33,12 +35,72 @@ comptime {
@export(__udivmodsi4, .{ .name = "__udivmodsi4", .linkage = common.linkage, .visibility = common.visibility });
}
pub fn __divmodti4(a: i128, b: i128, rem: *i128) callconv(.C) i128 {
const d = __divti3(a, b);
rem.* = a -% (d * b);
return d;
}
test "test_divmodti4" {
const cases = [_][4]i128{
[_]i128{ 0, 1, 0, 0 },
[_]i128{ 0, -1, 0, 0 },
[_]i128{ 2, 1, 2, 0 },
[_]i128{ 2, -1, -2, 0 },
[_]i128{ -2, 1, -2, 0 },
[_]i128{ -2, -1, 2, 0 },
[_]i128{ 7, 5, 1, 2 },
[_]i128{ -7, 5, -1, -2 },
[_]i128{ 19, 5, 3, 4 },
[_]i128{ 19, -5, -3, 4 },
[_]i128{ @bitCast(i128, @as(u128, 0x80000000000000000000000000000000)), 8, @bitCast(i128, @as(u128, 0xf0000000000000000000000000000000)), 0 },
[_]i128{ @bitCast(i128, @as(u128, 0x80000000000000000000000000000007)), 8, @bitCast(i128, @as(u128, 0xf0000000000000000000000000000001)), -1 },
};
for (cases) |case| {
try test_one_divmodti4(case[0], case[1], case[2], case[3]);
}
}
fn test_one_divmodti4(a: i128, b: i128, expected_q: i128, expected_r: i128) !void {
var r: i128 = undefined;
const q: i128 = __divmodti4(a, b, &r);
try testing.expect(q == expected_q and r == expected_r);
}
pub fn __divmoddi4(a: i64, b: i64, rem: *i64) callconv(.C) i64 {
const d = __divdi3(a, b);
rem.* = a -% (d *% b);
rem.* = a -% (d * b);
return d;
}
test "test_divmoddi4" {
const cases = [_][4]i64{
[_]i64{ 0, 1, 0, 0 },
[_]i64{ 0, -1, 0, 0 },
[_]i64{ 2, 1, 2, 0 },
[_]i64{ 2, -1, -2, 0 },
[_]i64{ -2, 1, -2, 0 },
[_]i64{ -2, -1, 2, 0 },
[_]i64{ 7, 5, 1, 2 },
[_]i64{ -7, 5, -1, -2 },
[_]i64{ 19, 5, 3, 4 },
[_]i64{ 19, -5, -3, 4 },
[_]i64{ @bitCast(i64, @as(u64, 0x8000000000000000)), 8, @bitCast(i64, @as(u64, 0xf000000000000000)), 0 },
[_]i64{ @bitCast(i64, @as(u64, 0x8000000000000007)), 8, @bitCast(i64, @as(u64, 0xf000000000000001)), -1 },
};
for (cases) |case| {
try test_one_divmoddi4(case[0], case[1], case[2], case[3]);
}
}
fn test_one_divmoddi4(a: i64, b: i64, expected_q: i64, expected_r: i64) !void {
var r: i64 = undefined;
const q: i64 = __divmoddi4(a, b, &r);
try testing.expect(q == expected_q and r == expected_r);
}
pub fn __udivmoddi4(a: u64, b: u64, maybe_rem: ?*u64) callconv(.C) u64 {
return udivmod(u64, a, b, maybe_rem);
}
@ -424,7 +486,7 @@ fn test_one_udivsi3(a: u32, b: u32, expected_q: u32) !void {
}
pub fn __modsi3(n: i32, d: i32) callconv(.C) i32 {
return n -% __divsi3(n, d) *% d;
return n -% __divsi3(n, d) * d;
}
test "test_modsi3" {
@ -453,7 +515,7 @@ fn test_one_modsi3(a: i32, b: i32, expected_r: i32) !void {
}
pub fn __umodsi3(n: u32, d: u32) callconv(.C) u32 {
return n -% __udivsi3(n, d) *% d;
return n -% __udivsi3(n, d) * d;
}
test "test_umodsi3" {

View File

@ -1,55 +0,0 @@
const __lshrdi3 = @import("shift.zig").__lshrdi3;
const testing = @import("std").testing;
fn test__lshrdi3(a: i64, b: i32, expected: u64) !void {
const x = __lshrdi3(a, b);
try testing.expectEqual(@bitCast(i64, expected), x);
}
test "lshrdi3" {
try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 0, 0x123456789ABCDEF);
try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 1, 0x91A2B3C4D5E6F7);
try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 2, 0x48D159E26AF37B);
try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 3, 0x2468ACF13579BD);
try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 4, 0x123456789ABCDE);
try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 28, 0x12345678);
try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 29, 0x91A2B3C);
try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 30, 0x48D159E);
try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 31, 0x2468ACF);
try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 32, 0x1234567);
try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 33, 0x91A2B3);
try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 34, 0x48D159);
try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 35, 0x2468AC);
try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 36, 0x123456);
try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 60, 0);
try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 61, 0);
try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 62, 0);
try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 63, 0);
try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 0, 0xFEDCBA9876543210);
try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 1, 0x7F6E5D4C3B2A1908);
try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 2, 0x3FB72EA61D950C84);
try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 3, 0x1FDB97530ECA8642);
try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 4, 0xFEDCBA987654321);
try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 28, 0xFEDCBA987);
try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 29, 0x7F6E5D4C3);
try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 30, 0x3FB72EA61);
try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 31, 0x1FDB97530);
try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 32, 0xFEDCBA98);
try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 33, 0x7F6E5D4C);
try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 34, 0x3FB72EA6);
try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 35, 0x1FDB9753);
try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 36, 0xFEDCBA9);
try test__lshrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 60, 0xA);
try test__lshrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 61, 0x5);
try test__lshrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 62, 0x2);
try test__lshrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 63, 0x1);
}

View File

@ -1,46 +0,0 @@
const __lshrti3 = @import("shift.zig").__lshrti3;
const testing = @import("std").testing;
fn test__lshrti3(a: i128, b: i32, expected: i128) !void {
const x = __lshrti3(a, b);
try testing.expectEqual(expected, x);
}
test "lshrti3" {
try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 0, @bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)));
try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 1, @bitCast(i128, @intCast(u128, 0x7F6E5D4C3B2A190AFF6E5D4C3B2A190A)));
try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 2, @bitCast(i128, @intCast(u128, 0x3FB72EA61D950C857FB72EA61D950C85)));
try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 3, @bitCast(i128, @intCast(u128, 0x1FDB97530ECA8642BFDB97530ECA8642)));
try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 4, @bitCast(i128, @intCast(u128, 0x0FEDCBA9876543215FEDCBA987654321)));
try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 28, @bitCast(i128, @intCast(u128, 0x0000000FEDCBA9876543215FEDCBA987)));
try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 29, @bitCast(i128, @intCast(u128, 0x00000007F6E5D4C3B2A190AFF6E5D4C3)));
try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 30, @bitCast(i128, @intCast(u128, 0x00000003FB72EA61D950C857FB72EA61)));
try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 31, @bitCast(i128, @intCast(u128, 0x00000001FDB97530ECA8642BFDB97530)));
try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 32, @bitCast(i128, @intCast(u128, 0x00000000FEDCBA9876543215FEDCBA98)));
try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 33, @bitCast(i128, @intCast(u128, 0x000000007F6E5D4C3B2A190AFF6E5D4C)));
try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 34, @bitCast(i128, @intCast(u128, 0x000000003FB72EA61D950C857FB72EA6)));
try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 35, @bitCast(i128, @intCast(u128, 0x000000001FDB97530ECA8642BFDB9753)));
try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 36, @bitCast(i128, @intCast(u128, 0x000000000FEDCBA9876543215FEDCBA9)));
try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 60, @bitCast(i128, @intCast(u128, 0x000000000000000FEDCBA9876543215F)));
try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 61, @bitCast(i128, @intCast(u128, 0x0000000000000007F6E5D4C3B2A190AF)));
try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 62, @bitCast(i128, @intCast(u128, 0x0000000000000003FB72EA61D950C857)));
try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 63, @bitCast(i128, @intCast(u128, 0x0000000000000001FDB97530ECA8642B)));
try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 64, @bitCast(i128, @intCast(u128, 0x0000000000000000FEDCBA9876543215)));
try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 65, @bitCast(i128, @intCast(u128, 0x00000000000000007F6E5D4C3B2A190A)));
try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 66, @bitCast(i128, @intCast(u128, 0x00000000000000003FB72EA61D950C85)));
try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 67, @bitCast(i128, @intCast(u128, 0x00000000000000001FDB97530ECA8642)));
try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 68, @bitCast(i128, @intCast(u128, 0x00000000000000000FEDCBA987654321)));
try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 92, @bitCast(i128, @intCast(u128, 0x00000000000000000000000FEDCBA987)));
try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 93, @bitCast(i128, @intCast(u128, 0x000000000000000000000007F6E5D4C3)));
try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 94, @bitCast(i128, @intCast(u128, 0x000000000000000000000003FB72EA61)));
try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 95, @bitCast(i128, @intCast(u128, 0x000000000000000000000001FDB97530)));
try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 96, @bitCast(i128, @intCast(u128, 0x000000000000000000000000FEDCBA98)));
try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 97, @bitCast(i128, @intCast(u128, 0x0000000000000000000000007F6E5D4C)));
try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 98, @bitCast(i128, @intCast(u128, 0x0000000000000000000000003FB72EA6)));
try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 99, @bitCast(i128, @intCast(u128, 0x0000000000000000000000001FDB9753)));
try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 100, @bitCast(i128, @intCast(u128, 0x0000000000000000000000000FEDCBA9)));
try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 124, @bitCast(i128, @intCast(u128, 0x0000000000000000000000000000000F)));
try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 125, @bitCast(i128, @intCast(u128, 0x00000000000000000000000000000007)));
try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 126, @bitCast(i128, @intCast(u128, 0x00000000000000000000000000000003)));
try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 127, @bitCast(i128, @intCast(u128, 0x00000000000000000000000000000001)));
}

View File

@ -0,0 +1,58 @@
//! a raised to integer power of b
//! ported from https://github.com/llvm-mirror/compiler-rt/blob/release_80/lib/builtins/powisf2.c
//! Multiplication order (left-to-right or right-to-left) does not matter for
//! error propagation and this method is optimized for performance, not accuracy.
const builtin = @import("builtin");
const common = @import("common.zig");
const std = @import("std");
pub const panic = common.panic;
comptime {
@export(__powihf2, .{ .name = "__powihf2", .linkage = common.linkage, .visibility = common.visibility });
@export(__powisf2, .{ .name = "__powisf2", .linkage = common.linkage, .visibility = common.visibility });
@export(__powidf2, .{ .name = "__powidf2", .linkage = common.linkage, .visibility = common.visibility });
@export(__powitf2, .{ .name = "__powitf2", .linkage = common.linkage, .visibility = common.visibility });
@export(__powixf2, .{ .name = "__powixf2", .linkage = common.linkage, .visibility = common.visibility });
}
inline fn powiXf2(comptime FT: type, a: FT, b: i32) FT {
var x_a: FT = a;
var x_b: i32 = b;
const is_recip: bool = b < 0;
var r: FT = 1.0;
while (true) {
if (@bitCast(u32, x_b) & @as(u32, 1) != 0) {
r *= x_a;
}
x_b = @divTrunc(x_b, @as(i32, 2));
if (x_b == 0) break;
x_a *= x_a; // Multiplication of x_a propagates the error
}
return if (is_recip) 1 / r else r;
}
pub fn __powihf2(a: f16, b: i32) callconv(.C) f16 {
return powiXf2(f16, a, b);
}
pub fn __powisf2(a: f32, b: i32) callconv(.C) f32 {
return powiXf2(f32, a, b);
}
pub fn __powidf2(a: f64, b: i32) callconv(.C) f64 {
return powiXf2(f64, a, b);
}
pub fn __powitf2(a: f128, b: i32) callconv(.C) f128 {
return powiXf2(f128, a, b);
}
pub fn __powixf2(a: f80, b: i32) callconv(.C) f80 {
return powiXf2(f80, a, b);
}
test {
_ = @import("powiXf2_test.zig");
}

View File

@ -0,0 +1,556 @@
// ported from https://github.com/llvm-mirror/compiler-rt/blob/release_80/test/builtins/Unit/
// powisf2_test.c, powidf2_test.c, powitf2_test.c, powixf2_test.c
// powihf2 adapted from powisf2 tests
const powiXf2 = @import("powiXf2.zig");
const testing = @import("std").testing;
const math = @import("std").math;
fn test__powihf2(a: f16, b: i32, expected: f16) !void {
var result = powiXf2.__powihf2(a, b);
try testing.expectEqual(expected, result);
}
fn test__powisf2(a: f32, b: i32, expected: f32) !void {
var result = powiXf2.__powisf2(a, b);
try testing.expectEqual(expected, result);
}
fn test__powidf2(a: f64, b: i32, expected: f64) !void {
var result = powiXf2.__powidf2(a, b);
try testing.expectEqual(expected, result);
}
fn test__powitf2(a: f128, b: i32, expected: f128) !void {
var result = powiXf2.__powitf2(a, b);
try testing.expectEqual(expected, result);
}
fn test__powixf2(a: f80, b: i32, expected: f80) !void {
var result = powiXf2.__powixf2(a, b);
try testing.expectEqual(expected, result);
}
test "powihf2" {
try test__powisf2(0, 0, 1);
try test__powihf2(1, 0, 1);
try test__powihf2(1.5, 0, 1);
try test__powihf2(2, 0, 1);
try test__powihf2(math.inf_f16, 0, 1);
try test__powihf2(-0.0, 0, 1);
try test__powihf2(-1, 0, 1);
try test__powihf2(-1.5, 0, 1);
try test__powihf2(-2, 0, 1);
try test__powihf2(-math.inf_f16, 0, 1);
try test__powihf2(0, 1, 0);
try test__powihf2(0, 2, 0);
try test__powihf2(0, 3, 0);
try test__powihf2(0, 4, 0);
try test__powihf2(0, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 0);
try test__powihf2(0, @bitCast(i32, @as(u32, 0x7FFFFFFF)), 0);
try test__powihf2(-0.0, 1, -0.0);
try test__powihf2(-0.0, 2, 0);
try test__powihf2(-0.0, 3, -0.0);
try test__powihf2(-0.0, 4, 0);
try test__powihf2(-0.0, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 0);
try test__powihf2(-0.0, @bitCast(i32, @as(u32, 0x7FFFFFFF)), -0.0);
try test__powihf2(1, 1, 1);
try test__powihf2(1, 2, 1);
try test__powihf2(1, 3, 1);
try test__powihf2(1, 4, 1);
try test__powihf2(1, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 1);
try test__powihf2(1, @bitCast(i32, @as(u32, 0x7FFFFFFF)), 1);
try test__powihf2(math.inf_f16, 1, math.inf_f16);
try test__powihf2(math.inf_f16, 2, math.inf_f16);
try test__powihf2(math.inf_f16, 3, math.inf_f16);
try test__powihf2(math.inf_f16, 4, math.inf_f16);
try test__powihf2(math.inf_f16, @bitCast(i32, @as(u32, 0x7FFFFFFE)), math.inf_f16);
try test__powihf2(math.inf_f16, @bitCast(i32, @as(u32, 0x7FFFFFFF)), math.inf_f16);
try test__powihf2(-math.inf_f16, 1, -math.inf_f16);
try test__powihf2(-math.inf_f16, 2, math.inf_f16);
try test__powihf2(-math.inf_f16, 3, -math.inf_f16);
try test__powihf2(-math.inf_f16, 4, math.inf_f16);
try test__powihf2(-math.inf_f16, @bitCast(i32, @as(u32, 0x7FFFFFFE)), math.inf_f16);
try test__powihf2(-math.inf_f16, @bitCast(i32, @as(u32, 0x7FFFFFFF)), -math.inf_f16);
//
try test__powihf2(0, -1, math.inf_f16);
try test__powihf2(0, -2, math.inf_f16);
try test__powihf2(0, -3, math.inf_f16);
try test__powihf2(0, -4, math.inf_f16);
try test__powihf2(0, @bitCast(i32, @as(u32, 0x80000002)), math.inf_f16); // 0 ^ anything = +inf
try test__powihf2(0, @bitCast(i32, @as(u32, 0x80000001)), math.inf_f16);
try test__powihf2(0, @bitCast(i32, @as(u32, 0x80000000)), math.inf_f16);
try test__powihf2(-0.0, -1, -math.inf_f16);
try test__powihf2(-0.0, -2, math.inf_f16);
try test__powihf2(-0.0, -3, -math.inf_f16);
try test__powihf2(-0.0, -4, math.inf_f16);
try test__powihf2(-0.0, @bitCast(i32, @as(u32, 0x80000002)), math.inf_f16); // -0 ^ anything even = +inf
try test__powihf2(-0.0, @bitCast(i32, @as(u32, 0x80000001)), -math.inf_f16); // -0 ^ anything odd = -inf
try test__powihf2(-0.0, @bitCast(i32, @as(u32, 0x80000000)), math.inf_f16);
try test__powihf2(1, -1, 1);
try test__powihf2(1, -2, 1);
try test__powihf2(1, -3, 1);
try test__powihf2(1, -4, 1);
try test__powihf2(1, @bitCast(i32, @as(u32, 0x80000002)), 1); // 1.0 ^ anything = 1
try test__powihf2(1, @bitCast(i32, @as(u32, 0x80000001)), 1);
try test__powihf2(1, @bitCast(i32, @as(u32, 0x80000000)), 1);
try test__powihf2(math.inf_f16, -1, 0);
try test__powihf2(math.inf_f16, -2, 0);
try test__powihf2(math.inf_f16, -3, 0);
try test__powihf2(math.inf_f16, -4, 0);
try test__powihf2(math.inf_f16, @bitCast(i32, @as(u32, 0x80000002)), 0);
try test__powihf2(math.inf_f16, @bitCast(i32, @as(u32, 0x80000001)), 0);
try test__powihf2(math.inf_f16, @bitCast(i32, @as(u32, 0x80000000)), 0);
//
try test__powihf2(-math.inf_f16, -1, -0.0);
try test__powihf2(-math.inf_f16, -2, 0);
try test__powihf2(-math.inf_f16, -3, -0.0);
try test__powihf2(-math.inf_f16, -4, 0);
try test__powihf2(-math.inf_f16, @bitCast(i32, @as(u32, 0x80000002)), 0);
try test__powihf2(-math.inf_f16, @bitCast(i32, @as(u32, 0x80000001)), -0.0);
try test__powihf2(-math.inf_f16, @bitCast(i32, @as(u32, 0x80000000)), 0);
try test__powihf2(2, 10, 1024.0);
try test__powihf2(-2, 10, 1024.0);
try test__powihf2(2, -10, 1.0 / 1024.0);
try test__powihf2(-2, -10, 1.0 / 1024.0);
try test__powihf2(2, 14, 16384.0);
try test__powihf2(-2, 14, 16384.0);
try test__powihf2(2, 15, 32768.0);
try test__powihf2(-2, 15, -32768.0);
try test__powihf2(2, 16, math.inf_f16);
try test__powihf2(-2, 16, math.inf_f16);
try test__powihf2(2, -13, 1.0 / 8192.0);
try test__powihf2(-2, -13, -1.0 / 8192.0);
try test__powihf2(2, -15, 1.0 / 32768.0);
try test__powihf2(-2, -15, -1.0 / 32768.0);
try test__powihf2(2, -16, 0.0); // expected = 0.0 = 1/(-2**16)
try test__powihf2(-2, -16, 0.0); // expected = 0.0 = 1/(2**16)
}
test "powisf2" {
try test__powisf2(0, 0, 1);
try test__powisf2(1, 0, 1);
try test__powisf2(1.5, 0, 1);
try test__powisf2(2, 0, 1);
try test__powisf2(math.inf_f32, 0, 1);
try test__powisf2(-0.0, 0, 1);
try test__powisf2(-1, 0, 1);
try test__powisf2(-1.5, 0, 1);
try test__powisf2(-2, 0, 1);
try test__powisf2(-math.inf_f32, 0, 1);
try test__powisf2(0, 1, 0);
try test__powisf2(0, 2, 0);
try test__powisf2(0, 3, 0);
try test__powisf2(0, 4, 0);
try test__powisf2(0, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 0);
try test__powisf2(0, @bitCast(i32, @as(u32, 0x7FFFFFFF)), 0);
try test__powisf2(-0.0, 1, -0.0);
try test__powisf2(-0.0, 2, 0);
try test__powisf2(-0.0, 3, -0.0);
try test__powisf2(-0.0, 4, 0);
try test__powisf2(-0.0, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 0);
try test__powisf2(-0.0, @bitCast(i32, @as(u32, 0x7FFFFFFF)), -0.0);
try test__powisf2(1, 1, 1);
try test__powisf2(1, 2, 1);
try test__powisf2(1, 3, 1);
try test__powisf2(1, 4, 1);
try test__powisf2(1, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 1);
try test__powisf2(1, @bitCast(i32, @as(u32, 0x7FFFFFFF)), 1);
try test__powisf2(math.inf_f32, 1, math.inf_f32);
try test__powisf2(math.inf_f32, 2, math.inf_f32);
try test__powisf2(math.inf_f32, 3, math.inf_f32);
try test__powisf2(math.inf_f32, 4, math.inf_f32);
try test__powisf2(math.inf_f32, @bitCast(i32, @as(u32, 0x7FFFFFFE)), math.inf_f32);
try test__powisf2(math.inf_f32, @bitCast(i32, @as(u32, 0x7FFFFFFF)), math.inf_f32);
try test__powisf2(-math.inf_f32, 1, -math.inf_f32);
try test__powisf2(-math.inf_f32, 2, math.inf_f32);
try test__powisf2(-math.inf_f32, 3, -math.inf_f32);
try test__powisf2(-math.inf_f32, 4, math.inf_f32);
try test__powisf2(-math.inf_f32, @bitCast(i32, @as(u32, 0x7FFFFFFE)), math.inf_f32);
try test__powisf2(-math.inf_f32, @bitCast(i32, @as(u32, 0x7FFFFFFF)), -math.inf_f32);
try test__powisf2(0, -1, math.inf_f32);
try test__powisf2(0, -2, math.inf_f32);
try test__powisf2(0, -3, math.inf_f32);
try test__powisf2(0, -4, math.inf_f32);
try test__powisf2(0, @bitCast(i32, @as(u32, 0x80000002)), math.inf_f32);
try test__powisf2(0, @bitCast(i32, @as(u32, 0x80000001)), math.inf_f32);
try test__powisf2(0, @bitCast(i32, @as(u32, 0x80000000)), math.inf_f32);
try test__powisf2(-0.0, -1, -math.inf_f32);
try test__powisf2(-0.0, -2, math.inf_f32);
try test__powisf2(-0.0, -3, -math.inf_f32);
try test__powisf2(-0.0, -4, math.inf_f32);
try test__powisf2(-0.0, @bitCast(i32, @as(u32, 0x80000002)), math.inf_f32);
try test__powisf2(-0.0, @bitCast(i32, @as(u32, 0x80000001)), -math.inf_f32);
try test__powisf2(-0.0, @bitCast(i32, @as(u32, 0x80000000)), math.inf_f32);
try test__powisf2(1, -1, 1);
try test__powisf2(1, -2, 1);
try test__powisf2(1, -3, 1);
try test__powisf2(1, -4, 1);
try test__powisf2(1, @bitCast(i32, @as(u32, 0x80000002)), 1);
try test__powisf2(1, @bitCast(i32, @as(u32, 0x80000001)), 1);
try test__powisf2(1, @bitCast(i32, @as(u32, 0x80000000)), 1);
try test__powisf2(math.inf_f32, -1, 0);
try test__powisf2(math.inf_f32, -2, 0);
try test__powisf2(math.inf_f32, -3, 0);
try test__powisf2(math.inf_f32, -4, 0);
try test__powisf2(math.inf_f32, @bitCast(i32, @as(u32, 0x80000002)), 0);
try test__powisf2(math.inf_f32, @bitCast(i32, @as(u32, 0x80000001)), 0);
try test__powisf2(math.inf_f32, @bitCast(i32, @as(u32, 0x80000000)), 0);
try test__powisf2(-math.inf_f32, -1, -0.0);
try test__powisf2(-math.inf_f32, -2, 0);
try test__powisf2(-math.inf_f32, -3, -0.0);
try test__powisf2(-math.inf_f32, -4, 0);
try test__powisf2(-math.inf_f32, @bitCast(i32, @as(u32, 0x80000002)), 0);
try test__powisf2(-math.inf_f32, @bitCast(i32, @as(u32, 0x80000001)), -0.0);
try test__powisf2(-math.inf_f32, @bitCast(i32, @as(u32, 0x80000000)), 0);
try test__powisf2(2.0, 10, 1024.0);
try test__powisf2(-2, 10, 1024.0);
try test__powisf2(2, -10, 1.0 / 1024.0);
try test__powisf2(-2, -10, 1.0 / 1024.0);
//
try test__powisf2(2, 19, 524288.0);
try test__powisf2(-2, 19, -524288.0);
try test__powisf2(2, -19, 1.0 / 524288.0);
try test__powisf2(-2, -19, -1.0 / 524288.0);
try test__powisf2(2, 31, 2147483648.0);
try test__powisf2(-2, 31, -2147483648.0);
try test__powisf2(2, -31, 1.0 / 2147483648.0);
try test__powisf2(-2, -31, -1.0 / 2147483648.0);
}
test "powidf2" {
try test__powidf2(0, 0, 1);
try test__powidf2(1, 0, 1);
try test__powidf2(1.5, 0, 1);
try test__powidf2(2, 0, 1);
try test__powidf2(math.inf_f64, 0, 1);
try test__powidf2(-0.0, 0, 1);
try test__powidf2(-1, 0, 1);
try test__powidf2(-1.5, 0, 1);
try test__powidf2(-2, 0, 1);
try test__powidf2(-math.inf_f64, 0, 1);
try test__powidf2(0, 1, 0);
try test__powidf2(0, 2, 0);
try test__powidf2(0, 3, 0);
try test__powidf2(0, 4, 0);
try test__powidf2(0, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 0);
try test__powidf2(0, @bitCast(i32, @as(u32, 0x7FFFFFFF)), 0);
try test__powidf2(-0.0, 1, -0.0);
try test__powidf2(-0.0, 2, 0);
try test__powidf2(-0.0, 3, -0.0);
try test__powidf2(-0.0, 4, 0);
try test__powidf2(-0.0, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 0);
try test__powidf2(-0.0, @bitCast(i32, @as(u32, 0x7FFFFFFF)), -0.0);
try test__powidf2(1, 1, 1);
try test__powidf2(1, 2, 1);
try test__powidf2(1, 3, 1);
try test__powidf2(1, 4, 1);
try test__powidf2(1, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 1);
try test__powidf2(1, @bitCast(i32, @as(u32, 0x7FFFFFFF)), 1);
try test__powidf2(math.inf_f64, 1, math.inf_f64);
try test__powidf2(math.inf_f64, 2, math.inf_f64);
try test__powidf2(math.inf_f64, 3, math.inf_f64);
try test__powidf2(math.inf_f64, 4, math.inf_f64);
try test__powidf2(math.inf_f64, @bitCast(i32, @as(u32, 0x7FFFFFFE)), math.inf_f64);
try test__powidf2(math.inf_f64, @bitCast(i32, @as(u32, 0x7FFFFFFF)), math.inf_f64);
try test__powidf2(-math.inf_f64, 1, -math.inf_f64);
try test__powidf2(-math.inf_f64, 2, math.inf_f64);
try test__powidf2(-math.inf_f64, 3, -math.inf_f64);
try test__powidf2(-math.inf_f64, 4, math.inf_f64);
try test__powidf2(-math.inf_f64, @bitCast(i32, @as(u32, 0x7FFFFFFE)), math.inf_f64);
try test__powidf2(-math.inf_f64, @bitCast(i32, @as(u32, 0x7FFFFFFF)), -math.inf_f64);
try test__powidf2(0, -1, math.inf_f64);
try test__powidf2(0, -2, math.inf_f64);
try test__powidf2(0, -3, math.inf_f64);
try test__powidf2(0, -4, math.inf_f64);
try test__powidf2(0, @bitCast(i32, @as(u32, 0x80000002)), math.inf_f64);
try test__powidf2(0, @bitCast(i32, @as(u32, 0x80000001)), math.inf_f64);
try test__powidf2(0, @bitCast(i32, @as(u32, 0x80000000)), math.inf_f64);
try test__powidf2(-0.0, -1, -math.inf_f64);
try test__powidf2(-0.0, -2, math.inf_f64);
try test__powidf2(-0.0, -3, -math.inf_f64);
try test__powidf2(-0.0, -4, math.inf_f64);
try test__powidf2(-0.0, @bitCast(i32, @as(u32, 0x80000002)), math.inf_f64);
try test__powidf2(-0.0, @bitCast(i32, @as(u32, 0x80000001)), -math.inf_f64);
try test__powidf2(-0.0, @bitCast(i32, @as(u32, 0x80000000)), math.inf_f64);
try test__powidf2(1, -1, 1);
try test__powidf2(1, -2, 1);
try test__powidf2(1, -3, 1);
try test__powidf2(1, -4, 1);
try test__powidf2(1, @bitCast(i32, @as(u32, 0x80000002)), 1);
try test__powidf2(1, @bitCast(i32, @as(u32, 0x80000001)), 1);
try test__powidf2(1, @bitCast(i32, @as(u32, 0x80000000)), 1);
try test__powidf2(math.inf_f64, -1, 0);
try test__powidf2(math.inf_f64, -2, 0);
try test__powidf2(math.inf_f64, -3, 0);
try test__powidf2(math.inf_f64, -4, 0);
try test__powidf2(math.inf_f64, @bitCast(i32, @as(u32, 0x80000002)), 0);
try test__powidf2(math.inf_f64, @bitCast(i32, @as(u32, 0x80000001)), 0);
try test__powidf2(math.inf_f64, @bitCast(i32, @as(u32, 0x80000000)), 0);
try test__powidf2(-math.inf_f64, -1, -0.0);
try test__powidf2(-math.inf_f64, -2, 0);
try test__powidf2(-math.inf_f64, -3, -0.0);
try test__powidf2(-math.inf_f64, -4, 0);
try test__powidf2(-math.inf_f64, @bitCast(i32, @as(u32, 0x80000002)), 0);
try test__powidf2(-math.inf_f64, @bitCast(i32, @as(u32, 0x80000001)), -0.0);
try test__powidf2(-math.inf_f64, @bitCast(i32, @as(u32, 0x80000000)), 0);
try test__powidf2(2, 10, 1024.0);
try test__powidf2(-2, 10, 1024.0);
try test__powidf2(2, -10, 1.0 / 1024.0);
try test__powidf2(-2, -10, 1.0 / 1024.0);
try test__powidf2(2, 19, 524288.0);
try test__powidf2(-2, 19, -524288.0);
try test__powidf2(2, -19, 1.0 / 524288.0);
try test__powidf2(-2, -19, -1.0 / 524288.0);
try test__powidf2(2, 31, 2147483648.0);
try test__powidf2(-2, 31, -2147483648.0);
try test__powidf2(2, -31, 1.0 / 2147483648.0);
try test__powidf2(-2, -31, -1.0 / 2147483648.0);
}
test "powitf2" {
try test__powitf2(0, 0, 1);
try test__powitf2(1, 0, 1);
try test__powitf2(1.5, 0, 1);
try test__powitf2(2, 0, 1);
try test__powitf2(math.inf_f128, 0, 1);
try test__powitf2(-0.0, 0, 1);
try test__powitf2(-1, 0, 1);
try test__powitf2(-1.5, 0, 1);
try test__powitf2(-2, 0, 1);
try test__powitf2(-math.inf_f128, 0, 1);
try test__powitf2(0, 1, 0);
try test__powitf2(0, 2, 0);
try test__powitf2(0, 3, 0);
try test__powitf2(0, 4, 0);
try test__powitf2(0, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 0);
try test__powitf2(0, 0x7FFFFFFF, 0);
try test__powitf2(-0.0, 1, -0.0);
try test__powitf2(-0.0, 2, 0);
try test__powitf2(-0.0, 3, -0.0);
try test__powitf2(-0.0, 4, 0);
try test__powitf2(-0.0, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 0);
try test__powitf2(-0.0, @bitCast(i32, @as(u32, 0x7FFFFFFF)), -0.0);
try test__powitf2(1, 1, 1);
try test__powitf2(1, 2, 1);
try test__powitf2(1, 3, 1);
try test__powitf2(1, 4, 1);
try test__powitf2(1, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 1);
try test__powitf2(1, @bitCast(i32, @as(u32, 0x7FFFFFFF)), 1);
try test__powitf2(math.inf_f128, 1, math.inf_f128);
try test__powitf2(math.inf_f128, 2, math.inf_f128);
try test__powitf2(math.inf_f128, 3, math.inf_f128);
try test__powitf2(math.inf_f128, 4, math.inf_f128);
try test__powitf2(math.inf_f128, @bitCast(i32, @as(u32, 0x7FFFFFFE)), math.inf_f128);
try test__powitf2(math.inf_f128, @bitCast(i32, @as(u32, 0x7FFFFFFF)), math.inf_f128);
try test__powitf2(-math.inf_f128, 1, -math.inf_f128);
try test__powitf2(-math.inf_f128, 2, math.inf_f128);
try test__powitf2(-math.inf_f128, 3, -math.inf_f128);
try test__powitf2(-math.inf_f128, 4, math.inf_f128);
try test__powitf2(-math.inf_f128, @bitCast(i32, @as(u32, 0x7FFFFFFE)), math.inf_f128);
try test__powitf2(-math.inf_f128, @bitCast(i32, @as(u32, 0x7FFFFFFF)), -math.inf_f128);
try test__powitf2(0, -1, math.inf_f128);
try test__powitf2(0, -2, math.inf_f128);
try test__powitf2(0, -3, math.inf_f128);
try test__powitf2(0, -4, math.inf_f128);
try test__powitf2(0, @bitCast(i32, @as(u32, 0x80000002)), math.inf_f128);
try test__powitf2(0, @bitCast(i32, @as(u32, 0x80000001)), math.inf_f128);
try test__powitf2(0, @bitCast(i32, @as(u32, 0x80000000)), math.inf_f128);
try test__powitf2(-0.0, -1, -math.inf_f128);
try test__powitf2(-0.0, -2, math.inf_f128);
try test__powitf2(-0.0, -3, -math.inf_f128);
try test__powitf2(-0.0, -4, math.inf_f128);
try test__powitf2(-0.0, @bitCast(i32, @as(u32, 0x80000002)), math.inf_f128);
try test__powitf2(-0.0, @bitCast(i32, @as(u32, 0x80000001)), -math.inf_f128);
try test__powitf2(-0.0, @bitCast(i32, @as(u32, 0x80000000)), math.inf_f128);
try test__powitf2(1, -1, 1);
try test__powitf2(1, -2, 1);
try test__powitf2(1, -3, 1);
try test__powitf2(1, -4, 1);
try test__powitf2(1, @bitCast(i32, @as(u32, 0x80000002)), 1);
try test__powitf2(1, @bitCast(i32, @as(u32, 0x80000001)), 1);
try test__powitf2(1, @bitCast(i32, @as(u32, 0x80000000)), 1);
try test__powitf2(math.inf_f128, -1, 0);
try test__powitf2(math.inf_f128, -2, 0);
try test__powitf2(math.inf_f128, -3, 0);
try test__powitf2(math.inf_f128, -4, 0);
try test__powitf2(math.inf_f128, @bitCast(i32, @as(u32, 0x80000002)), 0);
try test__powitf2(math.inf_f128, @bitCast(i32, @as(u32, 0x80000001)), 0);
try test__powitf2(math.inf_f128, @bitCast(i32, @as(u32, 0x80000000)), 0);
try test__powitf2(-math.inf_f128, -1, -0.0);
try test__powitf2(-math.inf_f128, -2, 0);
try test__powitf2(-math.inf_f128, -3, -0.0);
try test__powitf2(-math.inf_f128, -4, 0);
try test__powitf2(-math.inf_f128, @bitCast(i32, @as(u32, 0x80000002)), 0);
try test__powitf2(-math.inf_f128, @bitCast(i32, @as(u32, 0x80000001)), -0.0);
try test__powitf2(-math.inf_f128, @bitCast(i32, @as(u32, 0x80000000)), 0);
try test__powitf2(2, 10, 1024.0);
try test__powitf2(-2, 10, 1024.0);
try test__powitf2(2, -10, 1.0 / 1024.0);
try test__powitf2(-2, -10, 1.0 / 1024.0);
try test__powitf2(2, 19, 524288.0);
try test__powitf2(-2, 19, -524288.0);
try test__powitf2(2, -19, 1.0 / 524288.0);
try test__powitf2(-2, -19, -1.0 / 524288.0);
try test__powitf2(2, 31, 2147483648.0);
try test__powitf2(-2, 31, -2147483648.0);
try test__powitf2(2, -31, 1.0 / 2147483648.0);
try test__powitf2(-2, -31, -1.0 / 2147483648.0);
}
test "powixf2" {
try test__powixf2(0, 0, 1);
try test__powixf2(1, 0, 1);
try test__powixf2(1.5, 0, 1);
try test__powixf2(2, 0, 1);
try test__powixf2(math.inf_f80, 0, 1);
try test__powixf2(-0.0, 0, 1);
try test__powixf2(-1, 0, 1);
try test__powixf2(-1.5, 0, 1);
try test__powixf2(-2, 0, 1);
try test__powixf2(-math.inf_f80, 0, 1);
try test__powixf2(0, 1, 0);
try test__powixf2(0, 2, 0);
try test__powixf2(0, 3, 0);
try test__powixf2(0, 4, 0);
try test__powixf2(0, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 0);
try test__powixf2(0, @bitCast(i32, @as(u32, 0x7FFFFFFF)), 0);
try test__powixf2(-0.0, 1, -0.0);
try test__powixf2(-0.0, 2, 0);
try test__powixf2(-0.0, 3, -0.0);
try test__powixf2(-0.0, 4, 0);
try test__powixf2(-0.0, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 0);
try test__powixf2(-0.0, @bitCast(i32, @as(u32, 0x7FFFFFFF)), -0.0);
try test__powixf2(1, 1, 1);
try test__powixf2(1, 2, 1);
try test__powixf2(1, 3, 1);
try test__powixf2(1, 4, 1);
try test__powixf2(1, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 1);
try test__powixf2(1, @bitCast(i32, @as(u32, 0x7FFFFFFF)), 1);
try test__powixf2(math.inf_f80, 1, math.inf_f80);
try test__powixf2(math.inf_f80, 2, math.inf_f80);
try test__powixf2(math.inf_f80, 3, math.inf_f80);
try test__powixf2(math.inf_f80, 4, math.inf_f80);
try test__powixf2(math.inf_f80, @bitCast(i32, @as(u32, 0x7FFFFFFE)), math.inf_f80);
try test__powixf2(math.inf_f80, @bitCast(i32, @as(u32, 0x7FFFFFFF)), math.inf_f80);
try test__powixf2(-math.inf_f80, 1, -math.inf_f80);
try test__powixf2(-math.inf_f80, 2, math.inf_f80);
try test__powixf2(-math.inf_f80, 3, -math.inf_f80);
try test__powixf2(-math.inf_f80, 4, math.inf_f80);
try test__powixf2(-math.inf_f80, @bitCast(i32, @as(u32, 0x7FFFFFFE)), math.inf_f80);
try test__powixf2(-math.inf_f80, @bitCast(i32, @as(u32, 0x7FFFFFFF)), -math.inf_f80);
try test__powixf2(0, -1, math.inf_f80);
try test__powixf2(0, -2, math.inf_f80);
try test__powixf2(0, -3, math.inf_f80);
try test__powixf2(0, -4, math.inf_f80);
try test__powixf2(0, @bitCast(i32, @as(u32, 0x80000002)), math.inf_f80);
try test__powixf2(0, @bitCast(i32, @as(u32, 0x80000001)), math.inf_f80);
try test__powixf2(0, @bitCast(i32, @as(u32, 0x80000000)), math.inf_f80);
try test__powixf2(-0.0, -1, -math.inf_f80);
try test__powixf2(-0.0, -2, math.inf_f80);
try test__powixf2(-0.0, -3, -math.inf_f80);
try test__powixf2(-0.0, -4, math.inf_f80);
try test__powixf2(-0.0, @bitCast(i32, @as(u32, 0x80000002)), math.inf_f80);
try test__powixf2(-0.0, @bitCast(i32, @as(u32, 0x80000001)), -math.inf_f80);
try test__powixf2(-0.0, @bitCast(i32, @as(u32, 0x80000000)), math.inf_f80);
try test__powixf2(1, -1, 1);
try test__powixf2(1, -2, 1);
try test__powixf2(1, -3, 1);
try test__powixf2(1, -4, 1);
try test__powixf2(1, @bitCast(i32, @as(u32, 0x80000002)), 1);
try test__powixf2(1, @bitCast(i32, @as(u32, 0x80000001)), 1);
try test__powixf2(1, @bitCast(i32, @as(u32, 0x80000000)), 1);
try test__powixf2(math.inf_f80, -1, 0);
try test__powixf2(math.inf_f80, -2, 0);
try test__powixf2(math.inf_f80, -3, 0);
try test__powixf2(math.inf_f80, -4, 0);
try test__powixf2(math.inf_f80, @bitCast(i32, @as(u32, 0x80000002)), 0);
try test__powixf2(math.inf_f80, @bitCast(i32, @as(u32, 0x80000001)), 0);
try test__powixf2(math.inf_f80, @bitCast(i32, @as(u32, 0x80000000)), 0);
try test__powixf2(-math.inf_f80, -1, -0.0);
try test__powixf2(-math.inf_f80, -2, 0);
try test__powixf2(-math.inf_f80, -3, -0.0);
try test__powixf2(-math.inf_f80, -4, 0);
try test__powixf2(-math.inf_f80, @bitCast(i32, @as(u32, 0x80000002)), 0);
try test__powixf2(-math.inf_f80, @bitCast(i32, @as(u32, 0x80000001)), -0.0);
try test__powixf2(-math.inf_f80, @bitCast(i32, @as(u32, 0x80000000)), 0);
try test__powixf2(2, 10, 1024.0);
try test__powixf2(-2, 10, 1024.0);
try test__powixf2(2, -10, 1.0 / 1024.0);
try test__powixf2(-2, -10, 1.0 / 1024.0);
try test__powixf2(2, 19, 524288.0);
try test__powixf2(-2, 19, -524288.0);
try test__powixf2(2, -19, 1.0 / 524288.0);
try test__powixf2(-2, -19, -1.0 / 524288.0);
try test__powixf2(2, 31, 2147483648.0);
try test__powixf2(-2, 31, -2147483648.0);
try test__powixf2(2, -31, 1.0 / 2147483648.0);
try test__powixf2(-2, -31, -1.0 / 2147483648.0);
}

View File

@ -7,6 +7,11 @@ const common = @import("common.zig");
pub const panic = common.panic;
comptime {
// symbol compatibility with libgcc
@export(__ashlsi3, .{ .name = "__ashlsi3", .linkage = common.linkage, .visibility = common.visibility });
@export(__ashrsi3, .{ .name = "__ashrsi3", .linkage = common.linkage, .visibility = common.visibility });
@export(__lshrsi3, .{ .name = "__lshrsi3", .linkage = common.linkage, .visibility = common.visibility });
@export(__ashlti3, .{ .name = "__ashlti3", .linkage = common.linkage, .visibility = common.visibility });
@export(__ashrti3, .{ .name = "__ashrti3", .linkage = common.linkage, .visibility = common.visibility });
@export(__lshrti3, .{ .name = "__lshrti3", .linkage = common.linkage, .visibility = common.visibility });
@ -37,7 +42,7 @@ fn Dwords(comptime T: type, comptime signed_half: bool) type {
};
}
// Arithmetic shift left
// Arithmetic shift left: shift in 0 from right to left
// Precondition: 0 <= b < bits_in_dword
inline fn ashlXi3(comptime T: type, a: T, b: i32) T {
const dwords = Dwords(T, false);
@ -60,7 +65,7 @@ inline fn ashlXi3(comptime T: type, a: T, b: i32) T {
return output.all;
}
// Arithmetic shift right
// Arithmetic shift right: shift in 1 from left to right
// Precondition: 0 <= b < T.bit_count
inline fn ashrXi3(comptime T: type, a: T, b: i32) T {
const dwords = Dwords(T, true);
@ -87,7 +92,7 @@ inline fn ashrXi3(comptime T: type, a: T, b: i32) T {
return output.all;
}
// Logical shift right
// Logical shift right: shift in 0 from left to right
// Precondition: 0 <= b < T.bit_count
inline fn lshrXi3(comptime T: type, a: T, b: i32) T {
const dwords = Dwords(T, false);
@ -110,6 +115,18 @@ inline fn lshrXi3(comptime T: type, a: T, b: i32) T {
return output.all;
}
pub fn __ashlsi3(a: i32, b: i32) callconv(.C) i32 {
return ashlXi3(i32, a, b);
}
pub fn __ashrsi3(a: i32, b: i32) callconv(.C) i32 {
return ashrXi3(i32, a, b);
}
pub fn __lshrsi3(a: i32, b: i32) callconv(.C) i32 {
return lshrXi3(i32, a, b);
}
pub fn __ashldi3(a: i64, b: i32) callconv(.C) i64 {
return ashlXi3(i64, a, b);
}
@ -144,12 +161,5 @@ pub fn __lshrti3(a: i128, b: i32) callconv(.C) i128 {
}
test {
_ = @import("ashrdi3_test.zig");
_ = @import("ashrti3_test.zig");
_ = @import("ashldi3_test.zig");
_ = @import("ashlti3_test.zig");
_ = @import("lshrdi3_test.zig");
_ = @import("lshrti3_test.zig");
_ = @import("shift_test.zig");
}

View File

@ -0,0 +1,363 @@
const testing = @import("std").testing;
const shift = @import("shift.zig");
// arithmetic shift left
const __ashlsi3 = shift.__ashlsi3;
const __ashldi3 = shift.__ashldi3;
const __ashlti3 = shift.__ashlti3;
// arithmetic shift right
const __ashrsi3 = shift.__ashrsi3;
const __ashrdi3 = shift.__ashrdi3;
const __ashrti3 = shift.__ashrti3;
// logic shift right
const __lshrsi3 = shift.__lshrsi3;
const __lshrdi3 = shift.__lshrdi3;
const __lshrti3 = shift.__lshrti3;
fn test__ashlsi3(a: i32, b: i32, expected: u32) !void {
const x = __ashlsi3(a, b);
try testing.expectEqual(expected, @bitCast(u32, x));
}
fn test__ashldi3(a: i64, b: i32, expected: u64) !void {
const x = __ashldi3(a, b);
try testing.expectEqual(expected, @bitCast(u64, x));
}
fn test__ashlti3(a: i128, b: i32, expected: u128) !void {
const x = __ashlti3(a, b);
try testing.expectEqual(expected, @bitCast(u128, x));
}
test "ashlsi3" {
try test__ashlsi3(@bitCast(i32, @as(u32, 0x12ABCDEF)), 0, 0x12ABCDEF);
try test__ashlsi3(@bitCast(i32, @as(u32, 0x12ABCDEF)), 1, 0x25579BDE);
try test__ashlsi3(@bitCast(i32, @as(u32, 0x12ABCDEF)), 2, 0x4AAF37BC);
try test__ashlsi3(@bitCast(i32, @as(u32, 0x12ABCDEF)), 3, 0x955E6F78);
try test__ashlsi3(@bitCast(i32, @as(u32, 0x12ABCDEF)), 4, 0x2ABCDEF0);
try test__ashlsi3(@bitCast(i32, @as(u32, 0x12ABCDEF)), 28, 0xF0000000);
try test__ashlsi3(@bitCast(i32, @as(u32, 0x12ABCDEF)), 29, 0xE0000000);
try test__ashlsi3(@bitCast(i32, @as(u32, 0x12ABCDEF)), 30, 0xC0000000);
try test__ashlsi3(@bitCast(i32, @as(u32, 0x12ABCDEF)), 31, 0x80000000);
}
test "ashldi3" {
try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 0, 0x123456789ABCDEF);
try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 1, 0x2468ACF13579BDE);
try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 2, 0x48D159E26AF37BC);
try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 3, 0x91A2B3C4D5E6F78);
try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 4, 0x123456789ABCDEF0);
try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 28, 0x789ABCDEF0000000);
try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 29, 0xF13579BDE0000000);
try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 30, 0xE26AF37BC0000000);
try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 31, 0xC4D5E6F780000000);
try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 32, 0x89ABCDEF00000000);
try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 33, 0x13579BDE00000000);
try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 34, 0x26AF37BC00000000);
try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 35, 0x4D5E6F7800000000);
try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 36, 0x9ABCDEF000000000);
try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 60, 0xF000000000000000);
try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 61, 0xE000000000000000);
try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 62, 0xC000000000000000);
try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 63, 0x8000000000000000);
}
test "ashlti3" {
try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 0, 0xFEDCBA9876543215FEDCBA9876543215);
try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 1, 0xFDB97530ECA8642BFDB97530ECA8642A);
try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 2, 0xFB72EA61D950C857FB72EA61D950C854);
try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 3, 0xF6E5D4C3B2A190AFF6E5D4C3B2A190A8);
try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 4, 0xEDCBA9876543215FEDCBA98765432150);
try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 28, 0x876543215FEDCBA98765432150000000);
try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 29, 0x0ECA8642BFDB97530ECA8642A0000000);
try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 30, 0x1D950C857FB72EA61D950C8540000000);
try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 31, 0x3B2A190AFF6E5D4C3B2A190A80000000);
try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 32, 0x76543215FEDCBA987654321500000000);
try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 33, 0xECA8642BFDB97530ECA8642A00000000);
try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 34, 0xD950C857FB72EA61D950C85400000000);
try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 35, 0xB2A190AFF6E5D4C3B2A190A800000000);
try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 36, 0x6543215FEDCBA9876543215000000000);
try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 60, 0x5FEDCBA9876543215000000000000000);
try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 61, 0xBFDB97530ECA8642A000000000000000);
try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 62, 0x7FB72EA61D950C854000000000000000);
try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 63, 0xFF6E5D4C3B2A190A8000000000000000);
try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 64, 0xFEDCBA98765432150000000000000000);
try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 65, 0xFDB97530ECA8642A0000000000000000);
try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 66, 0xFB72EA61D950C8540000000000000000);
try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 67, 0xF6E5D4C3B2A190A80000000000000000);
try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 68, 0xEDCBA987654321500000000000000000);
try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 92, 0x87654321500000000000000000000000);
try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 93, 0x0ECA8642A00000000000000000000000);
try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 94, 0x1D950C85400000000000000000000000);
try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 95, 0x3B2A190A800000000000000000000000);
try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 96, 0x76543215000000000000000000000000);
try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 97, 0xECA8642A000000000000000000000000);
try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 98, 0xD950C854000000000000000000000000);
try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 99, 0xB2A190A8000000000000000000000000);
try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 100, 0x65432150000000000000000000000000);
try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 124, 0x50000000000000000000000000000000);
try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 125, 0xA0000000000000000000000000000000);
try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 126, 0x40000000000000000000000000000000);
try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 127, 0x80000000000000000000000000000000);
}
fn test__ashrsi3(a: i32, b: i32, expected: u32) !void {
const x = __ashrsi3(a, b);
try testing.expectEqual(expected, @bitCast(u32, x));
}
fn test__ashrdi3(a: i64, b: i32, expected: u64) !void {
const x = __ashrdi3(a, b);
try testing.expectEqual(expected, @bitCast(u64, x));
}
fn test__ashrti3(a: i128, b: i32, expected: u128) !void {
const x = __ashrti3(a, b);
try testing.expectEqual(expected, @bitCast(u128, x));
}
test "ashrsi3" {
try test__ashrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 0, 0xFEDBCA98);
try test__ashrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 1, 0xFF6DE54C);
try test__ashrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 2, 0xFFB6F2A6);
try test__ashrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 3, 0xFFDB7953);
try test__ashrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 4, 0xFFEDBCA9);
try test__ashrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 28, 0xFFFFFFFF);
try test__ashrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 31, 0xFFFFFFFF);
try test__ashrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 0, 0x8CEF8CEF);
try test__ashrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 1, 0xC677C677);
try test__ashrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 2, 0xE33BE33B);
try test__ashrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 3, 0xF19DF19D);
try test__ashrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 4, 0xF8CEF8CE);
try test__ashrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 28, 0xFFFFFFF8);
try test__ashrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 29, 0xFFFFFFFC);
try test__ashrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 30, 0xFFFFFFFE);
try test__ashrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 31, 0xFFFFFFFF);
}
test "ashrdi3" {
try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 0, 0x123456789ABCDEF);
try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 1, 0x91A2B3C4D5E6F7);
try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 2, 0x48D159E26AF37B);
try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 3, 0x2468ACF13579BD);
try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 4, 0x123456789ABCDE);
try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 28, 0x12345678);
try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 29, 0x91A2B3C);
try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 30, 0x48D159E);
try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 31, 0x2468ACF);
try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 32, 0x1234567);
try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 33, 0x91A2B3);
try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 34, 0x48D159);
try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 35, 0x2468AC);
try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 36, 0x123456);
try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 60, 0);
try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 61, 0);
try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 62, 0);
try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 63, 0);
try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 0, 0xFEDCBA9876543210);
try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 1, 0xFF6E5D4C3B2A1908);
try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 2, 0xFFB72EA61D950C84);
try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 3, 0xFFDB97530ECA8642);
try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 4, 0xFFEDCBA987654321);
try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 28, 0xFFFFFFFFEDCBA987);
try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 29, 0xFFFFFFFFF6E5D4C3);
try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 30, 0xFFFFFFFFFB72EA61);
try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 31, 0xFFFFFFFFFDB97530);
try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 32, 0xFFFFFFFFFEDCBA98);
try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 33, 0xFFFFFFFFFF6E5D4C);
try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 34, 0xFFFFFFFFFFB72EA6);
try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 35, 0xFFFFFFFFFFDB9753);
try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 36, 0xFFFFFFFFFFEDCBA9);
try test__ashrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 60, 0xFFFFFFFFFFFFFFFA);
try test__ashrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 61, 0xFFFFFFFFFFFFFFFD);
try test__ashrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 62, 0xFFFFFFFFFFFFFFFE);
try test__ashrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 63, 0xFFFFFFFFFFFFFFFF);
}
test "ashrti3" {
try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 0, 0xFEDCBA9876543215FEDCBA9876543215);
try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 1, 0xFF6E5D4C3B2A190AFF6E5D4C3B2A190A);
try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 2, 0xFFB72EA61D950C857FB72EA61D950C85);
try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 3, 0xFFDB97530ECA8642BFDB97530ECA8642);
try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 4, 0xFFEDCBA9876543215FEDCBA987654321);
try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 28, 0xFFFFFFFFEDCBA9876543215FEDCBA987);
try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 29, 0xFFFFFFFFF6E5D4C3B2A190AFF6E5D4C3);
try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 30, 0xFFFFFFFFFB72EA61D950C857FB72EA61);
try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 31, 0xFFFFFFFFFDB97530ECA8642BFDB97530);
try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 32, 0xFFFFFFFFFEDCBA9876543215FEDCBA98);
try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 33, 0xFFFFFFFFFF6E5D4C3B2A190AFF6E5D4C);
try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 34, 0xFFFFFFFFFFB72EA61D950C857FB72EA6);
try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 35, 0xFFFFFFFFFFDB97530ECA8642BFDB9753);
try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 36, 0xFFFFFFFFFFEDCBA9876543215FEDCBA9);
try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 60, 0xFFFFFFFFFFFFFFFFEDCBA9876543215F);
try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 61, 0xFFFFFFFFFFFFFFFFF6E5D4C3B2A190AF);
try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 62, 0xFFFFFFFFFFFFFFFFFB72EA61D950C857);
try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 63, 0xFFFFFFFFFFFFFFFFFDB97530ECA8642B);
try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 64, 0xFFFFFFFFFFFFFFFFFEDCBA9876543215);
try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 65, 0xFFFFFFFFFFFFFFFFFF6E5D4C3B2A190A);
try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 66, 0xFFFFFFFFFFFFFFFFFFB72EA61D950C85);
try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 67, 0xFFFFFFFFFFFFFFFFFFDB97530ECA8642);
try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 68, 0xFFFFFFFFFFFFFFFFFFEDCBA987654321);
try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 92, 0xFFFFFFFFFFFFFFFFFFFFFFFFEDCBA987);
try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 93, 0xFFFFFFFFFFFFFFFFFFFFFFFFF6E5D4C3);
try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 94, 0xFFFFFFFFFFFFFFFFFFFFFFFFFB72EA61);
try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 95, 0xFFFFFFFFFFFFFFFFFFFFFFFFFDB97530);
try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 96, 0xFFFFFFFFFFFFFFFFFFFFFFFFFEDCBA98);
try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 97, 0xFFFFFFFFFFFFFFFFFFFFFFFFFF6E5D4C);
try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 98, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFB72EA6);
try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 99, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFDB9753);
try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 100, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFEDCBA9);
try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 124, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 125, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 126, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 127, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
}
fn test__lshrsi3(a: i32, b: i32, expected: u32) !void {
const x = __lshrsi3(a, b);
try testing.expectEqual(expected, @bitCast(u32, x));
}
fn test__lshrdi3(a: i64, b: i32, expected: u64) !void {
const x = __lshrdi3(a, b);
try testing.expectEqual(expected, @bitCast(u64, x));
}
fn test__lshrti3(a: i128, b: i32, expected: u128) !void {
const x = __lshrti3(a, b);
try testing.expectEqual(expected, @bitCast(u128, x));
}
test "lshrsi3" {
try test__lshrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 0, 0xFEDBCA98);
try test__lshrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 1, 0x7F6DE54C);
try test__lshrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 2, 0x3FB6F2A6);
try test__lshrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 3, 0x1FDB7953);
try test__lshrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 4, 0xFEDBCA9);
try test__lshrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 28, 0xF);
try test__lshrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 29, 0x7);
try test__lshrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 30, 0x3);
try test__lshrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 31, 0x1);
try test__lshrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 0, 0x8CEF8CEF);
try test__lshrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 1, 0x4677C677);
try test__lshrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 2, 0x233BE33B);
try test__lshrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 3, 0x119DF19D);
try test__lshrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 4, 0x8CEF8CE);
try test__lshrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 28, 0x8);
try test__lshrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 29, 0x4);
try test__lshrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 30, 0x2);
try test__lshrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 31, 0x1);
}
test "lshrdi3" {
try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 0, 0x123456789ABCDEF);
try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 1, 0x91A2B3C4D5E6F7);
try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 2, 0x48D159E26AF37B);
try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 3, 0x2468ACF13579BD);
try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 4, 0x123456789ABCDE);
try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 28, 0x12345678);
try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 29, 0x91A2B3C);
try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 30, 0x48D159E);
try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 31, 0x2468ACF);
try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 32, 0x1234567);
try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 33, 0x91A2B3);
try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 34, 0x48D159);
try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 35, 0x2468AC);
try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 36, 0x123456);
try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 60, 0);
try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 61, 0);
try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 62, 0);
try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 63, 0);
try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 0, 0xFEDCBA9876543210);
try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 1, 0x7F6E5D4C3B2A1908);
try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 2, 0x3FB72EA61D950C84);
try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 3, 0x1FDB97530ECA8642);
try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 4, 0xFEDCBA987654321);
try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 28, 0xFEDCBA987);
try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 29, 0x7F6E5D4C3);
try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 30, 0x3FB72EA61);
try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 31, 0x1FDB97530);
try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 32, 0xFEDCBA98);
try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 33, 0x7F6E5D4C);
try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 34, 0x3FB72EA6);
try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 35, 0x1FDB9753);
try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 36, 0xFEDCBA9);
try test__lshrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 60, 0xA);
try test__lshrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 61, 0x5);
try test__lshrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 62, 0x2);
try test__lshrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 63, 0x1);
}
test "lshrti3" {
try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 0, 0xFEDCBA9876543215FEDCBA987654321F);
try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 1, 0x7F6E5D4C3B2A190AFF6E5D4C3B2A190F);
try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 2, 0x3FB72EA61D950C857FB72EA61D950C87);
try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 3, 0x1FDB97530ECA8642BFDB97530ECA8643);
try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 4, 0xFEDCBA9876543215FEDCBA987654321);
try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 28, 0xFEDCBA9876543215FEDCBA987);
try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 29, 0x7F6E5D4C3B2A190AFF6E5D4C3);
try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 30, 0x3FB72EA61D950C857FB72EA61);
try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 31, 0x1FDB97530ECA8642BFDB97530);
try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 32, 0xFEDCBA9876543215FEDCBA98);
try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 33, 0x7F6E5D4C3B2A190AFF6E5D4C);
try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 34, 0x3FB72EA61D950C857FB72EA6);
try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 35, 0x1FDB97530ECA8642BFDB9753);
try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 36, 0xFEDCBA9876543215FEDCBA9);
try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 60, 0xFEDCBA9876543215F);
try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 61, 0x7F6E5D4C3B2A190AF);
try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 62, 0x3FB72EA61D950C857);
try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 63, 0x1FDB97530ECA8642B);
try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 64, 0xFEDCBA9876543215);
try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 65, 0x7F6E5D4C3B2A190A);
try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 66, 0x3FB72EA61D950C85);
try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 67, 0x1FDB97530ECA8642);
try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 68, 0xFEDCBA987654321);
try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 92, 0xFEDCBA987);
try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 93, 0x7F6E5D4C3);
try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 94, 0x3FB72EA61);
try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 95, 0x1FDB97530);
try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 96, 0xFEDCBA98);
try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 97, 0x7F6E5D4C);
try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 98, 0x3FB72EA6);
try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 99, 0x1FDB9753);
try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 100, 0xFEDCBA9);
try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 124, 0xF);
try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 125, 0x7);
try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 126, 0x3);
try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 127, 0x1);
}

View File

@ -1354,8 +1354,12 @@ const NAV_MODES = {
payloadHtml += "ptrCast";
break;
}
case "qual_cast": {
payloadHtml += "qualCast";
case "const_cast": {
payloadHtml += "constCast";
break;
}
case "volatile_cast": {
payloadHtml += "volatileCast";
break;
}
case "truncate": {

View File

@ -19,6 +19,8 @@ const NativeTargetInfo = std.zig.system.NativeTargetInfo;
const Sha256 = std.crypto.hash.sha2.Sha256;
const Build = @This();
pub const Cache = @import("Build/Cache.zig");
/// deprecated: use `CompileStep`.
pub const LibExeObjStep = CompileStep;
/// deprecated: use `Build`.
@ -77,11 +79,12 @@ search_prefixes: ArrayList([]const u8),
libc_file: ?[]const u8 = null,
installed_files: ArrayList(InstalledFile),
/// Path to the directory containing build.zig.
build_root: []const u8,
cache_root: []const u8,
global_cache_root: []const u8,
/// zig lib dir
override_lib_dir: ?[]const u8,
build_root: Cache.Directory,
cache_root: Cache.Directory,
global_cache_root: Cache.Directory,
cache: *Cache,
/// If non-null, overrides the default zig lib dir.
zig_lib_dir: ?[]const u8,
vcpkg_root: VcpkgRoot = .unattempted,
pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null,
args: ?[][]const u8 = null,
@ -185,10 +188,11 @@ pub const DirList = struct {
pub fn create(
allocator: Allocator,
zig_exe: []const u8,
build_root: []const u8,
cache_root: []const u8,
global_cache_root: []const u8,
build_root: Cache.Directory,
cache_root: Cache.Directory,
global_cache_root: Cache.Directory,
host: NativeTargetInfo,
cache: *Cache,
) !*Build {
const env_map = try allocator.create(EnvMap);
env_map.* = try process.getEnvMap(allocator);
@ -197,8 +201,9 @@ pub fn create(
self.* = Build{
.zig_exe = zig_exe,
.build_root = build_root,
.cache_root = try fs.path.relative(allocator, build_root, cache_root),
.cache_root = cache_root,
.global_cache_root = global_cache_root,
.cache = cache,
.verbose = false,
.verbose_link = false,
.verbose_cc = false,
@ -230,7 +235,7 @@ pub fn create(
.step = Step.init(.top_level, "uninstall", allocator, makeUninstall),
.description = "Remove build artifacts from prefix path",
},
.override_lib_dir = null,
.zig_lib_dir = null,
.install_path = undefined,
.args = null,
.host = host,
@ -245,7 +250,7 @@ pub fn create(
fn createChild(
parent: *Build,
dep_name: []const u8,
build_root: []const u8,
build_root: Cache.Directory,
args: anytype,
) !*Build {
const child = try createChildOnly(parent, dep_name, build_root);
@ -253,7 +258,7 @@ fn createChild(
return child;
}
fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: []const u8) !*Build {
fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Directory) !*Build {
const allocator = parent.allocator;
const child = try allocator.create(Build);
child.* = .{
@ -297,7 +302,8 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: []const u8)
.build_root = build_root,
.cache_root = parent.cache_root,
.global_cache_root = parent.global_cache_root,
.override_lib_dir = parent.override_lib_dir,
.cache = parent.cache,
.zig_lib_dir = parent.zig_lib_dir,
.debug_log_scopes = parent.debug_log_scopes,
.debug_compile_errors = parent.debug_compile_errors,
.enable_darling = parent.enable_darling,
@ -348,7 +354,7 @@ fn applyArgs(b: *Build, args: anytype) !void {
.used = false,
});
},
.Enum => {
.Enum, .EnumLiteral => {
try b.user_input_options.put(field.name, .{
.name = field.name,
.value = .{ .scalar = @tagName(v) },
@ -379,7 +385,7 @@ fn applyArgs(b: *Build, args: anytype) !void {
_ = std.fmt.bufPrint(&hash_basename, "{s}", .{std.fmt.fmtSliceHexLower(&digest)}) catch
unreachable;
const install_prefix = b.pathJoin(&.{ b.cache_root, "i", &hash_basename });
const install_prefix = try b.cache_root.join(b.allocator, &.{ "i", &hash_basename });
b.resolveInstallPrefix(install_prefix, .{});
}
@ -396,7 +402,7 @@ pub fn resolveInstallPrefix(self: *Build, install_prefix: ?[]const u8, dir_list:
self.install_path = self.pathJoin(&.{ dest_dir, self.install_prefix });
} else {
self.install_prefix = install_prefix orelse
(self.pathJoin(&.{ self.build_root, "zig-out" }));
(self.build_root.join(self.allocator, &.{"zig-out"}) catch @panic("unhandled error"));
self.install_path = self.install_prefix;
}
@ -535,6 +541,7 @@ pub const AssemblyOptions = struct {
pub fn addAssembly(b: *Build, options: AssemblyOptions) *CompileStep {
const obj_step = CompileStep.create(b, .{
.name = options.name,
.kind = .obj,
.root_source_file = null,
.target = options.target,
.optimize = options.optimize,
@ -598,13 +605,39 @@ pub fn addSystemCommand(self: *Build, argv: []const []const u8) *RunStep {
return run_step;
}
/// Creates a `RunStep` with an executable built with `addExecutable`.
/// Add command line arguments with methods of `RunStep`.
pub fn addRunArtifact(b: *Build, exe: *CompileStep) *RunStep {
assert(exe.kind == .exe or exe.kind == .test_exe);
// It doesn't have to be native. We catch that if you actually try to run it.
// Consider that this is declarative; the run step may not be run unless a user
// option is supplied.
const run_step = RunStep.create(b, b.fmt("run {s}", .{exe.step.name}));
run_step.addArtifactArg(exe);
if (exe.kind == .test_exe) {
run_step.addArg(b.zig_exe);
}
if (exe.vcpkg_bin_path) |path| {
run_step.addPathDir(path);
}
return run_step;
}
/// Using the `values` provided, produces a C header file, possibly based on a
/// template input file (e.g. config.h.in).
/// When an input template file is provided, this function will fail the build
/// when an option not found in the input file is provided in `values`, and
/// when an option found in the input file is missing from `values`.
pub fn addConfigHeader(
b: *Build,
source: FileSource,
style: ConfigHeaderStep.Style,
options: ConfigHeaderStep.Options,
values: anytype,
) *ConfigHeaderStep {
const config_header_step = ConfigHeaderStep.create(b, source, style);
const config_header_step = ConfigHeaderStep.create(b, options);
config_header_step.addValues(values);
return config_header_step;
}
@ -669,8 +702,6 @@ pub fn addTranslateC(self: *Build, options: TranslateCStep.Options) *TranslateCS
}
pub fn make(self: *Build, step_names: []const []const u8) !void {
try self.makePath(self.cache_root);
var wanted_steps = ArrayList(*Step).init(self.allocator);
defer wanted_steps.deinit();
@ -901,7 +932,7 @@ pub fn standardOptimizeOption(self: *Build, options: StandardOptimizeOptionOptio
return self.option(
std.builtin.Mode,
"optimize",
"prioritize performance, safety, or binary size (-O flag)",
"Prioritize performance, safety, or binary size (-O flag)",
) orelse .Debug;
}
}
@ -1196,13 +1227,6 @@ pub fn spawnChildEnvMap(self: *Build, cwd: ?[]const u8, env_map: *const EnvMap,
}
}
pub fn makePath(self: *Build, path: []const u8) !void {
fs.cwd().makePath(self.pathFromRoot(path)) catch |err| {
log.err("Unable to create path {s}: {s}", .{ path, @errorName(err) });
return err;
};
}
pub fn installArtifact(self: *Build, artifact: *CompileStep) void {
self.getInstallStep().dependOn(&self.addInstallArtifact(artifact).step);
}
@ -1317,8 +1341,8 @@ pub fn truncateFile(self: *Build, dest_path: []const u8) !void {
src_file.close();
}
pub fn pathFromRoot(self: *Build, rel_path: []const u8) []u8 {
return fs.path.resolve(self.allocator, &[_][]const u8{ self.build_root, rel_path }) catch @panic("OOM");
pub fn pathFromRoot(b: *Build, p: []const u8) []u8 {
return fs.path.resolve(b.allocator, &.{ b.build_root.path orelse ".", p }) catch @panic("OOM");
}
pub fn pathJoin(self: *Build, paths: []const []const u8) []u8 {
@ -1539,10 +1563,19 @@ pub fn dependency(b: *Build, name: []const u8, args: anytype) *Dependency {
fn dependencyInner(
b: *Build,
name: []const u8,
build_root: []const u8,
build_root_string: []const u8,
comptime build_zig: type,
args: anytype,
) *Dependency {
const build_root: std.Build.Cache.Directory = .{
.path = build_root_string,
.handle = std.fs.cwd().openDir(build_root_string, .{}) catch |err| {
std.debug.print("unable to open '{s}': {s}\n", .{
build_root_string, @errorName(err),
});
std.process.exit(1);
},
};
const sub_builder = b.createChild(name, build_root, args) catch @panic("unhandled error");
sub_builder.runBuild(build_zig) catch @panic("unhandled error");
@ -1563,26 +1596,6 @@ pub fn runBuild(b: *Build, build_zig: anytype) anyerror!void {
}
}
test "builder.findProgram compiles" {
if (builtin.os.tag == .wasi) return error.SkipZigTest;
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const host = try NativeTargetInfo.detect(.{});
const builder = try Build.create(
arena.allocator(),
"zig",
"zig-cache",
"zig-cache",
"zig-cache",
host,
);
defer builder.destroy();
_ = builder.findProgram(&[_][]const u8{}, &[_][]const u8{}) catch null;
}
pub const Module = struct {
builder: *Build,
/// This could either be a generated file, in which case the module
@ -1611,7 +1624,6 @@ pub const GeneratedFile = struct {
};
/// A file source is a reference to an existing or future file.
///
pub const FileSource = union(enum) {
/// A plain file path, relative to build root or absolute.
path: []const u8,

View File

@ -2,6 +2,45 @@
//! This is not a general-purpose cache. It is designed to be fast and simple,
//! not to withstand attacks using specially-crafted input.
pub const Directory = struct {
/// This field is redundant for operations that can act on the open directory handle
/// directly, but it is needed when passing the directory to a child process.
/// `null` means cwd.
path: ?[]const u8,
handle: std.fs.Dir,
pub fn join(self: Directory, allocator: Allocator, paths: []const []const u8) ![]u8 {
if (self.path) |p| {
// TODO clean way to do this with only 1 allocation
const part2 = try std.fs.path.join(allocator, paths);
defer allocator.free(part2);
return std.fs.path.join(allocator, &[_][]const u8{ p, part2 });
} else {
return std.fs.path.join(allocator, paths);
}
}
pub fn joinZ(self: Directory, allocator: Allocator, paths: []const []const u8) ![:0]u8 {
if (self.path) |p| {
// TODO clean way to do this with only 1 allocation
const part2 = try std.fs.path.join(allocator, paths);
defer allocator.free(part2);
return std.fs.path.joinZ(allocator, &[_][]const u8{ p, part2 });
} else {
return std.fs.path.joinZ(allocator, paths);
}
}
/// Whether or not the handle should be closed, or the path should be freed
/// is determined by usage, however this function is provided for convenience
/// if it happens to be what the caller needs.
pub fn closeAndFree(self: *Directory, gpa: Allocator) void {
self.handle.close();
if (self.path) |p| gpa.free(p);
self.* = undefined;
}
};
gpa: Allocator,
manifest_dir: fs.Dir,
hash: HashHelper = .{},
@ -14,9 +53,11 @@ mutex: std.Thread.Mutex = .{},
/// are replaced with single-character indicators. This is not to save
/// space but to eliminate absolute file paths. This improves portability
/// and usefulness of the cache for advanced use cases.
prefixes_buffer: [3]Compilation.Directory = undefined,
prefixes_buffer: [4]Directory = undefined,
prefixes_len: usize = 0,
pub const DepTokenizer = @import("Cache/DepTokenizer.zig");
const Cache = @This();
const std = @import("std");
const builtin = @import("builtin");
@ -27,13 +68,9 @@ const testing = std.testing;
const mem = std.mem;
const fmt = std.fmt;
const Allocator = std.mem.Allocator;
const Compilation = @import("Compilation.zig");
const log = std.log.scoped(.cache);
pub fn addPrefix(cache: *Cache, directory: Compilation.Directory) void {
if (directory.path) |p| {
log.debug("Cache.addPrefix {d} {s}", .{ cache.prefixes_len, p });
}
pub fn addPrefix(cache: *Cache, directory: Directory) void {
cache.prefixes_buffer[cache.prefixes_len] = directory;
cache.prefixes_len += 1;
}
@ -49,7 +86,7 @@ pub fn obtain(cache: *Cache) Manifest {
};
}
pub fn prefixes(cache: *const Cache) []const Compilation.Directory {
pub fn prefixes(cache: *const Cache) []const Directory {
return cache.prefixes_buffer[0..cache.prefixes_len];
}
@ -80,8 +117,6 @@ fn findPrefixResolved(cache: *const Cache, resolved_path: []u8) !PrefixedPath {
.prefix = @intCast(u8, i),
.sub_path = sub_path,
};
} else {
log.debug("'{s}' does not start with '{s}'", .{ resolved_path, p });
}
}
@ -135,8 +170,6 @@ pub const File = struct {
pub const HashHelper = struct {
hasher: Hasher = hasher_init,
const EmitLoc = Compilation.EmitLoc;
/// Record a slice of bytes as an dependency of the process being cached
pub fn addBytes(hh: *HashHelper, bytes: []const u8) void {
hh.hasher.update(mem.asBytes(&bytes.len));
@ -148,15 +181,6 @@ pub const HashHelper = struct {
hh.addBytes(optional_bytes orelse return);
}
pub fn addEmitLoc(hh: *HashHelper, emit_loc: EmitLoc) void {
hh.addBytes(emit_loc.basename);
}
pub fn addOptionalEmitLoc(hh: *HashHelper, optional_emit_loc: ?EmitLoc) void {
hh.add(optional_emit_loc != null);
hh.addEmitLoc(optional_emit_loc orelse return);
}
pub fn addListOfBytes(hh: *HashHelper, list_of_bytes: []const []const u8) void {
hh.add(list_of_bytes.len);
for (list_of_bytes) |bytes| hh.addBytes(bytes);
@ -290,10 +314,6 @@ pub const Manifest = struct {
const prefixed_path = try self.cache.findPrefix(file_path);
errdefer gpa.free(prefixed_path.sub_path);
log.debug("Manifest.addFile {s} -> {d} {s}", .{
file_path, prefixed_path.prefix, prefixed_path.sub_path,
});
self.files.addOneAssumeCapacity().* = .{
.prefixed_path = prefixed_path,
.contents = null,
@ -308,24 +328,6 @@ pub const Manifest = struct {
return self.files.items.len - 1;
}
pub fn hashCSource(self: *Manifest, c_source: Compilation.CSourceFile) !void {
_ = try self.addFile(c_source.src_path, null);
// Hash the extra flags, with special care to call addFile for file parameters.
// TODO this logic can likely be improved by utilizing clang_options_data.zig.
const file_args = [_][]const u8{"-include"};
var arg_i: usize = 0;
while (arg_i < c_source.extra_flags.len) : (arg_i += 1) {
const arg = c_source.extra_flags[arg_i];
self.hash.addBytes(arg);
for (file_args) |file_arg| {
if (mem.eql(u8, file_arg, arg) and arg_i + 1 < c_source.extra_flags.len) {
arg_i += 1;
_ = try self.addFile(c_source.extra_flags[arg_i], null);
}
}
}
}
pub fn addOptionalFile(self: *Manifest, optional_file_path: ?[]const u8) !void {
self.hash.add(optional_file_path != null);
const file_path = optional_file_path orelse return;
@ -676,10 +678,6 @@ pub const Manifest = struct {
const prefixed_path = try self.cache.findPrefix(file_path);
errdefer gpa.free(prefixed_path.sub_path);
log.debug("Manifest.addFilePostFetch {s} -> {d} {s}", .{
file_path, prefixed_path.prefix, prefixed_path.sub_path,
});
const new_ch_file = try self.files.addOne(gpa);
new_ch_file.* = .{
.prefixed_path = prefixed_path,
@ -706,10 +704,6 @@ pub const Manifest = struct {
const prefixed_path = try self.cache.findPrefix(file_path);
errdefer gpa.free(prefixed_path.sub_path);
log.debug("Manifest.addFilePost {s} -> {d} {s}", .{
file_path, prefixed_path.prefix, prefixed_path.sub_path,
});
const new_ch_file = try self.files.addOne(gpa);
new_ch_file.* = .{
.prefixed_path = prefixed_path,
@ -737,15 +731,9 @@ pub const Manifest = struct {
const ch_file = try self.files.addOne(gpa);
errdefer self.files.shrinkRetainingCapacity(self.files.items.len - 1);
log.debug("Manifest.addFilePostContents resolved_path={s}", .{resolved_path});
const prefixed_path = try self.cache.findPrefixResolved(resolved_path);
errdefer gpa.free(prefixed_path.sub_path);
log.debug("Manifest.addFilePostContents -> {d} {s}", .{
prefixed_path.prefix, prefixed_path.sub_path,
});
ch_file.* = .{
.prefixed_path = prefixed_path,
.max_file_size = null,
@ -778,7 +766,7 @@ pub const Manifest = struct {
var error_buf = std.ArrayList(u8).init(self.cache.gpa);
defer error_buf.deinit();
var it: @import("DepTokenizer.zig") = .{ .bytes = dep_file_contents };
var it: DepTokenizer = .{ .bytes = dep_file_contents };
// Skip first token: target.
switch (it.next() orelse return) { // Empty dep file OK.

View File

@ -83,7 +83,7 @@ max_memory: ?u64 = null,
shared_memory: bool = false,
global_base: ?u64 = null,
c_std: std.Build.CStd,
override_lib_dir: ?[]const u8,
zig_lib_dir: ?[]const u8,
main_pkg_path: ?[]const u8,
exec_cmd_args: ?[]const ?[]const u8,
name_prefix: []const u8,
@ -344,7 +344,7 @@ pub fn create(builder: *std.Build, options: Options) *CompileStep {
.installed_headers = ArrayList(*Step).init(builder.allocator),
.object_src = undefined,
.c_std = std.Build.CStd.C99,
.override_lib_dir = null,
.zig_lib_dir = null,
.main_pkg_path = null,
.exec_cmd_args = null,
.name_prefix = "",
@ -442,6 +442,26 @@ pub fn installHeader(a: *CompileStep, src_path: []const u8, dest_rel_path: []con
a.installed_headers.append(&install_file.step) catch @panic("OOM");
}
pub const InstallConfigHeaderOptions = struct {
install_dir: InstallDir = .header,
dest_rel_path: ?[]const u8 = null,
};
pub fn installConfigHeader(
cs: *CompileStep,
config_header: *ConfigHeaderStep,
options: InstallConfigHeaderOptions,
) void {
const dest_rel_path = options.dest_rel_path orelse config_header.include_path;
const install_file = cs.builder.addInstallFileWithDir(
.{ .generated = &config_header.output_file },
options.install_dir,
dest_rel_path,
);
cs.builder.getInstallStep().dependOn(&install_file.step);
cs.installed_headers.append(&install_file.step) catch @panic("OOM");
}
pub fn installHeadersDirectory(
a: *CompileStep,
src_dir_path: []const u8,
@ -486,26 +506,11 @@ pub fn installLibraryHeaders(a: *CompileStep, l: *CompileStep) void {
a.installed_headers.appendSlice(l.installed_headers.items) catch @panic("OOM");
}
/// Creates a `RunStep` with an executable built with `addExecutable`.
/// Add command line arguments with `addArg`.
/// Deprecated: use `std.Build.addRunArtifact`
/// This function will run in the context of the package that created the executable,
/// which is undesirable when running an executable provided by a dependency package.
pub fn run(exe: *CompileStep) *RunStep {
assert(exe.kind == .exe or exe.kind == .test_exe);
// It doesn't have to be native. We catch that if you actually try to run it.
// Consider that this is declarative; the run step may not be run unless a user
// option is supplied.
const run_step = RunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}));
run_step.addArtifactArg(exe);
if (exe.kind == .test_exe) {
run_step.addArg(exe.builder.zig_exe);
}
if (exe.vcpkg_bin_path) |path| {
run_step.addPathDir(path);
}
return run_step;
return exe.builder.addRunArtifact(exe);
}
/// Creates an `EmulatableRunStep` with an executable built with `addExecutable`.
@ -852,7 +857,7 @@ pub fn setVerboseCC(self: *CompileStep, value: bool) void {
}
pub fn overrideZigLibDir(self: *CompileStep, dir_path: []const u8) void {
self.override_lib_dir = self.builder.dupePath(dir_path);
self.zig_lib_dir = self.builder.dupePath(dir_path);
}
pub fn setMainPkgPath(self: *CompileStep, dir_path: []const u8) void {
@ -1345,10 +1350,10 @@ fn make(step: *Step) !void {
}
try zig_args.append("--cache-dir");
try zig_args.append(builder.pathFromRoot(builder.cache_root));
try zig_args.append(builder.cache_root.path orelse ".");
try zig_args.append("--global-cache-dir");
try zig_args.append(builder.pathFromRoot(builder.global_cache_root));
try zig_args.append(builder.global_cache_root.path orelse ".");
try zig_args.append("--name");
try zig_args.append(self.name);
@ -1622,8 +1627,9 @@ fn make(step: *Step) !void {
}
},
.config_header_step => |config_header| {
try zig_args.append("-I");
try zig_args.append(config_header.output_dir);
const full_file_path = config_header.output_file.path.?;
const header_dir_path = full_file_path[0 .. full_file_path.len - config_header.include_path.len];
try zig_args.appendSlice(&.{ "-I", header_dir_path });
},
}
}
@ -1697,12 +1703,12 @@ fn make(step: *Step) !void {
try addFlag(&zig_args, "each-lib-rpath", self.each_lib_rpath);
try addFlag(&zig_args, "build-id", self.build_id);
if (self.override_lib_dir) |dir| {
if (self.zig_lib_dir) |dir| {
try zig_args.append("--zig-lib-dir");
try zig_args.append(builder.pathFromRoot(dir));
} else if (builder.override_lib_dir) |dir| {
} else if (builder.zig_lib_dir) |dir| {
try zig_args.append("--zig-lib-dir");
try zig_args.append(builder.pathFromRoot(dir));
try zig_args.append(dir);
}
if (self.main_pkg_path) |dir| {
@ -1739,23 +1745,15 @@ fn make(step: *Step) !void {
args_length += arg.len + 1; // +1 to account for null terminator
}
if (args_length >= 30 * 1024) {
const args_dir = try fs.path.join(
builder.allocator,
&[_][]const u8{ builder.pathFromRoot("zig-cache"), "args" },
);
try std.fs.cwd().makePath(args_dir);
var args_arena = std.heap.ArenaAllocator.init(builder.allocator);
defer args_arena.deinit();
try builder.cache_root.handle.makePath("args");
const args_to_escape = zig_args.items[2..];
var escaped_args = try ArrayList([]const u8).initCapacity(args_arena.allocator(), args_to_escape.len);
var escaped_args = try ArrayList([]const u8).initCapacity(builder.allocator, args_to_escape.len);
arg_blk: for (args_to_escape) |arg| {
for (arg) |c, arg_idx| {
if (c == '\\' or c == '"') {
// Slow path for arguments that need to be escaped. We'll need to allocate and copy
var escaped = try ArrayList(u8).initCapacity(args_arena.allocator(), arg.len + 1);
var escaped = try ArrayList(u8).initCapacity(builder.allocator, arg.len + 1);
const writer = escaped.writer();
try writer.writeAll(arg[0..arg_idx]);
for (arg[arg_idx..]) |to_escape| {
@ -1783,11 +1781,16 @@ fn make(step: *Step) !void {
.{std.fmt.fmtSliceHexLower(&args_hash)},
);
const args_file = try fs.path.join(builder.allocator, &[_][]const u8{ args_dir, args_hex_hash[0..] });
try std.fs.cwd().writeFile(args_file, args);
const args_file = "args" ++ fs.path.sep_str ++ args_hex_hash;
try builder.cache_root.handle.writeFile(args_file, args);
const resolved_args_file = try mem.concat(builder.allocator, u8, &.{
"@",
try builder.cache_root.join(builder.allocator, &.{args_file}),
});
zig_args.shrinkRetainingCapacity(2);
try zig_args.append(try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "@", args_file }));
try zig_args.append(resolved_args_file);
}
const output_dir_nl = try builder.execFromStep(zig_args.items, &self.step);

View File

@ -4,13 +4,24 @@ const Step = std.Build.Step;
pub const base_id: Step.Id = .config_header;
pub const Style = enum {
pub const Style = union(enum) {
/// The configure format supported by autotools. It uses `#undef foo` to
/// mark lines that can be substituted with different values.
autoconf,
autoconf: std.Build.FileSource,
/// The configure format supported by CMake. It uses `@@FOO@@` and
/// `#cmakedefine` for template substitution.
cmake,
cmake: std.Build.FileSource,
/// Instead of starting with an input file, start with nothing.
blank,
/// Start with nothing, like blank, and output a nasm .asm file.
nasm,
pub fn getFileSource(style: Style) ?std.Build.FileSource {
switch (style) {
.autoconf, .cmake => |s| return s,
.blank, .nasm => return null,
}
}
};
pub const Value = union(enum) {
@ -24,34 +35,50 @@ pub const Value = union(enum) {
step: Step,
builder: *std.Build,
source: std.Build.FileSource,
style: Style,
values: std.StringHashMap(Value),
max_bytes: usize = 2 * 1024 * 1024,
output_dir: []const u8,
output_basename: []const u8,
values: std.StringArrayHashMap(Value),
output_file: std.Build.GeneratedFile,
pub fn create(builder: *std.Build, source: std.Build.FileSource, style: Style) *ConfigHeaderStep {
style: Style,
max_bytes: usize,
include_path: []const u8,
pub const Options = struct {
style: Style = .blank,
max_bytes: usize = 2 * 1024 * 1024,
include_path: ?[]const u8 = null,
};
pub fn create(builder: *std.Build, options: Options) *ConfigHeaderStep {
const self = builder.allocator.create(ConfigHeaderStep) catch @panic("OOM");
const name = builder.fmt("configure header {s}", .{source.getDisplayName()});
const name = if (options.style.getFileSource()) |s|
builder.fmt("configure {s} header {s}", .{ @tagName(options.style), s.getDisplayName() })
else
builder.fmt("configure {s} header", .{@tagName(options.style)});
self.* = .{
.builder = builder,
.step = Step.init(base_id, name, builder.allocator, make),
.source = source,
.style = style,
.values = std.StringHashMap(Value).init(builder.allocator),
.output_dir = undefined,
.output_basename = "config.h",
.style = options.style,
.values = std.StringArrayHashMap(Value).init(builder.allocator),
.max_bytes = options.max_bytes,
.include_path = "config.h",
.output_file = .{ .step = &self.step },
};
switch (source) {
if (options.style.getFileSource()) |s| switch (s) {
.path => |p| {
const basename = std.fs.path.basename(p);
if (std.mem.endsWith(u8, basename, ".h.in")) {
self.output_basename = basename[0 .. basename.len - 3];
self.include_path = basename[0 .. basename.len - 3];
}
},
else => {},
};
if (options.include_path) |include_path| {
self.include_path = include_path;
}
return self;
}
@ -59,6 +86,10 @@ pub fn addValues(self: *ConfigHeaderStep, values: anytype) void {
return addValuesInner(self, values) catch @panic("OOM");
}
pub fn getFileSource(self: *ConfigHeaderStep) std.Build.FileSource {
return .{ .generated = &self.output_file };
}
fn addValuesInner(self: *ConfigHeaderStep, values: anytype) !void {
inline for (@typeInfo(@TypeOf(values)).Struct.fields) |field| {
try putValue(self, field.name, field.type, @field(values, field.name));
@ -100,6 +131,12 @@ fn putValue(self: *ConfigHeaderStep, field_name: []const u8, comptime T: type, v
return;
}
},
.Int => {
if (ptr.size == .Slice and ptr.child == u8) {
try self.values.put(field_name, .{ .string = v });
return;
}
},
else => {},
}
@ -112,8 +149,6 @@ fn putValue(self: *ConfigHeaderStep, field_name: []const u8, comptime T: type, v
fn make(step: *Step) !void {
const self = @fieldParentPtr(ConfigHeaderStep, "step", step);
const gpa = self.builder.allocator;
const src_path = self.source.getPath(self.builder);
const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes);
// The cache is used here not really as a way to speed things up - because writing
// the data to a file would probably be very fast - but as a way to find a canonical
@ -130,9 +165,39 @@ fn make(step: *Step) !void {
// Random bytes to make ConfigHeaderStep unique. Refresh this with new
// random bytes when ConfigHeaderStep implementation is modified in a
// non-backwards-compatible way.
var hash = Hasher.init("X1pQzdDt91Zlh7Eh");
hash.update(self.source.getDisplayName());
hash.update(contents);
var hash = Hasher.init("PGuDTpidxyMqnkGM");
var output = std.ArrayList(u8).init(gpa);
defer output.deinit();
const header_text = "This file was generated by ConfigHeaderStep using the Zig Build System.";
const c_generated_line = "/* " ++ header_text ++ " */\n";
const asm_generated_line = "; " ++ header_text ++ "\n";
switch (self.style) {
.autoconf => |file_source| {
try output.appendSlice(c_generated_line);
const src_path = file_source.getPath(self.builder);
const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes);
try render_autoconf(contents, &output, self.values, src_path);
},
.cmake => |file_source| {
try output.appendSlice(c_generated_line);
const src_path = file_source.getPath(self.builder);
const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes);
try render_cmake(contents, &output, self.values, src_path);
},
.blank => {
try output.appendSlice(c_generated_line);
try render_blank(&output, self.values, self.include_path);
},
.nasm => {
try output.appendSlice(asm_generated_line);
try render_nasm(&output, self.values);
},
}
hash.update(output.items);
var digest: [16]u8 = undefined;
hash.final(&digest);
@ -143,38 +208,40 @@ fn make(step: *Step) !void {
.{std.fmt.fmtSliceHexLower(&digest)},
) catch unreachable;
self.output_dir = try std.fs.path.join(gpa, &[_][]const u8{
self.builder.cache_root, "o", &hash_basename,
});
var dir = std.fs.cwd().makeOpenPath(self.output_dir, .{}) catch |err| {
std.debug.print("unable to make path {s}: {s}\n", .{ self.output_dir, @errorName(err) });
const output_dir = try self.builder.cache_root.join(gpa, &.{ "o", &hash_basename });
// If output_path has directory parts, deal with them. Example:
// output_dir is zig-cache/o/HASH
// output_path is libavutil/avconfig.h
// We want to open directory zig-cache/o/HASH/libavutil/
// but keep output_dir as zig-cache/o/HASH for -I include
const sub_dir_path = if (std.fs.path.dirname(self.include_path)) |d|
try std.fs.path.join(gpa, &.{ output_dir, d })
else
output_dir;
var dir = std.fs.cwd().makeOpenPath(sub_dir_path, .{}) catch |err| {
std.debug.print("unable to make path {s}: {s}\n", .{ output_dir, @errorName(err) });
return err;
};
defer dir.close();
var values_copy = try self.values.clone();
defer values_copy.deinit();
try dir.writeFile(std.fs.path.basename(self.include_path), output.items);
var output = std.ArrayList(u8).init(gpa);
defer output.deinit();
try output.ensureTotalCapacity(contents.len);
try output.appendSlice("/* This file was generated by ConfigHeaderStep using the Zig Build System. */\n");
switch (self.style) {
.autoconf => try render_autoconf(contents, &output, &values_copy, src_path),
.cmake => try render_cmake(contents, &output, &values_copy, src_path),
}
try dir.writeFile(self.output_basename, output.items);
self.output_file.path = try std.fs.path.join(self.builder.allocator, &.{
output_dir, self.include_path,
});
}
fn render_autoconf(
contents: []const u8,
output: *std.ArrayList(u8),
values_copy: *std.StringHashMap(Value),
values: std.StringArrayHashMap(Value),
src_path: []const u8,
) !void {
var values_copy = try values.clone();
defer values_copy.deinit();
var any_errors = false;
var line_index: u32 = 0;
var line_it = std.mem.split(u8, contents, "\n");
@ -192,22 +259,18 @@ fn render_autoconf(
continue;
}
const name = it.rest();
const kv = values_copy.fetchRemove(name) orelse {
const kv = values_copy.fetchSwapRemove(name) orelse {
std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{
src_path, line_index + 1, name,
});
any_errors = true;
continue;
};
try renderValue(output, name, kv.value);
try renderValueC(output, name, kv.value);
}
{
var it = values_copy.iterator();
while (it.next()) |entry| {
const name = entry.key_ptr.*;
std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name });
}
for (values_copy.keys()) |name| {
std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name });
}
if (any_errors) {
@ -218,9 +281,12 @@ fn render_autoconf(
fn render_cmake(
contents: []const u8,
output: *std.ArrayList(u8),
values_copy: *std.StringHashMap(Value),
values: std.StringArrayHashMap(Value),
src_path: []const u8,
) !void {
var values_copy = try values.clone();
defer values_copy.deinit();
var any_errors = false;
var line_index: u32 = 0;
var line_it = std.mem.split(u8, contents, "\n");
@ -244,22 +310,18 @@ fn render_cmake(
any_errors = true;
continue;
};
const kv = values_copy.fetchRemove(name) orelse {
const kv = values_copy.fetchSwapRemove(name) orelse {
std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{
src_path, line_index + 1, name,
});
any_errors = true;
continue;
};
try renderValue(output, name, kv.value);
try renderValueC(output, name, kv.value);
}
{
var it = values_copy.iterator();
while (it.next()) |entry| {
const name = entry.key_ptr.*;
std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name });
}
for (values_copy.keys()) |name| {
std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name });
}
if (any_errors) {
@ -267,7 +329,44 @@ fn render_cmake(
}
}
fn renderValue(output: *std.ArrayList(u8), name: []const u8, value: Value) !void {
fn render_blank(
output: *std.ArrayList(u8),
defines: std.StringArrayHashMap(Value),
include_path: []const u8,
) !void {
const include_guard_name = try output.allocator.dupe(u8, include_path);
for (include_guard_name) |*byte| {
switch (byte.*) {
'a'...'z' => byte.* = byte.* - 'a' + 'A',
'A'...'Z', '0'...'9' => continue,
else => byte.* = '_',
}
}
try output.appendSlice("#ifndef ");
try output.appendSlice(include_guard_name);
try output.appendSlice("\n#define ");
try output.appendSlice(include_guard_name);
try output.appendSlice("\n");
const values = defines.values();
for (defines.keys()) |name, i| {
try renderValueC(output, name, values[i]);
}
try output.appendSlice("#endif /* ");
try output.appendSlice(include_guard_name);
try output.appendSlice(" */\n");
}
fn render_nasm(output: *std.ArrayList(u8), defines: std.StringArrayHashMap(Value)) !void {
const values = defines.values();
for (defines.keys()) |name, i| {
try renderValueNasm(output, name, values[i]);
}
}
fn renderValueC(output: *std.ArrayList(u8), name: []const u8, value: Value) !void {
switch (value) {
.undef => {
try output.appendSlice("/* #undef ");
@ -297,3 +396,33 @@ fn renderValue(output: *std.ArrayList(u8), name: []const u8, value: Value) !void
},
}
}
fn renderValueNasm(output: *std.ArrayList(u8), name: []const u8, value: Value) !void {
switch (value) {
.undef => {
try output.appendSlice("; %undef ");
try output.appendSlice(name);
try output.appendSlice("\n");
},
.defined => {
try output.appendSlice("%define ");
try output.appendSlice(name);
try output.appendSlice("\n");
},
.boolean => |b| {
try output.appendSlice("%define ");
try output.appendSlice(name);
try output.appendSlice(if (b) " 1\n" else " 0\n");
},
.int => |i| {
try output.writer().print("%define {s} {d}\n", .{ name, i });
},
.ident => |ident| {
try output.writer().print("%define {s} {s}\n", .{ name, ident });
},
.string => |string| {
// TODO: use nasm-specific escaping instead of zig string literals
try output.writer().print("%define {s} \"{}\"\n", .{ name, std.zig.fmtEscapes(string) });
},
}
}

View File

@ -234,26 +234,20 @@ fn make(step: *Step) !void {
);
}
const options_directory = self.builder.pathFromRoot(
try fs.path.join(
self.builder.allocator,
&[_][]const u8{ self.builder.cache_root, "options" },
),
);
var options_dir = try self.builder.cache_root.handle.makeOpenPath("options", .{});
defer options_dir.close();
try fs.cwd().makePath(options_directory);
const basename = self.hashContentsToFileName();
const options_file = try fs.path.join(
self.builder.allocator,
&[_][]const u8{ options_directory, &self.hashContentsToFileName() },
);
try options_dir.writeFile(&basename, self.contents.items);
try fs.cwd().writeFile(options_file, self.contents.items);
self.generated_file.path = options_file;
self.generated_file.path = try self.builder.cache_root.join(self.builder.allocator, &.{
"options", &basename,
});
}
fn hashContentsToFileName(self: *OptionsStep) [64]u8 {
// TODO update to use the cache system instead of this
// This implementation is copied from `WriteFileStep.make`
var hash = std.crypto.hash.blake2.Blake2b384.init(.{});
@ -289,13 +283,19 @@ test "OptionsStep" {
const host = try std.zig.system.NativeTargetInfo.detect(.{});
var cache: std.Build.Cache = .{
.gpa = arena.allocator(),
.manifest_dir = std.fs.cwd(),
};
var builder = try std.Build.create(
arena.allocator(),
"test",
"test",
"test",
"test",
.{ .path = "test", .handle = std.fs.cwd() },
.{ .path = "test", .handle = std.fs.cwd() },
.{ .path = "test", .handle = std.fs.cwd() },
host,
&cache,
);
defer builder.destroy();

View File

@ -39,6 +39,14 @@ expected_exit_code: ?u8 = 0,
/// Print the command before running it
print: bool,
/// Controls whether execution is skipped if the output file is up-to-date.
/// The default is to always run if there is no output file, and to skip
/// running if all output files are up-to-date.
condition: enum { output_outdated, always } = .output_outdated,
/// Additional file paths relative to build.zig that, when modified, indicate
/// that the RunStep should be re-executed.
extra_file_dependencies: []const []const u8 = &.{},
pub const StdIoAction = union(enum) {
inherit,
@ -51,6 +59,12 @@ pub const Arg = union(enum) {
artifact: *CompileStep,
file_source: std.Build.FileSource,
bytes: []u8,
output: Output,
pub const Output = struct {
generated_file: *std.Build.GeneratedFile,
basename: []const u8,
};
};
pub fn create(builder: *std.Build, name: []const u8) *RunStep {
@ -71,6 +85,20 @@ pub fn addArtifactArg(self: *RunStep, artifact: *CompileStep) void {
self.step.dependOn(&artifact.step);
}
/// This provides file path as a command line argument to the command being
/// run, and returns a FileSource which can be used as inputs to other APIs
/// throughout the build system.
pub fn addOutputFileArg(rs: *RunStep, basename: []const u8) std.Build.FileSource {
const generated_file = rs.builder.allocator.create(std.Build.GeneratedFile) catch @panic("OOM");
generated_file.* = .{ .step = &rs.step };
rs.argv.append(.{ .output = .{
.generated_file = generated_file,
.basename = rs.builder.dupe(basename),
} }) catch @panic("OOM");
return .{ .generated = generated_file };
}
pub fn addFileSourceArg(self: *RunStep, file_source: std.Build.FileSource) void {
self.argv.append(Arg{
.file_source = file_source.dupe(self.builder),
@ -159,22 +187,102 @@ fn stdIoActionToBehavior(action: StdIoAction) std.ChildProcess.StdIo {
};
}
fn needOutputCheck(self: RunStep) bool {
if (self.extra_file_dependencies.len > 0) return true;
for (self.argv.items) |arg| switch (arg) {
.output => return true,
else => continue,
};
return switch (self.condition) {
.always => false,
.output_outdated => true,
};
}
fn make(step: *Step) !void {
const self = @fieldParentPtr(RunStep, "step", step);
const need_output_check = self.needOutputCheck();
var argv_list = ArrayList([]const u8).init(self.builder.allocator);
var output_placeholders = ArrayList(struct {
index: usize,
output: Arg.Output,
}).init(self.builder.allocator);
var man = self.builder.cache.obtain();
defer man.deinit();
for (self.argv.items) |arg| {
switch (arg) {
.bytes => |bytes| try argv_list.append(bytes),
.file_source => |file| try argv_list.append(file.getPath(self.builder)),
.bytes => |bytes| {
try argv_list.append(bytes);
man.hash.addBytes(bytes);
},
.file_source => |file| {
const file_path = file.getPath(self.builder);
try argv_list.append(file_path);
_ = try man.addFile(file_path, null);
},
.artifact => |artifact| {
if (artifact.target.isWindows()) {
// On Windows we don't have rpaths so we have to add .dll search paths to PATH
self.addPathForDynLibs(artifact);
}
const executable_path = artifact.installed_path orelse artifact.getOutputSource().getPath(self.builder);
try argv_list.append(executable_path);
const file_path = artifact.installed_path orelse
artifact.getOutputSource().getPath(self.builder);
try argv_list.append(file_path);
_ = try man.addFile(file_path, null);
},
.output => |output| {
man.hash.addBytes(output.basename);
// Add a placeholder into the argument list because we need the
// manifest hash to be updated with all arguments before the
// object directory is computed.
try argv_list.append("");
try output_placeholders.append(.{
.index = argv_list.items.len - 1,
.output = output,
});
},
}
}
if (need_output_check) {
for (self.extra_file_dependencies) |file_path| {
_ = try man.addFile(self.builder.pathFromRoot(file_path), null);
}
if (man.hit() catch |err| failWithCacheError(man, err)) {
// cache hit, skip running command
const digest = man.final();
for (output_placeholders.items) |placeholder| {
placeholder.output.generated_file.path = try self.builder.cache_root.join(
self.builder.allocator,
&.{ "o", &digest, placeholder.output.basename },
);
}
return;
}
const digest = man.final();
for (output_placeholders.items) |placeholder| {
const output_path = try self.builder.cache_root.join(
self.builder.allocator,
&.{ "o", &digest, placeholder.output.basename },
);
const output_dir = fs.path.dirname(output_path).?;
fs.cwd().makePath(output_dir) catch |err| {
std.debug.print("unable to make path {s}: {s}\n", .{ output_dir, @errorName(err) });
return err;
};
placeholder.output.generated_file.path = output_path;
argv_list.items[placeholder.index] = output_path;
}
}
@ -189,6 +297,10 @@ fn make(step: *Step) !void {
self.cwd,
self.print,
);
if (need_output_check) {
try man.writeManifest();
}
}
pub fn runCommand(
@ -202,11 +314,13 @@ pub fn runCommand(
maybe_cwd: ?[]const u8,
print: bool,
) !void {
const cwd = if (maybe_cwd) |cwd| builder.pathFromRoot(cwd) else builder.build_root;
const cwd = if (maybe_cwd) |cwd| builder.pathFromRoot(cwd) else builder.build_root.path;
if (!std.process.can_spawn) {
const cmd = try std.mem.join(builder.allocator, " ", argv);
std.debug.print("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(builtin.os.tag), cmd });
std.debug.print("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{
@tagName(builtin.os.tag), cmd,
});
builder.allocator.free(cmd);
return ExecError.ExecNotSupported;
}
@ -347,6 +461,19 @@ pub fn runCommand(
}
}
fn failWithCacheError(man: std.Build.Cache.Manifest, err: anyerror) noreturn {
const i = man.failed_file_index orelse failWithSimpleError(err);
const pp = man.files.items[i].prefixed_path orelse failWithSimpleError(err);
const prefix = man.cache.prefixes()[pp.prefix].path orelse "";
std.debug.print("{s}: {s}/{s}\n", .{ @errorName(err), prefix, pp.sub_path });
std.process.exit(1);
}
fn failWithSimpleError(err: anyerror) noreturn {
std.debug.print("{s}\n", .{@errorName(err)});
std.process.exit(1);
}
fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void {
if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd});
for (argv) |arg| {

View File

@ -15,7 +15,6 @@ builder: *std.Build,
source: std.Build.FileSource,
include_dirs: std.ArrayList([]const u8),
c_macros: std.ArrayList([]const u8),
output_dir: ?[]const u8,
out_basename: []const u8,
target: CrossTarget,
optimize: std.builtin.OptimizeMode,
@ -36,7 +35,6 @@ pub fn create(builder: *std.Build, options: Options) *TranslateCStep {
.source = source,
.include_dirs = std.ArrayList([]const u8).init(builder.allocator),
.c_macros = std.ArrayList([]const u8).init(builder.allocator),
.output_dir = null,
.out_basename = undefined,
.target = options.target,
.optimize = options.optimize,
@ -122,15 +120,10 @@ fn make(step: *Step) !void {
const output_path = mem.trimRight(u8, output_path_nl, "\r\n");
self.out_basename = fs.path.basename(output_path);
if (self.output_dir) |output_dir| {
const full_dest = try fs.path.join(self.builder.allocator, &[_][]const u8{ output_dir, self.out_basename });
try self.builder.updateFile(output_path, full_dest);
} else {
self.output_dir = fs.path.dirname(output_path).?;
}
const output_dir = fs.path.dirname(output_path).?;
self.output_file.path = try fs.path.join(
self.builder.allocator,
&[_][]const u8{ self.output_dir.?, self.out_basename },
&[_][]const u8{ output_dir, self.out_basename },
);
}

View File

@ -9,7 +9,6 @@ pub const base_id = .write_file;
step: Step,
builder: *std.Build,
output_dir: []const u8,
files: std.TailQueue(File),
pub const File = struct {
@ -23,7 +22,6 @@ pub fn init(builder: *std.Build) WriteFileStep {
.builder = builder,
.step = Step.init(.write_file, "writefile", builder.allocator, make),
.files = .{},
.output_dir = undefined,
};
}
@ -87,11 +85,11 @@ fn make(step: *Step) !void {
.{std.fmt.fmtSliceHexLower(&digest)},
) catch unreachable;
self.output_dir = try fs.path.join(self.builder.allocator, &[_][]const u8{
self.builder.cache_root, "o", &hash_basename,
const output_dir = try self.builder.cache_root.join(self.builder.allocator, &.{
"o", &hash_basename,
});
var dir = fs.cwd().makeOpenPath(self.output_dir, .{}) catch |err| {
std.debug.print("unable to make path {s}: {s}\n", .{ self.output_dir, @errorName(err) });
var dir = fs.cwd().makeOpenPath(output_dir, .{}) catch |err| {
std.debug.print("unable to make path {s}: {s}\n", .{ output_dir, @errorName(err) });
return err;
};
defer dir.close();
@ -101,14 +99,14 @@ fn make(step: *Step) !void {
dir.writeFile(node.data.basename, node.data.bytes) catch |err| {
std.debug.print("unable to write {s} into {s}: {s}\n", .{
node.data.basename,
self.output_dir,
output_dir,
@errorName(err),
});
return err;
};
node.data.source.path = try fs.path.join(
self.builder.allocator,
&[_][]const u8{ self.output_dir, node.data.basename },
&[_][]const u8{ output_dir, node.data.basename },
);
}
}

View File

@ -173,6 +173,7 @@ pub extern "c" fn readlink(noalias path: [*:0]const u8, noalias buf: [*]u8, bufs
pub extern "c" fn readlinkat(dirfd: c.fd_t, noalias path: [*:0]const u8, noalias buf: [*]u8, bufsize: usize) isize;
pub extern "c" fn fchmod(fd: c.fd_t, mode: c.mode_t) c_int;
pub extern "c" fn fchown(fd: c.fd_t, owner: c.uid_t, group: c.gid_t) c_int;
pub extern "c" fn umask(mode: c.mode_t) c.mode_t;
pub extern "c" fn rmdir(path: [*:0]const u8) c_int;
pub extern "c" fn getenv(name: [*:0]const u8) ?[*:0]u8;

View File

@ -1164,7 +1164,7 @@ fn windowsCreateProcessPathExt(
var app_name_unicode_string = windows.UNICODE_STRING{
.Length = app_name_len_bytes,
.MaximumLength = app_name_len_bytes,
.Buffer = @qualCast([*:0]u16, app_name_wildcard.ptr),
.Buffer = @constCast(app_name_wildcard.ptr),
};
const rc = windows.ntdll.NtQueryDirectoryFile(
dir.fd,
@ -1261,7 +1261,7 @@ fn windowsCreateProcessPathExt(
var app_name_unicode_string = windows.UNICODE_STRING{
.Length = app_name_len_bytes,
.MaximumLength = app_name_len_bytes,
.Buffer = @qualCast([*:0]u16, app_name_appended.ptr),
.Buffer = @constCast(app_name_appended.ptr),
};
// Re-use the directory handle but this time we call with the appended app name

View File

@ -2,8 +2,10 @@ const std = @import("std.zig");
pub const deflate = @import("compress/deflate.zig");
pub const gzip = @import("compress/gzip.zig");
pub const zlib = @import("compress/zlib.zig");
pub const lzma = @import("compress/lzma.zig");
pub const lzma2 = @import("compress/lzma2.zig");
pub const xz = @import("compress/xz.zig");
pub const zlib = @import("compress/zlib.zig");
pub fn HashedReader(
comptime ReaderType: anytype,
@ -38,6 +40,8 @@ pub fn hashedReader(
test {
_ = deflate;
_ = gzip;
_ = zlib;
_ = lzma;
_ = lzma2;
_ = xz;
_ = zlib;
}

90
lib/std/compress/lzma.zig Normal file
View File

@ -0,0 +1,90 @@
const std = @import("../std.zig");
const math = std.math;
const mem = std.mem;
const Allocator = std.mem.Allocator;
pub const decode = @import("lzma/decode.zig");
pub fn decompress(
allocator: Allocator,
reader: anytype,
) !Decompress(@TypeOf(reader)) {
return decompressWithOptions(allocator, reader, .{});
}
pub fn decompressWithOptions(
allocator: Allocator,
reader: anytype,
options: decode.Options,
) !Decompress(@TypeOf(reader)) {
const params = try decode.Params.readHeader(reader, options);
return Decompress(@TypeOf(reader)).init(allocator, reader, params, options.memlimit);
}
pub fn Decompress(comptime ReaderType: type) type {
return struct {
const Self = @This();
pub const Error =
ReaderType.Error ||
Allocator.Error ||
error{ CorruptInput, EndOfStream, Overflow };
pub const Reader = std.io.Reader(*Self, Error, read);
allocator: Allocator,
in_reader: ReaderType,
to_read: std.ArrayListUnmanaged(u8),
buffer: decode.lzbuffer.LzCircularBuffer,
decoder: decode.rangecoder.RangeDecoder,
state: decode.DecoderState,
pub fn init(allocator: Allocator, source: ReaderType, params: decode.Params, memlimit: ?usize) !Self {
return Self{
.allocator = allocator,
.in_reader = source,
.to_read = .{},
.buffer = decode.lzbuffer.LzCircularBuffer.init(params.dict_size, memlimit orelse math.maxInt(usize)),
.decoder = try decode.rangecoder.RangeDecoder.init(source),
.state = try decode.DecoderState.init(allocator, params.properties, params.unpacked_size),
};
}
pub fn reader(self: *Self) Reader {
return .{ .context = self };
}
pub fn deinit(self: *Self) void {
self.to_read.deinit(self.allocator);
self.buffer.deinit(self.allocator);
self.state.deinit(self.allocator);
self.* = undefined;
}
pub fn read(self: *Self, output: []u8) Error!usize {
const writer = self.to_read.writer(self.allocator);
while (self.to_read.items.len < output.len) {
switch (try self.state.process(self.allocator, self.in_reader, writer, &self.buffer, &self.decoder)) {
.continue_ => {},
.finished => {
try self.buffer.finish(writer);
break;
},
}
}
const input = self.to_read.items;
const n = math.min(input.len, output.len);
mem.copy(u8, output[0..n], input[0..n]);
mem.copy(u8, input, input[n..]);
self.to_read.shrinkRetainingCapacity(input.len - n);
return n;
}
};
}
test {
_ = @import("lzma/test.zig");
_ = @import("lzma/vec2d.zig");
}

View File

@ -0,0 +1,379 @@
const std = @import("../../std.zig");
const assert = std.debug.assert;
const math = std.math;
const Allocator = std.mem.Allocator;
pub const lzbuffer = @import("decode/lzbuffer.zig");
pub const rangecoder = @import("decode/rangecoder.zig");
const LzCircularBuffer = lzbuffer.LzCircularBuffer;
const BitTree = rangecoder.BitTree;
const LenDecoder = rangecoder.LenDecoder;
const RangeDecoder = rangecoder.RangeDecoder;
const Vec2D = @import("vec2d.zig").Vec2D;
pub const Options = struct {
unpacked_size: UnpackedSize = .read_from_header,
memlimit: ?usize = null,
allow_incomplete: bool = false,
};
pub const UnpackedSize = union(enum) {
read_from_header,
read_header_but_use_provided: ?u64,
use_provided: ?u64,
};
const ProcessingStatus = enum {
continue_,
finished,
};
pub const Properties = struct {
lc: u4,
lp: u3,
pb: u3,
fn validate(self: Properties) void {
assert(self.lc <= 8);
assert(self.lp <= 4);
assert(self.pb <= 4);
}
};
pub const Params = struct {
properties: Properties,
dict_size: u32,
unpacked_size: ?u64,
pub fn readHeader(reader: anytype, options: Options) !Params {
var props = try reader.readByte();
if (props >= 225) {
return error.CorruptInput;
}
const lc = @intCast(u4, props % 9);
props /= 9;
const lp = @intCast(u3, props % 5);
props /= 5;
const pb = @intCast(u3, props);
const dict_size_provided = try reader.readIntLittle(u32);
const dict_size = math.max(0x1000, dict_size_provided);
const unpacked_size = switch (options.unpacked_size) {
.read_from_header => blk: {
const unpacked_size_provided = try reader.readIntLittle(u64);
const marker_mandatory = unpacked_size_provided == 0xFFFF_FFFF_FFFF_FFFF;
break :blk if (marker_mandatory)
null
else
unpacked_size_provided;
},
.read_header_but_use_provided => |x| blk: {
_ = try reader.readIntLittle(u64);
break :blk x;
},
.use_provided => |x| x,
};
return Params{
.properties = Properties{ .lc = lc, .lp = lp, .pb = pb },
.dict_size = dict_size,
.unpacked_size = unpacked_size,
};
}
};
pub const DecoderState = struct {
lzma_props: Properties,
unpacked_size: ?u64,
literal_probs: Vec2D(u16),
pos_slot_decoder: [4]BitTree(6),
align_decoder: BitTree(4),
pos_decoders: [115]u16,
is_match: [192]u16,
is_rep: [12]u16,
is_rep_g0: [12]u16,
is_rep_g1: [12]u16,
is_rep_g2: [12]u16,
is_rep_0long: [192]u16,
state: usize,
rep: [4]usize,
len_decoder: LenDecoder,
rep_len_decoder: LenDecoder,
pub fn init(
allocator: Allocator,
lzma_props: Properties,
unpacked_size: ?u64,
) !DecoderState {
return .{
.lzma_props = lzma_props,
.unpacked_size = unpacked_size,
.literal_probs = try Vec2D(u16).init(allocator, 0x400, .{ @as(usize, 1) << (lzma_props.lc + lzma_props.lp), 0x300 }),
.pos_slot_decoder = .{.{}} ** 4,
.align_decoder = .{},
.pos_decoders = .{0x400} ** 115,
.is_match = .{0x400} ** 192,
.is_rep = .{0x400} ** 12,
.is_rep_g0 = .{0x400} ** 12,
.is_rep_g1 = .{0x400} ** 12,
.is_rep_g2 = .{0x400} ** 12,
.is_rep_0long = .{0x400} ** 192,
.state = 0,
.rep = .{0} ** 4,
.len_decoder = .{},
.rep_len_decoder = .{},
};
}
pub fn deinit(self: *DecoderState, allocator: Allocator) void {
self.literal_probs.deinit(allocator);
self.* = undefined;
}
pub fn resetState(self: *DecoderState, allocator: Allocator, new_props: Properties) !void {
new_props.validate();
if (self.lzma_props.lc + self.lzma_props.lp == new_props.lc + new_props.lp) {
self.literal_probs.fill(0x400);
} else {
self.literal_probs.deinit(allocator);
self.literal_probs = try Vec2D(u16).init(allocator, 0x400, .{ @as(usize, 1) << (new_props.lc + new_props.lp), 0x300 });
}
self.lzma_props = new_props;
for (self.pos_slot_decoder) |*t| t.reset();
self.align_decoder.reset();
self.pos_decoders = .{0x400} ** 115;
self.is_match = .{0x400} ** 192;
self.is_rep = .{0x400} ** 12;
self.is_rep_g0 = .{0x400} ** 12;
self.is_rep_g1 = .{0x400} ** 12;
self.is_rep_g2 = .{0x400} ** 12;
self.is_rep_0long = .{0x400} ** 192;
self.state = 0;
self.rep = .{0} ** 4;
self.len_decoder.reset();
self.rep_len_decoder.reset();
}
fn processNextInner(
self: *DecoderState,
allocator: Allocator,
reader: anytype,
writer: anytype,
buffer: anytype,
decoder: *RangeDecoder,
update: bool,
) !ProcessingStatus {
const pos_state = buffer.len & ((@as(usize, 1) << self.lzma_props.pb) - 1);
if (!try decoder.decodeBit(
reader,
&self.is_match[(self.state << 4) + pos_state],
update,
)) {
const byte: u8 = try self.decodeLiteral(reader, buffer, decoder, update);
if (update) {
try buffer.appendLiteral(allocator, byte, writer);
self.state = if (self.state < 4)
0
else if (self.state < 10)
self.state - 3
else
self.state - 6;
}
return .continue_;
}
var len: usize = undefined;
if (try decoder.decodeBit(reader, &self.is_rep[self.state], update)) {
if (!try decoder.decodeBit(reader, &self.is_rep_g0[self.state], update)) {
if (!try decoder.decodeBit(
reader,
&self.is_rep_0long[(self.state << 4) + pos_state],
update,
)) {
if (update) {
self.state = if (self.state < 7) 9 else 11;
const dist = self.rep[0] + 1;
try buffer.appendLz(allocator, 1, dist, writer);
}
return .continue_;
}
} else {
const idx: usize = if (!try decoder.decodeBit(reader, &self.is_rep_g1[self.state], update))
1
else if (!try decoder.decodeBit(reader, &self.is_rep_g2[self.state], update))
2
else
3;
if (update) {
const dist = self.rep[idx];
var i = idx;
while (i > 0) : (i -= 1) {
self.rep[i] = self.rep[i - 1];
}
self.rep[0] = dist;
}
}
len = try self.rep_len_decoder.decode(reader, decoder, pos_state, update);
if (update) {
self.state = if (self.state < 7) 8 else 11;
}
} else {
if (update) {
self.rep[3] = self.rep[2];
self.rep[2] = self.rep[1];
self.rep[1] = self.rep[0];
}
len = try self.len_decoder.decode(reader, decoder, pos_state, update);
if (update) {
self.state = if (self.state < 7) 7 else 10;
}
const rep_0 = try self.decodeDistance(reader, decoder, len, update);
if (update) {
self.rep[0] = rep_0;
if (self.rep[0] == 0xFFFF_FFFF) {
if (decoder.isFinished()) {
return .finished;
}
return error.CorruptInput;
}
}
}
if (update) {
len += 2;
const dist = self.rep[0] + 1;
try buffer.appendLz(allocator, len, dist, writer);
}
return .continue_;
}
fn processNext(
self: *DecoderState,
allocator: Allocator,
reader: anytype,
writer: anytype,
buffer: anytype,
decoder: *RangeDecoder,
) !ProcessingStatus {
return self.processNextInner(allocator, reader, writer, buffer, decoder, true);
}
pub fn process(
self: *DecoderState,
allocator: Allocator,
reader: anytype,
writer: anytype,
buffer: anytype,
decoder: *RangeDecoder,
) !ProcessingStatus {
process_next: {
if (self.unpacked_size) |unpacked_size| {
if (buffer.len >= unpacked_size) {
break :process_next;
}
} else if (decoder.isFinished()) {
break :process_next;
}
switch (try self.processNext(allocator, reader, writer, buffer, decoder)) {
.continue_ => return .continue_,
.finished => break :process_next,
}
}
if (self.unpacked_size) |unpacked_size| {
if (buffer.len != unpacked_size) {
return error.CorruptInput;
}
}
return .finished;
}
fn decodeLiteral(
self: *DecoderState,
reader: anytype,
buffer: anytype,
decoder: *RangeDecoder,
update: bool,
) !u8 {
const def_prev_byte = 0;
const prev_byte = @as(usize, buffer.lastOr(def_prev_byte));
var result: usize = 1;
const lit_state = ((buffer.len & ((@as(usize, 1) << self.lzma_props.lp) - 1)) << self.lzma_props.lc) +
(prev_byte >> (8 - self.lzma_props.lc));
const probs = try self.literal_probs.getMut(lit_state);
if (self.state >= 7) {
var match_byte = @as(usize, try buffer.lastN(self.rep[0] + 1));
while (result < 0x100) {
const match_bit = (match_byte >> 7) & 1;
match_byte <<= 1;
const bit = @boolToInt(try decoder.decodeBit(
reader,
&probs[((@as(usize, 1) + match_bit) << 8) + result],
update,
));
result = (result << 1) ^ bit;
if (match_bit != bit) {
break;
}
}
}
while (result < 0x100) {
result = (result << 1) ^ @boolToInt(try decoder.decodeBit(reader, &probs[result], update));
}
return @truncate(u8, result - 0x100);
}
fn decodeDistance(
self: *DecoderState,
reader: anytype,
decoder: *RangeDecoder,
length: usize,
update: bool,
) !usize {
const len_state = if (length > 3) 3 else length;
const pos_slot = @as(usize, try self.pos_slot_decoder[len_state].parse(reader, decoder, update));
if (pos_slot < 4)
return pos_slot;
const num_direct_bits = @intCast(u5, (pos_slot >> 1) - 1);
var result = (2 ^ (pos_slot & 1)) << num_direct_bits;
if (pos_slot < 14) {
result += try decoder.parseReverseBitTree(
reader,
num_direct_bits,
&self.pos_decoders,
result - pos_slot,
update,
);
} else {
result += @as(usize, try decoder.get(reader, num_direct_bits - 4)) << 4;
result += try self.align_decoder.parseReverse(reader, decoder, update);
}
return result;
}
};

View File

@ -0,0 +1,228 @@
const std = @import("../../../std.zig");
const math = std.math;
const mem = std.mem;
const Allocator = std.mem.Allocator;
const ArrayListUnmanaged = std.ArrayListUnmanaged;
/// An accumulating buffer for LZ sequences
pub const LzAccumBuffer = struct {
/// Buffer
buf: ArrayListUnmanaged(u8),
/// Buffer memory limit
memlimit: usize,
/// Total number of bytes sent through the buffer
len: usize,
const Self = @This();
pub fn init(memlimit: usize) Self {
return Self{
.buf = .{},
.memlimit = memlimit,
.len = 0,
};
}
pub fn appendByte(self: *Self, allocator: Allocator, byte: u8) !void {
try self.buf.append(allocator, byte);
self.len += 1;
}
/// Reset the internal dictionary
pub fn reset(self: *Self, writer: anytype) !void {
try writer.writeAll(self.buf.items);
self.buf.clearRetainingCapacity();
self.len = 0;
}
/// Retrieve the last byte or return a default
pub fn lastOr(self: Self, lit: u8) u8 {
const buf_len = self.buf.items.len;
return if (buf_len == 0)
lit
else
self.buf.items[buf_len - 1];
}
/// Retrieve the n-th last byte
pub fn lastN(self: Self, dist: usize) !u8 {
const buf_len = self.buf.items.len;
if (dist > buf_len) {
return error.CorruptInput;
}
return self.buf.items[buf_len - dist];
}
/// Append a literal
pub fn appendLiteral(
self: *Self,
allocator: Allocator,
lit: u8,
writer: anytype,
) !void {
_ = writer;
if (self.len >= self.memlimit) {
return error.CorruptInput;
}
try self.buf.append(allocator, lit);
self.len += 1;
}
/// Fetch an LZ sequence (length, distance) from inside the buffer
pub fn appendLz(
self: *Self,
allocator: Allocator,
len: usize,
dist: usize,
writer: anytype,
) !void {
_ = writer;
const buf_len = self.buf.items.len;
if (dist > buf_len) {
return error.CorruptInput;
}
var offset = buf_len - dist;
var i: usize = 0;
while (i < len) : (i += 1) {
const x = self.buf.items[offset];
try self.buf.append(allocator, x);
offset += 1;
}
self.len += len;
}
pub fn finish(self: *Self, writer: anytype) !void {
try writer.writeAll(self.buf.items);
self.buf.clearRetainingCapacity();
}
pub fn deinit(self: *Self, allocator: Allocator) void {
self.buf.deinit(allocator);
self.* = undefined;
}
};
/// A circular buffer for LZ sequences
pub const LzCircularBuffer = struct {
/// Circular buffer
buf: ArrayListUnmanaged(u8),
/// Length of the buffer
dict_size: usize,
/// Buffer memory limit
memlimit: usize,
/// Current position
cursor: usize,
/// Total number of bytes sent through the buffer
len: usize,
const Self = @This();
pub fn init(dict_size: usize, memlimit: usize) Self {
return Self{
.buf = .{},
.dict_size = dict_size,
.memlimit = memlimit,
.cursor = 0,
.len = 0,
};
}
pub fn get(self: Self, index: usize) u8 {
return if (0 <= index and index < self.buf.items.len)
self.buf.items[index]
else
0;
}
pub fn set(self: *Self, allocator: Allocator, index: usize, value: u8) !void {
if (index >= self.memlimit) {
return error.CorruptInput;
}
try self.buf.ensureTotalCapacity(allocator, index + 1);
while (self.buf.items.len < index) {
self.buf.appendAssumeCapacity(0);
}
self.buf.appendAssumeCapacity(value);
}
/// Retrieve the last byte or return a default
pub fn lastOr(self: Self, lit: u8) u8 {
return if (self.len == 0)
lit
else
self.get((self.dict_size + self.cursor - 1) % self.dict_size);
}
/// Retrieve the n-th last byte
pub fn lastN(self: Self, dist: usize) !u8 {
if (dist > self.dict_size or dist > self.len) {
return error.CorruptInput;
}
const offset = (self.dict_size + self.cursor - dist) % self.dict_size;
return self.get(offset);
}
/// Append a literal
pub fn appendLiteral(
self: *Self,
allocator: Allocator,
lit: u8,
writer: anytype,
) !void {
try self.set(allocator, self.cursor, lit);
self.cursor += 1;
self.len += 1;
// Flush the circular buffer to the output
if (self.cursor == self.dict_size) {
try writer.writeAll(self.buf.items);
self.cursor = 0;
}
}
/// Fetch an LZ sequence (length, distance) from inside the buffer
pub fn appendLz(
self: *Self,
allocator: Allocator,
len: usize,
dist: usize,
writer: anytype,
) !void {
if (dist > self.dict_size or dist > self.len) {
return error.CorruptInput;
}
var offset = (self.dict_size + self.cursor - dist) % self.dict_size;
var i: usize = 0;
while (i < len) : (i += 1) {
const x = self.get(offset);
try self.appendLiteral(allocator, x, writer);
offset += 1;
if (offset == self.dict_size) {
offset = 0;
}
}
}
pub fn finish(self: *Self, writer: anytype) !void {
if (self.cursor > 0) {
try writer.writeAll(self.buf.items[0..self.cursor]);
self.cursor = 0;
}
}
pub fn deinit(self: *Self, allocator: Allocator) void {
self.buf.deinit(allocator);
self.* = undefined;
}
};

View File

@ -0,0 +1,181 @@
const std = @import("../../../std.zig");
const mem = std.mem;
pub const RangeDecoder = struct {
range: u32,
code: u32,
pub fn init(reader: anytype) !RangeDecoder {
const reserved = try reader.readByte();
if (reserved != 0) {
return error.CorruptInput;
}
return RangeDecoder{
.range = 0xFFFF_FFFF,
.code = try reader.readIntBig(u32),
};
}
pub fn fromParts(
range: u32,
code: u32,
) RangeDecoder {
return .{
.range = range,
.code = code,
};
}
pub fn set(self: *RangeDecoder, range: u32, code: u32) void {
self.range = range;
self.code = code;
}
pub inline fn isFinished(self: RangeDecoder) bool {
return self.code == 0;
}
inline fn normalize(self: *RangeDecoder, reader: anytype) !void {
if (self.range < 0x0100_0000) {
self.range <<= 8;
self.code = (self.code << 8) ^ @as(u32, try reader.readByte());
}
}
inline fn getBit(self: *RangeDecoder, reader: anytype) !bool {
self.range >>= 1;
const bit = self.code >= self.range;
if (bit)
self.code -= self.range;
try self.normalize(reader);
return bit;
}
pub fn get(self: *RangeDecoder, reader: anytype, count: usize) !u32 {
var result: u32 = 0;
var i: usize = 0;
while (i < count) : (i += 1)
result = (result << 1) ^ @boolToInt(try self.getBit(reader));
return result;
}
pub inline fn decodeBit(self: *RangeDecoder, reader: anytype, prob: *u16, update: bool) !bool {
const bound = (self.range >> 11) * prob.*;
if (self.code < bound) {
if (update)
prob.* += (0x800 - prob.*) >> 5;
self.range = bound;
try self.normalize(reader);
return false;
} else {
if (update)
prob.* -= prob.* >> 5;
self.code -= bound;
self.range -= bound;
try self.normalize(reader);
return true;
}
}
fn parseBitTree(
self: *RangeDecoder,
reader: anytype,
num_bits: u5,
probs: []u16,
update: bool,
) !u32 {
var tmp: u32 = 1;
var i: @TypeOf(num_bits) = 0;
while (i < num_bits) : (i += 1) {
const bit = try self.decodeBit(reader, &probs[tmp], update);
tmp = (tmp << 1) ^ @boolToInt(bit);
}
return tmp - (@as(u32, 1) << num_bits);
}
pub fn parseReverseBitTree(
self: *RangeDecoder,
reader: anytype,
num_bits: u5,
probs: []u16,
offset: usize,
update: bool,
) !u32 {
var result: u32 = 0;
var tmp: usize = 1;
var i: @TypeOf(num_bits) = 0;
while (i < num_bits) : (i += 1) {
const bit = @boolToInt(try self.decodeBit(reader, &probs[offset + tmp], update));
tmp = (tmp << 1) ^ bit;
result ^= @as(u32, bit) << i;
}
return result;
}
};
pub fn BitTree(comptime num_bits: usize) type {
return struct {
probs: [1 << num_bits]u16 = .{0x400} ** (1 << num_bits),
const Self = @This();
pub fn parse(
self: *Self,
reader: anytype,
decoder: *RangeDecoder,
update: bool,
) !u32 {
return decoder.parseBitTree(reader, num_bits, &self.probs, update);
}
pub fn parseReverse(
self: *Self,
reader: anytype,
decoder: *RangeDecoder,
update: bool,
) !u32 {
return decoder.parseReverseBitTree(reader, num_bits, &self.probs, 0, update);
}
pub fn reset(self: *Self) void {
mem.set(u16, &self.probs, 0x400);
}
};
}
pub const LenDecoder = struct {
choice: u16 = 0x400,
choice2: u16 = 0x400,
low_coder: [16]BitTree(3) = .{.{}} ** 16,
mid_coder: [16]BitTree(3) = .{.{}} ** 16,
high_coder: BitTree(8) = .{},
pub fn decode(
self: *LenDecoder,
reader: anytype,
decoder: *RangeDecoder,
pos_state: usize,
update: bool,
) !usize {
if (!try decoder.decodeBit(reader, &self.choice, update)) {
return @as(usize, try self.low_coder[pos_state].parse(reader, decoder, update));
} else if (!try decoder.decodeBit(reader, &self.choice2, update)) {
return @as(usize, try self.mid_coder[pos_state].parse(reader, decoder, update)) + 8;
} else {
return @as(usize, try self.high_coder.parse(reader, decoder, update)) + 16;
}
}
pub fn reset(self: *LenDecoder) void {
self.choice = 0x400;
self.choice2 = 0x400;
for (self.low_coder) |*t| t.reset();
for (self.mid_coder) |*t| t.reset();
self.high_coder.reset();
}
};

View File

@ -0,0 +1,89 @@
const std = @import("../../std.zig");
const lzma = @import("../lzma.zig");
fn testDecompress(compressed: []const u8) ![]u8 {
const allocator = std.testing.allocator;
var stream = std.io.fixedBufferStream(compressed);
var decompressor = try lzma.decompress(allocator, stream.reader());
defer decompressor.deinit();
const reader = decompressor.reader();
return reader.readAllAlloc(allocator, std.math.maxInt(usize));
}
fn testDecompressEqual(expected: []const u8, compressed: []const u8) !void {
const allocator = std.testing.allocator;
const decomp = try testDecompress(compressed);
defer allocator.free(decomp);
try std.testing.expectEqualSlices(u8, expected, decomp);
}
fn testDecompressError(expected: anyerror, compressed: []const u8) !void {
return std.testing.expectError(expected, testDecompress(compressed));
}
test "LZMA: decompress empty world" {
try testDecompressEqual(
"",
&[_]u8{
0x5d, 0x00, 0x00, 0x80, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x83, 0xff,
0xfb, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00,
},
);
}
test "LZMA: decompress hello world" {
try testDecompressEqual(
"Hello world\n",
&[_]u8{
0x5d, 0x00, 0x00, 0x80, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x24, 0x19,
0x49, 0x98, 0x6f, 0x10, 0x19, 0xc6, 0xd7, 0x31, 0xeb, 0x36, 0x50, 0xb2, 0x98, 0x48, 0xff, 0xfe,
0xa5, 0xb0, 0x00,
},
);
}
test "LZMA: decompress huge dict" {
try testDecompressEqual(
"Hello world\n",
&[_]u8{
0x5d, 0x7f, 0x7f, 0x7f, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x24, 0x19,
0x49, 0x98, 0x6f, 0x10, 0x19, 0xc6, 0xd7, 0x31, 0xeb, 0x36, 0x50, 0xb2, 0x98, 0x48, 0xff, 0xfe,
0xa5, 0xb0, 0x00,
},
);
}
test "LZMA: unknown size with end of payload marker" {
try testDecompressEqual(
"Hello\nWorld!\n",
@embedFile("testdata/good-unknown_size-with_eopm.lzma"),
);
}
test "LZMA: known size without end of payload marker" {
try testDecompressEqual(
"Hello\nWorld!\n",
@embedFile("testdata/good-known_size-without_eopm.lzma"),
);
}
test "LZMA: known size with end of payload marker" {
try testDecompressEqual(
"Hello\nWorld!\n",
@embedFile("testdata/good-known_size-with_eopm.lzma"),
);
}
test "LZMA: too big uncompressed size in header" {
try testDecompressError(
error.CorruptInput,
@embedFile("testdata/bad-too_big_size-with_eopm.lzma"),
);
}
test "LZMA: too small uncompressed size in header" {
try testDecompressError(
error.CorruptInput,
@embedFile("testdata/bad-too_small_size-without_eopm-3.lzma"),
);
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,128 @@
const std = @import("../../std.zig");
const math = std.math;
const mem = std.mem;
const Allocator = std.mem.Allocator;
pub fn Vec2D(comptime T: type) type {
return struct {
data: []T,
cols: usize,
const Self = @This();
pub fn init(allocator: Allocator, value: T, size: struct { usize, usize }) !Self {
const len = try math.mul(usize, size[0], size[1]);
const data = try allocator.alloc(T, len);
mem.set(T, data, value);
return Self{
.data = data,
.cols = size[1],
};
}
pub fn deinit(self: *Self, allocator: Allocator) void {
allocator.free(self.data);
self.* = undefined;
}
pub fn fill(self: *Self, value: T) void {
mem.set(T, self.data, value);
}
inline fn _get(self: Self, row: usize) ![]T {
const start_row = try math.mul(usize, row, self.cols);
const end_row = try math.add(usize, start_row, self.cols);
return self.data[start_row..end_row];
}
pub fn get(self: Self, row: usize) ![]const T {
return self._get(row);
}
pub fn getMut(self: *Self, row: usize) ![]T {
return self._get(row);
}
};
}
const testing = std.testing;
const expectEqualSlices = std.testing.expectEqualSlices;
const expectError = std.testing.expectError;
test "Vec2D.init" {
const allocator = testing.allocator;
var vec2d = try Vec2D(i32).init(allocator, 1, .{ 2, 3 });
defer vec2d.deinit(allocator);
try expectEqualSlices(i32, &.{ 1, 1, 1 }, try vec2d.get(0));
try expectEqualSlices(i32, &.{ 1, 1, 1 }, try vec2d.get(1));
}
test "Vec2D.init overflow" {
const allocator = testing.allocator;
try expectError(
error.Overflow,
Vec2D(i32).init(allocator, 1, .{ math.maxInt(usize), math.maxInt(usize) }),
);
}
test "Vec2D.fill" {
const allocator = testing.allocator;
var vec2d = try Vec2D(i32).init(allocator, 0, .{ 2, 3 });
defer vec2d.deinit(allocator);
vec2d.fill(7);
try expectEqualSlices(i32, &.{ 7, 7, 7 }, try vec2d.get(0));
try expectEqualSlices(i32, &.{ 7, 7, 7 }, try vec2d.get(1));
}
test "Vec2D.get" {
var data = [_]i32{ 0, 1, 2, 3, 4, 5, 6, 7 };
const vec2d = Vec2D(i32){
.data = &data,
.cols = 2,
};
try expectEqualSlices(i32, &.{ 0, 1 }, try vec2d.get(0));
try expectEqualSlices(i32, &.{ 2, 3 }, try vec2d.get(1));
try expectEqualSlices(i32, &.{ 4, 5 }, try vec2d.get(2));
try expectEqualSlices(i32, &.{ 6, 7 }, try vec2d.get(3));
}
test "Vec2D.getMut" {
var data = [_]i32{ 0, 1, 2, 3, 4, 5, 6, 7 };
var vec2d = Vec2D(i32){
.data = &data,
.cols = 2,
};
const row = try vec2d.getMut(1);
row[1] = 9;
try expectEqualSlices(i32, &.{ 0, 1 }, try vec2d.get(0));
// (1, 1) should be 9.
try expectEqualSlices(i32, &.{ 2, 9 }, try vec2d.get(1));
try expectEqualSlices(i32, &.{ 4, 5 }, try vec2d.get(2));
try expectEqualSlices(i32, &.{ 6, 7 }, try vec2d.get(3));
}
test "Vec2D.get multiplication overflow" {
const allocator = testing.allocator;
var matrix = try Vec2D(i32).init(allocator, 0, .{ 3, 4 });
defer matrix.deinit(allocator);
const row = (math.maxInt(usize) / 4) + 1;
try expectError(error.Overflow, matrix.get(row));
try expectError(error.Overflow, matrix.getMut(row));
}
test "Vec2D.get addition overflow" {
const allocator = testing.allocator;
var matrix = try Vec2D(i32).init(allocator, 0, .{ 3, 5 });
defer matrix.deinit(allocator);
const row = math.maxInt(usize) / 5;
try expectError(error.Overflow, matrix.get(row));
try expectError(error.Overflow, matrix.getMut(row));
}

View File

@ -0,0 +1,26 @@
const std = @import("../std.zig");
const Allocator = std.mem.Allocator;
pub const decode = @import("lzma2/decode.zig");
pub fn decompress(
allocator: Allocator,
reader: anytype,
writer: anytype,
) !void {
var decoder = try decode.Decoder.init(allocator);
defer decoder.deinit(allocator);
return decoder.decompress(allocator, reader, writer);
}
test {
const expected = "Hello\nWorld!\n";
const compressed = &[_]u8{ 0x01, 0x00, 0x05, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x0A, 0x02, 0x00, 0x06, 0x57, 0x6F, 0x72, 0x6C, 0x64, 0x21, 0x0A, 0x00 };
const allocator = std.testing.allocator;
var decomp = std.ArrayList(u8).init(allocator);
defer decomp.deinit();
var stream = std.io.fixedBufferStream(compressed);
try decompress(allocator, stream.reader(), decomp.writer());
try std.testing.expectEqualSlices(u8, expected, decomp.items);
}

View File

@ -0,0 +1,169 @@
const std = @import("../../std.zig");
const Allocator = std.mem.Allocator;
const lzma = @import("../lzma.zig");
const DecoderState = lzma.decode.DecoderState;
const LzAccumBuffer = lzma.decode.lzbuffer.LzAccumBuffer;
const Properties = lzma.decode.Properties;
const RangeDecoder = lzma.decode.rangecoder.RangeDecoder;
pub const Decoder = struct {
lzma_state: DecoderState,
pub fn init(allocator: Allocator) !Decoder {
return Decoder{
.lzma_state = try DecoderState.init(
allocator,
Properties{
.lc = 0,
.lp = 0,
.pb = 0,
},
null,
),
};
}
pub fn deinit(self: *Decoder, allocator: Allocator) void {
self.lzma_state.deinit(allocator);
self.* = undefined;
}
pub fn decompress(
self: *Decoder,
allocator: Allocator,
reader: anytype,
writer: anytype,
) !void {
var accum = LzAccumBuffer.init(std.math.maxInt(usize));
defer accum.deinit(allocator);
while (true) {
const status = try reader.readByte();
switch (status) {
0 => break,
1 => try parseUncompressed(allocator, reader, writer, &accum, true),
2 => try parseUncompressed(allocator, reader, writer, &accum, false),
else => try self.parseLzma(allocator, reader, writer, &accum, status),
}
}
try accum.finish(writer);
}
fn parseLzma(
self: *Decoder,
allocator: Allocator,
reader: anytype,
writer: anytype,
accum: *LzAccumBuffer,
status: u8,
) !void {
if (status & 0x80 == 0) {
return error.CorruptInput;
}
const Reset = struct {
dict: bool,
state: bool,
props: bool,
};
const reset = switch ((status >> 5) & 0x3) {
0 => Reset{
.dict = false,
.state = false,
.props = false,
},
1 => Reset{
.dict = false,
.state = true,
.props = false,
},
2 => Reset{
.dict = false,
.state = true,
.props = true,
},
3 => Reset{
.dict = true,
.state = true,
.props = true,
},
else => unreachable,
};
const unpacked_size = blk: {
var tmp: u64 = status & 0x1F;
tmp <<= 16;
tmp |= try reader.readIntBig(u16);
break :blk tmp + 1;
};
const packed_size = blk: {
const tmp: u17 = try reader.readIntBig(u16);
break :blk tmp + 1;
};
if (reset.dict) {
try accum.reset(writer);
}
if (reset.state) {
var new_props = self.lzma_state.lzma_props;
if (reset.props) {
var props = try reader.readByte();
if (props >= 225) {
return error.CorruptInput;
}
const lc = @intCast(u4, props % 9);
props /= 9;
const lp = @intCast(u3, props % 5);
props /= 5;
const pb = @intCast(u3, props);
if (lc + lp > 4) {
return error.CorruptInput;
}
new_props = Properties{ .lc = lc, .lp = lp, .pb = pb };
}
try self.lzma_state.resetState(allocator, new_props);
}
self.lzma_state.unpacked_size = unpacked_size + accum.len;
var counter = std.io.countingReader(reader);
const counter_reader = counter.reader();
var rangecoder = try RangeDecoder.init(counter_reader);
while (try self.lzma_state.process(allocator, counter_reader, writer, accum, &rangecoder) == .continue_) {}
if (counter.bytes_read != packed_size) {
return error.CorruptInput;
}
}
fn parseUncompressed(
allocator: Allocator,
reader: anytype,
writer: anytype,
accum: *LzAccumBuffer,
reset_dict: bool,
) !void {
const unpacked_size = @as(u17, try reader.readIntBig(u16)) + 1;
if (reset_dict) {
try accum.reset(writer);
}
var i: @TypeOf(unpacked_size) = 0;
while (i < unpacked_size) : (i += 1) {
try accum.appendByte(allocator, try reader.readByte());
}
}
};

View File

@ -118,7 +118,7 @@ pub fn Decompress(comptime ReaderType: type) type {
var hasher = std.compress.hashedReader(self.in_reader, Crc32.init());
const hashed_reader = hasher.reader();
const backward_size = (try hashed_reader.readIntLittle(u32) + 1) * 4;
const backward_size = (@as(u64, try hashed_reader.readIntLittle(u32)) + 1) * 4;
if (backward_size != index_size)
return error.CorruptInput;

View File

@ -1,6 +1,7 @@
const std = @import("../../std.zig");
const lzma = @import("lzma.zig");
const lzma2 = std.compress.lzma2;
const Allocator = std.mem.Allocator;
const ArrayListUnmanaged = std.ArrayListUnmanaged;
const Crc32 = std.hash.Crc32;
const Crc64 = std.hash.crc.Crc64Xz;
const Sha256 = std.crypto.hash.sha2.Sha256;
@ -32,8 +33,7 @@ pub fn Decoder(comptime ReaderType: type) type {
inner_reader: ReaderType,
check: xz.Check,
err: ?Error,
accum: lzma.LzAccumBuffer,
lzma_state: lzma.DecoderState,
to_read: ArrayListUnmanaged(u8),
block_count: usize,
fn init(allocator: Allocator, in_reader: ReaderType, check: xz.Check) !Self {
@ -42,15 +42,13 @@ pub fn Decoder(comptime ReaderType: type) type {
.inner_reader = in_reader,
.check = check,
.err = null,
.accum = .{},
.lzma_state = try lzma.DecoderState.init(allocator),
.to_read = .{},
.block_count = 0,
};
}
pub fn deinit(self: *Self) void {
self.accum.deinit(self.allocator);
self.lzma_state.deinit(self.allocator);
self.to_read.deinit(self.allocator);
}
pub fn reader(self: *Self) Reader {
@ -59,9 +57,13 @@ pub fn Decoder(comptime ReaderType: type) type {
pub fn read(self: *Self, output: []u8) Error!usize {
while (true) {
if (self.accum.to_read.items.len > 0) {
const n = self.accum.read(output);
if (self.accum.to_read.items.len == 0 and self.err != null) {
if (self.to_read.items.len > 0) {
const input = self.to_read.items;
const n = std.math.min(input.len, output.len);
std.mem.copy(u8, output[0..n], input[0..n]);
std.mem.copy(u8, input, input[n..]);
self.to_read.shrinkRetainingCapacity(input.len - n);
if (self.to_read.items.len == 0 and self.err != null) {
if (self.err.? == DecodeError.EndOfStreamWithNoError) {
return n;
}
@ -77,15 +79,12 @@ pub fn Decoder(comptime ReaderType: type) type {
}
self.readBlock() catch |e| {
self.err = e;
if (self.accum.to_read.items.len == 0) {
try self.accum.reset(self.allocator);
}
};
}
}
fn readBlock(self: *Self) Error!void {
const unpacked_pos = self.accum.to_read.items.len;
const unpacked_pos = self.to_read.items.len;
var block_counter = std.io.countingReader(self.inner_reader);
const block_reader = block_counter.reader();
@ -98,7 +97,7 @@ pub fn Decoder(comptime ReaderType: type) type {
var header_hasher = std.compress.hashedReader(block_reader, Crc32.init());
const header_reader = header_hasher.reader();
const header_size = try header_reader.readByte() * 4;
const header_size = @as(u64, try header_reader.readByte()) * 4;
if (header_size == 0)
return error.EndOfStreamWithNoError;
@ -156,15 +155,18 @@ pub fn Decoder(comptime ReaderType: type) type {
// Compressed Data
var packed_counter = std.io.countingReader(block_reader);
const packed_reader = packed_counter.reader();
while (try self.readLzma2Chunk(packed_reader)) {}
try lzma2.decompress(
self.allocator,
packed_counter.reader(),
self.to_read.writer(self.allocator),
);
if (packed_size) |s| {
if (s != packed_counter.bytes_read)
return error.CorruptInput;
}
const unpacked_bytes = self.accum.to_read.items[unpacked_pos..];
const unpacked_bytes = self.to_read.items[unpacked_pos..];
if (unpacked_size) |s| {
if (s != unpacked_bytes.len)
return error.CorruptInput;
@ -205,113 +207,5 @@ pub fn Decoder(comptime ReaderType: type) type {
self.block_count += 1;
}
fn readLzma2Chunk(self: *Self, packed_reader: anytype) Error!bool {
const status = try packed_reader.readByte();
switch (status) {
0 => {
try self.accum.reset(self.allocator);
return false;
},
1, 2 => {
if (status == 1)
try self.accum.reset(self.allocator);
const size = try packed_reader.readIntBig(u16) + 1;
try self.accum.ensureUnusedCapacity(self.allocator, size);
var i: usize = 0;
while (i < size) : (i += 1)
self.accum.appendAssumeCapacity(try packed_reader.readByte());
return true;
},
else => {
if (status & 0x80 == 0)
return error.CorruptInput;
const Reset = struct {
dict: bool,
state: bool,
props: bool,
};
const reset = switch ((status >> 5) & 0x3) {
0 => Reset{
.dict = false,
.state = false,
.props = false,
},
1 => Reset{
.dict = false,
.state = true,
.props = false,
},
2 => Reset{
.dict = false,
.state = true,
.props = true,
},
3 => Reset{
.dict = true,
.state = true,
.props = true,
},
else => unreachable,
};
const unpacked_size = blk: {
var tmp: u64 = status & 0x1F;
tmp <<= 16;
tmp |= try packed_reader.readIntBig(u16);
break :blk tmp + 1;
};
const packed_size = blk: {
const tmp: u17 = try packed_reader.readIntBig(u16);
break :blk tmp + 1;
};
if (reset.dict)
try self.accum.reset(self.allocator);
if (reset.state) {
var new_props = self.lzma_state.lzma_props;
if (reset.props) {
var props = try packed_reader.readByte();
if (props >= 225)
return error.CorruptInput;
const lc = @intCast(u4, props % 9);
props /= 9;
const lp = @intCast(u3, props % 5);
props /= 5;
const pb = @intCast(u3, props);
if (lc + lp > 4)
return error.CorruptInput;
new_props = .{ .lc = lc, .lp = lp, .pb = pb };
}
try self.lzma_state.reset_state(self.allocator, new_props);
}
self.lzma_state.unpacked_size = unpacked_size + self.accum.len();
const buffer = try self.allocator.alloc(u8, packed_size);
defer self.allocator.free(buffer);
for (buffer) |*b|
b.* = try packed_reader.readByte();
var rangecoder = try lzma.RangeDecoder.init(buffer);
try self.lzma_state.process(self.allocator, &self.accum, &rangecoder);
return true;
},
}
}
};
}

View File

@ -1,658 +0,0 @@
// Ported from https://github.com/gendx/lzma-rs
const std = @import("../../std.zig");
const assert = std.debug.assert;
const Allocator = std.mem.Allocator;
const ArrayListUnmanaged = std.ArrayListUnmanaged;
const LzmaProperties = struct {
lc: u4,
lp: u3,
pb: u3,
fn validate(self: LzmaProperties) void {
assert(self.lc <= 8);
assert(self.lp <= 4);
assert(self.pb <= 4);
}
};
pub const DecoderState = struct {
lzma_props: LzmaProperties,
unpacked_size: ?u64,
literal_probs: Vec2D(u16),
pos_slot_decoder: [4]BitTree,
align_decoder: BitTree,
pos_decoders: [115]u16,
is_match: [192]u16,
is_rep: [12]u16,
is_rep_g0: [12]u16,
is_rep_g1: [12]u16,
is_rep_g2: [12]u16,
is_rep_0long: [192]u16,
state: usize,
rep: [4]usize,
len_decoder: LenDecoder,
rep_len_decoder: LenDecoder,
pub fn init(allocator: Allocator) !DecoderState {
return .{
.lzma_props = LzmaProperties{ .lc = 0, .lp = 0, .pb = 0 },
.unpacked_size = null,
.literal_probs = try Vec2D(u16).init(allocator, 0x400, 1, 0x300),
.pos_slot_decoder = .{
try BitTree.init(allocator, 6),
try BitTree.init(allocator, 6),
try BitTree.init(allocator, 6),
try BitTree.init(allocator, 6),
},
.align_decoder = try BitTree.init(allocator, 4),
.pos_decoders = .{0x400} ** 115,
.is_match = .{0x400} ** 192,
.is_rep = .{0x400} ** 12,
.is_rep_g0 = .{0x400} ** 12,
.is_rep_g1 = .{0x400} ** 12,
.is_rep_g2 = .{0x400} ** 12,
.is_rep_0long = .{0x400} ** 192,
.state = 0,
.rep = .{0} ** 4,
.len_decoder = try LenDecoder.init(allocator),
.rep_len_decoder = try LenDecoder.init(allocator),
};
}
pub fn deinit(self: *DecoderState, allocator: Allocator) void {
self.literal_probs.deinit(allocator);
for (self.pos_slot_decoder) |*t| t.deinit(allocator);
self.align_decoder.deinit(allocator);
self.len_decoder.deinit(allocator);
self.rep_len_decoder.deinit(allocator);
}
pub fn reset_state(self: *DecoderState, allocator: Allocator, new_props: LzmaProperties) !void {
new_props.validate();
if (self.lzma_props.lc + self.lzma_props.lp == new_props.lc + new_props.lp) {
self.literal_probs.fill(0x400);
} else {
self.literal_probs.deinit(allocator);
self.literal_probs = try Vec2D(u16).init(allocator, 0x400, @as(usize, 1) << (new_props.lc + new_props.lp), 0x300);
}
self.lzma_props = new_props;
for (self.pos_slot_decoder) |*t| t.reset();
self.align_decoder.reset();
self.pos_decoders = .{0x400} ** 115;
self.is_match = .{0x400} ** 192;
self.is_rep = .{0x400} ** 12;
self.is_rep_g0 = .{0x400} ** 12;
self.is_rep_g1 = .{0x400} ** 12;
self.is_rep_g2 = .{0x400} ** 12;
self.is_rep_0long = .{0x400} ** 192;
self.state = 0;
self.rep = .{0} ** 4;
self.len_decoder.reset();
self.rep_len_decoder.reset();
}
fn processNextInner(
self: *DecoderState,
allocator: Allocator,
output: *LzAccumBuffer,
rangecoder: *RangeDecoder,
update: bool,
) !ProcessingStatus {
const pos_state = output.len() & ((@as(usize, 1) << self.lzma_props.pb) - 1);
if (!try rangecoder.decodeBit(
&self.is_match[(self.state << 4) + pos_state],
update,
)) {
const byte: u8 = try self.decodeLiteral(output, rangecoder, update);
if (update) {
try output.appendLiteral(allocator, byte);
self.state = if (self.state < 4)
0
else if (self.state < 10)
self.state - 3
else
self.state - 6;
}
return .continue_;
}
var len: usize = undefined;
if (try rangecoder.decodeBit(&self.is_rep[self.state], update)) {
if (!try rangecoder.decodeBit(&self.is_rep_g0[self.state], update)) {
if (!try rangecoder.decodeBit(
&self.is_rep_0long[(self.state << 4) + pos_state],
update,
)) {
if (update) {
self.state = if (self.state < 7) 9 else 11;
const dist = self.rep[0] + 1;
try output.appendLz(allocator, 1, dist);
}
return .continue_;
}
} else {
const idx: usize = if (!try rangecoder.decodeBit(&self.is_rep_g1[self.state], update))
1
else if (!try rangecoder.decodeBit(&self.is_rep_g2[self.state], update))
2
else
3;
if (update) {
const dist = self.rep[idx];
var i = idx;
while (i > 0) : (i -= 1) {
self.rep[i] = self.rep[i - 1];
}
self.rep[0] = dist;
}
}
len = try self.rep_len_decoder.decode(rangecoder, pos_state, update);
if (update) {
self.state = if (self.state < 7) 8 else 11;
}
} else {
if (update) {
self.rep[3] = self.rep[2];
self.rep[2] = self.rep[1];
self.rep[1] = self.rep[0];
}
len = try self.len_decoder.decode(rangecoder, pos_state, update);
if (update) {
self.state = if (self.state < 7) 7 else 10;
}
const rep_0 = try self.decodeDistance(rangecoder, len, update);
if (update) {
self.rep[0] = rep_0;
if (self.rep[0] == 0xFFFF_FFFF) {
if (rangecoder.isFinished()) {
return .finished;
}
return error.CorruptInput;
}
}
}
if (update) {
len += 2;
const dist = self.rep[0] + 1;
try output.appendLz(allocator, len, dist);
}
return .continue_;
}
fn processNext(
self: *DecoderState,
allocator: Allocator,
output: *LzAccumBuffer,
rangecoder: *RangeDecoder,
) !ProcessingStatus {
return self.processNextInner(allocator, output, rangecoder, true);
}
pub fn process(
self: *DecoderState,
allocator: Allocator,
output: *LzAccumBuffer,
rangecoder: *RangeDecoder,
) !void {
while (true) {
if (self.unpacked_size) |unpacked_size| {
if (output.len() >= unpacked_size) {
break;
}
} else if (rangecoder.isFinished()) {
break;
}
if (try self.processNext(allocator, output, rangecoder) == .finished) {
break;
}
}
if (self.unpacked_size) |len| {
if (len != output.len()) {
return error.CorruptInput;
}
}
}
fn decodeLiteral(
self: *DecoderState,
output: *LzAccumBuffer,
rangecoder: *RangeDecoder,
update: bool,
) !u8 {
const def_prev_byte = 0;
const prev_byte = @as(usize, output.lastOr(def_prev_byte));
var result: usize = 1;
const lit_state = ((output.len() & ((@as(usize, 1) << self.lzma_props.lp) - 1)) << self.lzma_props.lc) +
(prev_byte >> (8 - self.lzma_props.lc));
const probs = try self.literal_probs.get(lit_state);
if (self.state >= 7) {
var match_byte = @as(usize, try output.lastN(self.rep[0] + 1));
while (result < 0x100) {
const match_bit = (match_byte >> 7) & 1;
match_byte <<= 1;
const bit = @boolToInt(try rangecoder.decodeBit(
&probs[((@as(usize, 1) + match_bit) << 8) + result],
update,
));
result = (result << 1) ^ bit;
if (match_bit != bit) {
break;
}
}
}
while (result < 0x100) {
result = (result << 1) ^ @boolToInt(try rangecoder.decodeBit(&probs[result], update));
}
return @truncate(u8, result - 0x100);
}
fn decodeDistance(
self: *DecoderState,
rangecoder: *RangeDecoder,
length: usize,
update: bool,
) !usize {
const len_state = if (length > 3) 3 else length;
const pos_slot = @as(usize, try self.pos_slot_decoder[len_state].parse(rangecoder, update));
if (pos_slot < 4)
return pos_slot;
const num_direct_bits = @intCast(u5, (pos_slot >> 1) - 1);
var result = (2 ^ (pos_slot & 1)) << num_direct_bits;
if (pos_slot < 14) {
result += try rangecoder.parseReverseBitTree(
num_direct_bits,
&self.pos_decoders,
result - pos_slot,
update,
);
} else {
result += @as(usize, try rangecoder.get(num_direct_bits - 4)) << 4;
result += try self.align_decoder.parseReverse(rangecoder, update);
}
return result;
}
};
const ProcessingStatus = enum {
continue_,
finished,
};
pub const LzAccumBuffer = struct {
to_read: ArrayListUnmanaged(u8) = .{},
buf: ArrayListUnmanaged(u8) = .{},
pub fn deinit(self: *LzAccumBuffer, allocator: Allocator) void {
self.to_read.deinit(allocator);
self.buf.deinit(allocator);
}
pub fn read(self: *LzAccumBuffer, output: []u8) usize {
const input = self.to_read.items;
const n = std.math.min(input.len, output.len);
std.mem.copy(u8, output[0..n], input[0..n]);
std.mem.copy(u8, input, input[n..]);
self.to_read.shrinkRetainingCapacity(input.len - n);
return n;
}
pub fn ensureUnusedCapacity(
self: *LzAccumBuffer,
allocator: Allocator,
additional_count: usize,
) !void {
try self.buf.ensureUnusedCapacity(allocator, additional_count);
}
pub fn appendAssumeCapacity(self: *LzAccumBuffer, byte: u8) void {
self.buf.appendAssumeCapacity(byte);
}
pub fn reset(self: *LzAccumBuffer, allocator: Allocator) !void {
try self.to_read.appendSlice(allocator, self.buf.items);
self.buf.clearRetainingCapacity();
}
pub fn len(self: *const LzAccumBuffer) usize {
return self.buf.items.len;
}
pub fn lastOr(self: *const LzAccumBuffer, lit: u8) u8 {
const buf_len = self.buf.items.len;
return if (buf_len == 0)
lit
else
self.buf.items[buf_len - 1];
}
pub fn lastN(self: *const LzAccumBuffer, dist: usize) !u8 {
const buf_len = self.buf.items.len;
if (dist > buf_len) {
return error.CorruptInput;
}
return self.buf.items[buf_len - dist];
}
pub fn appendLiteral(self: *LzAccumBuffer, allocator: Allocator, lit: u8) !void {
try self.buf.append(allocator, lit);
}
pub fn appendLz(self: *LzAccumBuffer, allocator: Allocator, length: usize, dist: usize) !void {
const buf_len = self.buf.items.len;
if (dist > buf_len) {
return error.CorruptInput;
}
var offset = buf_len - dist;
var i: usize = 0;
while (i < length) : (i += 1) {
const x = self.buf.items[offset];
try self.buf.append(allocator, x);
offset += 1;
}
}
};
pub const RangeDecoder = struct {
stream: std.io.FixedBufferStream([]const u8),
range: u32,
code: u32,
pub fn init(buffer: []const u8) !RangeDecoder {
var dec = RangeDecoder{
.stream = std.io.fixedBufferStream(buffer),
.range = 0xFFFF_FFFF,
.code = 0,
};
const reader = dec.stream.reader();
_ = try reader.readByte();
dec.code = try reader.readIntBig(u32);
return dec;
}
pub fn fromParts(
buffer: []const u8,
range: u32,
code: u32,
) RangeDecoder {
return .{
.stream = std.io.fixedBufferStream(buffer),
.range = range,
.code = code,
};
}
pub fn set(self: *RangeDecoder, range: u32, code: u32) void {
self.range = range;
self.code = code;
}
pub fn readInto(self: *RangeDecoder, dest: []u8) !usize {
return self.stream.read(dest);
}
pub inline fn isFinished(self: *const RangeDecoder) bool {
return self.code == 0 and self.isEof();
}
pub inline fn isEof(self: *const RangeDecoder) bool {
return self.stream.pos == self.stream.buffer.len;
}
inline fn normalize(self: *RangeDecoder) !void {
if (self.range < 0x0100_0000) {
self.range <<= 8;
self.code = (self.code << 8) ^ @as(u32, try self.stream.reader().readByte());
}
}
inline fn getBit(self: *RangeDecoder) !bool {
self.range >>= 1;
const bit = self.code >= self.range;
if (bit)
self.code -= self.range;
try self.normalize();
return bit;
}
fn get(self: *RangeDecoder, count: usize) !u32 {
var result: u32 = 0;
var i: usize = 0;
while (i < count) : (i += 1)
result = (result << 1) ^ @boolToInt(try self.getBit());
return result;
}
pub inline fn decodeBit(self: *RangeDecoder, prob: *u16, update: bool) !bool {
const bound = (self.range >> 11) * prob.*;
if (self.code < bound) {
if (update)
prob.* += (0x800 - prob.*) >> 5;
self.range = bound;
try self.normalize();
return false;
} else {
if (update)
prob.* -= prob.* >> 5;
self.code -= bound;
self.range -= bound;
try self.normalize();
return true;
}
}
fn parseBitTree(
self: *RangeDecoder,
num_bits: u5,
probs: []u16,
update: bool,
) !u32 {
var tmp: u32 = 1;
var i: u5 = 0;
while (i < num_bits) : (i += 1) {
const bit = try self.decodeBit(&probs[tmp], update);
tmp = (tmp << 1) ^ @boolToInt(bit);
}
return tmp - (@as(u32, 1) << num_bits);
}
pub fn parseReverseBitTree(
self: *RangeDecoder,
num_bits: u5,
probs: []u16,
offset: usize,
update: bool,
) !u32 {
var result: u32 = 0;
var tmp: usize = 1;
var i: u5 = 0;
while (i < num_bits) : (i += 1) {
const bit = @boolToInt(try self.decodeBit(&probs[offset + tmp], update));
tmp = (tmp << 1) ^ bit;
result ^= @as(u32, bit) << i;
}
return result;
}
};
fn Vec2D(comptime T: type) type {
return struct {
data: []T,
cols: usize,
const Self = @This();
pub fn init(allocator: Allocator, data: T, rows: usize, cols: usize) !Self {
const len = try std.math.mul(usize, rows, cols);
var vec2d = Self{
.data = try allocator.alloc(T, len),
.cols = cols,
};
vec2d.fill(data);
return vec2d;
}
pub fn deinit(self: *Self, allocator: Allocator) void {
allocator.free(self.data);
}
pub fn fill(self: *Self, value: T) void {
std.mem.set(T, self.data, value);
}
pub fn get(self: *Self, row: usize) ![]T {
const start_row = try std.math.mul(usize, row, self.cols);
return self.data[start_row .. start_row + self.cols];
}
};
}
const BitTree = struct {
num_bits: u5,
probs: ArrayListUnmanaged(u16),
pub fn init(allocator: Allocator, num_bits: u5) !BitTree {
var probs_len = @as(usize, 1) << num_bits;
var probs = try ArrayListUnmanaged(u16).initCapacity(allocator, probs_len);
while (probs_len > 0) : (probs_len -= 1)
probs.appendAssumeCapacity(0x400);
return .{ .num_bits = num_bits, .probs = probs };
}
pub fn deinit(self: *BitTree, allocator: Allocator) void {
self.probs.deinit(allocator);
}
pub fn parse(
self: *BitTree,
rangecoder: *RangeDecoder,
update: bool,
) !u32 {
return rangecoder.parseBitTree(self.num_bits, self.probs.items, update);
}
pub fn parseReverse(
self: *BitTree,
rangecoder: *RangeDecoder,
update: bool,
) !u32 {
return rangecoder.parseReverseBitTree(self.num_bits, self.probs.items, 0, update);
}
pub fn reset(self: *BitTree) void {
std.mem.set(u16, self.probs.items, 0x400);
}
};
const LenDecoder = struct {
choice: u16,
choice2: u16,
low_coder: [16]BitTree,
mid_coder: [16]BitTree,
high_coder: BitTree,
pub fn init(allocator: Allocator) !LenDecoder {
return .{
.choice = 0x400,
.choice2 = 0x400,
.low_coder = .{
try BitTree.init(allocator, 3),
try BitTree.init(allocator, 3),
try BitTree.init(allocator, 3),
try BitTree.init(allocator, 3),
try BitTree.init(allocator, 3),
try BitTree.init(allocator, 3),
try BitTree.init(allocator, 3),
try BitTree.init(allocator, 3),
try BitTree.init(allocator, 3),
try BitTree.init(allocator, 3),
try BitTree.init(allocator, 3),
try BitTree.init(allocator, 3),
try BitTree.init(allocator, 3),
try BitTree.init(allocator, 3),
try BitTree.init(allocator, 3),
try BitTree.init(allocator, 3),
},
.mid_coder = .{
try BitTree.init(allocator, 3),
try BitTree.init(allocator, 3),
try BitTree.init(allocator, 3),
try BitTree.init(allocator, 3),
try BitTree.init(allocator, 3),
try BitTree.init(allocator, 3),
try BitTree.init(allocator, 3),
try BitTree.init(allocator, 3),
try BitTree.init(allocator, 3),
try BitTree.init(allocator, 3),
try BitTree.init(allocator, 3),
try BitTree.init(allocator, 3),
try BitTree.init(allocator, 3),
try BitTree.init(allocator, 3),
try BitTree.init(allocator, 3),
try BitTree.init(allocator, 3),
},
.high_coder = try BitTree.init(allocator, 8),
};
}
pub fn deinit(self: *LenDecoder, allocator: Allocator) void {
for (self.low_coder) |*t| t.deinit(allocator);
for (self.mid_coder) |*t| t.deinit(allocator);
self.high_coder.deinit(allocator);
}
pub fn decode(
self: *LenDecoder,
rangecoder: *RangeDecoder,
pos_state: usize,
update: bool,
) !usize {
if (!try rangecoder.decodeBit(&self.choice, update)) {
return @as(usize, try self.low_coder[pos_state].parse(rangecoder, update));
} else if (!try rangecoder.decodeBit(&self.choice2, update)) {
return @as(usize, try self.mid_coder[pos_state].parse(rangecoder, update)) + 8;
} else {
return @as(usize, try self.high_coder.parse(rangecoder, update)) + 16;
}
}
pub fn reset(self: *LenDecoder) void {
self.choice = 0x400;
self.choice2 = 0x400;
for (self.low_coder) |*t| t.reset();
for (self.mid_coder) |*t| t.reset();
self.high_coder.reset();
}
};

View File

@ -78,3 +78,23 @@ test "unsupported" {
);
}
}
fn testDontPanic(data: []const u8) !void {
const buf = decompress(data) catch |err| switch (err) {
error.OutOfMemory => |e| return e,
else => return,
};
defer testing.allocator.free(buf);
}
test "size fields: integer overflow avoidance" {
// These cases were found via fuzz testing and each previously caused
// an integer overflow when decoding. We just want to ensure they no longer
// cause a panic
const header_size_overflow = "\xfd7zXZ\x00\x00\x01i\"\xde6z";
try testDontPanic(header_size_overflow);
const lzma2_chunk_size_overflow = "\xfd7zXZ\x00\x00\x01i\"\xde6\x02\x00!\x01\x08\x00\x00\x00\xd8\x0f#\x13\x01\xff\xff";
try testDontPanic(lzma2_chunk_size_overflow);
const backward_size_overflow = "\xfd7zXZ\x00\x00\x01i\"\xde6\x00\x00\x00\x00\x1c\xdfD!\x90B\x99\r\x01\x00\x00\xff\xff\x10\x00\x00\x00\x01DD\xff\xff\xff\x01";
try testDontPanic(backward_size_overflow);
}

View File

@ -41,11 +41,13 @@ pub const auth = struct {
pub const Aegis128LMac = @import("crypto/aegis.zig").Aegis128LMac;
pub const Aegis256Mac = @import("crypto/aegis.zig").Aegis256Mac;
};
pub const cmac = @import("crypto/cmac.zig");
};
/// Core functions, that should rarely be used directly by applications.
pub const core = struct {
pub const aes = @import("crypto/aes.zig");
pub const Ascon = @import("crypto/ascon.zig").State;
pub const Gimli = @import("crypto/gimli.zig").State;
pub const Xoodoo = @import("crypto/xoodoo.zig").State;
@ -202,10 +204,13 @@ test {
_ = aead.salsa_poly.XSalsa20Poly1305;
_ = auth.hmac;
_ = auth.cmac;
_ = auth.siphash;
_ = core.aes;
_ = core.Ascon;
_ = core.Gimli;
_ = core.Xoodoo;
_ = core.modes;
_ = dh.X25519;

227
lib/std/crypto/ascon.zig Normal file
View File

@ -0,0 +1,227 @@
//! Ascon is a 320-bit permutation, selected as new standard for lightweight cryptography
//! in the NIST Lightweight Cryptography competition (20192023).
//! https://csrc.nist.gov/News/2023/lightweight-cryptography-nist-selects-ascon
//!
//! The permutation is compact, and optimized for timing and side channel resistance,
//! making it a good choice for embedded applications.
//!
//! It is not meant to be used directly, but as a building block for symmetric cryptography.
const std = @import("std");
const builtin = std.builtin;
const debug = std.debug;
const mem = std.mem;
const testing = std.testing;
const rotr = std.math.rotr;
/// An Ascon state.
///
/// The state is represented as 5 64-bit words.
///
/// The NIST submission (v1.2) serializes these words as big-endian,
/// but software implementations are free to use native endianness.
pub fn State(comptime endian: builtin.Endian) type {
return struct {
const Self = @This();
/// Number of bytes in the state.
pub const block_bytes = 40;
const Block = [5]u64;
st: Block,
/// Initialize the state from a slice of bytes.
pub fn init(initial_state: [block_bytes]u8) Self {
var state = Self{ .st = undefined };
mem.copy(u8, state.asBytes(), &initial_state);
state.endianSwap();
return state;
}
/// Initialize the state from u64 words in native endianness.
pub fn initFromWords(initial_state: [5]u64) Self {
var state = Self{ .st = initial_state };
return state;
}
/// Initialize the state for Ascon XOF
pub fn initXof() Self {
return Self{ .st = Block{
0xb57e273b814cd416,
0x2b51042562ae2420,
0x66a3a7768ddf2218,
0x5aad0a7a8153650c,
0x4f3e0e32539493b6,
} };
}
/// Initialize the state for Ascon XOFa
pub fn initXofA() Self {
return Self{ .st = Block{
0x44906568b77b9832,
0xcd8d6cae53455532,
0xf7b5212756422129,
0x246885e1de0d225b,
0xa8cb5ce33449973f,
} };
}
/// A representation of the state as bytes. The byte order is architecture-dependent.
pub fn asBytes(self: *Self) *[block_bytes]u8 {
return mem.asBytes(&self.st);
}
/// Byte-swap the entire state if the architecture doesn't match the required endianness.
pub fn endianSwap(self: *Self) void {
for (self.st) |*w| {
w.* = mem.toNative(u64, w.*, endian);
}
}
/// Set bytes starting at the beginning of the state.
pub fn setBytes(self: *Self, bytes: []const u8) void {
var i: usize = 0;
while (i + 8 <= bytes.len) : (i += 8) {
self.st[i / 8] = mem.readInt(u64, bytes[i..][0..8], endian);
}
if (i < bytes.len) {
var padded = [_]u8{0} ** 8;
mem.copy(u8, padded[0 .. bytes.len - i], bytes[i..]);
self.st[i / 8] = mem.readInt(u64, padded[0..], endian);
}
}
/// XOR a byte into the state at a given offset.
pub fn addByte(self: *Self, byte: u8, offset: usize) void {
const z = switch (endian) {
.Big => 64 - 8 - 8 * @truncate(u6, offset % 8),
.Little => 8 * @truncate(u6, offset % 8),
};
self.st[offset / 8] ^= @as(u64, byte) << z;
}
/// XOR bytes into the beginning of the state.
pub fn addBytes(self: *Self, bytes: []const u8) void {
var i: usize = 0;
while (i + 8 <= bytes.len) : (i += 8) {
self.st[i / 8] ^= mem.readInt(u64, bytes[i..][0..8], endian);
}
if (i < bytes.len) {
var padded = [_]u8{0} ** 8;
mem.copy(u8, padded[0 .. bytes.len - i], bytes[i..]);
self.st[i / 8] ^= mem.readInt(u64, padded[0..], endian);
}
}
/// Extract the first bytes of the state.
pub fn extractBytes(self: *Self, out: []u8) void {
var i: usize = 0;
while (i + 8 <= out.len) : (i += 8) {
mem.writeInt(u64, out[i..][0..8], self.st[i / 8], endian);
}
if (i < out.len) {
var padded = [_]u8{0} ** 8;
mem.writeInt(u64, padded[0..], self.st[i / 8], endian);
mem.copy(u8, out[i..], padded[0 .. out.len - i]);
}
}
/// XOR the first bytes of the state into a slice of bytes.
pub fn xorBytes(self: *Self, out: []u8, in: []const u8) void {
debug.assert(out.len == in.len);
var i: usize = 0;
while (i + 8 <= in.len) : (i += 8) {
const x = mem.readIntNative(u64, in[i..][0..8]) ^ mem.nativeTo(u64, self.st[i / 8], endian);
mem.writeIntNative(u64, out[i..][0..8], x);
}
if (i < in.len) {
var padded = [_]u8{0} ** 8;
mem.copy(u8, padded[0 .. in.len - i], in[i..]);
const x = mem.readIntNative(u64, &padded) ^ mem.nativeTo(u64, self.st[i / 8], endian);
mem.writeIntNative(u64, &padded, x);
mem.copy(u8, out[i..], padded[0 .. in.len - i]);
}
}
/// Set the words storing the bytes of a given range to zero.
pub fn clear(self: *Self, from: usize, to: usize) void {
mem.set(u64, self.st[from / 8 .. (to + 7) / 8], 0);
}
/// Clear the entire state, disabling compiler optimizations.
pub fn secureZero(self: *Self) void {
std.crypto.utils.secureZero(u64, &self.st);
}
/// Apply a reduced-round permutation to the state.
pub inline fn permuteR(state: *Self, comptime rounds: u4) void {
const rks = [12]u64{ 0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b };
inline for (rks[rks.len - rounds ..]) |rk| {
state.round(rk);
}
}
/// Apply a full-round permutation to the state.
pub inline fn permute(state: *Self) void {
state.permuteR(12);
}
// Core Ascon permutation.
inline fn round(state: *Self, rk: u64) void {
const x = &state.st;
x[2] ^= rk;
x[0] ^= x[4];
x[4] ^= x[3];
x[2] ^= x[1];
var t: Block = .{
x[0] ^ (~x[1] & x[2]),
x[1] ^ (~x[2] & x[3]),
x[2] ^ (~x[3] & x[4]),
x[3] ^ (~x[4] & x[0]),
x[4] ^ (~x[0] & x[1]),
};
t[1] ^= t[0];
t[3] ^= t[2];
t[0] ^= t[4];
x[2] = t[2] ^ rotr(u64, t[2], 6 - 1);
x[3] = t[3] ^ rotr(u64, t[3], 17 - 10);
x[4] = t[4] ^ rotr(u64, t[4], 41 - 7);
x[0] = t[0] ^ rotr(u64, t[0], 28 - 19);
x[1] = t[1] ^ rotr(u64, t[1], 61 - 39);
x[2] = t[2] ^ rotr(u64, x[2], 1);
x[3] = t[3] ^ rotr(u64, x[3], 10);
x[4] = t[4] ^ rotr(u64, x[4], 7);
x[0] = t[0] ^ rotr(u64, x[0], 19);
x[1] = t[1] ^ rotr(u64, x[1], 39);
x[2] = ~x[2];
}
};
}
test "ascon" {
const Ascon = State(.Big);
const bytes = [_]u8{0x01} ** Ascon.block_bytes;
var st = Ascon.init(bytes);
var out: [Ascon.block_bytes]u8 = undefined;
st.permute();
st.extractBytes(&out);
const expected1 = [_]u8{ 148, 147, 49, 226, 218, 221, 208, 113, 186, 94, 96, 10, 183, 219, 119, 150, 169, 206, 65, 18, 215, 97, 78, 106, 118, 81, 211, 150, 52, 17, 117, 64, 216, 45, 148, 240, 65, 181, 90, 180 };
try testing.expectEqualSlices(u8, &expected1, &out);
st.clear(0, 10);
st.extractBytes(&out);
const expected2 = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 169, 206, 65, 18, 215, 97, 78, 106, 118, 81, 211, 150, 52, 17, 117, 64, 216, 45, 148, 240, 65, 181, 90, 180 };
try testing.expectEqualSlices(u8, &expected2, &out);
st.addByte(1, 5);
st.addByte(2, 5);
st.extractBytes(&out);
const expected3 = [_]u8{ 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 169, 206, 65, 18, 215, 97, 78, 106, 118, 81, 211, 150, 52, 17, 117, 64, 216, 45, 148, 240, 65, 181, 90, 180 };
try testing.expectEqualSlices(u8, &expected3, &out);
st.addBytes(&bytes);
st.extractBytes(&out);
const expected4 = [_]u8{ 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 168, 207, 64, 19, 214, 96, 79, 107, 119, 80, 210, 151, 53, 16, 116, 65, 217, 44, 149, 241, 64, 180, 91, 181 };
try testing.expectEqualSlices(u8, &expected4, &out);
}

View File

@ -66,6 +66,7 @@ const macs = [_]Crypto{
Crypto{ .ty = crypto.auth.siphash.SipHash128(1, 3), .name = "siphash128-1-3" },
Crypto{ .ty = crypto.auth.aegis.Aegis128LMac, .name = "aegis-128l mac" },
Crypto{ .ty = crypto.auth.aegis.Aegis256Mac, .name = "aegis-256 mac" },
Crypto{ .ty = crypto.auth.cmac.CmacAes128, .name = "aes-cmac" },
};
pub fn benchmarkMac(comptime Mac: anytype, comptime bytes: comptime_int) !u64 {

156
lib/std/crypto/cmac.zig Normal file
View File

@ -0,0 +1,156 @@
const std = @import("std");
const crypto = std.crypto;
const mem = std.mem;
/// CMAC with AES-128 - RFC 4493 https://www.rfc-editor.org/rfc/rfc4493
pub const CmacAes128 = Cmac(crypto.core.aes.Aes128);
/// NIST Special Publication 800-38B - The CMAC Mode for Authentication
/// https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38b.pdf
pub fn Cmac(comptime BlockCipher: type) type {
const BlockCipherCtx = @typeInfo(@TypeOf(BlockCipher.initEnc)).Fn.return_type.?;
const Block = [BlockCipher.block.block_length]u8;
return struct {
const Self = @This();
pub const key_length = BlockCipher.key_bits / 8;
pub const block_length = BlockCipher.block.block_length;
pub const mac_length = block_length;
cipher_ctx: BlockCipherCtx,
k1: Block,
k2: Block,
buf: Block = [_]u8{0} ** block_length,
pos: usize = 0,
pub fn create(out: *[mac_length]u8, msg: []const u8, key: *const [key_length]u8) void {
var ctx = Self.init(key);
ctx.update(msg);
ctx.final(out);
}
pub fn init(key: *const [key_length]u8) Self {
const cipher_ctx = BlockCipher.initEnc(key.*);
const zeros = [_]u8{0} ** block_length;
var k1: Block = undefined;
cipher_ctx.encrypt(&k1, &zeros);
k1 = double(k1);
return Self{
.cipher_ctx = cipher_ctx,
.k1 = k1,
.k2 = double(k1),
};
}
pub fn update(self: *Self, msg: []const u8) void {
const left = block_length - self.pos;
var m = msg;
if (m.len > left) {
for (self.buf[self.pos..]) |*b, i| b.* ^= m[i];
m = m[left..];
self.cipher_ctx.encrypt(&self.buf, &self.buf);
self.pos = 0;
}
while (m.len > block_length) {
for (self.buf[0..block_length]) |*b, i| b.* ^= m[i];
m = m[block_length..];
self.cipher_ctx.encrypt(&self.buf, &self.buf);
self.pos = 0;
}
if (m.len > 0) {
for (self.buf[self.pos..][0..m.len]) |*b, i| b.* ^= m[i];
self.pos += m.len;
}
}
pub fn final(self: *Self, out: *[mac_length]u8) void {
var mac = self.k1;
if (self.pos < block_length) {
mac = self.k2;
mac[self.pos] ^= 0x80;
}
for (mac) |*b, i| b.* ^= self.buf[i];
self.cipher_ctx.encrypt(out, &mac);
}
fn double(l: Block) Block {
const Int = std.meta.Int(.unsigned, block_length * 8);
const l_ = mem.readIntBig(Int, &l);
const l_2 = switch (block_length) {
8 => (l_ << 1) ^ (0x1b & -%(l_ >> 63)), // mod x^64 + x^4 + x^3 + x + 1
16 => (l_ << 1) ^ (0x87 & -%(l_ >> 127)), // mod x^128 + x^7 + x^2 + x + 1
32 => (l_ << 1) ^ (0x0425 & -%(l_ >> 255)), // mod x^256 + x^10 + x^5 + x^2 + 1
64 => (l_ << 1) ^ (0x0125 & -%(l_ >> 511)), // mod x^512 + x^8 + x^5 + x^2 + 1
else => @compileError("unsupported block length"),
};
var l2: Block = undefined;
mem.writeIntBig(Int, &l2, l_2);
return l2;
}
};
}
const testing = std.testing;
test "CmacAes128 - Example 1: len = 0" {
const key = [_]u8{
0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c,
};
var msg: [0]u8 = undefined;
const exp = [_]u8{
0xbb, 0x1d, 0x69, 0x29, 0xe9, 0x59, 0x37, 0x28, 0x7f, 0xa3, 0x7d, 0x12, 0x9b, 0x75, 0x67, 0x46,
};
var out: [CmacAes128.mac_length]u8 = undefined;
CmacAes128.create(&out, &msg, &key);
try testing.expectEqualSlices(u8, &out, &exp);
}
test "CmacAes128 - Example 2: len = 16" {
const key = [_]u8{
0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c,
};
const msg = [_]u8{
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
};
const exp = [_]u8{
0x07, 0x0a, 0x16, 0xb4, 0x6b, 0x4d, 0x41, 0x44, 0xf7, 0x9b, 0xdd, 0x9d, 0xd0, 0x4a, 0x28, 0x7c,
};
var out: [CmacAes128.mac_length]u8 = undefined;
CmacAes128.create(&out, &msg, &key);
try testing.expectEqualSlices(u8, &out, &exp);
}
test "CmacAes128 - Example 3: len = 40" {
const key = [_]u8{
0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c,
};
const msg = [_]u8{
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
};
const exp = [_]u8{
0xdf, 0xa6, 0x67, 0x47, 0xde, 0x9a, 0xe6, 0x30, 0x30, 0xca, 0x32, 0x61, 0x14, 0x97, 0xc8, 0x27,
};
var out: [CmacAes128.mac_length]u8 = undefined;
CmacAes128.create(&out, &msg, &key);
try testing.expectEqualSlices(u8, &out, &exp);
}
test "CmacAes128 - Example 4: len = 64" {
const key = [_]u8{
0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c,
};
const msg = [_]u8{
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10,
};
const exp = [_]u8{
0x51, 0xf0, 0xbe, 0xbf, 0x7e, 0x3b, 0x9d, 0x92, 0xfc, 0x49, 0x74, 0x17, 0x79, 0x36, 0x3c, 0xfe,
};
var out: [CmacAes128.mac_length]u8 = undefined;
CmacAes128.create(&out, &msg, &key);
try testing.expectEqualSlices(u8, &out, &exp);
}

View File

@ -1,9 +1,11 @@
const std = @import("std");
const crypto = std.crypto;
const debug = std.debug;
const mem = std.mem;
const math = std.math;
const testing = std.testing;
const AuthenticationError = std.crypto.errors.AuthenticationError;
const Ascon = crypto.core.Ascon(.Big);
const AuthenticationError = crypto.errors.AuthenticationError;
/// ISAPv2 is an authenticated encryption system hardened against side channels and fault attacks.
/// https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/round-2/spec-doc-rnd2/isap-spec-round2.pdf
@ -25,90 +27,26 @@ pub const IsapA128A = struct {
const iv2 = [_]u8{ 0x02, 0x80, 0x40, 0x01, 0x0c, 0x01, 0x06, 0x0c };
const iv3 = [_]u8{ 0x03, 0x80, 0x40, 0x01, 0x0c, 0x01, 0x06, 0x0c };
const Block = [5]u64;
block: Block,
fn round(isap: *IsapA128A, rk: u64) void {
var x = &isap.block;
x[2] ^= rk;
x[0] ^= x[4];
x[4] ^= x[3];
x[2] ^= x[1];
var t = x.*;
x[0] = t[0] ^ ((~t[1]) & t[2]);
x[2] = t[2] ^ ((~t[3]) & t[4]);
x[4] = t[4] ^ ((~t[0]) & t[1]);
x[1] = t[1] ^ ((~t[2]) & t[3]);
x[3] = t[3] ^ ((~t[4]) & t[0]);
x[1] ^= x[0];
t[1] = x[1];
x[1] = math.rotr(u64, x[1], 39);
x[3] ^= x[2];
t[2] = x[2];
x[2] = math.rotr(u64, x[2], 1);
t[4] = x[4];
t[2] ^= x[2];
x[2] = math.rotr(u64, x[2], 5);
t[3] = x[3];
t[1] ^= x[1];
x[3] = math.rotr(u64, x[3], 10);
x[0] ^= x[4];
x[4] = math.rotr(u64, x[4], 7);
t[3] ^= x[3];
x[2] ^= t[2];
x[1] = math.rotr(u64, x[1], 22);
t[0] = x[0];
x[2] = ~x[2];
x[3] = math.rotr(u64, x[3], 7);
t[4] ^= x[4];
x[4] = math.rotr(u64, x[4], 34);
x[3] ^= t[3];
x[1] ^= t[1];
x[0] = math.rotr(u64, x[0], 19);
x[4] ^= t[4];
t[0] ^= x[0];
x[0] = math.rotr(u64, x[0], 9);
x[0] ^= t[0];
}
fn p12(isap: *IsapA128A) void {
const rks = [12]u64{ 0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b };
inline for (rks) |rk| {
isap.round(rk);
}
}
fn p6(isap: *IsapA128A) void {
const rks = [6]u64{ 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b };
inline for (rks) |rk| {
isap.round(rk);
}
}
fn p1(isap: *IsapA128A) void {
isap.round(0x4b);
}
st: Ascon,
fn absorb(isap: *IsapA128A, m: []const u8) void {
var block = &isap.block;
var i: usize = 0;
while (true) : (i += 8) {
const left = m.len - i;
if (left >= 8) {
block[0] ^= mem.readIntBig(u64, m[i..][0..8]);
isap.p12();
isap.st.addBytes(m[i..][0..8]);
isap.st.permute();
if (left == 8) {
block[0] ^= 0x8000000000000000;
isap.p12();
isap.st.addByte(0x80, 0);
isap.st.permute();
break;
}
} else {
var padded = [_]u8{0} ** 8;
mem.copy(u8, padded[0..left], m[i..]);
padded[left] = 0x80;
block[0] ^= mem.readIntBig(u64, padded[0..]);
isap.p12();
isap.st.addBytes(&padded);
isap.st.permute();
break;
}
}
@ -116,65 +54,59 @@ pub const IsapA128A = struct {
fn trickle(k: [16]u8, iv: [8]u8, y: []const u8, comptime out_len: usize) [out_len]u8 {
var isap = IsapA128A{
.block = Block{
.st = Ascon.initFromWords(.{
mem.readIntBig(u64, k[0..8]),
mem.readIntBig(u64, k[8..16]),
mem.readIntBig(u64, iv[0..8]),
0,
0,
},
}),
};
isap.p12();
isap.st.permute();
var i: usize = 0;
while (i < y.len * 8 - 1) : (i += 1) {
const cur_byte_pos = i / 8;
const cur_bit_pos = @truncate(u3, 7 - (i % 8));
const cur_bit = @as(u64, ((y[cur_byte_pos] >> cur_bit_pos) & 1) << 7);
isap.block[0] ^= cur_bit << 56;
isap.p1();
const cur_bit = ((y[cur_byte_pos] >> cur_bit_pos) & 1) << 7;
isap.st.addByte(cur_bit, 0);
isap.st.permuteR(1);
}
const cur_bit = @as(u64, (y[y.len - 1] & 1) << 7);
isap.block[0] ^= cur_bit << 56;
isap.p12();
const cur_bit = (y[y.len - 1] & 1) << 7;
isap.st.addByte(cur_bit, 0);
isap.st.permute();
var out: [out_len]u8 = undefined;
var j: usize = 0;
while (j < out_len) : (j += 8) {
mem.writeIntBig(u64, out[j..][0..8], isap.block[j / 8]);
}
std.crypto.utils.secureZero(u64, &isap.block);
isap.st.extractBytes(&out);
isap.st.secureZero();
return out;
}
fn mac(c: []const u8, ad: []const u8, npub: [16]u8, key: [16]u8) [16]u8 {
var isap = IsapA128A{
.block = Block{
.st = Ascon.initFromWords(.{
mem.readIntBig(u64, npub[0..8]),
mem.readIntBig(u64, npub[8..16]),
mem.readIntBig(u64, iv1[0..]),
0,
0,
},
}),
};
isap.p12();
isap.st.permute();
isap.absorb(ad);
isap.block[4] ^= 1;
isap.st.addByte(1, Ascon.block_bytes - 1);
isap.absorb(c);
var y: [16]u8 = undefined;
mem.writeIntBig(u64, y[0..8], isap.block[0]);
mem.writeIntBig(u64, y[8..16], isap.block[1]);
isap.st.extractBytes(&y);
const nb = trickle(key, iv2, y[0..], 16);
isap.block[0] = mem.readIntBig(u64, nb[0..8]);
isap.block[1] = mem.readIntBig(u64, nb[8..16]);
isap.p12();
isap.st.setBytes(&nb);
isap.st.permute();
var tag: [16]u8 = undefined;
mem.writeIntBig(u64, tag[0..8], isap.block[0]);
mem.writeIntBig(u64, tag[8..16], isap.block[1]);
std.crypto.utils.secureZero(u64, &isap.block);
isap.st.extractBytes(&tag);
isap.st.secureZero();
return tag;
}
@ -183,34 +115,31 @@ pub const IsapA128A = struct {
const nb = trickle(key, iv3, npub[0..], 24);
var isap = IsapA128A{
.block = Block{
.st = Ascon.initFromWords(.{
mem.readIntBig(u64, nb[0..8]),
mem.readIntBig(u64, nb[8..16]),
mem.readIntBig(u64, nb[16..24]),
mem.readIntBig(u64, npub[0..8]),
mem.readIntBig(u64, npub[8..16]),
},
}),
};
isap.p6();
isap.st.permuteR(6);
var i: usize = 0;
while (true) : (i += 8) {
const left = in.len - i;
if (left >= 8) {
mem.writeIntNative(u64, out[i..][0..8], mem.bigToNative(u64, isap.block[0]) ^ mem.readIntNative(u64, in[i..][0..8]));
isap.st.xorBytes(out[i..][0..8], in[i..][0..8]);
if (left == 8) {
break;
}
isap.p6();
isap.st.permuteR(6);
} else {
var pad = [_]u8{0} ** 8;
mem.copy(u8, pad[0..left], in[i..][0..left]);
mem.writeIntNative(u64, pad[i..][0..8], mem.bigToNative(u64, isap.block[0]) ^ mem.readIntNative(u64, pad[i..][0..8]));
mem.copy(u8, out[i..][0..left], pad[0..left]);
isap.st.xorBytes(out[i..], in[i..]);
break;
}
}
std.crypto.utils.secureZero(u64, &isap.block);
isap.st.secureZero();
}
pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) void {
@ -220,12 +149,9 @@ pub const IsapA128A = struct {
pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) AuthenticationError!void {
var computed_tag = mac(c, ad, npub, key);
var acc: u8 = 0;
for (computed_tag) |_, j| {
acc |= (computed_tag[j] ^ tag[j]);
}
std.crypto.utils.secureZero(u8, &computed_tag);
if (acc != 0) {
const res = crypto.utils.timingSafeEql([tag_length]u8, computed_tag, tag);
crypto.utils.secureZero(u8, &computed_tag);
if (!res) {
return error.AuthenticationFailed;
}
xor(m, c, npub, key);

View File

@ -1763,7 +1763,7 @@ pub const Dir = struct {
var nt_name = w.UNICODE_STRING{
.Length = path_len_bytes,
.MaximumLength = path_len_bytes,
.Buffer = @qualCast([*:0]u16, sub_path_w),
.Buffer = @constCast(sub_path_w),
};
var attr = w.OBJECT_ATTRIBUTES{
.Length = @sizeOf(w.OBJECT_ATTRIBUTES),

View File

@ -1163,11 +1163,12 @@ const ArrayList = std.ArrayList;
const StringArrayHashMap = std.StringArrayHashMap;
pub const ValueTree = struct {
arena: ArenaAllocator,
arena: *ArenaAllocator,
root: Value,
pub fn deinit(self: *ValueTree) void {
self.arena.deinit();
self.arena.child_allocator.destroy(self.arena);
}
};
@ -1639,7 +1640,7 @@ fn parseInternal(
const allocator = options.allocator orelse return error.AllocatorRequired;
switch (ptrInfo.size) {
.One => {
const r: T = try allocator.create(ptrInfo.child);
const r: *ptrInfo.child = try allocator.create(ptrInfo.child);
errdefer allocator.destroy(r);
r.* = try parseInternal(ptrInfo.child, token, tokens, options);
return r;
@ -1678,19 +1679,16 @@ fn parseInternal(
if (ptrInfo.child != u8) return error.UnexpectedToken;
const source_slice = stringToken.slice(tokens.slice, tokens.i - 1);
const len = stringToken.decodedLength();
const output = try allocator.alloc(u8, len + @boolToInt(ptrInfo.sentinel != null));
const output = if (ptrInfo.sentinel) |sentinel_ptr|
try allocator.allocSentinel(u8, len, @ptrCast(*const u8, sentinel_ptr).*)
else
try allocator.alloc(u8, len);
errdefer allocator.free(output);
switch (stringToken.escapes) {
.None => mem.copy(u8, output, source_slice),
.Some => try unescapeValidString(output, source_slice),
}
if (ptrInfo.sentinel) |some| {
const char = @ptrCast(*const u8, some).*;
output[len] = char;
return output[0..len :char];
}
return output;
},
else => return error.UnexpectedToken,
@ -1744,7 +1742,37 @@ pub fn parseFree(comptime T: type, value: T, options: ParseOptions) void {
.Struct => |structInfo| {
inline for (structInfo.fields) |field| {
if (!field.is_comptime) {
parseFree(field.type, @field(value, field.name), options);
var should_free = true;
if (field.default_value) |default| {
switch (@typeInfo(field.type)) {
// We must not attempt to free pointers to struct default values
.Pointer => |fieldPtrInfo| {
const field_value = @field(value, field.name);
const field_ptr = switch (fieldPtrInfo.size) {
.One => field_value,
.Slice => field_value.ptr,
else => unreachable, // Other pointer types are not parseable
};
const field_addr = @ptrToInt(field_ptr);
const casted_default = @ptrCast(*const field.type, @alignCast(@alignOf(field.type), default)).*;
const default_ptr = switch (fieldPtrInfo.size) {
.One => casted_default,
.Slice => casted_default.ptr,
else => unreachable, // Other pointer types are not parseable
};
const default_addr = @ptrToInt(default_ptr);
if (field_addr == default_addr) {
should_free = false;
}
},
else => {},
}
}
if (should_free) {
parseFree(field.type, @field(value, field.name), options);
}
}
}
},
@ -1809,8 +1837,12 @@ pub const Parser = struct {
pub fn parse(p: *Parser, input: []const u8) !ValueTree {
var s = TokenStream.init(input);
var arena = ArenaAllocator.init(p.allocator);
var arena = try p.allocator.create(ArenaAllocator);
errdefer p.allocator.destroy(arena);
arena.* = ArenaAllocator.init(p.allocator);
errdefer arena.deinit();
const allocator = arena.allocator();
while (try s.next()) |token| {
@ -2684,3 +2716,16 @@ test "encodesTo" {
try testing.expectEqual(true, encodesTo("😂", "\\ud83d\\ude02"));
try testing.expectEqual(true, encodesTo("withąunicode😂", "with\\u0105unicode\\ud83d\\ude02"));
}
test "issue 14600" {
const json = "\"\\n\"";
var token_stream = std.json.TokenStream.init(json);
const options = ParseOptions{ .allocator = std.testing.allocator };
// Pre-fix, this line would panic:
const result = try std.json.parse([:0]const u8, &token_stream, options);
defer std.json.parseFree([:0]const u8, result, options);
// Double-check that we're getting the right result
try testing.expect(mem.eql(u8, result, "\n"));
}

View File

@ -2238,6 +2238,39 @@ test "parse into struct with no fields" {
try testing.expectEqual(T{}, try parse(T, &ts, ParseOptions{}));
}
const test_const_value: usize = 123;
test "parse into struct with default const pointer field" {
const T = struct { a: *const usize = &test_const_value };
var ts = TokenStream.init("{}");
try testing.expectEqual(T{}, try parse(T, &ts, .{}));
}
const test_default_usize: usize = 123;
const test_default_usize_ptr: *align(1) const usize = &test_default_usize;
const test_default_str: []const u8 = "test str";
const test_default_str_slice: [2][]const u8 = [_][]const u8{
"test1",
"test2",
};
test "freeing parsed structs with pointers to default values" {
const T = struct {
int: *const usize = &test_default_usize,
int_ptr: *allowzero align(1) const usize = test_default_usize_ptr,
str: []const u8 = test_default_str,
str_slice: []const []const u8 = &test_default_str_slice,
};
var ts = json.TokenStream.init("{}");
const options = .{ .allocator = std.heap.page_allocator };
const parsed = try json.parse(T, &ts, options);
try testing.expectEqual(T{}, parsed);
json.parseFree(T, parsed, options);
}
test "parse into struct where destination and source lengths mismatch" {
const T = struct { a: [2]u8 };
var ts = TokenStream.init("{\"a\": \"bbb\"}");
@ -2581,6 +2614,24 @@ test "parsing empty string gives appropriate error" {
try testing.expectError(error.UnexpectedEndOfJson, testParse(arena_allocator.allocator(), ""));
}
test "parse tree should not contain dangling pointers" {
var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena_allocator.deinit();
var p = json.Parser.init(arena_allocator.allocator(), false);
defer p.deinit();
var tree = try p.parse("[]");
defer tree.deinit();
// Allocation should succeed
var i: usize = 0;
while (i < 100) : (i += 1) {
try tree.root.Array.append(std.json.Value{ .Integer = 100 });
}
try testing.expectEqual(tree.root.Array.items.len, 100);
}
test "integer after float has proper type" {
var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena_allocator.deinit();

View File

@ -112,7 +112,7 @@ pub fn destroy(self: Allocator, ptr: anytype) void {
const info = @typeInfo(@TypeOf(ptr)).Pointer;
const T = info.child;
if (@sizeOf(T) == 0) return;
const non_const_ptr = @intToPtr([*]u8, @ptrToInt(ptr));
const non_const_ptr = @ptrCast([*]u8, @constCast(ptr));
self.rawFree(non_const_ptr[0..@sizeOf(T)], math.log2(info.alignment), @returnAddress());
}
@ -297,7 +297,7 @@ pub fn free(self: Allocator, memory: anytype) void {
const bytes = mem.sliceAsBytes(memory);
const bytes_len = bytes.len + if (Slice.sentinel != null) @sizeOf(Slice.child) else 0;
if (bytes_len == 0) return;
const non_const_ptr = @intToPtr([*]u8, @ptrToInt(bytes.ptr));
const non_const_ptr = @constCast(bytes.ptr);
// TODO: https://github.com/ziglang/zig/issues/4298
@memset(non_const_ptr, undefined, bytes_len);
self.rawFree(non_const_ptr[0..bytes_len], log2a(Slice.alignment), @returnAddress());

View File

@ -332,7 +332,7 @@ pub fn Sentinel(comptime T: type, comptime sentinel_val: Elem(T)) type {
@compileError("Unable to derive a sentinel pointer type from " ++ @typeName(T));
}
const assumeSentinel = @compileError("This function has been removed, consider using std.mem.sliceTo() or if needed a @ptrCast()");
pub const assumeSentinel = @compileError("This function has been removed, consider using std.mem.sliceTo() or if needed a @ptrCast()");
pub fn containerLayout(comptime T: type) Type.ContainerLayout {
return switch (@typeInfo(T)) {

View File

@ -4513,7 +4513,7 @@ pub fn faccessatW(dirfd: fd_t, sub_path_w: [*:0]const u16, mode: u32, flags: u32
var nt_name = windows.UNICODE_STRING{
.Length = path_len_bytes,
.MaximumLength = path_len_bytes,
.Buffer = @qualCast([*:0]u16, sub_path_w),
.Buffer = @constCast(sub_path_w),
};
var attr = windows.OBJECT_ATTRIBUTES{
.Length = @sizeOf(windows.OBJECT_ATTRIBUTES),

View File

@ -2057,7 +2057,7 @@ pub const Mips64 = enum(usize) {
writev = Linux + 19,
access = Linux + 20,
pipe = Linux + 21,
select = Linux + 22,
_newselect = Linux + 22,
sched_yield = Linux + 23,
mremap = Linux + 24,
msync = Linux + 25,
@ -2071,8 +2071,8 @@ pub const Mips64 = enum(usize) {
pause = Linux + 33,
nanosleep = Linux + 34,
getitimer = Linux + 35,
alarm = Linux + 36,
setitimer = Linux + 37,
setitimer = Linux + 36,
alarm = Linux + 37,
getpid = Linux + 38,
sendfile = Linux + 39,
socket = Linux + 40,
@ -2286,7 +2286,7 @@ pub const Mips64 = enum(usize) {
mknodat = Linux + 249,
fchownat = Linux + 250,
futimesat = Linux + 251,
newfstatat = Linux + 252,
fstatat64 = Linux + 252,
unlinkat = Linux + 253,
renameat = Linux + 254,
linkat = Linux + 255,
@ -2315,8 +2315,8 @@ pub const Mips64 = enum(usize) {
eventfd = Linux + 278,
fallocate = Linux + 279,
timerfd_create = Linux + 280,
timerfd_settime = Linux + 281,
timerfd_gettime = Linux + 282,
timerfd_gettime = Linux + 281,
timerfd_settime = Linux + 282,
signalfd4 = Linux + 283,
eventfd2 = Linux + 284,
epoll_create1 = Linux + 285,
@ -2382,9 +2382,13 @@ pub const Mips64 = enum(usize) {
process_madvise = Linux + 440,
epoll_pwait2 = Linux + 441,
mount_setattr = Linux + 442,
quotactl_fd = Linux + 443,
landlock_create_ruleset = Linux + 444,
landlock_add_rule = Linux + 445,
landlock_restrict_self = Linux + 446,
process_mrelease = Linux + 448,
futex_waitv = Linux + 449,
set_mempolicy_home_node = Linux + 450,
};
pub const PowerPC = enum(usize) {

View File

@ -85,7 +85,7 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN
var nt_name = UNICODE_STRING{
.Length = path_len_bytes,
.MaximumLength = path_len_bytes,
.Buffer = @qualCast([*]u16, sub_path_w.ptr),
.Buffer = @constCast(sub_path_w.ptr),
};
var attr = OBJECT_ATTRIBUTES{
.Length = @sizeOf(OBJECT_ATTRIBUTES),
@ -634,7 +634,7 @@ pub fn SetCurrentDirectory(path_name: []const u16) SetCurrentDirectoryError!void
var nt_name = UNICODE_STRING{
.Length = path_len_bytes,
.MaximumLength = path_len_bytes,
.Buffer = @qualCast([*]u16, path_name.ptr),
.Buffer = @constCast(path_name.ptr),
};
const rc = ntdll.RtlSetCurrentDirectory_U(&nt_name);
@ -766,7 +766,7 @@ pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u8) ReadLin
var nt_name = UNICODE_STRING{
.Length = path_len_bytes,
.MaximumLength = path_len_bytes,
.Buffer = @qualCast([*]u16, sub_path_w.ptr),
.Buffer = @constCast(sub_path_w.ptr),
};
var attr = OBJECT_ATTRIBUTES{
.Length = @sizeOf(OBJECT_ATTRIBUTES),
@ -876,7 +876,7 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil
.Length = path_len_bytes,
.MaximumLength = path_len_bytes,
// The Windows API makes this mutable, but it will not mutate here.
.Buffer = @qualCast([*]u16, sub_path_w.ptr),
.Buffer = @constCast(sub_path_w.ptr),
};
if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
@ -1414,7 +1414,7 @@ pub fn sendmsg(
}
pub fn sendto(s: ws2_32.SOCKET, buf: [*]const u8, len: usize, flags: u32, to: ?*const ws2_32.sockaddr, to_len: ws2_32.socklen_t) i32 {
var buffer = ws2_32.WSABUF{ .len = @truncate(u31, len), .buf = @qualCast([*]u8, buf) };
var buffer = ws2_32.WSABUF{ .len = @truncate(u31, len), .buf = @constCast(buf) };
var bytes_send: DWORD = undefined;
if (ws2_32.WSASendTo(s, @ptrCast([*]ws2_32.WSABUF, &buffer), 1, &bytes_send, flags, to, @intCast(i32, to_len), null, null) == ws2_32.SOCKET_ERROR) {
return ws2_32.SOCKET_ERROR;
@ -1876,13 +1876,13 @@ pub fn eqlIgnoreCaseWTF16(a: []const u16, b: []const u16) bool {
const a_string = UNICODE_STRING{
.Length = a_bytes,
.MaximumLength = a_bytes,
.Buffer = @qualCast([*]u16, a.ptr),
.Buffer = @constCast(a.ptr),
};
const b_bytes = @intCast(u16, b.len * 2);
const b_string = UNICODE_STRING{
.Length = b_bytes,
.MaximumLength = b_bytes,
.Buffer = @qualCast([*]u16, b.ptr),
.Buffer = @constCast(b.ptr),
};
return ntdll.RtlEqualUnicodeString(&a_string, &b_string, TRUE) == TRUE;
}

View File

@ -18,8 +18,9 @@ const maxInt = std.math.maxInt;
pub const DefaultPrng = Xoshiro256;
/// Cryptographically secure random numbers.
pub const DefaultCsprng = Xoodoo;
pub const DefaultCsprng = Ascon;
pub const Ascon = @import("rand/Ascon.zig");
pub const Isaac64 = @import("rand/Isaac64.zig");
pub const Xoodoo = @import("rand/Xoodoo.zig");
pub const Pcg = @import("rand/Pcg.zig");

45
lib/std/rand/Ascon.zig Normal file
View File

@ -0,0 +1,45 @@
//! CSPRNG based on the Ascon XOFa construction
const std = @import("std");
const min = std.math.min;
const mem = std.mem;
const Random = std.rand.Random;
const Self = @This();
state: std.crypto.core.Ascon(.Little),
const rate = 8;
pub const secret_seed_length = 32;
/// The seed must be uniform, secret and `secret_seed_length` bytes long.
pub fn init(secret_seed: [secret_seed_length]u8) Self {
var state = std.crypto.core.Ascon(.Little).initXofA();
var i: usize = 0;
while (i + rate <= secret_seed.len) : (i += rate) {
state.addBytes(secret_seed[i..][0..rate]);
state.permuteR(8);
}
const left = secret_seed.len - i;
if (left > 0) state.addBytes(secret_seed[i..]);
state.addByte(0x80, left);
state.permute();
return Self{ .state = state };
}
pub fn random(self: *Self) Random {
return Random.init(self, fill);
}
pub fn fill(self: *Self, buf: []u8) void {
var i: usize = 0;
while (true) {
const left = buf.len - i;
const n = min(left, rate);
self.state.extractBytes(buf[i..][0..n]);
if (left == 0) break;
self.state.permuteR(8);
i += n;
}
self.state.clear(0, rate);
self.state.permuteR(8);
}

View File

@ -207,7 +207,7 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void {
return stream.writeAll("declarations are not allowed between container fields");
},
.expected_block => {
return stream.print("expected block or field, found '{s}'", .{
return stream.print("expected block, found '{s}'", .{
token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(),
});
},

View File

@ -74,8 +74,10 @@ fn castPtr(comptime DestType: type, target: anytype) DestType {
const dest = ptrInfo(DestType);
const source = ptrInfo(@TypeOf(target));
if (source.is_const and !dest.is_const or source.is_volatile and !dest.is_volatile)
return @qualCast(DestType, target)
if (source.is_const and !dest.is_const)
return @constCast(target)
else if (source.is_volatile and !dest.is_volatile)
return @volatileCast(target)
else if (@typeInfo(dest.child) == .Opaque)
// dest.alignment would error out
return @ptrCast(DestType, target)

View File

@ -2807,14 +2807,6 @@ fn nodeIsBlock(tag: Ast.Node.Tag) bool {
.block_semicolon,
.block_two,
.block_two_semicolon,
.struct_init_dot,
.struct_init_dot_comma,
.struct_init_dot_two,
.struct_init_dot_two_comma,
.array_init_dot,
.array_init_dot_comma,
.array_init_dot_two,
.array_init_dot_two_comma,
=> true,
else => false,
};

View File

@ -739,6 +739,7 @@ pub const Tokenizer = struct {
},
0 => {
if (self.index == self.buffer.len) {
result.tag = .invalid;
break;
} else {
self.checkLiteralCharacter();
@ -1326,7 +1327,7 @@ test "newline in string literal" {
try testTokenize(
\\"
\\"
, &.{ .invalid, .string_literal });
, &.{ .invalid, .invalid });
}
test "code point literal with unicode escapes" {

View File

@ -1730,7 +1730,8 @@ fn structInitExprRlNone(
.container_type = ty_inst,
.name_start = str_index,
}) } }
else .{ .rl = .none };
else
.{ .rl = .none };
setExtra(astgen, extra_index, Zir.Inst.StructInitAnon.Item{
.field_name = str_index,
.init = try expr(gz, scope, sub_ri, field_init),
@ -2530,7 +2531,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
.bit_size_of,
.typeof_log2_int_type,
.ptr_to_int,
.qual_cast,
.align_of,
.bool_to_int,
.embed_file,
@ -8038,7 +8038,6 @@ fn builtinCall(
.float_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .float_cast),
.int_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .int_cast),
.ptr_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .ptr_cast),
.qual_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .qual_cast),
.truncate => return typeCast(gz, scope, ri, node, params[0], params[1], .truncate),
// zig fmt: on
@ -8114,6 +8113,22 @@ fn builtinCall(
});
return rvalue(gz, ri, result, node);
},
.const_cast => {
const operand = try expr(gz, scope, .{ .rl = .none }, params[0]);
const result = try gz.addExtendedPayload(.const_cast, Zir.Inst.UnNode{
.node = gz.nodeIndexToRelative(node),
.operand = operand,
});
return rvalue(gz, ri, result, node);
},
.volatile_cast => {
const operand = try expr(gz, scope, .{ .rl = .none }, params[0]);
const result = try gz.addExtendedPayload(.volatile_cast, Zir.Inst.UnNode{
.node = gz.nodeIndexToRelative(node),
.operand = operand,
});
return rvalue(gz, ri, result, node);
},
// zig fmt: off
.has_decl => return hasDeclOrField(gz, scope, ri, node, params[0], params[1], .has_decl),

View File

@ -1400,7 +1400,6 @@ fn walkInstruction(
.float_cast,
.int_cast,
.ptr_cast,
.qual_cast,
.truncate,
.align_cast,
.has_decl,
@ -2983,6 +2982,8 @@ fn walkInstruction(
.error_to_int,
.int_to_error,
.reify,
.const_cast,
.volatile_cast,
=> {
const extra = file.zir.extraData(Zir.Inst.UnNode, extended.operand).data;
const bin_index = self.exprs.items.len;

View File

@ -28,6 +28,7 @@ pub const Tag = enum {
cmpxchg_weak,
compile_error,
compile_log,
const_cast,
ctz,
c_undef,
c_va_arg,
@ -75,7 +76,6 @@ pub const Tag = enum {
prefetch,
ptr_cast,
ptr_to_int,
qual_cast,
rem,
return_address,
select,
@ -116,6 +116,7 @@ pub const Tag = enum {
TypeOf,
union_init,
Vector,
volatile_cast,
};
pub const MemLocRequirement = enum {
@ -345,6 +346,13 @@ pub const list = list: {
.param_count = null,
},
},
.{
"@constCast",
.{
.tag = .const_cast,
.param_count = 1,
},
},
.{
"@ctz",
.{
@ -675,13 +683,6 @@ pub const list = list: {
.param_count = 1,
},
},
.{
"@qualCast",
.{
.tag = .qual_cast,
.param_count = 2,
},
},
.{
"@rem",
.{
@ -964,5 +965,12 @@ pub const list = list: {
.param_count = 2,
},
},
.{
"@volatileCast",
.{
.tag = .volatile_cast,
.param_count = 1,
},
},
});
};

View File

@ -26,7 +26,7 @@ const wasi_libc = @import("wasi_libc.zig");
const fatal = @import("main.zig").fatal;
const clangMain = @import("main.zig").clangMain;
const Module = @import("Module.zig");
const Cache = @import("Cache.zig");
const Cache = std.Build.Cache;
const translate_c = @import("translate_c.zig");
const clang = @import("clang.zig");
const c_codegen = @import("codegen/c.zig");
@ -807,44 +807,7 @@ pub const AllErrors = struct {
}
};
pub const Directory = struct {
/// This field is redundant for operations that can act on the open directory handle
/// directly, but it is needed when passing the directory to a child process.
/// `null` means cwd.
path: ?[]const u8,
handle: std.fs.Dir,
pub fn join(self: Directory, allocator: Allocator, paths: []const []const u8) ![]u8 {
if (self.path) |p| {
// TODO clean way to do this with only 1 allocation
const part2 = try std.fs.path.join(allocator, paths);
defer allocator.free(part2);
return std.fs.path.join(allocator, &[_][]const u8{ p, part2 });
} else {
return std.fs.path.join(allocator, paths);
}
}
pub fn joinZ(self: Directory, allocator: Allocator, paths: []const []const u8) ![:0]u8 {
if (self.path) |p| {
// TODO clean way to do this with only 1 allocation
const part2 = try std.fs.path.join(allocator, paths);
defer allocator.free(part2);
return std.fs.path.joinZ(allocator, &[_][]const u8{ p, part2 });
} else {
return std.fs.path.joinZ(allocator, paths);
}
}
/// Whether or not the handle should be closed, or the path should be freed
/// is determined by usage, however this function is provided for convenience
/// if it happens to be what the caller needs.
pub fn closeAndFree(self: *Directory, gpa: Allocator) void {
self.handle.close();
if (self.path) |p| gpa.free(p);
self.* = undefined;
}
};
pub const Directory = Cache.Directory;
pub const EmitLoc = struct {
/// If this is `null` it means the file will be output to the cache directory.
@ -854,6 +817,35 @@ pub const EmitLoc = struct {
basename: []const u8,
};
pub const cache_helpers = struct {
pub fn addEmitLoc(hh: *Cache.HashHelper, emit_loc: EmitLoc) void {
hh.addBytes(emit_loc.basename);
}
pub fn addOptionalEmitLoc(hh: *Cache.HashHelper, optional_emit_loc: ?EmitLoc) void {
hh.add(optional_emit_loc != null);
addEmitLoc(hh, optional_emit_loc orelse return);
}
pub fn hashCSource(self: *Cache.Manifest, c_source: Compilation.CSourceFile) !void {
_ = try self.addFile(c_source.src_path, null);
// Hash the extra flags, with special care to call addFile for file parameters.
// TODO this logic can likely be improved by utilizing clang_options_data.zig.
const file_args = [_][]const u8{"-include"};
var arg_i: usize = 0;
while (arg_i < c_source.extra_flags.len) : (arg_i += 1) {
const arg = c_source.extra_flags[arg_i];
self.hash.addBytes(arg);
for (file_args) |file_arg| {
if (mem.eql(u8, file_arg, arg) and arg_i + 1 < c_source.extra_flags.len) {
arg_i += 1;
_ = try self.addFile(c_source.extra_flags[arg_i], null);
}
}
}
}
};
pub const ClangPreprocessorMode = enum {
no,
/// This means we are doing `zig cc -E -o <path>`.
@ -997,6 +989,7 @@ pub const InitOptions = struct {
linker_optimization: ?u8 = null,
linker_compress_debug_sections: ?link.CompressDebugSections = null,
linker_module_definition_file: ?[]const u8 = null,
linker_sort_section: ?link.SortSection = null,
major_subsystem_version: ?u32 = null,
minor_subsystem_version: ?u32 = null,
clang_passthrough_mode: bool = false,
@ -1029,6 +1022,7 @@ pub const InitOptions = struct {
/// This is for stage1 and should be deleted upon completion of self-hosting.
color: Color = .auto,
reference_trace: ?u32 = null,
error_tracing: ?bool = null,
test_filter: ?[]const u8 = null,
test_name_prefix: ?[]const u8 = null,
test_runner_path: ?[]const u8 = null,
@ -1522,8 +1516,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
cache.hash.add(link_libunwind);
cache.hash.add(options.output_mode);
cache.hash.add(options.machine_code_model);
cache.hash.addOptionalEmitLoc(options.emit_bin);
cache.hash.addOptionalEmitLoc(options.emit_implib);
cache_helpers.addOptionalEmitLoc(&cache.hash, options.emit_bin);
cache_helpers.addOptionalEmitLoc(&cache.hash, options.emit_implib);
cache.hash.addBytes(options.root_name);
if (options.target.os.tag == .wasi) cache.hash.add(wasi_exec_model);
// TODO audit this and make sure everything is in it
@ -1621,26 +1615,46 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
const root_pkg = if (options.is_test) root_pkg: {
// TODO: we currently have two packages named 'root' here, which is weird. This
// should be changed as part of the resolution of #12201
const test_pkg = if (options.test_runner_path) |test_runner|
try Package.create(gpa, "root", null, test_runner)
else
try Package.createWithDir(
gpa,
"root",
options.zig_lib_directory,
null,
"test_runner.zig",
);
const test_pkg = if (options.test_runner_path) |test_runner| test_pkg: {
const test_dir = std.fs.path.dirname(test_runner);
const basename = std.fs.path.basename(test_runner);
const pkg = try Package.create(gpa, "root", test_dir, basename);
// copy package table from main_pkg to root_pkg
pkg.table = try main_pkg.table.clone(gpa);
break :test_pkg pkg;
} else try Package.createWithDir(
gpa,
"root",
options.zig_lib_directory,
null,
"test_runner.zig",
);
errdefer test_pkg.destroy(gpa);
break :root_pkg test_pkg;
} else main_pkg;
errdefer if (options.is_test) root_pkg.destroy(gpa);
const compiler_rt_pkg = if (include_compiler_rt and options.output_mode == .Obj) compiler_rt_pkg: {
break :compiler_rt_pkg try Package.createWithDir(
gpa,
"compiler_rt",
options.zig_lib_directory,
null,
"compiler_rt.zig",
);
} else null;
errdefer if (compiler_rt_pkg) |p| p.destroy(gpa);
try main_pkg.addAndAdopt(gpa, builtin_pkg);
try main_pkg.add(gpa, root_pkg);
try main_pkg.addAndAdopt(gpa, std_pkg);
if (compiler_rt_pkg) |p| {
try main_pkg.addAndAdopt(gpa, p);
}
const main_pkg_is_std = m: {
const std_path = try std.fs.path.resolve(arena, &[_][]const u8{
std_pkg.root_src_directory.path orelse ".",
@ -1710,8 +1724,9 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
const error_return_tracing = !strip and switch (options.optimize_mode) {
.Debug, .ReleaseSafe => (!options.target.isWasm() or options.target.os.tag == .emscripten) and
!options.target.cpu.arch.isBpf(),
.ReleaseFast, .ReleaseSmall => false,
!options.target.cpu.arch.isBpf() and (options.error_tracing orelse true),
.ReleaseFast => options.error_tracing orelse false,
.ReleaseSmall => false,
};
// For resource management purposes.
@ -1839,6 +1854,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
.bind_global_refs_locally = options.linker_bind_global_refs_locally orelse false,
.compress_debug_sections = options.linker_compress_debug_sections orelse .none,
.module_definition_file = options.linker_module_definition_file,
.sort_section = options.linker_sort_section,
.import_memory = options.linker_import_memory orelse false,
.import_symbols = options.linker_import_symbols,
.import_table = options.linker_import_table,
@ -2356,6 +2372,10 @@ pub fn update(comp: *Compilation) !void {
_ = try module.importPkg(module.main_pkg);
}
if (module.main_pkg.table.get("compiler_rt")) |compiler_rt_pkg| {
_ = try module.importPkg(compiler_rt_pkg);
}
// Put a work item in for every known source file to detect if
// it changed, and, if so, re-compute ZIR and then queue the job
// to update it.
@ -2380,6 +2400,10 @@ pub fn update(comp: *Compilation) !void {
if (comp.bin_file.options.is_test) {
try comp.work_queue.writeItem(.{ .analyze_pkg = module.main_pkg });
}
if (module.main_pkg.table.get("compiler_rt")) |compiler_rt_pkg| {
try comp.work_queue.writeItem(.{ .analyze_pkg = compiler_rt_pkg });
}
}
// If the terminal is dumb, we dont want to show the user all the output.
@ -2631,11 +2655,11 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
man.hash.addListOfBytes(key.src.extra_flags);
}
man.hash.addOptionalEmitLoc(comp.emit_asm);
man.hash.addOptionalEmitLoc(comp.emit_llvm_ir);
man.hash.addOptionalEmitLoc(comp.emit_llvm_bc);
man.hash.addOptionalEmitLoc(comp.emit_analysis);
man.hash.addOptionalEmitLoc(comp.emit_docs);
cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_asm);
cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_ir);
cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_bc);
cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_analysis);
cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_docs);
man.hash.addListOfBytes(comp.clang_argv);
@ -2662,6 +2686,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
man.hash.add(comp.bin_file.options.hash_style);
man.hash.add(comp.bin_file.options.compress_debug_sections);
man.hash.add(comp.bin_file.options.include_compiler_rt);
man.hash.addOptional(comp.bin_file.options.sort_section);
if (comp.bin_file.options.link_libc) {
man.hash.add(comp.bin_file.options.libc_installation != null);
if (comp.bin_file.options.libc_installation) |libc_installation| {
@ -3954,11 +3979,11 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
defer man.deinit();
man.hash.add(comp.clang_preprocessor_mode);
man.hash.addOptionalEmitLoc(comp.emit_asm);
man.hash.addOptionalEmitLoc(comp.emit_llvm_ir);
man.hash.addOptionalEmitLoc(comp.emit_llvm_bc);
cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_asm);
cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_ir);
cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_bc);
try man.hashCSource(c_object.src);
try cache_helpers.hashCSource(&man, c_object.src);
var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
defer arena_allocator.deinit();

View File

@ -16,7 +16,7 @@ const Ast = std.zig.Ast;
const Module = @This();
const Compilation = @import("Compilation.zig");
const Cache = @import("Cache.zig");
const Cache = std.Build.Cache;
const Value = @import("value.zig").Value;
const Type = @import("type.zig").Type;
const TypedValue = @import("TypedValue.zig");
@ -4865,14 +4865,31 @@ pub fn importFile(
};
}
pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*EmbedFile {
pub fn embedFile(mod: *Module, cur_file: *File, import_string: []const u8) !*EmbedFile {
const gpa = mod.gpa;
// The resolved path is used as the key in the table, to detect if
// a file refers to the same as another, despite different relative paths.
if (cur_file.pkg.table.get(import_string)) |pkg| {
const resolved_path = try std.fs.path.resolve(gpa, &[_][]const u8{
pkg.root_src_directory.path orelse ".", pkg.root_src_path,
});
var keep_resolved_path = false;
defer if (!keep_resolved_path) gpa.free(resolved_path);
const gop = try mod.embed_table.getOrPut(gpa, resolved_path);
errdefer assert(mod.embed_table.remove(resolved_path));
if (gop.found_existing) return gop.value_ptr.*;
const sub_file_path = try gpa.dupe(u8, pkg.root_src_path);
errdefer gpa.free(sub_file_path);
return newEmbedFile(mod, pkg, sub_file_path, resolved_path, &keep_resolved_path, gop);
}
// The resolved path is used as the key in the table, to detect if a file
// refers to the same as another, despite different relative paths.
const cur_pkg_dir_path = cur_file.pkg.root_src_directory.path orelse ".";
const resolved_path = try std.fs.path.resolve(gpa, &[_][]const u8{
cur_pkg_dir_path, cur_file.sub_file_path, "..", rel_file_path,
cur_pkg_dir_path, cur_file.sub_file_path, "..", import_string,
});
var keep_resolved_path = false;
defer if (!keep_resolved_path) gpa.free(resolved_path);
@ -4881,9 +4898,6 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb
errdefer assert(mod.embed_table.remove(resolved_path));
if (gop.found_existing) return gop.value_ptr.*;
const new_file = try gpa.create(EmbedFile);
errdefer gpa.destroy(new_file);
const resolved_root_path = try std.fs.path.resolve(gpa, &[_][]const u8{cur_pkg_dir_path});
defer gpa.free(resolved_root_path);
@ -4902,7 +4916,23 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb
};
errdefer gpa.free(sub_file_path);
var file = try cur_file.pkg.root_src_directory.handle.openFile(sub_file_path, .{});
return newEmbedFile(mod, cur_file.pkg, sub_file_path, resolved_path, &keep_resolved_path, gop);
}
fn newEmbedFile(
mod: *Module,
pkg: *Package,
sub_file_path: []const u8,
resolved_path: []const u8,
keep_resolved_path: *bool,
gop: std.StringHashMapUnmanaged(*EmbedFile).GetOrPutResult,
) !*EmbedFile {
const gpa = mod.gpa;
const new_file = try gpa.create(EmbedFile);
errdefer gpa.destroy(new_file);
var file = try pkg.root_src_directory.handle.openFile(sub_file_path, .{});
defer file.close();
const actual_stat = try file.stat();
@ -4915,10 +4945,6 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb
const bytes = try file.readToEndAllocOptions(gpa, std.math.maxInt(u32), size_usize, 1, 0);
errdefer gpa.free(bytes);
log.debug("new embedFile. resolved_root_path={s}, resolved_path={s}, sub_file_path={s}, rel_file_path={s}", .{
resolved_root_path, resolved_path, sub_file_path, rel_file_path,
});
if (mod.comp.whole_cache_manifest) |whole_cache_manifest| {
const copied_resolved_path = try gpa.dupe(u8, resolved_path);
errdefer gpa.free(copied_resolved_path);
@ -4927,13 +4953,13 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb
try whole_cache_manifest.addFilePostContents(copied_resolved_path, bytes, stat);
}
keep_resolved_path = true; // It's now owned by embed_table.
keep_resolved_path.* = true; // It's now owned by embed_table.
gop.value_ptr.* = new_file;
new_file.* = .{
.sub_file_path = sub_file_path,
.bytes = bytes,
.stat = stat,
.pkg = cur_file.pkg,
.pkg = pkg,
.owner_decl = undefined, // Set by Sema immediately after this function returns.
};
return new_file;

View File

@ -13,7 +13,7 @@ const Compilation = @import("Compilation.zig");
const Module = @import("Module.zig");
const ThreadPool = @import("ThreadPool.zig");
const WaitGroup = @import("WaitGroup.zig");
const Cache = @import("Cache.zig");
const Cache = std.Build.Cache;
const build_options = @import("build_options");
const Manifest = @import("Manifest.zig");

View File

@ -1015,7 +1015,6 @@ fn analyzeBodyInner(
.float_cast => try sema.zirFloatCast(block, inst),
.int_cast => try sema.zirIntCast(block, inst),
.ptr_cast => try sema.zirPtrCast(block, inst),
.qual_cast => try sema.zirQualCast(block, inst),
.truncate => try sema.zirTruncate(block, inst),
.align_cast => try sema.zirAlignCast(block, inst),
.has_decl => try sema.zirHasDecl(block, inst),
@ -1147,6 +1146,8 @@ fn analyzeBodyInner(
.c_va_copy => try sema.zirCVaCopy( block, extended),
.c_va_end => try sema.zirCVaEnd( block, extended),
.c_va_start => try sema.zirCVaStart( block, extended),
.const_cast, => try sema.zirConstCast( block, extended),
.volatile_cast, => try sema.zirVolatileCast( block, extended),
// zig fmt: on
.fence => {
@ -7669,19 +7670,23 @@ fn zirErrorUnionType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
error_set.fmt(sema.mod),
});
}
if (payload.zigTypeTag() == .Opaque) {
return sema.fail(block, rhs_src, "error union with payload of opaque type '{}' not allowed", .{
payload.fmt(sema.mod),
});
} else if (payload.zigTypeTag() == .ErrorSet) {
return sema.fail(block, rhs_src, "error union with payload of error set type '{}' not allowed", .{
payload.fmt(sema.mod),
});
}
try sema.validateErrorUnionPayloadType(block, payload, rhs_src);
const err_union_ty = try Type.errorUnion(sema.arena, error_set, payload, sema.mod);
return sema.addType(err_union_ty);
}
fn validateErrorUnionPayloadType(sema: *Sema, block: *Block, payload_ty: Type, payload_src: LazySrcLoc) !void {
if (payload_ty.zigTypeTag() == .Opaque) {
return sema.fail(block, payload_src, "error union with payload of opaque type '{}' not allowed", .{
payload_ty.fmt(sema.mod),
});
} else if (payload_ty.zigTypeTag() == .ErrorSet) {
return sema.fail(block, payload_src, "error union with payload of error set type '{}' not allowed", .{
payload_ty.fmt(sema.mod),
});
}
}
fn zirErrorValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
_ = block;
const tracy = trace(@src());
@ -8639,6 +8644,7 @@ fn funcCommon(
const return_type = if (!inferred_error_set or ret_poison)
bare_return_type
else blk: {
try sema.validateErrorUnionPayloadType(block, bare_return_type, ret_ty_src);
const node = try sema.gpa.create(Module.Fn.InferredErrorSetListNode);
node.data = .{ .func = new_func };
maybe_inferred_error_set_node = node;
@ -8650,15 +8656,15 @@ fn funcCommon(
});
};
if (!bare_return_type.isValidReturnType()) {
const opaque_str = if (bare_return_type.zigTypeTag() == .Opaque) "opaque " else "";
if (!return_type.isValidReturnType()) {
const opaque_str = if (return_type.zigTypeTag() == .Opaque) "opaque " else "";
const msg = msg: {
const msg = try sema.errMsg(block, ret_ty_src, "{s}return type '{}' not allowed", .{
opaque_str, bare_return_type.fmt(sema.mod),
opaque_str, return_type.fmt(sema.mod),
});
errdefer msg.destroy(sema.gpa);
try sema.addDeclaredHereNote(msg, bare_return_type);
try sema.addDeclaredHereNote(msg, return_type);
break :msg msg;
};
return sema.failWithOwnedErrorMsg(msg);
@ -19540,7 +19546,7 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
const msg = try sema.errMsg(block, src, "cast discards const qualifier", .{});
errdefer msg.destroy(sema.gpa);
try sema.errNote(block, src, msg, "consider using '@qualCast'", .{});
try sema.errNote(block, src, msg, "consider using '@constCast'", .{});
break :msg msg;
};
return sema.failWithOwnedErrorMsg(msg);
@ -19550,7 +19556,7 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
const msg = try sema.errMsg(block, src, "cast discards volatile qualifier", .{});
errdefer msg.destroy(sema.gpa);
try sema.errNote(block, src, msg, "consider using '@qualCast'", .{});
try sema.errNote(block, src, msg, "consider using '@volatileCast'", .{});
break :msg msg;
};
return sema.failWithOwnedErrorMsg(msg);
@ -19655,41 +19661,38 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
return block.addBitCast(aligned_dest_ty, ptr);
}
fn zirQualCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
const src = inst_data.src();
const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs);
const operand = try sema.resolveInst(extra.rhs);
fn zirConstCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
const src = LazySrcLoc.nodeOffset(extra.node);
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
const operand = try sema.resolveInst(extra.operand);
const operand_ty = sema.typeOf(operand);
try sema.checkPtrType(block, dest_ty_src, dest_ty);
try sema.checkPtrOperand(block, operand_src, operand_ty);
var operand_payload = operand_ty.ptrInfo();
var dest_info = dest_ty.ptrInfo();
var ptr_info = operand_ty.ptrInfo().data;
ptr_info.mutable = true;
const dest_ty = try Type.ptr(sema.arena, sema.mod, ptr_info);
operand_payload.data.mutable = dest_info.data.mutable;
operand_payload.data.@"volatile" = dest_info.data.@"volatile";
const altered_operand_ty = Type.initPayload(&operand_payload.base);
if (!altered_operand_ty.eql(dest_ty, sema.mod)) {
const msg = msg: {
const msg = try sema.errMsg(block, src, "'@qualCast' can only modify 'const' and 'volatile' qualifiers", .{});
errdefer msg.destroy(sema.gpa);
dest_info.data.mutable = !operand_ty.isConstPtr();
dest_info.data.@"volatile" = operand_ty.isVolatilePtr();
const altered_dest_ty = Type.initPayload(&dest_info.base);
try sema.errNote(block, src, msg, "expected type '{}'", .{altered_dest_ty.fmt(sema.mod)});
try sema.errNote(block, src, msg, "got type '{}'", .{operand_ty.fmt(sema.mod)});
break :msg msg;
};
return sema.failWithOwnedErrorMsg(msg);
if (try sema.resolveMaybeUndefVal(operand)) |operand_val| {
return sema.addConstant(dest_ty, operand_val);
}
try sema.requireRuntimeBlock(block, src, null);
return block.addBitCast(dest_ty, operand);
}
fn zirVolatileCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
const src = LazySrcLoc.nodeOffset(extra.node);
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
const operand = try sema.resolveInst(extra.operand);
const operand_ty = sema.typeOf(operand);
try sema.checkPtrOperand(block, operand_src, operand_ty);
var ptr_info = operand_ty.ptrInfo().data;
ptr_info.@"volatile" = false;
const dest_ty = try Type.ptr(sema.arena, sema.mod, ptr_info);
if (try sema.resolveMaybeUndefVal(operand)) |operand_val| {
return sema.addConstant(dest_ty, operand_val);
}
@ -21930,7 +21933,7 @@ fn zirCUndef(
const src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
const name = try sema.resolveConstString(block, src, extra.operand, "name of macro being undefined must be comptime-known");
try block.c_import_buf.?.writer().print("#undefine {s}\n", .{name});
try block.c_import_buf.?.writer().print("#undef {s}\n", .{name});
return Air.Inst.Ref.void_value;
}
@ -29756,6 +29759,25 @@ fn resolvePeerTypes(
continue;
}
},
.ErrorSet => {
chosen = candidate;
chosen_i = candidate_i + 1;
if (err_set_ty) |chosen_set_ty| {
if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_set_ty, chosen_ty, src, src)) {
continue;
}
if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_ty, chosen_set_ty, src, src)) {
err_set_ty = chosen_ty;
continue;
}
err_set_ty = try chosen_set_ty.errorSetMerge(sema.arena, chosen_ty);
continue;
} else {
err_set_ty = chosen_ty;
continue;
}
},
else => {},
}

View File

@ -857,9 +857,6 @@ pub const Inst = struct {
/// Implements the `@ptrCast` builtin.
/// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
ptr_cast,
/// Implements the `@qualCast` builtin.
/// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
qual_cast,
/// Implements the `@truncate` builtin.
/// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
truncate,
@ -1198,7 +1195,6 @@ pub const Inst = struct {
.float_cast,
.int_cast,
.ptr_cast,
.qual_cast,
.truncate,
.align_cast,
.has_field,
@ -1488,7 +1484,6 @@ pub const Inst = struct {
.float_cast,
.int_cast,
.ptr_cast,
.qual_cast,
.truncate,
.align_cast,
.has_field,
@ -1760,7 +1755,6 @@ pub const Inst = struct {
.float_cast = .pl_node,
.int_cast = .pl_node,
.ptr_cast = .pl_node,
.qual_cast = .pl_node,
.truncate = .pl_node,
.align_cast = .pl_node,
.typeof_builtin = .pl_node,
@ -2004,6 +1998,12 @@ pub const Inst = struct {
/// Implement builtin `@cVaStart`.
/// `operand` is `src_node: i32`.
c_va_start,
/// Implements the `@constCast` builtin.
/// `operand` is payload index to `UnNode`.
const_cast,
/// Implements the `@volatileCast` builtin.
/// `operand` is payload index to `UnNode`.
volatile_cast,
pub const InstData = struct {
opcode: Extended,

View File

@ -2278,7 +2278,9 @@ pub const Object = struct {
const full_di_fields: [2]*llvm.DIType =
if (layout.tag_align >= layout.payload_align)
.{ tag_di, payload_di } else .{ payload_di, tag_di };
.{ tag_di, payload_di }
else
.{ payload_di, tag_di };
const full_di_ty = dib.createStructType(
compile_unit_scope,
@ -4167,6 +4169,10 @@ pub const DeclGen = struct {
if (func.data.owner_decl != decl_index) {
return self.lowerDeclRefValue(tv, func.data.owner_decl);
}
} else if (decl.val.castTag(.extern_fn)) |func| {
if (func.data.owner_decl != decl_index) {
return self.lowerDeclRefValue(tv, func.data.owner_decl);
}
}
const is_fn_body = decl.ty.zigTypeTag() == .Fn;

View File

@ -11,7 +11,7 @@ const target_util = @import("target.zig");
const Compilation = @import("Compilation.zig");
const build_options = @import("build_options");
const trace = @import("tracy.zig").trace;
const Cache = @import("Cache.zig");
const Cache = std.Build.Cache;
const Package = @import("Package.zig");
pub const Lib = struct {

View File

@ -10,7 +10,7 @@ const wasi_libc = @import("wasi_libc.zig");
const Air = @import("Air.zig");
const Allocator = std.mem.Allocator;
const Cache = @import("Cache.zig");
const Cache = std.Build.Cache;
const Compilation = @import("Compilation.zig");
const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
const Liveness = @import("Liveness.zig");
@ -25,6 +25,8 @@ pub const SystemLib = struct {
weak: bool = false,
};
pub const SortSection = enum { name, alignment };
pub const CacheMode = enum { incremental, whole };
pub fn hashAddSystemLibs(
@ -159,6 +161,7 @@ pub const Options = struct {
disable_lld_caching: bool,
is_test: bool,
hash_style: HashStyle,
sort_section: ?SortSection,
major_subsystem_version: ?u32,
minor_subsystem_version: ?u32,
gc_sections: ?bool = null,

View File

@ -5,6 +5,7 @@ const assert = std.debug.assert;
const fs = std.fs;
const log = std.log.scoped(.link);
const mem = std.mem;
const Cache = std.Build.Cache;
const mingw = @import("../../mingw.zig");
const link = @import("../../link.zig");
@ -13,7 +14,6 @@ const trace = @import("../../tracy.zig").trace;
const Allocator = mem.Allocator;
const Cache = @import("../../Cache.zig");
const Coff = @import("../Coff.zig");
const Compilation = @import("../../Compilation.zig");

View File

@ -21,7 +21,7 @@ const trace = @import("../tracy.zig").trace;
const Air = @import("../Air.zig");
const Allocator = std.mem.Allocator;
pub const Atom = @import("Elf/Atom.zig");
const Cache = @import("../Cache.zig");
const Cache = std.Build.Cache;
const Compilation = @import("../Compilation.zig");
const Dwarf = @import("Dwarf.zig");
const File = link.File;
@ -1324,6 +1324,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
man.hash.addOptionalBytes(self.base.options.entry);
man.hash.addOptional(self.base.options.image_base_override);
man.hash.add(gc_sections);
man.hash.addOptional(self.base.options.sort_section);
man.hash.add(self.base.options.eh_frame_hdr);
man.hash.add(self.base.options.emit_relocs);
man.hash.add(self.base.options.rdynamic);
@ -1488,6 +1489,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
try argv.append(linker_script);
}
if (self.base.options.sort_section) |how| {
const arg = try std.fmt.allocPrint(arena, "--sort-section={s}", .{@tagName(how)});
try argv.append(arg);
}
if (gc_sections) {
try argv.append("--gc-sections");
}

View File

@ -28,7 +28,7 @@ const Air = @import("../Air.zig");
const Allocator = mem.Allocator;
const Archive = @import("MachO/Archive.zig");
pub const Atom = @import("MachO/Atom.zig");
const Cache = @import("../Cache.zig");
const Cache = std.Build.Cache;
const CodeSignature = @import("MachO/CodeSignature.zig");
const Compilation = @import("../Compilation.zig");
const Dwarf = File.Dwarf;

View File

@ -166,6 +166,8 @@ pub fn parseFromBinary(
const symtab_cmd = cmd.cast(macho.symtab_command).?;
const symtab = @ptrCast(
[*]const macho.nlist_64,
// Alignment is guaranteed as a dylib is a final linked image and has to have sections
// properly aligned in order to be correctly loaded by the loader.
@alignCast(@alignOf(macho.nlist_64), &data[symtab_cmd.symoff]),
)[0..symtab_cmd.nsyms];
const strtab = data[symtab_cmd.stroff..][0..symtab_cmd.strsize];

View File

@ -60,14 +60,24 @@ globals_lookup: []i64 = undefined,
/// Can be undefined as set together with in_symtab.
relocs_lookup: []RelocEntry = undefined,
/// All relocations sorted and flatened, sorted by address descending
/// per section.
relocations: std.ArrayListUnmanaged(macho.relocation_info) = .{},
/// Beginning index to the relocations array for each input section
/// defined within this Object file.
section_relocs_lookup: std.ArrayListUnmanaged(u32) = .{},
/// Data-in-code records sorted by address.
data_in_code: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},
atoms: std.ArrayListUnmanaged(AtomIndex) = .{},
exec_atoms: std.ArrayListUnmanaged(AtomIndex) = .{},
eh_frame_sect: ?macho.section_64 = null,
eh_frame_sect_id: ?u8 = null,
eh_frame_relocs_lookup: std.AutoArrayHashMapUnmanaged(u32, Record) = .{},
eh_frame_records_lookup: std.AutoArrayHashMapUnmanaged(AtomIndex, u32) = .{},
unwind_info_sect: ?macho.section_64 = null,
unwind_info_sect_id: ?u8 = null,
unwind_relocs_lookup: []Record = undefined,
unwind_records_lookup: std.AutoHashMapUnmanaged(AtomIndex, u32) = .{},
@ -100,6 +110,9 @@ pub fn deinit(self: *Object, gpa: Allocator) void {
gpa.free(self.unwind_relocs_lookup);
}
self.unwind_records_lookup.deinit(gpa);
self.relocations.deinit(gpa);
self.section_relocs_lookup.deinit(gpa);
self.data_in_code.deinit(gpa);
}
pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch) !void {
@ -137,15 +150,18 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch)
.buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],
};
const nsects = self.getSourceSections().len;
// Prepopulate relocations per section lookup table.
try self.section_relocs_lookup.resize(allocator, nsects);
mem.set(u32, self.section_relocs_lookup.items, 0);
// Parse symtab.
const symtab = while (it.next()) |cmd| switch (cmd.cmd()) {
.SYMTAB => break cmd.cast(macho.symtab_command).?,
else => {},
} else return;
self.in_symtab = @ptrCast(
[*]const macho.nlist_64,
@alignCast(@alignOf(macho.nlist_64), &self.contents[symtab.symoff]),
)[0..symtab.nsyms];
self.in_symtab = @ptrCast([*]align(1) const macho.nlist_64, self.contents.ptr + symtab.symoff)[0..symtab.nsyms];
self.in_strtab = self.contents[symtab.stroff..][0..symtab.strsize];
self.symtab = try allocator.alloc(macho.nlist_64, self.in_symtab.?.len + nsects);
@ -212,10 +228,10 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch)
}
// Parse __TEXT,__eh_frame header if one exists
self.eh_frame_sect = self.getSourceSectionByName("__TEXT", "__eh_frame");
self.eh_frame_sect_id = self.getSourceSectionIndexByName("__TEXT", "__eh_frame");
// Parse __LD,__compact_unwind header if one exists
self.unwind_info_sect = self.getSourceSectionByName("__LD", "__compact_unwind");
self.unwind_info_sect_id = self.getSourceSectionIndexByName("__LD", "__compact_unwind");
if (self.hasUnwindRecords()) {
self.unwind_relocs_lookup = try allocator.alloc(Record, self.getUnwindRecords().len);
mem.set(Record, self.unwind_relocs_lookup, .{
@ -354,6 +370,7 @@ pub fn splitIntoAtoms(self: *Object, zld: *Zld, object_id: u32) !void {
try self.splitRegularSections(zld, object_id);
try self.parseEhFrameSection(zld, object_id);
try self.parseUnwindInfo(zld, object_id);
try self.parseDataInCode(zld.gpa);
}
/// Splits input regular sections into Atoms.
@ -452,6 +469,8 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
zld.sections.items(.header)[out_sect_id].sectName(),
});
try self.parseRelocs(gpa, section.id);
const cpu_arch = zld.options.target.cpu.arch;
const sect_loc = filterSymbolsBySection(symtab[sect_sym_index..], sect_id + 1);
const sect_start_index = sect_sym_index + sect_loc.index;
@ -623,25 +642,36 @@ fn filterRelocs(
return .{ .start = @intCast(u32, start), .len = @intCast(u32, len) };
}
/// Parse all relocs for the input section, and sort in descending order.
/// Previously, I have wrongly assumed the compilers output relocations for each
/// section in a sorted manner which is simply not true.
fn parseRelocs(self: *Object, gpa: Allocator, sect_id: u8) !void {
const section = self.getSourceSection(sect_id);
const start = @intCast(u32, self.relocations.items.len);
if (self.getSourceRelocs(section)) |relocs| {
try self.relocations.ensureUnusedCapacity(gpa, relocs.len);
self.relocations.appendUnalignedSliceAssumeCapacity(relocs);
std.sort.sort(macho.relocation_info, self.relocations.items[start..], {}, relocGreaterThan);
}
self.section_relocs_lookup.items[sect_id] = start;
}
fn cacheRelocs(self: *Object, zld: *Zld, atom_index: AtomIndex) !void {
const atom = zld.getAtom(atom_index);
const source_sect = if (self.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
const source_sect = self.getSourceSection(source_sym.n_sect - 1);
assert(!source_sect.isZerofill());
break :blk source_sect;
const source_sect_id = if (self.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
break :blk source_sym.n_sect - 1;
} else blk: {
// If there was no matching symbol present in the source symtab, this means
// we are dealing with either an entire section, or part of it, but also
// starting at the beginning.
const nbase = @intCast(u32, self.in_symtab.?.len);
const sect_id = @intCast(u16, atom.sym_index - nbase);
const source_sect = self.getSourceSection(sect_id);
assert(!source_sect.isZerofill());
break :blk source_sect;
const sect_id = @intCast(u8, atom.sym_index - nbase);
break :blk sect_id;
};
const relocs = self.getRelocs(source_sect);
const source_sect = self.getSourceSection(source_sect_id);
assert(!source_sect.isZerofill());
const relocs = self.getRelocs(source_sect_id);
self.relocs_lookup[atom.sym_index] = if (self.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
const offset = source_sym.n_value - source_sect.addr;
@ -649,8 +679,14 @@ fn cacheRelocs(self: *Object, zld: *Zld, atom_index: AtomIndex) !void {
} else filterRelocs(relocs, 0, atom.size);
}
fn relocGreaterThan(ctx: void, lhs: macho.relocation_info, rhs: macho.relocation_info) bool {
_ = ctx;
return lhs.r_address > rhs.r_address;
}
fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
const sect = self.eh_frame_sect orelse return;
const sect_id = self.eh_frame_sect_id orelse return;
const sect = self.getSourceSection(sect_id);
log.debug("parsing __TEXT,__eh_frame section", .{});
@ -660,7 +696,8 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
const gpa = zld.gpa;
const cpu_arch = zld.options.target.cpu.arch;
const relocs = self.getRelocs(sect);
try self.parseRelocs(gpa, sect_id);
const relocs = self.getRelocs(sect_id);
var it = self.getEhFrameRecordsIterator();
var record_count: u32 = 0;
@ -728,12 +765,12 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
}
fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {
const sect = self.unwind_info_sect orelse {
const sect_id = self.unwind_info_sect_id orelse {
// If it so happens that the object had `__eh_frame` section defined but no `__compact_unwind`,
// we will try fully synthesising unwind info records to somewhat match Apple ld's
// approach. However, we will only synthesise DWARF records and nothing more. For this reason,
// we still create the output `__TEXT,__unwind_info` section.
if (self.eh_frame_sect != null) {
if (self.hasEhFrameRecords()) {
if (zld.getSectionByName("__TEXT", "__unwind_info") == null) {
_ = try zld.initSection("__TEXT", "__unwind_info", .{});
}
@ -758,15 +795,15 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {
if (UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) break true;
} else false;
if (needs_eh_frame) {
if (self.eh_frame_sect == null) {
log.err("missing __TEXT,__eh_frame section", .{});
log.err(" in object {s}", .{self.name});
return error.MissingSection;
}
if (needs_eh_frame and !self.hasEhFrameRecords()) {
log.err("missing __TEXT,__eh_frame section", .{});
log.err(" in object {s}", .{self.name});
return error.MissingSection;
}
const relocs = self.getRelocs(sect);
try self.parseRelocs(gpa, sect_id);
const relocs = self.getRelocs(sect_id);
for (unwind_records) |record, record_id| {
const offset = record_id * @sizeOf(macho.compact_unwind_entry);
const rel_pos = filterRelocs(
@ -806,25 +843,23 @@ pub fn getSourceSymbol(self: Object, index: u32) ?macho.nlist_64 {
return symtab[mapped_index];
}
pub fn getSourceSection(self: Object, index: u16) macho.section_64 {
pub fn getSourceSection(self: Object, index: u8) macho.section_64 {
const sections = self.getSourceSections();
assert(index < sections.len);
return sections[index];
}
pub fn getSourceSectionByName(self: Object, segname: []const u8, sectname: []const u8) ?macho.section_64 {
const index = self.getSourceSectionIndexByName(segname, sectname) orelse return null;
const sections = self.getSourceSections();
for (sections) |sect| {
if (mem.eql(u8, segname, sect.segName()) and mem.eql(u8, sectname, sect.sectName()))
return sect;
} else return null;
return sections[index];
}
pub fn getSourceSectionIndexByName(self: Object, segname: []const u8, sectname: []const u8) ?u8 {
const sections = self.getSourceSections();
for (sections) |sect, i| {
if (mem.eql(u8, segname, sect.segName()) and mem.eql(u8, sectname, sect.sectName()))
return @intCast(u8, i + 1);
return @intCast(u8, i);
} else return null;
}
@ -841,24 +876,27 @@ pub fn getSourceSections(self: Object) []const macho.section_64 {
} else unreachable;
}
pub fn parseDataInCode(self: Object) ?[]const macho.data_in_code_entry {
pub fn parseDataInCode(self: *Object, gpa: Allocator) !void {
var it = LoadCommandIterator{
.ncmds = self.header.ncmds,
.buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],
};
while (it.next()) |cmd| {
const cmd = while (it.next()) |cmd| {
switch (cmd.cmd()) {
.DATA_IN_CODE => {
const dice = cmd.cast(macho.linkedit_data_command).?;
const ndice = @divExact(dice.datasize, @sizeOf(macho.data_in_code_entry));
return @ptrCast(
[*]const macho.data_in_code_entry,
@alignCast(@alignOf(macho.data_in_code_entry), &self.contents[dice.dataoff]),
)[0..ndice];
},
.DATA_IN_CODE => break cmd.cast(macho.linkedit_data_command).?,
else => {},
}
} else return null;
} else return;
const ndice = @divExact(cmd.datasize, @sizeOf(macho.data_in_code_entry));
const dice = @ptrCast([*]align(1) const macho.data_in_code_entry, self.contents.ptr + cmd.dataoff)[0..ndice];
try self.data_in_code.ensureTotalCapacityPrecise(gpa, dice.len);
self.data_in_code.appendUnalignedSliceAssumeCapacity(dice);
std.sort.sort(macho.data_in_code_entry, self.data_in_code.items, {}, diceLessThan);
}
fn diceLessThan(ctx: void, lhs: macho.data_in_code_entry, rhs: macho.data_in_code_entry) bool {
_ = ctx;
return lhs.offset < rhs.offset;
}
fn parseDysymtab(self: Object) ?macho.dysymtab_command {
@ -914,11 +952,18 @@ pub fn getSectionAliasSymbolPtr(self: *Object, sect_id: u8) *macho.nlist_64 {
return &self.symtab[self.getSectionAliasSymbolIndex(sect_id)];
}
pub fn getRelocs(self: Object, sect: macho.section_64) []align(1) const macho.relocation_info {
if (sect.nreloc == 0) return &[0]macho.relocation_info{};
fn getSourceRelocs(self: Object, sect: macho.section_64) ?[]align(1) const macho.relocation_info {
if (sect.nreloc == 0) return null;
return @ptrCast([*]align(1) const macho.relocation_info, self.contents.ptr + sect.reloff)[0..sect.nreloc];
}
pub fn getRelocs(self: Object, sect_id: u8) []const macho.relocation_info {
const sect = self.getSourceSection(sect_id);
const start = self.section_relocs_lookup.items[sect_id];
const len = sect.nreloc;
return self.relocations.items[start..][0..len];
}
pub fn getSymbolName(self: Object, index: u32) []const u8 {
const strtab = self.in_strtab.?;
const sym = self.symtab[index];
@ -976,22 +1021,28 @@ pub fn getAtomIndexForSymbol(self: Object, sym_index: u32) ?AtomIndex {
}
pub fn hasUnwindRecords(self: Object) bool {
return self.unwind_info_sect != null;
return self.unwind_info_sect_id != null;
}
pub fn getUnwindRecords(self: Object) []align(1) const macho.compact_unwind_entry {
const sect = self.unwind_info_sect orelse return &[0]macho.compact_unwind_entry{};
const sect_id = self.unwind_info_sect_id orelse return &[0]macho.compact_unwind_entry{};
const sect = self.getSourceSection(sect_id);
const data = self.getSectionContents(sect);
const num_entries = @divExact(data.len, @sizeOf(macho.compact_unwind_entry));
return @ptrCast([*]align(1) const macho.compact_unwind_entry, data)[0..num_entries];
}
pub fn hasEhFrameRecords(self: Object) bool {
return self.eh_frame_sect != null;
return self.eh_frame_sect_id != null;
}
pub fn getEhFrameRecordsIterator(self: Object) eh_frame.Iterator {
const sect = self.eh_frame_sect orelse return .{ .data = &[0]u8{} };
const sect_id = self.eh_frame_sect_id orelse return .{ .data = &[0]u8{} };
const sect = self.getSourceSection(sect_id);
const data = self.getSectionContents(sect);
return .{ .data = data };
}
pub fn hasDataInCode(self: Object) bool {
return self.data_in_code.items.len > 0;
}

View File

@ -68,7 +68,7 @@ const Page = struct {
start: RecordIndex,
count: u16,
page_encodings: [max_compact_encodings]RecordIndex = undefined,
page_encodings_count: u8 = 0,
page_encodings_count: u9 = 0,
fn appendPageEncoding(page: *Page, record_id: RecordIndex) void {
assert(page.page_encodings_count <= max_compact_encodings);
@ -81,13 +81,13 @@ const Page = struct {
info: *const UnwindInfo,
enc: macho.compact_unwind_encoding_t,
) ?u8 {
comptime var index: u8 = 0;
comptime var index: u9 = 0;
inline while (index < max_compact_encodings) : (index += 1) {
if (index >= page.page_encodings_count) return null;
const record_id = page.page_encodings[index];
const record = info.records.items[record_id];
if (record.compactUnwindEncoding == enc) {
return index;
return @intCast(u8, index);
}
}
return null;
@ -703,15 +703,11 @@ pub fn parseRelocTarget(
} else return sym_loc;
}
fn getRelocs(
zld: *Zld,
object_id: u32,
record_id: usize,
) []align(1) const macho.relocation_info {
fn getRelocs(zld: *Zld, object_id: u32, record_id: usize) []const macho.relocation_info {
const object = &zld.objects.items[object_id];
assert(object.hasUnwindRecords());
const rel_pos = object.unwind_relocs_lookup[record_id].reloc;
const relocs = object.getRelocs(object.unwind_info_sect.?);
const relocs = object.getRelocs(object.unwind_info_sect_id.?);
return relocs[rel_pos.start..][0..rel_pos.len];
}

View File

@ -143,7 +143,7 @@ pub fn calcInnerSymbolOffset(zld: *Zld, atom_index: AtomIndex, sym_index: u32) u
sym.n_value
else blk: {
const nbase = @intCast(u32, object.in_symtab.?.len);
const sect_id = @intCast(u16, atom.sym_index - nbase);
const sect_id = @intCast(u8, atom.sym_index - nbase);
const source_sect = object.getSourceSection(sect_id);
break :blk source_sect.addr;
};
@ -180,7 +180,7 @@ pub fn getRelocContext(zld: *Zld, atom_index: AtomIndex) RelocContext {
};
}
const nbase = @intCast(u32, object.in_symtab.?.len);
const sect_id = @intCast(u16, atom.sym_index - nbase);
const sect_id = @intCast(u8, atom.sym_index - nbase);
const source_sect = object.getSourceSection(sect_id);
return .{
.base_addr = source_sect.addr,
@ -724,7 +724,7 @@ fn resolveRelocsArm64(
if (rel.r_extern == 0) {
const base_addr = if (target.sym_index > object.source_address_lookup.len)
@intCast(i64, object.getSourceSection(@intCast(u16, rel.r_symbolnum - 1)).addr)
@intCast(i64, object.getSourceSection(@intCast(u8, rel.r_symbolnum - 1)).addr)
else
object.source_address_lookup[target.sym_index];
ptr_addend -= base_addr;
@ -861,7 +861,7 @@ fn resolveRelocsX86(
if (rel.r_extern == 0) {
const base_addr = if (target.sym_index > object.source_address_lookup.len)
@intCast(i64, object.getSourceSection(@intCast(u16, rel.r_symbolnum - 1)).addr)
@intCast(i64, object.getSourceSection(@intCast(u8, rel.r_symbolnum - 1)).addr)
else
object.source_address_lookup[target.sym_index];
addend += @intCast(i32, @intCast(i64, context.base_addr) + rel.r_address + 4 -
@ -884,7 +884,7 @@ fn resolveRelocsX86(
if (rel.r_extern == 0) {
const base_addr = if (target.sym_index > object.source_address_lookup.len)
@intCast(i64, object.getSourceSection(@intCast(u16, rel.r_symbolnum - 1)).addr)
@intCast(i64, object.getSourceSection(@intCast(u8, rel.r_symbolnum - 1)).addr)
else
object.source_address_lookup[target.sym_index];
addend -= base_addr;
@ -928,7 +928,7 @@ pub fn getAtomCode(zld: *Zld, atom_index: AtomIndex) []const u8 {
// we are dealing with either an entire section, or part of it, but also
// starting at the beginning.
const nbase = @intCast(u32, object.in_symtab.?.len);
const sect_id = @intCast(u16, atom.sym_index - nbase);
const sect_id = @intCast(u8, atom.sym_index - nbase);
const source_sect = object.getSourceSection(sect_id);
assert(!source_sect.isZerofill());
const code = object.getSectionContents(source_sect);
@ -943,28 +943,25 @@ pub fn getAtomCode(zld: *Zld, atom_index: AtomIndex) []const u8 {
return code[offset..][0..code_len];
}
pub fn getAtomRelocs(zld: *Zld, atom_index: AtomIndex) []align(1) const macho.relocation_info {
pub fn getAtomRelocs(zld: *Zld, atom_index: AtomIndex) []const macho.relocation_info {
const atom = zld.getAtom(atom_index);
assert(atom.getFile() != null); // Synthetic atom shouldn't need to unique for relocs.
const object = zld.objects.items[atom.getFile().?];
const cache = object.relocs_lookup[atom.sym_index];
const source_sect = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
const source_sect = object.getSourceSection(source_sym.n_sect - 1);
assert(!source_sect.isZerofill());
break :blk source_sect;
const source_sect_id = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
break :blk source_sym.n_sect - 1;
} else blk: {
// If there was no matching symbol present in the source symtab, this means
// we are dealing with either an entire section, or part of it, but also
// starting at the beginning.
const nbase = @intCast(u32, object.in_symtab.?.len);
const sect_id = @intCast(u16, atom.sym_index - nbase);
const source_sect = object.getSourceSection(sect_id);
assert(!source_sect.isZerofill());
break :blk source_sect;
const sect_id = @intCast(u8, atom.sym_index - nbase);
break :blk sect_id;
};
const relocs = object.getRelocs(source_sect);
const source_sect = object.getSourceSection(source_sect_id);
assert(!source_sect.isZerofill());
const relocs = object.getRelocs(source_sect_id);
return relocs[cache.start..][0..cache.len];
}

View File

@ -88,7 +88,7 @@ fn collectRoots(zld: *Zld, roots: *AtomTable) !void {
source_sym.n_sect - 1
else sect_id: {
const nbase = @intCast(u32, object.in_symtab.?.len);
const sect_id = @intCast(u16, atom.sym_index - nbase);
const sect_id = @intCast(u8, atom.sym_index - nbase);
break :sect_id sect_id;
};
const source_sect = object.getSourceSection(sect_id);
@ -223,7 +223,7 @@ fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable) !void {
source_sym.n_sect - 1
else blk: {
const nbase = @intCast(u32, object.in_symtab.?.len);
const sect_id = @intCast(u16, atom.sym_index - nbase);
const sect_id = @intCast(u8, atom.sym_index - nbase);
break :blk sect_id;
};
const source_sect = object.getSourceSection(sect_id);
@ -350,8 +350,9 @@ fn markEhFrameRecord(zld: *Zld, object_id: u32, atom_index: AtomIndex, alive: *A
}
},
.x86_64 => {
const sect = object.getSourceSection(object.eh_frame_sect_id.?);
const lsda_ptr = try fde.getLsdaPointer(cie, .{
.base_addr = object.eh_frame_sect.?.addr,
.base_addr = sect.addr,
.base_offset = fde_offset,
});
if (lsda_ptr) |lsda_address| {

View File

@ -171,8 +171,9 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
const cie_record = eh_records.get(
eh_frame_offset + 4 - fde_record.getCiePointer(),
).?;
const eh_frame_sect = object.getSourceSection(object.eh_frame_sect_id.?);
const source_lsda_ptr = try fde_record.getLsdaPointer(cie_record, .{
.base_addr = object.eh_frame_sect.?.addr,
.base_addr = eh_frame_sect.addr,
.base_offset = fde_record_offset,
});
if (source_lsda_ptr) |ptr| {
@ -552,16 +553,12 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
};
}
pub fn getRelocs(
zld: *Zld,
object_id: u32,
source_offset: u32,
) []align(1) const macho.relocation_info {
pub fn getRelocs(zld: *Zld, object_id: u32, source_offset: u32) []const macho.relocation_info {
const object = &zld.objects.items[object_id];
assert(object.hasEhFrameRecords());
const urel = object.eh_frame_relocs_lookup.get(source_offset) orelse
return &[0]macho.relocation_info{};
const all_relocs = object.getRelocs(object.eh_frame_sect.?);
const all_relocs = object.getRelocs(object.eh_frame_sect_id.?);
return all_relocs[urel.reloc.start..][0..urel.reloc.len];
}

View File

@ -20,7 +20,7 @@ const trace = @import("../../tracy.zig").trace;
const Allocator = mem.Allocator;
const Archive = @import("Archive.zig");
const Atom = @import("ZldAtom.zig");
const Cache = @import("../../Cache.zig");
const Cache = std.Build.Cache;
const CodeSignature = @import("CodeSignature.zig");
const Compilation = @import("../../Compilation.zig");
const DwarfInfo = @import("DwarfInfo.zig");
@ -1065,7 +1065,13 @@ pub const Zld = struct {
assert(offsets.items.len > 0);
const object_id = @intCast(u16, self.objects.items.len);
const object = try archive.parseObject(gpa, cpu_arch, offsets.items[0]);
const object = archive.parseObject(gpa, cpu_arch, offsets.items[0]) catch |e| switch (e) {
error.MismatchedCpuArchitecture => {
log.err("CPU architecture mismatch found in {s}", .{archive.name});
return e;
},
else => return e,
};
try self.objects.append(gpa, object);
try self.resolveSymbolsInObject(object_id, resolver);
@ -2391,22 +2397,20 @@ pub const Zld = struct {
const text_sect_header = self.sections.items(.header)[text_sect_id];
for (self.objects.items) |object| {
const dice = object.parseDataInCode() orelse continue;
if (!object.hasDataInCode()) continue;
const dice = object.data_in_code.items;
try out_dice.ensureUnusedCapacity(dice.len);
for (object.atoms.items) |atom_index| {
for (object.exec_atoms.items) |atom_index| {
const atom = self.getAtom(atom_index);
const sym = self.getSymbol(atom.getSymbolWithLoc());
const sect_id = sym.n_sect - 1;
if (sect_id != text_sect_id) {
continue;
}
if (sym.n_desc == N_DEAD) continue;
const source_addr = if (object.getSourceSymbol(atom.sym_index)) |source_sym|
source_sym.n_value
else blk: {
const nbase = @intCast(u32, object.in_symtab.?.len);
const source_sect_id = @intCast(u16, atom.sym_index - nbase);
const source_sect_id = @intCast(u8, atom.sym_index - nbase);
break :blk object.getSourceSection(source_sect_id).addr;
};
const filtered_dice = filterDataInCode(dice, source_addr, source_addr + atom.size);
@ -2699,12 +2703,12 @@ pub const Zld = struct {
// Exclude region comprising all symbol stabs.
const nlocals = self.dysymtab_cmd.nlocalsym;
const locals_buf = try self.gpa.alloc(u8, nlocals * @sizeOf(macho.nlist_64));
defer self.gpa.free(locals_buf);
const locals = try self.gpa.alloc(macho.nlist_64, nlocals);
defer self.gpa.free(locals);
const locals_buf = @ptrCast([*]u8, locals.ptr)[0 .. @sizeOf(macho.nlist_64) * nlocals];
const amt = try self.file.preadAll(locals_buf, self.symtab_cmd.symoff);
if (amt != locals_buf.len) return error.InputOutput;
const locals = @ptrCast([*]macho.nlist_64, @alignCast(@alignOf(macho.nlist_64), locals_buf))[0..nlocals];
const istab: usize = for (locals) |local, i| {
if (local.stab()) break i;

View File

@ -20,7 +20,7 @@ const lldMain = @import("../main.zig").lldMain;
const trace = @import("../tracy.zig").trace;
const build_options = @import("build_options");
const wasi_libc = @import("../wasi_libc.zig");
const Cache = @import("../Cache.zig");
const Cache = std.Build.Cache;
const Type = @import("../type.zig").Type;
const TypedValue = @import("../TypedValue.zig");
const LlvmObject = @import("../codegen/llvm.zig").Object;

View File

@ -20,7 +20,7 @@ const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
const wasi_libc = @import("wasi_libc.zig");
const translate_c = @import("translate_c.zig");
const clang = @import("clang.zig");
const Cache = @import("Cache.zig");
const Cache = std.Build.Cache;
const target_util = @import("target.zig");
const ThreadPool = @import("ThreadPool.zig");
const crash_report = @import("crash_report.zig");
@ -432,6 +432,8 @@ const usage_build_generic =
\\ -fno-Clang Prevent using Clang as the C/C++ compilation backend
\\ -freference-trace[=num] How many lines of reference trace should be shown per compile error
\\ -fno-reference-trace Disable reference trace
\\ -ferror-tracing Enable error tracing in ReleaseFast mode
\\ -fno-error-tracing Disable error tracing in Debug and ReleaseSafe mode
\\ -fsingle-threaded Code assumes there is only one thread
\\ -fno-single-threaded Code may not assume there is only one thread
\\ -fbuiltin Enable implicit builtin knowledge of functions
@ -490,6 +492,7 @@ const usage_build_generic =
\\ nodelete Indicate that the object cannot be deleted from a process
\\ notext Permit read-only relocations in read-only segments
\\ defs Force a fatal error if any undefined symbols remain
\\ undefs Reverse of -z defs
\\ origin Indicate that the object must have its origin processed
\\ nocopyreloc Disable the creation of copy relocations
\\ now (default) Force all relocations to be processed on load
@ -506,6 +509,7 @@ const usage_build_generic =
\\ zlib Compression with deflate/inflate
\\ --gc-sections Force removal of functions and data that are unreachable by the entry point or exported symbols
\\ --no-gc-sections Don't force removal of unreachable functions and data
\\ --sort-section=[value] Sort wildcard section patterns by 'name' or 'alignment'
\\ --subsystem [subsystem] (Windows) /SUBSYSTEM:<subsystem> to the linker
\\ --stack [size] Override default stack size
\\ --image-base [addr] Set base address for executable image
@ -728,6 +732,7 @@ fn buildOutputType(
var linker_script: ?[]const u8 = null;
var version_script: ?[]const u8 = null;
var disable_c_depfile = false;
var linker_sort_section: ?link.SortSection = null;
var linker_gc_sections: ?bool = null;
var linker_compress_debug_sections: ?link.CompressDebugSections = null;
var linker_allow_shlib_undefined: ?bool = null;
@ -797,6 +802,7 @@ fn buildOutputType(
var headerpad_max_install_names: bool = false;
var dead_strip_dylibs: bool = false;
var reference_trace: ?u32 = null;
var error_tracing: ?bool = null;
var pdb_out_path: ?[]const u8 = null;
// e.g. -m3dnow or -mno-outline-atomics. They correspond to std.Target llvm cpu feature names.
@ -1218,6 +1224,10 @@ fn buildOutputType(
};
} else if (mem.eql(u8, arg, "-fno-reference-trace")) {
reference_trace = null;
} else if (mem.eql(u8, arg, "-ferror-tracing")) {
error_tracing = true;
} else if (mem.eql(u8, arg, "-fno-error-tracing")) {
error_tracing = false;
} else if (mem.eql(u8, arg, "-rdynamic")) {
rdynamic = true;
} else if (mem.eql(u8, arg, "-fsoname")) {
@ -1326,6 +1336,8 @@ fn buildOutputType(
linker_z_notext = true;
} else if (mem.eql(u8, z_arg, "defs")) {
linker_z_defs = true;
} else if (mem.eql(u8, z_arg, "undefs")) {
linker_z_defs = false;
} else if (mem.eql(u8, z_arg, "origin")) {
linker_z_origin = true;
} else if (mem.eql(u8, z_arg, "nocopyreloc")) {
@ -1343,7 +1355,7 @@ fn buildOutputType(
} else if (mem.startsWith(u8, z_arg, "max-page-size=")) {
linker_z_max_page_size = parseIntSuffix(z_arg, "max-page-size=".len);
} else {
warn("unsupported linker extension flag: -z {s}", .{z_arg});
fatal("unsupported linker extension flag: -z {s}", .{z_arg});
}
} else if (mem.eql(u8, arg, "--import-memory")) {
linker_import_memory = true;
@ -1607,6 +1619,10 @@ fn buildOutputType(
build_id = true;
warn("ignoring build-id style argument: '{s}'", .{value});
continue;
} else if (mem.eql(u8, key, "--sort-common")) {
// this ignores --sort=common=<anything>; ignoring plain --sort-common
// is done below.
continue;
}
try linker_args.append(key);
try linker_args.append(value);
@ -1619,6 +1635,9 @@ fn buildOutputType(
needed = true;
} else if (mem.eql(u8, linker_arg, "-no-pie")) {
want_pie = false;
} else if (mem.eql(u8, linker_arg, "--sort-common")) {
// from ld.lld(1): --sort-common is ignored for GNU compatibility,
// this ignores plain --sort-common
} else if (mem.eql(u8, linker_arg, "--whole-archive") or
mem.eql(u8, linker_arg, "-whole-archive"))
{
@ -1872,6 +1891,8 @@ fn buildOutputType(
linker_gc_sections = true;
} else if (mem.eql(u8, arg, "-dead_strip_dylibs")) {
dead_strip_dylibs = true;
} else if (mem.eql(u8, arg, "--no-undefined")) {
linker_z_defs = true;
} else if (mem.eql(u8, arg, "--gc-sections")) {
linker_gc_sections = true;
} else if (mem.eql(u8, arg, "--no-gc-sections")) {
@ -1882,6 +1903,15 @@ fn buildOutputType(
linker_print_icf_sections = true;
} else if (mem.eql(u8, arg, "--print-map")) {
linker_print_map = true;
} else if (mem.eql(u8, arg, "--sort-section")) {
i += 1;
if (i >= linker_args.items.len) {
fatal("expected linker arg after '{s}'", .{arg});
}
const arg1 = linker_args.items[i];
linker_sort_section = std.meta.stringToEnum(link.SortSection, arg1) orelse {
fatal("expected [name|alignment] after --sort-section, found '{s}'", .{arg1});
};
} else if (mem.eql(u8, arg, "--allow-shlib-undefined") or
mem.eql(u8, arg, "-allow-shlib-undefined"))
{
@ -1937,6 +1967,8 @@ fn buildOutputType(
linker_z_notext = true;
} else if (mem.eql(u8, z_arg, "defs")) {
linker_z_defs = true;
} else if (mem.eql(u8, z_arg, "undefs")) {
linker_z_defs = false;
} else if (mem.eql(u8, z_arg, "origin")) {
linker_z_origin = true;
} else if (mem.eql(u8, z_arg, "nocopyreloc")) {
@ -1961,7 +1993,7 @@ fn buildOutputType(
} else if (mem.startsWith(u8, z_arg, "max-page-size=")) {
linker_z_max_page_size = parseIntSuffix(z_arg, "max-page-size=".len);
} else {
warn("unsupported linker extension flag: -z {s}", .{z_arg});
fatal("unsupported linker extension flag: -z {s}", .{z_arg});
}
} else if (mem.eql(u8, arg, "--major-image-version")) {
i += 1;
@ -2023,6 +2055,14 @@ fn buildOutputType(
// This option does not do anything.
} else if (mem.eql(u8, arg, "--export-all-symbols")) {
rdynamic = true;
} else if (mem.eql(u8, arg, "--color-diagnostics") or
mem.eql(u8, arg, "--color-diagnostics=always"))
{
color = .on;
} else if (mem.eql(u8, arg, "--no-color-diagnostics") or
mem.eql(u8, arg, "--color-diagnostics=never"))
{
color = .off;
} else if (mem.eql(u8, arg, "-s") or mem.eql(u8, arg, "--strip-all") or
mem.eql(u8, arg, "-S") or mem.eql(u8, arg, "--strip-debug"))
{
@ -2186,7 +2226,7 @@ fn buildOutputType(
have_version = true;
} else {
warn("unsupported linker arg: {s}", .{arg});
fatal("unsupported linker arg: {s}", .{arg});
}
}
@ -3056,6 +3096,7 @@ fn buildOutputType(
.version_script = version_script,
.disable_c_depfile = disable_c_depfile,
.soname = resolved_soname,
.linker_sort_section = linker_sort_section,
.linker_gc_sections = linker_gc_sections,
.linker_allow_shlib_undefined = linker_allow_shlib_undefined,
.linker_bind_global_refs_locally = linker_bind_global_refs_locally,
@ -3136,6 +3177,7 @@ fn buildOutputType(
.headerpad_max_install_names = headerpad_max_install_names,
.dead_strip_dylibs = dead_strip_dylibs,
.reference_trace = reference_trace,
.error_tracing = error_tracing,
.pdb_out_path = pdb_out_path,
}) catch |err| switch (err) {
error.LibCUnavailable => {
@ -3607,7 +3649,7 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void
defer if (enable_cache) man.deinit();
man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects
man.hashCSource(c_source_file) catch |err| {
Compilation.cache_helpers.hashCSource(&man, c_source_file) catch |err| {
fatal("unable to process '{s}': {s}", .{ c_source_file.src_path, @errorName(err) });
};
@ -4523,7 +4565,7 @@ fn fmtPathDir(
if (is_dir and (mem.eql(u8, entry.name, "zig-cache") or mem.eql(u8, entry.name, "zig-out"))) continue;
if (is_dir or entry.kind == .File and mem.endsWith(u8, entry.name, ".zig")) {
if (is_dir or entry.kind == .File and (mem.endsWith(u8, entry.name, ".zig") or mem.endsWith(u8, entry.name, ".zon"))) {
const full_path = try fs.path.join(fmt.gpa, &[_][]const u8{ file_path, entry.name });
defer fmt.gpa.free(full_path);

View File

@ -8,7 +8,7 @@ const log = std.log.scoped(.mingw);
const builtin = @import("builtin");
const Compilation = @import("Compilation.zig");
const build_options = @import("build_options");
const Cache = @import("Cache.zig");
const Cache = std.Build.Cache;
pub const CRTFile = enum {
crt2_o,

View File

@ -332,7 +332,6 @@ const Writer = struct {
.float_cast,
.int_cast,
.ptr_cast,
.qual_cast,
.truncate,
.align_cast,
.div_exact,
@ -507,6 +506,8 @@ const Writer = struct {
.reify,
.c_va_copy,
.c_va_end,
.const_cast,
.volatile_cast,
=> {
const inst_data = self.code.extraData(Zir.Inst.UnNode, extended.operand).data;
const src = LazySrcLoc.nodeOffset(inst_data.node);

View File

@ -1748,7 +1748,8 @@ fn transBinaryOperator(
const lhs_expr = stmt.getLHS();
const lhs_qt = getExprQualType(c, lhs_expr);
const lhs_qt_translated = try transQualType(c, scope, lhs_qt, lhs_expr.getBeginLoc());
const elem_type = lhs_qt_translated.castTag(.c_pointer).?.data.elem_type;
const c_pointer = getContainer(c, lhs_qt_translated).?;
const elem_type = c_pointer.castTag(.c_pointer).?.data.elem_type;
const sizeof = try Tag.sizeof.create(c.arena, elem_type);
const bitcast = try Tag.bit_cast.create(c.arena, .{ .lhs = ptrdiff_type, .rhs = infixOpNode });

Binary file not shown.

View File

@ -1541,3 +1541,30 @@ test "single item pointer to pointer to array to slice" {
const z1 = @as([]const i32, @as(*[1]i32, &x));
try expect(z1[0] == 1234);
}
test "peer type resolution forms error union" {
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
var foo: i32 = 123;
const result = if (foo < 0) switch (-foo) {
0 => unreachable,
42 => error.AccessDenied,
else => unreachable,
} else @intCast(u32, foo);
try expect(try result == 123);
}
test "@constCast without a result location" {
const x: i32 = 1234;
const y = @constCast(&x);
try expect(@TypeOf(y) == *i32);
try expect(y.* == 1234);
}
test "@volatileCast without a result location" {
var x: i32 = 1234;
var y: *volatile i32 = &x;
const z = @volatileCast(y);
try expect(@TypeOf(z) == *i32);
try expect(z.* == 1234);
}

View File

@ -1,11 +1,11 @@
const FooType = opaque {};
export fn bar() !FooType {
export fn bar() FooType {
return error.InvalidValue;
}
export fn bav() !@TypeOf(null) {
export fn bav() @TypeOf(null) {
return error.InvalidValue;
}
export fn baz() !@TypeOf(undefined) {
export fn baz() @TypeOf(undefined) {
return error.InvalidValue;
}
@ -13,7 +13,7 @@ export fn baz() !@TypeOf(undefined) {
// backend=stage2
// target=native
//
// :2:18: error: opaque return type 'tmp.FooType' not allowed
// :2:17: error: opaque return type 'tmp.FooType' not allowed
// :1:17: note: opaque declared here
// :5:18: error: return type '@TypeOf(null)' not allowed
// :8:18: error: return type '@TypeOf(undefined)' not allowed
// :5:17: error: return type '@TypeOf(null)' not allowed
// :8:17: error: return type '@TypeOf(undefined)' not allowed

View File

@ -4,6 +4,12 @@ comptime {
comptime {
_ = anyerror!anyerror;
}
fn someFunction() !anyerror {
return error.C;
}
comptime {
_ = someFunction;
}
// error
// backend=stage2
@ -11,3 +17,4 @@ comptime {
//
// :2:18: error: error union with payload of opaque type 'anyopaque' not allowed
// :5:18: error: error union with payload of error set type 'anyerror' not allowed
// :7:20: error: error union with payload of error set type 'anyerror' not allowed

View File

@ -1,12 +0,0 @@
pub export fn entry() void {
var a: [*:0]const volatile u16 = undefined;
_ = @qualCast([*]u16, a);
}
// error
// backend=stage2
// target=native
//
// :3:9: error: '@qualCast' can only modify 'const' and 'volatile' qualifiers
// :3:9: note: expected type '[*]const volatile u16'
// :3:9: note: got type '[*:0]const volatile u16'

View File

@ -9,4 +9,4 @@ export fn entry() void {
// target=native
//
// :3:15: error: cast discards const qualifier
// :3:15: note: consider using '@qualCast'
// :3:15: note: consider using '@constCast'

View File

@ -1,4 +1,8 @@
const std = @import("std");
const Builder = std.Build.Builder;
const CompileStep = std.Build.CompileStep;
const FileSource = std.Build.FileSource;
const Step = std.Build.Step;
pub fn build(b: *std.Build) void {
const test_step = b.step("test", "Test");
@ -10,18 +14,18 @@ pub fn build(b: *std.Build) void {
.os_tag = .macos,
};
testUuid(b, test_step, .ReleaseSafe, aarch64_macos, "675bb6ba8e5d3d3191f7936d7168f0e9");
testUuid(b, test_step, .ReleaseFast, aarch64_macos, "675bb6ba8e5d3d3191f7936d7168f0e9");
testUuid(b, test_step, .ReleaseSmall, aarch64_macos, "675bb6ba8e5d3d3191f7936d7168f0e9");
testUuid(b, test_step, .ReleaseSafe, aarch64_macos);
testUuid(b, test_step, .ReleaseFast, aarch64_macos);
testUuid(b, test_step, .ReleaseSmall, aarch64_macos);
const x86_64_macos = std.zig.CrossTarget{
.cpu_arch = .x86_64,
.os_tag = .macos,
};
testUuid(b, test_step, .ReleaseSafe, x86_64_macos, "5b7071b4587c3071b0d2352fadce0e48");
testUuid(b, test_step, .ReleaseFast, x86_64_macos, "5b7071b4587c3071b0d2352fadce0e48");
testUuid(b, test_step, .ReleaseSmall, x86_64_macos, "4b58f2583c383169bbe3a716bd240048");
testUuid(b, test_step, .ReleaseSafe, x86_64_macos);
testUuid(b, test_step, .ReleaseFast, x86_64_macos);
testUuid(b, test_step, .ReleaseSmall, x86_64_macos);
}
fn testUuid(
@ -29,25 +33,23 @@ fn testUuid(
test_step: *std.Build.Step,
optimize: std.builtin.OptimizeMode,
target: std.zig.CrossTarget,
comptime exp: []const u8,
) void {
// The calculated UUID value is independent of debug info and so it should
// stay the same across builds.
{
const dylib = simpleDylib(b, optimize, target);
const check_dylib = dylib.checkObject(.macho);
check_dylib.checkStart("cmd UUID");
check_dylib.checkNext("uuid " ++ exp);
test_step.dependOn(&check_dylib.step);
const install_step = installWithRename(dylib, "test1.dylib");
install_step.step.dependOn(&dylib.step);
}
{
const dylib = simpleDylib(b, optimize, target);
dylib.strip = true;
const check_dylib = dylib.checkObject(.macho);
check_dylib.checkStart("cmd UUID");
check_dylib.checkNext("uuid " ++ exp);
test_step.dependOn(&check_dylib.step);
const install_step = installWithRename(dylib, "test2.dylib");
install_step.step.dependOn(&dylib.step);
}
const cmp_step = CompareUuid.create(b, "test1.dylib", "test2.dylib");
test_step.dependOn(&cmp_step.step);
}
fn simpleDylib(
@ -65,3 +67,118 @@ fn simpleDylib(
dylib.linkLibC();
return dylib;
}
fn installWithRename(cs: *CompileStep, name: []const u8) *InstallWithRename {
const step = InstallWithRename.create(cs.builder, cs.getOutputSource(), name);
cs.builder.getInstallStep().dependOn(&step.step);
return step;
}
const InstallWithRename = struct {
pub const base_id = .custom;
step: Step,
builder: *Builder,
source: FileSource,
name: []const u8,
pub fn create(
builder: *Builder,
source: FileSource,
name: []const u8,
) *InstallWithRename {
const self = builder.allocator.create(InstallWithRename) catch @panic("OOM");
self.* = InstallWithRename{
.builder = builder,
.step = Step.init(.custom, builder.fmt("install and rename: {s} -> {s}", .{
source.getDisplayName(),
name,
}), builder.allocator, make),
.source = source,
.name = builder.dupe(name),
};
return self;
}
fn make(step: *Step) anyerror!void {
const self = @fieldParentPtr(InstallWithRename, "step", step);
const source_path = self.source.getPath(self.builder);
const target_path = self.builder.getInstallPath(.lib, self.name);
self.builder.updateFile(source_path, target_path) catch |err| {
std.log.err("Unable to rename: {s} -> {s}", .{ source_path, target_path });
return err;
};
}
};
const CompareUuid = struct {
pub const base_id = .custom;
step: Step,
builder: *Builder,
lhs: []const u8,
rhs: []const u8,
pub fn create(builder: *Builder, lhs: []const u8, rhs: []const u8) *CompareUuid {
const self = builder.allocator.create(CompareUuid) catch @panic("OOM");
self.* = CompareUuid{
.builder = builder,
.step = Step.init(
.custom,
builder.fmt("compare uuid: {s} and {s}", .{
lhs,
rhs,
}),
builder.allocator,
make,
),
.lhs = lhs,
.rhs = rhs,
};
return self;
}
fn make(step: *Step) anyerror!void {
const self = @fieldParentPtr(CompareUuid, "step", step);
const gpa = self.builder.allocator;
var lhs_uuid: [16]u8 = undefined;
const lhs_path = self.builder.getInstallPath(.lib, self.lhs);
try parseUuid(gpa, lhs_path, &lhs_uuid);
var rhs_uuid: [16]u8 = undefined;
const rhs_path = self.builder.getInstallPath(.lib, self.rhs);
try parseUuid(gpa, rhs_path, &rhs_uuid);
try std.testing.expectEqualStrings(&lhs_uuid, &rhs_uuid);
}
fn parseUuid(gpa: std.mem.Allocator, path: []const u8, uuid: *[16]u8) anyerror!void {
const max_bytes: usize = 20 * 1024 * 1024;
const data = try std.fs.cwd().readFileAllocOptions(
gpa,
path,
max_bytes,
null,
@alignOf(u64),
null,
);
var stream = std.io.fixedBufferStream(data);
const reader = stream.reader();
const hdr = try reader.readStruct(std.macho.mach_header_64);
if (hdr.magic != std.macho.MH_MAGIC_64) {
return error.InvalidMagicNumber;
}
var it = std.macho.LoadCommandIterator{
.ncmds = hdr.ncmds,
.buffer = data[@sizeOf(std.macho.mach_header_64)..][0..hdr.sizeofcmds],
};
const cmd = while (it.next()) |cmd| switch (cmd.cmd()) {
.UUID => break cmd.cast(std.macho.uuid_command).?,
else => {},
} else return error.UuidLoadCommandNotFound;
std.mem.copy(u8, uuid, &cmd.uuid);
}
};

View File

@ -27,6 +27,7 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
cases.add("test/standalone/noreturn_call/inline.zig");
cases.add("test/standalone/noreturn_call/as_arg.zig");
cases.addBuildFile("test/standalone/test_runner_path/build.zig", .{ .requires_stage2 = true });
cases.addBuildFile("test/standalone/issue_13970/build.zig", .{});
cases.addBuildFile("test/standalone/main_pkg_path/build.zig", .{});
cases.addBuildFile("test/standalone/shared_library/build.zig", .{});
cases.addBuildFile("test/standalone/mix_o_files/build.zig", .{});
@ -102,4 +103,5 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
cases.addBuildFile("test/standalone/issue_13030/build.zig", .{ .build_modes = true });
cases.addBuildFile("test/standalone/emit_asm_and_bin/build.zig", .{});
cases.addBuildFile("test/standalone/issue_12588/build.zig", .{});
cases.addBuildFile("test/standalone/embed_generated_file/build.zig", .{});
}

Some files were not shown because too many files have changed in this diff Show More