authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-02-18 09:33:27-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-02-18 09:33:27-07:00
logefdc94c10712f610e7de5e49fd9cd6f88b4bbbae
tree4b66ec30176843b0efd87b73199c75aa2fba675d
parent06df842e4d313e81444063803deff306602e0a17
parentc32171991b25b323cd68ff96c294bf5a6fa753b8

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


121 files changed, 6904 insertions(+), 4184 deletions(-)

CMakeLists.txt+3-2
...@@ -216,6 +216,9 @@ set(ZIG_STAGE2_SOURCES...@@ -216,6 +216,9 @@ set(ZIG_STAGE2_SOURCES
216 "${CMAKE_SOURCE_DIR}/lib/std/atomic/stack.zig"216 "${CMAKE_SOURCE_DIR}/lib/std/atomic/stack.zig"
217 "${CMAKE_SOURCE_DIR}/lib/std/base64.zig"217 "${CMAKE_SOURCE_DIR}/lib/std/base64.zig"
218 "${CMAKE_SOURCE_DIR}/lib/std/buf_map.zig"218 "${CMAKE_SOURCE_DIR}/lib/std/buf_map.zig"
219 "${CMAKE_SOURCE_DIR}/lib/std/Build.zig"
220 "${CMAKE_SOURCE_DIR}/lib/std/Build/Cache.zig"
221 "${CMAKE_SOURCE_DIR}/lib/std/Build/Cache/DepTokenizer.zig"
219 "${CMAKE_SOURCE_DIR}/lib/std/builtin.zig"222 "${CMAKE_SOURCE_DIR}/lib/std/builtin.zig"
220 "${CMAKE_SOURCE_DIR}/lib/std/c.zig"223 "${CMAKE_SOURCE_DIR}/lib/std/c.zig"
221 "${CMAKE_SOURCE_DIR}/lib/std/c/linux.zig"224 "${CMAKE_SOURCE_DIR}/lib/std/c/linux.zig"
...@@ -523,9 +526,7 @@ set(ZIG_STAGE2_SOURCES...@@ -523,9 +526,7 @@ set(ZIG_STAGE2_SOURCES
523 "${CMAKE_SOURCE_DIR}/lib/std/zig/tokenizer.zig"526 "${CMAKE_SOURCE_DIR}/lib/std/zig/tokenizer.zig"
524 "${CMAKE_SOURCE_DIR}/src/Air.zig"527 "${CMAKE_SOURCE_DIR}/src/Air.zig"
525 "${CMAKE_SOURCE_DIR}/src/AstGen.zig"528 "${CMAKE_SOURCE_DIR}/src/AstGen.zig"
526 "${CMAKE_SOURCE_DIR}/src/Cache.zig"
527 "${CMAKE_SOURCE_DIR}/src/Compilation.zig"529 "${CMAKE_SOURCE_DIR}/src/Compilation.zig"
528 "${CMAKE_SOURCE_DIR}/src/DepTokenizer.zig"
529 "${CMAKE_SOURCE_DIR}/src/Liveness.zig"530 "${CMAKE_SOURCE_DIR}/src/Liveness.zig"
530 "${CMAKE_SOURCE_DIR}/src/Module.zig"531 "${CMAKE_SOURCE_DIR}/src/Module.zig"
531 "${CMAKE_SOURCE_DIR}/src/Package.zig"532 "${CMAKE_SOURCE_DIR}/src/Package.zig"
build.zig+5-7
...@@ -40,15 +40,11 @@ pub fn build(b: *std.Build) !void {...@@ -40,15 +40,11 @@ pub fn build(b: *std.Build) !void {
40 });40 });
41 docgen_exe.single_threaded = single_threaded;41 docgen_exe.single_threaded = single_threaded;
4242
43 const rel_zig_exe = try fs.path.relative(b.allocator, b.build_root, b.zig_exe);43 const langref_out_path = try b.cache_root.join(b.allocator, &.{"langref.html"});
44 const langref_out_path = fs.path.join(
45 b.allocator,
46 &[_][]const u8{ b.cache_root, "langref.html" },
47 ) catch unreachable;
48 const docgen_cmd = docgen_exe.run();44 const docgen_cmd = docgen_exe.run();
49 docgen_cmd.addArgs(&[_][]const u8{45 docgen_cmd.addArgs(&[_][]const u8{
50 "--zig",46 "--zig",
51 rel_zig_exe,47 b.zig_exe,
52 "doc" ++ fs.path.sep_str ++ "langref.html.in",48 "doc" ++ fs.path.sep_str ++ "langref.html.in",
53 langref_out_path,49 langref_out_path,
54 });50 });
...@@ -133,6 +129,8 @@ pub fn build(b: *std.Build) !void {...@@ -133,6 +129,8 @@ pub fn build(b: *std.Build) !void {
133 "compress-gettysburg.txt",129 "compress-gettysburg.txt",
134 "compress-pi.txt",130 "compress-pi.txt",
135 "rfc1951.txt",131 "rfc1951.txt",
132 // exclude files from lib/std/compress/lzma/testdata
133 ".lzma",
136 // exclude files from lib/std/compress/xz/testdata134 // exclude files from lib/std/compress/xz/testdata
137 ".xz",135 ".xz",
138 // exclude files from lib/std/tz/136 // exclude files from lib/std/tz/
...@@ -219,7 +217,7 @@ pub fn build(b: *std.Build) !void {...@@ -219,7 +217,7 @@ pub fn build(b: *std.Build) !void {
219217
220 var code: u8 = undefined;218 var code: u8 = undefined;
221 const git_describe_untrimmed = b.execAllowFail(&[_][]const u8{219 const git_describe_untrimmed = b.execAllowFail(&[_][]const u8{
222 "git", "-C", b.build_root, "describe", "--match", "*.*.*", "--tags",220 "git", "-C", b.build_root.path orelse ".", "describe", "--match", "*.*.*", "--tags",
223 }, &code, .Ignore) catch {221 }, &code, .Ignore) catch {
224 break :v version_string;222 break :v version_string;
225 };223 };
doc/langref.html.in+79-70
...@@ -5393,7 +5393,6 @@ pub fn parseU64(buf: []const u8, radix: u8) !u64 {...@@ -5393,7 +5393,6 @@ pub fn parseU64(buf: []const u8, radix: u8) !u64 {
5393 // x *= radix5393 // x *= radix
5394 var ov = @mulWithOverflow(x, radix);5394 var ov = @mulWithOverflow(x, radix);
5395 if (ov[1] != 0) return error.OverFlow;5395 if (ov[1] != 0) return error.OverFlow;
5396
53975396
5398 // x += digit5397 // x += digit
5399 ov = @addWithOverflow(ov[0], digit);5398 ov = @addWithOverflow(ov[0], digit);
...@@ -6067,7 +6066,7 @@ struct Foo *do_a_thing(void) {...@@ -6067,7 +6066,7 @@ struct Foo *do_a_thing(void) {
6067 <p>Zig code</p>6066 <p>Zig code</p>
6068 {#syntax_block|zig|call_malloc_from_zig.zig#}6067 {#syntax_block|zig|call_malloc_from_zig.zig#}
6069// malloc prototype included for reference6068// malloc prototype included for reference
6070extern fn malloc(size: size_t) ?*u8;6069extern fn malloc(size: usize) ?*u8;
60716070
6072fn doAThing() ?*Foo {6071fn doAThing() ?*Foo {
6073 const ptr = malloc(1234) orelse return null;6072 const ptr = malloc(1234) orelse return null;
...@@ -7479,64 +7478,66 @@ pub fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) usize {...@@ -7479,64 +7478,66 @@ pub fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) usize {
7479 <p>7478 <p>
7480 Dissecting the syntax:7479 Dissecting the syntax:
7481 </p>7480 </p>
7482 {#syntax_block|zig|Assembly Syntax Explained#}7481 {#code_begin|syntax|Assembly Syntax Explained#}
7483// Inline assembly is an expression which returns a value.7482pub fn syscall1(number: usize, arg1: usize) usize {
7484// the `asm` keyword begins the expression.7483 // Inline assembly is an expression which returns a value.
7485_ = asm7484 // the `asm` keyword begins the expression.
7486// `volatile` is an optional modifier that tells Zig this7485 return asm
7487// inline assembly expression has side-effects. Without7486 // `volatile` is an optional modifier that tells Zig this
7488// `volatile`, Zig is allowed to delete the inline assembly7487 // inline assembly expression has side-effects. Without
7489// code if the result is unused.7488 // `volatile`, Zig is allowed to delete the inline assembly
7490volatile (7489 // code if the result is unused.
7491// Next is a comptime string which is the assembly code.7490 volatile (
7492// Inside this string one may use `%[ret]`, `%[number]`,7491 // Next is a comptime string which is the assembly code.
7493// or `%[arg1]` where a register is expected, to specify7492 // Inside this string one may use `%[ret]`, `%[number]`,
7494// the register that Zig uses for the argument or return value,7493 // or `%[arg1]` where a register is expected, to specify
7495// if the register constraint strings are used. However in7494 // the register that Zig uses for the argument or return value,
7496// the below code, this is not used. A literal `%` can be7495 // if the register constraint strings are used. However in
7497// obtained by escaping it with a double percent: `%%`.7496 // the below code, this is not used. A literal `%` can be
7498// Often multiline string syntax comes in handy here.7497 // obtained by escaping it with a double percent: `%%`.
7498 // Often multiline string syntax comes in handy here.
7499 \\syscall7499 \\syscall
7500// Next is the output. It is possible in the future Zig will7500 // Next is the output. It is possible in the future Zig will
7501// support multiple outputs, depending on how7501 // support multiple outputs, depending on how
7502// https://github.com/ziglang/zig/issues/215 is resolved.7502 // https://github.com/ziglang/zig/issues/215 is resolved.
7503// It is allowed for there to be no outputs, in which case7503 // It is allowed for there to be no outputs, in which case
7504// this colon would be directly followed by the colon for the inputs.7504 // this colon would be directly followed by the colon for the inputs.
7505 :7505 :
7506// This specifies the name to be used in `%[ret]` syntax in7506 // This specifies the name to be used in `%[ret]` syntax in
7507// the above assembly string. This example does not use it,7507 // the above assembly string. This example does not use it,
7508// but the syntax is mandatory.7508 // but the syntax is mandatory.
7509 [ret]7509 [ret]
7510// Next is the output constraint string. This feature is still7510 // Next is the output constraint string. This feature is still
7511// considered unstable in Zig, and so LLVM/GCC documentation7511 // considered unstable in Zig, and so LLVM/GCC documentation
7512// must be used to understand the semantics.7512 // must be used to understand the semantics.
7513// http://releases.llvm.org/10.0.0/docs/LangRef.html#inline-asm-constraint-string7513 // http://releases.llvm.org/10.0.0/docs/LangRef.html#inline-asm-constraint-string
7514// https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html7514 // https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html
7515// In this example, the constraint string means "the result value of7515 // In this example, the constraint string means "the result value of
7516// this inline assembly instruction is whatever is in $rax".7516 // this inline assembly instruction is whatever is in $rax".
7517 "={rax}"7517 "={rax}"
7518// Next is either a value binding, or `->` and then a type. The7518 // Next is either a value binding, or `->` and then a type. The
7519// type is the result type of the inline assembly expression.7519 // type is the result type of the inline assembly expression.
7520// If it is a value binding, then `%[ret]` syntax would be used7520 // If it is a value binding, then `%[ret]` syntax would be used
7521// to refer to the register bound to the value.7521 // to refer to the register bound to the value.
7522 (-> usize)7522 (-> usize)
7523// Next is the list of inputs.7523 // Next is the list of inputs.
7524// The constraint for these inputs means, "when the assembly code is7524 // The constraint for these inputs means, "when the assembly code is
7525// executed, $rax shall have the value of `number` and $rdi shall have7525 // executed, $rax shall have the value of `number` and $rdi shall have
7526// the value of `arg1`". Any number of input parameters is allowed,7526 // the value of `arg1`". Any number of input parameters is allowed,
7527// including none.7527 // including none.
7528 : [number] "{rax}" (number),7528 : [number] "{rax}" (number),
7529 [arg1] "{rdi}" (arg1)7529 [arg1] "{rdi}" (arg1)
7530// Next is the list of clobbers. These declare a set of registers whose7530 // Next is the list of clobbers. These declare a set of registers whose
7531// values will not be preserved by the execution of this assembly code.7531 // values will not be preserved by the execution of this assembly code.
7532// These do not include output or input registers. The special clobber7532 // These do not include output or input registers. The special clobber
7533// value of "memory" means that the assembly writes to arbitrary undeclared7533 // value of "memory" means that the assembly writes to arbitrary undeclared
7534// memory locations - not only the memory pointed to by a declared indirect7534 // memory locations - not only the memory pointed to by a declared indirect
7535// output. In this example we list $rcx and $r11 because it is known the7535 // output. In this example we list $rcx and $r11 because it is known the
7536// kernel syscall does not preserve these registers.7536 // kernel syscall does not preserve these registers.
7537 : "rcx", "r11"7537 : "rcx", "r11"
7538);7538 );
7539 {#end_syntax_block#}7539}
7540 {#code_end#}
7540 <p>7541 <p>
7541 For x86 and x86_64 targets, the syntax is AT&amp;T syntax, rather than the more7542 For x86 and x86_64 targets, the syntax is AT&amp;T syntax, rather than the more
7542 popular Intel syntax. This is due to technical constraints; assembly parsing is7543 popular Intel syntax. This is due to technical constraints; assembly parsing is
...@@ -7892,7 +7893,7 @@ fn add(a: i32, b: i32) i32 {...@@ -7892,7 +7893,7 @@ fn add(a: i32, b: i32) i32 {
7892 {#syntax#}@call{#endsyntax#} allows more flexibility than normal function call syntax does. The7893 {#syntax#}@call{#endsyntax#} allows more flexibility than normal function call syntax does. The
7893 {#syntax#}CallModifier{#endsyntax#} enum is reproduced here:7894 {#syntax#}CallModifier{#endsyntax#} enum is reproduced here:
7894 </p>7895 </p>
7895 {#syntax_block|zig|builtin.CallModifier struct#}7896 {#code_begin|syntax|builtin.CallModifier struct#}
7896pub const CallModifier = enum {7897pub const CallModifier = enum {
7897 /// Equivalent to function call syntax.7898 /// Equivalent to function call syntax.
7898 auto,7899 auto,
...@@ -7926,7 +7927,7 @@ pub const CallModifier = enum {...@@ -7926,7 +7927,7 @@ pub const CallModifier = enum {
7926 /// compile-time, a compile error is emitted instead.7927 /// compile-time, a compile error is emitted instead.
7927 compile_time,7928 compile_time,
7928};7929};
7929 {#end_syntax_block#}7930 {#code_end#}
7930 {#header_close#}7931 {#header_close#}
79317932
7932 {#header_open|@cDefine#}7933 {#header_open|@cDefine#}
...@@ -8128,6 +8129,13 @@ test "main" {...@@ -8128,6 +8129,13 @@ test "main" {
8128 {#code_end#}8129 {#code_end#}
8129 {#header_close#}8130 {#header_close#}
81308131
8132 {#header_open|@constCast#}
8133 <pre>{#syntax#}@constCast(value: anytype) DestType{#endsyntax#}</pre>
8134 <p>
8135 Remove {#syntax#}const{#endsyntax#} qualifier from a pointer.
8136 </p>
8137 {#header_close#}
8138
8131 {#header_open|@ctz#}8139 {#header_open|@ctz#}
8132 <pre>{#syntax#}@ctz(operand: anytype){#endsyntax#}</pre>8140 <pre>{#syntax#}@ctz(operand: anytype){#endsyntax#}</pre>
8133 <p>{#syntax#}@TypeOf(operand){#endsyntax#} must be an integer type or an integer vector type.</p>8141 <p>{#syntax#}@TypeOf(operand){#endsyntax#} must be an integer type or an integer vector type.</p>
...@@ -8813,7 +8821,8 @@ pub const PrefetchOptions = struct {...@@ -8813,7 +8821,8 @@ pub const PrefetchOptions = struct {
8813 {#syntax#}@ptrCast{#endsyntax#} cannot be used for:8821 {#syntax#}@ptrCast{#endsyntax#} cannot be used for:
8814 </p>8822 </p>
8815 <ul>8823 <ul>
8816 <li>Removing {#syntax#}const{#endsyntax#} or {#syntax#}volatile{#endsyntax#} qualifier, use {#link|@qualCast#}.</li>8824 <li>Removing {#syntax#}const{#endsyntax#} qualifier, use {#link|@constCast#}.</li>
8825 <li>Removing {#syntax#}volatile{#endsyntax#} qualifier, use {#link|@volatileCast#}.</li>
8817 <li>Changing pointer address space, use {#link|@addrSpaceCast#}.</li>8826 <li>Changing pointer address space, use {#link|@addrSpaceCast#}.</li>
8818 <li>Increasing pointer alignment, use {#link|@alignCast#}.</li>8827 <li>Increasing pointer alignment, use {#link|@alignCast#}.</li>
8819 <li>Casting a non-slice pointer to a slice, use slicing syntax {#syntax#}ptr[start..end]{#endsyntax#}.</li>8828 <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 {...@@ -8830,13 +8839,6 @@ pub const PrefetchOptions = struct {
88308839
8831 {#header_close#}8840 {#header_close#}
88328841
8833 {#header_open|@qualCast#}
8834 <pre>{#syntax#}@qualCast(comptime DestType: type, value: anytype) DestType{#endsyntax#}</pre>
8835 <p>
8836 Remove {#syntax#}const{#endsyntax#} or {#syntax#}volatile{#endsyntax#} qualifier from a pointer.
8837 </p>
8838 {#header_close#}
8839
8840 {#header_open|@rem#}8842 {#header_open|@rem#}
8841 <pre>{#syntax#}@rem(numerator: T, denominator: T) T{#endsyntax#}</pre>8843 <pre>{#syntax#}@rem(numerator: T, denominator: T) T{#endsyntax#}</pre>
8842 <p>8844 <p>
...@@ -9524,6 +9526,13 @@ fn foo(comptime T: type, ptr: *T) T {...@@ -9524,6 +9526,13 @@ fn foo(comptime T: type, ptr: *T) T {
9524 <pre>{#syntax#}@Vector(len: comptime_int, Element: type) type{#endsyntax#}</pre>9526 <pre>{#syntax#}@Vector(len: comptime_int, Element: type) type{#endsyntax#}</pre>
9525 <p>Creates {#link|Vectors#}.</p>9527 <p>Creates {#link|Vectors#}.</p>
9526 {#header_close#}9528 {#header_close#}
9529
9530 {#header_open|@volatileCast#}
9531 <pre>{#syntax#}@volatileCast(value: anytype) DestType{#endsyntax#}</pre>
9532 <p>
9533 Remove {#syntax#}volatile{#endsyntax#} qualifier from a pointer.
9534 </p>
9535 {#header_close#}
9527 {#header_close#}9536 {#header_close#}
95289537
9529 {#header_open|Build Mode#}9538 {#header_open|Build Mode#}
...@@ -9604,7 +9613,7 @@ pub fn build(b: *std.Build) void {...@@ -9604,7 +9613,7 @@ pub fn build(b: *std.Build) void {
9604 {#header_close#}9613 {#header_close#}
96059614
9606 {#header_open|Single Threaded Builds#}9615 {#header_open|Single Threaded Builds#}
9607 <p>Zig has a compile option <kbd>--single-threaded</kbd> which has the following effects:</p>9616 <p>Zig has a compile option <kbd>-fsingle-threaded</kbd> which has the following effects:</p>
9608 <ul>9617 <ul>
9609 <li>All {#link|Thread Local Variables#} are treated as regular {#link|Container Level Variables#}.</li>9618 <li>All {#link|Thread Local Variables#} are treated as regular {#link|Container Level Variables#}.</li>
9610 <li>The overhead of {#link|Async Functions#} becomes equivalent to function call overhead.</li>9619 <li>The overhead of {#link|Async Functions#} becomes equivalent to function call overhead.</li>
lib/build_runner.zig+53-12
...@@ -43,13 +43,40 @@ pub fn main() !void {...@@ -43,13 +43,40 @@ pub fn main() !void {
4343
44 const host = try std.zig.system.NativeTargetInfo.detect(.{});44 const host = try std.zig.system.NativeTargetInfo.detect(.{});
4545
46 const build_root_directory: std.Build.Cache.Directory = .{
47 .path = build_root,
48 .handle = try std.fs.cwd().openDir(build_root, .{}),
49 };
50
51 const local_cache_directory: std.Build.Cache.Directory = .{
52 .path = cache_root,
53 .handle = try std.fs.cwd().makeOpenPath(cache_root, .{}),
54 };
55
56 const global_cache_directory: std.Build.Cache.Directory = .{
57 .path = global_cache_root,
58 .handle = try std.fs.cwd().makeOpenPath(global_cache_root, .{}),
59 };
60
61 var cache: std.Build.Cache = .{
62 .gpa = allocator,
63 .manifest_dir = try local_cache_directory.handle.makeOpenPath("h", .{}),
64 };
65 cache.addPrefix(.{ .path = null, .handle = std.fs.cwd() });
66 cache.addPrefix(build_root_directory);
67 cache.addPrefix(local_cache_directory);
68 cache.addPrefix(global_cache_directory);
69
70 //cache.hash.addBytes(builtin.zig_version);
71
46 const builder = try std.Build.create(72 const builder = try std.Build.create(
47 allocator,73 allocator,
48 zig_exe,74 zig_exe,
49 build_root,75 build_root_directory,
50 cache_root,76 local_cache_directory,
51 global_cache_root,77 global_cache_directory,
52 host,78 host,
79 &cache,
53 );80 );
54 defer builder.destroy();81 defer builder.destroy();
5582
...@@ -93,6 +120,8 @@ pub fn main() !void {...@@ -93,6 +120,8 @@ pub fn main() !void {
93 std.debug.print("Expected argument after {s}\n\n", .{arg});120 std.debug.print("Expected argument after {s}\n\n", .{arg});
94 return usageAndErr(builder, false, stderr_stream);121 return usageAndErr(builder, false, stderr_stream);
95 };122 };
123 } else if (mem.eql(u8, arg, "-l") or mem.eql(u8, arg, "--list-steps")) {
124 return steps(builder, false, stdout_stream);
96 } else if (mem.eql(u8, arg, "--prefix-lib-dir")) {125 } else if (mem.eql(u8, arg, "--prefix-lib-dir")) {
97 dir_list.lib_dir = nextArg(args, &arg_idx) orelse {126 dir_list.lib_dir = nextArg(args, &arg_idx) orelse {
98 std.debug.print("Expected argument after {s}\n\n", .{arg});127 std.debug.print("Expected argument after {s}\n\n", .{arg});
...@@ -136,7 +165,7 @@ pub fn main() !void {...@@ -136,7 +165,7 @@ pub fn main() !void {
136 return usageAndErr(builder, false, stderr_stream);165 return usageAndErr(builder, false, stderr_stream);
137 };166 };
138 } else if (mem.eql(u8, arg, "--zig-lib-dir")) {167 } else if (mem.eql(u8, arg, "--zig-lib-dir")) {
139 builder.override_lib_dir = nextArg(args, &arg_idx) orelse {168 builder.zig_lib_dir = nextArg(args, &arg_idx) orelse {
140 std.debug.print("Expected argument after --zig-lib-dir\n\n", .{});169 std.debug.print("Expected argument after --zig-lib-dir\n\n", .{});
141 return usageAndErr(builder, false, stderr_stream);170 return usageAndErr(builder, false, stderr_stream);
142 };171 };
...@@ -232,20 +261,13 @@ pub fn main() !void {...@@ -232,20 +261,13 @@ pub fn main() !void {
232 };261 };
233}262}
234263
235fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !void {264fn steps(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !void {
236 // run the build script to collect the options265 // run the build script to collect the options
237 if (!already_ran_build) {266 if (!already_ran_build) {
238 builder.resolveInstallPrefix(null, .{});267 builder.resolveInstallPrefix(null, .{});
239 try builder.runBuild(root);268 try builder.runBuild(root);
240 }269 }
241270
242 try out_stream.print(
243 \\Usage: {s} build [steps] [options]
244 \\
245 \\Steps:
246 \\
247 , .{builder.zig_exe});
248
249 const allocator = builder.allocator;271 const allocator = builder.allocator;
250 for (builder.top_level_steps.items) |top_level_step| {272 for (builder.top_level_steps.items) |top_level_step| {
251 const name = if (&top_level_step.step == builder.default_step)273 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...@@ -254,6 +276,23 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi
254 top_level_step.step.name;276 top_level_step.step.name;
255 try out_stream.print(" {s:<28} {s}\n", .{ name, top_level_step.description });277 try out_stream.print(" {s:<28} {s}\n", .{ name, top_level_step.description });
256 }278 }
279}
280
281fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !void {
282 // run the build script to collect the options
283 if (!already_ran_build) {
284 builder.resolveInstallPrefix(null, .{});
285 try builder.runBuild(root);
286 }
287
288 try out_stream.print(
289 \\
290 \\Usage: {s} build [steps] [options]
291 \\
292 \\Steps:
293 \\
294 , .{builder.zig_exe});
295 try steps(builder, true, out_stream);
257296
258 try out_stream.writeAll(297 try out_stream.writeAll(
259 \\298 \\
...@@ -284,6 +323,7 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi...@@ -284,6 +323,7 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi
284 \\ Windows programs on Linux hosts. (default: no)323 \\ Windows programs on Linux hosts. (default: no)
285 \\324 \\
286 \\ -h, --help Print this help and exit325 \\ -h, --help Print this help and exit
326 \\ -l, --list-steps Print available steps
287 \\ --verbose Print commands before executing them327 \\ --verbose Print commands before executing them
288 \\ --color [auto|off|on] Enable or disable colored error messages328 \\ --color [auto|off|on] Enable or disable colored error messages
289 \\ --prominent-compile-errors Output compile errors formatted for a human to read329 \\ --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...@@ -292,6 +332,7 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi
292 \\332 \\
293 );333 );
294334
335 const allocator = builder.allocator;
295 if (builder.available_options_list.items.len == 0) {336 if (builder.available_options_list.items.len == 0) {
296 try out_stream.print(" (none)\n", .{});337 try out_stream.print(" (none)\n", .{});
297 } else {338 } else {
lib/compiler_rt.zig+1
...@@ -23,6 +23,7 @@ comptime {...@@ -23,6 +23,7 @@ comptime {
23 _ = @import("compiler_rt/multf3.zig");23 _ = @import("compiler_rt/multf3.zig");
24 _ = @import("compiler_rt/mulxf3.zig");24 _ = @import("compiler_rt/mulxf3.zig");
2525
26 _ = @import("compiler_rt/powiXf2.zig");
26 _ = @import("compiler_rt/mulc3.zig");27 _ = @import("compiler_rt/mulc3.zig");
27 _ = @import("compiler_rt/mulhc3.zig");28 _ = @import("compiler_rt/mulhc3.zig");
28 _ = @import("compiler_rt/mulsc3.zig");29 _ = @import("compiler_rt/mulsc3.zig");
lib/compiler_rt/README.md+155-19
...@@ -77,15 +77,15 @@ Integer and Float Operations...@@ -77,15 +77,15 @@ Integer and Float Operations
77| ✓ | __ucmpdi2 | u64 | u64 | i32 | .. |77| ✓ | __ucmpdi2 | u64 | u64 | i32 | .. |
78| ✓ | __ucmpti2 | u128 | u128 | i32 | .. |78| ✓ | __ucmpti2 | u128 | u128 | i32 | .. |
79| | | | | | **Integer Arithmetic** |79| | | | | | **Integer Arithmetic** |
80| ✗ | __ashlsi3 | i32 | i32 | i32 | `a << b` [^unused_rl78] |80| ✓ | __ashlsi3 | i32 | i32 | i32 | `a << b` [^unused_rl78] |
81| ✓ | __ashldi3 | i64 | i32 | i64 | .. |81| ✓ | __ashldi3 | i64 | i32 | i64 | .. |
82| ✓ | __ashlti3 | i128 | i32 | i128 | .. |82| ✓ | __ashlti3 | i128 | i32 | i128 | .. |
83| ✓ | __aeabi_llsl | i32 | i32 | i32 | .. ARM |83| ✓ | __aeabi_llsl | i32 | i32 | i32 | .. ARM |
84| ✗ | __ashrsi3 | i32 | i32 | i32 | `a >> b` arithmetic (sign fill) [^unused_rl78] |84| ✓ | __ashrsi3 | i32 | i32 | i32 | `a >> b` arithmetic (sign fill) [^unused_rl78] |
85| ✓ | __ashrdi3 | i64 | i32 | i64 | .. |85| ✓ | __ashrdi3 | i64 | i32 | i64 | .. |
86| ✓ | __ashrti3 | i128 | i32 | i128 | .. |86| ✓ | __ashrti3 | i128 | i32 | i128 | .. |
87| ✓ | __aeabi_lasr | i64 | i32 | i64 | .. ARM |87| ✓ | __aeabi_lasr | i64 | i32 | i64 | .. ARM |
88| ✗ | __lshrsi3 | i32 | i32 | i32 | `a >> b` logical (zero fill) [^unused_rl78] |88| ✓ | __lshrsi3 | i32 | i32 | i32 | `a >> b` logical (zero fill) [^unused_rl78] |
89| ✓ | __lshrdi3 | i64 | i32 | i64 | .. |89| ✓ | __lshrdi3 | i64 | i32 | i64 | .. |
90| ✓ | __lshrti3 | i128 | i32 | i128 | .. |90| ✓ | __lshrti3 | i128 | i32 | i128 | .. |
91| ✓ | __aeabi_llsr | i64 | i32 | i64 | .. ARM |91| ✓ | __aeabi_llsr | i64 | i32 | i64 | .. ARM |
...@@ -114,7 +114,7 @@ Integer and Float Operations...@@ -114,7 +114,7 @@ Integer and Float Operations
114| ✓ | __udivmodti4 | u128 | u128 | u128 | .. |114| ✓ | __udivmodti4 | u128 | u128 | u128 | .. |
115| ✓ | __divmodsi4 | i32 | i32 | i32 | `a / b, rem.* = a % b` |115| ✓ | __divmodsi4 | i32 | i32 | i32 | `a / b, rem.* = a % b` |
116| ✓ | __divmoddi4 | i64 | i64 | i64 | .. |116| ✓ | __divmoddi4 | i64 | i64 | i64 | .. |
117| ✗ | __divmodti4 | i128 | i128 | i128 | .. [^libgcc_compat] |117| ✓ | __divmodti4 | i128 | i128 | i128 | .. [^libgcc_compat] |
118| | | | | | **Integer Arithmetic with Trapping Overflow**|118| | | | | | **Integer Arithmetic with Trapping Overflow**|
119| ✓ | __absvsi2 | i32 | i32 | i32 | abs(a) |119| ✓ | __absvsi2 | i32 | i32 | i32 | abs(a) |
120| ✓ | __absvdi2 | i64 | i64 | i64 | .. |120| ✓ | __absvdi2 | i64 | i64 | i64 | .. |
...@@ -328,11 +328,11 @@ Integer and Float Operations...@@ -328,11 +328,11 @@ Integer and Float Operations
328| ✓ | __negtf2 | f128 | ∅ | f128 | .. |328| ✓ | __negtf2 | f128 | ∅ | f128 | .. |
329| ✓ | __negxf2 | f80 | ∅ | f80 | .. |329| ✓ | __negxf2 | f80 | ∅ | f80 | .. |
330| | | | | | **Floating point raised to integer power** |330| | | | | | **Floating point raised to integer power** |
331| ✗ | __powihf2 | f16 | f16 | f16 | `a ^ b` |331| ✓ | __powihf2 | f16 | i32 | f16 | `a ^ b` |
332| ✗ | __powisf2 | f32 | f32 | f32 | .. |332| ✓ | __powisf2 | f32 | i32 | f32 | .. |
333| ✗ | __powidf2 | f64 | f64 | f64 | .. |333| ✓ | __powidf2 | f64 | i32 | f64 | .. |
334| ✗ | __powitf2 | f128 | f128 | f128 | .. |334| ✓ | __powitf2 | f128 | i32 | f128 | .. |
335| ✗ | __powixf2 | f80 | f80 | f80 | .. |335| ✓ | __powixf2 | f80 | i32 | f80 | .. |
336| ✓ | __mulhc3 | all4 | f16 | f16 | `(a+ib) * (c+id)` |336| ✓ | __mulhc3 | all4 | f16 | f16 | `(a+ib) * (c+id)` |
337| ✓ | __mulsc3 | all4 | f32 | f32 | .. |337| ✓ | __mulsc3 | all4 | f32 | f32 | .. |
338| ✓ | __muldc3 | all4 | f64 | f64 | .. |338| ✓ | __muldc3 | all4 | f64 | f64 | .. |
...@@ -552,20 +552,156 @@ TODO brief explanation + implementation...@@ -552,20 +552,156 @@ TODO brief explanation + implementation
552| ------ | ------------- | --------- | --------- | --------- | -------------------------- |552| ------ | ------------- | --------- | --------- | --------- | -------------------------- |
553| | | | | | **Fixed-Point Fractional** |553| | | | | | **Fixed-Point Fractional** |
554554
555Further content:555Math functions according to C99 with gnu extension sincos. f16, f80 and f128 functions
556are additionally supported by Zig, but not part of C standard. Alphabetically sorted.
557
558| Done | Name | a | b | Out | Comment |
559| ---- | ------- | --------- | --------- | --------- | -------------------------- |
560| ✓ | __ceilh | f16 | ∅ | f16 |smallest integer value not less than a|
561| ✓ | ceilf | f32 | ∅ | f32 |If a is integer, +-0, +-NaN, or +-infinite, a itself is returned.|
562| ✓ | ceil | f64 | ∅ | f64 | .. |
563| ✓ | __ceilx | f80 | ∅ | f80 | |
564| ✓ | ceilf128 | f128 | ∅ | f128 | .. PPC |
565| ✓ | ceilq | f128 | ∅ | f128 | .. |
566| ✓ | ceill |long double| ∅ |long double| .. |
567| ✓ | __cosh | f16 | ∅ | f16 | `cos(a)=(e^(ia)+e^(-ia))/2`|
568| ✓ | cosf | f32 | ∅ | f32 | .. |
569| ✓ | cos | f64 | ∅ | f64 | .. |
570| ✓ | __cosx | f80 | ∅ | f80 | .. |
571| ✓ | cosf128 | f128 | ∅ | f128 | .. |
572| ✓ | cosq | f128 | ∅ | f128 | .. PPC |
573| ✓ | cosl |long double| ∅ |long double| .. |
574| ✓ | __exph | f16 | ∅ | f16 | `e^a` with e base of natural logarithms|
575| ✓ | expf | f32 | ∅ | f32 | .. |
576| ✓ | exp | f64 | ∅ | f64 | .. |
577| ✓ | __expx | f80 | ∅ | f80 | .. |
578| ✓ | expf128 | f128 | ∅ | f128 | .. |
579| ✓ | expq | f128 | ∅ | f128 | .. PPC |
580| ✓ | expl |long double| ∅ |long double| .. |
581| ✓ | __exp2h | f16 | ∅ | f16 | `2^a` |
582| ✓ | exp2f | f32 | ∅ | f32 | .. |
583| ✓ | exp2 | f64 | ∅ | f64 | .. |
584| ✓ | __exp2x | f80 | ∅ | f80 | .. |
585| ✓ | exp2f128 | f128 | ∅ | f128 | .. |
586| ✓ | exp2q | f128 | ∅ | f128 | .. PPC |
587| ✓ | exp2l |long double| ∅ |long double| .. |
588| ✓ | __fabsh | f16 | ∅ | f16 | absolute value of a |
589| ✓ | fabsf | f32 | ∅ | f32 | .. |
590| ✓ | fabs | f64 | ∅ | f64 | .. |
591| ✓ | __fabsx | f80 | ∅ | f80 | .. |
592| ✓ | fabsf128 | f128 | ∅ | f128 | .. |
593| ✓ | fabsq | f128 | ∅ | f128 | .. PPC |
594| ✓ | fabsl |long double| ∅ |long double| .. |
595| ✓ | __floorh | f16 | ∅ | f16 |largest integer value not greater than a|
596| ✓ | floorf | f32 | ∅ | f32 |If a is integer, +-0, +-NaN, or +-infinite, a itself is returned.|
597| ✓ | floor | f64 | ∅ | f64 | .. |
598| ✓ | __floorx | f80 | ∅ | f80 | .. |
599| ✓ | floorf128 | f128 | ∅ | f128 | .. |
600| ✓ | floorq | f128 | ∅ | f128 | .. PPC |
601| ✓ | floorl |long double| ∅ |long double| .. |
602| ✓ | __fmah | f16 | 2xf16 | f16 | args a,b,c result `(a*b)+c`|
603| ✓ | fmaf | f32 | 2xf32 | f32 |Fused multiply-add for hardware acceleration|
604| ✓ | fma | f64 | 2xf64 | f64 | .. |
605| ✓ | __fmax | f80 | 2xf80 | f80 | .. |
606| ✓ | fmaf128 | f128 | 2xf128 | f128 | .. |
607| ✓ | fmaq | f128 | 2xf128 | f128 | .. PPC |
608| ✓ | fmal |long double|2xlong double|long double| .. |
609| ✓ | __fmaxh | f16 | f16 | f16 | larger value of a,b |
610| ✓ | fmaxf | f32 | f32 | f32 | .. |
611| ✓ | fmax | f64 | f64 | f64 | .. |
612| ✓ | __fmaxx | f80 | f80 | f80 | .. |
613| ✓ | fmaxf128 | f128 | f128 | f128 | .. |
614| ✓ | fmaxq | f128 | f128 | f128 | .. PPC |
615| ✓ | fmaxl |long double|long double|long double| .. |
616| ✓ | __fminh | f16 | f16 | f16 | smaller value of a,b |
617| ✓ | fminf | f32 | f32 | f32 | .. |
618| ✓ | fmin | f64 | f64 | f64 | .. |
619| ✓ | __fminx | f80 | f80 | f80 | .. |
620| ✓ | fminf128 | f128 | f128 | f128 | .. |
621| ✓ | fminq | f128 | f128 | f128 | .. PPC |
622| ✓ | fminl |long double|long double|long double| .. |
623| ✓ | __fmodh | f16 | f16 | f16 |floating-point remainder of division a/b|
624| ✓ | fmodf | f32 | f32 | f32 | .. |
625| ✓ | fmod | f64 | f64 | f64 | .. |
626| ✓ | __fmodx | f80 | f80 | f80 | .. |
627| ✓ | fmodf128 | f128 | f128 | f128 | .. |
628| ✓ | fmodq | f128 | f128 | f128 | .. PPC |
629| ✓ | fmodl |long double|long double|long double| .. |
630| ✓ | __logh | f16 | ∅ | f16 |natural (base-e) logarithm of a|
631| ✓ | logf | f32 | ∅ | f32 | .. |
632| ✓ | log | f64 | ∅ | f64 | .. |
633| ✓ | __logx | f80 | ∅ | f80 | .. |
634| ✓ | logf128 | f128 | ∅ | f128 | .. |
635| ✓ | logq | f128 | ∅ | f128 | .. PPC |
636| ✓ | logl |long double| ∅ |long double| .. |
637| ✓ | __log10h | f16 | ∅ | f16 |common (base-10) logarithm of a|
638| ✓ | log10f | f32 | ∅ | f32 | .. |
639| ✓ | log10 | f64 | ∅ | f64 | .. |
640| ✓ | __log10x | f80 | ∅ | f80 | .. |
641| ✓ | log10f128 | f128 | ∅ | f128 | .. |
642| ✓ | log10q | f128 | ∅ | f128 | .. PPC |
643| ✓ | log10l |long double| ∅ |long double| .. |
644| ✓ | __log2h | f16 | ∅ | f16 | base-2 logarithm of a |
645| ✓ | log2f | f32 | ∅ | f32 | .. |
646| ✓ | log2 | f64 | ∅ | f64 | .. |
647| ✓ | __log2x | f80 | ∅ | f80 | .. |
648| ✓ | log2f128 | f128 | ∅ | f128 | .. |
649| ✓ | log2q | f128 | ∅ | f128 | .. PPC |
650| ✓ | log2l |long double| ∅ |long double| .. |
651| ✓ | __roundh | f16 | ∅ | f16 | a rounded to next int away from zero|
652| ✓ | roundf | f32 | ∅ | f32 | .. |
653| ✓ | round | f64 | ∅ | f64 | .. |
654| ✓ | __roundx | f80 | ∅ | f80 | .. |
655| ✓ | roundf128 | f128 | ∅ | f128 | .. |
656| ✓ | roundq | f128 | ∅ | f128 | .. PPC |
657| ✓ | roundl |long double| ∅ |long double| .. |
658| ✓ | __sinh | f16 | ∅ | f16 | `sin(a)=(e^(ia)-e^(-ia))/2`|
659| ✓ | sinf | f32 | ∅ | f32 | .. |
660| ✓ | sin | f64 | ∅ | f64 | .. |
661| ✓ | __sinx | f80 | ∅ | f80 | .. |
662| ✓ | sinf128 | f128 | ∅ | f128 | .. |
663| ✓ | sinq | f128 | ∅ | f128 | .. PPC |
664| ✓ | sinl |long double| ∅ |long double| .. |
665| ✓ | __sincosh | f16 | 2x *f16 | ∅ |sin and cos of the same angle a|
666| ✓ | sincosf | f32 | 2x *f32 | ∅ |args a,*b,*c, `b.*=sin(x),c.*=cos(x)`|
667| ✓ | sincos | f64 | 2x *f64 | ∅ | .. |
668| ✓ | __sincosx | f80 | 2x *f80 | ∅ | .. |
669| ✓ | sincosf128 | f128 | 2x *f128 | ∅ | .. |
670| ✓ | sincosq | f128 | 2x *f128 | ∅ | .. PPC |
671| ✓ | sincosl |long double| 2x *long double|∅ | .. |
672| ✓ | __sqrth | f16 | ∅ | f16 | square root of a (find `r st. a=r^2`)|
673| ✓ | sqrtf | f32 | ∅ | f32 | .. |
674| ✓ | sqrt | f64 | ∅ | f64 | .. |
675| ✓ | __sqrtx | f80 | ∅ | f80 | .. |
676| ✓ | sqrtf128 | f128 | ∅ | f128 | .. |
677| ✓ | sqrtq | f128 | ∅ | f128 | .. PPC |
678| ✓ | sqrtl |long double| ∅ |long double| .. |
679| ✓ | __tanh | f16 | ∅ | f16 | `tan(x)=sin(x)/cos(x) |
680| ✓ | tanf | f32 | ∅ | f32 | .. |
681| ✓ | tan | f64 | ∅ | f64 | .. |
682| ✓ | __tanx | f80 | ∅ | f80 | .. |
683| ✓ | tanf128 | f128 | ∅ | f128 | .. |
684| ✓ | tanq | f128 | ∅ | f128 | .. PPC |
685| ✓ | tanl |long double| ∅ |long double| .. |
686| ✓ | __trunch | f16 | ∅ | f16 | a rounded to next int towards zero|
687| ✓ | truncf | f32 | ∅ | f32 | .. |
688| ✓ | trunc | f64 | ∅ | f64 | .. |
689| ✓ | __truncx | f80 | ∅ | f80 | .. |
690| ✓ | truncf128 | f128 | ∅ | f128 | .. |
691| ✓ | truncq | f128 | ∅ | f128 | .. PPC |
692| ✓ | truncl |long double| ∅ |long double| .. |
693
694Further content (conditionally) exported with C abi:
556- aarch64 outline atomics695- aarch64 outline atomics
696- arm routines (memory routines + memclr [setting to 0], divmod routines and stubs for unwind_cpp)
557- atomics697- atomics
558- msvc things like _alldiv, _aulldiv, _allrem
559- clear cache
560- tls emulation
561- math routines (cos, sin, tan, ceil, floor, exp, exp2, fabs, log, log10, log2, sincos, sqrt)
562- bcmp698- bcmp
563- ieee float routines (fma, fmax, fmin, fmod, fabs, float rounding, )699- clear cache
564- arm routines (memory routines + memclr [setting to 0], divmod routines and stubs for unwind_cpp)
565- memory routines (memcmp, memcpy, memset, memmove)700- memory routines (memcmp, memcpy, memset, memmove)
701- msvc things like _alldiv, _aulldiv, _allrem
566- objective-c __isPlatformVersionAtLeast check702- objective-c __isPlatformVersionAtLeast check
567- stack probe routines703- stack probe routines
704- tls emulation
568705
569Future work706Future work:
570707- Arbitrary length integer library routines
571Arbitrary length integer library routines
lib/compiler_rt/ashldi3_test.zig deleted-32
...@@ -1,32 +0,0 @@
1const __ashldi3 = @import("shift.zig").__ashldi3;
2const testing = @import("std").testing;
3
4fn test__ashldi3(a: i64, b: i32, expected: u64) !void {
5 const x = __ashldi3(a, b);
6 try testing.expectEqual(@bitCast(i64, expected), x);
7}
8
9test "ashldi3" {
10 try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 0, 0x123456789ABCDEF);
11 try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 1, 0x2468ACF13579BDE);
12 try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 2, 0x48D159E26AF37BC);
13 try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 3, 0x91A2B3C4D5E6F78);
14 try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 4, 0x123456789ABCDEF0);
15
16 try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 28, 0x789ABCDEF0000000);
17 try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 29, 0xF13579BDE0000000);
18 try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 30, 0xE26AF37BC0000000);
19 try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 31, 0xC4D5E6F780000000);
20
21 try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 32, 0x89ABCDEF00000000);
22
23 try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 33, 0x13579BDE00000000);
24 try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 34, 0x26AF37BC00000000);
25 try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 35, 0x4D5E6F7800000000);
26 try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 36, 0x9ABCDEF000000000);
27
28 try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 60, 0xF000000000000000);
29 try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 61, 0xE000000000000000);
30 try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 62, 0xC000000000000000);
31 try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 63, 0x8000000000000000);
32}
lib/compiler_rt/ashlti3_test.zig deleted-46
...@@ -1,46 +0,0 @@
1const __ashlti3 = @import("shift.zig").__ashlti3;
2const testing = @import("std").testing;
3
4fn test__ashlti3(a: i128, b: i32, expected: i128) !void {
5 const x = __ashlti3(a, b);
6 try testing.expectEqual(expected, x);
7}
8
9test "ashlti3" {
10 try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 0, @bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)));
11 try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 1, @bitCast(i128, @intCast(u128, 0xFDB97530ECA8642BFDB97530ECA8642A)));
12 try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 2, @bitCast(i128, @intCast(u128, 0xFB72EA61D950C857FB72EA61D950C854)));
13 try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 3, @bitCast(i128, @intCast(u128, 0xF6E5D4C3B2A190AFF6E5D4C3B2A190A8)));
14 try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 4, @bitCast(i128, @intCast(u128, 0xEDCBA9876543215FEDCBA98765432150)));
15 try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 28, @bitCast(i128, @intCast(u128, 0x876543215FEDCBA98765432150000000)));
16 try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 29, @bitCast(i128, @intCast(u128, 0x0ECA8642BFDB97530ECA8642A0000000)));
17 try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 30, @bitCast(i128, @intCast(u128, 0x1D950C857FB72EA61D950C8540000000)));
18 try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 31, @bitCast(i128, @intCast(u128, 0x3B2A190AFF6E5D4C3B2A190A80000000)));
19 try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 32, @bitCast(i128, @intCast(u128, 0x76543215FEDCBA987654321500000000)));
20 try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 33, @bitCast(i128, @intCast(u128, 0xECA8642BFDB97530ECA8642A00000000)));
21 try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 34, @bitCast(i128, @intCast(u128, 0xD950C857FB72EA61D950C85400000000)));
22 try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 35, @bitCast(i128, @intCast(u128, 0xB2A190AFF6E5D4C3B2A190A800000000)));
23 try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 36, @bitCast(i128, @intCast(u128, 0x6543215FEDCBA9876543215000000000)));
24 try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 60, @bitCast(i128, @intCast(u128, 0x5FEDCBA9876543215000000000000000)));
25 try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 61, @bitCast(i128, @intCast(u128, 0xBFDB97530ECA8642A000000000000000)));
26 try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 62, @bitCast(i128, @intCast(u128, 0x7FB72EA61D950C854000000000000000)));
27 try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 63, @bitCast(i128, @intCast(u128, 0xFF6E5D4C3B2A190A8000000000000000)));
28 try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 64, @bitCast(i128, @intCast(u128, 0xFEDCBA98765432150000000000000000)));
29 try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 65, @bitCast(i128, @intCast(u128, 0xFDB97530ECA8642A0000000000000000)));
30 try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 66, @bitCast(i128, @intCast(u128, 0xFB72EA61D950C8540000000000000000)));
31 try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 67, @bitCast(i128, @intCast(u128, 0xF6E5D4C3B2A190A80000000000000000)));
32 try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 68, @bitCast(i128, @intCast(u128, 0xEDCBA987654321500000000000000000)));
33 try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 92, @bitCast(i128, @intCast(u128, 0x87654321500000000000000000000000)));
34 try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 93, @bitCast(i128, @intCast(u128, 0x0ECA8642A00000000000000000000000)));
35 try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 94, @bitCast(i128, @intCast(u128, 0x1D950C85400000000000000000000000)));
36 try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 95, @bitCast(i128, @intCast(u128, 0x3B2A190A800000000000000000000000)));
37 try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 96, @bitCast(i128, @intCast(u128, 0x76543215000000000000000000000000)));
38 try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 97, @bitCast(i128, @intCast(u128, 0xECA8642A000000000000000000000000)));
39 try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 98, @bitCast(i128, @intCast(u128, 0xD950C854000000000000000000000000)));
40 try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 99, @bitCast(i128, @intCast(u128, 0xB2A190A8000000000000000000000000)));
41 try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 100, @bitCast(i128, @intCast(u128, 0x65432150000000000000000000000000)));
42 try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 124, @bitCast(i128, @intCast(u128, 0x50000000000000000000000000000000)));
43 try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 125, @bitCast(i128, @intCast(u128, 0xA0000000000000000000000000000000)));
44 try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 126, @bitCast(i128, @intCast(u128, 0x40000000000000000000000000000000)));
45 try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 127, @bitCast(i128, @intCast(u128, 0x80000000000000000000000000000000)));
46}
lib/compiler_rt/ashrdi3_test.zig deleted-55
...@@ -1,55 +0,0 @@
1const __ashrdi3 = @import("shift.zig").__ashrdi3;
2const testing = @import("std").testing;
3
4fn test__ashrdi3(a: i64, b: i32, expected: u64) !void {
5 const x = __ashrdi3(a, b);
6 try testing.expectEqual(@bitCast(i64, expected), x);
7}
8
9test "ashrdi3" {
10 try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 0, 0x123456789ABCDEF);
11 try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 1, 0x91A2B3C4D5E6F7);
12 try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 2, 0x48D159E26AF37B);
13 try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 3, 0x2468ACF13579BD);
14 try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 4, 0x123456789ABCDE);
15
16 try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 28, 0x12345678);
17 try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 29, 0x91A2B3C);
18 try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 30, 0x48D159E);
19 try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 31, 0x2468ACF);
20
21 try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 32, 0x1234567);
22
23 try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 33, 0x91A2B3);
24 try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 34, 0x48D159);
25 try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 35, 0x2468AC);
26 try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 36, 0x123456);
27
28 try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 60, 0);
29 try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 61, 0);
30 try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 62, 0);
31 try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 63, 0);
32
33 try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 0, 0xFEDCBA9876543210);
34 try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 1, 0xFF6E5D4C3B2A1908);
35 try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 2, 0xFFB72EA61D950C84);
36 try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 3, 0xFFDB97530ECA8642);
37 try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 4, 0xFFEDCBA987654321);
38
39 try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 28, 0xFFFFFFFFEDCBA987);
40 try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 29, 0xFFFFFFFFF6E5D4C3);
41 try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 30, 0xFFFFFFFFFB72EA61);
42 try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 31, 0xFFFFFFFFFDB97530);
43
44 try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 32, 0xFFFFFFFFFEDCBA98);
45
46 try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 33, 0xFFFFFFFFFF6E5D4C);
47 try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 34, 0xFFFFFFFFFFB72EA6);
48 try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 35, 0xFFFFFFFFFFDB9753);
49 try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 36, 0xFFFFFFFFFFEDCBA9);
50
51 try test__ashrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 60, 0xFFFFFFFFFFFFFFFA);
52 try test__ashrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 61, 0xFFFFFFFFFFFFFFFD);
53 try test__ashrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 62, 0xFFFFFFFFFFFFFFFE);
54 try test__ashrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 63, 0xFFFFFFFFFFFFFFFF);
55}
lib/compiler_rt/ashrti3_test.zig deleted-56
...@@ -1,56 +0,0 @@
1const __ashrti3 = @import("shift.zig").__ashrti3;
2const testing = @import("std").testing;
3
4fn test__ashrti3(a: i128, b: i32, expected: i128) !void {
5 const x = __ashrti3(a, b);
6 try testing.expectEqual(expected, x);
7}
8
9test "ashrti3" {
10 try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 0, @bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)));
11 try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 1, @bitCast(i128, @intCast(u128, 0xFF6E5D4C3B2A190AFF6E5D4C3B2A190A)));
12 try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 2, @bitCast(i128, @intCast(u128, 0xFFB72EA61D950C857FB72EA61D950C85)));
13 try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 3, @bitCast(i128, @intCast(u128, 0xFFDB97530ECA8642BFDB97530ECA8642)));
14 try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 4, @bitCast(i128, @intCast(u128, 0xFFEDCBA9876543215FEDCBA987654321)));
15
16 try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 28, @bitCast(i128, @intCast(u128, 0xFFFFFFFFEDCBA9876543215FEDCBA987)));
17 try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 29, @bitCast(i128, @intCast(u128, 0xFFFFFFFFF6E5D4C3B2A190AFF6E5D4C3)));
18 try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 30, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFB72EA61D950C857FB72EA61)));
19 try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 31, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFDB97530ECA8642BFDB97530)));
20
21 try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 32, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFEDCBA9876543215FEDCBA98)));
22
23 try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 33, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFF6E5D4C3B2A190AFF6E5D4C)));
24 try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 34, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFB72EA61D950C857FB72EA6)));
25 try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 35, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFDB97530ECA8642BFDB9753)));
26 try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 36, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFEDCBA9876543215FEDCBA9)));
27
28 try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 60, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFEDCBA9876543215F)));
29 try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 61, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFF6E5D4C3B2A190AF)));
30 try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 62, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFB72EA61D950C857)));
31 try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 63, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFDB97530ECA8642B)));
32
33 try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 64, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFEDCBA9876543215)));
34
35 try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 65, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFF6E5D4C3B2A190A)));
36 try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 66, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFB72EA61D950C85)));
37 try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 67, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFDB97530ECA8642)));
38 try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 68, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFEDCBA987654321)));
39
40 try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 92, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFEDCBA987)));
41 try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 93, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFF6E5D4C3)));
42 try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 94, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFB72EA61)));
43 try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 95, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFDB97530)));
44
45 try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 96, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFEDCBA98)));
46
47 try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 97, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFF6E5D4C)));
48 try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 98, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFB72EA6)));
49 try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 99, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFDB9753)));
50 try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 100, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFEDCBA9)));
51
52 try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 124, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)));
53 try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 125, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)));
54 try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 126, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)));
55 try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 127, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)));
56}
lib/compiler_rt/int.zig+65-3
...@@ -9,10 +9,12 @@ const arch = builtin.cpu.arch;...@@ -9,10 +9,12 @@ const arch = builtin.cpu.arch;
9const is_test = builtin.is_test;9const is_test = builtin.is_test;
10const common = @import("common.zig");10const common = @import("common.zig");
11const udivmod = @import("udivmod.zig").udivmod;11const udivmod = @import("udivmod.zig").udivmod;
12const __divti3 = @import("divti3.zig").__divti3;
1213
13pub const panic = common.panic;14pub const panic = common.panic;
1415
15comptime {16comptime {
17 @export(__divmodti4, .{ .name = "__divmodti4", .linkage = common.linkage, .visibility = common.visibility });
16 @export(__udivmoddi4, .{ .name = "__udivmoddi4", .linkage = common.linkage, .visibility = common.visibility });18 @export(__udivmoddi4, .{ .name = "__udivmoddi4", .linkage = common.linkage, .visibility = common.visibility });
17 @export(__mulsi3, .{ .name = "__mulsi3", .linkage = common.linkage, .visibility = common.visibility });19 @export(__mulsi3, .{ .name = "__mulsi3", .linkage = common.linkage, .visibility = common.visibility });
18 @export(__divmoddi4, .{ .name = "__divmoddi4", .linkage = common.linkage, .visibility = common.visibility });20 @export(__divmoddi4, .{ .name = "__divmoddi4", .linkage = common.linkage, .visibility = common.visibility });
...@@ -33,12 +35,72 @@ comptime {...@@ -33,12 +35,72 @@ comptime {
33 @export(__udivmodsi4, .{ .name = "__udivmodsi4", .linkage = common.linkage, .visibility = common.visibility });35 @export(__udivmodsi4, .{ .name = "__udivmodsi4", .linkage = common.linkage, .visibility = common.visibility });
34}36}
3537
38pub fn __divmodti4(a: i128, b: i128, rem: *i128) callconv(.C) i128 {
39 const d = __divti3(a, b);
40 rem.* = a -% (d * b);
41 return d;
42}
43
44test "test_divmodti4" {
45 const cases = [_][4]i128{
46 [_]i128{ 0, 1, 0, 0 },
47 [_]i128{ 0, -1, 0, 0 },
48 [_]i128{ 2, 1, 2, 0 },
49 [_]i128{ 2, -1, -2, 0 },
50 [_]i128{ -2, 1, -2, 0 },
51 [_]i128{ -2, -1, 2, 0 },
52 [_]i128{ 7, 5, 1, 2 },
53 [_]i128{ -7, 5, -1, -2 },
54 [_]i128{ 19, 5, 3, 4 },
55 [_]i128{ 19, -5, -3, 4 },
56 [_]i128{ @bitCast(i128, @as(u128, 0x80000000000000000000000000000000)), 8, @bitCast(i128, @as(u128, 0xf0000000000000000000000000000000)), 0 },
57 [_]i128{ @bitCast(i128, @as(u128, 0x80000000000000000000000000000007)), 8, @bitCast(i128, @as(u128, 0xf0000000000000000000000000000001)), -1 },
58 };
59
60 for (cases) |case| {
61 try test_one_divmodti4(case[0], case[1], case[2], case[3]);
62 }
63}
64
65fn test_one_divmodti4(a: i128, b: i128, expected_q: i128, expected_r: i128) !void {
66 var r: i128 = undefined;
67 const q: i128 = __divmodti4(a, b, &r);
68 try testing.expect(q == expected_q and r == expected_r);
69}
70
36pub fn __divmoddi4(a: i64, b: i64, rem: *i64) callconv(.C) i64 {71pub fn __divmoddi4(a: i64, b: i64, rem: *i64) callconv(.C) i64 {
37 const d = __divdi3(a, b);72 const d = __divdi3(a, b);
38 rem.* = a -% (d *% b);73 rem.* = a -% (d * b);
39 return d;74 return d;
40}75}
4176
77test "test_divmoddi4" {
78 const cases = [_][4]i64{
79 [_]i64{ 0, 1, 0, 0 },
80 [_]i64{ 0, -1, 0, 0 },
81 [_]i64{ 2, 1, 2, 0 },
82 [_]i64{ 2, -1, -2, 0 },
83 [_]i64{ -2, 1, -2, 0 },
84 [_]i64{ -2, -1, 2, 0 },
85 [_]i64{ 7, 5, 1, 2 },
86 [_]i64{ -7, 5, -1, -2 },
87 [_]i64{ 19, 5, 3, 4 },
88 [_]i64{ 19, -5, -3, 4 },
89 [_]i64{ @bitCast(i64, @as(u64, 0x8000000000000000)), 8, @bitCast(i64, @as(u64, 0xf000000000000000)), 0 },
90 [_]i64{ @bitCast(i64, @as(u64, 0x8000000000000007)), 8, @bitCast(i64, @as(u64, 0xf000000000000001)), -1 },
91 };
92
93 for (cases) |case| {
94 try test_one_divmoddi4(case[0], case[1], case[2], case[3]);
95 }
96}
97
98fn test_one_divmoddi4(a: i64, b: i64, expected_q: i64, expected_r: i64) !void {
99 var r: i64 = undefined;
100 const q: i64 = __divmoddi4(a, b, &r);
101 try testing.expect(q == expected_q and r == expected_r);
102}
103
42pub fn __udivmoddi4(a: u64, b: u64, maybe_rem: ?*u64) callconv(.C) u64 {104pub fn __udivmoddi4(a: u64, b: u64, maybe_rem: ?*u64) callconv(.C) u64 {
43 return udivmod(u64, a, b, maybe_rem);105 return udivmod(u64, a, b, maybe_rem);
44}106}
...@@ -424,7 +486,7 @@ fn test_one_udivsi3(a: u32, b: u32, expected_q: u32) !void {...@@ -424,7 +486,7 @@ fn test_one_udivsi3(a: u32, b: u32, expected_q: u32) !void {
424}486}
425487
426pub fn __modsi3(n: i32, d: i32) callconv(.C) i32 {488pub fn __modsi3(n: i32, d: i32) callconv(.C) i32 {
427 return n -% __divsi3(n, d) *% d;489 return n -% __divsi3(n, d) * d;
428}490}
429491
430test "test_modsi3" {492test "test_modsi3" {
...@@ -453,7 +515,7 @@ fn test_one_modsi3(a: i32, b: i32, expected_r: i32) !void {...@@ -453,7 +515,7 @@ fn test_one_modsi3(a: i32, b: i32, expected_r: i32) !void {
453}515}
454516
455pub fn __umodsi3(n: u32, d: u32) callconv(.C) u32 {517pub fn __umodsi3(n: u32, d: u32) callconv(.C) u32 {
456 return n -% __udivsi3(n, d) *% d;518 return n -% __udivsi3(n, d) * d;
457}519}
458520
459test "test_umodsi3" {521test "test_umodsi3" {
lib/compiler_rt/lshrdi3_test.zig deleted-55
...@@ -1,55 +0,0 @@
1const __lshrdi3 = @import("shift.zig").__lshrdi3;
2const testing = @import("std").testing;
3
4fn test__lshrdi3(a: i64, b: i32, expected: u64) !void {
5 const x = __lshrdi3(a, b);
6 try testing.expectEqual(@bitCast(i64, expected), x);
7}
8
9test "lshrdi3" {
10 try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 0, 0x123456789ABCDEF);
11 try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 1, 0x91A2B3C4D5E6F7);
12 try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 2, 0x48D159E26AF37B);
13 try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 3, 0x2468ACF13579BD);
14 try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 4, 0x123456789ABCDE);
15
16 try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 28, 0x12345678);
17 try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 29, 0x91A2B3C);
18 try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 30, 0x48D159E);
19 try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 31, 0x2468ACF);
20
21 try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 32, 0x1234567);
22
23 try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 33, 0x91A2B3);
24 try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 34, 0x48D159);
25 try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 35, 0x2468AC);
26 try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 36, 0x123456);
27
28 try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 60, 0);
29 try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 61, 0);
30 try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 62, 0);
31 try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 63, 0);
32
33 try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 0, 0xFEDCBA9876543210);
34 try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 1, 0x7F6E5D4C3B2A1908);
35 try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 2, 0x3FB72EA61D950C84);
36 try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 3, 0x1FDB97530ECA8642);
37 try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 4, 0xFEDCBA987654321);
38
39 try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 28, 0xFEDCBA987);
40 try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 29, 0x7F6E5D4C3);
41 try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 30, 0x3FB72EA61);
42 try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 31, 0x1FDB97530);
43
44 try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 32, 0xFEDCBA98);
45
46 try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 33, 0x7F6E5D4C);
47 try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 34, 0x3FB72EA6);
48 try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 35, 0x1FDB9753);
49 try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 36, 0xFEDCBA9);
50
51 try test__lshrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 60, 0xA);
52 try test__lshrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 61, 0x5);
53 try test__lshrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 62, 0x2);
54 try test__lshrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 63, 0x1);
55}
lib/compiler_rt/lshrti3_test.zig deleted-46
...@@ -1,46 +0,0 @@
1const __lshrti3 = @import("shift.zig").__lshrti3;
2const testing = @import("std").testing;
3
4fn test__lshrti3(a: i128, b: i32, expected: i128) !void {
5 const x = __lshrti3(a, b);
6 try testing.expectEqual(expected, x);
7}
8
9test "lshrti3" {
10 try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 0, @bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)));
11 try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 1, @bitCast(i128, @intCast(u128, 0x7F6E5D4C3B2A190AFF6E5D4C3B2A190A)));
12 try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 2, @bitCast(i128, @intCast(u128, 0x3FB72EA61D950C857FB72EA61D950C85)));
13 try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 3, @bitCast(i128, @intCast(u128, 0x1FDB97530ECA8642BFDB97530ECA8642)));
14 try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 4, @bitCast(i128, @intCast(u128, 0x0FEDCBA9876543215FEDCBA987654321)));
15 try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 28, @bitCast(i128, @intCast(u128, 0x0000000FEDCBA9876543215FEDCBA987)));
16 try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 29, @bitCast(i128, @intCast(u128, 0x00000007F6E5D4C3B2A190AFF6E5D4C3)));
17 try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 30, @bitCast(i128, @intCast(u128, 0x00000003FB72EA61D950C857FB72EA61)));
18 try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 31, @bitCast(i128, @intCast(u128, 0x00000001FDB97530ECA8642BFDB97530)));
19 try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 32, @bitCast(i128, @intCast(u128, 0x00000000FEDCBA9876543215FEDCBA98)));
20 try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 33, @bitCast(i128, @intCast(u128, 0x000000007F6E5D4C3B2A190AFF6E5D4C)));
21 try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 34, @bitCast(i128, @intCast(u128, 0x000000003FB72EA61D950C857FB72EA6)));
22 try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 35, @bitCast(i128, @intCast(u128, 0x000000001FDB97530ECA8642BFDB9753)));
23 try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 36, @bitCast(i128, @intCast(u128, 0x000000000FEDCBA9876543215FEDCBA9)));
24 try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 60, @bitCast(i128, @intCast(u128, 0x000000000000000FEDCBA9876543215F)));
25 try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 61, @bitCast(i128, @intCast(u128, 0x0000000000000007F6E5D4C3B2A190AF)));
26 try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 62, @bitCast(i128, @intCast(u128, 0x0000000000000003FB72EA61D950C857)));
27 try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 63, @bitCast(i128, @intCast(u128, 0x0000000000000001FDB97530ECA8642B)));
28 try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 64, @bitCast(i128, @intCast(u128, 0x0000000000000000FEDCBA9876543215)));
29 try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 65, @bitCast(i128, @intCast(u128, 0x00000000000000007F6E5D4C3B2A190A)));
30 try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 66, @bitCast(i128, @intCast(u128, 0x00000000000000003FB72EA61D950C85)));
31 try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 67, @bitCast(i128, @intCast(u128, 0x00000000000000001FDB97530ECA8642)));
32 try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 68, @bitCast(i128, @intCast(u128, 0x00000000000000000FEDCBA987654321)));
33 try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 92, @bitCast(i128, @intCast(u128, 0x00000000000000000000000FEDCBA987)));
34 try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 93, @bitCast(i128, @intCast(u128, 0x000000000000000000000007F6E5D4C3)));
35 try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 94, @bitCast(i128, @intCast(u128, 0x000000000000000000000003FB72EA61)));
36 try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 95, @bitCast(i128, @intCast(u128, 0x000000000000000000000001FDB97530)));
37 try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 96, @bitCast(i128, @intCast(u128, 0x000000000000000000000000FEDCBA98)));
38 try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 97, @bitCast(i128, @intCast(u128, 0x0000000000000000000000007F6E5D4C)));
39 try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 98, @bitCast(i128, @intCast(u128, 0x0000000000000000000000003FB72EA6)));
40 try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 99, @bitCast(i128, @intCast(u128, 0x0000000000000000000000001FDB9753)));
41 try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 100, @bitCast(i128, @intCast(u128, 0x0000000000000000000000000FEDCBA9)));
42 try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 124, @bitCast(i128, @intCast(u128, 0x0000000000000000000000000000000F)));
43 try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 125, @bitCast(i128, @intCast(u128, 0x00000000000000000000000000000007)));
44 try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 126, @bitCast(i128, @intCast(u128, 0x00000000000000000000000000000003)));
45 try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 127, @bitCast(i128, @intCast(u128, 0x00000000000000000000000000000001)));
46}
lib/compiler_rt/powiXf2.zig created+58
...@@ -0,0 +1,58 @@
1//! a raised to integer power of b
2//! ported from https://github.com/llvm-mirror/compiler-rt/blob/release_80/lib/builtins/powisf2.c
3//! Multiplication order (left-to-right or right-to-left) does not matter for
4//! error propagation and this method is optimized for performance, not accuracy.
5
6const builtin = @import("builtin");
7const common = @import("common.zig");
8const std = @import("std");
9
10pub const panic = common.panic;
11
12comptime {
13 @export(__powihf2, .{ .name = "__powihf2", .linkage = common.linkage, .visibility = common.visibility });
14 @export(__powisf2, .{ .name = "__powisf2", .linkage = common.linkage, .visibility = common.visibility });
15 @export(__powidf2, .{ .name = "__powidf2", .linkage = common.linkage, .visibility = common.visibility });
16 @export(__powitf2, .{ .name = "__powitf2", .linkage = common.linkage, .visibility = common.visibility });
17 @export(__powixf2, .{ .name = "__powixf2", .linkage = common.linkage, .visibility = common.visibility });
18}
19
20inline fn powiXf2(comptime FT: type, a: FT, b: i32) FT {
21 var x_a: FT = a;
22 var x_b: i32 = b;
23 const is_recip: bool = b < 0;
24 var r: FT = 1.0;
25 while (true) {
26 if (@bitCast(u32, x_b) & @as(u32, 1) != 0) {
27 r *= x_a;
28 }
29 x_b = @divTrunc(x_b, @as(i32, 2));
30 if (x_b == 0) break;
31 x_a *= x_a; // Multiplication of x_a propagates the error
32 }
33 return if (is_recip) 1 / r else r;
34}
35
36pub fn __powihf2(a: f16, b: i32) callconv(.C) f16 {
37 return powiXf2(f16, a, b);
38}
39
40pub fn __powisf2(a: f32, b: i32) callconv(.C) f32 {
41 return powiXf2(f32, a, b);
42}
43
44pub fn __powidf2(a: f64, b: i32) callconv(.C) f64 {
45 return powiXf2(f64, a, b);
46}
47
48pub fn __powitf2(a: f128, b: i32) callconv(.C) f128 {
49 return powiXf2(f128, a, b);
50}
51
52pub fn __powixf2(a: f80, b: i32) callconv(.C) f80 {
53 return powiXf2(f80, a, b);
54}
55
56test {
57 _ = @import("powiXf2_test.zig");
58}
lib/compiler_rt/powiXf2_test.zig created+556
...@@ -0,0 +1,556 @@
1// ported from https://github.com/llvm-mirror/compiler-rt/blob/release_80/test/builtins/Unit/
2// powisf2_test.c, powidf2_test.c, powitf2_test.c, powixf2_test.c
3// powihf2 adapted from powisf2 tests
4
5const powiXf2 = @import("powiXf2.zig");
6const testing = @import("std").testing;
7const math = @import("std").math;
8
9fn test__powihf2(a: f16, b: i32, expected: f16) !void {
10 var result = powiXf2.__powihf2(a, b);
11 try testing.expectEqual(expected, result);
12}
13
14fn test__powisf2(a: f32, b: i32, expected: f32) !void {
15 var result = powiXf2.__powisf2(a, b);
16 try testing.expectEqual(expected, result);
17}
18
19fn test__powidf2(a: f64, b: i32, expected: f64) !void {
20 var result = powiXf2.__powidf2(a, b);
21 try testing.expectEqual(expected, result);
22}
23
24fn test__powitf2(a: f128, b: i32, expected: f128) !void {
25 var result = powiXf2.__powitf2(a, b);
26 try testing.expectEqual(expected, result);
27}
28
29fn test__powixf2(a: f80, b: i32, expected: f80) !void {
30 var result = powiXf2.__powixf2(a, b);
31 try testing.expectEqual(expected, result);
32}
33
34test "powihf2" {
35 try test__powisf2(0, 0, 1);
36 try test__powihf2(1, 0, 1);
37 try test__powihf2(1.5, 0, 1);
38 try test__powihf2(2, 0, 1);
39 try test__powihf2(math.inf_f16, 0, 1);
40
41 try test__powihf2(-0.0, 0, 1);
42 try test__powihf2(-1, 0, 1);
43 try test__powihf2(-1.5, 0, 1);
44 try test__powihf2(-2, 0, 1);
45 try test__powihf2(-math.inf_f16, 0, 1);
46
47 try test__powihf2(0, 1, 0);
48 try test__powihf2(0, 2, 0);
49 try test__powihf2(0, 3, 0);
50 try test__powihf2(0, 4, 0);
51 try test__powihf2(0, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 0);
52 try test__powihf2(0, @bitCast(i32, @as(u32, 0x7FFFFFFF)), 0);
53
54 try test__powihf2(-0.0, 1, -0.0);
55 try test__powihf2(-0.0, 2, 0);
56 try test__powihf2(-0.0, 3, -0.0);
57 try test__powihf2(-0.0, 4, 0);
58 try test__powihf2(-0.0, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 0);
59 try test__powihf2(-0.0, @bitCast(i32, @as(u32, 0x7FFFFFFF)), -0.0);
60
61 try test__powihf2(1, 1, 1);
62 try test__powihf2(1, 2, 1);
63 try test__powihf2(1, 3, 1);
64 try test__powihf2(1, 4, 1);
65 try test__powihf2(1, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 1);
66 try test__powihf2(1, @bitCast(i32, @as(u32, 0x7FFFFFFF)), 1);
67
68 try test__powihf2(math.inf_f16, 1, math.inf_f16);
69 try test__powihf2(math.inf_f16, 2, math.inf_f16);
70 try test__powihf2(math.inf_f16, 3, math.inf_f16);
71 try test__powihf2(math.inf_f16, 4, math.inf_f16);
72 try test__powihf2(math.inf_f16, @bitCast(i32, @as(u32, 0x7FFFFFFE)), math.inf_f16);
73 try test__powihf2(math.inf_f16, @bitCast(i32, @as(u32, 0x7FFFFFFF)), math.inf_f16);
74
75 try test__powihf2(-math.inf_f16, 1, -math.inf_f16);
76 try test__powihf2(-math.inf_f16, 2, math.inf_f16);
77 try test__powihf2(-math.inf_f16, 3, -math.inf_f16);
78 try test__powihf2(-math.inf_f16, 4, math.inf_f16);
79 try test__powihf2(-math.inf_f16, @bitCast(i32, @as(u32, 0x7FFFFFFE)), math.inf_f16);
80 try test__powihf2(-math.inf_f16, @bitCast(i32, @as(u32, 0x7FFFFFFF)), -math.inf_f16);
81 //
82 try test__powihf2(0, -1, math.inf_f16);
83 try test__powihf2(0, -2, math.inf_f16);
84 try test__powihf2(0, -3, math.inf_f16);
85 try test__powihf2(0, -4, math.inf_f16);
86 try test__powihf2(0, @bitCast(i32, @as(u32, 0x80000002)), math.inf_f16); // 0 ^ anything = +inf
87 try test__powihf2(0, @bitCast(i32, @as(u32, 0x80000001)), math.inf_f16);
88 try test__powihf2(0, @bitCast(i32, @as(u32, 0x80000000)), math.inf_f16);
89
90 try test__powihf2(-0.0, -1, -math.inf_f16);
91 try test__powihf2(-0.0, -2, math.inf_f16);
92 try test__powihf2(-0.0, -3, -math.inf_f16);
93 try test__powihf2(-0.0, -4, math.inf_f16);
94 try test__powihf2(-0.0, @bitCast(i32, @as(u32, 0x80000002)), math.inf_f16); // -0 ^ anything even = +inf
95 try test__powihf2(-0.0, @bitCast(i32, @as(u32, 0x80000001)), -math.inf_f16); // -0 ^ anything odd = -inf
96 try test__powihf2(-0.0, @bitCast(i32, @as(u32, 0x80000000)), math.inf_f16);
97
98 try test__powihf2(1, -1, 1);
99 try test__powihf2(1, -2, 1);
100 try test__powihf2(1, -3, 1);
101 try test__powihf2(1, -4, 1);
102 try test__powihf2(1, @bitCast(i32, @as(u32, 0x80000002)), 1); // 1.0 ^ anything = 1
103 try test__powihf2(1, @bitCast(i32, @as(u32, 0x80000001)), 1);
104 try test__powihf2(1, @bitCast(i32, @as(u32, 0x80000000)), 1);
105
106 try test__powihf2(math.inf_f16, -1, 0);
107 try test__powihf2(math.inf_f16, -2, 0);
108 try test__powihf2(math.inf_f16, -3, 0);
109 try test__powihf2(math.inf_f16, -4, 0);
110 try test__powihf2(math.inf_f16, @bitCast(i32, @as(u32, 0x80000002)), 0);
111 try test__powihf2(math.inf_f16, @bitCast(i32, @as(u32, 0x80000001)), 0);
112 try test__powihf2(math.inf_f16, @bitCast(i32, @as(u32, 0x80000000)), 0);
113 //
114 try test__powihf2(-math.inf_f16, -1, -0.0);
115 try test__powihf2(-math.inf_f16, -2, 0);
116 try test__powihf2(-math.inf_f16, -3, -0.0);
117 try test__powihf2(-math.inf_f16, -4, 0);
118 try test__powihf2(-math.inf_f16, @bitCast(i32, @as(u32, 0x80000002)), 0);
119 try test__powihf2(-math.inf_f16, @bitCast(i32, @as(u32, 0x80000001)), -0.0);
120 try test__powihf2(-math.inf_f16, @bitCast(i32, @as(u32, 0x80000000)), 0);
121
122 try test__powihf2(2, 10, 1024.0);
123 try test__powihf2(-2, 10, 1024.0);
124 try test__powihf2(2, -10, 1.0 / 1024.0);
125 try test__powihf2(-2, -10, 1.0 / 1024.0);
126
127 try test__powihf2(2, 14, 16384.0);
128 try test__powihf2(-2, 14, 16384.0);
129 try test__powihf2(2, 15, 32768.0);
130 try test__powihf2(-2, 15, -32768.0);
131 try test__powihf2(2, 16, math.inf_f16);
132 try test__powihf2(-2, 16, math.inf_f16);
133
134 try test__powihf2(2, -13, 1.0 / 8192.0);
135 try test__powihf2(-2, -13, -1.0 / 8192.0);
136 try test__powihf2(2, -15, 1.0 / 32768.0);
137 try test__powihf2(-2, -15, -1.0 / 32768.0);
138 try test__powihf2(2, -16, 0.0); // expected = 0.0 = 1/(-2**16)
139 try test__powihf2(-2, -16, 0.0); // expected = 0.0 = 1/(2**16)
140}
141
142test "powisf2" {
143 try test__powisf2(0, 0, 1);
144 try test__powisf2(1, 0, 1);
145 try test__powisf2(1.5, 0, 1);
146 try test__powisf2(2, 0, 1);
147 try test__powisf2(math.inf_f32, 0, 1);
148
149 try test__powisf2(-0.0, 0, 1);
150 try test__powisf2(-1, 0, 1);
151 try test__powisf2(-1.5, 0, 1);
152 try test__powisf2(-2, 0, 1);
153 try test__powisf2(-math.inf_f32, 0, 1);
154
155 try test__powisf2(0, 1, 0);
156 try test__powisf2(0, 2, 0);
157 try test__powisf2(0, 3, 0);
158 try test__powisf2(0, 4, 0);
159 try test__powisf2(0, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 0);
160 try test__powisf2(0, @bitCast(i32, @as(u32, 0x7FFFFFFF)), 0);
161
162 try test__powisf2(-0.0, 1, -0.0);
163 try test__powisf2(-0.0, 2, 0);
164 try test__powisf2(-0.0, 3, -0.0);
165 try test__powisf2(-0.0, 4, 0);
166 try test__powisf2(-0.0, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 0);
167 try test__powisf2(-0.0, @bitCast(i32, @as(u32, 0x7FFFFFFF)), -0.0);
168
169 try test__powisf2(1, 1, 1);
170 try test__powisf2(1, 2, 1);
171 try test__powisf2(1, 3, 1);
172 try test__powisf2(1, 4, 1);
173 try test__powisf2(1, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 1);
174 try test__powisf2(1, @bitCast(i32, @as(u32, 0x7FFFFFFF)), 1);
175
176 try test__powisf2(math.inf_f32, 1, math.inf_f32);
177 try test__powisf2(math.inf_f32, 2, math.inf_f32);
178 try test__powisf2(math.inf_f32, 3, math.inf_f32);
179 try test__powisf2(math.inf_f32, 4, math.inf_f32);
180 try test__powisf2(math.inf_f32, @bitCast(i32, @as(u32, 0x7FFFFFFE)), math.inf_f32);
181 try test__powisf2(math.inf_f32, @bitCast(i32, @as(u32, 0x7FFFFFFF)), math.inf_f32);
182
183 try test__powisf2(-math.inf_f32, 1, -math.inf_f32);
184 try test__powisf2(-math.inf_f32, 2, math.inf_f32);
185 try test__powisf2(-math.inf_f32, 3, -math.inf_f32);
186 try test__powisf2(-math.inf_f32, 4, math.inf_f32);
187 try test__powisf2(-math.inf_f32, @bitCast(i32, @as(u32, 0x7FFFFFFE)), math.inf_f32);
188 try test__powisf2(-math.inf_f32, @bitCast(i32, @as(u32, 0x7FFFFFFF)), -math.inf_f32);
189
190 try test__powisf2(0, -1, math.inf_f32);
191 try test__powisf2(0, -2, math.inf_f32);
192 try test__powisf2(0, -3, math.inf_f32);
193 try test__powisf2(0, -4, math.inf_f32);
194 try test__powisf2(0, @bitCast(i32, @as(u32, 0x80000002)), math.inf_f32);
195 try test__powisf2(0, @bitCast(i32, @as(u32, 0x80000001)), math.inf_f32);
196 try test__powisf2(0, @bitCast(i32, @as(u32, 0x80000000)), math.inf_f32);
197
198 try test__powisf2(-0.0, -1, -math.inf_f32);
199 try test__powisf2(-0.0, -2, math.inf_f32);
200 try test__powisf2(-0.0, -3, -math.inf_f32);
201 try test__powisf2(-0.0, -4, math.inf_f32);
202 try test__powisf2(-0.0, @bitCast(i32, @as(u32, 0x80000002)), math.inf_f32);
203 try test__powisf2(-0.0, @bitCast(i32, @as(u32, 0x80000001)), -math.inf_f32);
204 try test__powisf2(-0.0, @bitCast(i32, @as(u32, 0x80000000)), math.inf_f32);
205
206 try test__powisf2(1, -1, 1);
207 try test__powisf2(1, -2, 1);
208 try test__powisf2(1, -3, 1);
209 try test__powisf2(1, -4, 1);
210 try test__powisf2(1, @bitCast(i32, @as(u32, 0x80000002)), 1);
211 try test__powisf2(1, @bitCast(i32, @as(u32, 0x80000001)), 1);
212 try test__powisf2(1, @bitCast(i32, @as(u32, 0x80000000)), 1);
213
214 try test__powisf2(math.inf_f32, -1, 0);
215 try test__powisf2(math.inf_f32, -2, 0);
216 try test__powisf2(math.inf_f32, -3, 0);
217 try test__powisf2(math.inf_f32, -4, 0);
218 try test__powisf2(math.inf_f32, @bitCast(i32, @as(u32, 0x80000002)), 0);
219 try test__powisf2(math.inf_f32, @bitCast(i32, @as(u32, 0x80000001)), 0);
220 try test__powisf2(math.inf_f32, @bitCast(i32, @as(u32, 0x80000000)), 0);
221
222 try test__powisf2(-math.inf_f32, -1, -0.0);
223 try test__powisf2(-math.inf_f32, -2, 0);
224 try test__powisf2(-math.inf_f32, -3, -0.0);
225 try test__powisf2(-math.inf_f32, -4, 0);
226 try test__powisf2(-math.inf_f32, @bitCast(i32, @as(u32, 0x80000002)), 0);
227 try test__powisf2(-math.inf_f32, @bitCast(i32, @as(u32, 0x80000001)), -0.0);
228 try test__powisf2(-math.inf_f32, @bitCast(i32, @as(u32, 0x80000000)), 0);
229
230 try test__powisf2(2.0, 10, 1024.0);
231 try test__powisf2(-2, 10, 1024.0);
232 try test__powisf2(2, -10, 1.0 / 1024.0);
233 try test__powisf2(-2, -10, 1.0 / 1024.0);
234 //
235 try test__powisf2(2, 19, 524288.0);
236 try test__powisf2(-2, 19, -524288.0);
237 try test__powisf2(2, -19, 1.0 / 524288.0);
238 try test__powisf2(-2, -19, -1.0 / 524288.0);
239
240 try test__powisf2(2, 31, 2147483648.0);
241 try test__powisf2(-2, 31, -2147483648.0);
242 try test__powisf2(2, -31, 1.0 / 2147483648.0);
243 try test__powisf2(-2, -31, -1.0 / 2147483648.0);
244}
245
246test "powidf2" {
247 try test__powidf2(0, 0, 1);
248 try test__powidf2(1, 0, 1);
249 try test__powidf2(1.5, 0, 1);
250 try test__powidf2(2, 0, 1);
251 try test__powidf2(math.inf_f64, 0, 1);
252
253 try test__powidf2(-0.0, 0, 1);
254 try test__powidf2(-1, 0, 1);
255 try test__powidf2(-1.5, 0, 1);
256 try test__powidf2(-2, 0, 1);
257 try test__powidf2(-math.inf_f64, 0, 1);
258
259 try test__powidf2(0, 1, 0);
260 try test__powidf2(0, 2, 0);
261 try test__powidf2(0, 3, 0);
262 try test__powidf2(0, 4, 0);
263 try test__powidf2(0, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 0);
264 try test__powidf2(0, @bitCast(i32, @as(u32, 0x7FFFFFFF)), 0);
265
266 try test__powidf2(-0.0, 1, -0.0);
267 try test__powidf2(-0.0, 2, 0);
268 try test__powidf2(-0.0, 3, -0.0);
269 try test__powidf2(-0.0, 4, 0);
270 try test__powidf2(-0.0, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 0);
271 try test__powidf2(-0.0, @bitCast(i32, @as(u32, 0x7FFFFFFF)), -0.0);
272
273 try test__powidf2(1, 1, 1);
274 try test__powidf2(1, 2, 1);
275 try test__powidf2(1, 3, 1);
276 try test__powidf2(1, 4, 1);
277 try test__powidf2(1, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 1);
278 try test__powidf2(1, @bitCast(i32, @as(u32, 0x7FFFFFFF)), 1);
279
280 try test__powidf2(math.inf_f64, 1, math.inf_f64);
281 try test__powidf2(math.inf_f64, 2, math.inf_f64);
282 try test__powidf2(math.inf_f64, 3, math.inf_f64);
283 try test__powidf2(math.inf_f64, 4, math.inf_f64);
284 try test__powidf2(math.inf_f64, @bitCast(i32, @as(u32, 0x7FFFFFFE)), math.inf_f64);
285 try test__powidf2(math.inf_f64, @bitCast(i32, @as(u32, 0x7FFFFFFF)), math.inf_f64);
286
287 try test__powidf2(-math.inf_f64, 1, -math.inf_f64);
288 try test__powidf2(-math.inf_f64, 2, math.inf_f64);
289 try test__powidf2(-math.inf_f64, 3, -math.inf_f64);
290 try test__powidf2(-math.inf_f64, 4, math.inf_f64);
291 try test__powidf2(-math.inf_f64, @bitCast(i32, @as(u32, 0x7FFFFFFE)), math.inf_f64);
292 try test__powidf2(-math.inf_f64, @bitCast(i32, @as(u32, 0x7FFFFFFF)), -math.inf_f64);
293
294 try test__powidf2(0, -1, math.inf_f64);
295 try test__powidf2(0, -2, math.inf_f64);
296 try test__powidf2(0, -3, math.inf_f64);
297 try test__powidf2(0, -4, math.inf_f64);
298 try test__powidf2(0, @bitCast(i32, @as(u32, 0x80000002)), math.inf_f64);
299 try test__powidf2(0, @bitCast(i32, @as(u32, 0x80000001)), math.inf_f64);
300 try test__powidf2(0, @bitCast(i32, @as(u32, 0x80000000)), math.inf_f64);
301
302 try test__powidf2(-0.0, -1, -math.inf_f64);
303 try test__powidf2(-0.0, -2, math.inf_f64);
304 try test__powidf2(-0.0, -3, -math.inf_f64);
305 try test__powidf2(-0.0, -4, math.inf_f64);
306 try test__powidf2(-0.0, @bitCast(i32, @as(u32, 0x80000002)), math.inf_f64);
307 try test__powidf2(-0.0, @bitCast(i32, @as(u32, 0x80000001)), -math.inf_f64);
308 try test__powidf2(-0.0, @bitCast(i32, @as(u32, 0x80000000)), math.inf_f64);
309
310 try test__powidf2(1, -1, 1);
311 try test__powidf2(1, -2, 1);
312 try test__powidf2(1, -3, 1);
313 try test__powidf2(1, -4, 1);
314 try test__powidf2(1, @bitCast(i32, @as(u32, 0x80000002)), 1);
315 try test__powidf2(1, @bitCast(i32, @as(u32, 0x80000001)), 1);
316 try test__powidf2(1, @bitCast(i32, @as(u32, 0x80000000)), 1);
317
318 try test__powidf2(math.inf_f64, -1, 0);
319 try test__powidf2(math.inf_f64, -2, 0);
320 try test__powidf2(math.inf_f64, -3, 0);
321 try test__powidf2(math.inf_f64, -4, 0);
322 try test__powidf2(math.inf_f64, @bitCast(i32, @as(u32, 0x80000002)), 0);
323 try test__powidf2(math.inf_f64, @bitCast(i32, @as(u32, 0x80000001)), 0);
324 try test__powidf2(math.inf_f64, @bitCast(i32, @as(u32, 0x80000000)), 0);
325
326 try test__powidf2(-math.inf_f64, -1, -0.0);
327 try test__powidf2(-math.inf_f64, -2, 0);
328 try test__powidf2(-math.inf_f64, -3, -0.0);
329 try test__powidf2(-math.inf_f64, -4, 0);
330 try test__powidf2(-math.inf_f64, @bitCast(i32, @as(u32, 0x80000002)), 0);
331 try test__powidf2(-math.inf_f64, @bitCast(i32, @as(u32, 0x80000001)), -0.0);
332 try test__powidf2(-math.inf_f64, @bitCast(i32, @as(u32, 0x80000000)), 0);
333
334 try test__powidf2(2, 10, 1024.0);
335 try test__powidf2(-2, 10, 1024.0);
336 try test__powidf2(2, -10, 1.0 / 1024.0);
337 try test__powidf2(-2, -10, 1.0 / 1024.0);
338
339 try test__powidf2(2, 19, 524288.0);
340 try test__powidf2(-2, 19, -524288.0);
341 try test__powidf2(2, -19, 1.0 / 524288.0);
342 try test__powidf2(-2, -19, -1.0 / 524288.0);
343
344 try test__powidf2(2, 31, 2147483648.0);
345 try test__powidf2(-2, 31, -2147483648.0);
346 try test__powidf2(2, -31, 1.0 / 2147483648.0);
347 try test__powidf2(-2, -31, -1.0 / 2147483648.0);
348}
349
350test "powitf2" {
351 try test__powitf2(0, 0, 1);
352 try test__powitf2(1, 0, 1);
353 try test__powitf2(1.5, 0, 1);
354 try test__powitf2(2, 0, 1);
355 try test__powitf2(math.inf_f128, 0, 1);
356
357 try test__powitf2(-0.0, 0, 1);
358 try test__powitf2(-1, 0, 1);
359 try test__powitf2(-1.5, 0, 1);
360 try test__powitf2(-2, 0, 1);
361 try test__powitf2(-math.inf_f128, 0, 1);
362
363 try test__powitf2(0, 1, 0);
364 try test__powitf2(0, 2, 0);
365 try test__powitf2(0, 3, 0);
366 try test__powitf2(0, 4, 0);
367 try test__powitf2(0, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 0);
368 try test__powitf2(0, 0x7FFFFFFF, 0);
369
370 try test__powitf2(-0.0, 1, -0.0);
371 try test__powitf2(-0.0, 2, 0);
372 try test__powitf2(-0.0, 3, -0.0);
373 try test__powitf2(-0.0, 4, 0);
374 try test__powitf2(-0.0, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 0);
375 try test__powitf2(-0.0, @bitCast(i32, @as(u32, 0x7FFFFFFF)), -0.0);
376
377 try test__powitf2(1, 1, 1);
378 try test__powitf2(1, 2, 1);
379 try test__powitf2(1, 3, 1);
380 try test__powitf2(1, 4, 1);
381 try test__powitf2(1, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 1);
382 try test__powitf2(1, @bitCast(i32, @as(u32, 0x7FFFFFFF)), 1);
383
384 try test__powitf2(math.inf_f128, 1, math.inf_f128);
385 try test__powitf2(math.inf_f128, 2, math.inf_f128);
386 try test__powitf2(math.inf_f128, 3, math.inf_f128);
387 try test__powitf2(math.inf_f128, 4, math.inf_f128);
388 try test__powitf2(math.inf_f128, @bitCast(i32, @as(u32, 0x7FFFFFFE)), math.inf_f128);
389 try test__powitf2(math.inf_f128, @bitCast(i32, @as(u32, 0x7FFFFFFF)), math.inf_f128);
390
391 try test__powitf2(-math.inf_f128, 1, -math.inf_f128);
392 try test__powitf2(-math.inf_f128, 2, math.inf_f128);
393 try test__powitf2(-math.inf_f128, 3, -math.inf_f128);
394 try test__powitf2(-math.inf_f128, 4, math.inf_f128);
395 try test__powitf2(-math.inf_f128, @bitCast(i32, @as(u32, 0x7FFFFFFE)), math.inf_f128);
396 try test__powitf2(-math.inf_f128, @bitCast(i32, @as(u32, 0x7FFFFFFF)), -math.inf_f128);
397
398 try test__powitf2(0, -1, math.inf_f128);
399 try test__powitf2(0, -2, math.inf_f128);
400 try test__powitf2(0, -3, math.inf_f128);
401 try test__powitf2(0, -4, math.inf_f128);
402 try test__powitf2(0, @bitCast(i32, @as(u32, 0x80000002)), math.inf_f128);
403 try test__powitf2(0, @bitCast(i32, @as(u32, 0x80000001)), math.inf_f128);
404 try test__powitf2(0, @bitCast(i32, @as(u32, 0x80000000)), math.inf_f128);
405
406 try test__powitf2(-0.0, -1, -math.inf_f128);
407 try test__powitf2(-0.0, -2, math.inf_f128);
408 try test__powitf2(-0.0, -3, -math.inf_f128);
409 try test__powitf2(-0.0, -4, math.inf_f128);
410 try test__powitf2(-0.0, @bitCast(i32, @as(u32, 0x80000002)), math.inf_f128);
411 try test__powitf2(-0.0, @bitCast(i32, @as(u32, 0x80000001)), -math.inf_f128);
412 try test__powitf2(-0.0, @bitCast(i32, @as(u32, 0x80000000)), math.inf_f128);
413
414 try test__powitf2(1, -1, 1);
415 try test__powitf2(1, -2, 1);
416 try test__powitf2(1, -3, 1);
417 try test__powitf2(1, -4, 1);
418 try test__powitf2(1, @bitCast(i32, @as(u32, 0x80000002)), 1);
419 try test__powitf2(1, @bitCast(i32, @as(u32, 0x80000001)), 1);
420 try test__powitf2(1, @bitCast(i32, @as(u32, 0x80000000)), 1);
421
422 try test__powitf2(math.inf_f128, -1, 0);
423 try test__powitf2(math.inf_f128, -2, 0);
424 try test__powitf2(math.inf_f128, -3, 0);
425 try test__powitf2(math.inf_f128, -4, 0);
426 try test__powitf2(math.inf_f128, @bitCast(i32, @as(u32, 0x80000002)), 0);
427 try test__powitf2(math.inf_f128, @bitCast(i32, @as(u32, 0x80000001)), 0);
428 try test__powitf2(math.inf_f128, @bitCast(i32, @as(u32, 0x80000000)), 0);
429
430 try test__powitf2(-math.inf_f128, -1, -0.0);
431 try test__powitf2(-math.inf_f128, -2, 0);
432 try test__powitf2(-math.inf_f128, -3, -0.0);
433 try test__powitf2(-math.inf_f128, -4, 0);
434 try test__powitf2(-math.inf_f128, @bitCast(i32, @as(u32, 0x80000002)), 0);
435 try test__powitf2(-math.inf_f128, @bitCast(i32, @as(u32, 0x80000001)), -0.0);
436 try test__powitf2(-math.inf_f128, @bitCast(i32, @as(u32, 0x80000000)), 0);
437
438 try test__powitf2(2, 10, 1024.0);
439 try test__powitf2(-2, 10, 1024.0);
440 try test__powitf2(2, -10, 1.0 / 1024.0);
441 try test__powitf2(-2, -10, 1.0 / 1024.0);
442
443 try test__powitf2(2, 19, 524288.0);
444 try test__powitf2(-2, 19, -524288.0);
445 try test__powitf2(2, -19, 1.0 / 524288.0);
446 try test__powitf2(-2, -19, -1.0 / 524288.0);
447
448 try test__powitf2(2, 31, 2147483648.0);
449 try test__powitf2(-2, 31, -2147483648.0);
450 try test__powitf2(2, -31, 1.0 / 2147483648.0);
451 try test__powitf2(-2, -31, -1.0 / 2147483648.0);
452}
453
454test "powixf2" {
455 try test__powixf2(0, 0, 1);
456 try test__powixf2(1, 0, 1);
457 try test__powixf2(1.5, 0, 1);
458 try test__powixf2(2, 0, 1);
459 try test__powixf2(math.inf_f80, 0, 1);
460
461 try test__powixf2(-0.0, 0, 1);
462 try test__powixf2(-1, 0, 1);
463 try test__powixf2(-1.5, 0, 1);
464 try test__powixf2(-2, 0, 1);
465 try test__powixf2(-math.inf_f80, 0, 1);
466
467 try test__powixf2(0, 1, 0);
468 try test__powixf2(0, 2, 0);
469 try test__powixf2(0, 3, 0);
470 try test__powixf2(0, 4, 0);
471 try test__powixf2(0, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 0);
472 try test__powixf2(0, @bitCast(i32, @as(u32, 0x7FFFFFFF)), 0);
473
474 try test__powixf2(-0.0, 1, -0.0);
475 try test__powixf2(-0.0, 2, 0);
476 try test__powixf2(-0.0, 3, -0.0);
477 try test__powixf2(-0.0, 4, 0);
478 try test__powixf2(-0.0, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 0);
479 try test__powixf2(-0.0, @bitCast(i32, @as(u32, 0x7FFFFFFF)), -0.0);
480
481 try test__powixf2(1, 1, 1);
482 try test__powixf2(1, 2, 1);
483 try test__powixf2(1, 3, 1);
484 try test__powixf2(1, 4, 1);
485 try test__powixf2(1, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 1);
486 try test__powixf2(1, @bitCast(i32, @as(u32, 0x7FFFFFFF)), 1);
487
488 try test__powixf2(math.inf_f80, 1, math.inf_f80);
489 try test__powixf2(math.inf_f80, 2, math.inf_f80);
490 try test__powixf2(math.inf_f80, 3, math.inf_f80);
491 try test__powixf2(math.inf_f80, 4, math.inf_f80);
492 try test__powixf2(math.inf_f80, @bitCast(i32, @as(u32, 0x7FFFFFFE)), math.inf_f80);
493 try test__powixf2(math.inf_f80, @bitCast(i32, @as(u32, 0x7FFFFFFF)), math.inf_f80);
494
495 try test__powixf2(-math.inf_f80, 1, -math.inf_f80);
496 try test__powixf2(-math.inf_f80, 2, math.inf_f80);
497 try test__powixf2(-math.inf_f80, 3, -math.inf_f80);
498 try test__powixf2(-math.inf_f80, 4, math.inf_f80);
499 try test__powixf2(-math.inf_f80, @bitCast(i32, @as(u32, 0x7FFFFFFE)), math.inf_f80);
500 try test__powixf2(-math.inf_f80, @bitCast(i32, @as(u32, 0x7FFFFFFF)), -math.inf_f80);
501
502 try test__powixf2(0, -1, math.inf_f80);
503 try test__powixf2(0, -2, math.inf_f80);
504 try test__powixf2(0, -3, math.inf_f80);
505 try test__powixf2(0, -4, math.inf_f80);
506 try test__powixf2(0, @bitCast(i32, @as(u32, 0x80000002)), math.inf_f80);
507 try test__powixf2(0, @bitCast(i32, @as(u32, 0x80000001)), math.inf_f80);
508 try test__powixf2(0, @bitCast(i32, @as(u32, 0x80000000)), math.inf_f80);
509
510 try test__powixf2(-0.0, -1, -math.inf_f80);
511 try test__powixf2(-0.0, -2, math.inf_f80);
512 try test__powixf2(-0.0, -3, -math.inf_f80);
513 try test__powixf2(-0.0, -4, math.inf_f80);
514 try test__powixf2(-0.0, @bitCast(i32, @as(u32, 0x80000002)), math.inf_f80);
515 try test__powixf2(-0.0, @bitCast(i32, @as(u32, 0x80000001)), -math.inf_f80);
516 try test__powixf2(-0.0, @bitCast(i32, @as(u32, 0x80000000)), math.inf_f80);
517
518 try test__powixf2(1, -1, 1);
519 try test__powixf2(1, -2, 1);
520 try test__powixf2(1, -3, 1);
521 try test__powixf2(1, -4, 1);
522 try test__powixf2(1, @bitCast(i32, @as(u32, 0x80000002)), 1);
523 try test__powixf2(1, @bitCast(i32, @as(u32, 0x80000001)), 1);
524 try test__powixf2(1, @bitCast(i32, @as(u32, 0x80000000)), 1);
525
526 try test__powixf2(math.inf_f80, -1, 0);
527 try test__powixf2(math.inf_f80, -2, 0);
528 try test__powixf2(math.inf_f80, -3, 0);
529 try test__powixf2(math.inf_f80, -4, 0);
530 try test__powixf2(math.inf_f80, @bitCast(i32, @as(u32, 0x80000002)), 0);
531 try test__powixf2(math.inf_f80, @bitCast(i32, @as(u32, 0x80000001)), 0);
532 try test__powixf2(math.inf_f80, @bitCast(i32, @as(u32, 0x80000000)), 0);
533
534 try test__powixf2(-math.inf_f80, -1, -0.0);
535 try test__powixf2(-math.inf_f80, -2, 0);
536 try test__powixf2(-math.inf_f80, -3, -0.0);
537 try test__powixf2(-math.inf_f80, -4, 0);
538 try test__powixf2(-math.inf_f80, @bitCast(i32, @as(u32, 0x80000002)), 0);
539 try test__powixf2(-math.inf_f80, @bitCast(i32, @as(u32, 0x80000001)), -0.0);
540 try test__powixf2(-math.inf_f80, @bitCast(i32, @as(u32, 0x80000000)), 0);
541
542 try test__powixf2(2, 10, 1024.0);
543 try test__powixf2(-2, 10, 1024.0);
544 try test__powixf2(2, -10, 1.0 / 1024.0);
545 try test__powixf2(-2, -10, 1.0 / 1024.0);
546
547 try test__powixf2(2, 19, 524288.0);
548 try test__powixf2(-2, 19, -524288.0);
549 try test__powixf2(2, -19, 1.0 / 524288.0);
550 try test__powixf2(-2, -19, -1.0 / 524288.0);
551
552 try test__powixf2(2, 31, 2147483648.0);
553 try test__powixf2(-2, 31, -2147483648.0);
554 try test__powixf2(2, -31, 1.0 / 2147483648.0);
555 try test__powixf2(-2, -31, -1.0 / 2147483648.0);
556}
lib/compiler_rt/shift.zig+21-11
...@@ -7,6 +7,11 @@ const common = @import("common.zig");...@@ -7,6 +7,11 @@ const common = @import("common.zig");
7pub const panic = common.panic;7pub const panic = common.panic;
88
9comptime {9comptime {
10 // symbol compatibility with libgcc
11 @export(__ashlsi3, .{ .name = "__ashlsi3", .linkage = common.linkage, .visibility = common.visibility });
12 @export(__ashrsi3, .{ .name = "__ashrsi3", .linkage = common.linkage, .visibility = common.visibility });
13 @export(__lshrsi3, .{ .name = "__lshrsi3", .linkage = common.linkage, .visibility = common.visibility });
14
10 @export(__ashlti3, .{ .name = "__ashlti3", .linkage = common.linkage, .visibility = common.visibility });15 @export(__ashlti3, .{ .name = "__ashlti3", .linkage = common.linkage, .visibility = common.visibility });
11 @export(__ashrti3, .{ .name = "__ashrti3", .linkage = common.linkage, .visibility = common.visibility });16 @export(__ashrti3, .{ .name = "__ashrti3", .linkage = common.linkage, .visibility = common.visibility });
12 @export(__lshrti3, .{ .name = "__lshrti3", .linkage = common.linkage, .visibility = common.visibility });17 @export(__lshrti3, .{ .name = "__lshrti3", .linkage = common.linkage, .visibility = common.visibility });
...@@ -37,7 +42,7 @@ fn Dwords(comptime T: type, comptime signed_half: bool) type {...@@ -37,7 +42,7 @@ fn Dwords(comptime T: type, comptime signed_half: bool) type {
37 };42 };
38}43}
3944
40// Arithmetic shift left45// Arithmetic shift left: shift in 0 from right to left
41// Precondition: 0 <= b < bits_in_dword46// Precondition: 0 <= b < bits_in_dword
42inline fn ashlXi3(comptime T: type, a: T, b: i32) T {47inline fn ashlXi3(comptime T: type, a: T, b: i32) T {
43 const dwords = Dwords(T, false);48 const dwords = Dwords(T, false);
...@@ -60,7 +65,7 @@ inline fn ashlXi3(comptime T: type, a: T, b: i32) T {...@@ -60,7 +65,7 @@ inline fn ashlXi3(comptime T: type, a: T, b: i32) T {
60 return output.all;65 return output.all;
61}66}
6267
63// Arithmetic shift right68// Arithmetic shift right: shift in 1 from left to right
64// Precondition: 0 <= b < T.bit_count69// Precondition: 0 <= b < T.bit_count
65inline fn ashrXi3(comptime T: type, a: T, b: i32) T {70inline fn ashrXi3(comptime T: type, a: T, b: i32) T {
66 const dwords = Dwords(T, true);71 const dwords = Dwords(T, true);
...@@ -87,7 +92,7 @@ inline fn ashrXi3(comptime T: type, a: T, b: i32) T {...@@ -87,7 +92,7 @@ inline fn ashrXi3(comptime T: type, a: T, b: i32) T {
87 return output.all;92 return output.all;
88}93}
8994
90// Logical shift right95// Logical shift right: shift in 0 from left to right
91// Precondition: 0 <= b < T.bit_count96// Precondition: 0 <= b < T.bit_count
92inline fn lshrXi3(comptime T: type, a: T, b: i32) T {97inline fn lshrXi3(comptime T: type, a: T, b: i32) T {
93 const dwords = Dwords(T, false);98 const dwords = Dwords(T, false);
...@@ -110,6 +115,18 @@ inline fn lshrXi3(comptime T: type, a: T, b: i32) T {...@@ -110,6 +115,18 @@ inline fn lshrXi3(comptime T: type, a: T, b: i32) T {
110 return output.all;115 return output.all;
111}116}
112117
118pub fn __ashlsi3(a: i32, b: i32) callconv(.C) i32 {
119 return ashlXi3(i32, a, b);
120}
121
122pub fn __ashrsi3(a: i32, b: i32) callconv(.C) i32 {
123 return ashrXi3(i32, a, b);
124}
125
126pub fn __lshrsi3(a: i32, b: i32) callconv(.C) i32 {
127 return lshrXi3(i32, a, b);
128}
129
113pub fn __ashldi3(a: i64, b: i32) callconv(.C) i64 {130pub fn __ashldi3(a: i64, b: i32) callconv(.C) i64 {
114 return ashlXi3(i64, a, b);131 return ashlXi3(i64, a, b);
115}132}
...@@ -144,12 +161,5 @@ pub fn __lshrti3(a: i128, b: i32) callconv(.C) i128 {...@@ -144,12 +161,5 @@ pub fn __lshrti3(a: i128, b: i32) callconv(.C) i128 {
144}161}
145162
146test {163test {
147 _ = @import("ashrdi3_test.zig");164 _ = @import("shift_test.zig");
148 _ = @import("ashrti3_test.zig");
149
150 _ = @import("ashldi3_test.zig");
151 _ = @import("ashlti3_test.zig");
152
153 _ = @import("lshrdi3_test.zig");
154 _ = @import("lshrti3_test.zig");
155}165}
lib/compiler_rt/shift_test.zig created+363
...@@ -0,0 +1,363 @@
1const testing = @import("std").testing;
2const shift = @import("shift.zig");
3
4// arithmetic shift left
5const __ashlsi3 = shift.__ashlsi3;
6const __ashldi3 = shift.__ashldi3;
7const __ashlti3 = shift.__ashlti3;
8
9// arithmetic shift right
10const __ashrsi3 = shift.__ashrsi3;
11const __ashrdi3 = shift.__ashrdi3;
12const __ashrti3 = shift.__ashrti3;
13
14// logic shift right
15const __lshrsi3 = shift.__lshrsi3;
16const __lshrdi3 = shift.__lshrdi3;
17const __lshrti3 = shift.__lshrti3;
18
19fn test__ashlsi3(a: i32, b: i32, expected: u32) !void {
20 const x = __ashlsi3(a, b);
21 try testing.expectEqual(expected, @bitCast(u32, x));
22}
23fn test__ashldi3(a: i64, b: i32, expected: u64) !void {
24 const x = __ashldi3(a, b);
25 try testing.expectEqual(expected, @bitCast(u64, x));
26}
27fn test__ashlti3(a: i128, b: i32, expected: u128) !void {
28 const x = __ashlti3(a, b);
29 try testing.expectEqual(expected, @bitCast(u128, x));
30}
31
32test "ashlsi3" {
33 try test__ashlsi3(@bitCast(i32, @as(u32, 0x12ABCDEF)), 0, 0x12ABCDEF);
34 try test__ashlsi3(@bitCast(i32, @as(u32, 0x12ABCDEF)), 1, 0x25579BDE);
35 try test__ashlsi3(@bitCast(i32, @as(u32, 0x12ABCDEF)), 2, 0x4AAF37BC);
36 try test__ashlsi3(@bitCast(i32, @as(u32, 0x12ABCDEF)), 3, 0x955E6F78);
37 try test__ashlsi3(@bitCast(i32, @as(u32, 0x12ABCDEF)), 4, 0x2ABCDEF0);
38
39 try test__ashlsi3(@bitCast(i32, @as(u32, 0x12ABCDEF)), 28, 0xF0000000);
40 try test__ashlsi3(@bitCast(i32, @as(u32, 0x12ABCDEF)), 29, 0xE0000000);
41 try test__ashlsi3(@bitCast(i32, @as(u32, 0x12ABCDEF)), 30, 0xC0000000);
42 try test__ashlsi3(@bitCast(i32, @as(u32, 0x12ABCDEF)), 31, 0x80000000);
43}
44
45test "ashldi3" {
46 try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 0, 0x123456789ABCDEF);
47 try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 1, 0x2468ACF13579BDE);
48 try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 2, 0x48D159E26AF37BC);
49 try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 3, 0x91A2B3C4D5E6F78);
50 try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 4, 0x123456789ABCDEF0);
51
52 try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 28, 0x789ABCDEF0000000);
53 try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 29, 0xF13579BDE0000000);
54 try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 30, 0xE26AF37BC0000000);
55 try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 31, 0xC4D5E6F780000000);
56
57 try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 32, 0x89ABCDEF00000000);
58
59 try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 33, 0x13579BDE00000000);
60 try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 34, 0x26AF37BC00000000);
61 try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 35, 0x4D5E6F7800000000);
62 try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 36, 0x9ABCDEF000000000);
63
64 try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 60, 0xF000000000000000);
65 try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 61, 0xE000000000000000);
66 try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 62, 0xC000000000000000);
67 try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 63, 0x8000000000000000);
68}
69
70test "ashlti3" {
71 try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 0, 0xFEDCBA9876543215FEDCBA9876543215);
72 try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 1, 0xFDB97530ECA8642BFDB97530ECA8642A);
73 try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 2, 0xFB72EA61D950C857FB72EA61D950C854);
74 try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 3, 0xF6E5D4C3B2A190AFF6E5D4C3B2A190A8);
75 try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 4, 0xEDCBA9876543215FEDCBA98765432150);
76 try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 28, 0x876543215FEDCBA98765432150000000);
77 try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 29, 0x0ECA8642BFDB97530ECA8642A0000000);
78 try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 30, 0x1D950C857FB72EA61D950C8540000000);
79 try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 31, 0x3B2A190AFF6E5D4C3B2A190A80000000);
80 try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 32, 0x76543215FEDCBA987654321500000000);
81 try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 33, 0xECA8642BFDB97530ECA8642A00000000);
82 try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 34, 0xD950C857FB72EA61D950C85400000000);
83 try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 35, 0xB2A190AFF6E5D4C3B2A190A800000000);
84 try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 36, 0x6543215FEDCBA9876543215000000000);
85 try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 60, 0x5FEDCBA9876543215000000000000000);
86 try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 61, 0xBFDB97530ECA8642A000000000000000);
87 try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 62, 0x7FB72EA61D950C854000000000000000);
88 try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 63, 0xFF6E5D4C3B2A190A8000000000000000);
89 try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 64, 0xFEDCBA98765432150000000000000000);
90 try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 65, 0xFDB97530ECA8642A0000000000000000);
91 try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 66, 0xFB72EA61D950C8540000000000000000);
92 try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 67, 0xF6E5D4C3B2A190A80000000000000000);
93 try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 68, 0xEDCBA987654321500000000000000000);
94 try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 92, 0x87654321500000000000000000000000);
95 try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 93, 0x0ECA8642A00000000000000000000000);
96 try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 94, 0x1D950C85400000000000000000000000);
97 try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 95, 0x3B2A190A800000000000000000000000);
98 try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 96, 0x76543215000000000000000000000000);
99 try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 97, 0xECA8642A000000000000000000000000);
100 try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 98, 0xD950C854000000000000000000000000);
101 try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 99, 0xB2A190A8000000000000000000000000);
102 try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 100, 0x65432150000000000000000000000000);
103 try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 124, 0x50000000000000000000000000000000);
104 try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 125, 0xA0000000000000000000000000000000);
105 try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 126, 0x40000000000000000000000000000000);
106 try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 127, 0x80000000000000000000000000000000);
107}
108
109fn test__ashrsi3(a: i32, b: i32, expected: u32) !void {
110 const x = __ashrsi3(a, b);
111 try testing.expectEqual(expected, @bitCast(u32, x));
112}
113fn test__ashrdi3(a: i64, b: i32, expected: u64) !void {
114 const x = __ashrdi3(a, b);
115 try testing.expectEqual(expected, @bitCast(u64, x));
116}
117fn test__ashrti3(a: i128, b: i32, expected: u128) !void {
118 const x = __ashrti3(a, b);
119 try testing.expectEqual(expected, @bitCast(u128, x));
120}
121
122test "ashrsi3" {
123 try test__ashrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 0, 0xFEDBCA98);
124 try test__ashrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 1, 0xFF6DE54C);
125 try test__ashrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 2, 0xFFB6F2A6);
126 try test__ashrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 3, 0xFFDB7953);
127 try test__ashrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 4, 0xFFEDBCA9);
128
129 try test__ashrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 28, 0xFFFFFFFF);
130 try test__ashrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 31, 0xFFFFFFFF);
131
132 try test__ashrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 0, 0x8CEF8CEF);
133 try test__ashrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 1, 0xC677C677);
134 try test__ashrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 2, 0xE33BE33B);
135 try test__ashrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 3, 0xF19DF19D);
136 try test__ashrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 4, 0xF8CEF8CE);
137
138 try test__ashrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 28, 0xFFFFFFF8);
139 try test__ashrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 29, 0xFFFFFFFC);
140 try test__ashrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 30, 0xFFFFFFFE);
141 try test__ashrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 31, 0xFFFFFFFF);
142}
143
144test "ashrdi3" {
145 try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 0, 0x123456789ABCDEF);
146 try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 1, 0x91A2B3C4D5E6F7);
147 try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 2, 0x48D159E26AF37B);
148 try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 3, 0x2468ACF13579BD);
149 try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 4, 0x123456789ABCDE);
150
151 try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 28, 0x12345678);
152 try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 29, 0x91A2B3C);
153 try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 30, 0x48D159E);
154 try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 31, 0x2468ACF);
155
156 try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 32, 0x1234567);
157
158 try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 33, 0x91A2B3);
159 try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 34, 0x48D159);
160 try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 35, 0x2468AC);
161 try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 36, 0x123456);
162
163 try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 60, 0);
164 try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 61, 0);
165 try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 62, 0);
166 try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 63, 0);
167
168 try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 0, 0xFEDCBA9876543210);
169 try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 1, 0xFF6E5D4C3B2A1908);
170 try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 2, 0xFFB72EA61D950C84);
171 try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 3, 0xFFDB97530ECA8642);
172 try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 4, 0xFFEDCBA987654321);
173
174 try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 28, 0xFFFFFFFFEDCBA987);
175 try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 29, 0xFFFFFFFFF6E5D4C3);
176 try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 30, 0xFFFFFFFFFB72EA61);
177 try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 31, 0xFFFFFFFFFDB97530);
178
179 try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 32, 0xFFFFFFFFFEDCBA98);
180
181 try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 33, 0xFFFFFFFFFF6E5D4C);
182 try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 34, 0xFFFFFFFFFFB72EA6);
183 try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 35, 0xFFFFFFFFFFDB9753);
184 try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 36, 0xFFFFFFFFFFEDCBA9);
185
186 try test__ashrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 60, 0xFFFFFFFFFFFFFFFA);
187 try test__ashrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 61, 0xFFFFFFFFFFFFFFFD);
188 try test__ashrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 62, 0xFFFFFFFFFFFFFFFE);
189 try test__ashrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 63, 0xFFFFFFFFFFFFFFFF);
190}
191
192test "ashrti3" {
193 try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 0, 0xFEDCBA9876543215FEDCBA9876543215);
194 try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 1, 0xFF6E5D4C3B2A190AFF6E5D4C3B2A190A);
195 try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 2, 0xFFB72EA61D950C857FB72EA61D950C85);
196 try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 3, 0xFFDB97530ECA8642BFDB97530ECA8642);
197 try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 4, 0xFFEDCBA9876543215FEDCBA987654321);
198
199 try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 28, 0xFFFFFFFFEDCBA9876543215FEDCBA987);
200 try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 29, 0xFFFFFFFFF6E5D4C3B2A190AFF6E5D4C3);
201 try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 30, 0xFFFFFFFFFB72EA61D950C857FB72EA61);
202 try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 31, 0xFFFFFFFFFDB97530ECA8642BFDB97530);
203
204 try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 32, 0xFFFFFFFFFEDCBA9876543215FEDCBA98);
205
206 try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 33, 0xFFFFFFFFFF6E5D4C3B2A190AFF6E5D4C);
207 try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 34, 0xFFFFFFFFFFB72EA61D950C857FB72EA6);
208 try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 35, 0xFFFFFFFFFFDB97530ECA8642BFDB9753);
209 try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 36, 0xFFFFFFFFFFEDCBA9876543215FEDCBA9);
210
211 try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 60, 0xFFFFFFFFFFFFFFFFEDCBA9876543215F);
212 try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 61, 0xFFFFFFFFFFFFFFFFF6E5D4C3B2A190AF);
213 try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 62, 0xFFFFFFFFFFFFFFFFFB72EA61D950C857);
214 try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 63, 0xFFFFFFFFFFFFFFFFFDB97530ECA8642B);
215
216 try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 64, 0xFFFFFFFFFFFFFFFFFEDCBA9876543215);
217
218 try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 65, 0xFFFFFFFFFFFFFFFFFF6E5D4C3B2A190A);
219 try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 66, 0xFFFFFFFFFFFFFFFFFFB72EA61D950C85);
220 try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 67, 0xFFFFFFFFFFFFFFFFFFDB97530ECA8642);
221 try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 68, 0xFFFFFFFFFFFFFFFFFFEDCBA987654321);
222
223 try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 92, 0xFFFFFFFFFFFFFFFFFFFFFFFFEDCBA987);
224 try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 93, 0xFFFFFFFFFFFFFFFFFFFFFFFFF6E5D4C3);
225 try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 94, 0xFFFFFFFFFFFFFFFFFFFFFFFFFB72EA61);
226 try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 95, 0xFFFFFFFFFFFFFFFFFFFFFFFFFDB97530);
227
228 try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 96, 0xFFFFFFFFFFFFFFFFFFFFFFFFFEDCBA98);
229
230 try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 97, 0xFFFFFFFFFFFFFFFFFFFFFFFFFF6E5D4C);
231 try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 98, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFB72EA6);
232 try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 99, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFDB9753);
233 try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 100, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFEDCBA9);
234
235 try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 124, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
236 try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 125, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
237 try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 126, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
238 try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 127, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
239}
240
241fn test__lshrsi3(a: i32, b: i32, expected: u32) !void {
242 const x = __lshrsi3(a, b);
243 try testing.expectEqual(expected, @bitCast(u32, x));
244}
245fn test__lshrdi3(a: i64, b: i32, expected: u64) !void {
246 const x = __lshrdi3(a, b);
247 try testing.expectEqual(expected, @bitCast(u64, x));
248}
249fn test__lshrti3(a: i128, b: i32, expected: u128) !void {
250 const x = __lshrti3(a, b);
251 try testing.expectEqual(expected, @bitCast(u128, x));
252}
253
254test "lshrsi3" {
255 try test__lshrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 0, 0xFEDBCA98);
256 try test__lshrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 1, 0x7F6DE54C);
257 try test__lshrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 2, 0x3FB6F2A6);
258 try test__lshrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 3, 0x1FDB7953);
259 try test__lshrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 4, 0xFEDBCA9);
260
261 try test__lshrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 28, 0xF);
262 try test__lshrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 29, 0x7);
263 try test__lshrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 30, 0x3);
264 try test__lshrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 31, 0x1);
265
266 try test__lshrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 0, 0x8CEF8CEF);
267 try test__lshrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 1, 0x4677C677);
268 try test__lshrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 2, 0x233BE33B);
269 try test__lshrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 3, 0x119DF19D);
270 try test__lshrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 4, 0x8CEF8CE);
271
272 try test__lshrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 28, 0x8);
273 try test__lshrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 29, 0x4);
274 try test__lshrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 30, 0x2);
275 try test__lshrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 31, 0x1);
276}
277
278test "lshrdi3" {
279 try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 0, 0x123456789ABCDEF);
280 try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 1, 0x91A2B3C4D5E6F7);
281 try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 2, 0x48D159E26AF37B);
282 try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 3, 0x2468ACF13579BD);
283 try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 4, 0x123456789ABCDE);
284
285 try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 28, 0x12345678);
286 try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 29, 0x91A2B3C);
287 try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 30, 0x48D159E);
288 try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 31, 0x2468ACF);
289
290 try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 32, 0x1234567);
291
292 try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 33, 0x91A2B3);
293 try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 34, 0x48D159);
294 try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 35, 0x2468AC);
295 try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 36, 0x123456);
296
297 try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 60, 0);
298 try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 61, 0);
299 try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 62, 0);
300 try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 63, 0);
301
302 try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 0, 0xFEDCBA9876543210);
303 try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 1, 0x7F6E5D4C3B2A1908);
304 try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 2, 0x3FB72EA61D950C84);
305 try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 3, 0x1FDB97530ECA8642);
306 try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 4, 0xFEDCBA987654321);
307
308 try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 28, 0xFEDCBA987);
309 try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 29, 0x7F6E5D4C3);
310 try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 30, 0x3FB72EA61);
311 try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 31, 0x1FDB97530);
312
313 try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 32, 0xFEDCBA98);
314
315 try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 33, 0x7F6E5D4C);
316 try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 34, 0x3FB72EA6);
317 try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 35, 0x1FDB9753);
318 try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 36, 0xFEDCBA9);
319
320 try test__lshrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 60, 0xA);
321 try test__lshrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 61, 0x5);
322 try test__lshrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 62, 0x2);
323 try test__lshrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 63, 0x1);
324}
325
326test "lshrti3" {
327 try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 0, 0xFEDCBA9876543215FEDCBA987654321F);
328 try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 1, 0x7F6E5D4C3B2A190AFF6E5D4C3B2A190F);
329 try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 2, 0x3FB72EA61D950C857FB72EA61D950C87);
330 try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 3, 0x1FDB97530ECA8642BFDB97530ECA8643);
331 try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 4, 0xFEDCBA9876543215FEDCBA987654321);
332 try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 28, 0xFEDCBA9876543215FEDCBA987);
333 try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 29, 0x7F6E5D4C3B2A190AFF6E5D4C3);
334 try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 30, 0x3FB72EA61D950C857FB72EA61);
335 try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 31, 0x1FDB97530ECA8642BFDB97530);
336 try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 32, 0xFEDCBA9876543215FEDCBA98);
337 try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 33, 0x7F6E5D4C3B2A190AFF6E5D4C);
338 try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 34, 0x3FB72EA61D950C857FB72EA6);
339 try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 35, 0x1FDB97530ECA8642BFDB9753);
340 try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 36, 0xFEDCBA9876543215FEDCBA9);
341 try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 60, 0xFEDCBA9876543215F);
342 try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 61, 0x7F6E5D4C3B2A190AF);
343 try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 62, 0x3FB72EA61D950C857);
344 try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 63, 0x1FDB97530ECA8642B);
345 try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 64, 0xFEDCBA9876543215);
346 try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 65, 0x7F6E5D4C3B2A190A);
347 try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 66, 0x3FB72EA61D950C85);
348 try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 67, 0x1FDB97530ECA8642);
349 try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 68, 0xFEDCBA987654321);
350 try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 92, 0xFEDCBA987);
351 try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 93, 0x7F6E5D4C3);
352 try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 94, 0x3FB72EA61);
353 try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 95, 0x1FDB97530);
354 try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 96, 0xFEDCBA98);
355 try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 97, 0x7F6E5D4C);
356 try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 98, 0x3FB72EA6);
357 try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 99, 0x1FDB9753);
358 try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 100, 0xFEDCBA9);
359 try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 124, 0xF);
360 try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 125, 0x7);
361 try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 126, 0x3);
362 try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 127, 0x1);
363}
lib/docs/main.js+6-2
...@@ -1354,8 +1354,12 @@ const NAV_MODES = {...@@ -1354,8 +1354,12 @@ const NAV_MODES = {
1354 payloadHtml += "ptrCast";1354 payloadHtml += "ptrCast";
1355 break;1355 break;
1356 }1356 }
1357 case "qual_cast": {1357 case "const_cast": {
1358 payloadHtml += "qualCast";1358 payloadHtml += "constCast";
1359 break;
1360 }
1361 case "volatile_cast": {
1362 payloadHtml += "volatileCast";
1359 break;1363 break;
1360 }1364 }
1361 case "truncate": {1365 case "truncate": {
lib/std/Build.zig+65-53
...@@ -19,6 +19,8 @@ const NativeTargetInfo = std.zig.system.NativeTargetInfo;...@@ -19,6 +19,8 @@ const NativeTargetInfo = std.zig.system.NativeTargetInfo;
19const Sha256 = std.crypto.hash.sha2.Sha256;19const Sha256 = std.crypto.hash.sha2.Sha256;
20const Build = @This();20const Build = @This();
2121
22pub const Cache = @import("Build/Cache.zig");
23
22/// deprecated: use `CompileStep`.24/// deprecated: use `CompileStep`.
23pub const LibExeObjStep = CompileStep;25pub const LibExeObjStep = CompileStep;
24/// deprecated: use `Build`.26/// deprecated: use `Build`.
...@@ -77,11 +79,12 @@ search_prefixes: ArrayList([]const u8),...@@ -77,11 +79,12 @@ search_prefixes: ArrayList([]const u8),
77libc_file: ?[]const u8 = null,79libc_file: ?[]const u8 = null,
78installed_files: ArrayList(InstalledFile),80installed_files: ArrayList(InstalledFile),
79/// Path to the directory containing build.zig.81/// Path to the directory containing build.zig.
80build_root: []const u8,82build_root: Cache.Directory,
81cache_root: []const u8,83cache_root: Cache.Directory,
82global_cache_root: []const u8,84global_cache_root: Cache.Directory,
83/// zig lib dir85cache: *Cache,
84override_lib_dir: ?[]const u8,86/// If non-null, overrides the default zig lib dir.
87zig_lib_dir: ?[]const u8,
85vcpkg_root: VcpkgRoot = .unattempted,88vcpkg_root: VcpkgRoot = .unattempted,
86pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null,89pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null,
87args: ?[][]const u8 = null,90args: ?[][]const u8 = null,
...@@ -185,10 +188,11 @@ pub const DirList = struct {...@@ -185,10 +188,11 @@ pub const DirList = struct {
185pub fn create(188pub fn create(
186 allocator: Allocator,189 allocator: Allocator,
187 zig_exe: []const u8,190 zig_exe: []const u8,
188 build_root: []const u8,191 build_root: Cache.Directory,
189 cache_root: []const u8,192 cache_root: Cache.Directory,
190 global_cache_root: []const u8,193 global_cache_root: Cache.Directory,
191 host: NativeTargetInfo,194 host: NativeTargetInfo,
195 cache: *Cache,
192) !*Build {196) !*Build {
193 const env_map = try allocator.create(EnvMap);197 const env_map = try allocator.create(EnvMap);
194 env_map.* = try process.getEnvMap(allocator);198 env_map.* = try process.getEnvMap(allocator);
...@@ -197,8 +201,9 @@ pub fn create(...@@ -197,8 +201,9 @@ pub fn create(
197 self.* = Build{201 self.* = Build{
198 .zig_exe = zig_exe,202 .zig_exe = zig_exe,
199 .build_root = build_root,203 .build_root = build_root,
200 .cache_root = try fs.path.relative(allocator, build_root, cache_root),204 .cache_root = cache_root,
201 .global_cache_root = global_cache_root,205 .global_cache_root = global_cache_root,
206 .cache = cache,
202 .verbose = false,207 .verbose = false,
203 .verbose_link = false,208 .verbose_link = false,
204 .verbose_cc = false,209 .verbose_cc = false,
...@@ -230,7 +235,7 @@ pub fn create(...@@ -230,7 +235,7 @@ pub fn create(
230 .step = Step.init(.top_level, "uninstall", allocator, makeUninstall),235 .step = Step.init(.top_level, "uninstall", allocator, makeUninstall),
231 .description = "Remove build artifacts from prefix path",236 .description = "Remove build artifacts from prefix path",
232 },237 },
233 .override_lib_dir = null,238 .zig_lib_dir = null,
234 .install_path = undefined,239 .install_path = undefined,
235 .args = null,240 .args = null,
236 .host = host,241 .host = host,
...@@ -245,7 +250,7 @@ pub fn create(...@@ -245,7 +250,7 @@ pub fn create(
245fn createChild(250fn createChild(
246 parent: *Build,251 parent: *Build,
247 dep_name: []const u8,252 dep_name: []const u8,
248 build_root: []const u8,253 build_root: Cache.Directory,
249 args: anytype,254 args: anytype,
250) !*Build {255) !*Build {
251 const child = try createChildOnly(parent, dep_name, build_root);256 const child = try createChildOnly(parent, dep_name, build_root);
...@@ -253,7 +258,7 @@ fn createChild(...@@ -253,7 +258,7 @@ fn createChild(
253 return child;258 return child;
254}259}
255260
256fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: []const u8) !*Build {261fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Directory) !*Build {
257 const allocator = parent.allocator;262 const allocator = parent.allocator;
258 const child = try allocator.create(Build);263 const child = try allocator.create(Build);
259 child.* = .{264 child.* = .{
...@@ -297,7 +302,8 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: []const u8)...@@ -297,7 +302,8 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: []const u8)
297 .build_root = build_root,302 .build_root = build_root,
298 .cache_root = parent.cache_root,303 .cache_root = parent.cache_root,
299 .global_cache_root = parent.global_cache_root,304 .global_cache_root = parent.global_cache_root,
300 .override_lib_dir = parent.override_lib_dir,305 .cache = parent.cache,
306 .zig_lib_dir = parent.zig_lib_dir,
301 .debug_log_scopes = parent.debug_log_scopes,307 .debug_log_scopes = parent.debug_log_scopes,
302 .debug_compile_errors = parent.debug_compile_errors,308 .debug_compile_errors = parent.debug_compile_errors,
303 .enable_darling = parent.enable_darling,309 .enable_darling = parent.enable_darling,
...@@ -348,7 +354,7 @@ fn applyArgs(b: *Build, args: anytype) !void {...@@ -348,7 +354,7 @@ fn applyArgs(b: *Build, args: anytype) !void {
348 .used = false,354 .used = false,
349 });355 });
350 },356 },
351 .Enum => {357 .Enum, .EnumLiteral => {
352 try b.user_input_options.put(field.name, .{358 try b.user_input_options.put(field.name, .{
353 .name = field.name,359 .name = field.name,
354 .value = .{ .scalar = @tagName(v) },360 .value = .{ .scalar = @tagName(v) },
...@@ -379,7 +385,7 @@ fn applyArgs(b: *Build, args: anytype) !void {...@@ -379,7 +385,7 @@ fn applyArgs(b: *Build, args: anytype) !void {
379 _ = std.fmt.bufPrint(&hash_basename, "{s}", .{std.fmt.fmtSliceHexLower(&digest)}) catch385 _ = std.fmt.bufPrint(&hash_basename, "{s}", .{std.fmt.fmtSliceHexLower(&digest)}) catch
380 unreachable;386 unreachable;
381387
382 const install_prefix = b.pathJoin(&.{ b.cache_root, "i", &hash_basename });388 const install_prefix = try b.cache_root.join(b.allocator, &.{ "i", &hash_basename });
383 b.resolveInstallPrefix(install_prefix, .{});389 b.resolveInstallPrefix(install_prefix, .{});
384}390}
385391
...@@ -396,7 +402,7 @@ pub fn resolveInstallPrefix(self: *Build, install_prefix: ?[]const u8, dir_list:...@@ -396,7 +402,7 @@ pub fn resolveInstallPrefix(self: *Build, install_prefix: ?[]const u8, dir_list:
396 self.install_path = self.pathJoin(&.{ dest_dir, self.install_prefix });402 self.install_path = self.pathJoin(&.{ dest_dir, self.install_prefix });
397 } else {403 } else {
398 self.install_prefix = install_prefix orelse404 self.install_prefix = install_prefix orelse
399 (self.pathJoin(&.{ self.build_root, "zig-out" }));405 (self.build_root.join(self.allocator, &.{"zig-out"}) catch @panic("unhandled error"));
400 self.install_path = self.install_prefix;406 self.install_path = self.install_prefix;
401 }407 }
402408
...@@ -535,6 +541,7 @@ pub const AssemblyOptions = struct {...@@ -535,6 +541,7 @@ pub const AssemblyOptions = struct {
535pub fn addAssembly(b: *Build, options: AssemblyOptions) *CompileStep {541pub fn addAssembly(b: *Build, options: AssemblyOptions) *CompileStep {
536 const obj_step = CompileStep.create(b, .{542 const obj_step = CompileStep.create(b, .{
537 .name = options.name,543 .name = options.name,
544 .kind = .obj,
538 .root_source_file = null,545 .root_source_file = null,
539 .target = options.target,546 .target = options.target,
540 .optimize = options.optimize,547 .optimize = options.optimize,
...@@ -598,13 +605,39 @@ pub fn addSystemCommand(self: *Build, argv: []const []const u8) *RunStep {...@@ -598,13 +605,39 @@ pub fn addSystemCommand(self: *Build, argv: []const []const u8) *RunStep {
598 return run_step;605 return run_step;
599}606}
600607
608/// Creates a `RunStep` with an executable built with `addExecutable`.
609/// Add command line arguments with methods of `RunStep`.
610pub fn addRunArtifact(b: *Build, exe: *CompileStep) *RunStep {
611 assert(exe.kind == .exe or exe.kind == .test_exe);
612
613 // It doesn't have to be native. We catch that if you actually try to run it.
614 // Consider that this is declarative; the run step may not be run unless a user
615 // option is supplied.
616 const run_step = RunStep.create(b, b.fmt("run {s}", .{exe.step.name}));
617 run_step.addArtifactArg(exe);
618
619 if (exe.kind == .test_exe) {
620 run_step.addArg(b.zig_exe);
621 }
622
623 if (exe.vcpkg_bin_path) |path| {
624 run_step.addPathDir(path);
625 }
626
627 return run_step;
628}
629
630/// Using the `values` provided, produces a C header file, possibly based on a
631/// template input file (e.g. config.h.in).
632/// When an input template file is provided, this function will fail the build
633/// when an option not found in the input file is provided in `values`, and
634/// when an option found in the input file is missing from `values`.
601pub fn addConfigHeader(635pub fn addConfigHeader(
602 b: *Build,636 b: *Build,
603 source: FileSource,637 options: ConfigHeaderStep.Options,
604 style: ConfigHeaderStep.Style,
605 values: anytype,638 values: anytype,
606) *ConfigHeaderStep {639) *ConfigHeaderStep {
607 const config_header_step = ConfigHeaderStep.create(b, source, style);640 const config_header_step = ConfigHeaderStep.create(b, options);
608 config_header_step.addValues(values);641 config_header_step.addValues(values);
609 return config_header_step;642 return config_header_step;
610}643}
...@@ -669,8 +702,6 @@ pub fn addTranslateC(self: *Build, options: TranslateCStep.Options) *TranslateCS...@@ -669,8 +702,6 @@ pub fn addTranslateC(self: *Build, options: TranslateCStep.Options) *TranslateCS
669}702}
670703
671pub fn make(self: *Build, step_names: []const []const u8) !void {704pub fn make(self: *Build, step_names: []const []const u8) !void {
672 try self.makePath(self.cache_root);
673
674 var wanted_steps = ArrayList(*Step).init(self.allocator);705 var wanted_steps = ArrayList(*Step).init(self.allocator);
675 defer wanted_steps.deinit();706 defer wanted_steps.deinit();
676707
...@@ -901,7 +932,7 @@ pub fn standardOptimizeOption(self: *Build, options: StandardOptimizeOptionOptio...@@ -901,7 +932,7 @@ pub fn standardOptimizeOption(self: *Build, options: StandardOptimizeOptionOptio
901 return self.option(932 return self.option(
902 std.builtin.Mode,933 std.builtin.Mode,
903 "optimize",934 "optimize",
904 "prioritize performance, safety, or binary size (-O flag)",935 "Prioritize performance, safety, or binary size (-O flag)",
905 ) orelse .Debug;936 ) orelse .Debug;
906 }937 }
907}938}
...@@ -1196,13 +1227,6 @@ pub fn spawnChildEnvMap(self: *Build, cwd: ?[]const u8, env_map: *const EnvMap,...@@ -1196,13 +1227,6 @@ pub fn spawnChildEnvMap(self: *Build, cwd: ?[]const u8, env_map: *const EnvMap,
1196 }1227 }
1197}1228}
11981229
1199pub fn makePath(self: *Build, path: []const u8) !void {
1200 fs.cwd().makePath(self.pathFromRoot(path)) catch |err| {
1201 log.err("Unable to create path {s}: {s}", .{ path, @errorName(err) });
1202 return err;
1203 };
1204}
1205
1206pub fn installArtifact(self: *Build, artifact: *CompileStep) void {1230pub fn installArtifact(self: *Build, artifact: *CompileStep) void {
1207 self.getInstallStep().dependOn(&self.addInstallArtifact(artifact).step);1231 self.getInstallStep().dependOn(&self.addInstallArtifact(artifact).step);
1208}1232}
...@@ -1317,8 +1341,8 @@ pub fn truncateFile(self: *Build, dest_path: []const u8) !void {...@@ -1317,8 +1341,8 @@ pub fn truncateFile(self: *Build, dest_path: []const u8) !void {
1317 src_file.close();1341 src_file.close();
1318}1342}
13191343
1320pub fn pathFromRoot(self: *Build, rel_path: []const u8) []u8 {1344pub fn pathFromRoot(b: *Build, p: []const u8) []u8 {
1321 return fs.path.resolve(self.allocator, &[_][]const u8{ self.build_root, rel_path }) catch @panic("OOM");1345 return fs.path.resolve(b.allocator, &.{ b.build_root.path orelse ".", p }) catch @panic("OOM");
1322}1346}
13231347
1324pub fn pathJoin(self: *Build, paths: []const []const u8) []u8 {1348pub fn pathJoin(self: *Build, paths: []const []const u8) []u8 {
...@@ -1539,10 +1563,19 @@ pub fn dependency(b: *Build, name: []const u8, args: anytype) *Dependency {...@@ -1539,10 +1563,19 @@ pub fn dependency(b: *Build, name: []const u8, args: anytype) *Dependency {
1539fn dependencyInner(1563fn dependencyInner(
1540 b: *Build,1564 b: *Build,
1541 name: []const u8,1565 name: []const u8,
1542 build_root: []const u8,1566 build_root_string: []const u8,
1543 comptime build_zig: type,1567 comptime build_zig: type,
1544 args: anytype,1568 args: anytype,
1545) *Dependency {1569) *Dependency {
1570 const build_root: std.Build.Cache.Directory = .{
1571 .path = build_root_string,
1572 .handle = std.fs.cwd().openDir(build_root_string, .{}) catch |err| {
1573 std.debug.print("unable to open '{s}': {s}\n", .{
1574 build_root_string, @errorName(err),
1575 });
1576 std.process.exit(1);
1577 },
1578 };
1546 const sub_builder = b.createChild(name, build_root, args) catch @panic("unhandled error");1579 const sub_builder = b.createChild(name, build_root, args) catch @panic("unhandled error");
1547 sub_builder.runBuild(build_zig) catch @panic("unhandled error");1580 sub_builder.runBuild(build_zig) catch @panic("unhandled error");
15481581
...@@ -1563,26 +1596,6 @@ pub fn runBuild(b: *Build, build_zig: anytype) anyerror!void {...@@ -1563,26 +1596,6 @@ pub fn runBuild(b: *Build, build_zig: anytype) anyerror!void {
1563 }1596 }
1564}1597}
15651598
1566test "builder.findProgram compiles" {
1567 if (builtin.os.tag == .wasi) return error.SkipZigTest;
1568
1569 var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
1570 defer arena.deinit();
1571
1572 const host = try NativeTargetInfo.detect(.{});
1573
1574 const builder = try Build.create(
1575 arena.allocator(),
1576 "zig",
1577 "zig-cache",
1578 "zig-cache",
1579 "zig-cache",
1580 host,
1581 );
1582 defer builder.destroy();
1583 _ = builder.findProgram(&[_][]const u8{}, &[_][]const u8{}) catch null;
1584}
1585
1586pub const Module = struct {1599pub const Module = struct {
1587 builder: *Build,1600 builder: *Build,
1588 /// This could either be a generated file, in which case the module1601 /// This could either be a generated file, in which case the module
...@@ -1611,7 +1624,6 @@ pub const GeneratedFile = struct {...@@ -1611,7 +1624,6 @@ pub const GeneratedFile = struct {
1611};1624};
16121625
1613/// A file source is a reference to an existing or future file.1626/// A file source is a reference to an existing or future file.
1614///
1615pub const FileSource = union(enum) {1627pub const FileSource = union(enum) {
1616 /// A plain file path, relative to build root or absolute.1628 /// A plain file path, relative to build root or absolute.
1617 path: []const u8,1629 path: []const u8,
lib/std/Build/Cache.zig created+1253
...@@ -0,0 +1,1253 @@
1//! Manages `zig-cache` directories.
2//! This is not a general-purpose cache. It is designed to be fast and simple,
3//! not to withstand attacks using specially-crafted input.
4
5pub const Directory = struct {
6 /// This field is redundant for operations that can act on the open directory handle
7 /// directly, but it is needed when passing the directory to a child process.
8 /// `null` means cwd.
9 path: ?[]const u8,
10 handle: std.fs.Dir,
11
12 pub fn join(self: Directory, allocator: Allocator, paths: []const []const u8) ![]u8 {
13 if (self.path) |p| {
14 // TODO clean way to do this with only 1 allocation
15 const part2 = try std.fs.path.join(allocator, paths);
16 defer allocator.free(part2);
17 return std.fs.path.join(allocator, &[_][]const u8{ p, part2 });
18 } else {
19 return std.fs.path.join(allocator, paths);
20 }
21 }
22
23 pub fn joinZ(self: Directory, allocator: Allocator, paths: []const []const u8) ![:0]u8 {
24 if (self.path) |p| {
25 // TODO clean way to do this with only 1 allocation
26 const part2 = try std.fs.path.join(allocator, paths);
27 defer allocator.free(part2);
28 return std.fs.path.joinZ(allocator, &[_][]const u8{ p, part2 });
29 } else {
30 return std.fs.path.joinZ(allocator, paths);
31 }
32 }
33
34 /// Whether or not the handle should be closed, or the path should be freed
35 /// is determined by usage, however this function is provided for convenience
36 /// if it happens to be what the caller needs.
37 pub fn closeAndFree(self: *Directory, gpa: Allocator) void {
38 self.handle.close();
39 if (self.path) |p| gpa.free(p);
40 self.* = undefined;
41 }
42};
43
44gpa: Allocator,
45manifest_dir: fs.Dir,
46hash: HashHelper = .{},
47/// This value is accessed from multiple threads, protected by mutex.
48recent_problematic_timestamp: i128 = 0,
49mutex: std.Thread.Mutex = .{},
50
51/// A set of strings such as the zig library directory or project source root, which
52/// are stripped from the file paths before putting into the cache. They
53/// are replaced with single-character indicators. This is not to save
54/// space but to eliminate absolute file paths. This improves portability
55/// and usefulness of the cache for advanced use cases.
56prefixes_buffer: [4]Directory = undefined,
57prefixes_len: usize = 0,
58
59pub const DepTokenizer = @import("Cache/DepTokenizer.zig");
60
61const Cache = @This();
62const std = @import("std");
63const builtin = @import("builtin");
64const crypto = std.crypto;
65const fs = std.fs;
66const assert = std.debug.assert;
67const testing = std.testing;
68const mem = std.mem;
69const fmt = std.fmt;
70const Allocator = std.mem.Allocator;
71const log = std.log.scoped(.cache);
72
73pub fn addPrefix(cache: *Cache, directory: Directory) void {
74 cache.prefixes_buffer[cache.prefixes_len] = directory;
75 cache.prefixes_len += 1;
76}
77
78/// Be sure to call `Manifest.deinit` after successful initialization.
79pub fn obtain(cache: *Cache) Manifest {
80 return Manifest{
81 .cache = cache,
82 .hash = cache.hash,
83 .manifest_file = null,
84 .manifest_dirty = false,
85 .hex_digest = undefined,
86 };
87}
88
89pub fn prefixes(cache: *const Cache) []const Directory {
90 return cache.prefixes_buffer[0..cache.prefixes_len];
91}
92
93const PrefixedPath = struct {
94 prefix: u8,
95 sub_path: []u8,
96};
97
98fn findPrefix(cache: *const Cache, file_path: []const u8) !PrefixedPath {
99 const gpa = cache.gpa;
100 const resolved_path = try fs.path.resolve(gpa, &[_][]const u8{file_path});
101 errdefer gpa.free(resolved_path);
102 return findPrefixResolved(cache, resolved_path);
103}
104
105/// Takes ownership of `resolved_path` on success.
106fn findPrefixResolved(cache: *const Cache, resolved_path: []u8) !PrefixedPath {
107 const gpa = cache.gpa;
108 const prefixes_slice = cache.prefixes();
109 var i: u8 = 1; // Start at 1 to skip over checking the null prefix.
110 while (i < prefixes_slice.len) : (i += 1) {
111 const p = prefixes_slice[i].path.?;
112 if (mem.startsWith(u8, resolved_path, p)) {
113 // +1 to skip over the path separator here
114 const sub_path = try gpa.dupe(u8, resolved_path[p.len + 1 ..]);
115 gpa.free(resolved_path);
116 return PrefixedPath{
117 .prefix = @intCast(u8, i),
118 .sub_path = sub_path,
119 };
120 }
121 }
122
123 return PrefixedPath{
124 .prefix = 0,
125 .sub_path = resolved_path,
126 };
127}
128
129/// This is 128 bits - Even with 2^54 cache entries, the probably of a collision would be under 10^-6
130pub const bin_digest_len = 16;
131pub const hex_digest_len = bin_digest_len * 2;
132pub const BinDigest = [bin_digest_len]u8;
133
134const manifest_file_size_max = 50 * 1024 * 1024;
135
136/// The type used for hashing file contents. Currently, this is SipHash128(1, 3), because it
137/// provides enough collision resistance for the Manifest use cases, while being one of our
138/// fastest options right now.
139pub const Hasher = crypto.auth.siphash.SipHash128(1, 3);
140
141/// Initial state, that can be copied.
142pub const hasher_init: Hasher = Hasher.init(&[_]u8{0} ** Hasher.key_length);
143
144pub const File = struct {
145 prefixed_path: ?PrefixedPath,
146 max_file_size: ?usize,
147 stat: Stat,
148 bin_digest: BinDigest,
149 contents: ?[]const u8,
150
151 pub const Stat = struct {
152 inode: fs.File.INode,
153 size: u64,
154 mtime: i128,
155 };
156
157 pub fn deinit(self: *File, gpa: Allocator) void {
158 if (self.prefixed_path) |pp| {
159 gpa.free(pp.sub_path);
160 self.prefixed_path = null;
161 }
162 if (self.contents) |contents| {
163 gpa.free(contents);
164 self.contents = null;
165 }
166 self.* = undefined;
167 }
168};
169
170pub const HashHelper = struct {
171 hasher: Hasher = hasher_init,
172
173 /// Record a slice of bytes as an dependency of the process being cached
174 pub fn addBytes(hh: *HashHelper, bytes: []const u8) void {
175 hh.hasher.update(mem.asBytes(&bytes.len));
176 hh.hasher.update(bytes);
177 }
178
179 pub fn addOptionalBytes(hh: *HashHelper, optional_bytes: ?[]const u8) void {
180 hh.add(optional_bytes != null);
181 hh.addBytes(optional_bytes orelse return);
182 }
183
184 pub fn addListOfBytes(hh: *HashHelper, list_of_bytes: []const []const u8) void {
185 hh.add(list_of_bytes.len);
186 for (list_of_bytes) |bytes| hh.addBytes(bytes);
187 }
188
189 /// Convert the input value into bytes and record it as a dependency of the process being cached.
190 pub fn add(hh: *HashHelper, x: anytype) void {
191 switch (@TypeOf(x)) {
192 std.builtin.Version => {
193 hh.add(x.major);
194 hh.add(x.minor);
195 hh.add(x.patch);
196 },
197 std.Target.Os.TaggedVersionRange => {
198 switch (x) {
199 .linux => |linux| {
200 hh.add(linux.range.min);
201 hh.add(linux.range.max);
202 hh.add(linux.glibc);
203 },
204 .windows => |windows| {
205 hh.add(windows.min);
206 hh.add(windows.max);
207 },
208 .semver => |semver| {
209 hh.add(semver.min);
210 hh.add(semver.max);
211 },
212 .none => {},
213 }
214 },
215 else => switch (@typeInfo(@TypeOf(x))) {
216 .Bool, .Int, .Enum, .Array => hh.addBytes(mem.asBytes(&x)),
217 else => @compileError("unable to hash type " ++ @typeName(@TypeOf(x))),
218 },
219 }
220 }
221
222 pub fn addOptional(hh: *HashHelper, optional: anytype) void {
223 hh.add(optional != null);
224 hh.add(optional orelse return);
225 }
226
227 /// Returns a hex encoded hash of the inputs, without modifying state.
228 pub fn peek(hh: HashHelper) [hex_digest_len]u8 {
229 var copy = hh;
230 return copy.final();
231 }
232
233 pub fn peekBin(hh: HashHelper) BinDigest {
234 var copy = hh;
235 var bin_digest: BinDigest = undefined;
236 copy.hasher.final(&bin_digest);
237 return bin_digest;
238 }
239
240 /// Returns a hex encoded hash of the inputs, mutating the state of the hasher.
241 pub fn final(hh: *HashHelper) [hex_digest_len]u8 {
242 var bin_digest: BinDigest = undefined;
243 hh.hasher.final(&bin_digest);
244
245 var out_digest: [hex_digest_len]u8 = undefined;
246 _ = std.fmt.bufPrint(
247 &out_digest,
248 "{s}",
249 .{std.fmt.fmtSliceHexLower(&bin_digest)},
250 ) catch unreachable;
251 return out_digest;
252 }
253};
254
255pub const Lock = struct {
256 manifest_file: fs.File,
257
258 pub fn release(lock: *Lock) void {
259 if (builtin.os.tag == .windows) {
260 // Windows does not guarantee that locks are immediately unlocked when
261 // the file handle is closed. See LockFileEx documentation.
262 lock.manifest_file.unlock();
263 }
264
265 lock.manifest_file.close();
266 lock.* = undefined;
267 }
268};
269
270pub const Manifest = struct {
271 cache: *Cache,
272 /// Current state for incremental hashing.
273 hash: HashHelper,
274 manifest_file: ?fs.File,
275 manifest_dirty: bool,
276 /// Set this flag to true before calling hit() in order to indicate that
277 /// upon a cache hit, the code using the cache will not modify the files
278 /// within the cache directory. This allows multiple processes to utilize
279 /// the same cache directory at the same time.
280 want_shared_lock: bool = true,
281 have_exclusive_lock: bool = false,
282 // Indicate that we want isProblematicTimestamp to perform a filesystem write in
283 // order to obtain a problematic timestamp for the next call. Calls after that
284 // will then use the same timestamp, to avoid unnecessary filesystem writes.
285 want_refresh_timestamp: bool = true,
286 files: std.ArrayListUnmanaged(File) = .{},
287 hex_digest: [hex_digest_len]u8,
288 /// Populated when hit() returns an error because of one
289 /// of the files listed in the manifest.
290 failed_file_index: ?usize = null,
291 /// Keeps track of the last time we performed a file system write to observe
292 /// what time the file system thinks it is, according to its own granularity.
293 recent_problematic_timestamp: i128 = 0,
294
295 /// Add a file as a dependency of process being cached. When `hit` is
296 /// called, the file's contents will be checked to ensure that it matches
297 /// the contents from previous times.
298 ///
299 /// Max file size will be used to determine the amount of space the file contents
300 /// are allowed to take up in memory. If max_file_size is null, then the contents
301 /// will not be loaded into memory.
302 ///
303 /// Returns the index of the entry in the `files` array list. You can use it
304 /// to access the contents of the file after calling `hit()` like so:
305 ///
306 /// ```
307 /// var file_contents = cache_hash.files.items[file_index].contents.?;
308 /// ```
309 pub fn addFile(self: *Manifest, file_path: []const u8, max_file_size: ?usize) !usize {
310 assert(self.manifest_file == null);
311
312 const gpa = self.cache.gpa;
313 try self.files.ensureUnusedCapacity(gpa, 1);
314 const prefixed_path = try self.cache.findPrefix(file_path);
315 errdefer gpa.free(prefixed_path.sub_path);
316
317 self.files.addOneAssumeCapacity().* = .{
318 .prefixed_path = prefixed_path,
319 .contents = null,
320 .max_file_size = max_file_size,
321 .stat = undefined,
322 .bin_digest = undefined,
323 };
324
325 self.hash.add(prefixed_path.prefix);
326 self.hash.addBytes(prefixed_path.sub_path);
327
328 return self.files.items.len - 1;
329 }
330
331 pub fn addOptionalFile(self: *Manifest, optional_file_path: ?[]const u8) !void {
332 self.hash.add(optional_file_path != null);
333 const file_path = optional_file_path orelse return;
334 _ = try self.addFile(file_path, null);
335 }
336
337 pub fn addListOfFiles(self: *Manifest, list_of_files: []const []const u8) !void {
338 self.hash.add(list_of_files.len);
339 for (list_of_files) |file_path| {
340 _ = try self.addFile(file_path, null);
341 }
342 }
343
344 /// Check the cache to see if the input exists in it. If it exists, returns `true`.
345 /// A hex encoding of its hash is available by calling `final`.
346 ///
347 /// This function will also acquire an exclusive lock to the manifest file. This means
348 /// that a process holding a Manifest will block any other process attempting to
349 /// acquire the lock. If `want_shared_lock` is `true`, a cache hit guarantees the
350 /// manifest file to be locked in shared mode, and a cache miss guarantees the manifest
351 /// file to be locked in exclusive mode.
352 ///
353 /// The lock on the manifest file is released when `deinit` is called. As another
354 /// option, one may call `toOwnedLock` to obtain a smaller object which can represent
355 /// the lock. `deinit` is safe to call whether or not `toOwnedLock` has been called.
356 pub fn hit(self: *Manifest) !bool {
357 const gpa = self.cache.gpa;
358 assert(self.manifest_file == null);
359
360 self.failed_file_index = null;
361
362 const ext = ".txt";
363 var manifest_file_path: [self.hex_digest.len + ext.len]u8 = undefined;
364
365 var bin_digest: BinDigest = undefined;
366 self.hash.hasher.final(&bin_digest);
367
368 _ = std.fmt.bufPrint(
369 &self.hex_digest,
370 "{s}",
371 .{std.fmt.fmtSliceHexLower(&bin_digest)},
372 ) catch unreachable;
373
374 self.hash.hasher = hasher_init;
375 self.hash.hasher.update(&bin_digest);
376
377 mem.copy(u8, &manifest_file_path, &self.hex_digest);
378 manifest_file_path[self.hex_digest.len..][0..ext.len].* = ext.*;
379
380 if (self.files.items.len == 0) {
381 // If there are no file inputs, we check if the manifest file exists instead of
382 // comparing the hashes on the files used for the cached item
383 while (true) {
384 if (self.cache.manifest_dir.openFile(&manifest_file_path, .{
385 .mode = .read_write,
386 .lock = .Exclusive,
387 .lock_nonblocking = self.want_shared_lock,
388 })) |manifest_file| {
389 self.manifest_file = manifest_file;
390 self.have_exclusive_lock = true;
391 break;
392 } else |open_err| switch (open_err) {
393 error.WouldBlock => {
394 self.manifest_file = try self.cache.manifest_dir.openFile(&manifest_file_path, .{
395 .lock = .Shared,
396 });
397 break;
398 },
399 error.FileNotFound => {
400 if (self.cache.manifest_dir.createFile(&manifest_file_path, .{
401 .read = true,
402 .truncate = false,
403 .lock = .Exclusive,
404 .lock_nonblocking = self.want_shared_lock,
405 })) |manifest_file| {
406 self.manifest_file = manifest_file;
407 self.manifest_dirty = true;
408 self.have_exclusive_lock = true;
409 return false; // cache miss; exclusive lock already held
410 } else |err| switch (err) {
411 error.WouldBlock => continue,
412 else => |e| return e,
413 }
414 },
415 else => |e| return e,
416 }
417 }
418 } else {
419 if (self.cache.manifest_dir.createFile(&manifest_file_path, .{
420 .read = true,
421 .truncate = false,
422 .lock = .Exclusive,
423 .lock_nonblocking = self.want_shared_lock,
424 })) |manifest_file| {
425 self.manifest_file = manifest_file;
426 self.have_exclusive_lock = true;
427 } else |err| switch (err) {
428 error.WouldBlock => {
429 self.manifest_file = try self.cache.manifest_dir.openFile(&manifest_file_path, .{
430 .lock = .Shared,
431 });
432 },
433 else => |e| return e,
434 }
435 }
436
437 self.want_refresh_timestamp = true;
438
439 const file_contents = try self.manifest_file.?.reader().readAllAlloc(gpa, manifest_file_size_max);
440 defer gpa.free(file_contents);
441
442 const input_file_count = self.files.items.len;
443 var any_file_changed = false;
444 var line_iter = mem.tokenize(u8, file_contents, "\n");
445 var idx: usize = 0;
446 while (line_iter.next()) |line| {
447 defer idx += 1;
448
449 const cache_hash_file = if (idx < input_file_count) &self.files.items[idx] else blk: {
450 const new = try self.files.addOne(gpa);
451 new.* = .{
452 .prefixed_path = null,
453 .contents = null,
454 .max_file_size = null,
455 .stat = undefined,
456 .bin_digest = undefined,
457 };
458 break :blk new;
459 };
460
461 var iter = mem.tokenize(u8, line, " ");
462 const size = iter.next() orelse return error.InvalidFormat;
463 const inode = iter.next() orelse return error.InvalidFormat;
464 const mtime_nsec_str = iter.next() orelse return error.InvalidFormat;
465 const digest_str = iter.next() orelse return error.InvalidFormat;
466 const prefix_str = iter.next() orelse return error.InvalidFormat;
467 const file_path = iter.rest();
468
469 cache_hash_file.stat.size = fmt.parseInt(u64, size, 10) catch return error.InvalidFormat;
470 cache_hash_file.stat.inode = fmt.parseInt(fs.File.INode, inode, 10) catch return error.InvalidFormat;
471 cache_hash_file.stat.mtime = fmt.parseInt(i64, mtime_nsec_str, 10) catch return error.InvalidFormat;
472 _ = std.fmt.hexToBytes(&cache_hash_file.bin_digest, digest_str) catch return error.InvalidFormat;
473 const prefix = fmt.parseInt(u8, prefix_str, 10) catch return error.InvalidFormat;
474 if (prefix >= self.cache.prefixes_len) return error.InvalidFormat;
475
476 if (file_path.len == 0) {
477 return error.InvalidFormat;
478 }
479 if (cache_hash_file.prefixed_path) |pp| {
480 if (pp.prefix != prefix or !mem.eql(u8, file_path, pp.sub_path)) {
481 return error.InvalidFormat;
482 }
483 }
484
485 if (cache_hash_file.prefixed_path == null) {
486 cache_hash_file.prefixed_path = .{
487 .prefix = prefix,
488 .sub_path = try gpa.dupe(u8, file_path),
489 };
490 }
491
492 const pp = cache_hash_file.prefixed_path.?;
493 const dir = self.cache.prefixes()[pp.prefix].handle;
494 const this_file = dir.openFile(pp.sub_path, .{ .mode = .read_only }) catch |err| switch (err) {
495 error.FileNotFound => {
496 try self.upgradeToExclusiveLock();
497 return false;
498 },
499 else => return error.CacheUnavailable,
500 };
501 defer this_file.close();
502
503 const actual_stat = this_file.stat() catch |err| {
504 self.failed_file_index = idx;
505 return err;
506 };
507 const size_match = actual_stat.size == cache_hash_file.stat.size;
508 const mtime_match = actual_stat.mtime == cache_hash_file.stat.mtime;
509 const inode_match = actual_stat.inode == cache_hash_file.stat.inode;
510
511 if (!size_match or !mtime_match or !inode_match) {
512 self.manifest_dirty = true;
513
514 cache_hash_file.stat = .{
515 .size = actual_stat.size,
516 .mtime = actual_stat.mtime,
517 .inode = actual_stat.inode,
518 };
519
520 if (self.isProblematicTimestamp(cache_hash_file.stat.mtime)) {
521 // The actual file has an unreliable timestamp, force it to be hashed
522 cache_hash_file.stat.mtime = 0;
523 cache_hash_file.stat.inode = 0;
524 }
525
526 var actual_digest: BinDigest = undefined;
527 hashFile(this_file, &actual_digest) catch |err| {
528 self.failed_file_index = idx;
529 return err;
530 };
531
532 if (!mem.eql(u8, &cache_hash_file.bin_digest, &actual_digest)) {
533 cache_hash_file.bin_digest = actual_digest;
534 // keep going until we have the input file digests
535 any_file_changed = true;
536 }
537 }
538
539 if (!any_file_changed) {
540 self.hash.hasher.update(&cache_hash_file.bin_digest);
541 }
542 }
543
544 if (any_file_changed) {
545 // cache miss
546 // keep the manifest file open
547 self.unhit(bin_digest, input_file_count);
548 try self.upgradeToExclusiveLock();
549 return false;
550 }
551
552 if (idx < input_file_count) {
553 self.manifest_dirty = true;
554 while (idx < input_file_count) : (idx += 1) {
555 const ch_file = &self.files.items[idx];
556 self.populateFileHash(ch_file) catch |err| {
557 self.failed_file_index = idx;
558 return err;
559 };
560 }
561 try self.upgradeToExclusiveLock();
562 return false;
563 }
564
565 if (self.want_shared_lock) {
566 try self.downgradeToSharedLock();
567 }
568
569 return true;
570 }
571
572 pub fn unhit(self: *Manifest, bin_digest: BinDigest, input_file_count: usize) void {
573 // Reset the hash.
574 self.hash.hasher = hasher_init;
575 self.hash.hasher.update(&bin_digest);
576
577 // Remove files not in the initial hash.
578 for (self.files.items[input_file_count..]) |*file| {
579 file.deinit(self.cache.gpa);
580 }
581 self.files.shrinkRetainingCapacity(input_file_count);
582
583 for (self.files.items) |file| {
584 self.hash.hasher.update(&file.bin_digest);
585 }
586 }
587
588 fn isProblematicTimestamp(man: *Manifest, file_time: i128) bool {
589 // If the file_time is prior to the most recent problematic timestamp
590 // then we don't need to access the filesystem.
591 if (file_time < man.recent_problematic_timestamp)
592 return false;
593
594 // Next we will check the globally shared Cache timestamp, which is accessed
595 // from multiple threads.
596 man.cache.mutex.lock();
597 defer man.cache.mutex.unlock();
598
599 // Save the global one to our local one to avoid locking next time.
600 man.recent_problematic_timestamp = man.cache.recent_problematic_timestamp;
601 if (file_time < man.recent_problematic_timestamp)
602 return false;
603
604 // This flag prevents multiple filesystem writes for the same hit() call.
605 if (man.want_refresh_timestamp) {
606 man.want_refresh_timestamp = false;
607
608 var file = man.cache.manifest_dir.createFile("timestamp", .{
609 .read = true,
610 .truncate = true,
611 }) catch return true;
612 defer file.close();
613
614 // Save locally and also save globally (we still hold the global lock).
615 man.recent_problematic_timestamp = (file.stat() catch return true).mtime;
616 man.cache.recent_problematic_timestamp = man.recent_problematic_timestamp;
617 }
618
619 return file_time >= man.recent_problematic_timestamp;
620 }
621
622 fn populateFileHash(self: *Manifest, ch_file: *File) !void {
623 const pp = ch_file.prefixed_path.?;
624 const dir = self.cache.prefixes()[pp.prefix].handle;
625 const file = try dir.openFile(pp.sub_path, .{});
626 defer file.close();
627
628 const actual_stat = try file.stat();
629 ch_file.stat = .{
630 .size = actual_stat.size,
631 .mtime = actual_stat.mtime,
632 .inode = actual_stat.inode,
633 };
634
635 if (self.isProblematicTimestamp(ch_file.stat.mtime)) {
636 // The actual file has an unreliable timestamp, force it to be hashed
637 ch_file.stat.mtime = 0;
638 ch_file.stat.inode = 0;
639 }
640
641 if (ch_file.max_file_size) |max_file_size| {
642 if (ch_file.stat.size > max_file_size) {
643 return error.FileTooBig;
644 }
645
646 const contents = try self.cache.gpa.alloc(u8, @intCast(usize, ch_file.stat.size));
647 errdefer self.cache.gpa.free(contents);
648
649 // Hash while reading from disk, to keep the contents in the cpu cache while
650 // doing hashing.
651 var hasher = hasher_init;
652 var off: usize = 0;
653 while (true) {
654 // give me everything you've got, captain
655 const bytes_read = try file.read(contents[off..]);
656 if (bytes_read == 0) break;
657 hasher.update(contents[off..][0..bytes_read]);
658 off += bytes_read;
659 }
660 hasher.final(&ch_file.bin_digest);
661
662 ch_file.contents = contents;
663 } else {
664 try hashFile(file, &ch_file.bin_digest);
665 }
666
667 self.hash.hasher.update(&ch_file.bin_digest);
668 }
669
670 /// Add a file as a dependency of process being cached, after the initial hash has been
671 /// calculated. This is useful for processes that don't know all the files that
672 /// are depended on ahead of time. For example, a source file that can import other files
673 /// will need to be recompiled if the imported file is changed.
674 pub fn addFilePostFetch(self: *Manifest, file_path: []const u8, max_file_size: usize) ![]const u8 {
675 assert(self.manifest_file != null);
676
677 const gpa = self.cache.gpa;
678 const prefixed_path = try self.cache.findPrefix(file_path);
679 errdefer gpa.free(prefixed_path.sub_path);
680
681 const new_ch_file = try self.files.addOne(gpa);
682 new_ch_file.* = .{
683 .prefixed_path = prefixed_path,
684 .max_file_size = max_file_size,
685 .stat = undefined,
686 .bin_digest = undefined,
687 .contents = null,
688 };
689 errdefer self.files.shrinkRetainingCapacity(self.files.items.len - 1);
690
691 try self.populateFileHash(new_ch_file);
692
693 return new_ch_file.contents.?;
694 }
695
696 /// Add a file as a dependency of process being cached, after the initial hash has been
697 /// calculated. This is useful for processes that don't know the all the files that
698 /// are depended on ahead of time. For example, a source file that can import other files
699 /// will need to be recompiled if the imported file is changed.
700 pub fn addFilePost(self: *Manifest, file_path: []const u8) !void {
701 assert(self.manifest_file != null);
702
703 const gpa = self.cache.gpa;
704 const prefixed_path = try self.cache.findPrefix(file_path);
705 errdefer gpa.free(prefixed_path.sub_path);
706
707 const new_ch_file = try self.files.addOne(gpa);
708 new_ch_file.* = .{
709 .prefixed_path = prefixed_path,
710 .max_file_size = null,
711 .stat = undefined,
712 .bin_digest = undefined,
713 .contents = null,
714 };
715 errdefer self.files.shrinkRetainingCapacity(self.files.items.len - 1);
716
717 try self.populateFileHash(new_ch_file);
718 }
719
720 /// Like `addFilePost` but when the file contents have already been loaded from disk.
721 /// On success, cache takes ownership of `resolved_path`.
722 pub fn addFilePostContents(
723 self: *Manifest,
724 resolved_path: []u8,
725 bytes: []const u8,
726 stat: File.Stat,
727 ) error{OutOfMemory}!void {
728 assert(self.manifest_file != null);
729 const gpa = self.cache.gpa;
730
731 const ch_file = try self.files.addOne(gpa);
732 errdefer self.files.shrinkRetainingCapacity(self.files.items.len - 1);
733
734 const prefixed_path = try self.cache.findPrefixResolved(resolved_path);
735 errdefer gpa.free(prefixed_path.sub_path);
736
737 ch_file.* = .{
738 .prefixed_path = prefixed_path,
739 .max_file_size = null,
740 .stat = stat,
741 .bin_digest = undefined,
742 .contents = null,
743 };
744
745 if (self.isProblematicTimestamp(ch_file.stat.mtime)) {
746 // The actual file has an unreliable timestamp, force it to be hashed
747 ch_file.stat.mtime = 0;
748 ch_file.stat.inode = 0;
749 }
750
751 {
752 var hasher = hasher_init;
753 hasher.update(bytes);
754 hasher.final(&ch_file.bin_digest);
755 }
756
757 self.hash.hasher.update(&ch_file.bin_digest);
758 }
759
760 pub fn addDepFilePost(self: *Manifest, dir: fs.Dir, dep_file_basename: []const u8) !void {
761 assert(self.manifest_file != null);
762
763 const dep_file_contents = try dir.readFileAlloc(self.cache.gpa, dep_file_basename, manifest_file_size_max);
764 defer self.cache.gpa.free(dep_file_contents);
765
766 var error_buf = std.ArrayList(u8).init(self.cache.gpa);
767 defer error_buf.deinit();
768
769 var it: DepTokenizer = .{ .bytes = dep_file_contents };
770
771 // Skip first token: target.
772 switch (it.next() orelse return) { // Empty dep file OK.
773 .target, .target_must_resolve, .prereq => {},
774 else => |err| {
775 try err.printError(error_buf.writer());
776 log.err("failed parsing {s}: {s}", .{ dep_file_basename, error_buf.items });
777 return error.InvalidDepFile;
778 },
779 }
780 // Process 0+ preqreqs.
781 // Clang is invoked in single-source mode so we never get more targets.
782 while (true) {
783 switch (it.next() orelse return) {
784 .target, .target_must_resolve => return,
785 .prereq => |file_path| try self.addFilePost(file_path),
786 else => |err| {
787 try err.printError(error_buf.writer());
788 log.err("failed parsing {s}: {s}", .{ dep_file_basename, error_buf.items });
789 return error.InvalidDepFile;
790 },
791 }
792 }
793 }
794
795 /// Returns a hex encoded hash of the inputs.
796 pub fn final(self: *Manifest) [hex_digest_len]u8 {
797 assert(self.manifest_file != null);
798
799 // We don't close the manifest file yet, because we want to
800 // keep it locked until the API user is done using it.
801 // We also don't write out the manifest yet, because until
802 // cache_release is called we still might be working on creating
803 // the artifacts to cache.
804
805 var bin_digest: BinDigest = undefined;
806 self.hash.hasher.final(&bin_digest);
807
808 var out_digest: [hex_digest_len]u8 = undefined;
809 _ = std.fmt.bufPrint(
810 &out_digest,
811 "{s}",
812 .{std.fmt.fmtSliceHexLower(&bin_digest)},
813 ) catch unreachable;
814
815 return out_digest;
816 }
817
818 /// If `want_shared_lock` is true, this function automatically downgrades the
819 /// lock from exclusive to shared.
820 pub fn writeManifest(self: *Manifest) !void {
821 assert(self.have_exclusive_lock);
822
823 const manifest_file = self.manifest_file.?;
824 if (self.manifest_dirty) {
825 self.manifest_dirty = false;
826
827 var contents = std.ArrayList(u8).init(self.cache.gpa);
828 defer contents.deinit();
829
830 const writer = contents.writer();
831 var encoded_digest: [hex_digest_len]u8 = undefined;
832
833 for (self.files.items) |file| {
834 _ = std.fmt.bufPrint(
835 &encoded_digest,
836 "{s}",
837 .{std.fmt.fmtSliceHexLower(&file.bin_digest)},
838 ) catch unreachable;
839 try writer.print("{d} {d} {d} {s} {d} {s}\n", .{
840 file.stat.size,
841 file.stat.inode,
842 file.stat.mtime,
843 &encoded_digest,
844 file.prefixed_path.?.prefix,
845 file.prefixed_path.?.sub_path,
846 });
847 }
848
849 try manifest_file.setEndPos(contents.items.len);
850 try manifest_file.pwriteAll(contents.items, 0);
851 }
852
853 if (self.want_shared_lock) {
854 try self.downgradeToSharedLock();
855 }
856 }
857
858 fn downgradeToSharedLock(self: *Manifest) !void {
859 if (!self.have_exclusive_lock) return;
860
861 // WASI does not currently support flock, so we bypass it here.
862 // TODO: If/when flock is supported on WASI, this check should be removed.
863 // See https://github.com/WebAssembly/wasi-filesystem/issues/2
864 if (builtin.os.tag != .wasi or std.process.can_spawn or !builtin.single_threaded) {
865 const manifest_file = self.manifest_file.?;
866 try manifest_file.downgradeLock();
867 }
868
869 self.have_exclusive_lock = false;
870 }
871
872 fn upgradeToExclusiveLock(self: *Manifest) !void {
873 if (self.have_exclusive_lock) return;
874 assert(self.manifest_file != null);
875
876 // WASI does not currently support flock, so we bypass it here.
877 // TODO: If/when flock is supported on WASI, this check should be removed.
878 // See https://github.com/WebAssembly/wasi-filesystem/issues/2
879 if (builtin.os.tag != .wasi or std.process.can_spawn or !builtin.single_threaded) {
880 const manifest_file = self.manifest_file.?;
881 // Here we intentionally have a period where the lock is released, in case there are
882 // other processes holding a shared lock.
883 manifest_file.unlock();
884 try manifest_file.lock(.Exclusive);
885 }
886 self.have_exclusive_lock = true;
887 }
888
889 /// Obtain only the data needed to maintain a lock on the manifest file.
890 /// The `Manifest` remains safe to deinit.
891 /// Don't forget to call `writeManifest` before this!
892 pub fn toOwnedLock(self: *Manifest) Lock {
893 const lock: Lock = .{
894 .manifest_file = self.manifest_file.?,
895 };
896
897 self.manifest_file = null;
898 return lock;
899 }
900
901 /// Releases the manifest file and frees any memory the Manifest was using.
902 /// `Manifest.hit` must be called first.
903 /// Don't forget to call `writeManifest` before this!
904 pub fn deinit(self: *Manifest) void {
905 if (self.manifest_file) |file| {
906 if (builtin.os.tag == .windows) {
907 // See Lock.release for why this is required on Windows
908 file.unlock();
909 }
910
911 file.close();
912 }
913 for (self.files.items) |*file| {
914 file.deinit(self.cache.gpa);
915 }
916 self.files.deinit(self.cache.gpa);
917 }
918};
919
920/// On operating systems that support symlinks, does a readlink. On other operating systems,
921/// uses the file contents. Windows supports symlinks but only with elevated privileges, so
922/// it is treated as not supporting symlinks.
923pub fn readSmallFile(dir: fs.Dir, sub_path: []const u8, buffer: []u8) ![]u8 {
924 if (builtin.os.tag == .windows) {
925 return dir.readFile(sub_path, buffer);
926 } else {
927 return dir.readLink(sub_path, buffer);
928 }
929}
930
931/// On operating systems that support symlinks, does a symlink. On other operating systems,
932/// uses the file contents. Windows supports symlinks but only with elevated privileges, so
933/// it is treated as not supporting symlinks.
934/// `data` must be a valid UTF-8 encoded file path and 255 bytes or fewer.
935pub fn writeSmallFile(dir: fs.Dir, sub_path: []const u8, data: []const u8) !void {
936 assert(data.len <= 255);
937 if (builtin.os.tag == .windows) {
938 return dir.writeFile(sub_path, data);
939 } else {
940 return dir.symLink(data, sub_path, .{});
941 }
942}
943
944fn hashFile(file: fs.File, bin_digest: *[Hasher.mac_length]u8) !void {
945 var buf: [1024]u8 = undefined;
946
947 var hasher = hasher_init;
948 while (true) {
949 const bytes_read = try file.read(&buf);
950 if (bytes_read == 0) break;
951 hasher.update(buf[0..bytes_read]);
952 }
953
954 hasher.final(bin_digest);
955}
956
957// Create/Write a file, close it, then grab its stat.mtime timestamp.
958fn testGetCurrentFileTimestamp() !i128 {
959 var file = try fs.cwd().createFile("test-filetimestamp.tmp", .{
960 .read = true,
961 .truncate = true,
962 });
963 defer file.close();
964
965 return (try file.stat()).mtime;
966}
967
968test "cache file and then recall it" {
969 if (builtin.os.tag == .wasi) {
970 // https://github.com/ziglang/zig/issues/5437
971 return error.SkipZigTest;
972 }
973
974 const cwd = fs.cwd();
975
976 const temp_file = "test.txt";
977 const temp_manifest_dir = "temp_manifest_dir";
978
979 try cwd.writeFile(temp_file, "Hello, world!\n");
980
981 // Wait for file timestamps to tick
982 const initial_time = try testGetCurrentFileTimestamp();
983 while ((try testGetCurrentFileTimestamp()) == initial_time) {
984 std.time.sleep(1);
985 }
986
987 var digest1: [hex_digest_len]u8 = undefined;
988 var digest2: [hex_digest_len]u8 = undefined;
989
990 {
991 var cache = Cache{
992 .gpa = testing.allocator,
993 .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}),
994 };
995 cache.addPrefix(.{ .path = null, .handle = fs.cwd() });
996 defer cache.manifest_dir.close();
997
998 {
999 var ch = cache.obtain();
1000 defer ch.deinit();
1001
1002 ch.hash.add(true);
1003 ch.hash.add(@as(u16, 1234));
1004 ch.hash.addBytes("1234");
1005 _ = try ch.addFile(temp_file, null);
1006
1007 // There should be nothing in the cache
1008 try testing.expectEqual(false, try ch.hit());
1009
1010 digest1 = ch.final();
1011 try ch.writeManifest();
1012 }
1013 {
1014 var ch = cache.obtain();
1015 defer ch.deinit();
1016
1017 ch.hash.add(true);
1018 ch.hash.add(@as(u16, 1234));
1019 ch.hash.addBytes("1234");
1020 _ = try ch.addFile(temp_file, null);
1021
1022 // Cache hit! We just "built" the same file
1023 try testing.expect(try ch.hit());
1024 digest2 = ch.final();
1025
1026 try testing.expectEqual(false, ch.have_exclusive_lock);
1027 }
1028
1029 try testing.expectEqual(digest1, digest2);
1030 }
1031
1032 try cwd.deleteTree(temp_manifest_dir);
1033 try cwd.deleteFile(temp_file);
1034}
1035
1036test "check that changing a file makes cache fail" {
1037 if (builtin.os.tag == .wasi) {
1038 // https://github.com/ziglang/zig/issues/5437
1039 return error.SkipZigTest;
1040 }
1041 const cwd = fs.cwd();
1042
1043 const temp_file = "cache_hash_change_file_test.txt";
1044 const temp_manifest_dir = "cache_hash_change_file_manifest_dir";
1045 const original_temp_file_contents = "Hello, world!\n";
1046 const updated_temp_file_contents = "Hello, world; but updated!\n";
1047
1048 try cwd.deleteTree(temp_manifest_dir);
1049 try cwd.deleteTree(temp_file);
1050
1051 try cwd.writeFile(temp_file, original_temp_file_contents);
1052
1053 // Wait for file timestamps to tick
1054 const initial_time = try testGetCurrentFileTimestamp();
1055 while ((try testGetCurrentFileTimestamp()) == initial_time) {
1056 std.time.sleep(1);
1057 }
1058
1059 var digest1: [hex_digest_len]u8 = undefined;
1060 var digest2: [hex_digest_len]u8 = undefined;
1061
1062 {
1063 var cache = Cache{
1064 .gpa = testing.allocator,
1065 .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}),
1066 };
1067 cache.addPrefix(.{ .path = null, .handle = fs.cwd() });
1068 defer cache.manifest_dir.close();
1069
1070 {
1071 var ch = cache.obtain();
1072 defer ch.deinit();
1073
1074 ch.hash.addBytes("1234");
1075 const temp_file_idx = try ch.addFile(temp_file, 100);
1076
1077 // There should be nothing in the cache
1078 try testing.expectEqual(false, try ch.hit());
1079
1080 try testing.expect(mem.eql(u8, original_temp_file_contents, ch.files.items[temp_file_idx].contents.?));
1081
1082 digest1 = ch.final();
1083
1084 try ch.writeManifest();
1085 }
1086
1087 try cwd.writeFile(temp_file, updated_temp_file_contents);
1088
1089 {
1090 var ch = cache.obtain();
1091 defer ch.deinit();
1092
1093 ch.hash.addBytes("1234");
1094 const temp_file_idx = try ch.addFile(temp_file, 100);
1095
1096 // A file that we depend on has been updated, so the cache should not contain an entry for it
1097 try testing.expectEqual(false, try ch.hit());
1098
1099 // The cache system does not keep the contents of re-hashed input files.
1100 try testing.expect(ch.files.items[temp_file_idx].contents == null);
1101
1102 digest2 = ch.final();
1103
1104 try ch.writeManifest();
1105 }
1106
1107 try testing.expect(!mem.eql(u8, digest1[0..], digest2[0..]));
1108 }
1109
1110 try cwd.deleteTree(temp_manifest_dir);
1111 try cwd.deleteTree(temp_file);
1112}
1113
1114test "no file inputs" {
1115 if (builtin.os.tag == .wasi) {
1116 // https://github.com/ziglang/zig/issues/5437
1117 return error.SkipZigTest;
1118 }
1119 const cwd = fs.cwd();
1120 const temp_manifest_dir = "no_file_inputs_manifest_dir";
1121 defer cwd.deleteTree(temp_manifest_dir) catch {};
1122
1123 var digest1: [hex_digest_len]u8 = undefined;
1124 var digest2: [hex_digest_len]u8 = undefined;
1125
1126 var cache = Cache{
1127 .gpa = testing.allocator,
1128 .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}),
1129 };
1130 cache.addPrefix(.{ .path = null, .handle = fs.cwd() });
1131 defer cache.manifest_dir.close();
1132
1133 {
1134 var man = cache.obtain();
1135 defer man.deinit();
1136
1137 man.hash.addBytes("1234");
1138
1139 // There should be nothing in the cache
1140 try testing.expectEqual(false, try man.hit());
1141
1142 digest1 = man.final();
1143
1144 try man.writeManifest();
1145 }
1146 {
1147 var man = cache.obtain();
1148 defer man.deinit();
1149
1150 man.hash.addBytes("1234");
1151
1152 try testing.expect(try man.hit());
1153 digest2 = man.final();
1154 try testing.expectEqual(false, man.have_exclusive_lock);
1155 }
1156
1157 try testing.expectEqual(digest1, digest2);
1158}
1159
1160test "Manifest with files added after initial hash work" {
1161 if (builtin.os.tag == .wasi) {
1162 // https://github.com/ziglang/zig/issues/5437
1163 return error.SkipZigTest;
1164 }
1165 const cwd = fs.cwd();
1166
1167 const temp_file1 = "cache_hash_post_file_test1.txt";
1168 const temp_file2 = "cache_hash_post_file_test2.txt";
1169 const temp_manifest_dir = "cache_hash_post_file_manifest_dir";
1170
1171 try cwd.writeFile(temp_file1, "Hello, world!\n");
1172 try cwd.writeFile(temp_file2, "Hello world the second!\n");
1173
1174 // Wait for file timestamps to tick
1175 const initial_time = try testGetCurrentFileTimestamp();
1176 while ((try testGetCurrentFileTimestamp()) == initial_time) {
1177 std.time.sleep(1);
1178 }
1179
1180 var digest1: [hex_digest_len]u8 = undefined;
1181 var digest2: [hex_digest_len]u8 = undefined;
1182 var digest3: [hex_digest_len]u8 = undefined;
1183
1184 {
1185 var cache = Cache{
1186 .gpa = testing.allocator,
1187 .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}),
1188 };
1189 cache.addPrefix(.{ .path = null, .handle = fs.cwd() });
1190 defer cache.manifest_dir.close();
1191
1192 {
1193 var ch = cache.obtain();
1194 defer ch.deinit();
1195
1196 ch.hash.addBytes("1234");
1197 _ = try ch.addFile(temp_file1, null);
1198
1199 // There should be nothing in the cache
1200 try testing.expectEqual(false, try ch.hit());
1201
1202 _ = try ch.addFilePost(temp_file2);
1203
1204 digest1 = ch.final();
1205 try ch.writeManifest();
1206 }
1207 {
1208 var ch = cache.obtain();
1209 defer ch.deinit();
1210
1211 ch.hash.addBytes("1234");
1212 _ = try ch.addFile(temp_file1, null);
1213
1214 try testing.expect(try ch.hit());
1215 digest2 = ch.final();
1216
1217 try testing.expectEqual(false, ch.have_exclusive_lock);
1218 }
1219 try testing.expect(mem.eql(u8, &digest1, &digest2));
1220
1221 // Modify the file added after initial hash
1222 try cwd.writeFile(temp_file2, "Hello world the second, updated\n");
1223
1224 // Wait for file timestamps to tick
1225 const initial_time2 = try testGetCurrentFileTimestamp();
1226 while ((try testGetCurrentFileTimestamp()) == initial_time2) {
1227 std.time.sleep(1);
1228 }
1229
1230 {
1231 var ch = cache.obtain();
1232 defer ch.deinit();
1233
1234 ch.hash.addBytes("1234");
1235 _ = try ch.addFile(temp_file1, null);
1236
1237 // A file that we depend on has been updated, so the cache should not contain an entry for it
1238 try testing.expectEqual(false, try ch.hit());
1239
1240 _ = try ch.addFilePost(temp_file2);
1241
1242 digest3 = ch.final();
1243
1244 try ch.writeManifest();
1245 }
1246
1247 try testing.expect(!mem.eql(u8, &digest1, &digest3));
1248 }
1249
1250 try cwd.deleteTree(temp_manifest_dir);
1251 try cwd.deleteFile(temp_file1);
1252 try cwd.deleteFile(temp_file2);
1253}
lib/std/Build/Cache/DepTokenizer.zig created+1069
...@@ -0,0 +1,1069 @@
1const Tokenizer = @This();
2
3index: usize = 0,
4bytes: []const u8,
5state: State = .lhs,
6
7const std = @import("std");
8const testing = std.testing;
9const assert = std.debug.assert;
10
11pub fn next(self: *Tokenizer) ?Token {
12 var start = self.index;
13 var must_resolve = false;
14 while (self.index < self.bytes.len) {
15 const char = self.bytes[self.index];
16 switch (self.state) {
17 .lhs => switch (char) {
18 '\t', '\n', '\r', ' ' => {
19 // silently ignore whitespace
20 self.index += 1;
21 },
22 else => {
23 start = self.index;
24 self.state = .target;
25 },
26 },
27 .target => switch (char) {
28 '\t', '\n', '\r', ' ' => {
29 return errorIllegalChar(.invalid_target, self.index, char);
30 },
31 '$' => {
32 self.state = .target_dollar_sign;
33 self.index += 1;
34 },
35 '\\' => {
36 self.state = .target_reverse_solidus;
37 self.index += 1;
38 },
39 ':' => {
40 self.state = .target_colon;
41 self.index += 1;
42 },
43 else => {
44 self.index += 1;
45 },
46 },
47 .target_reverse_solidus => switch (char) {
48 '\t', '\n', '\r' => {
49 return errorIllegalChar(.bad_target_escape, self.index, char);
50 },
51 ' ', '#', '\\' => {
52 must_resolve = true;
53 self.state = .target;
54 self.index += 1;
55 },
56 '$' => {
57 self.state = .target_dollar_sign;
58 self.index += 1;
59 },
60 else => {
61 self.state = .target;
62 self.index += 1;
63 },
64 },
65 .target_dollar_sign => switch (char) {
66 '$' => {
67 must_resolve = true;
68 self.state = .target;
69 self.index += 1;
70 },
71 else => {
72 return errorIllegalChar(.expected_dollar_sign, self.index, char);
73 },
74 },
75 .target_colon => switch (char) {
76 '\n', '\r' => {
77 const bytes = self.bytes[start .. self.index - 1];
78 if (bytes.len != 0) {
79 self.state = .lhs;
80 return finishTarget(must_resolve, bytes);
81 }
82 // silently ignore null target
83 self.state = .lhs;
84 },
85 '/', '\\' => {
86 self.state = .target_colon_reverse_solidus;
87 self.index += 1;
88 },
89 else => {
90 const bytes = self.bytes[start .. self.index - 1];
91 if (bytes.len != 0) {
92 self.state = .rhs;
93 return finishTarget(must_resolve, bytes);
94 }
95 // silently ignore null target
96 self.state = .lhs;
97 },
98 },
99 .target_colon_reverse_solidus => switch (char) {
100 '\n', '\r' => {
101 const bytes = self.bytes[start .. self.index - 2];
102 if (bytes.len != 0) {
103 self.state = .lhs;
104 return finishTarget(must_resolve, bytes);
105 }
106 // silently ignore null target
107 self.state = .lhs;
108 },
109 else => {
110 self.state = .target;
111 },
112 },
113 .rhs => switch (char) {
114 '\t', ' ' => {
115 // silently ignore horizontal whitespace
116 self.index += 1;
117 },
118 '\n', '\r' => {
119 self.state = .lhs;
120 },
121 '\\' => {
122 self.state = .rhs_continuation;
123 self.index += 1;
124 },
125 '"' => {
126 self.state = .prereq_quote;
127 self.index += 1;
128 start = self.index;
129 },
130 else => {
131 start = self.index;
132 self.state = .prereq;
133 },
134 },
135 .rhs_continuation => switch (char) {
136 '\n' => {
137 self.state = .rhs;
138 self.index += 1;
139 },
140 '\r' => {
141 self.state = .rhs_continuation_linefeed;
142 self.index += 1;
143 },
144 else => {
145 return errorIllegalChar(.continuation_eol, self.index, char);
146 },
147 },
148 .rhs_continuation_linefeed => switch (char) {
149 '\n' => {
150 self.state = .rhs;
151 self.index += 1;
152 },
153 else => {
154 return errorIllegalChar(.continuation_eol, self.index, char);
155 },
156 },
157 .prereq_quote => switch (char) {
158 '"' => {
159 self.index += 1;
160 self.state = .rhs;
161 return Token{ .prereq = self.bytes[start .. self.index - 1] };
162 },
163 else => {
164 self.index += 1;
165 },
166 },
167 .prereq => switch (char) {
168 '\t', ' ' => {
169 self.state = .rhs;
170 return Token{ .prereq = self.bytes[start..self.index] };
171 },
172 '\n', '\r' => {
173 self.state = .lhs;
174 return Token{ .prereq = self.bytes[start..self.index] };
175 },
176 '\\' => {
177 self.state = .prereq_continuation;
178 self.index += 1;
179 },
180 else => {
181 self.index += 1;
182 },
183 },
184 .prereq_continuation => switch (char) {
185 '\n' => {
186 self.index += 1;
187 self.state = .rhs;
188 return Token{ .prereq = self.bytes[start .. self.index - 2] };
189 },
190 '\r' => {
191 self.state = .prereq_continuation_linefeed;
192 self.index += 1;
193 },
194 else => {
195 // not continuation
196 self.state = .prereq;
197 self.index += 1;
198 },
199 },
200 .prereq_continuation_linefeed => switch (char) {
201 '\n' => {
202 self.index += 1;
203 self.state = .rhs;
204 return Token{ .prereq = self.bytes[start .. self.index - 1] };
205 },
206 else => {
207 return errorIllegalChar(.continuation_eol, self.index, char);
208 },
209 },
210 }
211 } else {
212 switch (self.state) {
213 .lhs,
214 .rhs,
215 .rhs_continuation,
216 .rhs_continuation_linefeed,
217 => return null,
218 .target => {
219 return errorPosition(.incomplete_target, start, self.bytes[start..]);
220 },
221 .target_reverse_solidus,
222 .target_dollar_sign,
223 => {
224 const idx = self.index - 1;
225 return errorIllegalChar(.incomplete_escape, idx, self.bytes[idx]);
226 },
227 .target_colon => {
228 const bytes = self.bytes[start .. self.index - 1];
229 if (bytes.len != 0) {
230 self.index += 1;
231 self.state = .rhs;
232 return finishTarget(must_resolve, bytes);
233 }
234 // silently ignore null target
235 self.state = .lhs;
236 return null;
237 },
238 .target_colon_reverse_solidus => {
239 const bytes = self.bytes[start .. self.index - 2];
240 if (bytes.len != 0) {
241 self.index += 1;
242 self.state = .rhs;
243 return finishTarget(must_resolve, bytes);
244 }
245 // silently ignore null target
246 self.state = .lhs;
247 return null;
248 },
249 .prereq_quote => {
250 return errorPosition(.incomplete_quoted_prerequisite, start, self.bytes[start..]);
251 },
252 .prereq => {
253 self.state = .lhs;
254 return Token{ .prereq = self.bytes[start..] };
255 },
256 .prereq_continuation => {
257 self.state = .lhs;
258 return Token{ .prereq = self.bytes[start .. self.index - 1] };
259 },
260 .prereq_continuation_linefeed => {
261 self.state = .lhs;
262 return Token{ .prereq = self.bytes[start .. self.index - 2] };
263 },
264 }
265 }
266 unreachable;
267}
268
269fn errorPosition(comptime id: std.meta.Tag(Token), index: usize, bytes: []const u8) Token {
270 return @unionInit(Token, @tagName(id), .{ .index = index, .bytes = bytes });
271}
272
273fn errorIllegalChar(comptime id: std.meta.Tag(Token), index: usize, char: u8) Token {
274 return @unionInit(Token, @tagName(id), .{ .index = index, .char = char });
275}
276
277fn finishTarget(must_resolve: bool, bytes: []const u8) Token {
278 return if (must_resolve) .{ .target_must_resolve = bytes } else .{ .target = bytes };
279}
280
281const State = enum {
282 lhs,
283 target,
284 target_reverse_solidus,
285 target_dollar_sign,
286 target_colon,
287 target_colon_reverse_solidus,
288 rhs,
289 rhs_continuation,
290 rhs_continuation_linefeed,
291 prereq_quote,
292 prereq,
293 prereq_continuation,
294 prereq_continuation_linefeed,
295};
296
297pub const Token = union(enum) {
298 target: []const u8,
299 target_must_resolve: []const u8,
300 prereq: []const u8,
301
302 incomplete_quoted_prerequisite: IndexAndBytes,
303 incomplete_target: IndexAndBytes,
304
305 invalid_target: IndexAndChar,
306 bad_target_escape: IndexAndChar,
307 expected_dollar_sign: IndexAndChar,
308 continuation_eol: IndexAndChar,
309 incomplete_escape: IndexAndChar,
310
311 pub const IndexAndChar = struct {
312 index: usize,
313 char: u8,
314 };
315
316 pub const IndexAndBytes = struct {
317 index: usize,
318 bytes: []const u8,
319 };
320
321 /// Resolve escapes in target. Only valid with .target_must_resolve.
322 pub fn resolve(self: Token, writer: anytype) @TypeOf(writer).Error!void {
323 const bytes = self.target_must_resolve; // resolve called on incorrect token
324
325 var state: enum { start, escape, dollar } = .start;
326 for (bytes) |c| {
327 switch (state) {
328 .start => {
329 switch (c) {
330 '\\' => state = .escape,
331 '$' => state = .dollar,
332 else => try writer.writeByte(c),
333 }
334 },
335 .escape => {
336 switch (c) {
337 ' ', '#', '\\' => {},
338 '$' => {
339 try writer.writeByte('\\');
340 state = .dollar;
341 continue;
342 },
343 else => try writer.writeByte('\\'),
344 }
345 try writer.writeByte(c);
346 state = .start;
347 },
348 .dollar => {
349 try writer.writeByte('$');
350 switch (c) {
351 '$' => {},
352 else => try writer.writeByte(c),
353 }
354 state = .start;
355 },
356 }
357 }
358 }
359
360 pub fn printError(self: Token, writer: anytype) @TypeOf(writer).Error!void {
361 switch (self) {
362 .target, .target_must_resolve, .prereq => unreachable, // not an error
363 .incomplete_quoted_prerequisite,
364 .incomplete_target,
365 => |index_and_bytes| {
366 try writer.print("{s} '", .{self.errStr()});
367 if (self == .incomplete_target) {
368 const tmp = Token{ .target_must_resolve = index_and_bytes.bytes };
369 try tmp.resolve(writer);
370 } else {
371 try printCharValues(writer, index_and_bytes.bytes);
372 }
373 try writer.print("' at position {d}", .{index_and_bytes.index});
374 },
375 .invalid_target,
376 .bad_target_escape,
377 .expected_dollar_sign,
378 .continuation_eol,
379 .incomplete_escape,
380 => |index_and_char| {
381 try writer.writeAll("illegal char ");
382 try printUnderstandableChar(writer, index_and_char.char);
383 try writer.print(" at position {d}: {s}", .{ index_and_char.index, self.errStr() });
384 },
385 }
386 }
387
388 fn errStr(self: Token) []const u8 {
389 return switch (self) {
390 .target, .target_must_resolve, .prereq => unreachable, // not an error
391 .incomplete_quoted_prerequisite => "incomplete quoted prerequisite",
392 .incomplete_target => "incomplete target",
393 .invalid_target => "invalid target",
394 .bad_target_escape => "bad target escape",
395 .expected_dollar_sign => "expecting '$'",
396 .continuation_eol => "continuation expecting end-of-line",
397 .incomplete_escape => "incomplete escape",
398 };
399 }
400};
401
402test "empty file" {
403 try depTokenizer("", "");
404}
405
406test "empty whitespace" {
407 try depTokenizer("\n", "");
408 try depTokenizer("\r", "");
409 try depTokenizer("\r\n", "");
410 try depTokenizer(" ", "");
411}
412
413test "empty colon" {
414 try depTokenizer(":", "");
415 try depTokenizer("\n:", "");
416 try depTokenizer("\r:", "");
417 try depTokenizer("\r\n:", "");
418 try depTokenizer(" :", "");
419}
420
421test "empty target" {
422 try depTokenizer("foo.o:", "target = {foo.o}");
423 try depTokenizer(
424 \\foo.o:
425 \\bar.o:
426 \\abcd.o:
427 ,
428 \\target = {foo.o}
429 \\target = {bar.o}
430 \\target = {abcd.o}
431 );
432}
433
434test "whitespace empty target" {
435 try depTokenizer("\nfoo.o:", "target = {foo.o}");
436 try depTokenizer("\rfoo.o:", "target = {foo.o}");
437 try depTokenizer("\r\nfoo.o:", "target = {foo.o}");
438 try depTokenizer(" foo.o:", "target = {foo.o}");
439}
440
441test "escape empty target" {
442 try depTokenizer("\\ foo.o:", "target = { foo.o}");
443 try depTokenizer("\\#foo.o:", "target = {#foo.o}");
444 try depTokenizer("\\\\foo.o:", "target = {\\foo.o}");
445 try depTokenizer("$$foo.o:", "target = {$foo.o}");
446}
447
448test "empty target linefeeds" {
449 try depTokenizer("\n", "");
450 try depTokenizer("\r\n", "");
451
452 const expect = "target = {foo.o}";
453 try depTokenizer(
454 \\foo.o:
455 , expect);
456 try depTokenizer(
457 \\foo.o:
458 \\
459 , expect);
460 try depTokenizer(
461 \\foo.o:
462 , expect);
463 try depTokenizer(
464 \\foo.o:
465 \\
466 , expect);
467}
468
469test "empty target linefeeds + continuations" {
470 const expect = "target = {foo.o}";
471 try depTokenizer(
472 \\foo.o:\
473 , expect);
474 try depTokenizer(
475 \\foo.o:\
476 \\
477 , expect);
478 try depTokenizer(
479 \\foo.o:\
480 , expect);
481 try depTokenizer(
482 \\foo.o:\
483 \\
484 , expect);
485}
486
487test "empty target linefeeds + hspace + continuations" {
488 const expect = "target = {foo.o}";
489 try depTokenizer(
490 \\foo.o: \
491 , expect);
492 try depTokenizer(
493 \\foo.o: \
494 \\
495 , expect);
496 try depTokenizer(
497 \\foo.o: \
498 , expect);
499 try depTokenizer(
500 \\foo.o: \
501 \\
502 , expect);
503}
504
505test "prereq" {
506 const expect =
507 \\target = {foo.o}
508 \\prereq = {foo.c}
509 ;
510 try depTokenizer("foo.o: foo.c", expect);
511 try depTokenizer(
512 \\foo.o: \
513 \\foo.c
514 , expect);
515 try depTokenizer(
516 \\foo.o: \
517 \\ foo.c
518 , expect);
519 try depTokenizer(
520 \\foo.o: \
521 \\ foo.c
522 , expect);
523}
524
525test "prereq continuation" {
526 const expect =
527 \\target = {foo.o}
528 \\prereq = {foo.h}
529 \\prereq = {bar.h}
530 ;
531 try depTokenizer(
532 \\foo.o: foo.h\
533 \\bar.h
534 , expect);
535 try depTokenizer(
536 \\foo.o: foo.h\
537 \\bar.h
538 , expect);
539}
540
541test "multiple prereqs" {
542 const expect =
543 \\target = {foo.o}
544 \\prereq = {foo.c}
545 \\prereq = {foo.h}
546 \\prereq = {bar.h}
547 ;
548 try depTokenizer("foo.o: foo.c foo.h bar.h", expect);
549 try depTokenizer(
550 \\foo.o: \
551 \\foo.c foo.h bar.h
552 , expect);
553 try depTokenizer(
554 \\foo.o: foo.c foo.h bar.h\
555 , expect);
556 try depTokenizer(
557 \\foo.o: foo.c foo.h bar.h\
558 \\
559 , expect);
560 try depTokenizer(
561 \\foo.o: \
562 \\foo.c \
563 \\ foo.h\
564 \\bar.h
565 \\
566 , expect);
567 try depTokenizer(
568 \\foo.o: \
569 \\foo.c \
570 \\ foo.h\
571 \\bar.h\
572 \\
573 , expect);
574 try depTokenizer(
575 \\foo.o: \
576 \\foo.c \
577 \\ foo.h\
578 \\bar.h\
579 , expect);
580}
581
582test "multiple targets and prereqs" {
583 try depTokenizer(
584 \\foo.o: foo.c
585 \\bar.o: bar.c a.h b.h c.h
586 \\abc.o: abc.c \
587 \\ one.h two.h \
588 \\ three.h four.h
589 ,
590 \\target = {foo.o}
591 \\prereq = {foo.c}
592 \\target = {bar.o}
593 \\prereq = {bar.c}
594 \\prereq = {a.h}
595 \\prereq = {b.h}
596 \\prereq = {c.h}
597 \\target = {abc.o}
598 \\prereq = {abc.c}
599 \\prereq = {one.h}
600 \\prereq = {two.h}
601 \\prereq = {three.h}
602 \\prereq = {four.h}
603 );
604 try depTokenizer(
605 \\ascii.o: ascii.c
606 \\base64.o: base64.c stdio.h
607 \\elf.o: elf.c a.h b.h c.h
608 \\macho.o: \
609 \\ macho.c\
610 \\ a.h b.h c.h
611 ,
612 \\target = {ascii.o}
613 \\prereq = {ascii.c}
614 \\target = {base64.o}
615 \\prereq = {base64.c}
616 \\prereq = {stdio.h}
617 \\target = {elf.o}
618 \\prereq = {elf.c}
619 \\prereq = {a.h}
620 \\prereq = {b.h}
621 \\prereq = {c.h}
622 \\target = {macho.o}
623 \\prereq = {macho.c}
624 \\prereq = {a.h}
625 \\prereq = {b.h}
626 \\prereq = {c.h}
627 );
628 try depTokenizer(
629 \\a$$scii.o: ascii.c
630 \\\\base64.o: "\base64.c" "s t#dio.h"
631 \\e\\lf.o: "e\lf.c" "a.h$$" "$$b.h c.h$$"
632 \\macho.o: \
633 \\ "macho!.c" \
634 \\ a.h b.h c.h
635 ,
636 \\target = {a$scii.o}
637 \\prereq = {ascii.c}
638 \\target = {\base64.o}
639 \\prereq = {\base64.c}
640 \\prereq = {s t#dio.h}
641 \\target = {e\lf.o}
642 \\prereq = {e\lf.c}
643 \\prereq = {a.h$$}
644 \\prereq = {$$b.h c.h$$}
645 \\target = {macho.o}
646 \\prereq = {macho!.c}
647 \\prereq = {a.h}
648 \\prereq = {b.h}
649 \\prereq = {c.h}
650 );
651}
652
653test "windows quoted prereqs" {
654 try depTokenizer(
655 \\c:\foo.o: "C:\Program Files (x86)\Microsoft Visual Studio\foo.c"
656 \\c:\foo2.o: "C:\Program Files (x86)\Microsoft Visual Studio\foo2.c" \
657 \\ "C:\Program Files (x86)\Microsoft Visual Studio\foo1.h" \
658 \\ "C:\Program Files (x86)\Microsoft Visual Studio\foo2.h"
659 ,
660 \\target = {c:\foo.o}
661 \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\foo.c}
662 \\target = {c:\foo2.o}
663 \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\foo2.c}
664 \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\foo1.h}
665 \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\foo2.h}
666 );
667}
668
669test "windows mixed prereqs" {
670 try depTokenizer(
671 \\cimport.o: \
672 \\ C:\msys64\home\anon\project\zig\master\zig-cache\o\qhvhbUo7GU5iKyQ5mpA8TcQpncCYaQu0wwvr3ybiSTj_Dtqi1Nmcb70kfODJ2Qlg\cimport.h \
673 \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\stdio.h" \
674 \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt.h" \
675 \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vcruntime.h" \
676 \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\sal.h" \
677 \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\concurrencysal.h" \
678 \\ C:\msys64\opt\zig\lib\zig\include\vadefs.h \
679 \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vadefs.h" \
680 \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_wstdio.h" \
681 \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_stdio_config.h" \
682 \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\string.h" \
683 \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_memory.h" \
684 \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_memcpy_s.h" \
685 \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\errno.h" \
686 \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vcruntime_string.h" \
687 \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_wstring.h"
688 ,
689 \\target = {cimport.o}
690 \\prereq = {C:\msys64\home\anon\project\zig\master\zig-cache\o\qhvhbUo7GU5iKyQ5mpA8TcQpncCYaQu0wwvr3ybiSTj_Dtqi1Nmcb70kfODJ2Qlg\cimport.h}
691 \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\stdio.h}
692 \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt.h}
693 \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vcruntime.h}
694 \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\sal.h}
695 \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\concurrencysal.h}
696 \\prereq = {C:\msys64\opt\zig\lib\zig\include\vadefs.h}
697 \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vadefs.h}
698 \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_wstdio.h}
699 \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_stdio_config.h}
700 \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\string.h}
701 \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_memory.h}
702 \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_memcpy_s.h}
703 \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\errno.h}
704 \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vcruntime_string.h}
705 \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_wstring.h}
706 );
707}
708
709test "windows funky targets" {
710 try depTokenizer(
711 \\C:\Users\anon\foo.o:
712 \\C:\Users\anon\foo\ .o:
713 \\C:\Users\anon\foo\#.o:
714 \\C:\Users\anon\foo$$.o:
715 \\C:\Users\anon\\\ foo.o:
716 \\C:\Users\anon\\#foo.o:
717 \\C:\Users\anon\$$foo.o:
718 \\C:\Users\anon\\\ \ \ \ \ foo.o:
719 ,
720 \\target = {C:\Users\anon\foo.o}
721 \\target = {C:\Users\anon\foo .o}
722 \\target = {C:\Users\anon\foo#.o}
723 \\target = {C:\Users\anon\foo$.o}
724 \\target = {C:\Users\anon\ foo.o}
725 \\target = {C:\Users\anon\#foo.o}
726 \\target = {C:\Users\anon\$foo.o}
727 \\target = {C:\Users\anon\ foo.o}
728 );
729}
730
731test "windows drive and forward slashes" {
732 try depTokenizer(
733 \\C:/msys64/what/zig-cache\tmp\48ac4d78dd531abd-cxa_thread_atexit.obj: \
734 \\ C:/msys64/opt/zig3/lib/zig/libc/mingw/crt/cxa_thread_atexit.c
735 ,
736 \\target = {C:/msys64/what/zig-cache\tmp\48ac4d78dd531abd-cxa_thread_atexit.obj}
737 \\prereq = {C:/msys64/opt/zig3/lib/zig/libc/mingw/crt/cxa_thread_atexit.c}
738 );
739}
740
741test "error incomplete escape - reverse_solidus" {
742 try depTokenizer("\\",
743 \\ERROR: illegal char '\' at position 0: incomplete escape
744 );
745 try depTokenizer("\t\\",
746 \\ERROR: illegal char '\' at position 1: incomplete escape
747 );
748 try depTokenizer("\n\\",
749 \\ERROR: illegal char '\' at position 1: incomplete escape
750 );
751 try depTokenizer("\r\\",
752 \\ERROR: illegal char '\' at position 1: incomplete escape
753 );
754 try depTokenizer("\r\n\\",
755 \\ERROR: illegal char '\' at position 2: incomplete escape
756 );
757 try depTokenizer(" \\",
758 \\ERROR: illegal char '\' at position 1: incomplete escape
759 );
760}
761
762test "error incomplete escape - dollar_sign" {
763 try depTokenizer("$",
764 \\ERROR: illegal char '$' at position 0: incomplete escape
765 );
766 try depTokenizer("\t$",
767 \\ERROR: illegal char '$' at position 1: incomplete escape
768 );
769 try depTokenizer("\n$",
770 \\ERROR: illegal char '$' at position 1: incomplete escape
771 );
772 try depTokenizer("\r$",
773 \\ERROR: illegal char '$' at position 1: incomplete escape
774 );
775 try depTokenizer("\r\n$",
776 \\ERROR: illegal char '$' at position 2: incomplete escape
777 );
778 try depTokenizer(" $",
779 \\ERROR: illegal char '$' at position 1: incomplete escape
780 );
781}
782
783test "error incomplete target" {
784 try depTokenizer("foo.o",
785 \\ERROR: incomplete target 'foo.o' at position 0
786 );
787 try depTokenizer("\tfoo.o",
788 \\ERROR: incomplete target 'foo.o' at position 1
789 );
790 try depTokenizer("\nfoo.o",
791 \\ERROR: incomplete target 'foo.o' at position 1
792 );
793 try depTokenizer("\rfoo.o",
794 \\ERROR: incomplete target 'foo.o' at position 1
795 );
796 try depTokenizer("\r\nfoo.o",
797 \\ERROR: incomplete target 'foo.o' at position 2
798 );
799 try depTokenizer(" foo.o",
800 \\ERROR: incomplete target 'foo.o' at position 1
801 );
802
803 try depTokenizer("\\ foo.o",
804 \\ERROR: incomplete target ' foo.o' at position 0
805 );
806 try depTokenizer("\\#foo.o",
807 \\ERROR: incomplete target '#foo.o' at position 0
808 );
809 try depTokenizer("\\\\foo.o",
810 \\ERROR: incomplete target '\foo.o' at position 0
811 );
812 try depTokenizer("$$foo.o",
813 \\ERROR: incomplete target '$foo.o' at position 0
814 );
815}
816
817test "error illegal char at position - bad target escape" {
818 try depTokenizer("\\\t",
819 \\ERROR: illegal char \x09 at position 1: bad target escape
820 );
821 try depTokenizer("\\\n",
822 \\ERROR: illegal char \x0A at position 1: bad target escape
823 );
824 try depTokenizer("\\\r",
825 \\ERROR: illegal char \x0D at position 1: bad target escape
826 );
827 try depTokenizer("\\\r\n",
828 \\ERROR: illegal char \x0D at position 1: bad target escape
829 );
830}
831
832test "error illegal char at position - execting dollar_sign" {
833 try depTokenizer("$\t",
834 \\ERROR: illegal char \x09 at position 1: expecting '$'
835 );
836 try depTokenizer("$\n",
837 \\ERROR: illegal char \x0A at position 1: expecting '$'
838 );
839 try depTokenizer("$\r",
840 \\ERROR: illegal char \x0D at position 1: expecting '$'
841 );
842 try depTokenizer("$\r\n",
843 \\ERROR: illegal char \x0D at position 1: expecting '$'
844 );
845}
846
847test "error illegal char at position - invalid target" {
848 try depTokenizer("foo\t.o",
849 \\ERROR: illegal char \x09 at position 3: invalid target
850 );
851 try depTokenizer("foo\n.o",
852 \\ERROR: illegal char \x0A at position 3: invalid target
853 );
854 try depTokenizer("foo\r.o",
855 \\ERROR: illegal char \x0D at position 3: invalid target
856 );
857 try depTokenizer("foo\r\n.o",
858 \\ERROR: illegal char \x0D at position 3: invalid target
859 );
860}
861
862test "error target - continuation expecting end-of-line" {
863 try depTokenizer("foo.o: \\\t",
864 \\target = {foo.o}
865 \\ERROR: illegal char \x09 at position 8: continuation expecting end-of-line
866 );
867 try depTokenizer("foo.o: \\ ",
868 \\target = {foo.o}
869 \\ERROR: illegal char ' ' at position 8: continuation expecting end-of-line
870 );
871 try depTokenizer("foo.o: \\x",
872 \\target = {foo.o}
873 \\ERROR: illegal char 'x' at position 8: continuation expecting end-of-line
874 );
875 try depTokenizer("foo.o: \\\x0dx",
876 \\target = {foo.o}
877 \\ERROR: illegal char 'x' at position 9: continuation expecting end-of-line
878 );
879}
880
881test "error prereq - continuation expecting end-of-line" {
882 try depTokenizer("foo.o: foo.h\\\x0dx",
883 \\target = {foo.o}
884 \\ERROR: illegal char 'x' at position 14: continuation expecting end-of-line
885 );
886}
887
888// - tokenize input, emit textual representation, and compare to expect
889fn depTokenizer(input: []const u8, expect: []const u8) !void {
890 var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator);
891 const arena = arena_allocator.allocator();
892 defer arena_allocator.deinit();
893
894 var it: Tokenizer = .{ .bytes = input };
895 var buffer = std.ArrayList(u8).init(arena);
896 var resolve_buf = std.ArrayList(u8).init(arena);
897 var i: usize = 0;
898 while (it.next()) |token| {
899 if (i != 0) try buffer.appendSlice("\n");
900 switch (token) {
901 .target, .prereq => |bytes| {
902 try buffer.appendSlice(@tagName(token));
903 try buffer.appendSlice(" = {");
904 for (bytes) |b| {
905 try buffer.append(printable_char_tab[b]);
906 }
907 try buffer.appendSlice("}");
908 },
909 .target_must_resolve => {
910 try buffer.appendSlice("target = {");
911 try token.resolve(resolve_buf.writer());
912 for (resolve_buf.items) |b| {
913 try buffer.append(printable_char_tab[b]);
914 }
915 resolve_buf.items.len = 0;
916 try buffer.appendSlice("}");
917 },
918 else => {
919 try buffer.appendSlice("ERROR: ");
920 try token.printError(buffer.writer());
921 break;
922 },
923 }
924 i += 1;
925 }
926
927 if (std.mem.eql(u8, expect, buffer.items)) {
928 try testing.expect(true);
929 return;
930 }
931
932 const out = std.io.getStdErr().writer();
933
934 try out.writeAll("\n");
935 try printSection(out, "<<<< input", input);
936 try printSection(out, "==== expect", expect);
937 try printSection(out, ">>>> got", buffer.items);
938 try printRuler(out);
939
940 try testing.expect(false);
941}
942
943fn printSection(out: anytype, label: []const u8, bytes: []const u8) !void {
944 try printLabel(out, label, bytes);
945 try hexDump(out, bytes);
946 try printRuler(out);
947 try out.writeAll(bytes);
948 try out.writeAll("\n");
949}
950
951fn printLabel(out: anytype, label: []const u8, bytes: []const u8) !void {
952 var buf: [80]u8 = undefined;
953 var text = try std.fmt.bufPrint(buf[0..], "{s} {d} bytes ", .{ label, bytes.len });
954 try out.writeAll(text);
955 var i: usize = text.len;
956 const end = 79;
957 while (i < end) : (i += 1) {
958 try out.writeAll(&[_]u8{label[0]});
959 }
960 try out.writeAll("\n");
961}
962
963fn printRuler(out: anytype) !void {
964 var i: usize = 0;
965 const end = 79;
966 while (i < end) : (i += 1) {
967 try out.writeAll("-");
968 }
969 try out.writeAll("\n");
970}
971
972fn hexDump(out: anytype, bytes: []const u8) !void {
973 const n16 = bytes.len >> 4;
974 var line: usize = 0;
975 var offset: usize = 0;
976 while (line < n16) : (line += 1) {
977 try hexDump16(out, offset, bytes[offset .. offset + 16]);
978 offset += 16;
979 }
980
981 const n = bytes.len & 0x0f;
982 if (n > 0) {
983 try printDecValue(out, offset, 8);
984 try out.writeAll(":");
985 try out.writeAll(" ");
986 var end1 = std.math.min(offset + n, offset + 8);
987 for (bytes[offset..end1]) |b| {
988 try out.writeAll(" ");
989 try printHexValue(out, b, 2);
990 }
991 var end2 = offset + n;
992 if (end2 > end1) {
993 try out.writeAll(" ");
994 for (bytes[end1..end2]) |b| {
995 try out.writeAll(" ");
996 try printHexValue(out, b, 2);
997 }
998 }
999 const short = 16 - n;
1000 var i: usize = 0;
1001 while (i < short) : (i += 1) {
1002 try out.writeAll(" ");
1003 }
1004 if (end2 > end1) {
1005 try out.writeAll(" |");
1006 } else {
1007 try out.writeAll(" |");
1008 }
1009 try printCharValues(out, bytes[offset..end2]);
1010 try out.writeAll("|\n");
1011 offset += n;
1012 }
1013
1014 try printDecValue(out, offset, 8);
1015 try out.writeAll(":");
1016 try out.writeAll("\n");
1017}
1018
1019fn hexDump16(out: anytype, offset: usize, bytes: []const u8) !void {
1020 try printDecValue(out, offset, 8);
1021 try out.writeAll(":");
1022 try out.writeAll(" ");
1023 for (bytes[0..8]) |b| {
1024 try out.writeAll(" ");
1025 try printHexValue(out, b, 2);
1026 }
1027 try out.writeAll(" ");
1028 for (bytes[8..16]) |b| {
1029 try out.writeAll(" ");
1030 try printHexValue(out, b, 2);
1031 }
1032 try out.writeAll(" |");
1033 try printCharValues(out, bytes);
1034 try out.writeAll("|\n");
1035}
1036
1037fn printDecValue(out: anytype, value: u64, width: u8) !void {
1038 var buffer: [20]u8 = undefined;
1039 const len = std.fmt.formatIntBuf(buffer[0..], value, 10, .lower, .{ .width = width, .fill = '0' });
1040 try out.writeAll(buffer[0..len]);
1041}
1042
1043fn printHexValue(out: anytype, value: u64, width: u8) !void {
1044 var buffer: [16]u8 = undefined;
1045 const len = std.fmt.formatIntBuf(buffer[0..], value, 16, .lower, .{ .width = width, .fill = '0' });
1046 try out.writeAll(buffer[0..len]);
1047}
1048
1049fn printCharValues(out: anytype, bytes: []const u8) !void {
1050 for (bytes) |b| {
1051 try out.writeAll(&[_]u8{printable_char_tab[b]});
1052 }
1053}
1054
1055fn printUnderstandableChar(out: anytype, char: u8) !void {
1056 if (std.ascii.isPrint(char)) {
1057 try out.print("'{c}'", .{char});
1058 } else {
1059 try out.print("\\x{X:0>2}", .{char});
1060 }
1061}
1062
1063// zig fmt: off
1064const printable_char_tab: [256]u8 = (
1065 "................................ !\"#$%&'()*+,-./0123456789:;<=>?" ++
1066 "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~." ++
1067 "................................................................" ++
1068 "................................................................"
1069).*;
lib/std/Build/CompileStep.zig+46-43
...@@ -83,7 +83,7 @@ max_memory: ?u64 = null,...@@ -83,7 +83,7 @@ max_memory: ?u64 = null,
83shared_memory: bool = false,83shared_memory: bool = false,
84global_base: ?u64 = null,84global_base: ?u64 = null,
85c_std: std.Build.CStd,85c_std: std.Build.CStd,
86override_lib_dir: ?[]const u8,86zig_lib_dir: ?[]const u8,
87main_pkg_path: ?[]const u8,87main_pkg_path: ?[]const u8,
88exec_cmd_args: ?[]const ?[]const u8,88exec_cmd_args: ?[]const ?[]const u8,
89name_prefix: []const u8,89name_prefix: []const u8,
...@@ -344,7 +344,7 @@ pub fn create(builder: *std.Build, options: Options) *CompileStep {...@@ -344,7 +344,7 @@ pub fn create(builder: *std.Build, options: Options) *CompileStep {
344 .installed_headers = ArrayList(*Step).init(builder.allocator),344 .installed_headers = ArrayList(*Step).init(builder.allocator),
345 .object_src = undefined,345 .object_src = undefined,
346 .c_std = std.Build.CStd.C99,346 .c_std = std.Build.CStd.C99,
347 .override_lib_dir = null,347 .zig_lib_dir = null,
348 .main_pkg_path = null,348 .main_pkg_path = null,
349 .exec_cmd_args = null,349 .exec_cmd_args = null,
350 .name_prefix = "",350 .name_prefix = "",
...@@ -442,6 +442,26 @@ pub fn installHeader(a: *CompileStep, src_path: []const u8, dest_rel_path: []con...@@ -442,6 +442,26 @@ pub fn installHeader(a: *CompileStep, src_path: []const u8, dest_rel_path: []con
442 a.installed_headers.append(&install_file.step) catch @panic("OOM");442 a.installed_headers.append(&install_file.step) catch @panic("OOM");
443}443}
444444
445pub const InstallConfigHeaderOptions = struct {
446 install_dir: InstallDir = .header,
447 dest_rel_path: ?[]const u8 = null,
448};
449
450pub fn installConfigHeader(
451 cs: *CompileStep,
452 config_header: *ConfigHeaderStep,
453 options: InstallConfigHeaderOptions,
454) void {
455 const dest_rel_path = options.dest_rel_path orelse config_header.include_path;
456 const install_file = cs.builder.addInstallFileWithDir(
457 .{ .generated = &config_header.output_file },
458 options.install_dir,
459 dest_rel_path,
460 );
461 cs.builder.getInstallStep().dependOn(&install_file.step);
462 cs.installed_headers.append(&install_file.step) catch @panic("OOM");
463}
464
445pub fn installHeadersDirectory(465pub fn installHeadersDirectory(
446 a: *CompileStep,466 a: *CompileStep,
447 src_dir_path: []const u8,467 src_dir_path: []const u8,
...@@ -486,26 +506,11 @@ pub fn installLibraryHeaders(a: *CompileStep, l: *CompileStep) void {...@@ -486,26 +506,11 @@ pub fn installLibraryHeaders(a: *CompileStep, l: *CompileStep) void {
486 a.installed_headers.appendSlice(l.installed_headers.items) catch @panic("OOM");506 a.installed_headers.appendSlice(l.installed_headers.items) catch @panic("OOM");
487}507}
488508
489/// Creates a `RunStep` with an executable built with `addExecutable`.509/// Deprecated: use `std.Build.addRunArtifact`
490/// Add command line arguments with `addArg`.510/// This function will run in the context of the package that created the executable,
511/// which is undesirable when running an executable provided by a dependency package.
491pub fn run(exe: *CompileStep) *RunStep {512pub fn run(exe: *CompileStep) *RunStep {
492 assert(exe.kind == .exe or exe.kind == .test_exe);513 return exe.builder.addRunArtifact(exe);
493
494 // It doesn't have to be native. We catch that if you actually try to run it.
495 // Consider that this is declarative; the run step may not be run unless a user
496 // option is supplied.
497 const run_step = RunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}));
498 run_step.addArtifactArg(exe);
499
500 if (exe.kind == .test_exe) {
501 run_step.addArg(exe.builder.zig_exe);
502 }
503
504 if (exe.vcpkg_bin_path) |path| {
505 run_step.addPathDir(path);
506 }
507
508 return run_step;
509}514}
510515
511/// Creates an `EmulatableRunStep` with an executable built with `addExecutable`.516/// Creates an `EmulatableRunStep` with an executable built with `addExecutable`.
...@@ -852,7 +857,7 @@ pub fn setVerboseCC(self: *CompileStep, value: bool) void {...@@ -852,7 +857,7 @@ pub fn setVerboseCC(self: *CompileStep, value: bool) void {
852}857}
853858
854pub fn overrideZigLibDir(self: *CompileStep, dir_path: []const u8) void {859pub fn overrideZigLibDir(self: *CompileStep, dir_path: []const u8) void {
855 self.override_lib_dir = self.builder.dupePath(dir_path);860 self.zig_lib_dir = self.builder.dupePath(dir_path);
856}861}
857862
858pub fn setMainPkgPath(self: *CompileStep, dir_path: []const u8) void {863pub fn setMainPkgPath(self: *CompileStep, dir_path: []const u8) void {
...@@ -1345,10 +1350,10 @@ fn make(step: *Step) !void {...@@ -1345,10 +1350,10 @@ fn make(step: *Step) !void {
1345 }1350 }
13461351
1347 try zig_args.append("--cache-dir");1352 try zig_args.append("--cache-dir");
1348 try zig_args.append(builder.pathFromRoot(builder.cache_root));1353 try zig_args.append(builder.cache_root.path orelse ".");
13491354
1350 try zig_args.append("--global-cache-dir");1355 try zig_args.append("--global-cache-dir");
1351 try zig_args.append(builder.pathFromRoot(builder.global_cache_root));1356 try zig_args.append(builder.global_cache_root.path orelse ".");
13521357
1353 try zig_args.append("--name");1358 try zig_args.append("--name");
1354 try zig_args.append(self.name);1359 try zig_args.append(self.name);
...@@ -1622,8 +1627,9 @@ fn make(step: *Step) !void {...@@ -1622,8 +1627,9 @@ fn make(step: *Step) !void {
1622 }1627 }
1623 },1628 },
1624 .config_header_step => |config_header| {1629 .config_header_step => |config_header| {
1625 try zig_args.append("-I");1630 const full_file_path = config_header.output_file.path.?;
1626 try zig_args.append(config_header.output_dir);1631 const header_dir_path = full_file_path[0 .. full_file_path.len - config_header.include_path.len];
1632 try zig_args.appendSlice(&.{ "-I", header_dir_path });
1627 },1633 },
1628 }1634 }
1629 }1635 }
...@@ -1697,12 +1703,12 @@ fn make(step: *Step) !void {...@@ -1697,12 +1703,12 @@ fn make(step: *Step) !void {
1697 try addFlag(&zig_args, "each-lib-rpath", self.each_lib_rpath);1703 try addFlag(&zig_args, "each-lib-rpath", self.each_lib_rpath);
1698 try addFlag(&zig_args, "build-id", self.build_id);1704 try addFlag(&zig_args, "build-id", self.build_id);
16991705
1700 if (self.override_lib_dir) |dir| {1706 if (self.zig_lib_dir) |dir| {
1701 try zig_args.append("--zig-lib-dir");1707 try zig_args.append("--zig-lib-dir");
1702 try zig_args.append(builder.pathFromRoot(dir));1708 try zig_args.append(builder.pathFromRoot(dir));
1703 } else if (builder.override_lib_dir) |dir| {1709 } else if (builder.zig_lib_dir) |dir| {
1704 try zig_args.append("--zig-lib-dir");1710 try zig_args.append("--zig-lib-dir");
1705 try zig_args.append(builder.pathFromRoot(dir));1711 try zig_args.append(dir);
1706 }1712 }
17071713
1708 if (self.main_pkg_path) |dir| {1714 if (self.main_pkg_path) |dir| {
...@@ -1739,23 +1745,15 @@ fn make(step: *Step) !void {...@@ -1739,23 +1745,15 @@ fn make(step: *Step) !void {
1739 args_length += arg.len + 1; // +1 to account for null terminator1745 args_length += arg.len + 1; // +1 to account for null terminator
1740 }1746 }
1741 if (args_length >= 30 * 1024) {1747 if (args_length >= 30 * 1024) {
1742 const args_dir = try fs.path.join(1748 try builder.cache_root.handle.makePath("args");
1743 builder.allocator,
1744 &[_][]const u8{ builder.pathFromRoot("zig-cache"), "args" },
1745 );
1746 try std.fs.cwd().makePath(args_dir);
1747
1748 var args_arena = std.heap.ArenaAllocator.init(builder.allocator);
1749 defer args_arena.deinit();
17501749
1751 const args_to_escape = zig_args.items[2..];1750 const args_to_escape = zig_args.items[2..];
1752 var escaped_args = try ArrayList([]const u8).initCapacity(args_arena.allocator(), args_to_escape.len);1751 var escaped_args = try ArrayList([]const u8).initCapacity(builder.allocator, args_to_escape.len);
1753
1754 arg_blk: for (args_to_escape) |arg| {1752 arg_blk: for (args_to_escape) |arg| {
1755 for (arg) |c, arg_idx| {1753 for (arg) |c, arg_idx| {
1756 if (c == '\\' or c == '"') {1754 if (c == '\\' or c == '"') {
1757 // Slow path for arguments that need to be escaped. We'll need to allocate and copy1755 // Slow path for arguments that need to be escaped. We'll need to allocate and copy
1758 var escaped = try ArrayList(u8).initCapacity(args_arena.allocator(), arg.len + 1);1756 var escaped = try ArrayList(u8).initCapacity(builder.allocator, arg.len + 1);
1759 const writer = escaped.writer();1757 const writer = escaped.writer();
1760 try writer.writeAll(arg[0..arg_idx]);1758 try writer.writeAll(arg[0..arg_idx]);
1761 for (arg[arg_idx..]) |to_escape| {1759 for (arg[arg_idx..]) |to_escape| {
...@@ -1783,11 +1781,16 @@ fn make(step: *Step) !void {...@@ -1783,11 +1781,16 @@ fn make(step: *Step) !void {
1783 .{std.fmt.fmtSliceHexLower(&args_hash)},1781 .{std.fmt.fmtSliceHexLower(&args_hash)},
1784 );1782 );
17851783
1786 const args_file = try fs.path.join(builder.allocator, &[_][]const u8{ args_dir, args_hex_hash[0..] });1784 const args_file = "args" ++ fs.path.sep_str ++ args_hex_hash;
1787 try std.fs.cwd().writeFile(args_file, args);1785 try builder.cache_root.handle.writeFile(args_file, args);
1786
1787 const resolved_args_file = try mem.concat(builder.allocator, u8, &.{
1788 "@",
1789 try builder.cache_root.join(builder.allocator, &.{args_file}),
1790 });
17881791
1789 zig_args.shrinkRetainingCapacity(2);1792 zig_args.shrinkRetainingCapacity(2);
1790 try zig_args.append(try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "@", args_file }));1793 try zig_args.append(resolved_args_file);
1791 }1794 }
17921795
1793 const output_dir_nl = try builder.execFromStep(zig_args.items, &self.step);1796 const output_dir_nl = try builder.execFromStep(zig_args.items, &self.step);
lib/std/Build/ConfigHeaderStep.zig+189-60
...@@ -4,13 +4,24 @@ const Step = std.Build.Step;...@@ -4,13 +4,24 @@ const Step = std.Build.Step;
44
5pub const base_id: Step.Id = .config_header;5pub const base_id: Step.Id = .config_header;
66
7pub const Style = enum {7pub const Style = union(enum) {
8 /// The configure format supported by autotools. It uses `#undef foo` to8 /// The configure format supported by autotools. It uses `#undef foo` to
9 /// mark lines that can be substituted with different values.9 /// mark lines that can be substituted with different values.
10 autoconf,10 autoconf: std.Build.FileSource,
11 /// The configure format supported by CMake. It uses `@@FOO@@` and11 /// The configure format supported by CMake. It uses `@@FOO@@` and
12 /// `#cmakedefine` for template substitution.12 /// `#cmakedefine` for template substitution.
13 cmake,13 cmake: std.Build.FileSource,
14 /// Instead of starting with an input file, start with nothing.
15 blank,
16 /// Start with nothing, like blank, and output a nasm .asm file.
17 nasm,
18
19 pub fn getFileSource(style: Style) ?std.Build.FileSource {
20 switch (style) {
21 .autoconf, .cmake => |s| return s,
22 .blank, .nasm => return null,
23 }
24 }
14};25};
1526
16pub const Value = union(enum) {27pub const Value = union(enum) {
...@@ -24,34 +35,50 @@ pub const Value = union(enum) {...@@ -24,34 +35,50 @@ pub const Value = union(enum) {
2435
25step: Step,36step: Step,
26builder: *std.Build,37builder: *std.Build,
27source: std.Build.FileSource,38values: std.StringArrayHashMap(Value),
39output_file: std.Build.GeneratedFile,
40
28style: Style,41style: Style,
29values: std.StringHashMap(Value),42max_bytes: usize,
30max_bytes: usize = 2 * 1024 * 1024,43include_path: []const u8,
31output_dir: []const u8,44
32output_basename: []const u8,45pub const Options = struct {
46 style: Style = .blank,
47 max_bytes: usize = 2 * 1024 * 1024,
48 include_path: ?[]const u8 = null,
49};
3350
34pub fn create(builder: *std.Build, source: std.Build.FileSource, style: Style) *ConfigHeaderStep {51pub fn create(builder: *std.Build, options: Options) *ConfigHeaderStep {
35 const self = builder.allocator.create(ConfigHeaderStep) catch @panic("OOM");52 const self = builder.allocator.create(ConfigHeaderStep) catch @panic("OOM");
36 const name = builder.fmt("configure header {s}", .{source.getDisplayName()});53 const name = if (options.style.getFileSource()) |s|
54 builder.fmt("configure {s} header {s}", .{ @tagName(options.style), s.getDisplayName() })
55 else
56 builder.fmt("configure {s} header", .{@tagName(options.style)});
37 self.* = .{57 self.* = .{
38 .builder = builder,58 .builder = builder,
39 .step = Step.init(base_id, name, builder.allocator, make),59 .step = Step.init(base_id, name, builder.allocator, make),
40 .source = source,60 .style = options.style,
41 .style = style,61 .values = std.StringArrayHashMap(Value).init(builder.allocator),
42 .values = std.StringHashMap(Value).init(builder.allocator),62
43 .output_dir = undefined,63 .max_bytes = options.max_bytes,
44 .output_basename = "config.h",64 .include_path = "config.h",
65 .output_file = .{ .step = &self.step },
45 };66 };
46 switch (source) {67
68 if (options.style.getFileSource()) |s| switch (s) {
47 .path => |p| {69 .path => |p| {
48 const basename = std.fs.path.basename(p);70 const basename = std.fs.path.basename(p);
49 if (std.mem.endsWith(u8, basename, ".h.in")) {71 if (std.mem.endsWith(u8, basename, ".h.in")) {
50 self.output_basename = basename[0 .. basename.len - 3];72 self.include_path = basename[0 .. basename.len - 3];
51 }73 }
52 },74 },
53 else => {},75 else => {},
76 };
77
78 if (options.include_path) |include_path| {
79 self.include_path = include_path;
54 }80 }
81
55 return self;82 return self;
56}83}
5784
...@@ -59,6 +86,10 @@ pub fn addValues(self: *ConfigHeaderStep, values: anytype) void {...@@ -59,6 +86,10 @@ pub fn addValues(self: *ConfigHeaderStep, values: anytype) void {
59 return addValuesInner(self, values) catch @panic("OOM");86 return addValuesInner(self, values) catch @panic("OOM");
60}87}
6188
89pub fn getFileSource(self: *ConfigHeaderStep) std.Build.FileSource {
90 return .{ .generated = &self.output_file };
91}
92
62fn addValuesInner(self: *ConfigHeaderStep, values: anytype) !void {93fn addValuesInner(self: *ConfigHeaderStep, values: anytype) !void {
63 inline for (@typeInfo(@TypeOf(values)).Struct.fields) |field| {94 inline for (@typeInfo(@TypeOf(values)).Struct.fields) |field| {
64 try putValue(self, field.name, field.type, @field(values, field.name));95 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...@@ -100,6 +131,12 @@ fn putValue(self: *ConfigHeaderStep, field_name: []const u8, comptime T: type, v
100 return;131 return;
101 }132 }
102 },133 },
134 .Int => {
135 if (ptr.size == .Slice and ptr.child == u8) {
136 try self.values.put(field_name, .{ .string = v });
137 return;
138 }
139 },
103 else => {},140 else => {},
104 }141 }
105142
...@@ -112,8 +149,6 @@ fn putValue(self: *ConfigHeaderStep, field_name: []const u8, comptime T: type, v...@@ -112,8 +149,6 @@ fn putValue(self: *ConfigHeaderStep, field_name: []const u8, comptime T: type, v
112fn make(step: *Step) !void {149fn make(step: *Step) !void {
113 const self = @fieldParentPtr(ConfigHeaderStep, "step", step);150 const self = @fieldParentPtr(ConfigHeaderStep, "step", step);
114 const gpa = self.builder.allocator;151 const gpa = self.builder.allocator;
115 const src_path = self.source.getPath(self.builder);
116 const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes);
117152
118 // The cache is used here not really as a way to speed things up - because writing153 // The cache is used here not really as a way to speed things up - because writing
119 // the data to a file would probably be very fast - but as a way to find a canonical154 // 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 {...@@ -130,9 +165,39 @@ fn make(step: *Step) !void {
130 // Random bytes to make ConfigHeaderStep unique. Refresh this with new165 // Random bytes to make ConfigHeaderStep unique. Refresh this with new
131 // random bytes when ConfigHeaderStep implementation is modified in a166 // random bytes when ConfigHeaderStep implementation is modified in a
132 // non-backwards-compatible way.167 // non-backwards-compatible way.
133 var hash = Hasher.init("X1pQzdDt91Zlh7Eh");168 var hash = Hasher.init("PGuDTpidxyMqnkGM");
134 hash.update(self.source.getDisplayName());169
135 hash.update(contents);170 var output = std.ArrayList(u8).init(gpa);
171 defer output.deinit();
172
173 const header_text = "This file was generated by ConfigHeaderStep using the Zig Build System.";
174 const c_generated_line = "/* " ++ header_text ++ " */\n";
175 const asm_generated_line = "; " ++ header_text ++ "\n";
176
177 switch (self.style) {
178 .autoconf => |file_source| {
179 try output.appendSlice(c_generated_line);
180 const src_path = file_source.getPath(self.builder);
181 const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes);
182 try render_autoconf(contents, &output, self.values, src_path);
183 },
184 .cmake => |file_source| {
185 try output.appendSlice(c_generated_line);
186 const src_path = file_source.getPath(self.builder);
187 const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes);
188 try render_cmake(contents, &output, self.values, src_path);
189 },
190 .blank => {
191 try output.appendSlice(c_generated_line);
192 try render_blank(&output, self.values, self.include_path);
193 },
194 .nasm => {
195 try output.appendSlice(asm_generated_line);
196 try render_nasm(&output, self.values);
197 },
198 }
199
200 hash.update(output.items);
136201
137 var digest: [16]u8 = undefined;202 var digest: [16]u8 = undefined;
138 hash.final(&digest);203 hash.final(&digest);
...@@ -143,38 +208,40 @@ fn make(step: *Step) !void {...@@ -143,38 +208,40 @@ fn make(step: *Step) !void {
143 .{std.fmt.fmtSliceHexLower(&digest)},208 .{std.fmt.fmtSliceHexLower(&digest)},
144 ) catch unreachable;209 ) catch unreachable;
145210
146 self.output_dir = try std.fs.path.join(gpa, &[_][]const u8{211 const output_dir = try self.builder.cache_root.join(gpa, &.{ "o", &hash_basename });
147 self.builder.cache_root, "o", &hash_basename,212
148 });213 // If output_path has directory parts, deal with them. Example:
149 var dir = std.fs.cwd().makeOpenPath(self.output_dir, .{}) catch |err| {214 // output_dir is zig-cache/o/HASH
150 std.debug.print("unable to make path {s}: {s}\n", .{ self.output_dir, @errorName(err) });215 // output_path is libavutil/avconfig.h
216 // We want to open directory zig-cache/o/HASH/libavutil/
217 // but keep output_dir as zig-cache/o/HASH for -I include
218 const sub_dir_path = if (std.fs.path.dirname(self.include_path)) |d|
219 try std.fs.path.join(gpa, &.{ output_dir, d })
220 else
221 output_dir;
222
223 var dir = std.fs.cwd().makeOpenPath(sub_dir_path, .{}) catch |err| {
224 std.debug.print("unable to make path {s}: {s}\n", .{ output_dir, @errorName(err) });
151 return err;225 return err;
152 };226 };
153 defer dir.close();227 defer dir.close();
154228
155 var values_copy = try self.values.clone();229 try dir.writeFile(std.fs.path.basename(self.include_path), output.items);
156 defer values_copy.deinit();
157
158 var output = std.ArrayList(u8).init(gpa);
159 defer output.deinit();
160 try output.ensureTotalCapacity(contents.len);
161
162 try output.appendSlice("/* This file was generated by ConfigHeaderStep using the Zig Build System. */\n");
163230
164 switch (self.style) {231 self.output_file.path = try std.fs.path.join(self.builder.allocator, &.{
165 .autoconf => try render_autoconf(contents, &output, &values_copy, src_path),232 output_dir, self.include_path,
166 .cmake => try render_cmake(contents, &output, &values_copy, src_path),233 });
167 }
168
169 try dir.writeFile(self.output_basename, output.items);
170}234}
171235
172fn render_autoconf(236fn render_autoconf(
173 contents: []const u8,237 contents: []const u8,
174 output: *std.ArrayList(u8),238 output: *std.ArrayList(u8),
175 values_copy: *std.StringHashMap(Value),239 values: std.StringArrayHashMap(Value),
176 src_path: []const u8,240 src_path: []const u8,
177) !void {241) !void {
242 var values_copy = try values.clone();
243 defer values_copy.deinit();
244
178 var any_errors = false;245 var any_errors = false;
179 var line_index: u32 = 0;246 var line_index: u32 = 0;
180 var line_it = std.mem.split(u8, contents, "\n");247 var line_it = std.mem.split(u8, contents, "\n");
...@@ -192,22 +259,18 @@ fn render_autoconf(...@@ -192,22 +259,18 @@ fn render_autoconf(
192 continue;259 continue;
193 }260 }
194 const name = it.rest();261 const name = it.rest();
195 const kv = values_copy.fetchRemove(name) orelse {262 const kv = values_copy.fetchSwapRemove(name) orelse {
196 std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{263 std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{
197 src_path, line_index + 1, name,264 src_path, line_index + 1, name,
198 });265 });
199 any_errors = true;266 any_errors = true;
200 continue;267 continue;
201 };268 };
202 try renderValue(output, name, kv.value);269 try renderValueC(output, name, kv.value);
203 }270 }
204271
205 {272 for (values_copy.keys()) |name| {
206 var it = values_copy.iterator();273 std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name });
207 while (it.next()) |entry| {
208 const name = entry.key_ptr.*;
209 std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name });
210 }
211 }274 }
212275
213 if (any_errors) {276 if (any_errors) {
...@@ -218,9 +281,12 @@ fn render_autoconf(...@@ -218,9 +281,12 @@ fn render_autoconf(
218fn render_cmake(281fn render_cmake(
219 contents: []const u8,282 contents: []const u8,
220 output: *std.ArrayList(u8),283 output: *std.ArrayList(u8),
221 values_copy: *std.StringHashMap(Value),284 values: std.StringArrayHashMap(Value),
222 src_path: []const u8,285 src_path: []const u8,
223) !void {286) !void {
287 var values_copy = try values.clone();
288 defer values_copy.deinit();
289
224 var any_errors = false;290 var any_errors = false;
225 var line_index: u32 = 0;291 var line_index: u32 = 0;
226 var line_it = std.mem.split(u8, contents, "\n");292 var line_it = std.mem.split(u8, contents, "\n");
...@@ -244,22 +310,18 @@ fn render_cmake(...@@ -244,22 +310,18 @@ fn render_cmake(
244 any_errors = true;310 any_errors = true;
245 continue;311 continue;
246 };312 };
247 const kv = values_copy.fetchRemove(name) orelse {313 const kv = values_copy.fetchSwapRemove(name) orelse {
248 std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{314 std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{
249 src_path, line_index + 1, name,315 src_path, line_index + 1, name,
250 });316 });
251 any_errors = true;317 any_errors = true;
252 continue;318 continue;
253 };319 };
254 try renderValue(output, name, kv.value);320 try renderValueC(output, name, kv.value);
255 }321 }
256322
257 {323 for (values_copy.keys()) |name| {
258 var it = values_copy.iterator();324 std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name });
259 while (it.next()) |entry| {
260 const name = entry.key_ptr.*;
261 std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name });
262 }
263 }325 }
264326
265 if (any_errors) {327 if (any_errors) {
...@@ -267,7 +329,44 @@ fn render_cmake(...@@ -267,7 +329,44 @@ fn render_cmake(
267 }329 }
268}330}
269331
270fn renderValue(output: *std.ArrayList(u8), name: []const u8, value: Value) !void {332fn render_blank(
333 output: *std.ArrayList(u8),
334 defines: std.StringArrayHashMap(Value),
335 include_path: []const u8,
336) !void {
337 const include_guard_name = try output.allocator.dupe(u8, include_path);
338 for (include_guard_name) |*byte| {
339 switch (byte.*) {
340 'a'...'z' => byte.* = byte.* - 'a' + 'A',
341 'A'...'Z', '0'...'9' => continue,
342 else => byte.* = '_',
343 }
344 }
345
346 try output.appendSlice("#ifndef ");
347 try output.appendSlice(include_guard_name);
348 try output.appendSlice("\n#define ");
349 try output.appendSlice(include_guard_name);
350 try output.appendSlice("\n");
351
352 const values = defines.values();
353 for (defines.keys()) |name, i| {
354 try renderValueC(output, name, values[i]);
355 }
356
357 try output.appendSlice("#endif /* ");
358 try output.appendSlice(include_guard_name);
359 try output.appendSlice(" */\n");
360}
361
362fn render_nasm(output: *std.ArrayList(u8), defines: std.StringArrayHashMap(Value)) !void {
363 const values = defines.values();
364 for (defines.keys()) |name, i| {
365 try renderValueNasm(output, name, values[i]);
366 }
367}
368
369fn renderValueC(output: *std.ArrayList(u8), name: []const u8, value: Value) !void {
271 switch (value) {370 switch (value) {
272 .undef => {371 .undef => {
273 try output.appendSlice("/* #undef ");372 try output.appendSlice("/* #undef ");
...@@ -297,3 +396,33 @@ fn renderValue(output: *std.ArrayList(u8), name: []const u8, value: Value) !void...@@ -297,3 +396,33 @@ fn renderValue(output: *std.ArrayList(u8), name: []const u8, value: Value) !void
297 },396 },
298 }397 }
299}398}
399
400fn renderValueNasm(output: *std.ArrayList(u8), name: []const u8, value: Value) !void {
401 switch (value) {
402 .undef => {
403 try output.appendSlice("; %undef ");
404 try output.appendSlice(name);
405 try output.appendSlice("\n");
406 },
407 .defined => {
408 try output.appendSlice("%define ");
409 try output.appendSlice(name);
410 try output.appendSlice("\n");
411 },
412 .boolean => |b| {
413 try output.appendSlice("%define ");
414 try output.appendSlice(name);
415 try output.appendSlice(if (b) " 1\n" else " 0\n");
416 },
417 .int => |i| {
418 try output.writer().print("%define {s} {d}\n", .{ name, i });
419 },
420 .ident => |ident| {
421 try output.writer().print("%define {s} {s}\n", .{ name, ident });
422 },
423 .string => |string| {
424 // TODO: use nasm-specific escaping instead of zig string literals
425 try output.writer().print("%define {s} \"{}\"\n", .{ name, std.zig.fmtEscapes(string) });
426 },
427 }
428}
lib/std/Build/OptionsStep.zig+17-17
...@@ -234,26 +234,20 @@ fn make(step: *Step) !void {...@@ -234,26 +234,20 @@ fn make(step: *Step) !void {
234 );234 );
235 }235 }
236236
237 const options_directory = self.builder.pathFromRoot(237 var options_dir = try self.builder.cache_root.handle.makeOpenPath("options", .{});
238 try fs.path.join(238 defer options_dir.close();
239 self.builder.allocator,
240 &[_][]const u8{ self.builder.cache_root, "options" },
241 ),
242 );
243
244 try fs.cwd().makePath(options_directory);
245239
246 const options_file = try fs.path.join(240 const basename = self.hashContentsToFileName();
247 self.builder.allocator,
248 &[_][]const u8{ options_directory, &self.hashContentsToFileName() },
249 );
250241
251 try fs.cwd().writeFile(options_file, self.contents.items);242 try options_dir.writeFile(&basename, self.contents.items);
252243
253 self.generated_file.path = options_file;244 self.generated_file.path = try self.builder.cache_root.join(self.builder.allocator, &.{
245 "options", &basename,
246 });
254}247}
255248
256fn hashContentsToFileName(self: *OptionsStep) [64]u8 {249fn hashContentsToFileName(self: *OptionsStep) [64]u8 {
250 // TODO update to use the cache system instead of this
257 // This implementation is copied from `WriteFileStep.make`251 // This implementation is copied from `WriteFileStep.make`
258252
259 var hash = std.crypto.hash.blake2.Blake2b384.init(.{});253 var hash = std.crypto.hash.blake2.Blake2b384.init(.{});
...@@ -289,13 +283,19 @@ test "OptionsStep" {...@@ -289,13 +283,19 @@ test "OptionsStep" {
289283
290 const host = try std.zig.system.NativeTargetInfo.detect(.{});284 const host = try std.zig.system.NativeTargetInfo.detect(.{});
291285
286 var cache: std.Build.Cache = .{
287 .gpa = arena.allocator(),
288 .manifest_dir = std.fs.cwd(),
289 };
290
292 var builder = try std.Build.create(291 var builder = try std.Build.create(
293 arena.allocator(),292 arena.allocator(),
294 "test",293 "test",
295 "test",294 .{ .path = "test", .handle = std.fs.cwd() },
296 "test",295 .{ .path = "test", .handle = std.fs.cwd() },
297 "test",296 .{ .path = "test", .handle = std.fs.cwd() },
298 host,297 host,
298 &cache,
299 );299 );
300 defer builder.destroy();300 defer builder.destroy();
301301
lib/std/Build/RunStep.zig+133-6
...@@ -39,6 +39,14 @@ expected_exit_code: ?u8 = 0,...@@ -39,6 +39,14 @@ expected_exit_code: ?u8 = 0,
3939
40/// Print the command before running it40/// Print the command before running it
41print: bool,41print: bool,
42/// Controls whether execution is skipped if the output file is up-to-date.
43/// The default is to always run if there is no output file, and to skip
44/// running if all output files are up-to-date.
45condition: enum { output_outdated, always } = .output_outdated,
46
47/// Additional file paths relative to build.zig that, when modified, indicate
48/// that the RunStep should be re-executed.
49extra_file_dependencies: []const []const u8 = &.{},
4250
43pub const StdIoAction = union(enum) {51pub const StdIoAction = union(enum) {
44 inherit,52 inherit,
...@@ -51,6 +59,12 @@ pub const Arg = union(enum) {...@@ -51,6 +59,12 @@ pub const Arg = union(enum) {
51 artifact: *CompileStep,59 artifact: *CompileStep,
52 file_source: std.Build.FileSource,60 file_source: std.Build.FileSource,
53 bytes: []u8,61 bytes: []u8,
62 output: Output,
63
64 pub const Output = struct {
65 generated_file: *std.Build.GeneratedFile,
66 basename: []const u8,
67 };
54};68};
5569
56pub fn create(builder: *std.Build, name: []const u8) *RunStep {70pub fn create(builder: *std.Build, name: []const u8) *RunStep {
...@@ -71,6 +85,20 @@ pub fn addArtifactArg(self: *RunStep, artifact: *CompileStep) void {...@@ -71,6 +85,20 @@ pub fn addArtifactArg(self: *RunStep, artifact: *CompileStep) void {
71 self.step.dependOn(&artifact.step);85 self.step.dependOn(&artifact.step);
72}86}
7387
88/// This provides file path as a command line argument to the command being
89/// run, and returns a FileSource which can be used as inputs to other APIs
90/// throughout the build system.
91pub fn addOutputFileArg(rs: *RunStep, basename: []const u8) std.Build.FileSource {
92 const generated_file = rs.builder.allocator.create(std.Build.GeneratedFile) catch @panic("OOM");
93 generated_file.* = .{ .step = &rs.step };
94 rs.argv.append(.{ .output = .{
95 .generated_file = generated_file,
96 .basename = rs.builder.dupe(basename),
97 } }) catch @panic("OOM");
98
99 return .{ .generated = generated_file };
100}
101
74pub fn addFileSourceArg(self: *RunStep, file_source: std.Build.FileSource) void {102pub fn addFileSourceArg(self: *RunStep, file_source: std.Build.FileSource) void {
75 self.argv.append(Arg{103 self.argv.append(Arg{
76 .file_source = file_source.dupe(self.builder),104 .file_source = file_source.dupe(self.builder),
...@@ -159,25 +187,105 @@ fn stdIoActionToBehavior(action: StdIoAction) std.ChildProcess.StdIo {...@@ -159,25 +187,105 @@ fn stdIoActionToBehavior(action: StdIoAction) std.ChildProcess.StdIo {
159 };187 };
160}188}
161189
190fn needOutputCheck(self: RunStep) bool {
191 if (self.extra_file_dependencies.len > 0) return true;
192
193 for (self.argv.items) |arg| switch (arg) {
194 .output => return true,
195 else => continue,
196 };
197
198 return switch (self.condition) {
199 .always => false,
200 .output_outdated => true,
201 };
202}
203
162fn make(step: *Step) !void {204fn make(step: *Step) !void {
163 const self = @fieldParentPtr(RunStep, "step", step);205 const self = @fieldParentPtr(RunStep, "step", step);
206 const need_output_check = self.needOutputCheck();
164207
165 var argv_list = ArrayList([]const u8).init(self.builder.allocator);208 var argv_list = ArrayList([]const u8).init(self.builder.allocator);
209 var output_placeholders = ArrayList(struct {
210 index: usize,
211 output: Arg.Output,
212 }).init(self.builder.allocator);
213
214 var man = self.builder.cache.obtain();
215 defer man.deinit();
216
166 for (self.argv.items) |arg| {217 for (self.argv.items) |arg| {
167 switch (arg) {218 switch (arg) {
168 .bytes => |bytes| try argv_list.append(bytes),219 .bytes => |bytes| {
169 .file_source => |file| try argv_list.append(file.getPath(self.builder)),220 try argv_list.append(bytes);
221 man.hash.addBytes(bytes);
222 },
223 .file_source => |file| {
224 const file_path = file.getPath(self.builder);
225 try argv_list.append(file_path);
226 _ = try man.addFile(file_path, null);
227 },
170 .artifact => |artifact| {228 .artifact => |artifact| {
171 if (artifact.target.isWindows()) {229 if (artifact.target.isWindows()) {
172 // On Windows we don't have rpaths so we have to add .dll search paths to PATH230 // On Windows we don't have rpaths so we have to add .dll search paths to PATH
173 self.addPathForDynLibs(artifact);231 self.addPathForDynLibs(artifact);
174 }232 }
175 const executable_path = artifact.installed_path orelse artifact.getOutputSource().getPath(self.builder);233 const file_path = artifact.installed_path orelse
176 try argv_list.append(executable_path);234 artifact.getOutputSource().getPath(self.builder);
235
236 try argv_list.append(file_path);
237
238 _ = try man.addFile(file_path, null);
239 },
240 .output => |output| {
241 man.hash.addBytes(output.basename);
242 // Add a placeholder into the argument list because we need the
243 // manifest hash to be updated with all arguments before the
244 // object directory is computed.
245 try argv_list.append("");
246 try output_placeholders.append(.{
247 .index = argv_list.items.len - 1,
248 .output = output,
249 });
177 },250 },
178 }251 }
179 }252 }
180253
254 if (need_output_check) {
255 for (self.extra_file_dependencies) |file_path| {
256 _ = try man.addFile(self.builder.pathFromRoot(file_path), null);
257 }
258
259 if (man.hit() catch |err| failWithCacheError(man, err)) {
260 // cache hit, skip running command
261 const digest = man.final();
262 for (output_placeholders.items) |placeholder| {
263 placeholder.output.generated_file.path = try self.builder.cache_root.join(
264 self.builder.allocator,
265 &.{ "o", &digest, placeholder.output.basename },
266 );
267 }
268 return;
269 }
270
271 const digest = man.final();
272
273 for (output_placeholders.items) |placeholder| {
274 const output_path = try self.builder.cache_root.join(
275 self.builder.allocator,
276 &.{ "o", &digest, placeholder.output.basename },
277 );
278 const output_dir = fs.path.dirname(output_path).?;
279 fs.cwd().makePath(output_dir) catch |err| {
280 std.debug.print("unable to make path {s}: {s}\n", .{ output_dir, @errorName(err) });
281 return err;
282 };
283
284 placeholder.output.generated_file.path = output_path;
285 argv_list.items[placeholder.index] = output_path;
286 }
287 }
288
181 try runCommand(289 try runCommand(
182 argv_list.items,290 argv_list.items,
183 self.builder,291 self.builder,
...@@ -189,6 +297,10 @@ fn make(step: *Step) !void {...@@ -189,6 +297,10 @@ fn make(step: *Step) !void {
189 self.cwd,297 self.cwd,
190 self.print,298 self.print,
191 );299 );
300
301 if (need_output_check) {
302 try man.writeManifest();
303 }
192}304}
193305
194pub fn runCommand(306pub fn runCommand(
...@@ -202,11 +314,13 @@ pub fn runCommand(...@@ -202,11 +314,13 @@ pub fn runCommand(
202 maybe_cwd: ?[]const u8,314 maybe_cwd: ?[]const u8,
203 print: bool,315 print: bool,
204) !void {316) !void {
205 const cwd = if (maybe_cwd) |cwd| builder.pathFromRoot(cwd) else builder.build_root;317 const cwd = if (maybe_cwd) |cwd| builder.pathFromRoot(cwd) else builder.build_root.path;
206318
207 if (!std.process.can_spawn) {319 if (!std.process.can_spawn) {
208 const cmd = try std.mem.join(builder.allocator, " ", argv);320 const cmd = try std.mem.join(builder.allocator, " ", argv);
209 std.debug.print("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(builtin.os.tag), cmd });321 std.debug.print("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{
322 @tagName(builtin.os.tag), cmd,
323 });
210 builder.allocator.free(cmd);324 builder.allocator.free(cmd);
211 return ExecError.ExecNotSupported;325 return ExecError.ExecNotSupported;
212 }326 }
...@@ -347,6 +461,19 @@ pub fn runCommand(...@@ -347,6 +461,19 @@ pub fn runCommand(
347 }461 }
348}462}
349463
464fn failWithCacheError(man: std.Build.Cache.Manifest, err: anyerror) noreturn {
465 const i = man.failed_file_index orelse failWithSimpleError(err);
466 const pp = man.files.items[i].prefixed_path orelse failWithSimpleError(err);
467 const prefix = man.cache.prefixes()[pp.prefix].path orelse "";
468 std.debug.print("{s}: {s}/{s}\n", .{ @errorName(err), prefix, pp.sub_path });
469 std.process.exit(1);
470}
471
472fn failWithSimpleError(err: anyerror) noreturn {
473 std.debug.print("{s}\n", .{@errorName(err)});
474 std.process.exit(1);
475}
476
350fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void {477fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void {
351 if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd});478 if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd});
352 for (argv) |arg| {479 for (argv) |arg| {
lib/std/Build/TranslateCStep.zig+2-9
...@@ -15,7 +15,6 @@ builder: *std.Build,...@@ -15,7 +15,6 @@ builder: *std.Build,
15source: std.Build.FileSource,15source: std.Build.FileSource,
16include_dirs: std.ArrayList([]const u8),16include_dirs: std.ArrayList([]const u8),
17c_macros: std.ArrayList([]const u8),17c_macros: std.ArrayList([]const u8),
18output_dir: ?[]const u8,
19out_basename: []const u8,18out_basename: []const u8,
20target: CrossTarget,19target: CrossTarget,
21optimize: std.builtin.OptimizeMode,20optimize: std.builtin.OptimizeMode,
...@@ -36,7 +35,6 @@ pub fn create(builder: *std.Build, options: Options) *TranslateCStep {...@@ -36,7 +35,6 @@ pub fn create(builder: *std.Build, options: Options) *TranslateCStep {
36 .source = source,35 .source = source,
37 .include_dirs = std.ArrayList([]const u8).init(builder.allocator),36 .include_dirs = std.ArrayList([]const u8).init(builder.allocator),
38 .c_macros = std.ArrayList([]const u8).init(builder.allocator),37 .c_macros = std.ArrayList([]const u8).init(builder.allocator),
39 .output_dir = null,
40 .out_basename = undefined,38 .out_basename = undefined,
41 .target = options.target,39 .target = options.target,
42 .optimize = options.optimize,40 .optimize = options.optimize,
...@@ -122,15 +120,10 @@ fn make(step: *Step) !void {...@@ -122,15 +120,10 @@ fn make(step: *Step) !void {
122 const output_path = mem.trimRight(u8, output_path_nl, "\r\n");120 const output_path = mem.trimRight(u8, output_path_nl, "\r\n");
123121
124 self.out_basename = fs.path.basename(output_path);122 self.out_basename = fs.path.basename(output_path);
125 if (self.output_dir) |output_dir| {123 const output_dir = fs.path.dirname(output_path).?;
126 const full_dest = try fs.path.join(self.builder.allocator, &[_][]const u8{ output_dir, self.out_basename });
127 try self.builder.updateFile(output_path, full_dest);
128 } else {
129 self.output_dir = fs.path.dirname(output_path).?;
130 }
131124
132 self.output_file.path = try fs.path.join(125 self.output_file.path = try fs.path.join(
133 self.builder.allocator,126 self.builder.allocator,
134 &[_][]const u8{ self.output_dir.?, self.out_basename },127 &[_][]const u8{ output_dir, self.out_basename },
135 );128 );
136}129}
lib/std/Build/WriteFileStep.zig+6-8
...@@ -9,7 +9,6 @@ pub const base_id = .write_file;...@@ -9,7 +9,6 @@ pub const base_id = .write_file;
99
10step: Step,10step: Step,
11builder: *std.Build,11builder: *std.Build,
12output_dir: []const u8,
13files: std.TailQueue(File),12files: std.TailQueue(File),
1413
15pub const File = struct {14pub const File = struct {
...@@ -23,7 +22,6 @@ pub fn init(builder: *std.Build) WriteFileStep {...@@ -23,7 +22,6 @@ pub fn init(builder: *std.Build) WriteFileStep {
23 .builder = builder,22 .builder = builder,
24 .step = Step.init(.write_file, "writefile", builder.allocator, make),23 .step = Step.init(.write_file, "writefile", builder.allocator, make),
25 .files = .{},24 .files = .{},
26 .output_dir = undefined,
27 };25 };
28}26}
2927
...@@ -87,11 +85,11 @@ fn make(step: *Step) !void {...@@ -87,11 +85,11 @@ fn make(step: *Step) !void {
87 .{std.fmt.fmtSliceHexLower(&digest)},85 .{std.fmt.fmtSliceHexLower(&digest)},
88 ) catch unreachable;86 ) catch unreachable;
8987
90 self.output_dir = try fs.path.join(self.builder.allocator, &[_][]const u8{88 const output_dir = try self.builder.cache_root.join(self.builder.allocator, &.{
91 self.builder.cache_root, "o", &hash_basename,89 "o", &hash_basename,
92 });90 });
93 var dir = fs.cwd().makeOpenPath(self.output_dir, .{}) catch |err| {91 var dir = fs.cwd().makeOpenPath(output_dir, .{}) catch |err| {
94 std.debug.print("unable to make path {s}: {s}\n", .{ self.output_dir, @errorName(err) });92 std.debug.print("unable to make path {s}: {s}\n", .{ output_dir, @errorName(err) });
95 return err;93 return err;
96 };94 };
97 defer dir.close();95 defer dir.close();
...@@ -101,14 +99,14 @@ fn make(step: *Step) !void {...@@ -101,14 +99,14 @@ fn make(step: *Step) !void {
101 dir.writeFile(node.data.basename, node.data.bytes) catch |err| {99 dir.writeFile(node.data.basename, node.data.bytes) catch |err| {
102 std.debug.print("unable to write {s} into {s}: {s}\n", .{100 std.debug.print("unable to write {s} into {s}: {s}\n", .{
103 node.data.basename,101 node.data.basename,
104 self.output_dir,102 output_dir,
105 @errorName(err),103 @errorName(err),
106 });104 });
107 return err;105 return err;
108 };106 };
109 node.data.source.path = try fs.path.join(107 node.data.source.path = try fs.path.join(
110 self.builder.allocator,108 self.builder.allocator,
111 &[_][]const u8{ self.output_dir, node.data.basename },109 &[_][]const u8{ output_dir, node.data.basename },
112 );110 );
113 }111 }
114 }112 }
lib/std/c.zig+1
...@@ -173,6 +173,7 @@ pub extern "c" fn readlink(noalias path: [*:0]const u8, noalias buf: [*]u8, bufs...@@ -173,6 +173,7 @@ pub extern "c" fn readlink(noalias path: [*:0]const u8, noalias buf: [*]u8, bufs
173pub extern "c" fn readlinkat(dirfd: c.fd_t, noalias path: [*:0]const u8, noalias buf: [*]u8, bufsize: usize) isize;173pub extern "c" fn readlinkat(dirfd: c.fd_t, noalias path: [*:0]const u8, noalias buf: [*]u8, bufsize: usize) isize;
174pub extern "c" fn fchmod(fd: c.fd_t, mode: c.mode_t) c_int;174pub extern "c" fn fchmod(fd: c.fd_t, mode: c.mode_t) c_int;
175pub extern "c" fn fchown(fd: c.fd_t, owner: c.uid_t, group: c.gid_t) c_int;175pub extern "c" fn fchown(fd: c.fd_t, owner: c.uid_t, group: c.gid_t) c_int;
176pub extern "c" fn umask(mode: c.mode_t) c.mode_t;
176177
177pub extern "c" fn rmdir(path: [*:0]const u8) c_int;178pub extern "c" fn rmdir(path: [*:0]const u8) c_int;
178pub extern "c" fn getenv(name: [*:0]const u8) ?[*:0]u8;179pub extern "c" fn getenv(name: [*:0]const u8) ?[*:0]u8;
lib/std/child_process.zig+2-2
...@@ -1164,7 +1164,7 @@ fn windowsCreateProcessPathExt(...@@ -1164,7 +1164,7 @@ fn windowsCreateProcessPathExt(
1164 var app_name_unicode_string = windows.UNICODE_STRING{1164 var app_name_unicode_string = windows.UNICODE_STRING{
1165 .Length = app_name_len_bytes,1165 .Length = app_name_len_bytes,
1166 .MaximumLength = app_name_len_bytes,1166 .MaximumLength = app_name_len_bytes,
1167 .Buffer = @qualCast([*:0]u16, app_name_wildcard.ptr),1167 .Buffer = @constCast(app_name_wildcard.ptr),
1168 };1168 };
1169 const rc = windows.ntdll.NtQueryDirectoryFile(1169 const rc = windows.ntdll.NtQueryDirectoryFile(
1170 dir.fd,1170 dir.fd,
...@@ -1261,7 +1261,7 @@ fn windowsCreateProcessPathExt(...@@ -1261,7 +1261,7 @@ fn windowsCreateProcessPathExt(
1261 var app_name_unicode_string = windows.UNICODE_STRING{1261 var app_name_unicode_string = windows.UNICODE_STRING{
1262 .Length = app_name_len_bytes,1262 .Length = app_name_len_bytes,
1263 .MaximumLength = app_name_len_bytes,1263 .MaximumLength = app_name_len_bytes,
1264 .Buffer = @qualCast([*:0]u16, app_name_appended.ptr),1264 .Buffer = @constCast(app_name_appended.ptr),
1265 };1265 };
12661266
1267 // Re-use the directory handle but this time we call with the appended app name1267 // Re-use the directory handle but this time we call with the appended app name
lib/std/compress.zig+6-2
...@@ -2,8 +2,10 @@ const std = @import("std.zig");...@@ -2,8 +2,10 @@ const std = @import("std.zig");
22
3pub const deflate = @import("compress/deflate.zig");3pub const deflate = @import("compress/deflate.zig");
4pub const gzip = @import("compress/gzip.zig");4pub const gzip = @import("compress/gzip.zig");
5pub const zlib = @import("compress/zlib.zig");5pub const lzma = @import("compress/lzma.zig");
6pub const lzma2 = @import("compress/lzma2.zig");
6pub const xz = @import("compress/xz.zig");7pub const xz = @import("compress/xz.zig");
8pub const zlib = @import("compress/zlib.zig");
79
8pub fn HashedReader(10pub fn HashedReader(
9 comptime ReaderType: anytype,11 comptime ReaderType: anytype,
...@@ -38,6 +40,8 @@ pub fn hashedReader(...@@ -38,6 +40,8 @@ pub fn hashedReader(
38test {40test {
39 _ = deflate;41 _ = deflate;
40 _ = gzip;42 _ = gzip;
41 _ = zlib;43 _ = lzma;
44 _ = lzma2;
42 _ = xz;45 _ = xz;
46 _ = zlib;
43}47}
lib/std/compress/lzma.zig created+90
...@@ -0,0 +1,90 @@
1const std = @import("../std.zig");
2const math = std.math;
3const mem = std.mem;
4const Allocator = std.mem.Allocator;
5
6pub const decode = @import("lzma/decode.zig");
7
8pub fn decompress(
9 allocator: Allocator,
10 reader: anytype,
11) !Decompress(@TypeOf(reader)) {
12 return decompressWithOptions(allocator, reader, .{});
13}
14
15pub fn decompressWithOptions(
16 allocator: Allocator,
17 reader: anytype,
18 options: decode.Options,
19) !Decompress(@TypeOf(reader)) {
20 const params = try decode.Params.readHeader(reader, options);
21 return Decompress(@TypeOf(reader)).init(allocator, reader, params, options.memlimit);
22}
23
24pub fn Decompress(comptime ReaderType: type) type {
25 return struct {
26 const Self = @This();
27
28 pub const Error =
29 ReaderType.Error ||
30 Allocator.Error ||
31 error{ CorruptInput, EndOfStream, Overflow };
32
33 pub const Reader = std.io.Reader(*Self, Error, read);
34
35 allocator: Allocator,
36 in_reader: ReaderType,
37 to_read: std.ArrayListUnmanaged(u8),
38
39 buffer: decode.lzbuffer.LzCircularBuffer,
40 decoder: decode.rangecoder.RangeDecoder,
41 state: decode.DecoderState,
42
43 pub fn init(allocator: Allocator, source: ReaderType, params: decode.Params, memlimit: ?usize) !Self {
44 return Self{
45 .allocator = allocator,
46 .in_reader = source,
47 .to_read = .{},
48
49 .buffer = decode.lzbuffer.LzCircularBuffer.init(params.dict_size, memlimit orelse math.maxInt(usize)),
50 .decoder = try decode.rangecoder.RangeDecoder.init(source),
51 .state = try decode.DecoderState.init(allocator, params.properties, params.unpacked_size),
52 };
53 }
54
55 pub fn reader(self: *Self) Reader {
56 return .{ .context = self };
57 }
58
59 pub fn deinit(self: *Self) void {
60 self.to_read.deinit(self.allocator);
61 self.buffer.deinit(self.allocator);
62 self.state.deinit(self.allocator);
63 self.* = undefined;
64 }
65
66 pub fn read(self: *Self, output: []u8) Error!usize {
67 const writer = self.to_read.writer(self.allocator);
68 while (self.to_read.items.len < output.len) {
69 switch (try self.state.process(self.allocator, self.in_reader, writer, &self.buffer, &self.decoder)) {
70 .continue_ => {},
71 .finished => {
72 try self.buffer.finish(writer);
73 break;
74 },
75 }
76 }
77 const input = self.to_read.items;
78 const n = math.min(input.len, output.len);
79 mem.copy(u8, output[0..n], input[0..n]);
80 mem.copy(u8, input, input[n..]);
81 self.to_read.shrinkRetainingCapacity(input.len - n);
82 return n;
83 }
84 };
85}
86
87test {
88 _ = @import("lzma/test.zig");
89 _ = @import("lzma/vec2d.zig");
90}
lib/std/compress/lzma/decode.zig created+379
...@@ -0,0 +1,379 @@
1const std = @import("../../std.zig");
2const assert = std.debug.assert;
3const math = std.math;
4const Allocator = std.mem.Allocator;
5
6pub const lzbuffer = @import("decode/lzbuffer.zig");
7pub const rangecoder = @import("decode/rangecoder.zig");
8
9const LzCircularBuffer = lzbuffer.LzCircularBuffer;
10const BitTree = rangecoder.BitTree;
11const LenDecoder = rangecoder.LenDecoder;
12const RangeDecoder = rangecoder.RangeDecoder;
13const Vec2D = @import("vec2d.zig").Vec2D;
14
15pub const Options = struct {
16 unpacked_size: UnpackedSize = .read_from_header,
17 memlimit: ?usize = null,
18 allow_incomplete: bool = false,
19};
20
21pub const UnpackedSize = union(enum) {
22 read_from_header,
23 read_header_but_use_provided: ?u64,
24 use_provided: ?u64,
25};
26
27const ProcessingStatus = enum {
28 continue_,
29 finished,
30};
31
32pub const Properties = struct {
33 lc: u4,
34 lp: u3,
35 pb: u3,
36
37 fn validate(self: Properties) void {
38 assert(self.lc <= 8);
39 assert(self.lp <= 4);
40 assert(self.pb <= 4);
41 }
42};
43
44pub const Params = struct {
45 properties: Properties,
46 dict_size: u32,
47 unpacked_size: ?u64,
48
49 pub fn readHeader(reader: anytype, options: Options) !Params {
50 var props = try reader.readByte();
51 if (props >= 225) {
52 return error.CorruptInput;
53 }
54
55 const lc = @intCast(u4, props % 9);
56 props /= 9;
57 const lp = @intCast(u3, props % 5);
58 props /= 5;
59 const pb = @intCast(u3, props);
60
61 const dict_size_provided = try reader.readIntLittle(u32);
62 const dict_size = math.max(0x1000, dict_size_provided);
63
64 const unpacked_size = switch (options.unpacked_size) {
65 .read_from_header => blk: {
66 const unpacked_size_provided = try reader.readIntLittle(u64);
67 const marker_mandatory = unpacked_size_provided == 0xFFFF_FFFF_FFFF_FFFF;
68 break :blk if (marker_mandatory)
69 null
70 else
71 unpacked_size_provided;
72 },
73 .read_header_but_use_provided => |x| blk: {
74 _ = try reader.readIntLittle(u64);
75 break :blk x;
76 },
77 .use_provided => |x| x,
78 };
79
80 return Params{
81 .properties = Properties{ .lc = lc, .lp = lp, .pb = pb },
82 .dict_size = dict_size,
83 .unpacked_size = unpacked_size,
84 };
85 }
86};
87
88pub const DecoderState = struct {
89 lzma_props: Properties,
90 unpacked_size: ?u64,
91 literal_probs: Vec2D(u16),
92 pos_slot_decoder: [4]BitTree(6),
93 align_decoder: BitTree(4),
94 pos_decoders: [115]u16,
95 is_match: [192]u16,
96 is_rep: [12]u16,
97 is_rep_g0: [12]u16,
98 is_rep_g1: [12]u16,
99 is_rep_g2: [12]u16,
100 is_rep_0long: [192]u16,
101 state: usize,
102 rep: [4]usize,
103 len_decoder: LenDecoder,
104 rep_len_decoder: LenDecoder,
105
106 pub fn init(
107 allocator: Allocator,
108 lzma_props: Properties,
109 unpacked_size: ?u64,
110 ) !DecoderState {
111 return .{
112 .lzma_props = lzma_props,
113 .unpacked_size = unpacked_size,
114 .literal_probs = try Vec2D(u16).init(allocator, 0x400, .{ @as(usize, 1) << (lzma_props.lc + lzma_props.lp), 0x300 }),
115 .pos_slot_decoder = .{.{}} ** 4,
116 .align_decoder = .{},
117 .pos_decoders = .{0x400} ** 115,
118 .is_match = .{0x400} ** 192,
119 .is_rep = .{0x400} ** 12,
120 .is_rep_g0 = .{0x400} ** 12,
121 .is_rep_g1 = .{0x400} ** 12,
122 .is_rep_g2 = .{0x400} ** 12,
123 .is_rep_0long = .{0x400} ** 192,
124 .state = 0,
125 .rep = .{0} ** 4,
126 .len_decoder = .{},
127 .rep_len_decoder = .{},
128 };
129 }
130
131 pub fn deinit(self: *DecoderState, allocator: Allocator) void {
132 self.literal_probs.deinit(allocator);
133 self.* = undefined;
134 }
135
136 pub fn resetState(self: *DecoderState, allocator: Allocator, new_props: Properties) !void {
137 new_props.validate();
138 if (self.lzma_props.lc + self.lzma_props.lp == new_props.lc + new_props.lp) {
139 self.literal_probs.fill(0x400);
140 } else {
141 self.literal_probs.deinit(allocator);
142 self.literal_probs = try Vec2D(u16).init(allocator, 0x400, .{ @as(usize, 1) << (new_props.lc + new_props.lp), 0x300 });
143 }
144
145 self.lzma_props = new_props;
146 for (self.pos_slot_decoder) |*t| t.reset();
147 self.align_decoder.reset();
148 self.pos_decoders = .{0x400} ** 115;
149 self.is_match = .{0x400} ** 192;
150 self.is_rep = .{0x400} ** 12;
151 self.is_rep_g0 = .{0x400} ** 12;
152 self.is_rep_g1 = .{0x400} ** 12;
153 self.is_rep_g2 = .{0x400} ** 12;
154 self.is_rep_0long = .{0x400} ** 192;
155 self.state = 0;
156 self.rep = .{0} ** 4;
157 self.len_decoder.reset();
158 self.rep_len_decoder.reset();
159 }
160
161 fn processNextInner(
162 self: *DecoderState,
163 allocator: Allocator,
164 reader: anytype,
165 writer: anytype,
166 buffer: anytype,
167 decoder: *RangeDecoder,
168 update: bool,
169 ) !ProcessingStatus {
170 const pos_state = buffer.len & ((@as(usize, 1) << self.lzma_props.pb) - 1);
171
172 if (!try decoder.decodeBit(
173 reader,
174 &self.is_match[(self.state << 4) + pos_state],
175 update,
176 )) {
177 const byte: u8 = try self.decodeLiteral(reader, buffer, decoder, update);
178
179 if (update) {
180 try buffer.appendLiteral(allocator, byte, writer);
181
182 self.state = if (self.state < 4)
183 0
184 else if (self.state < 10)
185 self.state - 3
186 else
187 self.state - 6;
188 }
189 return .continue_;
190 }
191
192 var len: usize = undefined;
193 if (try decoder.decodeBit(reader, &self.is_rep[self.state], update)) {
194 if (!try decoder.decodeBit(reader, &self.is_rep_g0[self.state], update)) {
195 if (!try decoder.decodeBit(
196 reader,
197 &self.is_rep_0long[(self.state << 4) + pos_state],
198 update,
199 )) {
200 if (update) {
201 self.state = if (self.state < 7) 9 else 11;
202 const dist = self.rep[0] + 1;
203 try buffer.appendLz(allocator, 1, dist, writer);
204 }
205 return .continue_;
206 }
207 } else {
208 const idx: usize = if (!try decoder.decodeBit(reader, &self.is_rep_g1[self.state], update))
209 1
210 else if (!try decoder.decodeBit(reader, &self.is_rep_g2[self.state], update))
211 2
212 else
213 3;
214 if (update) {
215 const dist = self.rep[idx];
216 var i = idx;
217 while (i > 0) : (i -= 1) {
218 self.rep[i] = self.rep[i - 1];
219 }
220 self.rep[0] = dist;
221 }
222 }
223
224 len = try self.rep_len_decoder.decode(reader, decoder, pos_state, update);
225
226 if (update) {
227 self.state = if (self.state < 7) 8 else 11;
228 }
229 } else {
230 if (update) {
231 self.rep[3] = self.rep[2];
232 self.rep[2] = self.rep[1];
233 self.rep[1] = self.rep[0];
234 }
235
236 len = try self.len_decoder.decode(reader, decoder, pos_state, update);
237
238 if (update) {
239 self.state = if (self.state < 7) 7 else 10;
240 }
241
242 const rep_0 = try self.decodeDistance(reader, decoder, len, update);
243
244 if (update) {
245 self.rep[0] = rep_0;
246 if (self.rep[0] == 0xFFFF_FFFF) {
247 if (decoder.isFinished()) {
248 return .finished;
249 }
250 return error.CorruptInput;
251 }
252 }
253 }
254
255 if (update) {
256 len += 2;
257
258 const dist = self.rep[0] + 1;
259 try buffer.appendLz(allocator, len, dist, writer);
260 }
261
262 return .continue_;
263 }
264
265 fn processNext(
266 self: *DecoderState,
267 allocator: Allocator,
268 reader: anytype,
269 writer: anytype,
270 buffer: anytype,
271 decoder: *RangeDecoder,
272 ) !ProcessingStatus {
273 return self.processNextInner(allocator, reader, writer, buffer, decoder, true);
274 }
275
276 pub fn process(
277 self: *DecoderState,
278 allocator: Allocator,
279 reader: anytype,
280 writer: anytype,
281 buffer: anytype,
282 decoder: *RangeDecoder,
283 ) !ProcessingStatus {
284 process_next: {
285 if (self.unpacked_size) |unpacked_size| {
286 if (buffer.len >= unpacked_size) {
287 break :process_next;
288 }
289 } else if (decoder.isFinished()) {
290 break :process_next;
291 }
292
293 switch (try self.processNext(allocator, reader, writer, buffer, decoder)) {
294 .continue_ => return .continue_,
295 .finished => break :process_next,
296 }
297 }
298
299 if (self.unpacked_size) |unpacked_size| {
300 if (buffer.len != unpacked_size) {
301 return error.CorruptInput;
302 }
303 }
304
305 return .finished;
306 }
307
308 fn decodeLiteral(
309 self: *DecoderState,
310 reader: anytype,
311 buffer: anytype,
312 decoder: *RangeDecoder,
313 update: bool,
314 ) !u8 {
315 const def_prev_byte = 0;
316 const prev_byte = @as(usize, buffer.lastOr(def_prev_byte));
317
318 var result: usize = 1;
319 const lit_state = ((buffer.len & ((@as(usize, 1) << self.lzma_props.lp) - 1)) << self.lzma_props.lc) +
320 (prev_byte >> (8 - self.lzma_props.lc));
321 const probs = try self.literal_probs.getMut(lit_state);
322
323 if (self.state >= 7) {
324 var match_byte = @as(usize, try buffer.lastN(self.rep[0] + 1));
325
326 while (result < 0x100) {
327 const match_bit = (match_byte >> 7) & 1;
328 match_byte <<= 1;
329 const bit = @boolToInt(try decoder.decodeBit(
330 reader,
331 &probs[((@as(usize, 1) + match_bit) << 8) + result],
332 update,
333 ));
334 result = (result << 1) ^ bit;
335 if (match_bit != bit) {
336 break;
337 }
338 }
339 }
340
341 while (result < 0x100) {
342 result = (result << 1) ^ @boolToInt(try decoder.decodeBit(reader, &probs[result], update));
343 }
344
345 return @truncate(u8, result - 0x100);
346 }
347
348 fn decodeDistance(
349 self: *DecoderState,
350 reader: anytype,
351 decoder: *RangeDecoder,
352 length: usize,
353 update: bool,
354 ) !usize {
355 const len_state = if (length > 3) 3 else length;
356
357 const pos_slot = @as(usize, try self.pos_slot_decoder[len_state].parse(reader, decoder, update));
358 if (pos_slot < 4)
359 return pos_slot;
360
361 const num_direct_bits = @intCast(u5, (pos_slot >> 1) - 1);
362 var result = (2 ^ (pos_slot & 1)) << num_direct_bits;
363
364 if (pos_slot < 14) {
365 result += try decoder.parseReverseBitTree(
366 reader,
367 num_direct_bits,
368 &self.pos_decoders,
369 result - pos_slot,
370 update,
371 );
372 } else {
373 result += @as(usize, try decoder.get(reader, num_direct_bits - 4)) << 4;
374 result += try self.align_decoder.parseReverse(reader, decoder, update);
375 }
376
377 return result;
378 }
379};
lib/std/compress/lzma/decode/lzbuffer.zig created+228
...@@ -0,0 +1,228 @@
1const std = @import("../../../std.zig");
2const math = std.math;
3const mem = std.mem;
4const Allocator = std.mem.Allocator;
5const ArrayListUnmanaged = std.ArrayListUnmanaged;
6
7/// An accumulating buffer for LZ sequences
8pub const LzAccumBuffer = struct {
9 /// Buffer
10 buf: ArrayListUnmanaged(u8),
11
12 /// Buffer memory limit
13 memlimit: usize,
14
15 /// Total number of bytes sent through the buffer
16 len: usize,
17
18 const Self = @This();
19
20 pub fn init(memlimit: usize) Self {
21 return Self{
22 .buf = .{},
23 .memlimit = memlimit,
24 .len = 0,
25 };
26 }
27
28 pub fn appendByte(self: *Self, allocator: Allocator, byte: u8) !void {
29 try self.buf.append(allocator, byte);
30 self.len += 1;
31 }
32
33 /// Reset the internal dictionary
34 pub fn reset(self: *Self, writer: anytype) !void {
35 try writer.writeAll(self.buf.items);
36 self.buf.clearRetainingCapacity();
37 self.len = 0;
38 }
39
40 /// Retrieve the last byte or return a default
41 pub fn lastOr(self: Self, lit: u8) u8 {
42 const buf_len = self.buf.items.len;
43 return if (buf_len == 0)
44 lit
45 else
46 self.buf.items[buf_len - 1];
47 }
48
49 /// Retrieve the n-th last byte
50 pub fn lastN(self: Self, dist: usize) !u8 {
51 const buf_len = self.buf.items.len;
52 if (dist > buf_len) {
53 return error.CorruptInput;
54 }
55
56 return self.buf.items[buf_len - dist];
57 }
58
59 /// Append a literal
60 pub fn appendLiteral(
61 self: *Self,
62 allocator: Allocator,
63 lit: u8,
64 writer: anytype,
65 ) !void {
66 _ = writer;
67 if (self.len >= self.memlimit) {
68 return error.CorruptInput;
69 }
70 try self.buf.append(allocator, lit);
71 self.len += 1;
72 }
73
74 /// Fetch an LZ sequence (length, distance) from inside the buffer
75 pub fn appendLz(
76 self: *Self,
77 allocator: Allocator,
78 len: usize,
79 dist: usize,
80 writer: anytype,
81 ) !void {
82 _ = writer;
83
84 const buf_len = self.buf.items.len;
85 if (dist > buf_len) {
86 return error.CorruptInput;
87 }
88
89 var offset = buf_len - dist;
90 var i: usize = 0;
91 while (i < len) : (i += 1) {
92 const x = self.buf.items[offset];
93 try self.buf.append(allocator, x);
94 offset += 1;
95 }
96 self.len += len;
97 }
98
99 pub fn finish(self: *Self, writer: anytype) !void {
100 try writer.writeAll(self.buf.items);
101 self.buf.clearRetainingCapacity();
102 }
103
104 pub fn deinit(self: *Self, allocator: Allocator) void {
105 self.buf.deinit(allocator);
106 self.* = undefined;
107 }
108};
109
110/// A circular buffer for LZ sequences
111pub const LzCircularBuffer = struct {
112 /// Circular buffer
113 buf: ArrayListUnmanaged(u8),
114
115 /// Length of the buffer
116 dict_size: usize,
117
118 /// Buffer memory limit
119 memlimit: usize,
120
121 /// Current position
122 cursor: usize,
123
124 /// Total number of bytes sent through the buffer
125 len: usize,
126
127 const Self = @This();
128
129 pub fn init(dict_size: usize, memlimit: usize) Self {
130 return Self{
131 .buf = .{},
132 .dict_size = dict_size,
133 .memlimit = memlimit,
134 .cursor = 0,
135 .len = 0,
136 };
137 }
138
139 pub fn get(self: Self, index: usize) u8 {
140 return if (0 <= index and index < self.buf.items.len)
141 self.buf.items[index]
142 else
143 0;
144 }
145
146 pub fn set(self: *Self, allocator: Allocator, index: usize, value: u8) !void {
147 if (index >= self.memlimit) {
148 return error.CorruptInput;
149 }
150 try self.buf.ensureTotalCapacity(allocator, index + 1);
151 while (self.buf.items.len < index) {
152 self.buf.appendAssumeCapacity(0);
153 }
154 self.buf.appendAssumeCapacity(value);
155 }
156
157 /// Retrieve the last byte or return a default
158 pub fn lastOr(self: Self, lit: u8) u8 {
159 return if (self.len == 0)
160 lit
161 else
162 self.get((self.dict_size + self.cursor - 1) % self.dict_size);
163 }
164
165 /// Retrieve the n-th last byte
166 pub fn lastN(self: Self, dist: usize) !u8 {
167 if (dist > self.dict_size or dist > self.len) {
168 return error.CorruptInput;
169 }
170
171 const offset = (self.dict_size + self.cursor - dist) % self.dict_size;
172 return self.get(offset);
173 }
174
175 /// Append a literal
176 pub fn appendLiteral(
177 self: *Self,
178 allocator: Allocator,
179 lit: u8,
180 writer: anytype,
181 ) !void {
182 try self.set(allocator, self.cursor, lit);
183 self.cursor += 1;
184 self.len += 1;
185
186 // Flush the circular buffer to the output
187 if (self.cursor == self.dict_size) {
188 try writer.writeAll(self.buf.items);
189 self.cursor = 0;
190 }
191 }
192
193 /// Fetch an LZ sequence (length, distance) from inside the buffer
194 pub fn appendLz(
195 self: *Self,
196 allocator: Allocator,
197 len: usize,
198 dist: usize,
199 writer: anytype,
200 ) !void {
201 if (dist > self.dict_size or dist > self.len) {
202 return error.CorruptInput;
203 }
204
205 var offset = (self.dict_size + self.cursor - dist) % self.dict_size;
206 var i: usize = 0;
207 while (i < len) : (i += 1) {
208 const x = self.get(offset);
209 try self.appendLiteral(allocator, x, writer);
210 offset += 1;
211 if (offset == self.dict_size) {
212 offset = 0;
213 }
214 }
215 }
216
217 pub fn finish(self: *Self, writer: anytype) !void {
218 if (self.cursor > 0) {
219 try writer.writeAll(self.buf.items[0..self.cursor]);
220 self.cursor = 0;
221 }
222 }
223
224 pub fn deinit(self: *Self, allocator: Allocator) void {
225 self.buf.deinit(allocator);
226 self.* = undefined;
227 }
228};
lib/std/compress/lzma/decode/rangecoder.zig created+181
...@@ -0,0 +1,181 @@
1const std = @import("../../../std.zig");
2const mem = std.mem;
3
4pub const RangeDecoder = struct {
5 range: u32,
6 code: u32,
7
8 pub fn init(reader: anytype) !RangeDecoder {
9 const reserved = try reader.readByte();
10 if (reserved != 0) {
11 return error.CorruptInput;
12 }
13 return RangeDecoder{
14 .range = 0xFFFF_FFFF,
15 .code = try reader.readIntBig(u32),
16 };
17 }
18
19 pub fn fromParts(
20 range: u32,
21 code: u32,
22 ) RangeDecoder {
23 return .{
24 .range = range,
25 .code = code,
26 };
27 }
28
29 pub fn set(self: *RangeDecoder, range: u32, code: u32) void {
30 self.range = range;
31 self.code = code;
32 }
33
34 pub inline fn isFinished(self: RangeDecoder) bool {
35 return self.code == 0;
36 }
37
38 inline fn normalize(self: *RangeDecoder, reader: anytype) !void {
39 if (self.range < 0x0100_0000) {
40 self.range <<= 8;
41 self.code = (self.code << 8) ^ @as(u32, try reader.readByte());
42 }
43 }
44
45 inline fn getBit(self: *RangeDecoder, reader: anytype) !bool {
46 self.range >>= 1;
47
48 const bit = self.code >= self.range;
49 if (bit)
50 self.code -= self.range;
51
52 try self.normalize(reader);
53 return bit;
54 }
55
56 pub fn get(self: *RangeDecoder, reader: anytype, count: usize) !u32 {
57 var result: u32 = 0;
58 var i: usize = 0;
59 while (i < count) : (i += 1)
60 result = (result << 1) ^ @boolToInt(try self.getBit(reader));
61 return result;
62 }
63
64 pub inline fn decodeBit(self: *RangeDecoder, reader: anytype, prob: *u16, update: bool) !bool {
65 const bound = (self.range >> 11) * prob.*;
66
67 if (self.code < bound) {
68 if (update)
69 prob.* += (0x800 - prob.*) >> 5;
70 self.range = bound;
71
72 try self.normalize(reader);
73 return false;
74 } else {
75 if (update)
76 prob.* -= prob.* >> 5;
77 self.code -= bound;
78 self.range -= bound;
79
80 try self.normalize(reader);
81 return true;
82 }
83 }
84
85 fn parseBitTree(
86 self: *RangeDecoder,
87 reader: anytype,
88 num_bits: u5,
89 probs: []u16,
90 update: bool,
91 ) !u32 {
92 var tmp: u32 = 1;
93 var i: @TypeOf(num_bits) = 0;
94 while (i < num_bits) : (i += 1) {
95 const bit = try self.decodeBit(reader, &probs[tmp], update);
96 tmp = (tmp << 1) ^ @boolToInt(bit);
97 }
98 return tmp - (@as(u32, 1) << num_bits);
99 }
100
101 pub fn parseReverseBitTree(
102 self: *RangeDecoder,
103 reader: anytype,
104 num_bits: u5,
105 probs: []u16,
106 offset: usize,
107 update: bool,
108 ) !u32 {
109 var result: u32 = 0;
110 var tmp: usize = 1;
111 var i: @TypeOf(num_bits) = 0;
112 while (i < num_bits) : (i += 1) {
113 const bit = @boolToInt(try self.decodeBit(reader, &probs[offset + tmp], update));
114 tmp = (tmp << 1) ^ bit;
115 result ^= @as(u32, bit) << i;
116 }
117 return result;
118 }
119};
120
121pub fn BitTree(comptime num_bits: usize) type {
122 return struct {
123 probs: [1 << num_bits]u16 = .{0x400} ** (1 << num_bits),
124
125 const Self = @This();
126
127 pub fn parse(
128 self: *Self,
129 reader: anytype,
130 decoder: *RangeDecoder,
131 update: bool,
132 ) !u32 {
133 return decoder.parseBitTree(reader, num_bits, &self.probs, update);
134 }
135
136 pub fn parseReverse(
137 self: *Self,
138 reader: anytype,
139 decoder: *RangeDecoder,
140 update: bool,
141 ) !u32 {
142 return decoder.parseReverseBitTree(reader, num_bits, &self.probs, 0, update);
143 }
144
145 pub fn reset(self: *Self) void {
146 mem.set(u16, &self.probs, 0x400);
147 }
148 };
149}
150
151pub const LenDecoder = struct {
152 choice: u16 = 0x400,
153 choice2: u16 = 0x400,
154 low_coder: [16]BitTree(3) = .{.{}} ** 16,
155 mid_coder: [16]BitTree(3) = .{.{}} ** 16,
156 high_coder: BitTree(8) = .{},
157
158 pub fn decode(
159 self: *LenDecoder,
160 reader: anytype,
161 decoder: *RangeDecoder,
162 pos_state: usize,
163 update: bool,
164 ) !usize {
165 if (!try decoder.decodeBit(reader, &self.choice, update)) {
166 return @as(usize, try self.low_coder[pos_state].parse(reader, decoder, update));
167 } else if (!try decoder.decodeBit(reader, &self.choice2, update)) {
168 return @as(usize, try self.mid_coder[pos_state].parse(reader, decoder, update)) + 8;
169 } else {
170 return @as(usize, try self.high_coder.parse(reader, decoder, update)) + 16;
171 }
172 }
173
174 pub fn reset(self: *LenDecoder) void {
175 self.choice = 0x400;
176 self.choice2 = 0x400;
177 for (self.low_coder) |*t| t.reset();
178 for (self.mid_coder) |*t| t.reset();
179 self.high_coder.reset();
180 }
181};
lib/std/compress/lzma/test.zig created+89
...@@ -0,0 +1,89 @@
1const std = @import("../../std.zig");
2const lzma = @import("../lzma.zig");
3
4fn testDecompress(compressed: []const u8) ![]u8 {
5 const allocator = std.testing.allocator;
6 var stream = std.io.fixedBufferStream(compressed);
7 var decompressor = try lzma.decompress(allocator, stream.reader());
8 defer decompressor.deinit();
9 const reader = decompressor.reader();
10 return reader.readAllAlloc(allocator, std.math.maxInt(usize));
11}
12
13fn testDecompressEqual(expected: []const u8, compressed: []const u8) !void {
14 const allocator = std.testing.allocator;
15 const decomp = try testDecompress(compressed);
16 defer allocator.free(decomp);
17 try std.testing.expectEqualSlices(u8, expected, decomp);
18}
19
20fn testDecompressError(expected: anyerror, compressed: []const u8) !void {
21 return std.testing.expectError(expected, testDecompress(compressed));
22}
23
24test "LZMA: decompress empty world" {
25 try testDecompressEqual(
26 "",
27 &[_]u8{
28 0x5d, 0x00, 0x00, 0x80, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x83, 0xff,
29 0xfb, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00,
30 },
31 );
32}
33
34test "LZMA: decompress hello world" {
35 try testDecompressEqual(
36 "Hello world\n",
37 &[_]u8{
38 0x5d, 0x00, 0x00, 0x80, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x24, 0x19,
39 0x49, 0x98, 0x6f, 0x10, 0x19, 0xc6, 0xd7, 0x31, 0xeb, 0x36, 0x50, 0xb2, 0x98, 0x48, 0xff, 0xfe,
40 0xa5, 0xb0, 0x00,
41 },
42 );
43}
44
45test "LZMA: decompress huge dict" {
46 try testDecompressEqual(
47 "Hello world\n",
48 &[_]u8{
49 0x5d, 0x7f, 0x7f, 0x7f, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x24, 0x19,
50 0x49, 0x98, 0x6f, 0x10, 0x19, 0xc6, 0xd7, 0x31, 0xeb, 0x36, 0x50, 0xb2, 0x98, 0x48, 0xff, 0xfe,
51 0xa5, 0xb0, 0x00,
52 },
53 );
54}
55
56test "LZMA: unknown size with end of payload marker" {
57 try testDecompressEqual(
58 "Hello\nWorld!\n",
59 @embedFile("testdata/good-unknown_size-with_eopm.lzma"),
60 );
61}
62
63test "LZMA: known size without end of payload marker" {
64 try testDecompressEqual(
65 "Hello\nWorld!\n",
66 @embedFile("testdata/good-known_size-without_eopm.lzma"),
67 );
68}
69
70test "LZMA: known size with end of payload marker" {
71 try testDecompressEqual(
72 "Hello\nWorld!\n",
73 @embedFile("testdata/good-known_size-with_eopm.lzma"),
74 );
75}
76
77test "LZMA: too big uncompressed size in header" {
78 try testDecompressError(
79 error.CorruptInput,
80 @embedFile("testdata/bad-too_big_size-with_eopm.lzma"),
81 );
82}
83
84test "LZMA: too small uncompressed size in header" {
85 try testDecompressError(
86 error.CorruptInput,
87 @embedFile("testdata/bad-too_small_size-without_eopm-3.lzma"),
88 );
89}
lib/std/compress/lzma/testdata/bad-too_big_size-with_eopm.lzma created
Binary files /dev/null and b/lib/std/compress/lzma/testdata/bad-too_big_size-with_eopm.lzma differ
lib/std/compress/lzma/testdata/bad-too_small_size-without_eopm-3.lzma created
Binary files /dev/null and b/lib/std/compress/lzma/testdata/bad-too_small_size-without_eopm-3.lzma differ
lib/std/compress/lzma/testdata/good-known_size-with_eopm.lzma created
Binary files /dev/null and b/lib/std/compress/lzma/testdata/good-known_size-with_eopm.lzma differ
lib/std/compress/lzma/testdata/good-known_size-without_eopm.lzma created
Binary files /dev/null and b/lib/std/compress/lzma/testdata/good-known_size-without_eopm.lzma differ
lib/std/compress/lzma/testdata/good-unknown_size-with_eopm.lzma created
Binary files /dev/null and b/lib/std/compress/lzma/testdata/good-unknown_size-with_eopm.lzma differ
lib/std/compress/lzma/vec2d.zig created+128
...@@ -0,0 +1,128 @@
1const std = @import("../../std.zig");
2const math = std.math;
3const mem = std.mem;
4const Allocator = std.mem.Allocator;
5
6pub fn Vec2D(comptime T: type) type {
7 return struct {
8 data: []T,
9 cols: usize,
10
11 const Self = @This();
12
13 pub fn init(allocator: Allocator, value: T, size: struct { usize, usize }) !Self {
14 const len = try math.mul(usize, size[0], size[1]);
15 const data = try allocator.alloc(T, len);
16 mem.set(T, data, value);
17 return Self{
18 .data = data,
19 .cols = size[1],
20 };
21 }
22
23 pub fn deinit(self: *Self, allocator: Allocator) void {
24 allocator.free(self.data);
25 self.* = undefined;
26 }
27
28 pub fn fill(self: *Self, value: T) void {
29 mem.set(T, self.data, value);
30 }
31
32 inline fn _get(self: Self, row: usize) ![]T {
33 const start_row = try math.mul(usize, row, self.cols);
34 const end_row = try math.add(usize, start_row, self.cols);
35 return self.data[start_row..end_row];
36 }
37
38 pub fn get(self: Self, row: usize) ![]const T {
39 return self._get(row);
40 }
41
42 pub fn getMut(self: *Self, row: usize) ![]T {
43 return self._get(row);
44 }
45 };
46}
47
48const testing = std.testing;
49const expectEqualSlices = std.testing.expectEqualSlices;
50const expectError = std.testing.expectError;
51
52test "Vec2D.init" {
53 const allocator = testing.allocator;
54 var vec2d = try Vec2D(i32).init(allocator, 1, .{ 2, 3 });
55 defer vec2d.deinit(allocator);
56
57 try expectEqualSlices(i32, &.{ 1, 1, 1 }, try vec2d.get(0));
58 try expectEqualSlices(i32, &.{ 1, 1, 1 }, try vec2d.get(1));
59}
60
61test "Vec2D.init overflow" {
62 const allocator = testing.allocator;
63 try expectError(
64 error.Overflow,
65 Vec2D(i32).init(allocator, 1, .{ math.maxInt(usize), math.maxInt(usize) }),
66 );
67}
68
69test "Vec2D.fill" {
70 const allocator = testing.allocator;
71 var vec2d = try Vec2D(i32).init(allocator, 0, .{ 2, 3 });
72 defer vec2d.deinit(allocator);
73
74 vec2d.fill(7);
75
76 try expectEqualSlices(i32, &.{ 7, 7, 7 }, try vec2d.get(0));
77 try expectEqualSlices(i32, &.{ 7, 7, 7 }, try vec2d.get(1));
78}
79
80test "Vec2D.get" {
81 var data = [_]i32{ 0, 1, 2, 3, 4, 5, 6, 7 };
82 const vec2d = Vec2D(i32){
83 .data = &data,
84 .cols = 2,
85 };
86
87 try expectEqualSlices(i32, &.{ 0, 1 }, try vec2d.get(0));
88 try expectEqualSlices(i32, &.{ 2, 3 }, try vec2d.get(1));
89 try expectEqualSlices(i32, &.{ 4, 5 }, try vec2d.get(2));
90 try expectEqualSlices(i32, &.{ 6, 7 }, try vec2d.get(3));
91}
92
93test "Vec2D.getMut" {
94 var data = [_]i32{ 0, 1, 2, 3, 4, 5, 6, 7 };
95 var vec2d = Vec2D(i32){
96 .data = &data,
97 .cols = 2,
98 };
99
100 const row = try vec2d.getMut(1);
101 row[1] = 9;
102
103 try expectEqualSlices(i32, &.{ 0, 1 }, try vec2d.get(0));
104 // (1, 1) should be 9.
105 try expectEqualSlices(i32, &.{ 2, 9 }, try vec2d.get(1));
106 try expectEqualSlices(i32, &.{ 4, 5 }, try vec2d.get(2));
107 try expectEqualSlices(i32, &.{ 6, 7 }, try vec2d.get(3));
108}
109
110test "Vec2D.get multiplication overflow" {
111 const allocator = testing.allocator;
112 var matrix = try Vec2D(i32).init(allocator, 0, .{ 3, 4 });
113 defer matrix.deinit(allocator);
114
115 const row = (math.maxInt(usize) / 4) + 1;
116 try expectError(error.Overflow, matrix.get(row));
117 try expectError(error.Overflow, matrix.getMut(row));
118}
119
120test "Vec2D.get addition overflow" {
121 const allocator = testing.allocator;
122 var matrix = try Vec2D(i32).init(allocator, 0, .{ 3, 5 });
123 defer matrix.deinit(allocator);
124
125 const row = math.maxInt(usize) / 5;
126 try expectError(error.Overflow, matrix.get(row));
127 try expectError(error.Overflow, matrix.getMut(row));
128}
lib/std/compress/lzma2.zig created+26
...@@ -0,0 +1,26 @@
1const std = @import("../std.zig");
2const Allocator = std.mem.Allocator;
3
4pub const decode = @import("lzma2/decode.zig");
5
6pub fn decompress(
7 allocator: Allocator,
8 reader: anytype,
9 writer: anytype,
10) !void {
11 var decoder = try decode.Decoder.init(allocator);
12 defer decoder.deinit(allocator);
13 return decoder.decompress(allocator, reader, writer);
14}
15
16test {
17 const expected = "Hello\nWorld!\n";
18 const compressed = &[_]u8{ 0x01, 0x00, 0x05, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x0A, 0x02, 0x00, 0x06, 0x57, 0x6F, 0x72, 0x6C, 0x64, 0x21, 0x0A, 0x00 };
19
20 const allocator = std.testing.allocator;
21 var decomp = std.ArrayList(u8).init(allocator);
22 defer decomp.deinit();
23 var stream = std.io.fixedBufferStream(compressed);
24 try decompress(allocator, stream.reader(), decomp.writer());
25 try std.testing.expectEqualSlices(u8, expected, decomp.items);
26}
lib/std/compress/lzma2/decode.zig created+169
...@@ -0,0 +1,169 @@
1const std = @import("../../std.zig");
2const Allocator = std.mem.Allocator;
3
4const lzma = @import("../lzma.zig");
5const DecoderState = lzma.decode.DecoderState;
6const LzAccumBuffer = lzma.decode.lzbuffer.LzAccumBuffer;
7const Properties = lzma.decode.Properties;
8const RangeDecoder = lzma.decode.rangecoder.RangeDecoder;
9
10pub const Decoder = struct {
11 lzma_state: DecoderState,
12
13 pub fn init(allocator: Allocator) !Decoder {
14 return Decoder{
15 .lzma_state = try DecoderState.init(
16 allocator,
17 Properties{
18 .lc = 0,
19 .lp = 0,
20 .pb = 0,
21 },
22 null,
23 ),
24 };
25 }
26
27 pub fn deinit(self: *Decoder, allocator: Allocator) void {
28 self.lzma_state.deinit(allocator);
29 self.* = undefined;
30 }
31
32 pub fn decompress(
33 self: *Decoder,
34 allocator: Allocator,
35 reader: anytype,
36 writer: anytype,
37 ) !void {
38 var accum = LzAccumBuffer.init(std.math.maxInt(usize));
39 defer accum.deinit(allocator);
40
41 while (true) {
42 const status = try reader.readByte();
43
44 switch (status) {
45 0 => break,
46 1 => try parseUncompressed(allocator, reader, writer, &accum, true),
47 2 => try parseUncompressed(allocator, reader, writer, &accum, false),
48 else => try self.parseLzma(allocator, reader, writer, &accum, status),
49 }
50 }
51
52 try accum.finish(writer);
53 }
54
55 fn parseLzma(
56 self: *Decoder,
57 allocator: Allocator,
58 reader: anytype,
59 writer: anytype,
60 accum: *LzAccumBuffer,
61 status: u8,
62 ) !void {
63 if (status & 0x80 == 0) {
64 return error.CorruptInput;
65 }
66
67 const Reset = struct {
68 dict: bool,
69 state: bool,
70 props: bool,
71 };
72
73 const reset = switch ((status >> 5) & 0x3) {
74 0 => Reset{
75 .dict = false,
76 .state = false,
77 .props = false,
78 },
79 1 => Reset{
80 .dict = false,
81 .state = true,
82 .props = false,
83 },
84 2 => Reset{
85 .dict = false,
86 .state = true,
87 .props = true,
88 },
89 3 => Reset{
90 .dict = true,
91 .state = true,
92 .props = true,
93 },
94 else => unreachable,
95 };
96
97 const unpacked_size = blk: {
98 var tmp: u64 = status & 0x1F;
99 tmp <<= 16;
100 tmp |= try reader.readIntBig(u16);
101 break :blk tmp + 1;
102 };
103
104 const packed_size = blk: {
105 const tmp: u17 = try reader.readIntBig(u16);
106 break :blk tmp + 1;
107 };
108
109 if (reset.dict) {
110 try accum.reset(writer);
111 }
112
113 if (reset.state) {
114 var new_props = self.lzma_state.lzma_props;
115
116 if (reset.props) {
117 var props = try reader.readByte();
118 if (props >= 225) {
119 return error.CorruptInput;
120 }
121
122 const lc = @intCast(u4, props % 9);
123 props /= 9;
124 const lp = @intCast(u3, props % 5);
125 props /= 5;
126 const pb = @intCast(u3, props);
127
128 if (lc + lp > 4) {
129 return error.CorruptInput;
130 }
131
132 new_props = Properties{ .lc = lc, .lp = lp, .pb = pb };
133 }
134
135 try self.lzma_state.resetState(allocator, new_props);
136 }
137
138 self.lzma_state.unpacked_size = unpacked_size + accum.len;
139
140 var counter = std.io.countingReader(reader);
141 const counter_reader = counter.reader();
142
143 var rangecoder = try RangeDecoder.init(counter_reader);
144 while (try self.lzma_state.process(allocator, counter_reader, writer, accum, &rangecoder) == .continue_) {}
145
146 if (counter.bytes_read != packed_size) {
147 return error.CorruptInput;
148 }
149 }
150
151 fn parseUncompressed(
152 allocator: Allocator,
153 reader: anytype,
154 writer: anytype,
155 accum: *LzAccumBuffer,
156 reset_dict: bool,
157 ) !void {
158 const unpacked_size = @as(u17, try reader.readIntBig(u16)) + 1;
159
160 if (reset_dict) {
161 try accum.reset(writer);
162 }
163
164 var i: @TypeOf(unpacked_size) = 0;
165 while (i < unpacked_size) : (i += 1) {
166 try accum.appendByte(allocator, try reader.readByte());
167 }
168 }
169};
lib/std/compress/xz.zig+1-1
...@@ -118,7 +118,7 @@ pub fn Decompress(comptime ReaderType: type) type {...@@ -118,7 +118,7 @@ pub fn Decompress(comptime ReaderType: type) type {
118 var hasher = std.compress.hashedReader(self.in_reader, Crc32.init());118 var hasher = std.compress.hashedReader(self.in_reader, Crc32.init());
119 const hashed_reader = hasher.reader();119 const hashed_reader = hasher.reader();
120120
121 const backward_size = (try hashed_reader.readIntLittle(u32) + 1) * 4;121 const backward_size = (@as(u64, try hashed_reader.readIntLittle(u32)) + 1) * 4;
122 if (backward_size != index_size)122 if (backward_size != index_size)
123 return error.CorruptInput;123 return error.CorruptInput;
124124
lib/std/compress/xz/block.zig+20-126
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1const std = @import("../../std.zig");1const std = @import("../../std.zig");
2const lzma = @import("lzma.zig");2const lzma2 = std.compress.lzma2;
3const Allocator = std.mem.Allocator;3const Allocator = std.mem.Allocator;
4const ArrayListUnmanaged = std.ArrayListUnmanaged;
4const Crc32 = std.hash.Crc32;5const Crc32 = std.hash.Crc32;
5const Crc64 = std.hash.crc.Crc64Xz;6const Crc64 = std.hash.crc.Crc64Xz;
6const Sha256 = std.crypto.hash.sha2.Sha256;7const Sha256 = std.crypto.hash.sha2.Sha256;
...@@ -32,8 +33,7 @@ pub fn Decoder(comptime ReaderType: type) type {...@@ -32,8 +33,7 @@ pub fn Decoder(comptime ReaderType: type) type {
32 inner_reader: ReaderType,33 inner_reader: ReaderType,
33 check: xz.Check,34 check: xz.Check,
34 err: ?Error,35 err: ?Error,
35 accum: lzma.LzAccumBuffer,36 to_read: ArrayListUnmanaged(u8),
36 lzma_state: lzma.DecoderState,
37 block_count: usize,37 block_count: usize,
3838
39 fn init(allocator: Allocator, in_reader: ReaderType, check: xz.Check) !Self {39 fn init(allocator: Allocator, in_reader: ReaderType, check: xz.Check) !Self {
...@@ -42,15 +42,13 @@ pub fn Decoder(comptime ReaderType: type) type {...@@ -42,15 +42,13 @@ pub fn Decoder(comptime ReaderType: type) type {
42 .inner_reader = in_reader,42 .inner_reader = in_reader,
43 .check = check,43 .check = check,
44 .err = null,44 .err = null,
45 .accum = .{},45 .to_read = .{},
46 .lzma_state = try lzma.DecoderState.init(allocator),
47 .block_count = 0,46 .block_count = 0,
48 };47 };
49 }48 }
5049
51 pub fn deinit(self: *Self) void {50 pub fn deinit(self: *Self) void {
52 self.accum.deinit(self.allocator);51 self.to_read.deinit(self.allocator);
53 self.lzma_state.deinit(self.allocator);
54 }52 }
5553
56 pub fn reader(self: *Self) Reader {54 pub fn reader(self: *Self) Reader {
...@@ -59,9 +57,13 @@ pub fn Decoder(comptime ReaderType: type) type {...@@ -59,9 +57,13 @@ pub fn Decoder(comptime ReaderType: type) type {
5957
60 pub fn read(self: *Self, output: []u8) Error!usize {58 pub fn read(self: *Self, output: []u8) Error!usize {
61 while (true) {59 while (true) {
62 if (self.accum.to_read.items.len > 0) {60 if (self.to_read.items.len > 0) {
63 const n = self.accum.read(output);61 const input = self.to_read.items;
64 if (self.accum.to_read.items.len == 0 and self.err != null) {62 const n = std.math.min(input.len, output.len);
63 std.mem.copy(u8, output[0..n], input[0..n]);
64 std.mem.copy(u8, input, input[n..]);
65 self.to_read.shrinkRetainingCapacity(input.len - n);
66 if (self.to_read.items.len == 0 and self.err != null) {
65 if (self.err.? == DecodeError.EndOfStreamWithNoError) {67 if (self.err.? == DecodeError.EndOfStreamWithNoError) {
66 return n;68 return n;
67 }69 }
...@@ -77,15 +79,12 @@ pub fn Decoder(comptime ReaderType: type) type {...@@ -77,15 +79,12 @@ pub fn Decoder(comptime ReaderType: type) type {
77 }79 }
78 self.readBlock() catch |e| {80 self.readBlock() catch |e| {
79 self.err = e;81 self.err = e;
80 if (self.accum.to_read.items.len == 0) {
81 try self.accum.reset(self.allocator);
82 }
83 };82 };
84 }83 }
85 }84 }
8685
87 fn readBlock(self: *Self) Error!void {86 fn readBlock(self: *Self) Error!void {
88 const unpacked_pos = self.accum.to_read.items.len;87 const unpacked_pos = self.to_read.items.len;
8988
90 var block_counter = std.io.countingReader(self.inner_reader);89 var block_counter = std.io.countingReader(self.inner_reader);
91 const block_reader = block_counter.reader();90 const block_reader = block_counter.reader();
...@@ -98,7 +97,7 @@ pub fn Decoder(comptime ReaderType: type) type {...@@ -98,7 +97,7 @@ pub fn Decoder(comptime ReaderType: type) type {
98 var header_hasher = std.compress.hashedReader(block_reader, Crc32.init());97 var header_hasher = std.compress.hashedReader(block_reader, Crc32.init());
99 const header_reader = header_hasher.reader();98 const header_reader = header_hasher.reader();
10099
101 const header_size = try header_reader.readByte() * 4;100 const header_size = @as(u64, try header_reader.readByte()) * 4;
102 if (header_size == 0)101 if (header_size == 0)
103 return error.EndOfStreamWithNoError;102 return error.EndOfStreamWithNoError;
104103
...@@ -156,15 +155,18 @@ pub fn Decoder(comptime ReaderType: type) type {...@@ -156,15 +155,18 @@ pub fn Decoder(comptime ReaderType: type) type {
156155
157 // Compressed Data156 // Compressed Data
158 var packed_counter = std.io.countingReader(block_reader);157 var packed_counter = std.io.countingReader(block_reader);
159 const packed_reader = packed_counter.reader();158 try lzma2.decompress(
160 while (try self.readLzma2Chunk(packed_reader)) {}159 self.allocator,
160 packed_counter.reader(),
161 self.to_read.writer(self.allocator),
162 );
161163
162 if (packed_size) |s| {164 if (packed_size) |s| {
163 if (s != packed_counter.bytes_read)165 if (s != packed_counter.bytes_read)
164 return error.CorruptInput;166 return error.CorruptInput;
165 }167 }
166168
167 const unpacked_bytes = self.accum.to_read.items[unpacked_pos..];169 const unpacked_bytes = self.to_read.items[unpacked_pos..];
168 if (unpacked_size) |s| {170 if (unpacked_size) |s| {
169 if (s != unpacked_bytes.len)171 if (s != unpacked_bytes.len)
170 return error.CorruptInput;172 return error.CorruptInput;
...@@ -205,113 +207,5 @@ pub fn Decoder(comptime ReaderType: type) type {...@@ -205,113 +207,5 @@ pub fn Decoder(comptime ReaderType: type) type {
205207
206 self.block_count += 1;208 self.block_count += 1;
207 }209 }
208
209 fn readLzma2Chunk(self: *Self, packed_reader: anytype) Error!bool {
210 const status = try packed_reader.readByte();
211 switch (status) {
212 0 => {
213 try self.accum.reset(self.allocator);
214 return false;
215 },
216 1, 2 => {
217 if (status == 1)
218 try self.accum.reset(self.allocator);
219
220 const size = try packed_reader.readIntBig(u16) + 1;
221 try self.accum.ensureUnusedCapacity(self.allocator, size);
222
223 var i: usize = 0;
224 while (i < size) : (i += 1)
225 self.accum.appendAssumeCapacity(try packed_reader.readByte());
226
227 return true;
228 },
229 else => {
230 if (status & 0x80 == 0)
231 return error.CorruptInput;
232
233 const Reset = struct {
234 dict: bool,
235 state: bool,
236 props: bool,
237 };
238
239 const reset = switch ((status >> 5) & 0x3) {
240 0 => Reset{
241 .dict = false,
242 .state = false,
243 .props = false,
244 },
245 1 => Reset{
246 .dict = false,
247 .state = true,
248 .props = false,
249 },
250 2 => Reset{
251 .dict = false,
252 .state = true,
253 .props = true,
254 },
255 3 => Reset{
256 .dict = true,
257 .state = true,
258 .props = true,
259 },
260 else => unreachable,
261 };
262
263 const unpacked_size = blk: {
264 var tmp: u64 = status & 0x1F;
265 tmp <<= 16;
266 tmp |= try packed_reader.readIntBig(u16);
267 break :blk tmp + 1;
268 };
269
270 const packed_size = blk: {
271 const tmp: u17 = try packed_reader.readIntBig(u16);
272 break :blk tmp + 1;
273 };
274
275 if (reset.dict)
276 try self.accum.reset(self.allocator);
277
278 if (reset.state) {
279 var new_props = self.lzma_state.lzma_props;
280
281 if (reset.props) {
282 var props = try packed_reader.readByte();
283 if (props >= 225)
284 return error.CorruptInput;
285
286 const lc = @intCast(u4, props % 9);
287 props /= 9;
288 const lp = @intCast(u3, props % 5);
289 props /= 5;
290 const pb = @intCast(u3, props);
291
292 if (lc + lp > 4)
293 return error.CorruptInput;
294
295 new_props = .{ .lc = lc, .lp = lp, .pb = pb };
296 }
297
298 try self.lzma_state.reset_state(self.allocator, new_props);
299 }
300
301 self.lzma_state.unpacked_size = unpacked_size + self.accum.len();
302
303 const buffer = try self.allocator.alloc(u8, packed_size);
304 defer self.allocator.free(buffer);
305
306 for (buffer) |*b|
307 b.* = try packed_reader.readByte();
308
309 var rangecoder = try lzma.RangeDecoder.init(buffer);
310 try self.lzma_state.process(self.allocator, &self.accum, &rangecoder);
311
312 return true;
313 },
314 }
315 }
316 };210 };
317}211}
lib/std/compress/xz/lzma.zig deleted-658
...@@ -1,658 +0,0 @@
1// Ported from https://github.com/gendx/lzma-rs
2
3const std = @import("../../std.zig");
4const assert = std.debug.assert;
5const Allocator = std.mem.Allocator;
6const ArrayListUnmanaged = std.ArrayListUnmanaged;
7
8const LzmaProperties = struct {
9 lc: u4,
10 lp: u3,
11 pb: u3,
12
13 fn validate(self: LzmaProperties) void {
14 assert(self.lc <= 8);
15 assert(self.lp <= 4);
16 assert(self.pb <= 4);
17 }
18};
19
20pub const DecoderState = struct {
21 lzma_props: LzmaProperties,
22 unpacked_size: ?u64,
23 literal_probs: Vec2D(u16),
24 pos_slot_decoder: [4]BitTree,
25 align_decoder: BitTree,
26 pos_decoders: [115]u16,
27 is_match: [192]u16,
28 is_rep: [12]u16,
29 is_rep_g0: [12]u16,
30 is_rep_g1: [12]u16,
31 is_rep_g2: [12]u16,
32 is_rep_0long: [192]u16,
33 state: usize,
34 rep: [4]usize,
35 len_decoder: LenDecoder,
36 rep_len_decoder: LenDecoder,
37
38 pub fn init(allocator: Allocator) !DecoderState {
39 return .{
40 .lzma_props = LzmaProperties{ .lc = 0, .lp = 0, .pb = 0 },
41 .unpacked_size = null,
42 .literal_probs = try Vec2D(u16).init(allocator, 0x400, 1, 0x300),
43 .pos_slot_decoder = .{
44 try BitTree.init(allocator, 6),
45 try BitTree.init(allocator, 6),
46 try BitTree.init(allocator, 6),
47 try BitTree.init(allocator, 6),
48 },
49 .align_decoder = try BitTree.init(allocator, 4),
50 .pos_decoders = .{0x400} ** 115,
51 .is_match = .{0x400} ** 192,
52 .is_rep = .{0x400} ** 12,
53 .is_rep_g0 = .{0x400} ** 12,
54 .is_rep_g1 = .{0x400} ** 12,
55 .is_rep_g2 = .{0x400} ** 12,
56 .is_rep_0long = .{0x400} ** 192,
57 .state = 0,
58 .rep = .{0} ** 4,
59 .len_decoder = try LenDecoder.init(allocator),
60 .rep_len_decoder = try LenDecoder.init(allocator),
61 };
62 }
63
64 pub fn deinit(self: *DecoderState, allocator: Allocator) void {
65 self.literal_probs.deinit(allocator);
66 for (self.pos_slot_decoder) |*t| t.deinit(allocator);
67 self.align_decoder.deinit(allocator);
68 self.len_decoder.deinit(allocator);
69 self.rep_len_decoder.deinit(allocator);
70 }
71
72 pub fn reset_state(self: *DecoderState, allocator: Allocator, new_props: LzmaProperties) !void {
73 new_props.validate();
74 if (self.lzma_props.lc + self.lzma_props.lp == new_props.lc + new_props.lp) {
75 self.literal_probs.fill(0x400);
76 } else {
77 self.literal_probs.deinit(allocator);
78 self.literal_probs = try Vec2D(u16).init(allocator, 0x400, @as(usize, 1) << (new_props.lc + new_props.lp), 0x300);
79 }
80
81 self.lzma_props = new_props;
82 for (self.pos_slot_decoder) |*t| t.reset();
83 self.align_decoder.reset();
84 self.pos_decoders = .{0x400} ** 115;
85 self.is_match = .{0x400} ** 192;
86 self.is_rep = .{0x400} ** 12;
87 self.is_rep_g0 = .{0x400} ** 12;
88 self.is_rep_g1 = .{0x400} ** 12;
89 self.is_rep_g2 = .{0x400} ** 12;
90 self.is_rep_0long = .{0x400} ** 192;
91 self.state = 0;
92 self.rep = .{0} ** 4;
93 self.len_decoder.reset();
94 self.rep_len_decoder.reset();
95 }
96
97 fn processNextInner(
98 self: *DecoderState,
99 allocator: Allocator,
100 output: *LzAccumBuffer,
101 rangecoder: *RangeDecoder,
102 update: bool,
103 ) !ProcessingStatus {
104 const pos_state = output.len() & ((@as(usize, 1) << self.lzma_props.pb) - 1);
105
106 if (!try rangecoder.decodeBit(
107 &self.is_match[(self.state << 4) + pos_state],
108 update,
109 )) {
110 const byte: u8 = try self.decodeLiteral(output, rangecoder, update);
111
112 if (update) {
113 try output.appendLiteral(allocator, byte);
114
115 self.state = if (self.state < 4)
116 0
117 else if (self.state < 10)
118 self.state - 3
119 else
120 self.state - 6;
121 }
122 return .continue_;
123 }
124
125 var len: usize = undefined;
126 if (try rangecoder.decodeBit(&self.is_rep[self.state], update)) {
127 if (!try rangecoder.decodeBit(&self.is_rep_g0[self.state], update)) {
128 if (!try rangecoder.decodeBit(
129 &self.is_rep_0long[(self.state << 4) + pos_state],
130 update,
131 )) {
132 if (update) {
133 self.state = if (self.state < 7) 9 else 11;
134 const dist = self.rep[0] + 1;
135 try output.appendLz(allocator, 1, dist);
136 }
137 return .continue_;
138 }
139 } else {
140 const idx: usize = if (!try rangecoder.decodeBit(&self.is_rep_g1[self.state], update))
141 1
142 else if (!try rangecoder.decodeBit(&self.is_rep_g2[self.state], update))
143 2
144 else
145 3;
146 if (update) {
147 const dist = self.rep[idx];
148 var i = idx;
149 while (i > 0) : (i -= 1) {
150 self.rep[i] = self.rep[i - 1];
151 }
152 self.rep[0] = dist;
153 }
154 }
155
156 len = try self.rep_len_decoder.decode(rangecoder, pos_state, update);
157
158 if (update) {
159 self.state = if (self.state < 7) 8 else 11;
160 }
161 } else {
162 if (update) {
163 self.rep[3] = self.rep[2];
164 self.rep[2] = self.rep[1];
165 self.rep[1] = self.rep[0];
166 }
167
168 len = try self.len_decoder.decode(rangecoder, pos_state, update);
169
170 if (update) {
171 self.state = if (self.state < 7) 7 else 10;
172 }
173
174 const rep_0 = try self.decodeDistance(rangecoder, len, update);
175
176 if (update) {
177 self.rep[0] = rep_0;
178 if (self.rep[0] == 0xFFFF_FFFF) {
179 if (rangecoder.isFinished()) {
180 return .finished;
181 }
182 return error.CorruptInput;
183 }
184 }
185 }
186
187 if (update) {
188 len += 2;
189
190 const dist = self.rep[0] + 1;
191 try output.appendLz(allocator, len, dist);
192 }
193
194 return .continue_;
195 }
196
197 fn processNext(
198 self: *DecoderState,
199 allocator: Allocator,
200 output: *LzAccumBuffer,
201 rangecoder: *RangeDecoder,
202 ) !ProcessingStatus {
203 return self.processNextInner(allocator, output, rangecoder, true);
204 }
205
206 pub fn process(
207 self: *DecoderState,
208 allocator: Allocator,
209 output: *LzAccumBuffer,
210 rangecoder: *RangeDecoder,
211 ) !void {
212 while (true) {
213 if (self.unpacked_size) |unpacked_size| {
214 if (output.len() >= unpacked_size) {
215 break;
216 }
217 } else if (rangecoder.isFinished()) {
218 break;
219 }
220
221 if (try self.processNext(allocator, output, rangecoder) == .finished) {
222 break;
223 }
224 }
225
226 if (self.unpacked_size) |len| {
227 if (len != output.len()) {
228 return error.CorruptInput;
229 }
230 }
231 }
232
233 fn decodeLiteral(
234 self: *DecoderState,
235 output: *LzAccumBuffer,
236 rangecoder: *RangeDecoder,
237 update: bool,
238 ) !u8 {
239 const def_prev_byte = 0;
240 const prev_byte = @as(usize, output.lastOr(def_prev_byte));
241
242 var result: usize = 1;
243 const lit_state = ((output.len() & ((@as(usize, 1) << self.lzma_props.lp) - 1)) << self.lzma_props.lc) +
244 (prev_byte >> (8 - self.lzma_props.lc));
245 const probs = try self.literal_probs.get(lit_state);
246
247 if (self.state >= 7) {
248 var match_byte = @as(usize, try output.lastN(self.rep[0] + 1));
249
250 while (result < 0x100) {
251 const match_bit = (match_byte >> 7) & 1;
252 match_byte <<= 1;
253 const bit = @boolToInt(try rangecoder.decodeBit(
254 &probs[((@as(usize, 1) + match_bit) << 8) + result],
255 update,
256 ));
257 result = (result << 1) ^ bit;
258 if (match_bit != bit) {
259 break;
260 }
261 }
262 }
263
264 while (result < 0x100) {
265 result = (result << 1) ^ @boolToInt(try rangecoder.decodeBit(&probs[result], update));
266 }
267
268 return @truncate(u8, result - 0x100);
269 }
270
271 fn decodeDistance(
272 self: *DecoderState,
273 rangecoder: *RangeDecoder,
274 length: usize,
275 update: bool,
276 ) !usize {
277 const len_state = if (length > 3) 3 else length;
278
279 const pos_slot = @as(usize, try self.pos_slot_decoder[len_state].parse(rangecoder, update));
280 if (pos_slot < 4)
281 return pos_slot;
282
283 const num_direct_bits = @intCast(u5, (pos_slot >> 1) - 1);
284 var result = (2 ^ (pos_slot & 1)) << num_direct_bits;
285
286 if (pos_slot < 14) {
287 result += try rangecoder.parseReverseBitTree(
288 num_direct_bits,
289 &self.pos_decoders,
290 result - pos_slot,
291 update,
292 );
293 } else {
294 result += @as(usize, try rangecoder.get(num_direct_bits - 4)) << 4;
295 result += try self.align_decoder.parseReverse(rangecoder, update);
296 }
297
298 return result;
299 }
300};
301
302const ProcessingStatus = enum {
303 continue_,
304 finished,
305};
306
307pub const LzAccumBuffer = struct {
308 to_read: ArrayListUnmanaged(u8) = .{},
309 buf: ArrayListUnmanaged(u8) = .{},
310
311 pub fn deinit(self: *LzAccumBuffer, allocator: Allocator) void {
312 self.to_read.deinit(allocator);
313 self.buf.deinit(allocator);
314 }
315
316 pub fn read(self: *LzAccumBuffer, output: []u8) usize {
317 const input = self.to_read.items;
318 const n = std.math.min(input.len, output.len);
319 std.mem.copy(u8, output[0..n], input[0..n]);
320 std.mem.copy(u8, input, input[n..]);
321 self.to_read.shrinkRetainingCapacity(input.len - n);
322 return n;
323 }
324
325 pub fn ensureUnusedCapacity(
326 self: *LzAccumBuffer,
327 allocator: Allocator,
328 additional_count: usize,
329 ) !void {
330 try self.buf.ensureUnusedCapacity(allocator, additional_count);
331 }
332
333 pub fn appendAssumeCapacity(self: *LzAccumBuffer, byte: u8) void {
334 self.buf.appendAssumeCapacity(byte);
335 }
336
337 pub fn reset(self: *LzAccumBuffer, allocator: Allocator) !void {
338 try self.to_read.appendSlice(allocator, self.buf.items);
339 self.buf.clearRetainingCapacity();
340 }
341
342 pub fn len(self: *const LzAccumBuffer) usize {
343 return self.buf.items.len;
344 }
345
346 pub fn lastOr(self: *const LzAccumBuffer, lit: u8) u8 {
347 const buf_len = self.buf.items.len;
348 return if (buf_len == 0)
349 lit
350 else
351 self.buf.items[buf_len - 1];
352 }
353
354 pub fn lastN(self: *const LzAccumBuffer, dist: usize) !u8 {
355 const buf_len = self.buf.items.len;
356 if (dist > buf_len) {
357 return error.CorruptInput;
358 }
359
360 return self.buf.items[buf_len - dist];
361 }
362
363 pub fn appendLiteral(self: *LzAccumBuffer, allocator: Allocator, lit: u8) !void {
364 try self.buf.append(allocator, lit);
365 }
366
367 pub fn appendLz(self: *LzAccumBuffer, allocator: Allocator, length: usize, dist: usize) !void {
368 const buf_len = self.buf.items.len;
369 if (dist > buf_len) {
370 return error.CorruptInput;
371 }
372
373 var offset = buf_len - dist;
374 var i: usize = 0;
375 while (i < length) : (i += 1) {
376 const x = self.buf.items[offset];
377 try self.buf.append(allocator, x);
378 offset += 1;
379 }
380 }
381};
382
383pub const RangeDecoder = struct {
384 stream: std.io.FixedBufferStream([]const u8),
385 range: u32,
386 code: u32,
387
388 pub fn init(buffer: []const u8) !RangeDecoder {
389 var dec = RangeDecoder{
390 .stream = std.io.fixedBufferStream(buffer),
391 .range = 0xFFFF_FFFF,
392 .code = 0,
393 };
394 const reader = dec.stream.reader();
395 _ = try reader.readByte();
396 dec.code = try reader.readIntBig(u32);
397 return dec;
398 }
399
400 pub fn fromParts(
401 buffer: []const u8,
402 range: u32,
403 code: u32,
404 ) RangeDecoder {
405 return .{
406 .stream = std.io.fixedBufferStream(buffer),
407 .range = range,
408 .code = code,
409 };
410 }
411
412 pub fn set(self: *RangeDecoder, range: u32, code: u32) void {
413 self.range = range;
414 self.code = code;
415 }
416
417 pub fn readInto(self: *RangeDecoder, dest: []u8) !usize {
418 return self.stream.read(dest);
419 }
420
421 pub inline fn isFinished(self: *const RangeDecoder) bool {
422 return self.code == 0 and self.isEof();
423 }
424
425 pub inline fn isEof(self: *const RangeDecoder) bool {
426 return self.stream.pos == self.stream.buffer.len;
427 }
428
429 inline fn normalize(self: *RangeDecoder) !void {
430 if (self.range < 0x0100_0000) {
431 self.range <<= 8;
432 self.code = (self.code << 8) ^ @as(u32, try self.stream.reader().readByte());
433 }
434 }
435
436 inline fn getBit(self: *RangeDecoder) !bool {
437 self.range >>= 1;
438
439 const bit = self.code >= self.range;
440 if (bit)
441 self.code -= self.range;
442
443 try self.normalize();
444 return bit;
445 }
446
447 fn get(self: *RangeDecoder, count: usize) !u32 {
448 var result: u32 = 0;
449 var i: usize = 0;
450 while (i < count) : (i += 1)
451 result = (result << 1) ^ @boolToInt(try self.getBit());
452 return result;
453 }
454
455 pub inline fn decodeBit(self: *RangeDecoder, prob: *u16, update: bool) !bool {
456 const bound = (self.range >> 11) * prob.*;
457
458 if (self.code < bound) {
459 if (update)
460 prob.* += (0x800 - prob.*) >> 5;
461 self.range = bound;
462
463 try self.normalize();
464 return false;
465 } else {
466 if (update)
467 prob.* -= prob.* >> 5;
468 self.code -= bound;
469 self.range -= bound;
470
471 try self.normalize();
472 return true;
473 }
474 }
475
476 fn parseBitTree(
477 self: *RangeDecoder,
478 num_bits: u5,
479 probs: []u16,
480 update: bool,
481 ) !u32 {
482 var tmp: u32 = 1;
483 var i: u5 = 0;
484 while (i < num_bits) : (i += 1) {
485 const bit = try self.decodeBit(&probs[tmp], update);
486 tmp = (tmp << 1) ^ @boolToInt(bit);
487 }
488 return tmp - (@as(u32, 1) << num_bits);
489 }
490
491 pub fn parseReverseBitTree(
492 self: *RangeDecoder,
493 num_bits: u5,
494 probs: []u16,
495 offset: usize,
496 update: bool,
497 ) !u32 {
498 var result: u32 = 0;
499 var tmp: usize = 1;
500 var i: u5 = 0;
501 while (i < num_bits) : (i += 1) {
502 const bit = @boolToInt(try self.decodeBit(&probs[offset + tmp], update));
503 tmp = (tmp << 1) ^ bit;
504 result ^= @as(u32, bit) << i;
505 }
506 return result;
507 }
508};
509
510fn Vec2D(comptime T: type) type {
511 return struct {
512 data: []T,
513 cols: usize,
514
515 const Self = @This();
516
517 pub fn init(allocator: Allocator, data: T, rows: usize, cols: usize) !Self {
518 const len = try std.math.mul(usize, rows, cols);
519 var vec2d = Self{
520 .data = try allocator.alloc(T, len),
521 .cols = cols,
522 };
523 vec2d.fill(data);
524 return vec2d;
525 }
526
527 pub fn deinit(self: *Self, allocator: Allocator) void {
528 allocator.free(self.data);
529 }
530
531 pub fn fill(self: *Self, value: T) void {
532 std.mem.set(T, self.data, value);
533 }
534
535 pub fn get(self: *Self, row: usize) ![]T {
536 const start_row = try std.math.mul(usize, row, self.cols);
537 return self.data[start_row .. start_row + self.cols];
538 }
539 };
540}
541
542const BitTree = struct {
543 num_bits: u5,
544 probs: ArrayListUnmanaged(u16),
545
546 pub fn init(allocator: Allocator, num_bits: u5) !BitTree {
547 var probs_len = @as(usize, 1) << num_bits;
548 var probs = try ArrayListUnmanaged(u16).initCapacity(allocator, probs_len);
549 while (probs_len > 0) : (probs_len -= 1)
550 probs.appendAssumeCapacity(0x400);
551 return .{ .num_bits = num_bits, .probs = probs };
552 }
553
554 pub fn deinit(self: *BitTree, allocator: Allocator) void {
555 self.probs.deinit(allocator);
556 }
557
558 pub fn parse(
559 self: *BitTree,
560 rangecoder: *RangeDecoder,
561 update: bool,
562 ) !u32 {
563 return rangecoder.parseBitTree(self.num_bits, self.probs.items, update);
564 }
565
566 pub fn parseReverse(
567 self: *BitTree,
568 rangecoder: *RangeDecoder,
569 update: bool,
570 ) !u32 {
571 return rangecoder.parseReverseBitTree(self.num_bits, self.probs.items, 0, update);
572 }
573
574 pub fn reset(self: *BitTree) void {
575 std.mem.set(u16, self.probs.items, 0x400);
576 }
577};
578
579const LenDecoder = struct {
580 choice: u16,
581 choice2: u16,
582 low_coder: [16]BitTree,
583 mid_coder: [16]BitTree,
584 high_coder: BitTree,
585
586 pub fn init(allocator: Allocator) !LenDecoder {
587 return .{
588 .choice = 0x400,
589 .choice2 = 0x400,
590 .low_coder = .{
591 try BitTree.init(allocator, 3),
592 try BitTree.init(allocator, 3),
593 try BitTree.init(allocator, 3),
594 try BitTree.init(allocator, 3),
595 try BitTree.init(allocator, 3),
596 try BitTree.init(allocator, 3),
597 try BitTree.init(allocator, 3),
598 try BitTree.init(allocator, 3),
599 try BitTree.init(allocator, 3),
600 try BitTree.init(allocator, 3),
601 try BitTree.init(allocator, 3),
602 try BitTree.init(allocator, 3),
603 try BitTree.init(allocator, 3),
604 try BitTree.init(allocator, 3),
605 try BitTree.init(allocator, 3),
606 try BitTree.init(allocator, 3),
607 },
608 .mid_coder = .{
609 try BitTree.init(allocator, 3),
610 try BitTree.init(allocator, 3),
611 try BitTree.init(allocator, 3),
612 try BitTree.init(allocator, 3),
613 try BitTree.init(allocator, 3),
614 try BitTree.init(allocator, 3),
615 try BitTree.init(allocator, 3),
616 try BitTree.init(allocator, 3),
617 try BitTree.init(allocator, 3),
618 try BitTree.init(allocator, 3),
619 try BitTree.init(allocator, 3),
620 try BitTree.init(allocator, 3),
621 try BitTree.init(allocator, 3),
622 try BitTree.init(allocator, 3),
623 try BitTree.init(allocator, 3),
624 try BitTree.init(allocator, 3),
625 },
626 .high_coder = try BitTree.init(allocator, 8),
627 };
628 }
629
630 pub fn deinit(self: *LenDecoder, allocator: Allocator) void {
631 for (self.low_coder) |*t| t.deinit(allocator);
632 for (self.mid_coder) |*t| t.deinit(allocator);
633 self.high_coder.deinit(allocator);
634 }
635
636 pub fn decode(
637 self: *LenDecoder,
638 rangecoder: *RangeDecoder,
639 pos_state: usize,
640 update: bool,
641 ) !usize {
642 if (!try rangecoder.decodeBit(&self.choice, update)) {
643 return @as(usize, try self.low_coder[pos_state].parse(rangecoder, update));
644 } else if (!try rangecoder.decodeBit(&self.choice2, update)) {
645 return @as(usize, try self.mid_coder[pos_state].parse(rangecoder, update)) + 8;
646 } else {
647 return @as(usize, try self.high_coder.parse(rangecoder, update)) + 16;
648 }
649 }
650
651 pub fn reset(self: *LenDecoder) void {
652 self.choice = 0x400;
653 self.choice2 = 0x400;
654 for (self.low_coder) |*t| t.reset();
655 for (self.mid_coder) |*t| t.reset();
656 self.high_coder.reset();
657 }
658};
lib/std/compress/xz/test.zig+20
...@@ -78,3 +78,23 @@ test "unsupported" {...@@ -78,3 +78,23 @@ test "unsupported" {
78 );78 );
79 }79 }
80}80}
81
82fn testDontPanic(data: []const u8) !void {
83 const buf = decompress(data) catch |err| switch (err) {
84 error.OutOfMemory => |e| return e,
85 else => return,
86 };
87 defer testing.allocator.free(buf);
88}
89
90test "size fields: integer overflow avoidance" {
91 // These cases were found via fuzz testing and each previously caused
92 // an integer overflow when decoding. We just want to ensure they no longer
93 // cause a panic
94 const header_size_overflow = "\xfd7zXZ\x00\x00\x01i\"\xde6z";
95 try testDontPanic(header_size_overflow);
96 const lzma2_chunk_size_overflow = "\xfd7zXZ\x00\x00\x01i\"\xde6\x02\x00!\x01\x08\x00\x00\x00\xd8\x0f#\x13\x01\xff\xff";
97 try testDontPanic(lzma2_chunk_size_overflow);
98 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";
99 try testDontPanic(backward_size_overflow);
100}
lib/std/crypto.zig+5
...@@ -41,11 +41,13 @@ pub const auth = struct {...@@ -41,11 +41,13 @@ pub const auth = struct {
41 pub const Aegis128LMac = @import("crypto/aegis.zig").Aegis128LMac;41 pub const Aegis128LMac = @import("crypto/aegis.zig").Aegis128LMac;
42 pub const Aegis256Mac = @import("crypto/aegis.zig").Aegis256Mac;42 pub const Aegis256Mac = @import("crypto/aegis.zig").Aegis256Mac;
43 };43 };
44 pub const cmac = @import("crypto/cmac.zig");
44};45};
4546
46/// Core functions, that should rarely be used directly by applications.47/// Core functions, that should rarely be used directly by applications.
47pub const core = struct {48pub const core = struct {
48 pub const aes = @import("crypto/aes.zig");49 pub const aes = @import("crypto/aes.zig");
50 pub const Ascon = @import("crypto/ascon.zig").State;
49 pub const Gimli = @import("crypto/gimli.zig").State;51 pub const Gimli = @import("crypto/gimli.zig").State;
50 pub const Xoodoo = @import("crypto/xoodoo.zig").State;52 pub const Xoodoo = @import("crypto/xoodoo.zig").State;
5153
...@@ -202,10 +204,13 @@ test {...@@ -202,10 +204,13 @@ test {
202 _ = aead.salsa_poly.XSalsa20Poly1305;204 _ = aead.salsa_poly.XSalsa20Poly1305;
203205
204 _ = auth.hmac;206 _ = auth.hmac;
207 _ = auth.cmac;
205 _ = auth.siphash;208 _ = auth.siphash;
206209
207 _ = core.aes;210 _ = core.aes;
211 _ = core.Ascon;
208 _ = core.Gimli;212 _ = core.Gimli;
213 _ = core.Xoodoo;
209 _ = core.modes;214 _ = core.modes;
210215
211 _ = dh.X25519;216 _ = dh.X25519;
lib/std/crypto/ascon.zig created+227
...@@ -0,0 +1,227 @@
1//! Ascon is a 320-bit permutation, selected as new standard for lightweight cryptography
2//! in the NIST Lightweight Cryptography competition (2019–2023).
3//! https://csrc.nist.gov/News/2023/lightweight-cryptography-nist-selects-ascon
4//!
5//! The permutation is compact, and optimized for timing and side channel resistance,
6//! making it a good choice for embedded applications.
7//!
8//! It is not meant to be used directly, but as a building block for symmetric cryptography.
9
10const std = @import("std");
11const builtin = std.builtin;
12const debug = std.debug;
13const mem = std.mem;
14const testing = std.testing;
15const rotr = std.math.rotr;
16
17/// An Ascon state.
18///
19/// The state is represented as 5 64-bit words.
20///
21/// The NIST submission (v1.2) serializes these words as big-endian,
22/// but software implementations are free to use native endianness.
23pub fn State(comptime endian: builtin.Endian) type {
24 return struct {
25 const Self = @This();
26
27 /// Number of bytes in the state.
28 pub const block_bytes = 40;
29
30 const Block = [5]u64;
31
32 st: Block,
33
34 /// Initialize the state from a slice of bytes.
35 pub fn init(initial_state: [block_bytes]u8) Self {
36 var state = Self{ .st = undefined };
37 mem.copy(u8, state.asBytes(), &initial_state);
38 state.endianSwap();
39 return state;
40 }
41
42 /// Initialize the state from u64 words in native endianness.
43 pub fn initFromWords(initial_state: [5]u64) Self {
44 var state = Self{ .st = initial_state };
45 return state;
46 }
47
48 /// Initialize the state for Ascon XOF
49 pub fn initXof() Self {
50 return Self{ .st = Block{
51 0xb57e273b814cd416,
52 0x2b51042562ae2420,
53 0x66a3a7768ddf2218,
54 0x5aad0a7a8153650c,
55 0x4f3e0e32539493b6,
56 } };
57 }
58
59 /// Initialize the state for Ascon XOFa
60 pub fn initXofA() Self {
61 return Self{ .st = Block{
62 0x44906568b77b9832,
63 0xcd8d6cae53455532,
64 0xf7b5212756422129,
65 0x246885e1de0d225b,
66 0xa8cb5ce33449973f,
67 } };
68 }
69
70 /// A representation of the state as bytes. The byte order is architecture-dependent.
71 pub fn asBytes(self: *Self) *[block_bytes]u8 {
72 return mem.asBytes(&self.st);
73 }
74
75 /// Byte-swap the entire state if the architecture doesn't match the required endianness.
76 pub fn endianSwap(self: *Self) void {
77 for (self.st) |*w| {
78 w.* = mem.toNative(u64, w.*, endian);
79 }
80 }
81
82 /// Set bytes starting at the beginning of the state.
83 pub fn setBytes(self: *Self, bytes: []const u8) void {
84 var i: usize = 0;
85 while (i + 8 <= bytes.len) : (i += 8) {
86 self.st[i / 8] = mem.readInt(u64, bytes[i..][0..8], endian);
87 }
88 if (i < bytes.len) {
89 var padded = [_]u8{0} ** 8;
90 mem.copy(u8, padded[0 .. bytes.len - i], bytes[i..]);
91 self.st[i / 8] = mem.readInt(u64, padded[0..], endian);
92 }
93 }
94
95 /// XOR a byte into the state at a given offset.
96 pub fn addByte(self: *Self, byte: u8, offset: usize) void {
97 const z = switch (endian) {
98 .Big => 64 - 8 - 8 * @truncate(u6, offset % 8),
99 .Little => 8 * @truncate(u6, offset % 8),
100 };
101 self.st[offset / 8] ^= @as(u64, byte) << z;
102 }
103
104 /// XOR bytes into the beginning of the state.
105 pub fn addBytes(self: *Self, bytes: []const u8) void {
106 var i: usize = 0;
107 while (i + 8 <= bytes.len) : (i += 8) {
108 self.st[i / 8] ^= mem.readInt(u64, bytes[i..][0..8], endian);
109 }
110 if (i < bytes.len) {
111 var padded = [_]u8{0} ** 8;
112 mem.copy(u8, padded[0 .. bytes.len - i], bytes[i..]);
113 self.st[i / 8] ^= mem.readInt(u64, padded[0..], endian);
114 }
115 }
116
117 /// Extract the first bytes of the state.
118 pub fn extractBytes(self: *Self, out: []u8) void {
119 var i: usize = 0;
120 while (i + 8 <= out.len) : (i += 8) {
121 mem.writeInt(u64, out[i..][0..8], self.st[i / 8], endian);
122 }
123 if (i < out.len) {
124 var padded = [_]u8{0} ** 8;
125 mem.writeInt(u64, padded[0..], self.st[i / 8], endian);
126 mem.copy(u8, out[i..], padded[0 .. out.len - i]);
127 }
128 }
129
130 /// XOR the first bytes of the state into a slice of bytes.
131 pub fn xorBytes(self: *Self, out: []u8, in: []const u8) void {
132 debug.assert(out.len == in.len);
133
134 var i: usize = 0;
135 while (i + 8 <= in.len) : (i += 8) {
136 const x = mem.readIntNative(u64, in[i..][0..8]) ^ mem.nativeTo(u64, self.st[i / 8], endian);
137 mem.writeIntNative(u64, out[i..][0..8], x);
138 }
139 if (i < in.len) {
140 var padded = [_]u8{0} ** 8;
141 mem.copy(u8, padded[0 .. in.len - i], in[i..]);
142 const x = mem.readIntNative(u64, &padded) ^ mem.nativeTo(u64, self.st[i / 8], endian);
143 mem.writeIntNative(u64, &padded, x);
144 mem.copy(u8, out[i..], padded[0 .. in.len - i]);
145 }
146 }
147
148 /// Set the words storing the bytes of a given range to zero.
149 pub fn clear(self: *Self, from: usize, to: usize) void {
150 mem.set(u64, self.st[from / 8 .. (to + 7) / 8], 0);
151 }
152
153 /// Clear the entire state, disabling compiler optimizations.
154 pub fn secureZero(self: *Self) void {
155 std.crypto.utils.secureZero(u64, &self.st);
156 }
157
158 /// Apply a reduced-round permutation to the state.
159 pub inline fn permuteR(state: *Self, comptime rounds: u4) void {
160 const rks = [12]u64{ 0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b };
161 inline for (rks[rks.len - rounds ..]) |rk| {
162 state.round(rk);
163 }
164 }
165
166 /// Apply a full-round permutation to the state.
167 pub inline fn permute(state: *Self) void {
168 state.permuteR(12);
169 }
170
171 // Core Ascon permutation.
172 inline fn round(state: *Self, rk: u64) void {
173 const x = &state.st;
174 x[2] ^= rk;
175
176 x[0] ^= x[4];
177 x[4] ^= x[3];
178 x[2] ^= x[1];
179 var t: Block = .{
180 x[0] ^ (~x[1] & x[2]),
181 x[1] ^ (~x[2] & x[3]),
182 x[2] ^ (~x[3] & x[4]),
183 x[3] ^ (~x[4] & x[0]),
184 x[4] ^ (~x[0] & x[1]),
185 };
186 t[1] ^= t[0];
187 t[3] ^= t[2];
188 t[0] ^= t[4];
189
190 x[2] = t[2] ^ rotr(u64, t[2], 6 - 1);
191 x[3] = t[3] ^ rotr(u64, t[3], 17 - 10);
192 x[4] = t[4] ^ rotr(u64, t[4], 41 - 7);
193 x[0] = t[0] ^ rotr(u64, t[0], 28 - 19);
194 x[1] = t[1] ^ rotr(u64, t[1], 61 - 39);
195 x[2] = t[2] ^ rotr(u64, x[2], 1);
196 x[3] = t[3] ^ rotr(u64, x[3], 10);
197 x[4] = t[4] ^ rotr(u64, x[4], 7);
198 x[0] = t[0] ^ rotr(u64, x[0], 19);
199 x[1] = t[1] ^ rotr(u64, x[1], 39);
200 x[2] = ~x[2];
201 }
202 };
203}
204
205test "ascon" {
206 const Ascon = State(.Big);
207 const bytes = [_]u8{0x01} ** Ascon.block_bytes;
208 var st = Ascon.init(bytes);
209 var out: [Ascon.block_bytes]u8 = undefined;
210 st.permute();
211 st.extractBytes(&out);
212 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 };
213 try testing.expectEqualSlices(u8, &expected1, &out);
214 st.clear(0, 10);
215 st.extractBytes(&out);
216 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 };
217 try testing.expectEqualSlices(u8, &expected2, &out);
218 st.addByte(1, 5);
219 st.addByte(2, 5);
220 st.extractBytes(&out);
221 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 };
222 try testing.expectEqualSlices(u8, &expected3, &out);
223 st.addBytes(&bytes);
224 st.extractBytes(&out);
225 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 };
226 try testing.expectEqualSlices(u8, &expected4, &out);
227}
lib/std/crypto/benchmark.zig+1
...@@ -66,6 +66,7 @@ const macs = [_]Crypto{...@@ -66,6 +66,7 @@ const macs = [_]Crypto{
66 Crypto{ .ty = crypto.auth.siphash.SipHash128(1, 3), .name = "siphash128-1-3" },66 Crypto{ .ty = crypto.auth.siphash.SipHash128(1, 3), .name = "siphash128-1-3" },
67 Crypto{ .ty = crypto.auth.aegis.Aegis128LMac, .name = "aegis-128l mac" },67 Crypto{ .ty = crypto.auth.aegis.Aegis128LMac, .name = "aegis-128l mac" },
68 Crypto{ .ty = crypto.auth.aegis.Aegis256Mac, .name = "aegis-256 mac" },68 Crypto{ .ty = crypto.auth.aegis.Aegis256Mac, .name = "aegis-256 mac" },
69 Crypto{ .ty = crypto.auth.cmac.CmacAes128, .name = "aes-cmac" },
69};70};
7071
71pub fn benchmarkMac(comptime Mac: anytype, comptime bytes: comptime_int) !u64 {72pub fn benchmarkMac(comptime Mac: anytype, comptime bytes: comptime_int) !u64 {
lib/std/crypto/cmac.zig created+156
...@@ -0,0 +1,156 @@
1const std = @import("std");
2const crypto = std.crypto;
3const mem = std.mem;
4
5/// CMAC with AES-128 - RFC 4493 https://www.rfc-editor.org/rfc/rfc4493
6pub const CmacAes128 = Cmac(crypto.core.aes.Aes128);
7
8/// NIST Special Publication 800-38B - The CMAC Mode for Authentication
9/// https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38b.pdf
10pub fn Cmac(comptime BlockCipher: type) type {
11 const BlockCipherCtx = @typeInfo(@TypeOf(BlockCipher.initEnc)).Fn.return_type.?;
12 const Block = [BlockCipher.block.block_length]u8;
13
14 return struct {
15 const Self = @This();
16 pub const key_length = BlockCipher.key_bits / 8;
17 pub const block_length = BlockCipher.block.block_length;
18 pub const mac_length = block_length;
19
20 cipher_ctx: BlockCipherCtx,
21 k1: Block,
22 k2: Block,
23 buf: Block = [_]u8{0} ** block_length,
24 pos: usize = 0,
25
26 pub fn create(out: *[mac_length]u8, msg: []const u8, key: *const [key_length]u8) void {
27 var ctx = Self.init(key);
28 ctx.update(msg);
29 ctx.final(out);
30 }
31
32 pub fn init(key: *const [key_length]u8) Self {
33 const cipher_ctx = BlockCipher.initEnc(key.*);
34 const zeros = [_]u8{0} ** block_length;
35 var k1: Block = undefined;
36 cipher_ctx.encrypt(&k1, &zeros);
37 k1 = double(k1);
38 return Self{
39 .cipher_ctx = cipher_ctx,
40 .k1 = k1,
41 .k2 = double(k1),
42 };
43 }
44
45 pub fn update(self: *Self, msg: []const u8) void {
46 const left = block_length - self.pos;
47 var m = msg;
48 if (m.len > left) {
49 for (self.buf[self.pos..]) |*b, i| b.* ^= m[i];
50 m = m[left..];
51 self.cipher_ctx.encrypt(&self.buf, &self.buf);
52 self.pos = 0;
53 }
54 while (m.len > block_length) {
55 for (self.buf[0..block_length]) |*b, i| b.* ^= m[i];
56 m = m[block_length..];
57 self.cipher_ctx.encrypt(&self.buf, &self.buf);
58 self.pos = 0;
59 }
60 if (m.len > 0) {
61 for (self.buf[self.pos..][0..m.len]) |*b, i| b.* ^= m[i];
62 self.pos += m.len;
63 }
64 }
65
66 pub fn final(self: *Self, out: *[mac_length]u8) void {
67 var mac = self.k1;
68 if (self.pos < block_length) {
69 mac = self.k2;
70 mac[self.pos] ^= 0x80;
71 }
72 for (mac) |*b, i| b.* ^= self.buf[i];
73 self.cipher_ctx.encrypt(out, &mac);
74 }
75
76 fn double(l: Block) Block {
77 const Int = std.meta.Int(.unsigned, block_length * 8);
78 const l_ = mem.readIntBig(Int, &l);
79 const l_2 = switch (block_length) {
80 8 => (l_ << 1) ^ (0x1b & -%(l_ >> 63)), // mod x^64 + x^4 + x^3 + x + 1
81 16 => (l_ << 1) ^ (0x87 & -%(l_ >> 127)), // mod x^128 + x^7 + x^2 + x + 1
82 32 => (l_ << 1) ^ (0x0425 & -%(l_ >> 255)), // mod x^256 + x^10 + x^5 + x^2 + 1
83 64 => (l_ << 1) ^ (0x0125 & -%(l_ >> 511)), // mod x^512 + x^8 + x^5 + x^2 + 1
84 else => @compileError("unsupported block length"),
85 };
86 var l2: Block = undefined;
87 mem.writeIntBig(Int, &l2, l_2);
88 return l2;
89 }
90 };
91}
92
93const testing = std.testing;
94
95test "CmacAes128 - Example 1: len = 0" {
96 const key = [_]u8{
97 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c,
98 };
99 var msg: [0]u8 = undefined;
100 const exp = [_]u8{
101 0xbb, 0x1d, 0x69, 0x29, 0xe9, 0x59, 0x37, 0x28, 0x7f, 0xa3, 0x7d, 0x12, 0x9b, 0x75, 0x67, 0x46,
102 };
103 var out: [CmacAes128.mac_length]u8 = undefined;
104 CmacAes128.create(&out, &msg, &key);
105 try testing.expectEqualSlices(u8, &out, &exp);
106}
107
108test "CmacAes128 - Example 2: len = 16" {
109 const key = [_]u8{
110 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c,
111 };
112 const msg = [_]u8{
113 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
114 };
115 const exp = [_]u8{
116 0x07, 0x0a, 0x16, 0xb4, 0x6b, 0x4d, 0x41, 0x44, 0xf7, 0x9b, 0xdd, 0x9d, 0xd0, 0x4a, 0x28, 0x7c,
117 };
118 var out: [CmacAes128.mac_length]u8 = undefined;
119 CmacAes128.create(&out, &msg, &key);
120 try testing.expectEqualSlices(u8, &out, &exp);
121}
122
123test "CmacAes128 - Example 3: len = 40" {
124 const key = [_]u8{
125 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c,
126 };
127 const msg = [_]u8{
128 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
129 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
130 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
131 };
132 const exp = [_]u8{
133 0xdf, 0xa6, 0x67, 0x47, 0xde, 0x9a, 0xe6, 0x30, 0x30, 0xca, 0x32, 0x61, 0x14, 0x97, 0xc8, 0x27,
134 };
135 var out: [CmacAes128.mac_length]u8 = undefined;
136 CmacAes128.create(&out, &msg, &key);
137 try testing.expectEqualSlices(u8, &out, &exp);
138}
139
140test "CmacAes128 - Example 4: len = 64" {
141 const key = [_]u8{
142 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c,
143 };
144 const msg = [_]u8{
145 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
146 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
147 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
148 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10,
149 };
150 const exp = [_]u8{
151 0x51, 0xf0, 0xbe, 0xbf, 0x7e, 0x3b, 0x9d, 0x92, 0xfc, 0x49, 0x74, 0x17, 0x79, 0x36, 0x3c, 0xfe,
152 };
153 var out: [CmacAes128.mac_length]u8 = undefined;
154 CmacAes128.create(&out, &msg, &key);
155 try testing.expectEqualSlices(u8, &out, &exp);
156}
lib/std/crypto/isap.zig+40-114
...@@ -1,9 +1,11 @@...@@ -1,9 +1,11 @@
1const std = @import("std");1const std = @import("std");
2const crypto = std.crypto;
2const debug = std.debug;3const debug = std.debug;
3const mem = std.mem;4const mem = std.mem;
4const math = std.math;5const math = std.math;
5const testing = std.testing;6const testing = std.testing;
6const AuthenticationError = std.crypto.errors.AuthenticationError;7const Ascon = crypto.core.Ascon(.Big);
8const AuthenticationError = crypto.errors.AuthenticationError;
79
8/// ISAPv2 is an authenticated encryption system hardened against side channels and fault attacks.10/// ISAPv2 is an authenticated encryption system hardened against side channels and fault attacks.
9/// https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/round-2/spec-doc-rnd2/isap-spec-round2.pdf11/// 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 {...@@ -25,90 +27,26 @@ pub const IsapA128A = struct {
25 const iv2 = [_]u8{ 0x02, 0x80, 0x40, 0x01, 0x0c, 0x01, 0x06, 0x0c };27 const iv2 = [_]u8{ 0x02, 0x80, 0x40, 0x01, 0x0c, 0x01, 0x06, 0x0c };
26 const iv3 = [_]u8{ 0x03, 0x80, 0x40, 0x01, 0x0c, 0x01, 0x06, 0x0c };28 const iv3 = [_]u8{ 0x03, 0x80, 0x40, 0x01, 0x0c, 0x01, 0x06, 0x0c };
2729
28 const Block = [5]u64;30 st: Ascon,
29
30 block: Block,
31
32 fn round(isap: *IsapA128A, rk: u64) void {
33 var x = &isap.block;
34 x[2] ^= rk;
35 x[0] ^= x[4];
36 x[4] ^= x[3];
37 x[2] ^= x[1];
38 var t = x.*;
39 x[0] = t[0] ^ ((~t[1]) & t[2]);
40 x[2] = t[2] ^ ((~t[3]) & t[4]);
41 x[4] = t[4] ^ ((~t[0]) & t[1]);
42 x[1] = t[1] ^ ((~t[2]) & t[3]);
43 x[3] = t[3] ^ ((~t[4]) & t[0]);
44 x[1] ^= x[0];
45 t[1] = x[1];
46 x[1] = math.rotr(u64, x[1], 39);
47 x[3] ^= x[2];
48 t[2] = x[2];
49 x[2] = math.rotr(u64, x[2], 1);
50 t[4] = x[4];
51 t[2] ^= x[2];
52 x[2] = math.rotr(u64, x[2], 5);
53 t[3] = x[3];
54 t[1] ^= x[1];
55 x[3] = math.rotr(u64, x[3], 10);
56 x[0] ^= x[4];
57 x[4] = math.rotr(u64, x[4], 7);
58 t[3] ^= x[3];
59 x[2] ^= t[2];
60 x[1] = math.rotr(u64, x[1], 22);
61 t[0] = x[0];
62 x[2] = ~x[2];
63 x[3] = math.rotr(u64, x[3], 7);
64 t[4] ^= x[4];
65 x[4] = math.rotr(u64, x[4], 34);
66 x[3] ^= t[3];
67 x[1] ^= t[1];
68 x[0] = math.rotr(u64, x[0], 19);
69 x[4] ^= t[4];
70 t[0] ^= x[0];
71 x[0] = math.rotr(u64, x[0], 9);
72 x[0] ^= t[0];
73 }
74
75 fn p12(isap: *IsapA128A) void {
76 const rks = [12]u64{ 0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b };
77 inline for (rks) |rk| {
78 isap.round(rk);
79 }
80 }
81
82 fn p6(isap: *IsapA128A) void {
83 const rks = [6]u64{ 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b };
84 inline for (rks) |rk| {
85 isap.round(rk);
86 }
87 }
88
89 fn p1(isap: *IsapA128A) void {
90 isap.round(0x4b);
91 }
9231
93 fn absorb(isap: *IsapA128A, m: []const u8) void {32 fn absorb(isap: *IsapA128A, m: []const u8) void {
94 var block = &isap.block;
95 var i: usize = 0;33 var i: usize = 0;
96 while (true) : (i += 8) {34 while (true) : (i += 8) {
97 const left = m.len - i;35 const left = m.len - i;
98 if (left >= 8) {36 if (left >= 8) {
99 block[0] ^= mem.readIntBig(u64, m[i..][0..8]);37 isap.st.addBytes(m[i..][0..8]);
100 isap.p12();38 isap.st.permute();
101 if (left == 8) {39 if (left == 8) {
102 block[0] ^= 0x8000000000000000;40 isap.st.addByte(0x80, 0);
103 isap.p12();41 isap.st.permute();
104 break;42 break;
105 }43 }
106 } else {44 } else {
107 var padded = [_]u8{0} ** 8;45 var padded = [_]u8{0} ** 8;
108 mem.copy(u8, padded[0..left], m[i..]);46 mem.copy(u8, padded[0..left], m[i..]);
109 padded[left] = 0x80;47 padded[left] = 0x80;
110 block[0] ^= mem.readIntBig(u64, padded[0..]);48 isap.st.addBytes(&padded);
111 isap.p12();49 isap.st.permute();
112 break;50 break;
113 }51 }
114 }52 }
...@@ -116,65 +54,59 @@ pub const IsapA128A = struct {...@@ -116,65 +54,59 @@ pub const IsapA128A = struct {
11654
117 fn trickle(k: [16]u8, iv: [8]u8, y: []const u8, comptime out_len: usize) [out_len]u8 {55 fn trickle(k: [16]u8, iv: [8]u8, y: []const u8, comptime out_len: usize) [out_len]u8 {
118 var isap = IsapA128A{56 var isap = IsapA128A{
119 .block = Block{57 .st = Ascon.initFromWords(.{
120 mem.readIntBig(u64, k[0..8]),58 mem.readIntBig(u64, k[0..8]),
121 mem.readIntBig(u64, k[8..16]),59 mem.readIntBig(u64, k[8..16]),
122 mem.readIntBig(u64, iv[0..8]),60 mem.readIntBig(u64, iv[0..8]),
123 0,61 0,
124 0,62 0,
125 },63 }),
126 };64 };
127 isap.p12();65 isap.st.permute();
12866
129 var i: usize = 0;67 var i: usize = 0;
130 while (i < y.len * 8 - 1) : (i += 1) {68 while (i < y.len * 8 - 1) : (i += 1) {
131 const cur_byte_pos = i / 8;69 const cur_byte_pos = i / 8;
132 const cur_bit_pos = @truncate(u3, 7 - (i % 8));70 const cur_bit_pos = @truncate(u3, 7 - (i % 8));
133 const cur_bit = @as(u64, ((y[cur_byte_pos] >> cur_bit_pos) & 1) << 7);71 const cur_bit = ((y[cur_byte_pos] >> cur_bit_pos) & 1) << 7;
134 isap.block[0] ^= cur_bit << 56;72 isap.st.addByte(cur_bit, 0);
135 isap.p1();73 isap.st.permuteR(1);
136 }74 }
137 const cur_bit = @as(u64, (y[y.len - 1] & 1) << 7);75 const cur_bit = (y[y.len - 1] & 1) << 7;
138 isap.block[0] ^= cur_bit << 56;76 isap.st.addByte(cur_bit, 0);
139 isap.p12();77 isap.st.permute();
14078
141 var out: [out_len]u8 = undefined;79 var out: [out_len]u8 = undefined;
142 var j: usize = 0;80 isap.st.extractBytes(&out);
143 while (j < out_len) : (j += 8) {81 isap.st.secureZero();
144 mem.writeIntBig(u64, out[j..][0..8], isap.block[j / 8]);
145 }
146 std.crypto.utils.secureZero(u64, &isap.block);
147 return out;82 return out;
148 }83 }
14984
150 fn mac(c: []const u8, ad: []const u8, npub: [16]u8, key: [16]u8) [16]u8 {85 fn mac(c: []const u8, ad: []const u8, npub: [16]u8, key: [16]u8) [16]u8 {
151 var isap = IsapA128A{86 var isap = IsapA128A{
152 .block = Block{87 .st = Ascon.initFromWords(.{
153 mem.readIntBig(u64, npub[0..8]),88 mem.readIntBig(u64, npub[0..8]),
154 mem.readIntBig(u64, npub[8..16]),89 mem.readIntBig(u64, npub[8..16]),
155 mem.readIntBig(u64, iv1[0..]),90 mem.readIntBig(u64, iv1[0..]),
156 0,91 0,
157 0,92 0,
158 },93 }),
159 };94 };
160 isap.p12();95 isap.st.permute();
16196
162 isap.absorb(ad);97 isap.absorb(ad);
163 isap.block[4] ^= 1;98 isap.st.addByte(1, Ascon.block_bytes - 1);
164 isap.absorb(c);99 isap.absorb(c);
165100
166 var y: [16]u8 = undefined;101 var y: [16]u8 = undefined;
167 mem.writeIntBig(u64, y[0..8], isap.block[0]);102 isap.st.extractBytes(&y);
168 mem.writeIntBig(u64, y[8..16], isap.block[1]);
169 const nb = trickle(key, iv2, y[0..], 16);103 const nb = trickle(key, iv2, y[0..], 16);
170 isap.block[0] = mem.readIntBig(u64, nb[0..8]);104 isap.st.setBytes(&nb);
171 isap.block[1] = mem.readIntBig(u64, nb[8..16]);105 isap.st.permute();
172 isap.p12();
173106
174 var tag: [16]u8 = undefined;107 var tag: [16]u8 = undefined;
175 mem.writeIntBig(u64, tag[0..8], isap.block[0]);108 isap.st.extractBytes(&tag);
176 mem.writeIntBig(u64, tag[8..16], isap.block[1]);109 isap.st.secureZero();
177 std.crypto.utils.secureZero(u64, &isap.block);
178 return tag;110 return tag;
179 }111 }
180112
...@@ -183,34 +115,31 @@ pub const IsapA128A = struct {...@@ -183,34 +115,31 @@ pub const IsapA128A = struct {
183115
184 const nb = trickle(key, iv3, npub[0..], 24);116 const nb = trickle(key, iv3, npub[0..], 24);
185 var isap = IsapA128A{117 var isap = IsapA128A{
186 .block = Block{118 .st = Ascon.initFromWords(.{
187 mem.readIntBig(u64, nb[0..8]),119 mem.readIntBig(u64, nb[0..8]),
188 mem.readIntBig(u64, nb[8..16]),120 mem.readIntBig(u64, nb[8..16]),
189 mem.readIntBig(u64, nb[16..24]),121 mem.readIntBig(u64, nb[16..24]),
190 mem.readIntBig(u64, npub[0..8]),122 mem.readIntBig(u64, npub[0..8]),
191 mem.readIntBig(u64, npub[8..16]),123 mem.readIntBig(u64, npub[8..16]),
192 },124 }),
193 };125 };
194 isap.p6();126 isap.st.permuteR(6);
195127
196 var i: usize = 0;128 var i: usize = 0;
197 while (true) : (i += 8) {129 while (true) : (i += 8) {
198 const left = in.len - i;130 const left = in.len - i;
199 if (left >= 8) {131 if (left >= 8) {
200 mem.writeIntNative(u64, out[i..][0..8], mem.bigToNative(u64, isap.block[0]) ^ mem.readIntNative(u64, in[i..][0..8]));132 isap.st.xorBytes(out[i..][0..8], in[i..][0..8]);
201 if (left == 8) {133 if (left == 8) {
202 break;134 break;
203 }135 }
204 isap.p6();136 isap.st.permuteR(6);
205 } else {137 } else {
206 var pad = [_]u8{0} ** 8;138 isap.st.xorBytes(out[i..], in[i..]);
207 mem.copy(u8, pad[0..left], in[i..][0..left]);
208 mem.writeIntNative(u64, pad[i..][0..8], mem.bigToNative(u64, isap.block[0]) ^ mem.readIntNative(u64, pad[i..][0..8]));
209 mem.copy(u8, out[i..][0..left], pad[0..left]);
210 break;139 break;
211 }140 }
212 }141 }
213 std.crypto.utils.secureZero(u64, &isap.block);142 isap.st.secureZero();
214 }143 }
215144
216 pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) void {145 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 {...@@ -220,12 +149,9 @@ pub const IsapA128A = struct {
220149
221 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 {150 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 {
222 var computed_tag = mac(c, ad, npub, key);151 var computed_tag = mac(c, ad, npub, key);
223 var acc: u8 = 0;152 const res = crypto.utils.timingSafeEql([tag_length]u8, computed_tag, tag);
224 for (computed_tag) |_, j| {153 crypto.utils.secureZero(u8, &computed_tag);
225 acc |= (computed_tag[j] ^ tag[j]);154 if (!res) {
226 }
227 std.crypto.utils.secureZero(u8, &computed_tag);
228 if (acc != 0) {
229 return error.AuthenticationFailed;155 return error.AuthenticationFailed;
230 }156 }
231 xor(m, c, npub, key);157 xor(m, c, npub, key);
lib/std/fs.zig+1-1
...@@ -1763,7 +1763,7 @@ pub const Dir = struct {...@@ -1763,7 +1763,7 @@ pub const Dir = struct {
1763 var nt_name = w.UNICODE_STRING{1763 var nt_name = w.UNICODE_STRING{
1764 .Length = path_len_bytes,1764 .Length = path_len_bytes,
1765 .MaximumLength = path_len_bytes,1765 .MaximumLength = path_len_bytes,
1766 .Buffer = @qualCast([*:0]u16, sub_path_w),1766 .Buffer = @constCast(sub_path_w),
1767 };1767 };
1768 var attr = w.OBJECT_ATTRIBUTES{1768 var attr = w.OBJECT_ATTRIBUTES{
1769 .Length = @sizeOf(w.OBJECT_ATTRIBUTES),1769 .Length = @sizeOf(w.OBJECT_ATTRIBUTES),
lib/std/json.zig+56-11
...@@ -1163,11 +1163,12 @@ const ArrayList = std.ArrayList;...@@ -1163,11 +1163,12 @@ const ArrayList = std.ArrayList;
1163const StringArrayHashMap = std.StringArrayHashMap;1163const StringArrayHashMap = std.StringArrayHashMap;
11641164
1165pub const ValueTree = struct {1165pub const ValueTree = struct {
1166 arena: ArenaAllocator,1166 arena: *ArenaAllocator,
1167 root: Value,1167 root: Value,
11681168
1169 pub fn deinit(self: *ValueTree) void {1169 pub fn deinit(self: *ValueTree) void {
1170 self.arena.deinit();1170 self.arena.deinit();
1171 self.arena.child_allocator.destroy(self.arena);
1171 }1172 }
1172};1173};
11731174
...@@ -1639,7 +1640,7 @@ fn parseInternal(...@@ -1639,7 +1640,7 @@ fn parseInternal(
1639 const allocator = options.allocator orelse return error.AllocatorRequired;1640 const allocator = options.allocator orelse return error.AllocatorRequired;
1640 switch (ptrInfo.size) {1641 switch (ptrInfo.size) {
1641 .One => {1642 .One => {
1642 const r: T = try allocator.create(ptrInfo.child);1643 const r: *ptrInfo.child = try allocator.create(ptrInfo.child);
1643 errdefer allocator.destroy(r);1644 errdefer allocator.destroy(r);
1644 r.* = try parseInternal(ptrInfo.child, token, tokens, options);1645 r.* = try parseInternal(ptrInfo.child, token, tokens, options);
1645 return r;1646 return r;
...@@ -1678,19 +1679,16 @@ fn parseInternal(...@@ -1678,19 +1679,16 @@ fn parseInternal(
1678 if (ptrInfo.child != u8) return error.UnexpectedToken;1679 if (ptrInfo.child != u8) return error.UnexpectedToken;
1679 const source_slice = stringToken.slice(tokens.slice, tokens.i - 1);1680 const source_slice = stringToken.slice(tokens.slice, tokens.i - 1);
1680 const len = stringToken.decodedLength();1681 const len = stringToken.decodedLength();
1681 const output = try allocator.alloc(u8, len + @boolToInt(ptrInfo.sentinel != null));1682 const output = if (ptrInfo.sentinel) |sentinel_ptr|
1683 try allocator.allocSentinel(u8, len, @ptrCast(*const u8, sentinel_ptr).*)
1684 else
1685 try allocator.alloc(u8, len);
1682 errdefer allocator.free(output);1686 errdefer allocator.free(output);
1683 switch (stringToken.escapes) {1687 switch (stringToken.escapes) {
1684 .None => mem.copy(u8, output, source_slice),1688 .None => mem.copy(u8, output, source_slice),
1685 .Some => try unescapeValidString(output, source_slice),1689 .Some => try unescapeValidString(output, source_slice),
1686 }1690 }
16871691
1688 if (ptrInfo.sentinel) |some| {
1689 const char = @ptrCast(*const u8, some).*;
1690 output[len] = char;
1691 return output[0..len :char];
1692 }
1693
1694 return output;1692 return output;
1695 },1693 },
1696 else => return error.UnexpectedToken,1694 else => return error.UnexpectedToken,
...@@ -1744,7 +1742,37 @@ pub fn parseFree(comptime T: type, value: T, options: ParseOptions) void {...@@ -1744,7 +1742,37 @@ pub fn parseFree(comptime T: type, value: T, options: ParseOptions) void {
1744 .Struct => |structInfo| {1742 .Struct => |structInfo| {
1745 inline for (structInfo.fields) |field| {1743 inline for (structInfo.fields) |field| {
1746 if (!field.is_comptime) {1744 if (!field.is_comptime) {
1747 parseFree(field.type, @field(value, field.name), options);1745 var should_free = true;
1746 if (field.default_value) |default| {
1747 switch (@typeInfo(field.type)) {
1748 // We must not attempt to free pointers to struct default values
1749 .Pointer => |fieldPtrInfo| {
1750 const field_value = @field(value, field.name);
1751 const field_ptr = switch (fieldPtrInfo.size) {
1752 .One => field_value,
1753 .Slice => field_value.ptr,
1754 else => unreachable, // Other pointer types are not parseable
1755 };
1756 const field_addr = @ptrToInt(field_ptr);
1757
1758 const casted_default = @ptrCast(*const field.type, @alignCast(@alignOf(field.type), default)).*;
1759 const default_ptr = switch (fieldPtrInfo.size) {
1760 .One => casted_default,
1761 .Slice => casted_default.ptr,
1762 else => unreachable, // Other pointer types are not parseable
1763 };
1764 const default_addr = @ptrToInt(default_ptr);
1765
1766 if (field_addr == default_addr) {
1767 should_free = false;
1768 }
1769 },
1770 else => {},
1771 }
1772 }
1773 if (should_free) {
1774 parseFree(field.type, @field(value, field.name), options);
1775 }
1748 }1776 }
1749 }1777 }
1750 },1778 },
...@@ -1809,8 +1837,12 @@ pub const Parser = struct {...@@ -1809,8 +1837,12 @@ pub const Parser = struct {
1809 pub fn parse(p: *Parser, input: []const u8) !ValueTree {1837 pub fn parse(p: *Parser, input: []const u8) !ValueTree {
1810 var s = TokenStream.init(input);1838 var s = TokenStream.init(input);
18111839
1812 var arena = ArenaAllocator.init(p.allocator);1840 var arena = try p.allocator.create(ArenaAllocator);
1841 errdefer p.allocator.destroy(arena);
1842
1843 arena.* = ArenaAllocator.init(p.allocator);
1813 errdefer arena.deinit();1844 errdefer arena.deinit();
1845
1814 const allocator = arena.allocator();1846 const allocator = arena.allocator();
18151847
1816 while (try s.next()) |token| {1848 while (try s.next()) |token| {
...@@ -2684,3 +2716,16 @@ test "encodesTo" {...@@ -2684,3 +2716,16 @@ test "encodesTo" {
2684 try testing.expectEqual(true, encodesTo("😂", "\\ud83d\\ude02"));2716 try testing.expectEqual(true, encodesTo("😂", "\\ud83d\\ude02"));
2685 try testing.expectEqual(true, encodesTo("withąunicode😂", "with\\u0105unicode\\ud83d\\ude02"));2717 try testing.expectEqual(true, encodesTo("withąunicode😂", "with\\u0105unicode\\ud83d\\ude02"));
2686}2718}
2719
2720test "issue 14600" {
2721 const json = "\"\\n\"";
2722 var token_stream = std.json.TokenStream.init(json);
2723 const options = ParseOptions{ .allocator = std.testing.allocator };
2724
2725 // Pre-fix, this line would panic:
2726 const result = try std.json.parse([:0]const u8, &token_stream, options);
2727 defer std.json.parseFree([:0]const u8, result, options);
2728
2729 // Double-check that we're getting the right result
2730 try testing.expect(mem.eql(u8, result, "\n"));
2731}
lib/std/json/test.zig+51
...@@ -2238,6 +2238,39 @@ test "parse into struct with no fields" {...@@ -2238,6 +2238,39 @@ test "parse into struct with no fields" {
2238 try testing.expectEqual(T{}, try parse(T, &ts, ParseOptions{}));2238 try testing.expectEqual(T{}, try parse(T, &ts, ParseOptions{}));
2239}2239}
22402240
2241const test_const_value: usize = 123;
2242
2243test "parse into struct with default const pointer field" {
2244 const T = struct { a: *const usize = &test_const_value };
2245 var ts = TokenStream.init("{}");
2246 try testing.expectEqual(T{}, try parse(T, &ts, .{}));
2247}
2248
2249const test_default_usize: usize = 123;
2250const test_default_usize_ptr: *align(1) const usize = &test_default_usize;
2251const test_default_str: []const u8 = "test str";
2252const test_default_str_slice: [2][]const u8 = [_][]const u8{
2253 "test1",
2254 "test2",
2255};
2256
2257test "freeing parsed structs with pointers to default values" {
2258 const T = struct {
2259 int: *const usize = &test_default_usize,
2260 int_ptr: *allowzero align(1) const usize = test_default_usize_ptr,
2261 str: []const u8 = test_default_str,
2262 str_slice: []const []const u8 = &test_default_str_slice,
2263 };
2264
2265 var ts = json.TokenStream.init("{}");
2266 const options = .{ .allocator = std.heap.page_allocator };
2267 const parsed = try json.parse(T, &ts, options);
2268
2269 try testing.expectEqual(T{}, parsed);
2270
2271 json.parseFree(T, parsed, options);
2272}
2273
2241test "parse into struct where destination and source lengths mismatch" {2274test "parse into struct where destination and source lengths mismatch" {
2242 const T = struct { a: [2]u8 };2275 const T = struct { a: [2]u8 };
2243 var ts = TokenStream.init("{\"a\": \"bbb\"}");2276 var ts = TokenStream.init("{\"a\": \"bbb\"}");
...@@ -2581,6 +2614,24 @@ test "parsing empty string gives appropriate error" {...@@ -2581,6 +2614,24 @@ test "parsing empty string gives appropriate error" {
2581 try testing.expectError(error.UnexpectedEndOfJson, testParse(arena_allocator.allocator(), ""));2614 try testing.expectError(error.UnexpectedEndOfJson, testParse(arena_allocator.allocator(), ""));
2582}2615}
25832616
2617test "parse tree should not contain dangling pointers" {
2618 var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator);
2619 defer arena_allocator.deinit();
2620
2621 var p = json.Parser.init(arena_allocator.allocator(), false);
2622 defer p.deinit();
2623
2624 var tree = try p.parse("[]");
2625 defer tree.deinit();
2626
2627 // Allocation should succeed
2628 var i: usize = 0;
2629 while (i < 100) : (i += 1) {
2630 try tree.root.Array.append(std.json.Value{ .Integer = 100 });
2631 }
2632 try testing.expectEqual(tree.root.Array.items.len, 100);
2633}
2634
2584test "integer after float has proper type" {2635test "integer after float has proper type" {
2585 var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator);2636 var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator);
2586 defer arena_allocator.deinit();2637 defer arena_allocator.deinit();
lib/std/mem/Allocator.zig+2-2
...@@ -112,7 +112,7 @@ pub fn destroy(self: Allocator, ptr: anytype) void {...@@ -112,7 +112,7 @@ pub fn destroy(self: Allocator, ptr: anytype) void {
112 const info = @typeInfo(@TypeOf(ptr)).Pointer;112 const info = @typeInfo(@TypeOf(ptr)).Pointer;
113 const T = info.child;113 const T = info.child;
114 if (@sizeOf(T) == 0) return;114 if (@sizeOf(T) == 0) return;
115 const non_const_ptr = @intToPtr([*]u8, @ptrToInt(ptr));115 const non_const_ptr = @ptrCast([*]u8, @constCast(ptr));
116 self.rawFree(non_const_ptr[0..@sizeOf(T)], math.log2(info.alignment), @returnAddress());116 self.rawFree(non_const_ptr[0..@sizeOf(T)], math.log2(info.alignment), @returnAddress());
117}117}
118118
...@@ -297,7 +297,7 @@ pub fn free(self: Allocator, memory: anytype) void {...@@ -297,7 +297,7 @@ pub fn free(self: Allocator, memory: anytype) void {
297 const bytes = mem.sliceAsBytes(memory);297 const bytes = mem.sliceAsBytes(memory);
298 const bytes_len = bytes.len + if (Slice.sentinel != null) @sizeOf(Slice.child) else 0;298 const bytes_len = bytes.len + if (Slice.sentinel != null) @sizeOf(Slice.child) else 0;
299 if (bytes_len == 0) return;299 if (bytes_len == 0) return;
300 const non_const_ptr = @intToPtr([*]u8, @ptrToInt(bytes.ptr));300 const non_const_ptr = @constCast(bytes.ptr);
301 // TODO: https://github.com/ziglang/zig/issues/4298301 // TODO: https://github.com/ziglang/zig/issues/4298
302 @memset(non_const_ptr, undefined, bytes_len);302 @memset(non_const_ptr, undefined, bytes_len);
303 self.rawFree(non_const_ptr[0..bytes_len], log2a(Slice.alignment), @returnAddress());303 self.rawFree(non_const_ptr[0..bytes_len], log2a(Slice.alignment), @returnAddress());
lib/std/meta.zig+1-1
...@@ -332,7 +332,7 @@ pub fn Sentinel(comptime T: type, comptime sentinel_val: Elem(T)) type {...@@ -332,7 +332,7 @@ pub fn Sentinel(comptime T: type, comptime sentinel_val: Elem(T)) type {
332 @compileError("Unable to derive a sentinel pointer type from " ++ @typeName(T));332 @compileError("Unable to derive a sentinel pointer type from " ++ @typeName(T));
333}333}
334334
335const assumeSentinel = @compileError("This function has been removed, consider using std.mem.sliceTo() or if needed a @ptrCast()");335pub const assumeSentinel = @compileError("This function has been removed, consider using std.mem.sliceTo() or if needed a @ptrCast()");
336336
337pub fn containerLayout(comptime T: type) Type.ContainerLayout {337pub fn containerLayout(comptime T: type) Type.ContainerLayout {
338 return switch (@typeInfo(T)) {338 return switch (@typeInfo(T)) {
lib/std/os.zig+1-1
...@@ -4513,7 +4513,7 @@ pub fn faccessatW(dirfd: fd_t, sub_path_w: [*:0]const u16, mode: u32, flags: u32...@@ -4513,7 +4513,7 @@ pub fn faccessatW(dirfd: fd_t, sub_path_w: [*:0]const u16, mode: u32, flags: u32
4513 var nt_name = windows.UNICODE_STRING{4513 var nt_name = windows.UNICODE_STRING{
4514 .Length = path_len_bytes,4514 .Length = path_len_bytes,
4515 .MaximumLength = path_len_bytes,4515 .MaximumLength = path_len_bytes,
4516 .Buffer = @qualCast([*:0]u16, sub_path_w),4516 .Buffer = @constCast(sub_path_w),
4517 };4517 };
4518 var attr = windows.OBJECT_ATTRIBUTES{4518 var attr = windows.OBJECT_ATTRIBUTES{
4519 .Length = @sizeOf(windows.OBJECT_ATTRIBUTES),4519 .Length = @sizeOf(windows.OBJECT_ATTRIBUTES),
lib/std/os/linux/syscalls.zig+10-6
...@@ -2057,7 +2057,7 @@ pub const Mips64 = enum(usize) {...@@ -2057,7 +2057,7 @@ pub const Mips64 = enum(usize) {
2057 writev = Linux + 19,2057 writev = Linux + 19,
2058 access = Linux + 20,2058 access = Linux + 20,
2059 pipe = Linux + 21,2059 pipe = Linux + 21,
2060 select = Linux + 22,2060 _newselect = Linux + 22,
2061 sched_yield = Linux + 23,2061 sched_yield = Linux + 23,
2062 mremap = Linux + 24,2062 mremap = Linux + 24,
2063 msync = Linux + 25,2063 msync = Linux + 25,
...@@ -2071,8 +2071,8 @@ pub const Mips64 = enum(usize) {...@@ -2071,8 +2071,8 @@ pub const Mips64 = enum(usize) {
2071 pause = Linux + 33,2071 pause = Linux + 33,
2072 nanosleep = Linux + 34,2072 nanosleep = Linux + 34,
2073 getitimer = Linux + 35,2073 getitimer = Linux + 35,
2074 alarm = Linux + 36,2074 setitimer = Linux + 36,
2075 setitimer = Linux + 37,2075 alarm = Linux + 37,
2076 getpid = Linux + 38,2076 getpid = Linux + 38,
2077 sendfile = Linux + 39,2077 sendfile = Linux + 39,
2078 socket = Linux + 40,2078 socket = Linux + 40,
...@@ -2286,7 +2286,7 @@ pub const Mips64 = enum(usize) {...@@ -2286,7 +2286,7 @@ pub const Mips64 = enum(usize) {
2286 mknodat = Linux + 249,2286 mknodat = Linux + 249,
2287 fchownat = Linux + 250,2287 fchownat = Linux + 250,
2288 futimesat = Linux + 251,2288 futimesat = Linux + 251,
2289 newfstatat = Linux + 252,2289 fstatat64 = Linux + 252,
2290 unlinkat = Linux + 253,2290 unlinkat = Linux + 253,
2291 renameat = Linux + 254,2291 renameat = Linux + 254,
2292 linkat = Linux + 255,2292 linkat = Linux + 255,
...@@ -2315,8 +2315,8 @@ pub const Mips64 = enum(usize) {...@@ -2315,8 +2315,8 @@ pub const Mips64 = enum(usize) {
2315 eventfd = Linux + 278,2315 eventfd = Linux + 278,
2316 fallocate = Linux + 279,2316 fallocate = Linux + 279,
2317 timerfd_create = Linux + 280,2317 timerfd_create = Linux + 280,
2318 timerfd_settime = Linux + 281,2318 timerfd_gettime = Linux + 281,
2319 timerfd_gettime = Linux + 282,2319 timerfd_settime = Linux + 282,
2320 signalfd4 = Linux + 283,2320 signalfd4 = Linux + 283,
2321 eventfd2 = Linux + 284,2321 eventfd2 = Linux + 284,
2322 epoll_create1 = Linux + 285,2322 epoll_create1 = Linux + 285,
...@@ -2382,9 +2382,13 @@ pub const Mips64 = enum(usize) {...@@ -2382,9 +2382,13 @@ pub const Mips64 = enum(usize) {
2382 process_madvise = Linux + 440,2382 process_madvise = Linux + 440,
2383 epoll_pwait2 = Linux + 441,2383 epoll_pwait2 = Linux + 441,
2384 mount_setattr = Linux + 442,2384 mount_setattr = Linux + 442,
2385 quotactl_fd = Linux + 443,
2385 landlock_create_ruleset = Linux + 444,2386 landlock_create_ruleset = Linux + 444,
2386 landlock_add_rule = Linux + 445,2387 landlock_add_rule = Linux + 445,
2387 landlock_restrict_self = Linux + 446,2388 landlock_restrict_self = Linux + 446,
2389 process_mrelease = Linux + 448,
2390 futex_waitv = Linux + 449,
2391 set_mempolicy_home_node = Linux + 450,
2388};2392};
23892393
2390pub const PowerPC = enum(usize) {2394pub const PowerPC = enum(usize) {
lib/std/os/windows.zig+7-7
...@@ -85,7 +85,7 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN...@@ -85,7 +85,7 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN
85 var nt_name = UNICODE_STRING{85 var nt_name = UNICODE_STRING{
86 .Length = path_len_bytes,86 .Length = path_len_bytes,
87 .MaximumLength = path_len_bytes,87 .MaximumLength = path_len_bytes,
88 .Buffer = @qualCast([*]u16, sub_path_w.ptr),88 .Buffer = @constCast(sub_path_w.ptr),
89 };89 };
90 var attr = OBJECT_ATTRIBUTES{90 var attr = OBJECT_ATTRIBUTES{
91 .Length = @sizeOf(OBJECT_ATTRIBUTES),91 .Length = @sizeOf(OBJECT_ATTRIBUTES),
...@@ -634,7 +634,7 @@ pub fn SetCurrentDirectory(path_name: []const u16) SetCurrentDirectoryError!void...@@ -634,7 +634,7 @@ pub fn SetCurrentDirectory(path_name: []const u16) SetCurrentDirectoryError!void
634 var nt_name = UNICODE_STRING{634 var nt_name = UNICODE_STRING{
635 .Length = path_len_bytes,635 .Length = path_len_bytes,
636 .MaximumLength = path_len_bytes,636 .MaximumLength = path_len_bytes,
637 .Buffer = @qualCast([*]u16, path_name.ptr),637 .Buffer = @constCast(path_name.ptr),
638 };638 };
639639
640 const rc = ntdll.RtlSetCurrentDirectory_U(&nt_name);640 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...@@ -766,7 +766,7 @@ pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u8) ReadLin
766 var nt_name = UNICODE_STRING{766 var nt_name = UNICODE_STRING{
767 .Length = path_len_bytes,767 .Length = path_len_bytes,
768 .MaximumLength = path_len_bytes,768 .MaximumLength = path_len_bytes,
769 .Buffer = @qualCast([*]u16, sub_path_w.ptr),769 .Buffer = @constCast(sub_path_w.ptr),
770 };770 };
771 var attr = OBJECT_ATTRIBUTES{771 var attr = OBJECT_ATTRIBUTES{
772 .Length = @sizeOf(OBJECT_ATTRIBUTES),772 .Length = @sizeOf(OBJECT_ATTRIBUTES),
...@@ -876,7 +876,7 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil...@@ -876,7 +876,7 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil
876 .Length = path_len_bytes,876 .Length = path_len_bytes,
877 .MaximumLength = path_len_bytes,877 .MaximumLength = path_len_bytes,
878 // The Windows API makes this mutable, but it will not mutate here.878 // The Windows API makes this mutable, but it will not mutate here.
879 .Buffer = @qualCast([*]u16, sub_path_w.ptr),879 .Buffer = @constCast(sub_path_w.ptr),
880 };880 };
881881
882 if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {882 if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
...@@ -1414,7 +1414,7 @@ pub fn sendmsg(...@@ -1414,7 +1414,7 @@ pub fn sendmsg(
1414}1414}
14151415
1416pub 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 {1416pub 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 {
1417 var buffer = ws2_32.WSABUF{ .len = @truncate(u31, len), .buf = @qualCast([*]u8, buf) };1417 var buffer = ws2_32.WSABUF{ .len = @truncate(u31, len), .buf = @constCast(buf) };
1418 var bytes_send: DWORD = undefined;1418 var bytes_send: DWORD = undefined;
1419 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) {1419 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) {
1420 return ws2_32.SOCKET_ERROR;1420 return ws2_32.SOCKET_ERROR;
...@@ -1876,13 +1876,13 @@ pub fn eqlIgnoreCaseWTF16(a: []const u16, b: []const u16) bool {...@@ -1876,13 +1876,13 @@ pub fn eqlIgnoreCaseWTF16(a: []const u16, b: []const u16) bool {
1876 const a_string = UNICODE_STRING{1876 const a_string = UNICODE_STRING{
1877 .Length = a_bytes,1877 .Length = a_bytes,
1878 .MaximumLength = a_bytes,1878 .MaximumLength = a_bytes,
1879 .Buffer = @qualCast([*]u16, a.ptr),1879 .Buffer = @constCast(a.ptr),
1880 };1880 };
1881 const b_bytes = @intCast(u16, b.len * 2);1881 const b_bytes = @intCast(u16, b.len * 2);
1882 const b_string = UNICODE_STRING{1882 const b_string = UNICODE_STRING{
1883 .Length = b_bytes,1883 .Length = b_bytes,
1884 .MaximumLength = b_bytes,1884 .MaximumLength = b_bytes,
1885 .Buffer = @qualCast([*]u16, b.ptr),1885 .Buffer = @constCast(b.ptr),
1886 };1886 };
1887 return ntdll.RtlEqualUnicodeString(&a_string, &b_string, TRUE) == TRUE;1887 return ntdll.RtlEqualUnicodeString(&a_string, &b_string, TRUE) == TRUE;
1888}1888}
lib/std/rand.zig+2-1
...@@ -18,8 +18,9 @@ const maxInt = std.math.maxInt;...@@ -18,8 +18,9 @@ const maxInt = std.math.maxInt;
18pub const DefaultPrng = Xoshiro256;18pub const DefaultPrng = Xoshiro256;
1919
20/// Cryptographically secure random numbers.20/// Cryptographically secure random numbers.
21pub const DefaultCsprng = Xoodoo;21pub const DefaultCsprng = Ascon;
2222
23pub const Ascon = @import("rand/Ascon.zig");
23pub const Isaac64 = @import("rand/Isaac64.zig");24pub const Isaac64 = @import("rand/Isaac64.zig");
24pub const Xoodoo = @import("rand/Xoodoo.zig");25pub const Xoodoo = @import("rand/Xoodoo.zig");
25pub const Pcg = @import("rand/Pcg.zig");26pub const Pcg = @import("rand/Pcg.zig");
lib/std/rand/Ascon.zig created+45
...@@ -0,0 +1,45 @@
1//! CSPRNG based on the Ascon XOFa construction
2
3const std = @import("std");
4const min = std.math.min;
5const mem = std.mem;
6const Random = std.rand.Random;
7const Self = @This();
8
9state: std.crypto.core.Ascon(.Little),
10
11const rate = 8;
12pub const secret_seed_length = 32;
13
14/// The seed must be uniform, secret and `secret_seed_length` bytes long.
15pub fn init(secret_seed: [secret_seed_length]u8) Self {
16 var state = std.crypto.core.Ascon(.Little).initXofA();
17 var i: usize = 0;
18 while (i + rate <= secret_seed.len) : (i += rate) {
19 state.addBytes(secret_seed[i..][0..rate]);
20 state.permuteR(8);
21 }
22 const left = secret_seed.len - i;
23 if (left > 0) state.addBytes(secret_seed[i..]);
24 state.addByte(0x80, left);
25 state.permute();
26 return Self{ .state = state };
27}
28
29pub fn random(self: *Self) Random {
30 return Random.init(self, fill);
31}
32
33pub fn fill(self: *Self, buf: []u8) void {
34 var i: usize = 0;
35 while (true) {
36 const left = buf.len - i;
37 const n = min(left, rate);
38 self.state.extractBytes(buf[i..][0..n]);
39 if (left == 0) break;
40 self.state.permuteR(8);
41 i += n;
42 }
43 self.state.clear(0, rate);
44 self.state.permuteR(8);
45}
lib/std/zig/Ast.zig+1-1
...@@ -207,7 +207,7 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void {...@@ -207,7 +207,7 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void {
207 return stream.writeAll("declarations are not allowed between container fields");207 return stream.writeAll("declarations are not allowed between container fields");
208 },208 },
209 .expected_block => {209 .expected_block => {
210 return stream.print("expected block or field, found '{s}'", .{210 return stream.print("expected block, found '{s}'", .{
211 token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(),211 token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(),
212 });212 });
213 },213 },
lib/std/zig/c_translation.zig+4-2
...@@ -74,8 +74,10 @@ fn castPtr(comptime DestType: type, target: anytype) DestType {...@@ -74,8 +74,10 @@ fn castPtr(comptime DestType: type, target: anytype) DestType {
74 const dest = ptrInfo(DestType);74 const dest = ptrInfo(DestType);
75 const source = ptrInfo(@TypeOf(target));75 const source = ptrInfo(@TypeOf(target));
7676
77 if (source.is_const and !dest.is_const or source.is_volatile and !dest.is_volatile)77 if (source.is_const and !dest.is_const)
78 return @qualCast(DestType, target)78 return @constCast(target)
79 else if (source.is_volatile and !dest.is_volatile)
80 return @volatileCast(target)
79 else if (@typeInfo(dest.child) == .Opaque)81 else if (@typeInfo(dest.child) == .Opaque)
80 // dest.alignment would error out82 // dest.alignment would error out
81 return @ptrCast(DestType, target)83 return @ptrCast(DestType, target)
lib/std/zig/render.zig-8
...@@ -2807,14 +2807,6 @@ fn nodeIsBlock(tag: Ast.Node.Tag) bool {...@@ -2807,14 +2807,6 @@ fn nodeIsBlock(tag: Ast.Node.Tag) bool {
2807 .block_semicolon,2807 .block_semicolon,
2808 .block_two,2808 .block_two,
2809 .block_two_semicolon,2809 .block_two_semicolon,
2810 .struct_init_dot,
2811 .struct_init_dot_comma,
2812 .struct_init_dot_two,
2813 .struct_init_dot_two_comma,
2814 .array_init_dot,
2815 .array_init_dot_comma,
2816 .array_init_dot_two,
2817 .array_init_dot_two_comma,
2818 => true,2810 => true,
2819 else => false,2811 else => false,
2820 };2812 };
lib/std/zig/tokenizer.zig+2-1
...@@ -739,6 +739,7 @@ pub const Tokenizer = struct {...@@ -739,6 +739,7 @@ pub const Tokenizer = struct {
739 },739 },
740 0 => {740 0 => {
741 if (self.index == self.buffer.len) {741 if (self.index == self.buffer.len) {
742 result.tag = .invalid;
742 break;743 break;
743 } else {744 } else {
744 self.checkLiteralCharacter();745 self.checkLiteralCharacter();
...@@ -1326,7 +1327,7 @@ test "newline in string literal" {...@@ -1326,7 +1327,7 @@ test "newline in string literal" {
1326 try testTokenize(1327 try testTokenize(
1327 \\"1328 \\"
1328 \\"1329 \\"
1329 , &.{ .invalid, .string_literal });1330 , &.{ .invalid, .invalid });
1330}1331}
13311332
1332test "code point literal with unicode escapes" {1333test "code point literal with unicode escapes" {
src/AstGen.zig+18-3
...@@ -1730,7 +1730,8 @@ fn structInitExprRlNone(...@@ -1730,7 +1730,8 @@ fn structInitExprRlNone(
1730 .container_type = ty_inst,1730 .container_type = ty_inst,
1731 .name_start = str_index,1731 .name_start = str_index,
1732 }) } }1732 }) } }
1733 else .{ .rl = .none };1733 else
1734 .{ .rl = .none };
1734 setExtra(astgen, extra_index, Zir.Inst.StructInitAnon.Item{1735 setExtra(astgen, extra_index, Zir.Inst.StructInitAnon.Item{
1735 .field_name = str_index,1736 .field_name = str_index,
1736 .init = try expr(gz, scope, sub_ri, field_init),1737 .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...@@ -2530,7 +2531,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
2530 .bit_size_of,2531 .bit_size_of,
2531 .typeof_log2_int_type,2532 .typeof_log2_int_type,
2532 .ptr_to_int,2533 .ptr_to_int,
2533 .qual_cast,
2534 .align_of,2534 .align_of,
2535 .bool_to_int,2535 .bool_to_int,
2536 .embed_file,2536 .embed_file,
...@@ -8038,7 +8038,6 @@ fn builtinCall(...@@ -8038,7 +8038,6 @@ fn builtinCall(
8038 .float_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .float_cast),8038 .float_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .float_cast),
8039 .int_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .int_cast),8039 .int_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .int_cast),
8040 .ptr_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .ptr_cast),8040 .ptr_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .ptr_cast),
8041 .qual_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .qual_cast),
8042 .truncate => return typeCast(gz, scope, ri, node, params[0], params[1], .truncate),8041 .truncate => return typeCast(gz, scope, ri, node, params[0], params[1], .truncate),
8043 // zig fmt: on8042 // zig fmt: on
80448043
...@@ -8114,6 +8113,22 @@ fn builtinCall(...@@ -8114,6 +8113,22 @@ fn builtinCall(
8114 });8113 });
8115 return rvalue(gz, ri, result, node);8114 return rvalue(gz, ri, result, node);
8116 },8115 },
8116 .const_cast => {
8117 const operand = try expr(gz, scope, .{ .rl = .none }, params[0]);
8118 const result = try gz.addExtendedPayload(.const_cast, Zir.Inst.UnNode{
8119 .node = gz.nodeIndexToRelative(node),
8120 .operand = operand,
8121 });
8122 return rvalue(gz, ri, result, node);
8123 },
8124 .volatile_cast => {
8125 const operand = try expr(gz, scope, .{ .rl = .none }, params[0]);
8126 const result = try gz.addExtendedPayload(.volatile_cast, Zir.Inst.UnNode{
8127 .node = gz.nodeIndexToRelative(node),
8128 .operand = operand,
8129 });
8130 return rvalue(gz, ri, result, node);
8131 },
81178132
8118 // zig fmt: off8133 // zig fmt: off
8119 .has_decl => return hasDeclOrField(gz, scope, ri, node, params[0], params[1], .has_decl),8134 .has_decl => return hasDeclOrField(gz, scope, ri, node, params[0], params[1], .has_decl),
src/Autodoc.zig+2-1
...@@ -1400,7 +1400,6 @@ fn walkInstruction(...@@ -1400,7 +1400,6 @@ fn walkInstruction(
1400 .float_cast,1400 .float_cast,
1401 .int_cast,1401 .int_cast,
1402 .ptr_cast,1402 .ptr_cast,
1403 .qual_cast,
1404 .truncate,1403 .truncate,
1405 .align_cast,1404 .align_cast,
1406 .has_decl,1405 .has_decl,
...@@ -2983,6 +2982,8 @@ fn walkInstruction(...@@ -2983,6 +2982,8 @@ fn walkInstruction(
2983 .error_to_int,2982 .error_to_int,
2984 .int_to_error,2983 .int_to_error,
2985 .reify,2984 .reify,
2985 .const_cast,
2986 .volatile_cast,
2986 => {2987 => {
2987 const extra = file.zir.extraData(Zir.Inst.UnNode, extended.operand).data;2988 const extra = file.zir.extraData(Zir.Inst.UnNode, extended.operand).data;
2988 const bin_index = self.exprs.items.len;2989 const bin_index = self.exprs.items.len;
src/BuiltinFn.zig+16-8
...@@ -28,6 +28,7 @@ pub const Tag = enum {...@@ -28,6 +28,7 @@ pub const Tag = enum {
28 cmpxchg_weak,28 cmpxchg_weak,
29 compile_error,29 compile_error,
30 compile_log,30 compile_log,
31 const_cast,
31 ctz,32 ctz,
32 c_undef,33 c_undef,
33 c_va_arg,34 c_va_arg,
...@@ -75,7 +76,6 @@ pub const Tag = enum {...@@ -75,7 +76,6 @@ pub const Tag = enum {
75 prefetch,76 prefetch,
76 ptr_cast,77 ptr_cast,
77 ptr_to_int,78 ptr_to_int,
78 qual_cast,
79 rem,79 rem,
80 return_address,80 return_address,
81 select,81 select,
...@@ -116,6 +116,7 @@ pub const Tag = enum {...@@ -116,6 +116,7 @@ pub const Tag = enum {
116 TypeOf,116 TypeOf,
117 union_init,117 union_init,
118 Vector,118 Vector,
119 volatile_cast,
119};120};
120121
121pub const MemLocRequirement = enum {122pub const MemLocRequirement = enum {
...@@ -345,6 +346,13 @@ pub const list = list: {...@@ -345,6 +346,13 @@ pub const list = list: {
345 .param_count = null,346 .param_count = null,
346 },347 },
347 },348 },
349 .{
350 "@constCast",
351 .{
352 .tag = .const_cast,
353 .param_count = 1,
354 },
355 },
348 .{356 .{
349 "@ctz",357 "@ctz",
350 .{358 .{
...@@ -675,13 +683,6 @@ pub const list = list: {...@@ -675,13 +683,6 @@ pub const list = list: {
675 .param_count = 1,683 .param_count = 1,
676 },684 },
677 },685 },
678 .{
679 "@qualCast",
680 .{
681 .tag = .qual_cast,
682 .param_count = 2,
683 },
684 },
685 .{686 .{
686 "@rem",687 "@rem",
687 .{688 .{
...@@ -964,5 +965,12 @@ pub const list = list: {...@@ -964,5 +965,12 @@ pub const list = list: {
964 .param_count = 2,965 .param_count = 2,
965 },966 },
966 },967 },
968 .{
969 "@volatileCast",
970 .{
971 .tag = .volatile_cast,
972 .param_count = 1,
973 },
974 },
967 });975 });
968};976};
src/Cache.zig deleted-1265
...@@ -1,1265 +0,0 @@
1//! Manages `zig-cache` directories.
2//! This is not a general-purpose cache. It is designed to be fast and simple,
3//! not to withstand attacks using specially-crafted input.
4
5gpa: Allocator,
6manifest_dir: fs.Dir,
7hash: HashHelper = .{},
8/// This value is accessed from multiple threads, protected by mutex.
9recent_problematic_timestamp: i128 = 0,
10mutex: std.Thread.Mutex = .{},
11
12/// A set of strings such as the zig library directory or project source root, which
13/// are stripped from the file paths before putting into the cache. They
14/// are replaced with single-character indicators. This is not to save
15/// space but to eliminate absolute file paths. This improves portability
16/// and usefulness of the cache for advanced use cases.
17prefixes_buffer: [3]Compilation.Directory = undefined,
18prefixes_len: usize = 0,
19
20const Cache = @This();
21const std = @import("std");
22const builtin = @import("builtin");
23const crypto = std.crypto;
24const fs = std.fs;
25const assert = std.debug.assert;
26const testing = std.testing;
27const mem = std.mem;
28const fmt = std.fmt;
29const Allocator = std.mem.Allocator;
30const Compilation = @import("Compilation.zig");
31const log = std.log.scoped(.cache);
32
33pub fn addPrefix(cache: *Cache, directory: Compilation.Directory) void {
34 if (directory.path) |p| {
35 log.debug("Cache.addPrefix {d} {s}", .{ cache.prefixes_len, p });
36 }
37 cache.prefixes_buffer[cache.prefixes_len] = directory;
38 cache.prefixes_len += 1;
39}
40
41/// Be sure to call `Manifest.deinit` after successful initialization.
42pub fn obtain(cache: *Cache) Manifest {
43 return Manifest{
44 .cache = cache,
45 .hash = cache.hash,
46 .manifest_file = null,
47 .manifest_dirty = false,
48 .hex_digest = undefined,
49 };
50}
51
52pub fn prefixes(cache: *const Cache) []const Compilation.Directory {
53 return cache.prefixes_buffer[0..cache.prefixes_len];
54}
55
56const PrefixedPath = struct {
57 prefix: u8,
58 sub_path: []u8,
59};
60
61fn findPrefix(cache: *const Cache, file_path: []const u8) !PrefixedPath {
62 const gpa = cache.gpa;
63 const resolved_path = try fs.path.resolve(gpa, &[_][]const u8{file_path});
64 errdefer gpa.free(resolved_path);
65 return findPrefixResolved(cache, resolved_path);
66}
67
68/// Takes ownership of `resolved_path` on success.
69fn findPrefixResolved(cache: *const Cache, resolved_path: []u8) !PrefixedPath {
70 const gpa = cache.gpa;
71 const prefixes_slice = cache.prefixes();
72 var i: u8 = 1; // Start at 1 to skip over checking the null prefix.
73 while (i < prefixes_slice.len) : (i += 1) {
74 const p = prefixes_slice[i].path.?;
75 if (mem.startsWith(u8, resolved_path, p)) {
76 // +1 to skip over the path separator here
77 const sub_path = try gpa.dupe(u8, resolved_path[p.len + 1 ..]);
78 gpa.free(resolved_path);
79 return PrefixedPath{
80 .prefix = @intCast(u8, i),
81 .sub_path = sub_path,
82 };
83 } else {
84 log.debug("'{s}' does not start with '{s}'", .{ resolved_path, p });
85 }
86 }
87
88 return PrefixedPath{
89 .prefix = 0,
90 .sub_path = resolved_path,
91 };
92}
93
94/// This is 128 bits - Even with 2^54 cache entries, the probably of a collision would be under 10^-6
95pub const bin_digest_len = 16;
96pub const hex_digest_len = bin_digest_len * 2;
97pub const BinDigest = [bin_digest_len]u8;
98
99const manifest_file_size_max = 50 * 1024 * 1024;
100
101/// The type used for hashing file contents. Currently, this is SipHash128(1, 3), because it
102/// provides enough collision resistance for the Manifest use cases, while being one of our
103/// fastest options right now.
104pub const Hasher = crypto.auth.siphash.SipHash128(1, 3);
105
106/// Initial state, that can be copied.
107pub const hasher_init: Hasher = Hasher.init(&[_]u8{0} ** Hasher.key_length);
108
109pub const File = struct {
110 prefixed_path: ?PrefixedPath,
111 max_file_size: ?usize,
112 stat: Stat,
113 bin_digest: BinDigest,
114 contents: ?[]const u8,
115
116 pub const Stat = struct {
117 inode: fs.File.INode,
118 size: u64,
119 mtime: i128,
120 };
121
122 pub fn deinit(self: *File, gpa: Allocator) void {
123 if (self.prefixed_path) |pp| {
124 gpa.free(pp.sub_path);
125 self.prefixed_path = null;
126 }
127 if (self.contents) |contents| {
128 gpa.free(contents);
129 self.contents = null;
130 }
131 self.* = undefined;
132 }
133};
134
135pub const HashHelper = struct {
136 hasher: Hasher = hasher_init,
137
138 const EmitLoc = Compilation.EmitLoc;
139
140 /// Record a slice of bytes as an dependency of the process being cached
141 pub fn addBytes(hh: *HashHelper, bytes: []const u8) void {
142 hh.hasher.update(mem.asBytes(&bytes.len));
143 hh.hasher.update(bytes);
144 }
145
146 pub fn addOptionalBytes(hh: *HashHelper, optional_bytes: ?[]const u8) void {
147 hh.add(optional_bytes != null);
148 hh.addBytes(optional_bytes orelse return);
149 }
150
151 pub fn addEmitLoc(hh: *HashHelper, emit_loc: EmitLoc) void {
152 hh.addBytes(emit_loc.basename);
153 }
154
155 pub fn addOptionalEmitLoc(hh: *HashHelper, optional_emit_loc: ?EmitLoc) void {
156 hh.add(optional_emit_loc != null);
157 hh.addEmitLoc(optional_emit_loc orelse return);
158 }
159
160 pub fn addListOfBytes(hh: *HashHelper, list_of_bytes: []const []const u8) void {
161 hh.add(list_of_bytes.len);
162 for (list_of_bytes) |bytes| hh.addBytes(bytes);
163 }
164
165 /// Convert the input value into bytes and record it as a dependency of the process being cached.
166 pub fn add(hh: *HashHelper, x: anytype) void {
167 switch (@TypeOf(x)) {
168 std.builtin.Version => {
169 hh.add(x.major);
170 hh.add(x.minor);
171 hh.add(x.patch);
172 },
173 std.Target.Os.TaggedVersionRange => {
174 switch (x) {
175 .linux => |linux| {
176 hh.add(linux.range.min);
177 hh.add(linux.range.max);
178 hh.add(linux.glibc);
179 },
180 .windows => |windows| {
181 hh.add(windows.min);
182 hh.add(windows.max);
183 },
184 .semver => |semver| {
185 hh.add(semver.min);
186 hh.add(semver.max);
187 },
188 .none => {},
189 }
190 },
191 else => switch (@typeInfo(@TypeOf(x))) {
192 .Bool, .Int, .Enum, .Array => hh.addBytes(mem.asBytes(&x)),
193 else => @compileError("unable to hash type " ++ @typeName(@TypeOf(x))),
194 },
195 }
196 }
197
198 pub fn addOptional(hh: *HashHelper, optional: anytype) void {
199 hh.add(optional != null);
200 hh.add(optional orelse return);
201 }
202
203 /// Returns a hex encoded hash of the inputs, without modifying state.
204 pub fn peek(hh: HashHelper) [hex_digest_len]u8 {
205 var copy = hh;
206 return copy.final();
207 }
208
209 pub fn peekBin(hh: HashHelper) BinDigest {
210 var copy = hh;
211 var bin_digest: BinDigest = undefined;
212 copy.hasher.final(&bin_digest);
213 return bin_digest;
214 }
215
216 /// Returns a hex encoded hash of the inputs, mutating the state of the hasher.
217 pub fn final(hh: *HashHelper) [hex_digest_len]u8 {
218 var bin_digest: BinDigest = undefined;
219 hh.hasher.final(&bin_digest);
220
221 var out_digest: [hex_digest_len]u8 = undefined;
222 _ = std.fmt.bufPrint(
223 &out_digest,
224 "{s}",
225 .{std.fmt.fmtSliceHexLower(&bin_digest)},
226 ) catch unreachable;
227 return out_digest;
228 }
229};
230
231pub const Lock = struct {
232 manifest_file: fs.File,
233
234 pub fn release(lock: *Lock) void {
235 if (builtin.os.tag == .windows) {
236 // Windows does not guarantee that locks are immediately unlocked when
237 // the file handle is closed. See LockFileEx documentation.
238 lock.manifest_file.unlock();
239 }
240
241 lock.manifest_file.close();
242 lock.* = undefined;
243 }
244};
245
246pub const Manifest = struct {
247 cache: *Cache,
248 /// Current state for incremental hashing.
249 hash: HashHelper,
250 manifest_file: ?fs.File,
251 manifest_dirty: bool,
252 /// Set this flag to true before calling hit() in order to indicate that
253 /// upon a cache hit, the code using the cache will not modify the files
254 /// within the cache directory. This allows multiple processes to utilize
255 /// the same cache directory at the same time.
256 want_shared_lock: bool = true,
257 have_exclusive_lock: bool = false,
258 // Indicate that we want isProblematicTimestamp to perform a filesystem write in
259 // order to obtain a problematic timestamp for the next call. Calls after that
260 // will then use the same timestamp, to avoid unnecessary filesystem writes.
261 want_refresh_timestamp: bool = true,
262 files: std.ArrayListUnmanaged(File) = .{},
263 hex_digest: [hex_digest_len]u8,
264 /// Populated when hit() returns an error because of one
265 /// of the files listed in the manifest.
266 failed_file_index: ?usize = null,
267 /// Keeps track of the last time we performed a file system write to observe
268 /// what time the file system thinks it is, according to its own granularity.
269 recent_problematic_timestamp: i128 = 0,
270
271 /// Add a file as a dependency of process being cached. When `hit` is
272 /// called, the file's contents will be checked to ensure that it matches
273 /// the contents from previous times.
274 ///
275 /// Max file size will be used to determine the amount of space the file contents
276 /// are allowed to take up in memory. If max_file_size is null, then the contents
277 /// will not be loaded into memory.
278 ///
279 /// Returns the index of the entry in the `files` array list. You can use it
280 /// to access the contents of the file after calling `hit()` like so:
281 ///
282 /// ```
283 /// var file_contents = cache_hash.files.items[file_index].contents.?;
284 /// ```
285 pub fn addFile(self: *Manifest, file_path: []const u8, max_file_size: ?usize) !usize {
286 assert(self.manifest_file == null);
287
288 const gpa = self.cache.gpa;
289 try self.files.ensureUnusedCapacity(gpa, 1);
290 const prefixed_path = try self.cache.findPrefix(file_path);
291 errdefer gpa.free(prefixed_path.sub_path);
292
293 log.debug("Manifest.addFile {s} -> {d} {s}", .{
294 file_path, prefixed_path.prefix, prefixed_path.sub_path,
295 });
296
297 self.files.addOneAssumeCapacity().* = .{
298 .prefixed_path = prefixed_path,
299 .contents = null,
300 .max_file_size = max_file_size,
301 .stat = undefined,
302 .bin_digest = undefined,
303 };
304
305 self.hash.add(prefixed_path.prefix);
306 self.hash.addBytes(prefixed_path.sub_path);
307
308 return self.files.items.len - 1;
309 }
310
311 pub fn hashCSource(self: *Manifest, c_source: Compilation.CSourceFile) !void {
312 _ = try self.addFile(c_source.src_path, null);
313 // Hash the extra flags, with special care to call addFile for file parameters.
314 // TODO this logic can likely be improved by utilizing clang_options_data.zig.
315 const file_args = [_][]const u8{"-include"};
316 var arg_i: usize = 0;
317 while (arg_i < c_source.extra_flags.len) : (arg_i += 1) {
318 const arg = c_source.extra_flags[arg_i];
319 self.hash.addBytes(arg);
320 for (file_args) |file_arg| {
321 if (mem.eql(u8, file_arg, arg) and arg_i + 1 < c_source.extra_flags.len) {
322 arg_i += 1;
323 _ = try self.addFile(c_source.extra_flags[arg_i], null);
324 }
325 }
326 }
327 }
328
329 pub fn addOptionalFile(self: *Manifest, optional_file_path: ?[]const u8) !void {
330 self.hash.add(optional_file_path != null);
331 const file_path = optional_file_path orelse return;
332 _ = try self.addFile(file_path, null);
333 }
334
335 pub fn addListOfFiles(self: *Manifest, list_of_files: []const []const u8) !void {
336 self.hash.add(list_of_files.len);
337 for (list_of_files) |file_path| {
338 _ = try self.addFile(file_path, null);
339 }
340 }
341
342 /// Check the cache to see if the input exists in it. If it exists, returns `true`.
343 /// A hex encoding of its hash is available by calling `final`.
344 ///
345 /// This function will also acquire an exclusive lock to the manifest file. This means
346 /// that a process holding a Manifest will block any other process attempting to
347 /// acquire the lock. If `want_shared_lock` is `true`, a cache hit guarantees the
348 /// manifest file to be locked in shared mode, and a cache miss guarantees the manifest
349 /// file to be locked in exclusive mode.
350 ///
351 /// The lock on the manifest file is released when `deinit` is called. As another
352 /// option, one may call `toOwnedLock` to obtain a smaller object which can represent
353 /// the lock. `deinit` is safe to call whether or not `toOwnedLock` has been called.
354 pub fn hit(self: *Manifest) !bool {
355 const gpa = self.cache.gpa;
356 assert(self.manifest_file == null);
357
358 self.failed_file_index = null;
359
360 const ext = ".txt";
361 var manifest_file_path: [self.hex_digest.len + ext.len]u8 = undefined;
362
363 var bin_digest: BinDigest = undefined;
364 self.hash.hasher.final(&bin_digest);
365
366 _ = std.fmt.bufPrint(
367 &self.hex_digest,
368 "{s}",
369 .{std.fmt.fmtSliceHexLower(&bin_digest)},
370 ) catch unreachable;
371
372 self.hash.hasher = hasher_init;
373 self.hash.hasher.update(&bin_digest);
374
375 mem.copy(u8, &manifest_file_path, &self.hex_digest);
376 manifest_file_path[self.hex_digest.len..][0..ext.len].* = ext.*;
377
378 if (self.files.items.len == 0) {
379 // If there are no file inputs, we check if the manifest file exists instead of
380 // comparing the hashes on the files used for the cached item
381 while (true) {
382 if (self.cache.manifest_dir.openFile(&manifest_file_path, .{
383 .mode = .read_write,
384 .lock = .Exclusive,
385 .lock_nonblocking = self.want_shared_lock,
386 })) |manifest_file| {
387 self.manifest_file = manifest_file;
388 self.have_exclusive_lock = true;
389 break;
390 } else |open_err| switch (open_err) {
391 error.WouldBlock => {
392 self.manifest_file = try self.cache.manifest_dir.openFile(&manifest_file_path, .{
393 .lock = .Shared,
394 });
395 break;
396 },
397 error.FileNotFound => {
398 if (self.cache.manifest_dir.createFile(&manifest_file_path, .{
399 .read = true,
400 .truncate = false,
401 .lock = .Exclusive,
402 .lock_nonblocking = self.want_shared_lock,
403 })) |manifest_file| {
404 self.manifest_file = manifest_file;
405 self.manifest_dirty = true;
406 self.have_exclusive_lock = true;
407 return false; // cache miss; exclusive lock already held
408 } else |err| switch (err) {
409 error.WouldBlock => continue,
410 else => |e| return e,
411 }
412 },
413 else => |e| return e,
414 }
415 }
416 } else {
417 if (self.cache.manifest_dir.createFile(&manifest_file_path, .{
418 .read = true,
419 .truncate = false,
420 .lock = .Exclusive,
421 .lock_nonblocking = self.want_shared_lock,
422 })) |manifest_file| {
423 self.manifest_file = manifest_file;
424 self.have_exclusive_lock = true;
425 } else |err| switch (err) {
426 error.WouldBlock => {
427 self.manifest_file = try self.cache.manifest_dir.openFile(&manifest_file_path, .{
428 .lock = .Shared,
429 });
430 },
431 else => |e| return e,
432 }
433 }
434
435 self.want_refresh_timestamp = true;
436
437 const file_contents = try self.manifest_file.?.reader().readAllAlloc(gpa, manifest_file_size_max);
438 defer gpa.free(file_contents);
439
440 const input_file_count = self.files.items.len;
441 var any_file_changed = false;
442 var line_iter = mem.tokenize(u8, file_contents, "\n");
443 var idx: usize = 0;
444 while (line_iter.next()) |line| {
445 defer idx += 1;
446
447 const cache_hash_file = if (idx < input_file_count) &self.files.items[idx] else blk: {
448 const new = try self.files.addOne(gpa);
449 new.* = .{
450 .prefixed_path = null,
451 .contents = null,
452 .max_file_size = null,
453 .stat = undefined,
454 .bin_digest = undefined,
455 };
456 break :blk new;
457 };
458
459 var iter = mem.tokenize(u8, line, " ");
460 const size = iter.next() orelse return error.InvalidFormat;
461 const inode = iter.next() orelse return error.InvalidFormat;
462 const mtime_nsec_str = iter.next() orelse return error.InvalidFormat;
463 const digest_str = iter.next() orelse return error.InvalidFormat;
464 const prefix_str = iter.next() orelse return error.InvalidFormat;
465 const file_path = iter.rest();
466
467 cache_hash_file.stat.size = fmt.parseInt(u64, size, 10) catch return error.InvalidFormat;
468 cache_hash_file.stat.inode = fmt.parseInt(fs.File.INode, inode, 10) catch return error.InvalidFormat;
469 cache_hash_file.stat.mtime = fmt.parseInt(i64, mtime_nsec_str, 10) catch return error.InvalidFormat;
470 _ = std.fmt.hexToBytes(&cache_hash_file.bin_digest, digest_str) catch return error.InvalidFormat;
471 const prefix = fmt.parseInt(u8, prefix_str, 10) catch return error.InvalidFormat;
472 if (prefix >= self.cache.prefixes_len) return error.InvalidFormat;
473
474 if (file_path.len == 0) {
475 return error.InvalidFormat;
476 }
477 if (cache_hash_file.prefixed_path) |pp| {
478 if (pp.prefix != prefix or !mem.eql(u8, file_path, pp.sub_path)) {
479 return error.InvalidFormat;
480 }
481 }
482
483 if (cache_hash_file.prefixed_path == null) {
484 cache_hash_file.prefixed_path = .{
485 .prefix = prefix,
486 .sub_path = try gpa.dupe(u8, file_path),
487 };
488 }
489
490 const pp = cache_hash_file.prefixed_path.?;
491 const dir = self.cache.prefixes()[pp.prefix].handle;
492 const this_file = dir.openFile(pp.sub_path, .{ .mode = .read_only }) catch |err| switch (err) {
493 error.FileNotFound => {
494 try self.upgradeToExclusiveLock();
495 return false;
496 },
497 else => return error.CacheUnavailable,
498 };
499 defer this_file.close();
500
501 const actual_stat = this_file.stat() catch |err| {
502 self.failed_file_index = idx;
503 return err;
504 };
505 const size_match = actual_stat.size == cache_hash_file.stat.size;
506 const mtime_match = actual_stat.mtime == cache_hash_file.stat.mtime;
507 const inode_match = actual_stat.inode == cache_hash_file.stat.inode;
508
509 if (!size_match or !mtime_match or !inode_match) {
510 self.manifest_dirty = true;
511
512 cache_hash_file.stat = .{
513 .size = actual_stat.size,
514 .mtime = actual_stat.mtime,
515 .inode = actual_stat.inode,
516 };
517
518 if (self.isProblematicTimestamp(cache_hash_file.stat.mtime)) {
519 // The actual file has an unreliable timestamp, force it to be hashed
520 cache_hash_file.stat.mtime = 0;
521 cache_hash_file.stat.inode = 0;
522 }
523
524 var actual_digest: BinDigest = undefined;
525 hashFile(this_file, &actual_digest) catch |err| {
526 self.failed_file_index = idx;
527 return err;
528 };
529
530 if (!mem.eql(u8, &cache_hash_file.bin_digest, &actual_digest)) {
531 cache_hash_file.bin_digest = actual_digest;
532 // keep going until we have the input file digests
533 any_file_changed = true;
534 }
535 }
536
537 if (!any_file_changed) {
538 self.hash.hasher.update(&cache_hash_file.bin_digest);
539 }
540 }
541
542 if (any_file_changed) {
543 // cache miss
544 // keep the manifest file open
545 self.unhit(bin_digest, input_file_count);
546 try self.upgradeToExclusiveLock();
547 return false;
548 }
549
550 if (idx < input_file_count) {
551 self.manifest_dirty = true;
552 while (idx < input_file_count) : (idx += 1) {
553 const ch_file = &self.files.items[idx];
554 self.populateFileHash(ch_file) catch |err| {
555 self.failed_file_index = idx;
556 return err;
557 };
558 }
559 try self.upgradeToExclusiveLock();
560 return false;
561 }
562
563 if (self.want_shared_lock) {
564 try self.downgradeToSharedLock();
565 }
566
567 return true;
568 }
569
570 pub fn unhit(self: *Manifest, bin_digest: BinDigest, input_file_count: usize) void {
571 // Reset the hash.
572 self.hash.hasher = hasher_init;
573 self.hash.hasher.update(&bin_digest);
574
575 // Remove files not in the initial hash.
576 for (self.files.items[input_file_count..]) |*file| {
577 file.deinit(self.cache.gpa);
578 }
579 self.files.shrinkRetainingCapacity(input_file_count);
580
581 for (self.files.items) |file| {
582 self.hash.hasher.update(&file.bin_digest);
583 }
584 }
585
586 fn isProblematicTimestamp(man: *Manifest, file_time: i128) bool {
587 // If the file_time is prior to the most recent problematic timestamp
588 // then we don't need to access the filesystem.
589 if (file_time < man.recent_problematic_timestamp)
590 return false;
591
592 // Next we will check the globally shared Cache timestamp, which is accessed
593 // from multiple threads.
594 man.cache.mutex.lock();
595 defer man.cache.mutex.unlock();
596
597 // Save the global one to our local one to avoid locking next time.
598 man.recent_problematic_timestamp = man.cache.recent_problematic_timestamp;
599 if (file_time < man.recent_problematic_timestamp)
600 return false;
601
602 // This flag prevents multiple filesystem writes for the same hit() call.
603 if (man.want_refresh_timestamp) {
604 man.want_refresh_timestamp = false;
605
606 var file = man.cache.manifest_dir.createFile("timestamp", .{
607 .read = true,
608 .truncate = true,
609 }) catch return true;
610 defer file.close();
611
612 // Save locally and also save globally (we still hold the global lock).
613 man.recent_problematic_timestamp = (file.stat() catch return true).mtime;
614 man.cache.recent_problematic_timestamp = man.recent_problematic_timestamp;
615 }
616
617 return file_time >= man.recent_problematic_timestamp;
618 }
619
620 fn populateFileHash(self: *Manifest, ch_file: *File) !void {
621 const pp = ch_file.prefixed_path.?;
622 const dir = self.cache.prefixes()[pp.prefix].handle;
623 const file = try dir.openFile(pp.sub_path, .{});
624 defer file.close();
625
626 const actual_stat = try file.stat();
627 ch_file.stat = .{
628 .size = actual_stat.size,
629 .mtime = actual_stat.mtime,
630 .inode = actual_stat.inode,
631 };
632
633 if (self.isProblematicTimestamp(ch_file.stat.mtime)) {
634 // The actual file has an unreliable timestamp, force it to be hashed
635 ch_file.stat.mtime = 0;
636 ch_file.stat.inode = 0;
637 }
638
639 if (ch_file.max_file_size) |max_file_size| {
640 if (ch_file.stat.size > max_file_size) {
641 return error.FileTooBig;
642 }
643
644 const contents = try self.cache.gpa.alloc(u8, @intCast(usize, ch_file.stat.size));
645 errdefer self.cache.gpa.free(contents);
646
647 // Hash while reading from disk, to keep the contents in the cpu cache while
648 // doing hashing.
649 var hasher = hasher_init;
650 var off: usize = 0;
651 while (true) {
652 // give me everything you've got, captain
653 const bytes_read = try file.read(contents[off..]);
654 if (bytes_read == 0) break;
655 hasher.update(contents[off..][0..bytes_read]);
656 off += bytes_read;
657 }
658 hasher.final(&ch_file.bin_digest);
659
660 ch_file.contents = contents;
661 } else {
662 try hashFile(file, &ch_file.bin_digest);
663 }
664
665 self.hash.hasher.update(&ch_file.bin_digest);
666 }
667
668 /// Add a file as a dependency of process being cached, after the initial hash has been
669 /// calculated. This is useful for processes that don't know all the files that
670 /// are depended on ahead of time. For example, a source file that can import other files
671 /// will need to be recompiled if the imported file is changed.
672 pub fn addFilePostFetch(self: *Manifest, file_path: []const u8, max_file_size: usize) ![]const u8 {
673 assert(self.manifest_file != null);
674
675 const gpa = self.cache.gpa;
676 const prefixed_path = try self.cache.findPrefix(file_path);
677 errdefer gpa.free(prefixed_path.sub_path);
678
679 log.debug("Manifest.addFilePostFetch {s} -> {d} {s}", .{
680 file_path, prefixed_path.prefix, prefixed_path.sub_path,
681 });
682
683 const new_ch_file = try self.files.addOne(gpa);
684 new_ch_file.* = .{
685 .prefixed_path = prefixed_path,
686 .max_file_size = max_file_size,
687 .stat = undefined,
688 .bin_digest = undefined,
689 .contents = null,
690 };
691 errdefer self.files.shrinkRetainingCapacity(self.files.items.len - 1);
692
693 try self.populateFileHash(new_ch_file);
694
695 return new_ch_file.contents.?;
696 }
697
698 /// Add a file as a dependency of process being cached, after the initial hash has been
699 /// calculated. This is useful for processes that don't know the all the files that
700 /// are depended on ahead of time. For example, a source file that can import other files
701 /// will need to be recompiled if the imported file is changed.
702 pub fn addFilePost(self: *Manifest, file_path: []const u8) !void {
703 assert(self.manifest_file != null);
704
705 const gpa = self.cache.gpa;
706 const prefixed_path = try self.cache.findPrefix(file_path);
707 errdefer gpa.free(prefixed_path.sub_path);
708
709 log.debug("Manifest.addFilePost {s} -> {d} {s}", .{
710 file_path, prefixed_path.prefix, prefixed_path.sub_path,
711 });
712
713 const new_ch_file = try self.files.addOne(gpa);
714 new_ch_file.* = .{
715 .prefixed_path = prefixed_path,
716 .max_file_size = null,
717 .stat = undefined,
718 .bin_digest = undefined,
719 .contents = null,
720 };
721 errdefer self.files.shrinkRetainingCapacity(self.files.items.len - 1);
722
723 try self.populateFileHash(new_ch_file);
724 }
725
726 /// Like `addFilePost` but when the file contents have already been loaded from disk.
727 /// On success, cache takes ownership of `resolved_path`.
728 pub fn addFilePostContents(
729 self: *Manifest,
730 resolved_path: []u8,
731 bytes: []const u8,
732 stat: File.Stat,
733 ) error{OutOfMemory}!void {
734 assert(self.manifest_file != null);
735 const gpa = self.cache.gpa;
736
737 const ch_file = try self.files.addOne(gpa);
738 errdefer self.files.shrinkRetainingCapacity(self.files.items.len - 1);
739
740 log.debug("Manifest.addFilePostContents resolved_path={s}", .{resolved_path});
741
742 const prefixed_path = try self.cache.findPrefixResolved(resolved_path);
743 errdefer gpa.free(prefixed_path.sub_path);
744
745 log.debug("Manifest.addFilePostContents -> {d} {s}", .{
746 prefixed_path.prefix, prefixed_path.sub_path,
747 });
748
749 ch_file.* = .{
750 .prefixed_path = prefixed_path,
751 .max_file_size = null,
752 .stat = stat,
753 .bin_digest = undefined,
754 .contents = null,
755 };
756
757 if (self.isProblematicTimestamp(ch_file.stat.mtime)) {
758 // The actual file has an unreliable timestamp, force it to be hashed
759 ch_file.stat.mtime = 0;
760 ch_file.stat.inode = 0;
761 }
762
763 {
764 var hasher = hasher_init;
765 hasher.update(bytes);
766 hasher.final(&ch_file.bin_digest);
767 }
768
769 self.hash.hasher.update(&ch_file.bin_digest);
770 }
771
772 pub fn addDepFilePost(self: *Manifest, dir: fs.Dir, dep_file_basename: []const u8) !void {
773 assert(self.manifest_file != null);
774
775 const dep_file_contents = try dir.readFileAlloc(self.cache.gpa, dep_file_basename, manifest_file_size_max);
776 defer self.cache.gpa.free(dep_file_contents);
777
778 var error_buf = std.ArrayList(u8).init(self.cache.gpa);
779 defer error_buf.deinit();
780
781 var it: @import("DepTokenizer.zig") = .{ .bytes = dep_file_contents };
782
783 // Skip first token: target.
784 switch (it.next() orelse return) { // Empty dep file OK.
785 .target, .target_must_resolve, .prereq => {},
786 else => |err| {
787 try err.printError(error_buf.writer());
788 log.err("failed parsing {s}: {s}", .{ dep_file_basename, error_buf.items });
789 return error.InvalidDepFile;
790 },
791 }
792 // Process 0+ preqreqs.
793 // Clang is invoked in single-source mode so we never get more targets.
794 while (true) {
795 switch (it.next() orelse return) {
796 .target, .target_must_resolve => return,
797 .prereq => |file_path| try self.addFilePost(file_path),
798 else => |err| {
799 try err.printError(error_buf.writer());
800 log.err("failed parsing {s}: {s}", .{ dep_file_basename, error_buf.items });
801 return error.InvalidDepFile;
802 },
803 }
804 }
805 }
806
807 /// Returns a hex encoded hash of the inputs.
808 pub fn final(self: *Manifest) [hex_digest_len]u8 {
809 assert(self.manifest_file != null);
810
811 // We don't close the manifest file yet, because we want to
812 // keep it locked until the API user is done using it.
813 // We also don't write out the manifest yet, because until
814 // cache_release is called we still might be working on creating
815 // the artifacts to cache.
816
817 var bin_digest: BinDigest = undefined;
818 self.hash.hasher.final(&bin_digest);
819
820 var out_digest: [hex_digest_len]u8 = undefined;
821 _ = std.fmt.bufPrint(
822 &out_digest,
823 "{s}",
824 .{std.fmt.fmtSliceHexLower(&bin_digest)},
825 ) catch unreachable;
826
827 return out_digest;
828 }
829
830 /// If `want_shared_lock` is true, this function automatically downgrades the
831 /// lock from exclusive to shared.
832 pub fn writeManifest(self: *Manifest) !void {
833 assert(self.have_exclusive_lock);
834
835 const manifest_file = self.manifest_file.?;
836 if (self.manifest_dirty) {
837 self.manifest_dirty = false;
838
839 var contents = std.ArrayList(u8).init(self.cache.gpa);
840 defer contents.deinit();
841
842 const writer = contents.writer();
843 var encoded_digest: [hex_digest_len]u8 = undefined;
844
845 for (self.files.items) |file| {
846 _ = std.fmt.bufPrint(
847 &encoded_digest,
848 "{s}",
849 .{std.fmt.fmtSliceHexLower(&file.bin_digest)},
850 ) catch unreachable;
851 try writer.print("{d} {d} {d} {s} {d} {s}\n", .{
852 file.stat.size,
853 file.stat.inode,
854 file.stat.mtime,
855 &encoded_digest,
856 file.prefixed_path.?.prefix,
857 file.prefixed_path.?.sub_path,
858 });
859 }
860
861 try manifest_file.setEndPos(contents.items.len);
862 try manifest_file.pwriteAll(contents.items, 0);
863 }
864
865 if (self.want_shared_lock) {
866 try self.downgradeToSharedLock();
867 }
868 }
869
870 fn downgradeToSharedLock(self: *Manifest) !void {
871 if (!self.have_exclusive_lock) return;
872
873 // WASI does not currently support flock, so we bypass it here.
874 // TODO: If/when flock is supported on WASI, this check should be removed.
875 // See https://github.com/WebAssembly/wasi-filesystem/issues/2
876 if (builtin.os.tag != .wasi or std.process.can_spawn or !builtin.single_threaded) {
877 const manifest_file = self.manifest_file.?;
878 try manifest_file.downgradeLock();
879 }
880
881 self.have_exclusive_lock = false;
882 }
883
884 fn upgradeToExclusiveLock(self: *Manifest) !void {
885 if (self.have_exclusive_lock) return;
886 assert(self.manifest_file != null);
887
888 // WASI does not currently support flock, so we bypass it here.
889 // TODO: If/when flock is supported on WASI, this check should be removed.
890 // See https://github.com/WebAssembly/wasi-filesystem/issues/2
891 if (builtin.os.tag != .wasi or std.process.can_spawn or !builtin.single_threaded) {
892 const manifest_file = self.manifest_file.?;
893 // Here we intentionally have a period where the lock is released, in case there are
894 // other processes holding a shared lock.
895 manifest_file.unlock();
896 try manifest_file.lock(.Exclusive);
897 }
898 self.have_exclusive_lock = true;
899 }
900
901 /// Obtain only the data needed to maintain a lock on the manifest file.
902 /// The `Manifest` remains safe to deinit.
903 /// Don't forget to call `writeManifest` before this!
904 pub fn toOwnedLock(self: *Manifest) Lock {
905 const lock: Lock = .{
906 .manifest_file = self.manifest_file.?,
907 };
908
909 self.manifest_file = null;
910 return lock;
911 }
912
913 /// Releases the manifest file and frees any memory the Manifest was using.
914 /// `Manifest.hit` must be called first.
915 /// Don't forget to call `writeManifest` before this!
916 pub fn deinit(self: *Manifest) void {
917 if (self.manifest_file) |file| {
918 if (builtin.os.tag == .windows) {
919 // See Lock.release for why this is required on Windows
920 file.unlock();
921 }
922
923 file.close();
924 }
925 for (self.files.items) |*file| {
926 file.deinit(self.cache.gpa);
927 }
928 self.files.deinit(self.cache.gpa);
929 }
930};
931
932/// On operating systems that support symlinks, does a readlink. On other operating systems,
933/// uses the file contents. Windows supports symlinks but only with elevated privileges, so
934/// it is treated as not supporting symlinks.
935pub fn readSmallFile(dir: fs.Dir, sub_path: []const u8, buffer: []u8) ![]u8 {
936 if (builtin.os.tag == .windows) {
937 return dir.readFile(sub_path, buffer);
938 } else {
939 return dir.readLink(sub_path, buffer);
940 }
941}
942
943/// On operating systems that support symlinks, does a symlink. On other operating systems,
944/// uses the file contents. Windows supports symlinks but only with elevated privileges, so
945/// it is treated as not supporting symlinks.
946/// `data` must be a valid UTF-8 encoded file path and 255 bytes or fewer.
947pub fn writeSmallFile(dir: fs.Dir, sub_path: []const u8, data: []const u8) !void {
948 assert(data.len <= 255);
949 if (builtin.os.tag == .windows) {
950 return dir.writeFile(sub_path, data);
951 } else {
952 return dir.symLink(data, sub_path, .{});
953 }
954}
955
956fn hashFile(file: fs.File, bin_digest: *[Hasher.mac_length]u8) !void {
957 var buf: [1024]u8 = undefined;
958
959 var hasher = hasher_init;
960 while (true) {
961 const bytes_read = try file.read(&buf);
962 if (bytes_read == 0) break;
963 hasher.update(buf[0..bytes_read]);
964 }
965
966 hasher.final(bin_digest);
967}
968
969// Create/Write a file, close it, then grab its stat.mtime timestamp.
970fn testGetCurrentFileTimestamp() !i128 {
971 var file = try fs.cwd().createFile("test-filetimestamp.tmp", .{
972 .read = true,
973 .truncate = true,
974 });
975 defer file.close();
976
977 return (try file.stat()).mtime;
978}
979
980test "cache file and then recall it" {
981 if (builtin.os.tag == .wasi) {
982 // https://github.com/ziglang/zig/issues/5437
983 return error.SkipZigTest;
984 }
985
986 const cwd = fs.cwd();
987
988 const temp_file = "test.txt";
989 const temp_manifest_dir = "temp_manifest_dir";
990
991 try cwd.writeFile(temp_file, "Hello, world!\n");
992
993 // Wait for file timestamps to tick
994 const initial_time = try testGetCurrentFileTimestamp();
995 while ((try testGetCurrentFileTimestamp()) == initial_time) {
996 std.time.sleep(1);
997 }
998
999 var digest1: [hex_digest_len]u8 = undefined;
1000 var digest2: [hex_digest_len]u8 = undefined;
1001
1002 {
1003 var cache = Cache{
1004 .gpa = testing.allocator,
1005 .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}),
1006 };
1007 cache.addPrefix(.{ .path = null, .handle = fs.cwd() });
1008 defer cache.manifest_dir.close();
1009
1010 {
1011 var ch = cache.obtain();
1012 defer ch.deinit();
1013
1014 ch.hash.add(true);
1015 ch.hash.add(@as(u16, 1234));
1016 ch.hash.addBytes("1234");
1017 _ = try ch.addFile(temp_file, null);
1018
1019 // There should be nothing in the cache
1020 try testing.expectEqual(false, try ch.hit());
1021
1022 digest1 = ch.final();
1023 try ch.writeManifest();
1024 }
1025 {
1026 var ch = cache.obtain();
1027 defer ch.deinit();
1028
1029 ch.hash.add(true);
1030 ch.hash.add(@as(u16, 1234));
1031 ch.hash.addBytes("1234");
1032 _ = try ch.addFile(temp_file, null);
1033
1034 // Cache hit! We just "built" the same file
1035 try testing.expect(try ch.hit());
1036 digest2 = ch.final();
1037
1038 try testing.expectEqual(false, ch.have_exclusive_lock);
1039 }
1040
1041 try testing.expectEqual(digest1, digest2);
1042 }
1043
1044 try cwd.deleteTree(temp_manifest_dir);
1045 try cwd.deleteFile(temp_file);
1046}
1047
1048test "check that changing a file makes cache fail" {
1049 if (builtin.os.tag == .wasi) {
1050 // https://github.com/ziglang/zig/issues/5437
1051 return error.SkipZigTest;
1052 }
1053 const cwd = fs.cwd();
1054
1055 const temp_file = "cache_hash_change_file_test.txt";
1056 const temp_manifest_dir = "cache_hash_change_file_manifest_dir";
1057 const original_temp_file_contents = "Hello, world!\n";
1058 const updated_temp_file_contents = "Hello, world; but updated!\n";
1059
1060 try cwd.deleteTree(temp_manifest_dir);
1061 try cwd.deleteTree(temp_file);
1062
1063 try cwd.writeFile(temp_file, original_temp_file_contents);
1064
1065 // Wait for file timestamps to tick
1066 const initial_time = try testGetCurrentFileTimestamp();
1067 while ((try testGetCurrentFileTimestamp()) == initial_time) {
1068 std.time.sleep(1);
1069 }
1070
1071 var digest1: [hex_digest_len]u8 = undefined;
1072 var digest2: [hex_digest_len]u8 = undefined;
1073
1074 {
1075 var cache = Cache{
1076 .gpa = testing.allocator,
1077 .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}),
1078 };
1079 cache.addPrefix(.{ .path = null, .handle = fs.cwd() });
1080 defer cache.manifest_dir.close();
1081
1082 {
1083 var ch = cache.obtain();
1084 defer ch.deinit();
1085
1086 ch.hash.addBytes("1234");
1087 const temp_file_idx = try ch.addFile(temp_file, 100);
1088
1089 // There should be nothing in the cache
1090 try testing.expectEqual(false, try ch.hit());
1091
1092 try testing.expect(mem.eql(u8, original_temp_file_contents, ch.files.items[temp_file_idx].contents.?));
1093
1094 digest1 = ch.final();
1095
1096 try ch.writeManifest();
1097 }
1098
1099 try cwd.writeFile(temp_file, updated_temp_file_contents);
1100
1101 {
1102 var ch = cache.obtain();
1103 defer ch.deinit();
1104
1105 ch.hash.addBytes("1234");
1106 const temp_file_idx = try ch.addFile(temp_file, 100);
1107
1108 // A file that we depend on has been updated, so the cache should not contain an entry for it
1109 try testing.expectEqual(false, try ch.hit());
1110
1111 // The cache system does not keep the contents of re-hashed input files.
1112 try testing.expect(ch.files.items[temp_file_idx].contents == null);
1113
1114 digest2 = ch.final();
1115
1116 try ch.writeManifest();
1117 }
1118
1119 try testing.expect(!mem.eql(u8, digest1[0..], digest2[0..]));
1120 }
1121
1122 try cwd.deleteTree(temp_manifest_dir);
1123 try cwd.deleteTree(temp_file);
1124}
1125
1126test "no file inputs" {
1127 if (builtin.os.tag == .wasi) {
1128 // https://github.com/ziglang/zig/issues/5437
1129 return error.SkipZigTest;
1130 }
1131 const cwd = fs.cwd();
1132 const temp_manifest_dir = "no_file_inputs_manifest_dir";
1133 defer cwd.deleteTree(temp_manifest_dir) catch {};
1134
1135 var digest1: [hex_digest_len]u8 = undefined;
1136 var digest2: [hex_digest_len]u8 = undefined;
1137
1138 var cache = Cache{
1139 .gpa = testing.allocator,
1140 .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}),
1141 };
1142 cache.addPrefix(.{ .path = null, .handle = fs.cwd() });
1143 defer cache.manifest_dir.close();
1144
1145 {
1146 var man = cache.obtain();
1147 defer man.deinit();
1148
1149 man.hash.addBytes("1234");
1150
1151 // There should be nothing in the cache
1152 try testing.expectEqual(false, try man.hit());
1153
1154 digest1 = man.final();
1155
1156 try man.writeManifest();
1157 }
1158 {
1159 var man = cache.obtain();
1160 defer man.deinit();
1161
1162 man.hash.addBytes("1234");
1163
1164 try testing.expect(try man.hit());
1165 digest2 = man.final();
1166 try testing.expectEqual(false, man.have_exclusive_lock);
1167 }
1168
1169 try testing.expectEqual(digest1, digest2);
1170}
1171
1172test "Manifest with files added after initial hash work" {
1173 if (builtin.os.tag == .wasi) {
1174 // https://github.com/ziglang/zig/issues/5437
1175 return error.SkipZigTest;
1176 }
1177 const cwd = fs.cwd();
1178
1179 const temp_file1 = "cache_hash_post_file_test1.txt";
1180 const temp_file2 = "cache_hash_post_file_test2.txt";
1181 const temp_manifest_dir = "cache_hash_post_file_manifest_dir";
1182
1183 try cwd.writeFile(temp_file1, "Hello, world!\n");
1184 try cwd.writeFile(temp_file2, "Hello world the second!\n");
1185
1186 // Wait for file timestamps to tick
1187 const initial_time = try testGetCurrentFileTimestamp();
1188 while ((try testGetCurrentFileTimestamp()) == initial_time) {
1189 std.time.sleep(1);
1190 }
1191
1192 var digest1: [hex_digest_len]u8 = undefined;
1193 var digest2: [hex_digest_len]u8 = undefined;
1194 var digest3: [hex_digest_len]u8 = undefined;
1195
1196 {
1197 var cache = Cache{
1198 .gpa = testing.allocator,
1199 .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}),
1200 };
1201 cache.addPrefix(.{ .path = null, .handle = fs.cwd() });
1202 defer cache.manifest_dir.close();
1203
1204 {
1205 var ch = cache.obtain();
1206 defer ch.deinit();
1207
1208 ch.hash.addBytes("1234");
1209 _ = try ch.addFile(temp_file1, null);
1210
1211 // There should be nothing in the cache
1212 try testing.expectEqual(false, try ch.hit());
1213
1214 _ = try ch.addFilePost(temp_file2);
1215
1216 digest1 = ch.final();
1217 try ch.writeManifest();
1218 }
1219 {
1220 var ch = cache.obtain();
1221 defer ch.deinit();
1222
1223 ch.hash.addBytes("1234");
1224 _ = try ch.addFile(temp_file1, null);
1225
1226 try testing.expect(try ch.hit());
1227 digest2 = ch.final();
1228
1229 try testing.expectEqual(false, ch.have_exclusive_lock);
1230 }
1231 try testing.expect(mem.eql(u8, &digest1, &digest2));
1232
1233 // Modify the file added after initial hash
1234 try cwd.writeFile(temp_file2, "Hello world the second, updated\n");
1235
1236 // Wait for file timestamps to tick
1237 const initial_time2 = try testGetCurrentFileTimestamp();
1238 while ((try testGetCurrentFileTimestamp()) == initial_time2) {
1239 std.time.sleep(1);
1240 }
1241
1242 {
1243 var ch = cache.obtain();
1244 defer ch.deinit();
1245
1246 ch.hash.addBytes("1234");
1247 _ = try ch.addFile(temp_file1, null);
1248
1249 // A file that we depend on has been updated, so the cache should not contain an entry for it
1250 try testing.expectEqual(false, try ch.hit());
1251
1252 _ = try ch.addFilePost(temp_file2);
1253
1254 digest3 = ch.final();
1255
1256 try ch.writeManifest();
1257 }
1258
1259 try testing.expect(!mem.eql(u8, &digest1, &digest3));
1260 }
1261
1262 try cwd.deleteTree(temp_manifest_dir);
1263 try cwd.deleteFile(temp_file1);
1264 try cwd.deleteFile(temp_file2);
1265}
src/Compilation.zig+87-62
...@@ -26,7 +26,7 @@ const wasi_libc = @import("wasi_libc.zig");...@@ -26,7 +26,7 @@ const wasi_libc = @import("wasi_libc.zig");
26const fatal = @import("main.zig").fatal;26const fatal = @import("main.zig").fatal;
27const clangMain = @import("main.zig").clangMain;27const clangMain = @import("main.zig").clangMain;
28const Module = @import("Module.zig");28const Module = @import("Module.zig");
29const Cache = @import("Cache.zig");29const Cache = std.Build.Cache;
30const translate_c = @import("translate_c.zig");30const translate_c = @import("translate_c.zig");
31const clang = @import("clang.zig");31const clang = @import("clang.zig");
32const c_codegen = @import("codegen/c.zig");32const c_codegen = @import("codegen/c.zig");
...@@ -807,44 +807,7 @@ pub const AllErrors = struct {...@@ -807,44 +807,7 @@ pub const AllErrors = struct {
807 }807 }
808};808};
809809
810pub const Directory = struct {810pub const Directory = Cache.Directory;
811 /// This field is redundant for operations that can act on the open directory handle
812 /// directly, but it is needed when passing the directory to a child process.
813 /// `null` means cwd.
814 path: ?[]const u8,
815 handle: std.fs.Dir,
816
817 pub fn join(self: Directory, allocator: Allocator, paths: []const []const u8) ![]u8 {
818 if (self.path) |p| {
819 // TODO clean way to do this with only 1 allocation
820 const part2 = try std.fs.path.join(allocator, paths);
821 defer allocator.free(part2);
822 return std.fs.path.join(allocator, &[_][]const u8{ p, part2 });
823 } else {
824 return std.fs.path.join(allocator, paths);
825 }
826 }
827
828 pub fn joinZ(self: Directory, allocator: Allocator, paths: []const []const u8) ![:0]u8 {
829 if (self.path) |p| {
830 // TODO clean way to do this with only 1 allocation
831 const part2 = try std.fs.path.join(allocator, paths);
832 defer allocator.free(part2);
833 return std.fs.path.joinZ(allocator, &[_][]const u8{ p, part2 });
834 } else {
835 return std.fs.path.joinZ(allocator, paths);
836 }
837 }
838
839 /// Whether or not the handle should be closed, or the path should be freed
840 /// is determined by usage, however this function is provided for convenience
841 /// if it happens to be what the caller needs.
842 pub fn closeAndFree(self: *Directory, gpa: Allocator) void {
843 self.handle.close();
844 if (self.path) |p| gpa.free(p);
845 self.* = undefined;
846 }
847};
848811
849pub const EmitLoc = struct {812pub const EmitLoc = struct {
850 /// If this is `null` it means the file will be output to the cache directory.813 /// If this is `null` it means the file will be output to the cache directory.
...@@ -854,6 +817,35 @@ pub const EmitLoc = struct {...@@ -854,6 +817,35 @@ pub const EmitLoc = struct {
854 basename: []const u8,817 basename: []const u8,
855};818};
856819
820pub const cache_helpers = struct {
821 pub fn addEmitLoc(hh: *Cache.HashHelper, emit_loc: EmitLoc) void {
822 hh.addBytes(emit_loc.basename);
823 }
824
825 pub fn addOptionalEmitLoc(hh: *Cache.HashHelper, optional_emit_loc: ?EmitLoc) void {
826 hh.add(optional_emit_loc != null);
827 addEmitLoc(hh, optional_emit_loc orelse return);
828 }
829
830 pub fn hashCSource(self: *Cache.Manifest, c_source: Compilation.CSourceFile) !void {
831 _ = try self.addFile(c_source.src_path, null);
832 // Hash the extra flags, with special care to call addFile for file parameters.
833 // TODO this logic can likely be improved by utilizing clang_options_data.zig.
834 const file_args = [_][]const u8{"-include"};
835 var arg_i: usize = 0;
836 while (arg_i < c_source.extra_flags.len) : (arg_i += 1) {
837 const arg = c_source.extra_flags[arg_i];
838 self.hash.addBytes(arg);
839 for (file_args) |file_arg| {
840 if (mem.eql(u8, file_arg, arg) and arg_i + 1 < c_source.extra_flags.len) {
841 arg_i += 1;
842 _ = try self.addFile(c_source.extra_flags[arg_i], null);
843 }
844 }
845 }
846 }
847};
848
857pub const ClangPreprocessorMode = enum {849pub const ClangPreprocessorMode = enum {
858 no,850 no,
859 /// This means we are doing `zig cc -E -o <path>`.851 /// This means we are doing `zig cc -E -o <path>`.
...@@ -997,6 +989,7 @@ pub const InitOptions = struct {...@@ -997,6 +989,7 @@ pub const InitOptions = struct {
997 linker_optimization: ?u8 = null,989 linker_optimization: ?u8 = null,
998 linker_compress_debug_sections: ?link.CompressDebugSections = null,990 linker_compress_debug_sections: ?link.CompressDebugSections = null,
999 linker_module_definition_file: ?[]const u8 = null,991 linker_module_definition_file: ?[]const u8 = null,
992 linker_sort_section: ?link.SortSection = null,
1000 major_subsystem_version: ?u32 = null,993 major_subsystem_version: ?u32 = null,
1001 minor_subsystem_version: ?u32 = null,994 minor_subsystem_version: ?u32 = null,
1002 clang_passthrough_mode: bool = false,995 clang_passthrough_mode: bool = false,
...@@ -1029,6 +1022,7 @@ pub const InitOptions = struct {...@@ -1029,6 +1022,7 @@ pub const InitOptions = struct {
1029 /// This is for stage1 and should be deleted upon completion of self-hosting.1022 /// This is for stage1 and should be deleted upon completion of self-hosting.
1030 color: Color = .auto,1023 color: Color = .auto,
1031 reference_trace: ?u32 = null,1024 reference_trace: ?u32 = null,
1025 error_tracing: ?bool = null,
1032 test_filter: ?[]const u8 = null,1026 test_filter: ?[]const u8 = null,
1033 test_name_prefix: ?[]const u8 = null,1027 test_name_prefix: ?[]const u8 = null,
1034 test_runner_path: ?[]const u8 = null,1028 test_runner_path: ?[]const u8 = null,
...@@ -1522,8 +1516,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1522,8 +1516,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1522 cache.hash.add(link_libunwind);1516 cache.hash.add(link_libunwind);
1523 cache.hash.add(options.output_mode);1517 cache.hash.add(options.output_mode);
1524 cache.hash.add(options.machine_code_model);1518 cache.hash.add(options.machine_code_model);
1525 cache.hash.addOptionalEmitLoc(options.emit_bin);1519 cache_helpers.addOptionalEmitLoc(&cache.hash, options.emit_bin);
1526 cache.hash.addOptionalEmitLoc(options.emit_implib);1520 cache_helpers.addOptionalEmitLoc(&cache.hash, options.emit_implib);
1527 cache.hash.addBytes(options.root_name);1521 cache.hash.addBytes(options.root_name);
1528 if (options.target.os.tag == .wasi) cache.hash.add(wasi_exec_model);1522 if (options.target.os.tag == .wasi) cache.hash.add(wasi_exec_model);
1529 // TODO audit this and make sure everything is in it1523 // TODO audit this and make sure everything is in it
...@@ -1621,26 +1615,46 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1621,26 +1615,46 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1621 const root_pkg = if (options.is_test) root_pkg: {1615 const root_pkg = if (options.is_test) root_pkg: {
1622 // TODO: we currently have two packages named 'root' here, which is weird. This1616 // TODO: we currently have two packages named 'root' here, which is weird. This
1623 // should be changed as part of the resolution of #122011617 // should be changed as part of the resolution of #12201
1624 const test_pkg = if (options.test_runner_path) |test_runner|1618 const test_pkg = if (options.test_runner_path) |test_runner| test_pkg: {
1625 try Package.create(gpa, "root", null, test_runner)1619 const test_dir = std.fs.path.dirname(test_runner);
1626 else1620 const basename = std.fs.path.basename(test_runner);
1627 try Package.createWithDir(1621 const pkg = try Package.create(gpa, "root", test_dir, basename);
1628 gpa,1622
1629 "root",1623 // copy package table from main_pkg to root_pkg
1630 options.zig_lib_directory,1624 pkg.table = try main_pkg.table.clone(gpa);
1631 null,1625 break :test_pkg pkg;
1632 "test_runner.zig",1626 } else try Package.createWithDir(
1633 );1627 gpa,
1628 "root",
1629 options.zig_lib_directory,
1630 null,
1631 "test_runner.zig",
1632 );
1634 errdefer test_pkg.destroy(gpa);1633 errdefer test_pkg.destroy(gpa);
16351634
1636 break :root_pkg test_pkg;1635 break :root_pkg test_pkg;
1637 } else main_pkg;1636 } else main_pkg;
1638 errdefer if (options.is_test) root_pkg.destroy(gpa);1637 errdefer if (options.is_test) root_pkg.destroy(gpa);
16391638
1639 const compiler_rt_pkg = if (include_compiler_rt and options.output_mode == .Obj) compiler_rt_pkg: {
1640 break :compiler_rt_pkg try Package.createWithDir(
1641 gpa,
1642 "compiler_rt",
1643 options.zig_lib_directory,
1644 null,
1645 "compiler_rt.zig",
1646 );
1647 } else null;
1648 errdefer if (compiler_rt_pkg) |p| p.destroy(gpa);
1649
1640 try main_pkg.addAndAdopt(gpa, builtin_pkg);1650 try main_pkg.addAndAdopt(gpa, builtin_pkg);
1641 try main_pkg.add(gpa, root_pkg);1651 try main_pkg.add(gpa, root_pkg);
1642 try main_pkg.addAndAdopt(gpa, std_pkg);1652 try main_pkg.addAndAdopt(gpa, std_pkg);
16431653
1654 if (compiler_rt_pkg) |p| {
1655 try main_pkg.addAndAdopt(gpa, p);
1656 }
1657
1644 const main_pkg_is_std = m: {1658 const main_pkg_is_std = m: {
1645 const std_path = try std.fs.path.resolve(arena, &[_][]const u8{1659 const std_path = try std.fs.path.resolve(arena, &[_][]const u8{
1646 std_pkg.root_src_directory.path orelse ".",1660 std_pkg.root_src_directory.path orelse ".",
...@@ -1710,8 +1724,9 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1710,8 +1724,9 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
17101724
1711 const error_return_tracing = !strip and switch (options.optimize_mode) {1725 const error_return_tracing = !strip and switch (options.optimize_mode) {
1712 .Debug, .ReleaseSafe => (!options.target.isWasm() or options.target.os.tag == .emscripten) and1726 .Debug, .ReleaseSafe => (!options.target.isWasm() or options.target.os.tag == .emscripten) and
1713 !options.target.cpu.arch.isBpf(),1727 !options.target.cpu.arch.isBpf() and (options.error_tracing orelse true),
1714 .ReleaseFast, .ReleaseSmall => false,1728 .ReleaseFast => options.error_tracing orelse false,
1729 .ReleaseSmall => false,
1715 };1730 };
17161731
1717 // For resource management purposes.1732 // For resource management purposes.
...@@ -1839,6 +1854,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1839,6 +1854,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1839 .bind_global_refs_locally = options.linker_bind_global_refs_locally orelse false,1854 .bind_global_refs_locally = options.linker_bind_global_refs_locally orelse false,
1840 .compress_debug_sections = options.linker_compress_debug_sections orelse .none,1855 .compress_debug_sections = options.linker_compress_debug_sections orelse .none,
1841 .module_definition_file = options.linker_module_definition_file,1856 .module_definition_file = options.linker_module_definition_file,
1857 .sort_section = options.linker_sort_section,
1842 .import_memory = options.linker_import_memory orelse false,1858 .import_memory = options.linker_import_memory orelse false,
1843 .import_symbols = options.linker_import_symbols,1859 .import_symbols = options.linker_import_symbols,
1844 .import_table = options.linker_import_table,1860 .import_table = options.linker_import_table,
...@@ -2356,6 +2372,10 @@ pub fn update(comp: *Compilation) !void {...@@ -2356,6 +2372,10 @@ pub fn update(comp: *Compilation) !void {
2356 _ = try module.importPkg(module.main_pkg);2372 _ = try module.importPkg(module.main_pkg);
2357 }2373 }
23582374
2375 if (module.main_pkg.table.get("compiler_rt")) |compiler_rt_pkg| {
2376 _ = try module.importPkg(compiler_rt_pkg);
2377 }
2378
2359 // Put a work item in for every known source file to detect if2379 // Put a work item in for every known source file to detect if
2360 // it changed, and, if so, re-compute ZIR and then queue the job2380 // it changed, and, if so, re-compute ZIR and then queue the job
2361 // to update it.2381 // to update it.
...@@ -2380,6 +2400,10 @@ pub fn update(comp: *Compilation) !void {...@@ -2380,6 +2400,10 @@ pub fn update(comp: *Compilation) !void {
2380 if (comp.bin_file.options.is_test) {2400 if (comp.bin_file.options.is_test) {
2381 try comp.work_queue.writeItem(.{ .analyze_pkg = module.main_pkg });2401 try comp.work_queue.writeItem(.{ .analyze_pkg = module.main_pkg });
2382 }2402 }
2403
2404 if (module.main_pkg.table.get("compiler_rt")) |compiler_rt_pkg| {
2405 try comp.work_queue.writeItem(.{ .analyze_pkg = compiler_rt_pkg });
2406 }
2383 }2407 }
23842408
2385 // If the terminal is dumb, we dont want to show the user all the output.2409 // 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...@@ -2631,11 +2655,11 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
2631 man.hash.addListOfBytes(key.src.extra_flags);2655 man.hash.addListOfBytes(key.src.extra_flags);
2632 }2656 }
26332657
2634 man.hash.addOptionalEmitLoc(comp.emit_asm);2658 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_asm);
2635 man.hash.addOptionalEmitLoc(comp.emit_llvm_ir);2659 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_ir);
2636 man.hash.addOptionalEmitLoc(comp.emit_llvm_bc);2660 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_bc);
2637 man.hash.addOptionalEmitLoc(comp.emit_analysis);2661 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_analysis);
2638 man.hash.addOptionalEmitLoc(comp.emit_docs);2662 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_docs);
26392663
2640 man.hash.addListOfBytes(comp.clang_argv);2664 man.hash.addListOfBytes(comp.clang_argv);
26412665
...@@ -2662,6 +2686,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes...@@ -2662,6 +2686,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
2662 man.hash.add(comp.bin_file.options.hash_style);2686 man.hash.add(comp.bin_file.options.hash_style);
2663 man.hash.add(comp.bin_file.options.compress_debug_sections);2687 man.hash.add(comp.bin_file.options.compress_debug_sections);
2664 man.hash.add(comp.bin_file.options.include_compiler_rt);2688 man.hash.add(comp.bin_file.options.include_compiler_rt);
2689 man.hash.addOptional(comp.bin_file.options.sort_section);
2665 if (comp.bin_file.options.link_libc) {2690 if (comp.bin_file.options.link_libc) {
2666 man.hash.add(comp.bin_file.options.libc_installation != null);2691 man.hash.add(comp.bin_file.options.libc_installation != null);
2667 if (comp.bin_file.options.libc_installation) |libc_installation| {2692 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...@@ -3954,11 +3979,11 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
3954 defer man.deinit();3979 defer man.deinit();
39553980
3956 man.hash.add(comp.clang_preprocessor_mode);3981 man.hash.add(comp.clang_preprocessor_mode);
3957 man.hash.addOptionalEmitLoc(comp.emit_asm);3982 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_asm);
3958 man.hash.addOptionalEmitLoc(comp.emit_llvm_ir);3983 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_ir);
3959 man.hash.addOptionalEmitLoc(comp.emit_llvm_bc);3984 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_bc);
39603985
3961 try man.hashCSource(c_object.src);3986 try cache_helpers.hashCSource(&man, c_object.src);
39623987
3963 var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);3988 var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
3964 defer arena_allocator.deinit();3989 defer arena_allocator.deinit();
src/DepTokenizer.zig deleted-1069
...@@ -1,1069 +0,0 @@
1const Tokenizer = @This();
2
3index: usize = 0,
4bytes: []const u8,
5state: State = .lhs,
6
7const std = @import("std");
8const testing = std.testing;
9const assert = std.debug.assert;
10
11pub fn next(self: *Tokenizer) ?Token {
12 var start = self.index;
13 var must_resolve = false;
14 while (self.index < self.bytes.len) {
15 const char = self.bytes[self.index];
16 switch (self.state) {
17 .lhs => switch (char) {
18 '\t', '\n', '\r', ' ' => {
19 // silently ignore whitespace
20 self.index += 1;
21 },
22 else => {
23 start = self.index;
24 self.state = .target;
25 },
26 },
27 .target => switch (char) {
28 '\t', '\n', '\r', ' ' => {
29 return errorIllegalChar(.invalid_target, self.index, char);
30 },
31 '$' => {
32 self.state = .target_dollar_sign;
33 self.index += 1;
34 },
35 '\\' => {
36 self.state = .target_reverse_solidus;
37 self.index += 1;
38 },
39 ':' => {
40 self.state = .target_colon;
41 self.index += 1;
42 },
43 else => {
44 self.index += 1;
45 },
46 },
47 .target_reverse_solidus => switch (char) {
48 '\t', '\n', '\r' => {
49 return errorIllegalChar(.bad_target_escape, self.index, char);
50 },
51 ' ', '#', '\\' => {
52 must_resolve = true;
53 self.state = .target;
54 self.index += 1;
55 },
56 '$' => {
57 self.state = .target_dollar_sign;
58 self.index += 1;
59 },
60 else => {
61 self.state = .target;
62 self.index += 1;
63 },
64 },
65 .target_dollar_sign => switch (char) {
66 '$' => {
67 must_resolve = true;
68 self.state = .target;
69 self.index += 1;
70 },
71 else => {
72 return errorIllegalChar(.expected_dollar_sign, self.index, char);
73 },
74 },
75 .target_colon => switch (char) {
76 '\n', '\r' => {
77 const bytes = self.bytes[start .. self.index - 1];
78 if (bytes.len != 0) {
79 self.state = .lhs;
80 return finishTarget(must_resolve, bytes);
81 }
82 // silently ignore null target
83 self.state = .lhs;
84 },
85 '/', '\\' => {
86 self.state = .target_colon_reverse_solidus;
87 self.index += 1;
88 },
89 else => {
90 const bytes = self.bytes[start .. self.index - 1];
91 if (bytes.len != 0) {
92 self.state = .rhs;
93 return finishTarget(must_resolve, bytes);
94 }
95 // silently ignore null target
96 self.state = .lhs;
97 },
98 },
99 .target_colon_reverse_solidus => switch (char) {
100 '\n', '\r' => {
101 const bytes = self.bytes[start .. self.index - 2];
102 if (bytes.len != 0) {
103 self.state = .lhs;
104 return finishTarget(must_resolve, bytes);
105 }
106 // silently ignore null target
107 self.state = .lhs;
108 },
109 else => {
110 self.state = .target;
111 },
112 },
113 .rhs => switch (char) {
114 '\t', ' ' => {
115 // silently ignore horizontal whitespace
116 self.index += 1;
117 },
118 '\n', '\r' => {
119 self.state = .lhs;
120 },
121 '\\' => {
122 self.state = .rhs_continuation;
123 self.index += 1;
124 },
125 '"' => {
126 self.state = .prereq_quote;
127 self.index += 1;
128 start = self.index;
129 },
130 else => {
131 start = self.index;
132 self.state = .prereq;
133 },
134 },
135 .rhs_continuation => switch (char) {
136 '\n' => {
137 self.state = .rhs;
138 self.index += 1;
139 },
140 '\r' => {
141 self.state = .rhs_continuation_linefeed;
142 self.index += 1;
143 },
144 else => {
145 return errorIllegalChar(.continuation_eol, self.index, char);
146 },
147 },
148 .rhs_continuation_linefeed => switch (char) {
149 '\n' => {
150 self.state = .rhs;
151 self.index += 1;
152 },
153 else => {
154 return errorIllegalChar(.continuation_eol, self.index, char);
155 },
156 },
157 .prereq_quote => switch (char) {
158 '"' => {
159 self.index += 1;
160 self.state = .rhs;
161 return Token{ .prereq = self.bytes[start .. self.index - 1] };
162 },
163 else => {
164 self.index += 1;
165 },
166 },
167 .prereq => switch (char) {
168 '\t', ' ' => {
169 self.state = .rhs;
170 return Token{ .prereq = self.bytes[start..self.index] };
171 },
172 '\n', '\r' => {
173 self.state = .lhs;
174 return Token{ .prereq = self.bytes[start..self.index] };
175 },
176 '\\' => {
177 self.state = .prereq_continuation;
178 self.index += 1;
179 },
180 else => {
181 self.index += 1;
182 },
183 },
184 .prereq_continuation => switch (char) {
185 '\n' => {
186 self.index += 1;
187 self.state = .rhs;
188 return Token{ .prereq = self.bytes[start .. self.index - 2] };
189 },
190 '\r' => {
191 self.state = .prereq_continuation_linefeed;
192 self.index += 1;
193 },
194 else => {
195 // not continuation
196 self.state = .prereq;
197 self.index += 1;
198 },
199 },
200 .prereq_continuation_linefeed => switch (char) {
201 '\n' => {
202 self.index += 1;
203 self.state = .rhs;
204 return Token{ .prereq = self.bytes[start .. self.index - 1] };
205 },
206 else => {
207 return errorIllegalChar(.continuation_eol, self.index, char);
208 },
209 },
210 }
211 } else {
212 switch (self.state) {
213 .lhs,
214 .rhs,
215 .rhs_continuation,
216 .rhs_continuation_linefeed,
217 => return null,
218 .target => {
219 return errorPosition(.incomplete_target, start, self.bytes[start..]);
220 },
221 .target_reverse_solidus,
222 .target_dollar_sign,
223 => {
224 const idx = self.index - 1;
225 return errorIllegalChar(.incomplete_escape, idx, self.bytes[idx]);
226 },
227 .target_colon => {
228 const bytes = self.bytes[start .. self.index - 1];
229 if (bytes.len != 0) {
230 self.index += 1;
231 self.state = .rhs;
232 return finishTarget(must_resolve, bytes);
233 }
234 // silently ignore null target
235 self.state = .lhs;
236 return null;
237 },
238 .target_colon_reverse_solidus => {
239 const bytes = self.bytes[start .. self.index - 2];
240 if (bytes.len != 0) {
241 self.index += 1;
242 self.state = .rhs;
243 return finishTarget(must_resolve, bytes);
244 }
245 // silently ignore null target
246 self.state = .lhs;
247 return null;
248 },
249 .prereq_quote => {
250 return errorPosition(.incomplete_quoted_prerequisite, start, self.bytes[start..]);
251 },
252 .prereq => {
253 self.state = .lhs;
254 return Token{ .prereq = self.bytes[start..] };
255 },
256 .prereq_continuation => {
257 self.state = .lhs;
258 return Token{ .prereq = self.bytes[start .. self.index - 1] };
259 },
260 .prereq_continuation_linefeed => {
261 self.state = .lhs;
262 return Token{ .prereq = self.bytes[start .. self.index - 2] };
263 },
264 }
265 }
266 unreachable;
267}
268
269fn errorPosition(comptime id: std.meta.Tag(Token), index: usize, bytes: []const u8) Token {
270 return @unionInit(Token, @tagName(id), .{ .index = index, .bytes = bytes });
271}
272
273fn errorIllegalChar(comptime id: std.meta.Tag(Token), index: usize, char: u8) Token {
274 return @unionInit(Token, @tagName(id), .{ .index = index, .char = char });
275}
276
277fn finishTarget(must_resolve: bool, bytes: []const u8) Token {
278 return if (must_resolve) .{ .target_must_resolve = bytes } else .{ .target = bytes };
279}
280
281const State = enum {
282 lhs,
283 target,
284 target_reverse_solidus,
285 target_dollar_sign,
286 target_colon,
287 target_colon_reverse_solidus,
288 rhs,
289 rhs_continuation,
290 rhs_continuation_linefeed,
291 prereq_quote,
292 prereq,
293 prereq_continuation,
294 prereq_continuation_linefeed,
295};
296
297pub const Token = union(enum) {
298 target: []const u8,
299 target_must_resolve: []const u8,
300 prereq: []const u8,
301
302 incomplete_quoted_prerequisite: IndexAndBytes,
303 incomplete_target: IndexAndBytes,
304
305 invalid_target: IndexAndChar,
306 bad_target_escape: IndexAndChar,
307 expected_dollar_sign: IndexAndChar,
308 continuation_eol: IndexAndChar,
309 incomplete_escape: IndexAndChar,
310
311 pub const IndexAndChar = struct {
312 index: usize,
313 char: u8,
314 };
315
316 pub const IndexAndBytes = struct {
317 index: usize,
318 bytes: []const u8,
319 };
320
321 /// Resolve escapes in target. Only valid with .target_must_resolve.
322 pub fn resolve(self: Token, writer: anytype) @TypeOf(writer).Error!void {
323 const bytes = self.target_must_resolve; // resolve called on incorrect token
324
325 var state: enum { start, escape, dollar } = .start;
326 for (bytes) |c| {
327 switch (state) {
328 .start => {
329 switch (c) {
330 '\\' => state = .escape,
331 '$' => state = .dollar,
332 else => try writer.writeByte(c),
333 }
334 },
335 .escape => {
336 switch (c) {
337 ' ', '#', '\\' => {},
338 '$' => {
339 try writer.writeByte('\\');
340 state = .dollar;
341 continue;
342 },
343 else => try writer.writeByte('\\'),
344 }
345 try writer.writeByte(c);
346 state = .start;
347 },
348 .dollar => {
349 try writer.writeByte('$');
350 switch (c) {
351 '$' => {},
352 else => try writer.writeByte(c),
353 }
354 state = .start;
355 },
356 }
357 }
358 }
359
360 pub fn printError(self: Token, writer: anytype) @TypeOf(writer).Error!void {
361 switch (self) {
362 .target, .target_must_resolve, .prereq => unreachable, // not an error
363 .incomplete_quoted_prerequisite,
364 .incomplete_target,
365 => |index_and_bytes| {
366 try writer.print("{s} '", .{self.errStr()});
367 if (self == .incomplete_target) {
368 const tmp = Token{ .target_must_resolve = index_and_bytes.bytes };
369 try tmp.resolve(writer);
370 } else {
371 try printCharValues(writer, index_and_bytes.bytes);
372 }
373 try writer.print("' at position {d}", .{index_and_bytes.index});
374 },
375 .invalid_target,
376 .bad_target_escape,
377 .expected_dollar_sign,
378 .continuation_eol,
379 .incomplete_escape,
380 => |index_and_char| {
381 try writer.writeAll("illegal char ");
382 try printUnderstandableChar(writer, index_and_char.char);
383 try writer.print(" at position {d}: {s}", .{ index_and_char.index, self.errStr() });
384 },
385 }
386 }
387
388 fn errStr(self: Token) []const u8 {
389 return switch (self) {
390 .target, .target_must_resolve, .prereq => unreachable, // not an error
391 .incomplete_quoted_prerequisite => "incomplete quoted prerequisite",
392 .incomplete_target => "incomplete target",
393 .invalid_target => "invalid target",
394 .bad_target_escape => "bad target escape",
395 .expected_dollar_sign => "expecting '$'",
396 .continuation_eol => "continuation expecting end-of-line",
397 .incomplete_escape => "incomplete escape",
398 };
399 }
400};
401
402test "empty file" {
403 try depTokenizer("", "");
404}
405
406test "empty whitespace" {
407 try depTokenizer("\n", "");
408 try depTokenizer("\r", "");
409 try depTokenizer("\r\n", "");
410 try depTokenizer(" ", "");
411}
412
413test "empty colon" {
414 try depTokenizer(":", "");
415 try depTokenizer("\n:", "");
416 try depTokenizer("\r:", "");
417 try depTokenizer("\r\n:", "");
418 try depTokenizer(" :", "");
419}
420
421test "empty target" {
422 try depTokenizer("foo.o:", "target = {foo.o}");
423 try depTokenizer(
424 \\foo.o:
425 \\bar.o:
426 \\abcd.o:
427 ,
428 \\target = {foo.o}
429 \\target = {bar.o}
430 \\target = {abcd.o}
431 );
432}
433
434test "whitespace empty target" {
435 try depTokenizer("\nfoo.o:", "target = {foo.o}");
436 try depTokenizer("\rfoo.o:", "target = {foo.o}");
437 try depTokenizer("\r\nfoo.o:", "target = {foo.o}");
438 try depTokenizer(" foo.o:", "target = {foo.o}");
439}
440
441test "escape empty target" {
442 try depTokenizer("\\ foo.o:", "target = { foo.o}");
443 try depTokenizer("\\#foo.o:", "target = {#foo.o}");
444 try depTokenizer("\\\\foo.o:", "target = {\\foo.o}");
445 try depTokenizer("$$foo.o:", "target = {$foo.o}");
446}
447
448test "empty target linefeeds" {
449 try depTokenizer("\n", "");
450 try depTokenizer("\r\n", "");
451
452 const expect = "target = {foo.o}";
453 try depTokenizer(
454 \\foo.o:
455 , expect);
456 try depTokenizer(
457 \\foo.o:
458 \\
459 , expect);
460 try depTokenizer(
461 \\foo.o:
462 , expect);
463 try depTokenizer(
464 \\foo.o:
465 \\
466 , expect);
467}
468
469test "empty target linefeeds + continuations" {
470 const expect = "target = {foo.o}";
471 try depTokenizer(
472 \\foo.o:\
473 , expect);
474 try depTokenizer(
475 \\foo.o:\
476 \\
477 , expect);
478 try depTokenizer(
479 \\foo.o:\
480 , expect);
481 try depTokenizer(
482 \\foo.o:\
483 \\
484 , expect);
485}
486
487test "empty target linefeeds + hspace + continuations" {
488 const expect = "target = {foo.o}";
489 try depTokenizer(
490 \\foo.o: \
491 , expect);
492 try depTokenizer(
493 \\foo.o: \
494 \\
495 , expect);
496 try depTokenizer(
497 \\foo.o: \
498 , expect);
499 try depTokenizer(
500 \\foo.o: \
501 \\
502 , expect);
503}
504
505test "prereq" {
506 const expect =
507 \\target = {foo.o}
508 \\prereq = {foo.c}
509 ;
510 try depTokenizer("foo.o: foo.c", expect);
511 try depTokenizer(
512 \\foo.o: \
513 \\foo.c
514 , expect);
515 try depTokenizer(
516 \\foo.o: \
517 \\ foo.c
518 , expect);
519 try depTokenizer(
520 \\foo.o: \
521 \\ foo.c
522 , expect);
523}
524
525test "prereq continuation" {
526 const expect =
527 \\target = {foo.o}
528 \\prereq = {foo.h}
529 \\prereq = {bar.h}
530 ;
531 try depTokenizer(
532 \\foo.o: foo.h\
533 \\bar.h
534 , expect);
535 try depTokenizer(
536 \\foo.o: foo.h\
537 \\bar.h
538 , expect);
539}
540
541test "multiple prereqs" {
542 const expect =
543 \\target = {foo.o}
544 \\prereq = {foo.c}
545 \\prereq = {foo.h}
546 \\prereq = {bar.h}
547 ;
548 try depTokenizer("foo.o: foo.c foo.h bar.h", expect);
549 try depTokenizer(
550 \\foo.o: \
551 \\foo.c foo.h bar.h
552 , expect);
553 try depTokenizer(
554 \\foo.o: foo.c foo.h bar.h\
555 , expect);
556 try depTokenizer(
557 \\foo.o: foo.c foo.h bar.h\
558 \\
559 , expect);
560 try depTokenizer(
561 \\foo.o: \
562 \\foo.c \
563 \\ foo.h\
564 \\bar.h
565 \\
566 , expect);
567 try depTokenizer(
568 \\foo.o: \
569 \\foo.c \
570 \\ foo.h\
571 \\bar.h\
572 \\
573 , expect);
574 try depTokenizer(
575 \\foo.o: \
576 \\foo.c \
577 \\ foo.h\
578 \\bar.h\
579 , expect);
580}
581
582test "multiple targets and prereqs" {
583 try depTokenizer(
584 \\foo.o: foo.c
585 \\bar.o: bar.c a.h b.h c.h
586 \\abc.o: abc.c \
587 \\ one.h two.h \
588 \\ three.h four.h
589 ,
590 \\target = {foo.o}
591 \\prereq = {foo.c}
592 \\target = {bar.o}
593 \\prereq = {bar.c}
594 \\prereq = {a.h}
595 \\prereq = {b.h}
596 \\prereq = {c.h}
597 \\target = {abc.o}
598 \\prereq = {abc.c}
599 \\prereq = {one.h}
600 \\prereq = {two.h}
601 \\prereq = {three.h}
602 \\prereq = {four.h}
603 );
604 try depTokenizer(
605 \\ascii.o: ascii.c
606 \\base64.o: base64.c stdio.h
607 \\elf.o: elf.c a.h b.h c.h
608 \\macho.o: \
609 \\ macho.c\
610 \\ a.h b.h c.h
611 ,
612 \\target = {ascii.o}
613 \\prereq = {ascii.c}
614 \\target = {base64.o}
615 \\prereq = {base64.c}
616 \\prereq = {stdio.h}
617 \\target = {elf.o}
618 \\prereq = {elf.c}
619 \\prereq = {a.h}
620 \\prereq = {b.h}
621 \\prereq = {c.h}
622 \\target = {macho.o}
623 \\prereq = {macho.c}
624 \\prereq = {a.h}
625 \\prereq = {b.h}
626 \\prereq = {c.h}
627 );
628 try depTokenizer(
629 \\a$$scii.o: ascii.c
630 \\\\base64.o: "\base64.c" "s t#dio.h"
631 \\e\\lf.o: "e\lf.c" "a.h$$" "$$b.h c.h$$"
632 \\macho.o: \
633 \\ "macho!.c" \
634 \\ a.h b.h c.h
635 ,
636 \\target = {a$scii.o}
637 \\prereq = {ascii.c}
638 \\target = {\base64.o}
639 \\prereq = {\base64.c}
640 \\prereq = {s t#dio.h}
641 \\target = {e\lf.o}
642 \\prereq = {e\lf.c}
643 \\prereq = {a.h$$}
644 \\prereq = {$$b.h c.h$$}
645 \\target = {macho.o}
646 \\prereq = {macho!.c}
647 \\prereq = {a.h}
648 \\prereq = {b.h}
649 \\prereq = {c.h}
650 );
651}
652
653test "windows quoted prereqs" {
654 try depTokenizer(
655 \\c:\foo.o: "C:\Program Files (x86)\Microsoft Visual Studio\foo.c"
656 \\c:\foo2.o: "C:\Program Files (x86)\Microsoft Visual Studio\foo2.c" \
657 \\ "C:\Program Files (x86)\Microsoft Visual Studio\foo1.h" \
658 \\ "C:\Program Files (x86)\Microsoft Visual Studio\foo2.h"
659 ,
660 \\target = {c:\foo.o}
661 \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\foo.c}
662 \\target = {c:\foo2.o}
663 \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\foo2.c}
664 \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\foo1.h}
665 \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\foo2.h}
666 );
667}
668
669test "windows mixed prereqs" {
670 try depTokenizer(
671 \\cimport.o: \
672 \\ C:\msys64\home\anon\project\zig\master\zig-cache\o\qhvhbUo7GU5iKyQ5mpA8TcQpncCYaQu0wwvr3ybiSTj_Dtqi1Nmcb70kfODJ2Qlg\cimport.h \
673 \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\stdio.h" \
674 \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt.h" \
675 \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vcruntime.h" \
676 \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\sal.h" \
677 \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\concurrencysal.h" \
678 \\ C:\msys64\opt\zig\lib\zig\include\vadefs.h \
679 \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vadefs.h" \
680 \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_wstdio.h" \
681 \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_stdio_config.h" \
682 \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\string.h" \
683 \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_memory.h" \
684 \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_memcpy_s.h" \
685 \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\errno.h" \
686 \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vcruntime_string.h" \
687 \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_wstring.h"
688 ,
689 \\target = {cimport.o}
690 \\prereq = {C:\msys64\home\anon\project\zig\master\zig-cache\o\qhvhbUo7GU5iKyQ5mpA8TcQpncCYaQu0wwvr3ybiSTj_Dtqi1Nmcb70kfODJ2Qlg\cimport.h}
691 \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\stdio.h}
692 \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt.h}
693 \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vcruntime.h}
694 \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\sal.h}
695 \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\concurrencysal.h}
696 \\prereq = {C:\msys64\opt\zig\lib\zig\include\vadefs.h}
697 \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vadefs.h}
698 \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_wstdio.h}
699 \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_stdio_config.h}
700 \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\string.h}
701 \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_memory.h}
702 \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_memcpy_s.h}
703 \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\errno.h}
704 \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vcruntime_string.h}
705 \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_wstring.h}
706 );
707}
708
709test "windows funky targets" {
710 try depTokenizer(
711 \\C:\Users\anon\foo.o:
712 \\C:\Users\anon\foo\ .o:
713 \\C:\Users\anon\foo\#.o:
714 \\C:\Users\anon\foo$$.o:
715 \\C:\Users\anon\\\ foo.o:
716 \\C:\Users\anon\\#foo.o:
717 \\C:\Users\anon\$$foo.o:
718 \\C:\Users\anon\\\ \ \ \ \ foo.o:
719 ,
720 \\target = {C:\Users\anon\foo.o}
721 \\target = {C:\Users\anon\foo .o}
722 \\target = {C:\Users\anon\foo#.o}
723 \\target = {C:\Users\anon\foo$.o}
724 \\target = {C:\Users\anon\ foo.o}
725 \\target = {C:\Users\anon\#foo.o}
726 \\target = {C:\Users\anon\$foo.o}
727 \\target = {C:\Users\anon\ foo.o}
728 );
729}
730
731test "windows drive and forward slashes" {
732 try depTokenizer(
733 \\C:/msys64/what/zig-cache\tmp\48ac4d78dd531abd-cxa_thread_atexit.obj: \
734 \\ C:/msys64/opt/zig3/lib/zig/libc/mingw/crt/cxa_thread_atexit.c
735 ,
736 \\target = {C:/msys64/what/zig-cache\tmp\48ac4d78dd531abd-cxa_thread_atexit.obj}
737 \\prereq = {C:/msys64/opt/zig3/lib/zig/libc/mingw/crt/cxa_thread_atexit.c}
738 );
739}
740
741test "error incomplete escape - reverse_solidus" {
742 try depTokenizer("\\",
743 \\ERROR: illegal char '\' at position 0: incomplete escape
744 );
745 try depTokenizer("\t\\",
746 \\ERROR: illegal char '\' at position 1: incomplete escape
747 );
748 try depTokenizer("\n\\",
749 \\ERROR: illegal char '\' at position 1: incomplete escape
750 );
751 try depTokenizer("\r\\",
752 \\ERROR: illegal char '\' at position 1: incomplete escape
753 );
754 try depTokenizer("\r\n\\",
755 \\ERROR: illegal char '\' at position 2: incomplete escape
756 );
757 try depTokenizer(" \\",
758 \\ERROR: illegal char '\' at position 1: incomplete escape
759 );
760}
761
762test "error incomplete escape - dollar_sign" {
763 try depTokenizer("$",
764 \\ERROR: illegal char '$' at position 0: incomplete escape
765 );
766 try depTokenizer("\t$",
767 \\ERROR: illegal char '$' at position 1: incomplete escape
768 );
769 try depTokenizer("\n$",
770 \\ERROR: illegal char '$' at position 1: incomplete escape
771 );
772 try depTokenizer("\r$",
773 \\ERROR: illegal char '$' at position 1: incomplete escape
774 );
775 try depTokenizer("\r\n$",
776 \\ERROR: illegal char '$' at position 2: incomplete escape
777 );
778 try depTokenizer(" $",
779 \\ERROR: illegal char '$' at position 1: incomplete escape
780 );
781}
782
783test "error incomplete target" {
784 try depTokenizer("foo.o",
785 \\ERROR: incomplete target 'foo.o' at position 0
786 );
787 try depTokenizer("\tfoo.o",
788 \\ERROR: incomplete target 'foo.o' at position 1
789 );
790 try depTokenizer("\nfoo.o",
791 \\ERROR: incomplete target 'foo.o' at position 1
792 );
793 try depTokenizer("\rfoo.o",
794 \\ERROR: incomplete target 'foo.o' at position 1
795 );
796 try depTokenizer("\r\nfoo.o",
797 \\ERROR: incomplete target 'foo.o' at position 2
798 );
799 try depTokenizer(" foo.o",
800 \\ERROR: incomplete target 'foo.o' at position 1
801 );
802
803 try depTokenizer("\\ foo.o",
804 \\ERROR: incomplete target ' foo.o' at position 0
805 );
806 try depTokenizer("\\#foo.o",
807 \\ERROR: incomplete target '#foo.o' at position 0
808 );
809 try depTokenizer("\\\\foo.o",
810 \\ERROR: incomplete target '\foo.o' at position 0
811 );
812 try depTokenizer("$$foo.o",
813 \\ERROR: incomplete target '$foo.o' at position 0
814 );
815}
816
817test "error illegal char at position - bad target escape" {
818 try depTokenizer("\\\t",
819 \\ERROR: illegal char \x09 at position 1: bad target escape
820 );
821 try depTokenizer("\\\n",
822 \\ERROR: illegal char \x0A at position 1: bad target escape
823 );
824 try depTokenizer("\\\r",
825 \\ERROR: illegal char \x0D at position 1: bad target escape
826 );
827 try depTokenizer("\\\r\n",
828 \\ERROR: illegal char \x0D at position 1: bad target escape
829 );
830}
831
832test "error illegal char at position - execting dollar_sign" {
833 try depTokenizer("$\t",
834 \\ERROR: illegal char \x09 at position 1: expecting '$'
835 );
836 try depTokenizer("$\n",
837 \\ERROR: illegal char \x0A at position 1: expecting '$'
838 );
839 try depTokenizer("$\r",
840 \\ERROR: illegal char \x0D at position 1: expecting '$'
841 );
842 try depTokenizer("$\r\n",
843 \\ERROR: illegal char \x0D at position 1: expecting '$'
844 );
845}
846
847test "error illegal char at position - invalid target" {
848 try depTokenizer("foo\t.o",
849 \\ERROR: illegal char \x09 at position 3: invalid target
850 );
851 try depTokenizer("foo\n.o",
852 \\ERROR: illegal char \x0A at position 3: invalid target
853 );
854 try depTokenizer("foo\r.o",
855 \\ERROR: illegal char \x0D at position 3: invalid target
856 );
857 try depTokenizer("foo\r\n.o",
858 \\ERROR: illegal char \x0D at position 3: invalid target
859 );
860}
861
862test "error target - continuation expecting end-of-line" {
863 try depTokenizer("foo.o: \\\t",
864 \\target = {foo.o}
865 \\ERROR: illegal char \x09 at position 8: continuation expecting end-of-line
866 );
867 try depTokenizer("foo.o: \\ ",
868 \\target = {foo.o}
869 \\ERROR: illegal char ' ' at position 8: continuation expecting end-of-line
870 );
871 try depTokenizer("foo.o: \\x",
872 \\target = {foo.o}
873 \\ERROR: illegal char 'x' at position 8: continuation expecting end-of-line
874 );
875 try depTokenizer("foo.o: \\\x0dx",
876 \\target = {foo.o}
877 \\ERROR: illegal char 'x' at position 9: continuation expecting end-of-line
878 );
879}
880
881test "error prereq - continuation expecting end-of-line" {
882 try depTokenizer("foo.o: foo.h\\\x0dx",
883 \\target = {foo.o}
884 \\ERROR: illegal char 'x' at position 14: continuation expecting end-of-line
885 );
886}
887
888// - tokenize input, emit textual representation, and compare to expect
889fn depTokenizer(input: []const u8, expect: []const u8) !void {
890 var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator);
891 const arena = arena_allocator.allocator();
892 defer arena_allocator.deinit();
893
894 var it: Tokenizer = .{ .bytes = input };
895 var buffer = std.ArrayList(u8).init(arena);
896 var resolve_buf = std.ArrayList(u8).init(arena);
897 var i: usize = 0;
898 while (it.next()) |token| {
899 if (i != 0) try buffer.appendSlice("\n");
900 switch (token) {
901 .target, .prereq => |bytes| {
902 try buffer.appendSlice(@tagName(token));
903 try buffer.appendSlice(" = {");
904 for (bytes) |b| {
905 try buffer.append(printable_char_tab[b]);
906 }
907 try buffer.appendSlice("}");
908 },
909 .target_must_resolve => {
910 try buffer.appendSlice("target = {");
911 try token.resolve(resolve_buf.writer());
912 for (resolve_buf.items) |b| {
913 try buffer.append(printable_char_tab[b]);
914 }
915 resolve_buf.items.len = 0;
916 try buffer.appendSlice("}");
917 },
918 else => {
919 try buffer.appendSlice("ERROR: ");
920 try token.printError(buffer.writer());
921 break;
922 },
923 }
924 i += 1;
925 }
926
927 if (std.mem.eql(u8, expect, buffer.items)) {
928 try testing.expect(true);
929 return;
930 }
931
932 const out = std.io.getStdErr().writer();
933
934 try out.writeAll("\n");
935 try printSection(out, "<<<< input", input);
936 try printSection(out, "==== expect", expect);
937 try printSection(out, ">>>> got", buffer.items);
938 try printRuler(out);
939
940 try testing.expect(false);
941}
942
943fn printSection(out: anytype, label: []const u8, bytes: []const u8) !void {
944 try printLabel(out, label, bytes);
945 try hexDump(out, bytes);
946 try printRuler(out);
947 try out.writeAll(bytes);
948 try out.writeAll("\n");
949}
950
951fn printLabel(out: anytype, label: []const u8, bytes: []const u8) !void {
952 var buf: [80]u8 = undefined;
953 var text = try std.fmt.bufPrint(buf[0..], "{s} {d} bytes ", .{ label, bytes.len });
954 try out.writeAll(text);
955 var i: usize = text.len;
956 const end = 79;
957 while (i < end) : (i += 1) {
958 try out.writeAll(&[_]u8{label[0]});
959 }
960 try out.writeAll("\n");
961}
962
963fn printRuler(out: anytype) !void {
964 var i: usize = 0;
965 const end = 79;
966 while (i < end) : (i += 1) {
967 try out.writeAll("-");
968 }
969 try out.writeAll("\n");
970}
971
972fn hexDump(out: anytype, bytes: []const u8) !void {
973 const n16 = bytes.len >> 4;
974 var line: usize = 0;
975 var offset: usize = 0;
976 while (line < n16) : (line += 1) {
977 try hexDump16(out, offset, bytes[offset .. offset + 16]);
978 offset += 16;
979 }
980
981 const n = bytes.len & 0x0f;
982 if (n > 0) {
983 try printDecValue(out, offset, 8);
984 try out.writeAll(":");
985 try out.writeAll(" ");
986 var end1 = std.math.min(offset + n, offset + 8);
987 for (bytes[offset..end1]) |b| {
988 try out.writeAll(" ");
989 try printHexValue(out, b, 2);
990 }
991 var end2 = offset + n;
992 if (end2 > end1) {
993 try out.writeAll(" ");
994 for (bytes[end1..end2]) |b| {
995 try out.writeAll(" ");
996 try printHexValue(out, b, 2);
997 }
998 }
999 const short = 16 - n;
1000 var i: usize = 0;
1001 while (i < short) : (i += 1) {
1002 try out.writeAll(" ");
1003 }
1004 if (end2 > end1) {
1005 try out.writeAll(" |");
1006 } else {
1007 try out.writeAll(" |");
1008 }
1009 try printCharValues(out, bytes[offset..end2]);
1010 try out.writeAll("|\n");
1011 offset += n;
1012 }
1013
1014 try printDecValue(out, offset, 8);
1015 try out.writeAll(":");
1016 try out.writeAll("\n");
1017}
1018
1019fn hexDump16(out: anytype, offset: usize, bytes: []const u8) !void {
1020 try printDecValue(out, offset, 8);
1021 try out.writeAll(":");
1022 try out.writeAll(" ");
1023 for (bytes[0..8]) |b| {
1024 try out.writeAll(" ");
1025 try printHexValue(out, b, 2);
1026 }
1027 try out.writeAll(" ");
1028 for (bytes[8..16]) |b| {
1029 try out.writeAll(" ");
1030 try printHexValue(out, b, 2);
1031 }
1032 try out.writeAll(" |");
1033 try printCharValues(out, bytes);
1034 try out.writeAll("|\n");
1035}
1036
1037fn printDecValue(out: anytype, value: u64, width: u8) !void {
1038 var buffer: [20]u8 = undefined;
1039 const len = std.fmt.formatIntBuf(buffer[0..], value, 10, .lower, .{ .width = width, .fill = '0' });
1040 try out.writeAll(buffer[0..len]);
1041}
1042
1043fn printHexValue(out: anytype, value: u64, width: u8) !void {
1044 var buffer: [16]u8 = undefined;
1045 const len = std.fmt.formatIntBuf(buffer[0..], value, 16, .lower, .{ .width = width, .fill = '0' });
1046 try out.writeAll(buffer[0..len]);
1047}
1048
1049fn printCharValues(out: anytype, bytes: []const u8) !void {
1050 for (bytes) |b| {
1051 try out.writeAll(&[_]u8{printable_char_tab[b]});
1052 }
1053}
1054
1055fn printUnderstandableChar(out: anytype, char: u8) !void {
1056 if (std.ascii.isPrint(char)) {
1057 try out.print("'{c}'", .{char});
1058 } else {
1059 try out.print("\\x{X:0>2}", .{char});
1060 }
1061}
1062
1063// zig fmt: off
1064const printable_char_tab: [256]u8 = (
1065 "................................ !\"#$%&'()*+,-./0123456789:;<=>?" ++
1066 "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~." ++
1067 "................................................................" ++
1068 "................................................................"
1069).*;
src/Module.zig+41-15
...@@ -16,7 +16,7 @@ const Ast = std.zig.Ast;...@@ -16,7 +16,7 @@ const Ast = std.zig.Ast;
1616
17const Module = @This();17const Module = @This();
18const Compilation = @import("Compilation.zig");18const Compilation = @import("Compilation.zig");
19const Cache = @import("Cache.zig");19const Cache = std.Build.Cache;
20const Value = @import("value.zig").Value;20const Value = @import("value.zig").Value;
21const Type = @import("type.zig").Type;21const Type = @import("type.zig").Type;
22const TypedValue = @import("TypedValue.zig");22const TypedValue = @import("TypedValue.zig");
...@@ -4865,14 +4865,31 @@ pub fn importFile(...@@ -4865,14 +4865,31 @@ pub fn importFile(
4865 };4865 };
4866}4866}
48674867
4868pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*EmbedFile {4868pub fn embedFile(mod: *Module, cur_file: *File, import_string: []const u8) !*EmbedFile {
4869 const gpa = mod.gpa;4869 const gpa = mod.gpa;
48704870
4871 // The resolved path is used as the key in the table, to detect if4871 if (cur_file.pkg.table.get(import_string)) |pkg| {
4872 // a file refers to the same as another, despite different relative paths.4872 const resolved_path = try std.fs.path.resolve(gpa, &[_][]const u8{
4873 pkg.root_src_directory.path orelse ".", pkg.root_src_path,
4874 });
4875 var keep_resolved_path = false;
4876 defer if (!keep_resolved_path) gpa.free(resolved_path);
4877
4878 const gop = try mod.embed_table.getOrPut(gpa, resolved_path);
4879 errdefer assert(mod.embed_table.remove(resolved_path));
4880 if (gop.found_existing) return gop.value_ptr.*;
4881
4882 const sub_file_path = try gpa.dupe(u8, pkg.root_src_path);
4883 errdefer gpa.free(sub_file_path);
4884
4885 return newEmbedFile(mod, pkg, sub_file_path, resolved_path, &keep_resolved_path, gop);
4886 }
4887
4888 // The resolved path is used as the key in the table, to detect if a file
4889 // refers to the same as another, despite different relative paths.
4873 const cur_pkg_dir_path = cur_file.pkg.root_src_directory.path orelse ".";4890 const cur_pkg_dir_path = cur_file.pkg.root_src_directory.path orelse ".";
4874 const resolved_path = try std.fs.path.resolve(gpa, &[_][]const u8{4891 const resolved_path = try std.fs.path.resolve(gpa, &[_][]const u8{
4875 cur_pkg_dir_path, cur_file.sub_file_path, "..", rel_file_path,4892 cur_pkg_dir_path, cur_file.sub_file_path, "..", import_string,
4876 });4893 });
4877 var keep_resolved_path = false;4894 var keep_resolved_path = false;
4878 defer if (!keep_resolved_path) gpa.free(resolved_path);4895 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...@@ -4881,9 +4898,6 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb
4881 errdefer assert(mod.embed_table.remove(resolved_path));4898 errdefer assert(mod.embed_table.remove(resolved_path));
4882 if (gop.found_existing) return gop.value_ptr.*;4899 if (gop.found_existing) return gop.value_ptr.*;
48834900
4884 const new_file = try gpa.create(EmbedFile);
4885 errdefer gpa.destroy(new_file);
4886
4887 const resolved_root_path = try std.fs.path.resolve(gpa, &[_][]const u8{cur_pkg_dir_path});4901 const resolved_root_path = try std.fs.path.resolve(gpa, &[_][]const u8{cur_pkg_dir_path});
4888 defer gpa.free(resolved_root_path);4902 defer gpa.free(resolved_root_path);
48894903
...@@ -4902,7 +4916,23 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb...@@ -4902,7 +4916,23 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb
4902 };4916 };
4903 errdefer gpa.free(sub_file_path);4917 errdefer gpa.free(sub_file_path);
49044918
4905 var file = try cur_file.pkg.root_src_directory.handle.openFile(sub_file_path, .{});4919 return newEmbedFile(mod, cur_file.pkg, sub_file_path, resolved_path, &keep_resolved_path, gop);
4920}
4921
4922fn newEmbedFile(
4923 mod: *Module,
4924 pkg: *Package,
4925 sub_file_path: []const u8,
4926 resolved_path: []const u8,
4927 keep_resolved_path: *bool,
4928 gop: std.StringHashMapUnmanaged(*EmbedFile).GetOrPutResult,
4929) !*EmbedFile {
4930 const gpa = mod.gpa;
4931
4932 const new_file = try gpa.create(EmbedFile);
4933 errdefer gpa.destroy(new_file);
4934
4935 var file = try pkg.root_src_directory.handle.openFile(sub_file_path, .{});
4906 defer file.close();4936 defer file.close();
49074937
4908 const actual_stat = try file.stat();4938 const actual_stat = try file.stat();
...@@ -4915,10 +4945,6 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb...@@ -4915,10 +4945,6 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb
4915 const bytes = try file.readToEndAllocOptions(gpa, std.math.maxInt(u32), size_usize, 1, 0);4945 const bytes = try file.readToEndAllocOptions(gpa, std.math.maxInt(u32), size_usize, 1, 0);
4916 errdefer gpa.free(bytes);4946 errdefer gpa.free(bytes);
49174947
4918 log.debug("new embedFile. resolved_root_path={s}, resolved_path={s}, sub_file_path={s}, rel_file_path={s}", .{
4919 resolved_root_path, resolved_path, sub_file_path, rel_file_path,
4920 });
4921
4922 if (mod.comp.whole_cache_manifest) |whole_cache_manifest| {4948 if (mod.comp.whole_cache_manifest) |whole_cache_manifest| {
4923 const copied_resolved_path = try gpa.dupe(u8, resolved_path);4949 const copied_resolved_path = try gpa.dupe(u8, resolved_path);
4924 errdefer gpa.free(copied_resolved_path);4950 errdefer gpa.free(copied_resolved_path);
...@@ -4927,13 +4953,13 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb...@@ -4927,13 +4953,13 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb
4927 try whole_cache_manifest.addFilePostContents(copied_resolved_path, bytes, stat);4953 try whole_cache_manifest.addFilePostContents(copied_resolved_path, bytes, stat);
4928 }4954 }
49294955
4930 keep_resolved_path = true; // It's now owned by embed_table.4956 keep_resolved_path.* = true; // It's now owned by embed_table.
4931 gop.value_ptr.* = new_file;4957 gop.value_ptr.* = new_file;
4932 new_file.* = .{4958 new_file.* = .{
4933 .sub_file_path = sub_file_path,4959 .sub_file_path = sub_file_path,
4934 .bytes = bytes,4960 .bytes = bytes,
4935 .stat = stat,4961 .stat = stat,
4936 .pkg = cur_file.pkg,4962 .pkg = pkg,
4937 .owner_decl = undefined, // Set by Sema immediately after this function returns.4963 .owner_decl = undefined, // Set by Sema immediately after this function returns.
4938 };4964 };
4939 return new_file;4965 return new_file;
src/Package.zig+1-1
...@@ -13,7 +13,7 @@ const Compilation = @import("Compilation.zig");...@@ -13,7 +13,7 @@ const Compilation = @import("Compilation.zig");
13const Module = @import("Module.zig");13const Module = @import("Module.zig");
14const ThreadPool = @import("ThreadPool.zig");14const ThreadPool = @import("ThreadPool.zig");
15const WaitGroup = @import("WaitGroup.zig");15const WaitGroup = @import("WaitGroup.zig");
16const Cache = @import("Cache.zig");16const Cache = std.Build.Cache;
17const build_options = @import("build_options");17const build_options = @import("build_options");
18const Manifest = @import("Manifest.zig");18const Manifest = @import("Manifest.zig");
1919
src/Sema.zig+66-44
...@@ -1015,7 +1015,6 @@ fn analyzeBodyInner(...@@ -1015,7 +1015,6 @@ fn analyzeBodyInner(
1015 .float_cast => try sema.zirFloatCast(block, inst),1015 .float_cast => try sema.zirFloatCast(block, inst),
1016 .int_cast => try sema.zirIntCast(block, inst),1016 .int_cast => try sema.zirIntCast(block, inst),
1017 .ptr_cast => try sema.zirPtrCast(block, inst),1017 .ptr_cast => try sema.zirPtrCast(block, inst),
1018 .qual_cast => try sema.zirQualCast(block, inst),
1019 .truncate => try sema.zirTruncate(block, inst),1018 .truncate => try sema.zirTruncate(block, inst),
1020 .align_cast => try sema.zirAlignCast(block, inst),1019 .align_cast => try sema.zirAlignCast(block, inst),
1021 .has_decl => try sema.zirHasDecl(block, inst),1020 .has_decl => try sema.zirHasDecl(block, inst),
...@@ -1147,6 +1146,8 @@ fn analyzeBodyInner(...@@ -1147,6 +1146,8 @@ fn analyzeBodyInner(
1147 .c_va_copy => try sema.zirCVaCopy( block, extended),1146 .c_va_copy => try sema.zirCVaCopy( block, extended),
1148 .c_va_end => try sema.zirCVaEnd( block, extended),1147 .c_va_end => try sema.zirCVaEnd( block, extended),
1149 .c_va_start => try sema.zirCVaStart( block, extended),1148 .c_va_start => try sema.zirCVaStart( block, extended),
1149 .const_cast, => try sema.zirConstCast( block, extended),
1150 .volatile_cast, => try sema.zirVolatileCast( block, extended),
1150 // zig fmt: on1151 // zig fmt: on
11511152
1152 .fence => {1153 .fence => {
...@@ -7669,17 +7670,21 @@ fn zirErrorUnionType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr...@@ -7669,17 +7670,21 @@ fn zirErrorUnionType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
7669 error_set.fmt(sema.mod),7670 error_set.fmt(sema.mod),
7670 });7671 });
7671 }7672 }
7672 if (payload.zigTypeTag() == .Opaque) {7673 try sema.validateErrorUnionPayloadType(block, payload, rhs_src);
7673 return sema.fail(block, rhs_src, "error union with payload of opaque type '{}' not allowed", .{7674 const err_union_ty = try Type.errorUnion(sema.arena, error_set, payload, sema.mod);
7674 payload.fmt(sema.mod),7675 return sema.addType(err_union_ty);
7676}
7677
7678fn validateErrorUnionPayloadType(sema: *Sema, block: *Block, payload_ty: Type, payload_src: LazySrcLoc) !void {
7679 if (payload_ty.zigTypeTag() == .Opaque) {
7680 return sema.fail(block, payload_src, "error union with payload of opaque type '{}' not allowed", .{
7681 payload_ty.fmt(sema.mod),
7675 });7682 });
7676 } else if (payload.zigTypeTag() == .ErrorSet) {7683 } else if (payload_ty.zigTypeTag() == .ErrorSet) {
7677 return sema.fail(block, rhs_src, "error union with payload of error set type '{}' not allowed", .{7684 return sema.fail(block, payload_src, "error union with payload of error set type '{}' not allowed", .{
7678 payload.fmt(sema.mod),7685 payload_ty.fmt(sema.mod),
7679 });7686 });
7680 }7687 }
7681 const err_union_ty = try Type.errorUnion(sema.arena, error_set, payload, sema.mod);
7682 return sema.addType(err_union_ty);
7683}7688}
76847689
7685fn zirErrorValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {7690fn zirErrorValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
...@@ -8639,6 +8644,7 @@ fn funcCommon(...@@ -8639,6 +8644,7 @@ fn funcCommon(
8639 const return_type = if (!inferred_error_set or ret_poison)8644 const return_type = if (!inferred_error_set or ret_poison)
8640 bare_return_type8645 bare_return_type
8641 else blk: {8646 else blk: {
8647 try sema.validateErrorUnionPayloadType(block, bare_return_type, ret_ty_src);
8642 const node = try sema.gpa.create(Module.Fn.InferredErrorSetListNode);8648 const node = try sema.gpa.create(Module.Fn.InferredErrorSetListNode);
8643 node.data = .{ .func = new_func };8649 node.data = .{ .func = new_func };
8644 maybe_inferred_error_set_node = node;8650 maybe_inferred_error_set_node = node;
...@@ -8650,15 +8656,15 @@ fn funcCommon(...@@ -8650,15 +8656,15 @@ fn funcCommon(
8650 });8656 });
8651 };8657 };
86528658
8653 if (!bare_return_type.isValidReturnType()) {8659 if (!return_type.isValidReturnType()) {
8654 const opaque_str = if (bare_return_type.zigTypeTag() == .Opaque) "opaque " else "";8660 const opaque_str = if (return_type.zigTypeTag() == .Opaque) "opaque " else "";
8655 const msg = msg: {8661 const msg = msg: {
8656 const msg = try sema.errMsg(block, ret_ty_src, "{s}return type '{}' not allowed", .{8662 const msg = try sema.errMsg(block, ret_ty_src, "{s}return type '{}' not allowed", .{
8657 opaque_str, bare_return_type.fmt(sema.mod),8663 opaque_str, return_type.fmt(sema.mod),
8658 });8664 });
8659 errdefer msg.destroy(sema.gpa);8665 errdefer msg.destroy(sema.gpa);
86608666
8661 try sema.addDeclaredHereNote(msg, bare_return_type);8667 try sema.addDeclaredHereNote(msg, return_type);
8662 break :msg msg;8668 break :msg msg;
8663 };8669 };
8664 return sema.failWithOwnedErrorMsg(msg);8670 return sema.failWithOwnedErrorMsg(msg);
...@@ -19540,7 +19546,7 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -19540,7 +19546,7 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
19540 const msg = try sema.errMsg(block, src, "cast discards const qualifier", .{});19546 const msg = try sema.errMsg(block, src, "cast discards const qualifier", .{});
19541 errdefer msg.destroy(sema.gpa);19547 errdefer msg.destroy(sema.gpa);
1954219548
19543 try sema.errNote(block, src, msg, "consider using '@qualCast'", .{});19549 try sema.errNote(block, src, msg, "consider using '@constCast'", .{});
19544 break :msg msg;19550 break :msg msg;
19545 };19551 };
19546 return sema.failWithOwnedErrorMsg(msg);19552 return sema.failWithOwnedErrorMsg(msg);
...@@ -19550,7 +19556,7 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -19550,7 +19556,7 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
19550 const msg = try sema.errMsg(block, src, "cast discards volatile qualifier", .{});19556 const msg = try sema.errMsg(block, src, "cast discards volatile qualifier", .{});
19551 errdefer msg.destroy(sema.gpa);19557 errdefer msg.destroy(sema.gpa);
1955219558
19553 try sema.errNote(block, src, msg, "consider using '@qualCast'", .{});19559 try sema.errNote(block, src, msg, "consider using '@volatileCast'", .{});
19554 break :msg msg;19560 break :msg msg;
19555 };19561 };
19556 return sema.failWithOwnedErrorMsg(msg);19562 return sema.failWithOwnedErrorMsg(msg);
...@@ -19655,40 +19661,37 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -19655,40 +19661,37 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
19655 return block.addBitCast(aligned_dest_ty, ptr);19661 return block.addBitCast(aligned_dest_ty, ptr);
19656}19662}
1965719663
19658fn zirQualCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {19664fn zirConstCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
19659 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;19665 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
19660 const src = inst_data.src();19666 const src = LazySrcLoc.nodeOffset(extra.node);
19661 const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };19667 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
19662 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };19668 const operand = try sema.resolveInst(extra.operand);
19663 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
19664 const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs);
19665 const operand = try sema.resolveInst(extra.rhs);
19666 const operand_ty = sema.typeOf(operand);19669 const operand_ty = sema.typeOf(operand);
19667
19668 try sema.checkPtrType(block, dest_ty_src, dest_ty);
19669 try sema.checkPtrOperand(block, operand_src, operand_ty);19670 try sema.checkPtrOperand(block, operand_src, operand_ty);
1967019671
19671 var operand_payload = operand_ty.ptrInfo();19672 var ptr_info = operand_ty.ptrInfo().data;
19672 var dest_info = dest_ty.ptrInfo();19673 ptr_info.mutable = true;
19674 const dest_ty = try Type.ptr(sema.arena, sema.mod, ptr_info);
1967319675
19674 operand_payload.data.mutable = dest_info.data.mutable;19676 if (try sema.resolveMaybeUndefVal(operand)) |operand_val| {
19675 operand_payload.data.@"volatile" = dest_info.data.@"volatile";19677 return sema.addConstant(dest_ty, operand_val);
19678 }
1967619679
19677 const altered_operand_ty = Type.initPayload(&operand_payload.base);19680 try sema.requireRuntimeBlock(block, src, null);
19678 if (!altered_operand_ty.eql(dest_ty, sema.mod)) {19681 return block.addBitCast(dest_ty, operand);
19679 const msg = msg: {19682}
19680 const msg = try sema.errMsg(block, src, "'@qualCast' can only modify 'const' and 'volatile' qualifiers", .{});
19681 errdefer msg.destroy(sema.gpa);
1968219683
19683 dest_info.data.mutable = !operand_ty.isConstPtr();19684fn zirVolatileCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
19684 dest_info.data.@"volatile" = operand_ty.isVolatilePtr();19685 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
19685 const altered_dest_ty = Type.initPayload(&dest_info.base);19686 const src = LazySrcLoc.nodeOffset(extra.node);
19686 try sema.errNote(block, src, msg, "expected type '{}'", .{altered_dest_ty.fmt(sema.mod)});19687 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
19687 try sema.errNote(block, src, msg, "got type '{}'", .{operand_ty.fmt(sema.mod)});19688 const operand = try sema.resolveInst(extra.operand);
19688 break :msg msg;19689 const operand_ty = sema.typeOf(operand);
19689 };19690 try sema.checkPtrOperand(block, operand_src, operand_ty);
19690 return sema.failWithOwnedErrorMsg(msg);19691
19691 }19692 var ptr_info = operand_ty.ptrInfo().data;
19693 ptr_info.@"volatile" = false;
19694 const dest_ty = try Type.ptr(sema.arena, sema.mod, ptr_info);
1969219695
19693 if (try sema.resolveMaybeUndefVal(operand)) |operand_val| {19696 if (try sema.resolveMaybeUndefVal(operand)) |operand_val| {
19694 return sema.addConstant(dest_ty, operand_val);19697 return sema.addConstant(dest_ty, operand_val);
...@@ -21930,7 +21933,7 @@ fn zirCUndef(...@@ -21930,7 +21933,7 @@ fn zirCUndef(
21930 const src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };21933 const src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
2193121934
21932 const name = try sema.resolveConstString(block, src, extra.operand, "name of macro being undefined must be comptime-known");21935 const name = try sema.resolveConstString(block, src, extra.operand, "name of macro being undefined must be comptime-known");
21933 try block.c_import_buf.?.writer().print("#undefine {s}\n", .{name});21936 try block.c_import_buf.?.writer().print("#undef {s}\n", .{name});
21934 return Air.Inst.Ref.void_value;21937 return Air.Inst.Ref.void_value;
21935}21938}
2193621939
...@@ -29756,6 +29759,25 @@ fn resolvePeerTypes(...@@ -29756,6 +29759,25 @@ fn resolvePeerTypes(
29756 continue;29759 continue;
29757 }29760 }
29758 },29761 },
29762 .ErrorSet => {
29763 chosen = candidate;
29764 chosen_i = candidate_i + 1;
29765 if (err_set_ty) |chosen_set_ty| {
29766 if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_set_ty, chosen_ty, src, src)) {
29767 continue;
29768 }
29769 if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_ty, chosen_set_ty, src, src)) {
29770 err_set_ty = chosen_ty;
29771 continue;
29772 }
29773
29774 err_set_ty = try chosen_set_ty.errorSetMerge(sema.arena, chosen_ty);
29775 continue;
29776 } else {
29777 err_set_ty = chosen_ty;
29778 continue;
29779 }
29780 },
29759 else => {},29781 else => {},
29760 }29782 }
2976129783
src/Zir.zig+6-6
...@@ -857,9 +857,6 @@ pub const Inst = struct {...@@ -857,9 +857,6 @@ pub const Inst = struct {
857 /// Implements the `@ptrCast` builtin.857 /// Implements the `@ptrCast` builtin.
858 /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.858 /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
859 ptr_cast,859 ptr_cast,
860 /// Implements the `@qualCast` builtin.
861 /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
862 qual_cast,
863 /// Implements the `@truncate` builtin.860 /// Implements the `@truncate` builtin.
864 /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.861 /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
865 truncate,862 truncate,
...@@ -1198,7 +1195,6 @@ pub const Inst = struct {...@@ -1198,7 +1195,6 @@ pub const Inst = struct {
1198 .float_cast,1195 .float_cast,
1199 .int_cast,1196 .int_cast,
1200 .ptr_cast,1197 .ptr_cast,
1201 .qual_cast,
1202 .truncate,1198 .truncate,
1203 .align_cast,1199 .align_cast,
1204 .has_field,1200 .has_field,
...@@ -1488,7 +1484,6 @@ pub const Inst = struct {...@@ -1488,7 +1484,6 @@ pub const Inst = struct {
1488 .float_cast,1484 .float_cast,
1489 .int_cast,1485 .int_cast,
1490 .ptr_cast,1486 .ptr_cast,
1491 .qual_cast,
1492 .truncate,1487 .truncate,
1493 .align_cast,1488 .align_cast,
1494 .has_field,1489 .has_field,
...@@ -1760,7 +1755,6 @@ pub const Inst = struct {...@@ -1760,7 +1755,6 @@ pub const Inst = struct {
1760 .float_cast = .pl_node,1755 .float_cast = .pl_node,
1761 .int_cast = .pl_node,1756 .int_cast = .pl_node,
1762 .ptr_cast = .pl_node,1757 .ptr_cast = .pl_node,
1763 .qual_cast = .pl_node,
1764 .truncate = .pl_node,1758 .truncate = .pl_node,
1765 .align_cast = .pl_node,1759 .align_cast = .pl_node,
1766 .typeof_builtin = .pl_node,1760 .typeof_builtin = .pl_node,
...@@ -2004,6 +1998,12 @@ pub const Inst = struct {...@@ -2004,6 +1998,12 @@ pub const Inst = struct {
2004 /// Implement builtin `@cVaStart`.1998 /// Implement builtin `@cVaStart`.
2005 /// `operand` is `src_node: i32`.1999 /// `operand` is `src_node: i32`.
2006 c_va_start,2000 c_va_start,
2001 /// Implements the `@constCast` builtin.
2002 /// `operand` is payload index to `UnNode`.
2003 const_cast,
2004 /// Implements the `@volatileCast` builtin.
2005 /// `operand` is payload index to `UnNode`.
2006 volatile_cast,
20072007
2008 pub const InstData = struct {2008 pub const InstData = struct {
2009 opcode: Extended,2009 opcode: Extended,
src/codegen/llvm.zig+7-1
...@@ -2278,7 +2278,9 @@ pub const Object = struct {...@@ -2278,7 +2278,9 @@ pub const Object = struct {
22782278
2279 const full_di_fields: [2]*llvm.DIType =2279 const full_di_fields: [2]*llvm.DIType =
2280 if (layout.tag_align >= layout.payload_align)2280 if (layout.tag_align >= layout.payload_align)
2281 .{ tag_di, payload_di } else .{ payload_di, tag_di };2281 .{ tag_di, payload_di }
2282 else
2283 .{ payload_di, tag_di };
22822284
2283 const full_di_ty = dib.createStructType(2285 const full_di_ty = dib.createStructType(
2284 compile_unit_scope,2286 compile_unit_scope,
...@@ -4167,6 +4169,10 @@ pub const DeclGen = struct {...@@ -4167,6 +4169,10 @@ pub const DeclGen = struct {
4167 if (func.data.owner_decl != decl_index) {4169 if (func.data.owner_decl != decl_index) {
4168 return self.lowerDeclRefValue(tv, func.data.owner_decl);4170 return self.lowerDeclRefValue(tv, func.data.owner_decl);
4169 }4171 }
4172 } else if (decl.val.castTag(.extern_fn)) |func| {
4173 if (func.data.owner_decl != decl_index) {
4174 return self.lowerDeclRefValue(tv, func.data.owner_decl);
4175 }
4170 }4176 }
41714177
4172 const is_fn_body = decl.ty.zigTypeTag() == .Fn;4178 const is_fn_body = decl.ty.zigTypeTag() == .Fn;
src/glibc.zig+1-1
...@@ -11,7 +11,7 @@ const target_util = @import("target.zig");...@@ -11,7 +11,7 @@ const target_util = @import("target.zig");
11const Compilation = @import("Compilation.zig");11const Compilation = @import("Compilation.zig");
12const build_options = @import("build_options");12const build_options = @import("build_options");
13const trace = @import("tracy.zig").trace;13const trace = @import("tracy.zig").trace;
14const Cache = @import("Cache.zig");14const Cache = std.Build.Cache;
15const Package = @import("Package.zig");15const Package = @import("Package.zig");
1616
17pub const Lib = struct {17pub const Lib = struct {
src/link.zig+4-1
...@@ -10,7 +10,7 @@ const wasi_libc = @import("wasi_libc.zig");...@@ -10,7 +10,7 @@ const wasi_libc = @import("wasi_libc.zig");
1010
11const Air = @import("Air.zig");11const Air = @import("Air.zig");
12const Allocator = std.mem.Allocator;12const Allocator = std.mem.Allocator;
13const Cache = @import("Cache.zig");13const Cache = std.Build.Cache;
14const Compilation = @import("Compilation.zig");14const Compilation = @import("Compilation.zig");
15const LibCInstallation = @import("libc_installation.zig").LibCInstallation;15const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
16const Liveness = @import("Liveness.zig");16const Liveness = @import("Liveness.zig");
...@@ -25,6 +25,8 @@ pub const SystemLib = struct {...@@ -25,6 +25,8 @@ pub const SystemLib = struct {
25 weak: bool = false,25 weak: bool = false,
26};26};
2727
28pub const SortSection = enum { name, alignment };
29
28pub const CacheMode = enum { incremental, whole };30pub const CacheMode = enum { incremental, whole };
2931
30pub fn hashAddSystemLibs(32pub fn hashAddSystemLibs(
...@@ -159,6 +161,7 @@ pub const Options = struct {...@@ -159,6 +161,7 @@ pub const Options = struct {
159 disable_lld_caching: bool,161 disable_lld_caching: bool,
160 is_test: bool,162 is_test: bool,
161 hash_style: HashStyle,163 hash_style: HashStyle,
164 sort_section: ?SortSection,
162 major_subsystem_version: ?u32,165 major_subsystem_version: ?u32,
163 minor_subsystem_version: ?u32,166 minor_subsystem_version: ?u32,
164 gc_sections: ?bool = null,167 gc_sections: ?bool = null,
src/link/Coff/lld.zig+1-1
...@@ -5,6 +5,7 @@ const assert = std.debug.assert;...@@ -5,6 +5,7 @@ const assert = std.debug.assert;
5const fs = std.fs;5const fs = std.fs;
6const log = std.log.scoped(.link);6const log = std.log.scoped(.link);
7const mem = std.mem;7const mem = std.mem;
8const Cache = std.Build.Cache;
89
9const mingw = @import("../../mingw.zig");10const mingw = @import("../../mingw.zig");
10const link = @import("../../link.zig");11const link = @import("../../link.zig");
...@@ -13,7 +14,6 @@ const trace = @import("../../tracy.zig").trace;...@@ -13,7 +14,6 @@ const trace = @import("../../tracy.zig").trace;
1314
14const Allocator = mem.Allocator;15const Allocator = mem.Allocator;
1516
16const Cache = @import("../../Cache.zig");
17const Coff = @import("../Coff.zig");17const Coff = @import("../Coff.zig");
18const Compilation = @import("../../Compilation.zig");18const Compilation = @import("../../Compilation.zig");
1919
src/link/Elf.zig+7-1
...@@ -21,7 +21,7 @@ const trace = @import("../tracy.zig").trace;...@@ -21,7 +21,7 @@ const trace = @import("../tracy.zig").trace;
21const Air = @import("../Air.zig");21const Air = @import("../Air.zig");
22const Allocator = std.mem.Allocator;22const Allocator = std.mem.Allocator;
23pub const Atom = @import("Elf/Atom.zig");23pub const Atom = @import("Elf/Atom.zig");
24const Cache = @import("../Cache.zig");24const Cache = std.Build.Cache;
25const Compilation = @import("../Compilation.zig");25const Compilation = @import("../Compilation.zig");
26const Dwarf = @import("Dwarf.zig");26const Dwarf = @import("Dwarf.zig");
27const File = link.File;27const File = link.File;
...@@ -1324,6 +1324,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v...@@ -1324,6 +1324,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
1324 man.hash.addOptionalBytes(self.base.options.entry);1324 man.hash.addOptionalBytes(self.base.options.entry);
1325 man.hash.addOptional(self.base.options.image_base_override);1325 man.hash.addOptional(self.base.options.image_base_override);
1326 man.hash.add(gc_sections);1326 man.hash.add(gc_sections);
1327 man.hash.addOptional(self.base.options.sort_section);
1327 man.hash.add(self.base.options.eh_frame_hdr);1328 man.hash.add(self.base.options.eh_frame_hdr);
1328 man.hash.add(self.base.options.emit_relocs);1329 man.hash.add(self.base.options.emit_relocs);
1329 man.hash.add(self.base.options.rdynamic);1330 man.hash.add(self.base.options.rdynamic);
...@@ -1488,6 +1489,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v...@@ -1488,6 +1489,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
1488 try argv.append(linker_script);1489 try argv.append(linker_script);
1489 }1490 }
14901491
1492 if (self.base.options.sort_section) |how| {
1493 const arg = try std.fmt.allocPrint(arena, "--sort-section={s}", .{@tagName(how)});
1494 try argv.append(arg);
1495 }
1496
1491 if (gc_sections) {1497 if (gc_sections) {
1492 try argv.append("--gc-sections");1498 try argv.append("--gc-sections");
1493 }1499 }
src/link/MachO.zig+1-1
...@@ -28,7 +28,7 @@ const Air = @import("../Air.zig");...@@ -28,7 +28,7 @@ const Air = @import("../Air.zig");
28const Allocator = mem.Allocator;28const Allocator = mem.Allocator;
29const Archive = @import("MachO/Archive.zig");29const Archive = @import("MachO/Archive.zig");
30pub const Atom = @import("MachO/Atom.zig");30pub const Atom = @import("MachO/Atom.zig");
31const Cache = @import("../Cache.zig");31const Cache = std.Build.Cache;
32const CodeSignature = @import("MachO/CodeSignature.zig");32const CodeSignature = @import("MachO/CodeSignature.zig");
33const Compilation = @import("../Compilation.zig");33const Compilation = @import("../Compilation.zig");
34const Dwarf = File.Dwarf;34const Dwarf = File.Dwarf;
src/link/MachO/Dylib.zig+2
...@@ -166,6 +166,8 @@ pub fn parseFromBinary(...@@ -166,6 +166,8 @@ pub fn parseFromBinary(
166 const symtab_cmd = cmd.cast(macho.symtab_command).?;166 const symtab_cmd = cmd.cast(macho.symtab_command).?;
167 const symtab = @ptrCast(167 const symtab = @ptrCast(
168 [*]const macho.nlist_64,168 [*]const macho.nlist_64,
169 // Alignment is guaranteed as a dylib is a final linked image and has to have sections
170 // properly aligned in order to be correctly loaded by the loader.
169 @alignCast(@alignOf(macho.nlist_64), &data[symtab_cmd.symoff]),171 @alignCast(@alignOf(macho.nlist_64), &data[symtab_cmd.symoff]),
170 )[0..symtab_cmd.nsyms];172 )[0..symtab_cmd.nsyms];
171 const strtab = data[symtab_cmd.stroff..][0..symtab_cmd.strsize];173 const strtab = data[symtab_cmd.stroff..][0..symtab_cmd.strsize];
src/link/MachO/Object.zig+103-52
...@@ -60,14 +60,24 @@ globals_lookup: []i64 = undefined,...@@ -60,14 +60,24 @@ globals_lookup: []i64 = undefined,
60/// Can be undefined as set together with in_symtab.60/// Can be undefined as set together with in_symtab.
61relocs_lookup: []RelocEntry = undefined,61relocs_lookup: []RelocEntry = undefined,
6262
63/// All relocations sorted and flatened, sorted by address descending
64/// per section.
65relocations: std.ArrayListUnmanaged(macho.relocation_info) = .{},
66/// Beginning index to the relocations array for each input section
67/// defined within this Object file.
68section_relocs_lookup: std.ArrayListUnmanaged(u32) = .{},
69
70/// Data-in-code records sorted by address.
71data_in_code: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},
72
63atoms: std.ArrayListUnmanaged(AtomIndex) = .{},73atoms: std.ArrayListUnmanaged(AtomIndex) = .{},
64exec_atoms: std.ArrayListUnmanaged(AtomIndex) = .{},74exec_atoms: std.ArrayListUnmanaged(AtomIndex) = .{},
6575
66eh_frame_sect: ?macho.section_64 = null,76eh_frame_sect_id: ?u8 = null,
67eh_frame_relocs_lookup: std.AutoArrayHashMapUnmanaged(u32, Record) = .{},77eh_frame_relocs_lookup: std.AutoArrayHashMapUnmanaged(u32, Record) = .{},
68eh_frame_records_lookup: std.AutoArrayHashMapUnmanaged(AtomIndex, u32) = .{},78eh_frame_records_lookup: std.AutoArrayHashMapUnmanaged(AtomIndex, u32) = .{},
6979
70unwind_info_sect: ?macho.section_64 = null,80unwind_info_sect_id: ?u8 = null,
71unwind_relocs_lookup: []Record = undefined,81unwind_relocs_lookup: []Record = undefined,
72unwind_records_lookup: std.AutoHashMapUnmanaged(AtomIndex, u32) = .{},82unwind_records_lookup: std.AutoHashMapUnmanaged(AtomIndex, u32) = .{},
7383
...@@ -100,6 +110,9 @@ pub fn deinit(self: *Object, gpa: Allocator) void {...@@ -100,6 +110,9 @@ pub fn deinit(self: *Object, gpa: Allocator) void {
100 gpa.free(self.unwind_relocs_lookup);110 gpa.free(self.unwind_relocs_lookup);
101 }111 }
102 self.unwind_records_lookup.deinit(gpa);112 self.unwind_records_lookup.deinit(gpa);
113 self.relocations.deinit(gpa);
114 self.section_relocs_lookup.deinit(gpa);
115 self.data_in_code.deinit(gpa);
103}116}
104117
105pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch) !void {118pub 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)...@@ -137,15 +150,18 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch)
137 .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],150 .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],
138 };151 };
139 const nsects = self.getSourceSections().len;152 const nsects = self.getSourceSections().len;
153
154 // Prepopulate relocations per section lookup table.
155 try self.section_relocs_lookup.resize(allocator, nsects);
156 mem.set(u32, self.section_relocs_lookup.items, 0);
157
158 // Parse symtab.
140 const symtab = while (it.next()) |cmd| switch (cmd.cmd()) {159 const symtab = while (it.next()) |cmd| switch (cmd.cmd()) {
141 .SYMTAB => break cmd.cast(macho.symtab_command).?,160 .SYMTAB => break cmd.cast(macho.symtab_command).?,
142 else => {},161 else => {},
143 } else return;162 } else return;
144163
145 self.in_symtab = @ptrCast(164 self.in_symtab = @ptrCast([*]align(1) const macho.nlist_64, self.contents.ptr + symtab.symoff)[0..symtab.nsyms];
146 [*]const macho.nlist_64,
147 @alignCast(@alignOf(macho.nlist_64), &self.contents[symtab.symoff]),
148 )[0..symtab.nsyms];
149 self.in_strtab = self.contents[symtab.stroff..][0..symtab.strsize];165 self.in_strtab = self.contents[symtab.stroff..][0..symtab.strsize];
150166
151 self.symtab = try allocator.alloc(macho.nlist_64, self.in_symtab.?.len + nsects);167 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)...@@ -212,10 +228,10 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch)
212 }228 }
213229
214 // Parse __TEXT,__eh_frame header if one exists230 // Parse __TEXT,__eh_frame header if one exists
215 self.eh_frame_sect = self.getSourceSectionByName("__TEXT", "__eh_frame");231 self.eh_frame_sect_id = self.getSourceSectionIndexByName("__TEXT", "__eh_frame");
216232
217 // Parse __LD,__compact_unwind header if one exists233 // Parse __LD,__compact_unwind header if one exists
218 self.unwind_info_sect = self.getSourceSectionByName("__LD", "__compact_unwind");234 self.unwind_info_sect_id = self.getSourceSectionIndexByName("__LD", "__compact_unwind");
219 if (self.hasUnwindRecords()) {235 if (self.hasUnwindRecords()) {
220 self.unwind_relocs_lookup = try allocator.alloc(Record, self.getUnwindRecords().len);236 self.unwind_relocs_lookup = try allocator.alloc(Record, self.getUnwindRecords().len);
221 mem.set(Record, self.unwind_relocs_lookup, .{237 mem.set(Record, self.unwind_relocs_lookup, .{
...@@ -354,6 +370,7 @@ pub fn splitIntoAtoms(self: *Object, zld: *Zld, object_id: u32) !void {...@@ -354,6 +370,7 @@ pub fn splitIntoAtoms(self: *Object, zld: *Zld, object_id: u32) !void {
354 try self.splitRegularSections(zld, object_id);370 try self.splitRegularSections(zld, object_id);
355 try self.parseEhFrameSection(zld, object_id);371 try self.parseEhFrameSection(zld, object_id);
356 try self.parseUnwindInfo(zld, object_id);372 try self.parseUnwindInfo(zld, object_id);
373 try self.parseDataInCode(zld.gpa);
357}374}
358375
359/// Splits input regular sections into Atoms.376/// Splits input regular sections into Atoms.
...@@ -452,6 +469,8 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {...@@ -452,6 +469,8 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
452 zld.sections.items(.header)[out_sect_id].sectName(),469 zld.sections.items(.header)[out_sect_id].sectName(),
453 });470 });
454471
472 try self.parseRelocs(gpa, section.id);
473
455 const cpu_arch = zld.options.target.cpu.arch;474 const cpu_arch = zld.options.target.cpu.arch;
456 const sect_loc = filterSymbolsBySection(symtab[sect_sym_index..], sect_id + 1);475 const sect_loc = filterSymbolsBySection(symtab[sect_sym_index..], sect_id + 1);
457 const sect_start_index = sect_sym_index + sect_loc.index;476 const sect_start_index = sect_sym_index + sect_loc.index;
...@@ -623,25 +642,36 @@ fn filterRelocs(...@@ -623,25 +642,36 @@ fn filterRelocs(
623 return .{ .start = @intCast(u32, start), .len = @intCast(u32, len) };642 return .{ .start = @intCast(u32, start), .len = @intCast(u32, len) };
624}643}
625644
645/// Parse all relocs for the input section, and sort in descending order.
646/// Previously, I have wrongly assumed the compilers output relocations for each
647/// section in a sorted manner which is simply not true.
648fn parseRelocs(self: *Object, gpa: Allocator, sect_id: u8) !void {
649 const section = self.getSourceSection(sect_id);
650 const start = @intCast(u32, self.relocations.items.len);
651 if (self.getSourceRelocs(section)) |relocs| {
652 try self.relocations.ensureUnusedCapacity(gpa, relocs.len);
653 self.relocations.appendUnalignedSliceAssumeCapacity(relocs);
654 std.sort.sort(macho.relocation_info, self.relocations.items[start..], {}, relocGreaterThan);
655 }
656 self.section_relocs_lookup.items[sect_id] = start;
657}
658
626fn cacheRelocs(self: *Object, zld: *Zld, atom_index: AtomIndex) !void {659fn cacheRelocs(self: *Object, zld: *Zld, atom_index: AtomIndex) !void {
627 const atom = zld.getAtom(atom_index);660 const atom = zld.getAtom(atom_index);
628661
629 const source_sect = if (self.getSourceSymbol(atom.sym_index)) |source_sym| blk: {662 const source_sect_id = if (self.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
630 const source_sect = self.getSourceSection(source_sym.n_sect - 1);663 break :blk source_sym.n_sect - 1;
631 assert(!source_sect.isZerofill());
632 break :blk source_sect;
633 } else blk: {664 } else blk: {
634 // If there was no matching symbol present in the source symtab, this means665 // If there was no matching symbol present in the source symtab, this means
635 // we are dealing with either an entire section, or part of it, but also666 // we are dealing with either an entire section, or part of it, but also
636 // starting at the beginning.667 // starting at the beginning.
637 const nbase = @intCast(u32, self.in_symtab.?.len);668 const nbase = @intCast(u32, self.in_symtab.?.len);
638 const sect_id = @intCast(u16, atom.sym_index - nbase);669 const sect_id = @intCast(u8, atom.sym_index - nbase);
639 const source_sect = self.getSourceSection(sect_id);670 break :blk sect_id;
640 assert(!source_sect.isZerofill());
641 break :blk source_sect;
642 };671 };
643672 const source_sect = self.getSourceSection(source_sect_id);
644 const relocs = self.getRelocs(source_sect);673 assert(!source_sect.isZerofill());
674 const relocs = self.getRelocs(source_sect_id);
645675
646 self.relocs_lookup[atom.sym_index] = if (self.getSourceSymbol(atom.sym_index)) |source_sym| blk: {676 self.relocs_lookup[atom.sym_index] = if (self.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
647 const offset = source_sym.n_value - source_sect.addr;677 const offset = source_sym.n_value - source_sect.addr;
...@@ -649,8 +679,14 @@ fn cacheRelocs(self: *Object, zld: *Zld, atom_index: AtomIndex) !void {...@@ -649,8 +679,14 @@ fn cacheRelocs(self: *Object, zld: *Zld, atom_index: AtomIndex) !void {
649 } else filterRelocs(relocs, 0, atom.size);679 } else filterRelocs(relocs, 0, atom.size);
650}680}
651681
682fn relocGreaterThan(ctx: void, lhs: macho.relocation_info, rhs: macho.relocation_info) bool {
683 _ = ctx;
684 return lhs.r_address > rhs.r_address;
685}
686
652fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {687fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
653 const sect = self.eh_frame_sect orelse return;688 const sect_id = self.eh_frame_sect_id orelse return;
689 const sect = self.getSourceSection(sect_id);
654690
655 log.debug("parsing __TEXT,__eh_frame section", .{});691 log.debug("parsing __TEXT,__eh_frame section", .{});
656692
...@@ -660,7 +696,8 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {...@@ -660,7 +696,8 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
660696
661 const gpa = zld.gpa;697 const gpa = zld.gpa;
662 const cpu_arch = zld.options.target.cpu.arch;698 const cpu_arch = zld.options.target.cpu.arch;
663 const relocs = self.getRelocs(sect);699 try self.parseRelocs(gpa, sect_id);
700 const relocs = self.getRelocs(sect_id);
664701
665 var it = self.getEhFrameRecordsIterator();702 var it = self.getEhFrameRecordsIterator();
666 var record_count: u32 = 0;703 var record_count: u32 = 0;
...@@ -728,12 +765,12 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {...@@ -728,12 +765,12 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
728}765}
729766
730fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {767fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {
731 const sect = self.unwind_info_sect orelse {768 const sect_id = self.unwind_info_sect_id orelse {
732 // If it so happens that the object had `__eh_frame` section defined but no `__compact_unwind`,769 // If it so happens that the object had `__eh_frame` section defined but no `__compact_unwind`,
733 // we will try fully synthesising unwind info records to somewhat match Apple ld's770 // we will try fully synthesising unwind info records to somewhat match Apple ld's
734 // approach. However, we will only synthesise DWARF records and nothing more. For this reason,771 // approach. However, we will only synthesise DWARF records and nothing more. For this reason,
735 // we still create the output `__TEXT,__unwind_info` section.772 // we still create the output `__TEXT,__unwind_info` section.
736 if (self.eh_frame_sect != null) {773 if (self.hasEhFrameRecords()) {
737 if (zld.getSectionByName("__TEXT", "__unwind_info") == null) {774 if (zld.getSectionByName("__TEXT", "__unwind_info") == null) {
738 _ = try zld.initSection("__TEXT", "__unwind_info", .{});775 _ = try zld.initSection("__TEXT", "__unwind_info", .{});
739 }776 }
...@@ -758,15 +795,15 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {...@@ -758,15 +795,15 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {
758 if (UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) break true;795 if (UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) break true;
759 } else false;796 } else false;
760797
761 if (needs_eh_frame) {798 if (needs_eh_frame and !self.hasEhFrameRecords()) {
762 if (self.eh_frame_sect == null) {799 log.err("missing __TEXT,__eh_frame section", .{});
763 log.err("missing __TEXT,__eh_frame section", .{});800 log.err(" in object {s}", .{self.name});
764 log.err(" in object {s}", .{self.name});801 return error.MissingSection;
765 return error.MissingSection;
766 }
767 }802 }
768803
769 const relocs = self.getRelocs(sect);804 try self.parseRelocs(gpa, sect_id);
805 const relocs = self.getRelocs(sect_id);
806
770 for (unwind_records) |record, record_id| {807 for (unwind_records) |record, record_id| {
771 const offset = record_id * @sizeOf(macho.compact_unwind_entry);808 const offset = record_id * @sizeOf(macho.compact_unwind_entry);
772 const rel_pos = filterRelocs(809 const rel_pos = filterRelocs(
...@@ -806,25 +843,23 @@ pub fn getSourceSymbol(self: Object, index: u32) ?macho.nlist_64 {...@@ -806,25 +843,23 @@ pub fn getSourceSymbol(self: Object, index: u32) ?macho.nlist_64 {
806 return symtab[mapped_index];843 return symtab[mapped_index];
807}844}
808845
809pub fn getSourceSection(self: Object, index: u16) macho.section_64 {846pub fn getSourceSection(self: Object, index: u8) macho.section_64 {
810 const sections = self.getSourceSections();847 const sections = self.getSourceSections();
811 assert(index < sections.len);848 assert(index < sections.len);
812 return sections[index];849 return sections[index];
813}850}
814851
815pub fn getSourceSectionByName(self: Object, segname: []const u8, sectname: []const u8) ?macho.section_64 {852pub fn getSourceSectionByName(self: Object, segname: []const u8, sectname: []const u8) ?macho.section_64 {
853 const index = self.getSourceSectionIndexByName(segname, sectname) orelse return null;
816 const sections = self.getSourceSections();854 const sections = self.getSourceSections();
817 for (sections) |sect| {855 return sections[index];
818 if (mem.eql(u8, segname, sect.segName()) and mem.eql(u8, sectname, sect.sectName()))
819 return sect;
820 } else return null;
821}856}
822857
823pub fn getSourceSectionIndexByName(self: Object, segname: []const u8, sectname: []const u8) ?u8 {858pub fn getSourceSectionIndexByName(self: Object, segname: []const u8, sectname: []const u8) ?u8 {
824 const sections = self.getSourceSections();859 const sections = self.getSourceSections();
825 for (sections) |sect, i| {860 for (sections) |sect, i| {
826 if (mem.eql(u8, segname, sect.segName()) and mem.eql(u8, sectname, sect.sectName()))861 if (mem.eql(u8, segname, sect.segName()) and mem.eql(u8, sectname, sect.sectName()))
827 return @intCast(u8, i + 1);862 return @intCast(u8, i);
828 } else return null;863 } else return null;
829}864}
830865
...@@ -841,24 +876,27 @@ pub fn getSourceSections(self: Object) []const macho.section_64 {...@@ -841,24 +876,27 @@ pub fn getSourceSections(self: Object) []const macho.section_64 {
841 } else unreachable;876 } else unreachable;
842}877}
843878
844pub fn parseDataInCode(self: Object) ?[]const macho.data_in_code_entry {879pub fn parseDataInCode(self: *Object, gpa: Allocator) !void {
845 var it = LoadCommandIterator{880 var it = LoadCommandIterator{
846 .ncmds = self.header.ncmds,881 .ncmds = self.header.ncmds,
847 .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],882 .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],
848 };883 };
849 while (it.next()) |cmd| {884 const cmd = while (it.next()) |cmd| {
850 switch (cmd.cmd()) {885 switch (cmd.cmd()) {
851 .DATA_IN_CODE => {886 .DATA_IN_CODE => break cmd.cast(macho.linkedit_data_command).?,
852 const dice = cmd.cast(macho.linkedit_data_command).?;
853 const ndice = @divExact(dice.datasize, @sizeOf(macho.data_in_code_entry));
854 return @ptrCast(
855 [*]const macho.data_in_code_entry,
856 @alignCast(@alignOf(macho.data_in_code_entry), &self.contents[dice.dataoff]),
857 )[0..ndice];
858 },
859 else => {},887 else => {},
860 }888 }
861 } else return null;889 } else return;
890 const ndice = @divExact(cmd.datasize, @sizeOf(macho.data_in_code_entry));
891 const dice = @ptrCast([*]align(1) const macho.data_in_code_entry, self.contents.ptr + cmd.dataoff)[0..ndice];
892 try self.data_in_code.ensureTotalCapacityPrecise(gpa, dice.len);
893 self.data_in_code.appendUnalignedSliceAssumeCapacity(dice);
894 std.sort.sort(macho.data_in_code_entry, self.data_in_code.items, {}, diceLessThan);
895}
896
897fn diceLessThan(ctx: void, lhs: macho.data_in_code_entry, rhs: macho.data_in_code_entry) bool {
898 _ = ctx;
899 return lhs.offset < rhs.offset;
862}900}
863901
864fn parseDysymtab(self: Object) ?macho.dysymtab_command {902fn parseDysymtab(self: Object) ?macho.dysymtab_command {
...@@ -914,11 +952,18 @@ pub fn getSectionAliasSymbolPtr(self: *Object, sect_id: u8) *macho.nlist_64 {...@@ -914,11 +952,18 @@ pub fn getSectionAliasSymbolPtr(self: *Object, sect_id: u8) *macho.nlist_64 {
914 return &self.symtab[self.getSectionAliasSymbolIndex(sect_id)];952 return &self.symtab[self.getSectionAliasSymbolIndex(sect_id)];
915}953}
916954
917pub fn getRelocs(self: Object, sect: macho.section_64) []align(1) const macho.relocation_info {955fn getSourceRelocs(self: Object, sect: macho.section_64) ?[]align(1) const macho.relocation_info {
918 if (sect.nreloc == 0) return &[0]macho.relocation_info{};956 if (sect.nreloc == 0) return null;
919 return @ptrCast([*]align(1) const macho.relocation_info, self.contents.ptr + sect.reloff)[0..sect.nreloc];957 return @ptrCast([*]align(1) const macho.relocation_info, self.contents.ptr + sect.reloff)[0..sect.nreloc];
920}958}
921959
960pub fn getRelocs(self: Object, sect_id: u8) []const macho.relocation_info {
961 const sect = self.getSourceSection(sect_id);
962 const start = self.section_relocs_lookup.items[sect_id];
963 const len = sect.nreloc;
964 return self.relocations.items[start..][0..len];
965}
966
922pub fn getSymbolName(self: Object, index: u32) []const u8 {967pub fn getSymbolName(self: Object, index: u32) []const u8 {
923 const strtab = self.in_strtab.?;968 const strtab = self.in_strtab.?;
924 const sym = self.symtab[index];969 const sym = self.symtab[index];
...@@ -976,22 +1021,28 @@ pub fn getAtomIndexForSymbol(self: Object, sym_index: u32) ?AtomIndex {...@@ -976,22 +1021,28 @@ pub fn getAtomIndexForSymbol(self: Object, sym_index: u32) ?AtomIndex {
976}1021}
9771022
978pub fn hasUnwindRecords(self: Object) bool {1023pub fn hasUnwindRecords(self: Object) bool {
979 return self.unwind_info_sect != null;1024 return self.unwind_info_sect_id != null;
980}1025}
9811026
982pub fn getUnwindRecords(self: Object) []align(1) const macho.compact_unwind_entry {1027pub fn getUnwindRecords(self: Object) []align(1) const macho.compact_unwind_entry {
983 const sect = self.unwind_info_sect orelse return &[0]macho.compact_unwind_entry{};1028 const sect_id = self.unwind_info_sect_id orelse return &[0]macho.compact_unwind_entry{};
1029 const sect = self.getSourceSection(sect_id);
984 const data = self.getSectionContents(sect);1030 const data = self.getSectionContents(sect);
985 const num_entries = @divExact(data.len, @sizeOf(macho.compact_unwind_entry));1031 const num_entries = @divExact(data.len, @sizeOf(macho.compact_unwind_entry));
986 return @ptrCast([*]align(1) const macho.compact_unwind_entry, data)[0..num_entries];1032 return @ptrCast([*]align(1) const macho.compact_unwind_entry, data)[0..num_entries];
987}1033}
9881034
989pub fn hasEhFrameRecords(self: Object) bool {1035pub fn hasEhFrameRecords(self: Object) bool {
990 return self.eh_frame_sect != null;1036 return self.eh_frame_sect_id != null;
991}1037}
9921038
993pub fn getEhFrameRecordsIterator(self: Object) eh_frame.Iterator {1039pub fn getEhFrameRecordsIterator(self: Object) eh_frame.Iterator {
994 const sect = self.eh_frame_sect orelse return .{ .data = &[0]u8{} };1040 const sect_id = self.eh_frame_sect_id orelse return .{ .data = &[0]u8{} };
1041 const sect = self.getSourceSection(sect_id);
995 const data = self.getSectionContents(sect);1042 const data = self.getSectionContents(sect);
996 return .{ .data = data };1043 return .{ .data = data };
997}1044}
1045
1046pub fn hasDataInCode(self: Object) bool {
1047 return self.data_in_code.items.len > 0;
1048}
src/link/MachO/UnwindInfo.zig+5-9
...@@ -68,7 +68,7 @@ const Page = struct {...@@ -68,7 +68,7 @@ const Page = struct {
68 start: RecordIndex,68 start: RecordIndex,
69 count: u16,69 count: u16,
70 page_encodings: [max_compact_encodings]RecordIndex = undefined,70 page_encodings: [max_compact_encodings]RecordIndex = undefined,
71 page_encodings_count: u8 = 0,71 page_encodings_count: u9 = 0,
7272
73 fn appendPageEncoding(page: *Page, record_id: RecordIndex) void {73 fn appendPageEncoding(page: *Page, record_id: RecordIndex) void {
74 assert(page.page_encodings_count <= max_compact_encodings);74 assert(page.page_encodings_count <= max_compact_encodings);
...@@ -81,13 +81,13 @@ const Page = struct {...@@ -81,13 +81,13 @@ const Page = struct {
81 info: *const UnwindInfo,81 info: *const UnwindInfo,
82 enc: macho.compact_unwind_encoding_t,82 enc: macho.compact_unwind_encoding_t,
83 ) ?u8 {83 ) ?u8 {
84 comptime var index: u8 = 0;84 comptime var index: u9 = 0;
85 inline while (index < max_compact_encodings) : (index += 1) {85 inline while (index < max_compact_encodings) : (index += 1) {
86 if (index >= page.page_encodings_count) return null;86 if (index >= page.page_encodings_count) return null;
87 const record_id = page.page_encodings[index];87 const record_id = page.page_encodings[index];
88 const record = info.records.items[record_id];88 const record = info.records.items[record_id];
89 if (record.compactUnwindEncoding == enc) {89 if (record.compactUnwindEncoding == enc) {
90 return index;90 return @intCast(u8, index);
91 }91 }
92 }92 }
93 return null;93 return null;
...@@ -703,15 +703,11 @@ pub fn parseRelocTarget(...@@ -703,15 +703,11 @@ pub fn parseRelocTarget(
703 } else return sym_loc;703 } else return sym_loc;
704}704}
705705
706fn getRelocs(706fn getRelocs(zld: *Zld, object_id: u32, record_id: usize) []const macho.relocation_info {
707 zld: *Zld,
708 object_id: u32,
709 record_id: usize,
710) []align(1) const macho.relocation_info {
711 const object = &zld.objects.items[object_id];707 const object = &zld.objects.items[object_id];
712 assert(object.hasUnwindRecords());708 assert(object.hasUnwindRecords());
713 const rel_pos = object.unwind_relocs_lookup[record_id].reloc;709 const rel_pos = object.unwind_relocs_lookup[record_id].reloc;
714 const relocs = object.getRelocs(object.unwind_info_sect.?);710 const relocs = object.getRelocs(object.unwind_info_sect_id.?);
715 return relocs[rel_pos.start..][0..rel_pos.len];711 return relocs[rel_pos.start..][0..rel_pos.len];
716}712}
717713
src/link/MachO/ZldAtom.zig+14-17
...@@ -143,7 +143,7 @@ pub fn calcInnerSymbolOffset(zld: *Zld, atom_index: AtomIndex, sym_index: u32) u...@@ -143,7 +143,7 @@ pub fn calcInnerSymbolOffset(zld: *Zld, atom_index: AtomIndex, sym_index: u32) u
143 sym.n_value143 sym.n_value
144 else blk: {144 else blk: {
145 const nbase = @intCast(u32, object.in_symtab.?.len);145 const nbase = @intCast(u32, object.in_symtab.?.len);
146 const sect_id = @intCast(u16, atom.sym_index - nbase);146 const sect_id = @intCast(u8, atom.sym_index - nbase);
147 const source_sect = object.getSourceSection(sect_id);147 const source_sect = object.getSourceSection(sect_id);
148 break :blk source_sect.addr;148 break :blk source_sect.addr;
149 };149 };
...@@ -180,7 +180,7 @@ pub fn getRelocContext(zld: *Zld, atom_index: AtomIndex) RelocContext {...@@ -180,7 +180,7 @@ pub fn getRelocContext(zld: *Zld, atom_index: AtomIndex) RelocContext {
180 };180 };
181 }181 }
182 const nbase = @intCast(u32, object.in_symtab.?.len);182 const nbase = @intCast(u32, object.in_symtab.?.len);
183 const sect_id = @intCast(u16, atom.sym_index - nbase);183 const sect_id = @intCast(u8, atom.sym_index - nbase);
184 const source_sect = object.getSourceSection(sect_id);184 const source_sect = object.getSourceSection(sect_id);
185 return .{185 return .{
186 .base_addr = source_sect.addr,186 .base_addr = source_sect.addr,
...@@ -724,7 +724,7 @@ fn resolveRelocsArm64(...@@ -724,7 +724,7 @@ fn resolveRelocsArm64(
724724
725 if (rel.r_extern == 0) {725 if (rel.r_extern == 0) {
726 const base_addr = if (target.sym_index > object.source_address_lookup.len)726 const base_addr = if (target.sym_index > object.source_address_lookup.len)
727 @intCast(i64, object.getSourceSection(@intCast(u16, rel.r_symbolnum - 1)).addr)727 @intCast(i64, object.getSourceSection(@intCast(u8, rel.r_symbolnum - 1)).addr)
728 else728 else
729 object.source_address_lookup[target.sym_index];729 object.source_address_lookup[target.sym_index];
730 ptr_addend -= base_addr;730 ptr_addend -= base_addr;
...@@ -861,7 +861,7 @@ fn resolveRelocsX86(...@@ -861,7 +861,7 @@ fn resolveRelocsX86(
861861
862 if (rel.r_extern == 0) {862 if (rel.r_extern == 0) {
863 const base_addr = if (target.sym_index > object.source_address_lookup.len)863 const base_addr = if (target.sym_index > object.source_address_lookup.len)
864 @intCast(i64, object.getSourceSection(@intCast(u16, rel.r_symbolnum - 1)).addr)864 @intCast(i64, object.getSourceSection(@intCast(u8, rel.r_symbolnum - 1)).addr)
865 else865 else
866 object.source_address_lookup[target.sym_index];866 object.source_address_lookup[target.sym_index];
867 addend += @intCast(i32, @intCast(i64, context.base_addr) + rel.r_address + 4 -867 addend += @intCast(i32, @intCast(i64, context.base_addr) + rel.r_address + 4 -
...@@ -884,7 +884,7 @@ fn resolveRelocsX86(...@@ -884,7 +884,7 @@ fn resolveRelocsX86(
884884
885 if (rel.r_extern == 0) {885 if (rel.r_extern == 0) {
886 const base_addr = if (target.sym_index > object.source_address_lookup.len)886 const base_addr = if (target.sym_index > object.source_address_lookup.len)
887 @intCast(i64, object.getSourceSection(@intCast(u16, rel.r_symbolnum - 1)).addr)887 @intCast(i64, object.getSourceSection(@intCast(u8, rel.r_symbolnum - 1)).addr)
888 else888 else
889 object.source_address_lookup[target.sym_index];889 object.source_address_lookup[target.sym_index];
890 addend -= base_addr;890 addend -= base_addr;
...@@ -928,7 +928,7 @@ pub fn getAtomCode(zld: *Zld, atom_index: AtomIndex) []const u8 {...@@ -928,7 +928,7 @@ pub fn getAtomCode(zld: *Zld, atom_index: AtomIndex) []const u8 {
928 // we are dealing with either an entire section, or part of it, but also928 // we are dealing with either an entire section, or part of it, but also
929 // starting at the beginning.929 // starting at the beginning.
930 const nbase = @intCast(u32, object.in_symtab.?.len);930 const nbase = @intCast(u32, object.in_symtab.?.len);
931 const sect_id = @intCast(u16, atom.sym_index - nbase);931 const sect_id = @intCast(u8, atom.sym_index - nbase);
932 const source_sect = object.getSourceSection(sect_id);932 const source_sect = object.getSourceSection(sect_id);
933 assert(!source_sect.isZerofill());933 assert(!source_sect.isZerofill());
934 const code = object.getSectionContents(source_sect);934 const code = object.getSectionContents(source_sect);
...@@ -943,28 +943,25 @@ pub fn getAtomCode(zld: *Zld, atom_index: AtomIndex) []const u8 {...@@ -943,28 +943,25 @@ pub fn getAtomCode(zld: *Zld, atom_index: AtomIndex) []const u8 {
943 return code[offset..][0..code_len];943 return code[offset..][0..code_len];
944}944}
945945
946pub fn getAtomRelocs(zld: *Zld, atom_index: AtomIndex) []align(1) const macho.relocation_info {946pub fn getAtomRelocs(zld: *Zld, atom_index: AtomIndex) []const macho.relocation_info {
947 const atom = zld.getAtom(atom_index);947 const atom = zld.getAtom(atom_index);
948 assert(atom.getFile() != null); // Synthetic atom shouldn't need to unique for relocs.948 assert(atom.getFile() != null); // Synthetic atom shouldn't need to unique for relocs.
949 const object = zld.objects.items[atom.getFile().?];949 const object = zld.objects.items[atom.getFile().?];
950 const cache = object.relocs_lookup[atom.sym_index];950 const cache = object.relocs_lookup[atom.sym_index];
951951
952 const source_sect = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: {952 const source_sect_id = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
953 const source_sect = object.getSourceSection(source_sym.n_sect - 1);953 break :blk source_sym.n_sect - 1;
954 assert(!source_sect.isZerofill());
955 break :blk source_sect;
956 } else blk: {954 } else blk: {
957 // If there was no matching symbol present in the source symtab, this means955 // If there was no matching symbol present in the source symtab, this means
958 // we are dealing with either an entire section, or part of it, but also956 // we are dealing with either an entire section, or part of it, but also
959 // starting at the beginning.957 // starting at the beginning.
960 const nbase = @intCast(u32, object.in_symtab.?.len);958 const nbase = @intCast(u32, object.in_symtab.?.len);
961 const sect_id = @intCast(u16, atom.sym_index - nbase);959 const sect_id = @intCast(u8, atom.sym_index - nbase);
962 const source_sect = object.getSourceSection(sect_id);960 break :blk sect_id;
963 assert(!source_sect.isZerofill());
964 break :blk source_sect;
965 };961 };
966962 const source_sect = object.getSourceSection(source_sect_id);
967 const relocs = object.getRelocs(source_sect);963 assert(!source_sect.isZerofill());
964 const relocs = object.getRelocs(source_sect_id);
968 return relocs[cache.start..][0..cache.len];965 return relocs[cache.start..][0..cache.len];
969}966}
970967
src/link/MachO/dead_strip.zig+4-3
...@@ -88,7 +88,7 @@ fn collectRoots(zld: *Zld, roots: *AtomTable) !void {...@@ -88,7 +88,7 @@ fn collectRoots(zld: *Zld, roots: *AtomTable) !void {
88 source_sym.n_sect - 188 source_sym.n_sect - 1
89 else sect_id: {89 else sect_id: {
90 const nbase = @intCast(u32, object.in_symtab.?.len);90 const nbase = @intCast(u32, object.in_symtab.?.len);
91 const sect_id = @intCast(u16, atom.sym_index - nbase);91 const sect_id = @intCast(u8, atom.sym_index - nbase);
92 break :sect_id sect_id;92 break :sect_id sect_id;
93 };93 };
94 const source_sect = object.getSourceSection(sect_id);94 const source_sect = object.getSourceSection(sect_id);
...@@ -223,7 +223,7 @@ fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable) !void {...@@ -223,7 +223,7 @@ fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable) !void {
223 source_sym.n_sect - 1223 source_sym.n_sect - 1
224 else blk: {224 else blk: {
225 const nbase = @intCast(u32, object.in_symtab.?.len);225 const nbase = @intCast(u32, object.in_symtab.?.len);
226 const sect_id = @intCast(u16, atom.sym_index - nbase);226 const sect_id = @intCast(u8, atom.sym_index - nbase);
227 break :blk sect_id;227 break :blk sect_id;
228 };228 };
229 const source_sect = object.getSourceSection(sect_id);229 const source_sect = object.getSourceSection(sect_id);
...@@ -350,8 +350,9 @@ fn markEhFrameRecord(zld: *Zld, object_id: u32, atom_index: AtomIndex, alive: *A...@@ -350,8 +350,9 @@ fn markEhFrameRecord(zld: *Zld, object_id: u32, atom_index: AtomIndex, alive: *A
350 }350 }
351 },351 },
352 .x86_64 => {352 .x86_64 => {
353 const sect = object.getSourceSection(object.eh_frame_sect_id.?);
353 const lsda_ptr = try fde.getLsdaPointer(cie, .{354 const lsda_ptr = try fde.getLsdaPointer(cie, .{
354 .base_addr = object.eh_frame_sect.?.addr,355 .base_addr = sect.addr,
355 .base_offset = fde_offset,356 .base_offset = fde_offset,
356 });357 });
357 if (lsda_ptr) |lsda_address| {358 if (lsda_ptr) |lsda_address| {
src/link/MachO/eh_frame.zig+4-7
...@@ -171,8 +171,9 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {...@@ -171,8 +171,9 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
171 const cie_record = eh_records.get(171 const cie_record = eh_records.get(
172 eh_frame_offset + 4 - fde_record.getCiePointer(),172 eh_frame_offset + 4 - fde_record.getCiePointer(),
173 ).?;173 ).?;
174 const eh_frame_sect = object.getSourceSection(object.eh_frame_sect_id.?);
174 const source_lsda_ptr = try fde_record.getLsdaPointer(cie_record, .{175 const source_lsda_ptr = try fde_record.getLsdaPointer(cie_record, .{
175 .base_addr = object.eh_frame_sect.?.addr,176 .base_addr = eh_frame_sect.addr,
176 .base_offset = fde_record_offset,177 .base_offset = fde_record_offset,
177 });178 });
178 if (source_lsda_ptr) |ptr| {179 if (source_lsda_ptr) |ptr| {
...@@ -552,16 +553,12 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {...@@ -552,16 +553,12 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
552 };553 };
553}554}
554555
555pub fn getRelocs(556pub fn getRelocs(zld: *Zld, object_id: u32, source_offset: u32) []const macho.relocation_info {
556 zld: *Zld,
557 object_id: u32,
558 source_offset: u32,
559) []align(1) const macho.relocation_info {
560 const object = &zld.objects.items[object_id];557 const object = &zld.objects.items[object_id];
561 assert(object.hasEhFrameRecords());558 assert(object.hasEhFrameRecords());
562 const urel = object.eh_frame_relocs_lookup.get(source_offset) orelse559 const urel = object.eh_frame_relocs_lookup.get(source_offset) orelse
563 return &[0]macho.relocation_info{};560 return &[0]macho.relocation_info{};
564 const all_relocs = object.getRelocs(object.eh_frame_sect.?);561 const all_relocs = object.getRelocs(object.eh_frame_sect_id.?);
565 return all_relocs[urel.reloc.start..][0..urel.reloc.len];562 return all_relocs[urel.reloc.start..][0..urel.reloc.len];
566}563}
567564
src/link/MachO/zld.zig+16-12
...@@ -20,7 +20,7 @@ const trace = @import("../../tracy.zig").trace;...@@ -20,7 +20,7 @@ const trace = @import("../../tracy.zig").trace;
20const Allocator = mem.Allocator;20const Allocator = mem.Allocator;
21const Archive = @import("Archive.zig");21const Archive = @import("Archive.zig");
22const Atom = @import("ZldAtom.zig");22const Atom = @import("ZldAtom.zig");
23const Cache = @import("../../Cache.zig");23const Cache = std.Build.Cache;
24const CodeSignature = @import("CodeSignature.zig");24const CodeSignature = @import("CodeSignature.zig");
25const Compilation = @import("../../Compilation.zig");25const Compilation = @import("../../Compilation.zig");
26const DwarfInfo = @import("DwarfInfo.zig");26const DwarfInfo = @import("DwarfInfo.zig");
...@@ -1065,7 +1065,13 @@ pub const Zld = struct {...@@ -1065,7 +1065,13 @@ pub const Zld = struct {
1065 assert(offsets.items.len > 0);1065 assert(offsets.items.len > 0);
10661066
1067 const object_id = @intCast(u16, self.objects.items.len);1067 const object_id = @intCast(u16, self.objects.items.len);
1068 const object = try archive.parseObject(gpa, cpu_arch, offsets.items[0]);1068 const object = archive.parseObject(gpa, cpu_arch, offsets.items[0]) catch |e| switch (e) {
1069 error.MismatchedCpuArchitecture => {
1070 log.err("CPU architecture mismatch found in {s}", .{archive.name});
1071 return e;
1072 },
1073 else => return e,
1074 };
1069 try self.objects.append(gpa, object);1075 try self.objects.append(gpa, object);
1070 try self.resolveSymbolsInObject(object_id, resolver);1076 try self.resolveSymbolsInObject(object_id, resolver);
10711077
...@@ -2391,22 +2397,20 @@ pub const Zld = struct {...@@ -2391,22 +2397,20 @@ pub const Zld = struct {
2391 const text_sect_header = self.sections.items(.header)[text_sect_id];2397 const text_sect_header = self.sections.items(.header)[text_sect_id];
23922398
2393 for (self.objects.items) |object| {2399 for (self.objects.items) |object| {
2394 const dice = object.parseDataInCode() orelse continue;2400 if (!object.hasDataInCode()) continue;
2401 const dice = object.data_in_code.items;
2395 try out_dice.ensureUnusedCapacity(dice.len);2402 try out_dice.ensureUnusedCapacity(dice.len);
23962403
2397 for (object.atoms.items) |atom_index| {2404 for (object.exec_atoms.items) |atom_index| {
2398 const atom = self.getAtom(atom_index);2405 const atom = self.getAtom(atom_index);
2399 const sym = self.getSymbol(atom.getSymbolWithLoc());2406 const sym = self.getSymbol(atom.getSymbolWithLoc());
2400 const sect_id = sym.n_sect - 1;2407 if (sym.n_desc == N_DEAD) continue;
2401 if (sect_id != text_sect_id) {
2402 continue;
2403 }
24042408
2405 const source_addr = if (object.getSourceSymbol(atom.sym_index)) |source_sym|2409 const source_addr = if (object.getSourceSymbol(atom.sym_index)) |source_sym|
2406 source_sym.n_value2410 source_sym.n_value
2407 else blk: {2411 else blk: {
2408 const nbase = @intCast(u32, object.in_symtab.?.len);2412 const nbase = @intCast(u32, object.in_symtab.?.len);
2409 const source_sect_id = @intCast(u16, atom.sym_index - nbase);2413 const source_sect_id = @intCast(u8, atom.sym_index - nbase);
2410 break :blk object.getSourceSection(source_sect_id).addr;2414 break :blk object.getSourceSection(source_sect_id).addr;
2411 };2415 };
2412 const filtered_dice = filterDataInCode(dice, source_addr, source_addr + atom.size);2416 const filtered_dice = filterDataInCode(dice, source_addr, source_addr + atom.size);
...@@ -2699,12 +2703,12 @@ pub const Zld = struct {...@@ -2699,12 +2703,12 @@ pub const Zld = struct {
2699 // Exclude region comprising all symbol stabs.2703 // Exclude region comprising all symbol stabs.
2700 const nlocals = self.dysymtab_cmd.nlocalsym;2704 const nlocals = self.dysymtab_cmd.nlocalsym;
27012705
2702 const locals_buf = try self.gpa.alloc(u8, nlocals * @sizeOf(macho.nlist_64));2706 const locals = try self.gpa.alloc(macho.nlist_64, nlocals);
2703 defer self.gpa.free(locals_buf);2707 defer self.gpa.free(locals);
27042708
2709 const locals_buf = @ptrCast([*]u8, locals.ptr)[0 .. @sizeOf(macho.nlist_64) * nlocals];
2705 const amt = try self.file.preadAll(locals_buf, self.symtab_cmd.symoff);2710 const amt = try self.file.preadAll(locals_buf, self.symtab_cmd.symoff);
2706 if (amt != locals_buf.len) return error.InputOutput;2711 if (amt != locals_buf.len) return error.InputOutput;
2707 const locals = @ptrCast([*]macho.nlist_64, @alignCast(@alignOf(macho.nlist_64), locals_buf))[0..nlocals];
27082712
2709 const istab: usize = for (locals) |local, i| {2713 const istab: usize = for (locals) |local, i| {
2710 if (local.stab()) break i;2714 if (local.stab()) break i;
src/link/Wasm.zig+1-1
...@@ -20,7 +20,7 @@ const lldMain = @import("../main.zig").lldMain;...@@ -20,7 +20,7 @@ const lldMain = @import("../main.zig").lldMain;
20const trace = @import("../tracy.zig").trace;20const trace = @import("../tracy.zig").trace;
21const build_options = @import("build_options");21const build_options = @import("build_options");
22const wasi_libc = @import("../wasi_libc.zig");22const wasi_libc = @import("../wasi_libc.zig");
23const Cache = @import("../Cache.zig");23const Cache = std.Build.Cache;
24const Type = @import("../type.zig").Type;24const Type = @import("../type.zig").Type;
25const TypedValue = @import("../TypedValue.zig");25const TypedValue = @import("../TypedValue.zig");
26const LlvmObject = @import("../codegen/llvm.zig").Object;26const LlvmObject = @import("../codegen/llvm.zig").Object;
src/main.zig+48-6
...@@ -20,7 +20,7 @@ const LibCInstallation = @import("libc_installation.zig").LibCInstallation;...@@ -20,7 +20,7 @@ const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
20const wasi_libc = @import("wasi_libc.zig");20const wasi_libc = @import("wasi_libc.zig");
21const translate_c = @import("translate_c.zig");21const translate_c = @import("translate_c.zig");
22const clang = @import("clang.zig");22const clang = @import("clang.zig");
23const Cache = @import("Cache.zig");23const Cache = std.Build.Cache;
24const target_util = @import("target.zig");24const target_util = @import("target.zig");
25const ThreadPool = @import("ThreadPool.zig");25const ThreadPool = @import("ThreadPool.zig");
26const crash_report = @import("crash_report.zig");26const crash_report = @import("crash_report.zig");
...@@ -432,6 +432,8 @@ const usage_build_generic =...@@ -432,6 +432,8 @@ const usage_build_generic =
432 \\ -fno-Clang Prevent using Clang as the C/C++ compilation backend432 \\ -fno-Clang Prevent using Clang as the C/C++ compilation backend
433 \\ -freference-trace[=num] How many lines of reference trace should be shown per compile error433 \\ -freference-trace[=num] How many lines of reference trace should be shown per compile error
434 \\ -fno-reference-trace Disable reference trace434 \\ -fno-reference-trace Disable reference trace
435 \\ -ferror-tracing Enable error tracing in ReleaseFast mode
436 \\ -fno-error-tracing Disable error tracing in Debug and ReleaseSafe mode
435 \\ -fsingle-threaded Code assumes there is only one thread437 \\ -fsingle-threaded Code assumes there is only one thread
436 \\ -fno-single-threaded Code may not assume there is only one thread438 \\ -fno-single-threaded Code may not assume there is only one thread
437 \\ -fbuiltin Enable implicit builtin knowledge of functions439 \\ -fbuiltin Enable implicit builtin knowledge of functions
...@@ -490,6 +492,7 @@ const usage_build_generic =...@@ -490,6 +492,7 @@ const usage_build_generic =
490 \\ nodelete Indicate that the object cannot be deleted from a process492 \\ nodelete Indicate that the object cannot be deleted from a process
491 \\ notext Permit read-only relocations in read-only segments493 \\ notext Permit read-only relocations in read-only segments
492 \\ defs Force a fatal error if any undefined symbols remain494 \\ defs Force a fatal error if any undefined symbols remain
495 \\ undefs Reverse of -z defs
493 \\ origin Indicate that the object must have its origin processed496 \\ origin Indicate that the object must have its origin processed
494 \\ nocopyreloc Disable the creation of copy relocations497 \\ nocopyreloc Disable the creation of copy relocations
495 \\ now (default) Force all relocations to be processed on load498 \\ now (default) Force all relocations to be processed on load
...@@ -506,6 +509,7 @@ const usage_build_generic =...@@ -506,6 +509,7 @@ const usage_build_generic =
506 \\ zlib Compression with deflate/inflate509 \\ zlib Compression with deflate/inflate
507 \\ --gc-sections Force removal of functions and data that are unreachable by the entry point or exported symbols510 \\ --gc-sections Force removal of functions and data that are unreachable by the entry point or exported symbols
508 \\ --no-gc-sections Don't force removal of unreachable functions and data511 \\ --no-gc-sections Don't force removal of unreachable functions and data
512 \\ --sort-section=[value] Sort wildcard section patterns by 'name' or 'alignment'
509 \\ --subsystem [subsystem] (Windows) /SUBSYSTEM:<subsystem> to the linker513 \\ --subsystem [subsystem] (Windows) /SUBSYSTEM:<subsystem> to the linker
510 \\ --stack [size] Override default stack size514 \\ --stack [size] Override default stack size
511 \\ --image-base [addr] Set base address for executable image515 \\ --image-base [addr] Set base address for executable image
...@@ -728,6 +732,7 @@ fn buildOutputType(...@@ -728,6 +732,7 @@ fn buildOutputType(
728 var linker_script: ?[]const u8 = null;732 var linker_script: ?[]const u8 = null;
729 var version_script: ?[]const u8 = null;733 var version_script: ?[]const u8 = null;
730 var disable_c_depfile = false;734 var disable_c_depfile = false;
735 var linker_sort_section: ?link.SortSection = null;
731 var linker_gc_sections: ?bool = null;736 var linker_gc_sections: ?bool = null;
732 var linker_compress_debug_sections: ?link.CompressDebugSections = null;737 var linker_compress_debug_sections: ?link.CompressDebugSections = null;
733 var linker_allow_shlib_undefined: ?bool = null;738 var linker_allow_shlib_undefined: ?bool = null;
...@@ -797,6 +802,7 @@ fn buildOutputType(...@@ -797,6 +802,7 @@ fn buildOutputType(
797 var headerpad_max_install_names: bool = false;802 var headerpad_max_install_names: bool = false;
798 var dead_strip_dylibs: bool = false;803 var dead_strip_dylibs: bool = false;
799 var reference_trace: ?u32 = null;804 var reference_trace: ?u32 = null;
805 var error_tracing: ?bool = null;
800 var pdb_out_path: ?[]const u8 = null;806 var pdb_out_path: ?[]const u8 = null;
801807
802 // e.g. -m3dnow or -mno-outline-atomics. They correspond to std.Target llvm cpu feature names.808 // e.g. -m3dnow or -mno-outline-atomics. They correspond to std.Target llvm cpu feature names.
...@@ -1218,6 +1224,10 @@ fn buildOutputType(...@@ -1218,6 +1224,10 @@ fn buildOutputType(
1218 };1224 };
1219 } else if (mem.eql(u8, arg, "-fno-reference-trace")) {1225 } else if (mem.eql(u8, arg, "-fno-reference-trace")) {
1220 reference_trace = null;1226 reference_trace = null;
1227 } else if (mem.eql(u8, arg, "-ferror-tracing")) {
1228 error_tracing = true;
1229 } else if (mem.eql(u8, arg, "-fno-error-tracing")) {
1230 error_tracing = false;
1221 } else if (mem.eql(u8, arg, "-rdynamic")) {1231 } else if (mem.eql(u8, arg, "-rdynamic")) {
1222 rdynamic = true;1232 rdynamic = true;
1223 } else if (mem.eql(u8, arg, "-fsoname")) {1233 } else if (mem.eql(u8, arg, "-fsoname")) {
...@@ -1326,6 +1336,8 @@ fn buildOutputType(...@@ -1326,6 +1336,8 @@ fn buildOutputType(
1326 linker_z_notext = true;1336 linker_z_notext = true;
1327 } else if (mem.eql(u8, z_arg, "defs")) {1337 } else if (mem.eql(u8, z_arg, "defs")) {
1328 linker_z_defs = true;1338 linker_z_defs = true;
1339 } else if (mem.eql(u8, z_arg, "undefs")) {
1340 linker_z_defs = false;
1329 } else if (mem.eql(u8, z_arg, "origin")) {1341 } else if (mem.eql(u8, z_arg, "origin")) {
1330 linker_z_origin = true;1342 linker_z_origin = true;
1331 } else if (mem.eql(u8, z_arg, "nocopyreloc")) {1343 } else if (mem.eql(u8, z_arg, "nocopyreloc")) {
...@@ -1343,7 +1355,7 @@ fn buildOutputType(...@@ -1343,7 +1355,7 @@ fn buildOutputType(
1343 } else if (mem.startsWith(u8, z_arg, "max-page-size=")) {1355 } else if (mem.startsWith(u8, z_arg, "max-page-size=")) {
1344 linker_z_max_page_size = parseIntSuffix(z_arg, "max-page-size=".len);1356 linker_z_max_page_size = parseIntSuffix(z_arg, "max-page-size=".len);
1345 } else {1357 } else {
1346 warn("unsupported linker extension flag: -z {s}", .{z_arg});1358 fatal("unsupported linker extension flag: -z {s}", .{z_arg});
1347 }1359 }
1348 } else if (mem.eql(u8, arg, "--import-memory")) {1360 } else if (mem.eql(u8, arg, "--import-memory")) {
1349 linker_import_memory = true;1361 linker_import_memory = true;
...@@ -1607,6 +1619,10 @@ fn buildOutputType(...@@ -1607,6 +1619,10 @@ fn buildOutputType(
1607 build_id = true;1619 build_id = true;
1608 warn("ignoring build-id style argument: '{s}'", .{value});1620 warn("ignoring build-id style argument: '{s}'", .{value});
1609 continue;1621 continue;
1622 } else if (mem.eql(u8, key, "--sort-common")) {
1623 // this ignores --sort=common=<anything>; ignoring plain --sort-common
1624 // is done below.
1625 continue;
1610 }1626 }
1611 try linker_args.append(key);1627 try linker_args.append(key);
1612 try linker_args.append(value);1628 try linker_args.append(value);
...@@ -1619,6 +1635,9 @@ fn buildOutputType(...@@ -1619,6 +1635,9 @@ fn buildOutputType(
1619 needed = true;1635 needed = true;
1620 } else if (mem.eql(u8, linker_arg, "-no-pie")) {1636 } else if (mem.eql(u8, linker_arg, "-no-pie")) {
1621 want_pie = false;1637 want_pie = false;
1638 } else if (mem.eql(u8, linker_arg, "--sort-common")) {
1639 // from ld.lld(1): --sort-common is ignored for GNU compatibility,
1640 // this ignores plain --sort-common
1622 } else if (mem.eql(u8, linker_arg, "--whole-archive") or1641 } else if (mem.eql(u8, linker_arg, "--whole-archive") or
1623 mem.eql(u8, linker_arg, "-whole-archive"))1642 mem.eql(u8, linker_arg, "-whole-archive"))
1624 {1643 {
...@@ -1872,6 +1891,8 @@ fn buildOutputType(...@@ -1872,6 +1891,8 @@ fn buildOutputType(
1872 linker_gc_sections = true;1891 linker_gc_sections = true;
1873 } else if (mem.eql(u8, arg, "-dead_strip_dylibs")) {1892 } else if (mem.eql(u8, arg, "-dead_strip_dylibs")) {
1874 dead_strip_dylibs = true;1893 dead_strip_dylibs = true;
1894 } else if (mem.eql(u8, arg, "--no-undefined")) {
1895 linker_z_defs = true;
1875 } else if (mem.eql(u8, arg, "--gc-sections")) {1896 } else if (mem.eql(u8, arg, "--gc-sections")) {
1876 linker_gc_sections = true;1897 linker_gc_sections = true;
1877 } else if (mem.eql(u8, arg, "--no-gc-sections")) {1898 } else if (mem.eql(u8, arg, "--no-gc-sections")) {
...@@ -1882,6 +1903,15 @@ fn buildOutputType(...@@ -1882,6 +1903,15 @@ fn buildOutputType(
1882 linker_print_icf_sections = true;1903 linker_print_icf_sections = true;
1883 } else if (mem.eql(u8, arg, "--print-map")) {1904 } else if (mem.eql(u8, arg, "--print-map")) {
1884 linker_print_map = true;1905 linker_print_map = true;
1906 } else if (mem.eql(u8, arg, "--sort-section")) {
1907 i += 1;
1908 if (i >= linker_args.items.len) {
1909 fatal("expected linker arg after '{s}'", .{arg});
1910 }
1911 const arg1 = linker_args.items[i];
1912 linker_sort_section = std.meta.stringToEnum(link.SortSection, arg1) orelse {
1913 fatal("expected [name|alignment] after --sort-section, found '{s}'", .{arg1});
1914 };
1885 } else if (mem.eql(u8, arg, "--allow-shlib-undefined") or1915 } else if (mem.eql(u8, arg, "--allow-shlib-undefined") or
1886 mem.eql(u8, arg, "-allow-shlib-undefined"))1916 mem.eql(u8, arg, "-allow-shlib-undefined"))
1887 {1917 {
...@@ -1937,6 +1967,8 @@ fn buildOutputType(...@@ -1937,6 +1967,8 @@ fn buildOutputType(
1937 linker_z_notext = true;1967 linker_z_notext = true;
1938 } else if (mem.eql(u8, z_arg, "defs")) {1968 } else if (mem.eql(u8, z_arg, "defs")) {
1939 linker_z_defs = true;1969 linker_z_defs = true;
1970 } else if (mem.eql(u8, z_arg, "undefs")) {
1971 linker_z_defs = false;
1940 } else if (mem.eql(u8, z_arg, "origin")) {1972 } else if (mem.eql(u8, z_arg, "origin")) {
1941 linker_z_origin = true;1973 linker_z_origin = true;
1942 } else if (mem.eql(u8, z_arg, "nocopyreloc")) {1974 } else if (mem.eql(u8, z_arg, "nocopyreloc")) {
...@@ -1961,7 +1993,7 @@ fn buildOutputType(...@@ -1961,7 +1993,7 @@ fn buildOutputType(
1961 } else if (mem.startsWith(u8, z_arg, "max-page-size=")) {1993 } else if (mem.startsWith(u8, z_arg, "max-page-size=")) {
1962 linker_z_max_page_size = parseIntSuffix(z_arg, "max-page-size=".len);1994 linker_z_max_page_size = parseIntSuffix(z_arg, "max-page-size=".len);
1963 } else {1995 } else {
1964 warn("unsupported linker extension flag: -z {s}", .{z_arg});1996 fatal("unsupported linker extension flag: -z {s}", .{z_arg});
1965 }1997 }
1966 } else if (mem.eql(u8, arg, "--major-image-version")) {1998 } else if (mem.eql(u8, arg, "--major-image-version")) {
1967 i += 1;1999 i += 1;
...@@ -2023,6 +2055,14 @@ fn buildOutputType(...@@ -2023,6 +2055,14 @@ fn buildOutputType(
2023 // This option does not do anything.2055 // This option does not do anything.
2024 } else if (mem.eql(u8, arg, "--export-all-symbols")) {2056 } else if (mem.eql(u8, arg, "--export-all-symbols")) {
2025 rdynamic = true;2057 rdynamic = true;
2058 } else if (mem.eql(u8, arg, "--color-diagnostics") or
2059 mem.eql(u8, arg, "--color-diagnostics=always"))
2060 {
2061 color = .on;
2062 } else if (mem.eql(u8, arg, "--no-color-diagnostics") or
2063 mem.eql(u8, arg, "--color-diagnostics=never"))
2064 {
2065 color = .off;
2026 } else if (mem.eql(u8, arg, "-s") or mem.eql(u8, arg, "--strip-all") or2066 } else if (mem.eql(u8, arg, "-s") or mem.eql(u8, arg, "--strip-all") or
2027 mem.eql(u8, arg, "-S") or mem.eql(u8, arg, "--strip-debug"))2067 mem.eql(u8, arg, "-S") or mem.eql(u8, arg, "--strip-debug"))
2028 {2068 {
...@@ -2186,7 +2226,7 @@ fn buildOutputType(...@@ -2186,7 +2226,7 @@ fn buildOutputType(
21862226
2187 have_version = true;2227 have_version = true;
2188 } else {2228 } else {
2189 warn("unsupported linker arg: {s}", .{arg});2229 fatal("unsupported linker arg: {s}", .{arg});
2190 }2230 }
2191 }2231 }
21922232
...@@ -3056,6 +3096,7 @@ fn buildOutputType(...@@ -3056,6 +3096,7 @@ fn buildOutputType(
3056 .version_script = version_script,3096 .version_script = version_script,
3057 .disable_c_depfile = disable_c_depfile,3097 .disable_c_depfile = disable_c_depfile,
3058 .soname = resolved_soname,3098 .soname = resolved_soname,
3099 .linker_sort_section = linker_sort_section,
3059 .linker_gc_sections = linker_gc_sections,3100 .linker_gc_sections = linker_gc_sections,
3060 .linker_allow_shlib_undefined = linker_allow_shlib_undefined,3101 .linker_allow_shlib_undefined = linker_allow_shlib_undefined,
3061 .linker_bind_global_refs_locally = linker_bind_global_refs_locally,3102 .linker_bind_global_refs_locally = linker_bind_global_refs_locally,
...@@ -3136,6 +3177,7 @@ fn buildOutputType(...@@ -3136,6 +3177,7 @@ fn buildOutputType(
3136 .headerpad_max_install_names = headerpad_max_install_names,3177 .headerpad_max_install_names = headerpad_max_install_names,
3137 .dead_strip_dylibs = dead_strip_dylibs,3178 .dead_strip_dylibs = dead_strip_dylibs,
3138 .reference_trace = reference_trace,3179 .reference_trace = reference_trace,
3180 .error_tracing = error_tracing,
3139 .pdb_out_path = pdb_out_path,3181 .pdb_out_path = pdb_out_path,
3140 }) catch |err| switch (err) {3182 }) catch |err| switch (err) {
3141 error.LibCUnavailable => {3183 error.LibCUnavailable => {
...@@ -3607,7 +3649,7 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void...@@ -3607,7 +3649,7 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void
3607 defer if (enable_cache) man.deinit();3649 defer if (enable_cache) man.deinit();
36083650
3609 man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects3651 man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects
3610 man.hashCSource(c_source_file) catch |err| {3652 Compilation.cache_helpers.hashCSource(&man, c_source_file) catch |err| {
3611 fatal("unable to process '{s}': {s}", .{ c_source_file.src_path, @errorName(err) });3653 fatal("unable to process '{s}': {s}", .{ c_source_file.src_path, @errorName(err) });
3612 };3654 };
36133655
...@@ -4523,7 +4565,7 @@ fn fmtPathDir(...@@ -4523,7 +4565,7 @@ fn fmtPathDir(
45234565
4524 if (is_dir and (mem.eql(u8, entry.name, "zig-cache") or mem.eql(u8, entry.name, "zig-out"))) continue;4566 if (is_dir and (mem.eql(u8, entry.name, "zig-cache") or mem.eql(u8, entry.name, "zig-out"))) continue;
45254567
4526 if (is_dir or entry.kind == .File and mem.endsWith(u8, entry.name, ".zig")) {4568 if (is_dir or entry.kind == .File and (mem.endsWith(u8, entry.name, ".zig") or mem.endsWith(u8, entry.name, ".zon"))) {
4527 const full_path = try fs.path.join(fmt.gpa, &[_][]const u8{ file_path, entry.name });4569 const full_path = try fs.path.join(fmt.gpa, &[_][]const u8{ file_path, entry.name });
4528 defer fmt.gpa.free(full_path);4570 defer fmt.gpa.free(full_path);
45294571
src/mingw.zig+1-1
...@@ -8,7 +8,7 @@ const log = std.log.scoped(.mingw);...@@ -8,7 +8,7 @@ const log = std.log.scoped(.mingw);
8const builtin = @import("builtin");8const builtin = @import("builtin");
9const Compilation = @import("Compilation.zig");9const Compilation = @import("Compilation.zig");
10const build_options = @import("build_options");10const build_options = @import("build_options");
11const Cache = @import("Cache.zig");11const Cache = std.Build.Cache;
1212
13pub const CRTFile = enum {13pub const CRTFile = enum {
14 crt2_o,14 crt2_o,
src/print_zir.zig+2-1
...@@ -332,7 +332,6 @@ const Writer = struct {...@@ -332,7 +332,6 @@ const Writer = struct {
332 .float_cast,332 .float_cast,
333 .int_cast,333 .int_cast,
334 .ptr_cast,334 .ptr_cast,
335 .qual_cast,
336 .truncate,335 .truncate,
337 .align_cast,336 .align_cast,
338 .div_exact,337 .div_exact,
...@@ -507,6 +506,8 @@ const Writer = struct {...@@ -507,6 +506,8 @@ const Writer = struct {
507 .reify,506 .reify,
508 .c_va_copy,507 .c_va_copy,
509 .c_va_end,508 .c_va_end,
509 .const_cast,
510 .volatile_cast,
510 => {511 => {
511 const inst_data = self.code.extraData(Zir.Inst.UnNode, extended.operand).data;512 const inst_data = self.code.extraData(Zir.Inst.UnNode, extended.operand).data;
512 const src = LazySrcLoc.nodeOffset(inst_data.node);513 const src = LazySrcLoc.nodeOffset(inst_data.node);
src/translate_c.zig+2-1
...@@ -1748,7 +1748,8 @@ fn transBinaryOperator(...@@ -1748,7 +1748,8 @@ fn transBinaryOperator(
1748 const lhs_expr = stmt.getLHS();1748 const lhs_expr = stmt.getLHS();
1749 const lhs_qt = getExprQualType(c, lhs_expr);1749 const lhs_qt = getExprQualType(c, lhs_expr);
1750 const lhs_qt_translated = try transQualType(c, scope, lhs_qt, lhs_expr.getBeginLoc());1750 const lhs_qt_translated = try transQualType(c, scope, lhs_qt, lhs_expr.getBeginLoc());
1751 const elem_type = lhs_qt_translated.castTag(.c_pointer).?.data.elem_type;1751 const c_pointer = getContainer(c, lhs_qt_translated).?;
1752 const elem_type = c_pointer.castTag(.c_pointer).?.data.elem_type;
1752 const sizeof = try Tag.sizeof.create(c.arena, elem_type);1753 const sizeof = try Tag.sizeof.create(c.arena, elem_type);
17531754
1754 const bitcast = try Tag.bit_cast.create(c.arena, .{ .lhs = ptrdiff_type, .rhs = infixOpNode });1755 const bitcast = try Tag.bit_cast.create(c.arena, .{ .lhs = ptrdiff_type, .rhs = infixOpNode });
stage1/zig1.wasm
Binary files a/stage1/zig1.wasm and b/stage1/zig1.wasm differ
test/behavior/cast.zig+27
...@@ -1541,3 +1541,30 @@ test "single item pointer to pointer to array to slice" {...@@ -1541,3 +1541,30 @@ test "single item pointer to pointer to array to slice" {
1541 const z1 = @as([]const i32, @as(*[1]i32, &x));1541 const z1 = @as([]const i32, @as(*[1]i32, &x));
1542 try expect(z1[0] == 1234);1542 try expect(z1[0] == 1234);
1543}1543}
1544
1545test "peer type resolution forms error union" {
1546 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1547
1548 var foo: i32 = 123;
1549 const result = if (foo < 0) switch (-foo) {
1550 0 => unreachable,
1551 42 => error.AccessDenied,
1552 else => unreachable,
1553 } else @intCast(u32, foo);
1554 try expect(try result == 123);
1555}
1556
1557test "@constCast without a result location" {
1558 const x: i32 = 1234;
1559 const y = @constCast(&x);
1560 try expect(@TypeOf(y) == *i32);
1561 try expect(y.* == 1234);
1562}
1563
1564test "@volatileCast without a result location" {
1565 var x: i32 = 1234;
1566 var y: *volatile i32 = &x;
1567 const z = @volatileCast(y);
1568 try expect(@TypeOf(z) == *i32);
1569 try expect(z.* == 1234);
1570}
test/cases/compile_errors/function_returning_opaque_type.zig+6-6
...@@ -1,11 +1,11 @@...@@ -1,11 +1,11 @@
1const FooType = opaque {};1const FooType = opaque {};
2export fn bar() !FooType {2export fn bar() FooType {
3 return error.InvalidValue;3 return error.InvalidValue;
4}4}
5export fn bav() !@TypeOf(null) {5export fn bav() @TypeOf(null) {
6 return error.InvalidValue;6 return error.InvalidValue;
7}7}
8export fn baz() !@TypeOf(undefined) {8export fn baz() @TypeOf(undefined) {
9 return error.InvalidValue;9 return error.InvalidValue;
10}10}
1111
...@@ -13,7 +13,7 @@ export fn baz() !@TypeOf(undefined) {...@@ -13,7 +13,7 @@ export fn baz() !@TypeOf(undefined) {
13// backend=stage213// backend=stage2
14// target=native14// target=native
15//15//
16// :2:18: error: opaque return type 'tmp.FooType' not allowed16// :2:17: error: opaque return type 'tmp.FooType' not allowed
17// :1:17: note: opaque declared here17// :1:17: note: opaque declared here
18// :5:18: error: return type '@TypeOf(null)' not allowed18// :5:17: error: return type '@TypeOf(null)' not allowed
19// :8:18: error: return type '@TypeOf(undefined)' not allowed19// :8:17: error: return type '@TypeOf(undefined)' not allowed
test/cases/compile_errors/invalid_error_union_payload_type.zig+7
...@@ -4,6 +4,12 @@ comptime {...@@ -4,6 +4,12 @@ comptime {
4comptime {4comptime {
5 _ = anyerror!anyerror;5 _ = anyerror!anyerror;
6}6}
7fn someFunction() !anyerror {
8 return error.C;
9}
10comptime {
11 _ = someFunction;
12}
713
8// error14// error
9// backend=stage215// backend=stage2
...@@ -11,3 +17,4 @@ comptime {...@@ -11,3 +17,4 @@ comptime {
11//17//
12// :2:18: error: error union with payload of opaque type 'anyopaque' not allowed18// :2:18: error: error union with payload of opaque type 'anyopaque' not allowed
13// :5:18: error: error union with payload of error set type 'anyerror' not allowed19// :5:18: error: error union with payload of error set type 'anyerror' not allowed
20// :7:20: error: error union with payload of error set type 'anyerror' not allowed
test/cases/compile_errors/invalid_qualcast.zig deleted-12
...@@ -1,12 +0,0 @@
1pub export fn entry() void {
2 var a: [*:0]const volatile u16 = undefined;
3 _ = @qualCast([*]u16, a);
4}
5
6// error
7// backend=stage2
8// target=native
9//
10// :3:9: error: '@qualCast' can only modify 'const' and 'volatile' qualifiers
11// :3:9: note: expected type '[*]const volatile u16'
12// :3:9: note: got type '[*:0]const volatile u16'
test/cases/compile_errors/ptrCast_discards_const_qualifier.zig+1-1
...@@ -9,4 +9,4 @@ export fn entry() void {...@@ -9,4 +9,4 @@ export fn entry() void {
9// target=native9// target=native
10//10//
11// :3:15: error: cast discards const qualifier11// :3:15: error: cast discards const qualifier
12// :3:15: note: consider using '@qualCast'12// :3:15: note: consider using '@constCast'
test/link/macho/uuid/build.zig+132-15
...@@ -1,4 +1,8 @@...@@ -1,4 +1,8 @@
1const std = @import("std");1const std = @import("std");
2const Builder = std.Build.Builder;
3const CompileStep = std.Build.CompileStep;
4const FileSource = std.Build.FileSource;
5const Step = std.Build.Step;
26
3pub fn build(b: *std.Build) void {7pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test");8 const test_step = b.step("test", "Test");
...@@ -10,18 +14,18 @@ pub fn build(b: *std.Build) void {...@@ -10,18 +14,18 @@ pub fn build(b: *std.Build) void {
10 .os_tag = .macos,14 .os_tag = .macos,
11 };15 };
1216
13 testUuid(b, test_step, .ReleaseSafe, aarch64_macos, "675bb6ba8e5d3d3191f7936d7168f0e9");17 testUuid(b, test_step, .ReleaseSafe, aarch64_macos);
14 testUuid(b, test_step, .ReleaseFast, aarch64_macos, "675bb6ba8e5d3d3191f7936d7168f0e9");18 testUuid(b, test_step, .ReleaseFast, aarch64_macos);
15 testUuid(b, test_step, .ReleaseSmall, aarch64_macos, "675bb6ba8e5d3d3191f7936d7168f0e9");19 testUuid(b, test_step, .ReleaseSmall, aarch64_macos);
1620
17 const x86_64_macos = std.zig.CrossTarget{21 const x86_64_macos = std.zig.CrossTarget{
18 .cpu_arch = .x86_64,22 .cpu_arch = .x86_64,
19 .os_tag = .macos,23 .os_tag = .macos,
20 };24 };
2125
22 testUuid(b, test_step, .ReleaseSafe, x86_64_macos, "5b7071b4587c3071b0d2352fadce0e48");26 testUuid(b, test_step, .ReleaseSafe, x86_64_macos);
23 testUuid(b, test_step, .ReleaseFast, x86_64_macos, "5b7071b4587c3071b0d2352fadce0e48");27 testUuid(b, test_step, .ReleaseFast, x86_64_macos);
24 testUuid(b, test_step, .ReleaseSmall, x86_64_macos, "4b58f2583c383169bbe3a716bd240048");28 testUuid(b, test_step, .ReleaseSmall, x86_64_macos);
25}29}
2630
27fn testUuid(31fn testUuid(
...@@ -29,25 +33,23 @@ fn testUuid(...@@ -29,25 +33,23 @@ fn testUuid(
29 test_step: *std.Build.Step,33 test_step: *std.Build.Step,
30 optimize: std.builtin.OptimizeMode,34 optimize: std.builtin.OptimizeMode,
31 target: std.zig.CrossTarget,35 target: std.zig.CrossTarget,
32 comptime exp: []const u8,
33) void {36) void {
34 // The calculated UUID value is independent of debug info and so it should37 // The calculated UUID value is independent of debug info and so it should
35 // stay the same across builds.38 // stay the same across builds.
36 {39 {
37 const dylib = simpleDylib(b, optimize, target);40 const dylib = simpleDylib(b, optimize, target);
38 const check_dylib = dylib.checkObject(.macho);41 const install_step = installWithRename(dylib, "test1.dylib");
39 check_dylib.checkStart("cmd UUID");42 install_step.step.dependOn(&dylib.step);
40 check_dylib.checkNext("uuid " ++ exp);
41 test_step.dependOn(&check_dylib.step);
42 }43 }
43 {44 {
44 const dylib = simpleDylib(b, optimize, target);45 const dylib = simpleDylib(b, optimize, target);
45 dylib.strip = true;46 dylib.strip = true;
46 const check_dylib = dylib.checkObject(.macho);47 const install_step = installWithRename(dylib, "test2.dylib");
47 check_dylib.checkStart("cmd UUID");48 install_step.step.dependOn(&dylib.step);
48 check_dylib.checkNext("uuid " ++ exp);
49 test_step.dependOn(&check_dylib.step);
50 }49 }
50
51 const cmp_step = CompareUuid.create(b, "test1.dylib", "test2.dylib");
52 test_step.dependOn(&cmp_step.step);
51}53}
5254
53fn simpleDylib(55fn simpleDylib(
...@@ -65,3 +67,118 @@ fn simpleDylib(...@@ -65,3 +67,118 @@ fn simpleDylib(
65 dylib.linkLibC();67 dylib.linkLibC();
66 return dylib;68 return dylib;
67}69}
70
71fn installWithRename(cs: *CompileStep, name: []const u8) *InstallWithRename {
72 const step = InstallWithRename.create(cs.builder, cs.getOutputSource(), name);
73 cs.builder.getInstallStep().dependOn(&step.step);
74 return step;
75}
76
77const InstallWithRename = struct {
78 pub const base_id = .custom;
79
80 step: Step,
81 builder: *Builder,
82 source: FileSource,
83 name: []const u8,
84
85 pub fn create(
86 builder: *Builder,
87 source: FileSource,
88 name: []const u8,
89 ) *InstallWithRename {
90 const self = builder.allocator.create(InstallWithRename) catch @panic("OOM");
91 self.* = InstallWithRename{
92 .builder = builder,
93 .step = Step.init(.custom, builder.fmt("install and rename: {s} -> {s}", .{
94 source.getDisplayName(),
95 name,
96 }), builder.allocator, make),
97 .source = source,
98 .name = builder.dupe(name),
99 };
100 return self;
101 }
102
103 fn make(step: *Step) anyerror!void {
104 const self = @fieldParentPtr(InstallWithRename, "step", step);
105 const source_path = self.source.getPath(self.builder);
106 const target_path = self.builder.getInstallPath(.lib, self.name);
107 self.builder.updateFile(source_path, target_path) catch |err| {
108 std.log.err("Unable to rename: {s} -> {s}", .{ source_path, target_path });
109 return err;
110 };
111 }
112};
113
114const CompareUuid = struct {
115 pub const base_id = .custom;
116
117 step: Step,
118 builder: *Builder,
119 lhs: []const u8,
120 rhs: []const u8,
121
122 pub fn create(builder: *Builder, lhs: []const u8, rhs: []const u8) *CompareUuid {
123 const self = builder.allocator.create(CompareUuid) catch @panic("OOM");
124 self.* = CompareUuid{
125 .builder = builder,
126 .step = Step.init(
127 .custom,
128 builder.fmt("compare uuid: {s} and {s}", .{
129 lhs,
130 rhs,
131 }),
132 builder.allocator,
133 make,
134 ),
135 .lhs = lhs,
136 .rhs = rhs,
137 };
138 return self;
139 }
140
141 fn make(step: *Step) anyerror!void {
142 const self = @fieldParentPtr(CompareUuid, "step", step);
143 const gpa = self.builder.allocator;
144
145 var lhs_uuid: [16]u8 = undefined;
146 const lhs_path = self.builder.getInstallPath(.lib, self.lhs);
147 try parseUuid(gpa, lhs_path, &lhs_uuid);
148
149 var rhs_uuid: [16]u8 = undefined;
150 const rhs_path = self.builder.getInstallPath(.lib, self.rhs);
151 try parseUuid(gpa, rhs_path, &rhs_uuid);
152
153 try std.testing.expectEqualStrings(&lhs_uuid, &rhs_uuid);
154 }
155
156 fn parseUuid(gpa: std.mem.Allocator, path: []const u8, uuid: *[16]u8) anyerror!void {
157 const max_bytes: usize = 20 * 1024 * 1024;
158 const data = try std.fs.cwd().readFileAllocOptions(
159 gpa,
160 path,
161 max_bytes,
162 null,
163 @alignOf(u64),
164 null,
165 );
166 var stream = std.io.fixedBufferStream(data);
167 const reader = stream.reader();
168
169 const hdr = try reader.readStruct(std.macho.mach_header_64);
170 if (hdr.magic != std.macho.MH_MAGIC_64) {
171 return error.InvalidMagicNumber;
172 }
173
174 var it = std.macho.LoadCommandIterator{
175 .ncmds = hdr.ncmds,
176 .buffer = data[@sizeOf(std.macho.mach_header_64)..][0..hdr.sizeofcmds],
177 };
178 const cmd = while (it.next()) |cmd| switch (cmd.cmd()) {
179 .UUID => break cmd.cast(std.macho.uuid_command).?,
180 else => {},
181 } else return error.UuidLoadCommandNotFound;
182 std.mem.copy(u8, uuid, &cmd.uuid);
183 }
184};
test/standalone.zig+2
...@@ -27,6 +27,7 @@ pub fn addCases(cases: *tests.StandaloneContext) void {...@@ -27,6 +27,7 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
27 cases.add("test/standalone/noreturn_call/inline.zig");27 cases.add("test/standalone/noreturn_call/inline.zig");
28 cases.add("test/standalone/noreturn_call/as_arg.zig");28 cases.add("test/standalone/noreturn_call/as_arg.zig");
29 cases.addBuildFile("test/standalone/test_runner_path/build.zig", .{ .requires_stage2 = true });29 cases.addBuildFile("test/standalone/test_runner_path/build.zig", .{ .requires_stage2 = true });
30 cases.addBuildFile("test/standalone/issue_13970/build.zig", .{});
30 cases.addBuildFile("test/standalone/main_pkg_path/build.zig", .{});31 cases.addBuildFile("test/standalone/main_pkg_path/build.zig", .{});
31 cases.addBuildFile("test/standalone/shared_library/build.zig", .{});32 cases.addBuildFile("test/standalone/shared_library/build.zig", .{});
32 cases.addBuildFile("test/standalone/mix_o_files/build.zig", .{});33 cases.addBuildFile("test/standalone/mix_o_files/build.zig", .{});
...@@ -102,4 +103,5 @@ pub fn addCases(cases: *tests.StandaloneContext) void {...@@ -102,4 +103,5 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
102 cases.addBuildFile("test/standalone/issue_13030/build.zig", .{ .build_modes = true });103 cases.addBuildFile("test/standalone/issue_13030/build.zig", .{ .build_modes = true });
103 cases.addBuildFile("test/standalone/emit_asm_and_bin/build.zig", .{});104 cases.addBuildFile("test/standalone/emit_asm_and_bin/build.zig", .{});
104 cases.addBuildFile("test/standalone/issue_12588/build.zig", .{});105 cases.addBuildFile("test/standalone/issue_12588/build.zig", .{});
106 cases.addBuildFile("test/standalone/embed_generated_file/build.zig", .{});
105}107}
test/standalone/embed_generated_file/bootloader.zig created+1
...@@ -0,0 +1 @@
1pub export fn _start() void {}
test/standalone/embed_generated_file/build.zig created+28
...@@ -0,0 +1,28 @@
1const std = @import("std");
2
3pub fn build(b: *std.Build) void {
4 const target = b.standardTargetOptions(.{});
5 const optimize = b.standardOptimizeOption(.{});
6
7 const bootloader = b.addExecutable(.{
8 .name = "bootloader",
9 .root_source_file = .{ .path = "bootloader.zig" },
10 .target = .{
11 .cpu_arch = .x86,
12 .os_tag = .freestanding,
13 },
14 .optimize = .ReleaseSmall,
15 });
16
17 const exe = b.addTest(.{
18 .root_source_file = .{ .path = "main.zig" },
19 .target = target,
20 .optimize = optimize,
21 });
22 exe.addAnonymousModule("bootloader.elf", .{
23 .source_file = bootloader.getOutputSource(),
24 });
25
26 const test_step = b.step("test", "Test the program");
27 test_step.dependOn(&exe.step);
28}
test/standalone/embed_generated_file/main.zig created+8
...@@ -0,0 +1,8 @@
1const std = @import("std");
2const blah = @embedFile("bootloader.elf");
3
4test {
5 comptime {
6 std.debug.assert(std.mem.eql(u8, blah[1..][0..3], "ELF"));
7 }
8}
test/standalone/issue_13970/build.zig created+21
...@@ -0,0 +1,21 @@
1const std = @import("std");
2
3pub fn build(b: *std.Build) void {
4 const test1 = b.addTest(.{
5 .root_source_file = .{ .path = "test_root/empty.zig" },
6 });
7 const test2 = b.addTest(.{
8 .root_source_file = .{ .path = "src/empty.zig" },
9 });
10 const test3 = b.addTest(.{
11 .root_source_file = .{ .path = "empty.zig" },
12 });
13 test1.setTestRunner("src/main.zig");
14 test2.setTestRunner("src/main.zig");
15 test3.setTestRunner("src/main.zig");
16
17 const test_step = b.step("test", "Test package path resolution of custom test runner");
18 test_step.dependOn(&test1.step);
19 test_step.dependOn(&test2.step);
20 test_step.dependOn(&test3.step);
21}
test/standalone/issue_13970/empty.zig created
test/standalone/issue_13970/src/empty.zig created
test/standalone/issue_13970/src/main.zig created+8
...@@ -0,0 +1,8 @@
1const std = @import("std");
2const package = @import("package.zig");
3const root = @import("root");
4const builtin = @import("builtin");
5
6pub fn main() !void {
7 _ = package.decl;
8}
test/standalone/issue_13970/src/package.zig created+1
...@@ -0,0 +1 @@
1pub const decl = 0;
test/standalone/issue_13970/test_root/empty.zig created
test/standalone/test_runner_module_imports/build.zig created+19
...@@ -0,0 +1,19 @@
1const std = @import("std");
2
3pub fn build(b: *std.Build) void {
4 const t = b.addTest(.{
5 .root_source_file = .{ .path = "src/main.zig" },
6 });
7 t.setTestRunner("test_runner/main.zig");
8
9 const module1 = b.createModule(.{ .source_file = .{ .path = "module1/main.zig" } });
10 const module2 = b.createModule(.{
11 .source_file = .{ .path = "module2/main.zig" },
12 .dependencies = &.{.{ .name = "module1", .module = module1 }},
13 });
14
15 t.addModule("module2", module2);
16
17 const test_step = b.step("test", "Run unit tests");
18 test_step.dependOn(&t.step);
19}
test/standalone/test_runner_module_imports/module1/main.zig created+1
...@@ -0,0 +1 @@
1pub const decl: usize = 1234567890;
test/standalone/test_runner_module_imports/module2/main.zig created+1
...@@ -0,0 +1 @@
1pub const mod1 = @import("module1");
test/standalone/test_runner_module_imports/src/main.zig created+6
...@@ -0,0 +1,6 @@
1const mod2 = @import("module2");
2const std = @import("std");
3
4test {
5 try std.testing.expectEqual(@as(usize, 1234567890), mod2.mod1.decl);
6}
test/standalone/test_runner_module_imports/test_runner/main.zig created+9
...@@ -0,0 +1,9 @@
1const std = @import("std");
2const mod2 = @import("module2");
3
4pub fn main() !void {
5 try std.testing.expectEqual(@as(usize, 1234567890), mod2.mod1.decl);
6 for (@import("builtin").test_functions) |test_fn| {
7 try test_fn.func();
8 }
9}
test/tests.zig+2-2
...@@ -570,7 +570,7 @@ pub fn addCliTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []co...@@ -570,7 +570,7 @@ pub fn addCliTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []co
570 const run_cmd = exe.run();570 const run_cmd = exe.run();
571 run_cmd.addArgs(&[_][]const u8{571 run_cmd.addArgs(&[_][]const u8{
572 fs.realpathAlloc(b.allocator, b.zig_exe) catch unreachable,572 fs.realpathAlloc(b.allocator, b.zig_exe) catch unreachable,
573 b.pathFromRoot(b.cache_root),573 b.pathFromRoot(b.cache_root.path orelse "."),
574 });574 });
575575
576 step.dependOn(&run_cmd.step);576 step.dependOn(&run_cmd.step);
...@@ -1059,7 +1059,7 @@ pub const StandaloneContext = struct {...@@ -1059,7 +1059,7 @@ pub const StandaloneContext = struct {
1059 }1059 }
10601060
1061 var zig_args = ArrayList([]const u8).init(b.allocator);1061 var zig_args = ArrayList([]const u8).init(b.allocator);
1062 const rel_zig_exe = fs.path.relative(b.allocator, b.build_root, b.zig_exe) catch unreachable;1062 const rel_zig_exe = fs.path.relative(b.allocator, b.build_root.path orelse ".", b.zig_exe) catch unreachable;
1063 zig_args.append(rel_zig_exe) catch unreachable;1063 zig_args.append(rel_zig_exe) catch unreachable;
1064 zig_args.append("build") catch unreachable;1064 zig_args.append("build") catch unreachable;
10651065
test/translate_c.zig+32
...@@ -3916,4 +3916,36 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -3916,4 +3916,36 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
3916 \\ }) != 0) {}3916 \\ }) != 0) {}
3917 \\}3917 \\}
3918 });3918 });
3919
3920 if (builtin.os.tag == .windows) {
3921 cases.add("Pointer subtraction with typedef",
3922 \\typedef char* S;
3923 \\void foo() {
3924 \\ S a, b;
3925 \\ long long c = a - b;
3926 \\}
3927 , &[_][]const u8{
3928 \\pub export fn foo() void {
3929 \\ var a: S = undefined;
3930 \\ var b: S = undefined;
3931 \\ var c: c_longlong = @divExact(@bitCast(c_longlong, @ptrToInt(a) -% @ptrToInt(b)), @sizeOf(u8));
3932 \\ _ = @TypeOf(c);
3933 \\}
3934 });
3935 } else {
3936 cases.add("Pointer subtraction with typedef",
3937 \\typedef char* S;
3938 \\void foo() {
3939 \\ S a, b;
3940 \\ long c = a - b;
3941 \\}
3942 , &[_][]const u8{
3943 \\pub export fn foo() void {
3944 \\ var a: S = undefined;
3945 \\ var b: S = undefined;
3946 \\ var c: c_long = @divExact(@bitCast(c_long, @ptrToInt(a) -% @ptrToInt(b)), @sizeOf(u8));
3947 \\ _ = @TypeOf(c);
3948 \\}
3949 });
3950 }
3919}3951}
tools/gen_stubs.zig+9
...@@ -592,8 +592,10 @@ const blacklisted_symbols = [_][]const u8{...@@ -592,8 +592,10 @@ const blacklisted_symbols = [_][]const u8{
592 "__addtf3",592 "__addtf3",
593 "__addxf3",593 "__addxf3",
594 "__ashldi3",594 "__ashldi3",
595 "__ashlsi3",
595 "__ashlti3",596 "__ashlti3",
596 "__ashrdi3",597 "__ashrdi3",
598 "__ashrsi3",
597 "__ashrti3",599 "__ashrti3",
598 "__atomic_compare_exchange",600 "__atomic_compare_exchange",
599 "__atomic_compare_exchange_1",601 "__atomic_compare_exchange_1",
...@@ -664,6 +666,7 @@ const blacklisted_symbols = [_][]const u8{...@@ -664,6 +666,7 @@ const blacklisted_symbols = [_][]const u8{
664 "__divkf3",666 "__divkf3",
665 "__divmoddi4",667 "__divmoddi4",
666 "__divmodsi4",668 "__divmodsi4",
669 "__divmodti4",
667 "__divsf3",670 "__divsf3",
668 "__divsi3",671 "__divsi3",
669 "__divtf3",672 "__divtf3",
...@@ -786,6 +789,7 @@ const blacklisted_symbols = [_][]const u8{...@@ -786,6 +789,7 @@ const blacklisted_symbols = [_][]const u8{
786 "__logh",789 "__logh",
787 "__logx",790 "__logx",
788 "__lshrdi3",791 "__lshrdi3",
792 "__lshrsi3",
789 "__lshrti3",793 "__lshrti3",
790 "__ltdf2",794 "__ltdf2",
791 "__ltkf2",795 "__ltkf2",
...@@ -829,6 +833,11 @@ const blacklisted_symbols = [_][]const u8{...@@ -829,6 +833,11 @@ const blacklisted_symbols = [_][]const u8{
829 "__popcountdi2",833 "__popcountdi2",
830 "__popcountsi2",834 "__popcountsi2",
831 "__popcountti2",835 "__popcountti2",
836 "__powidf2",
837 "__powihf2",
838 "__powisf2",
839 "__powitf2",
840 "__powixf2",
832 "__roundh",841 "__roundh",
833 "__roundx",842 "__roundx",
834 "__sincosh",843 "__sincosh",
tools/generate_linux_syscalls.zig+25
...@@ -167,6 +167,31 @@ pub fn main() !void {...@@ -167,6 +167,31 @@ pub fn main() !void {
167167
168 try writer.writeAll("};\n\n");168 try writer.writeAll("};\n\n");
169 }169 }
170 {
171 try writer.writeAll(
172 \\pub const Mips64 = enum(usize) {
173 \\ pub const Linux = 5000;
174 \\
175 \\
176 );
177
178 const table = try linux_dir.readFile("arch/mips/kernel/syscalls/syscall_n64.tbl", buf);
179 var lines = mem.tokenize(u8, table, "\n");
180 while (lines.next()) |line| {
181 if (line[0] == '#') continue;
182
183 var fields = mem.tokenize(u8, line, " \t");
184 const number = fields.next() orelse return error.Incomplete;
185 // abi is always n64
186 _ = fields.next() orelse return error.Incomplete;
187 const name = fields.next() orelse return error.Incomplete;
188 const fixed_name = if (stdlib_renames.get(name)) |fixed| fixed else name;
189
190 try writer.print(" {s} = Linux + {s},\n", .{ zig.fmtId(fixed_name), number });
191 }
192
193 try writer.writeAll("};\n\n");
194 }
170 {195 {
171 try writer.writeAll("pub const PowerPC = enum(usize) {\n");196 try writer.writeAll("pub const PowerPC = enum(usize) {\n");
172197
tools/update_cpu_features.zig+1-1
...@@ -1385,7 +1385,7 @@ fn usageAndExit(file: fs.File, arg0: []const u8, code: u8) noreturn {...@@ -1385,7 +1385,7 @@ fn usageAndExit(file: fs.File, arg0: []const u8, code: u8) noreturn {
1385 \\1385 \\
1386 \\Updates lib/std/target/<target>.zig from llvm/lib/Target/<Target>/<Target>.td .1386 \\Updates lib/std/target/<target>.zig from llvm/lib/Target/<Target>/<Target>.td .
1387 \\1387 \\
1388 \\On a less beefy system, or when debugging, compile with --single-threaded.1388 \\On a less beefy system, or when debugging, compile with -fsingle-threaded.
1389 \\1389 \\
1390 , .{arg0}) catch std.process.exit(1);1390 , .{arg0}) catch std.process.exit(1);
1391 std.process.exit(code);1391 std.process.exit(code);