From 01aa74a93fa7c18f8a2f27a4c5f76333935c3502 Mon Sep 17 00:00:00 2001 From: Shachaf Ben-Kiki Date: Sat, 24 Apr 2021 16:41:10 -0700 Subject: [PATCH 01/42] parser: Use an operator precedence table Instead of having one function per precedence level, mirroring the BNF grammar, this uses the "precedence climbing" algorithm with a table of operator precedences (and a few special cases that don't fit into the table as it is). This is a first pass -- it's probably possible to put more of the parser into this form, e.g. to support prefix/suffix operators with precedence, if necessary, or just to simplify the code more. It may also be possible to speed this up by putting more useful information into the tokens during tokenization, to avoid the extra branch on the token in operInfo. --- lib/std/zig/parse.zig | 334 ++++++++++-------------------------- lib/std/zig/parser_test.zig | 11 ++ 2 files changed, 98 insertions(+), 247 deletions(-) diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index d6880ad273..cc9a506e1c 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -1309,9 +1309,8 @@ const Parser = struct { return expr; } - /// Expr <- BoolOrExpr fn parseExpr(p: *Parser) Error!Node.Index { - return p.parseBoolOrExpr(); + return p.parseExprPrecedence(0); } fn expectExpr(p: *Parser) Error!Node.Index { @@ -1323,263 +1322,104 @@ const Parser = struct { } } - /// BoolOrExpr <- BoolAndExpr (KEYWORD_or BoolAndExpr)* - fn parseBoolOrExpr(p: *Parser) Error!Node.Index { - var res = try p.parseBoolAndExpr(); - if (res == 0) return null_node; + const Assoc = enum { + left, + none, + }; - while (true) { - switch (p.token_tags[p.tok_i]) { - .keyword_or => { - const or_token = p.nextToken(); - const rhs = try p.parseBoolAndExpr(); - if (rhs == 0) { - return p.fail(.invalid_token); - } - res = try p.addNode(.{ - .tag = .bool_or, - .main_token = or_token, - .data = .{ - .lhs = res, - .rhs = rhs, - }, - }); - }, - else => return res, - } - } + const OperInfo = struct { + prec: i8, + tag: Node.Tag, + assoc: Assoc = Assoc.left, + }; + + // A table of binary operator information. Higher precedence numbers are + // stickier. All operators at the same precedence level should have the same + // associativity. + fn operInfo(tag: Token.Tag) OperInfo { + return switch (tag) { + .keyword_or => .{ .prec = 10, .tag = .bool_or }, + + .keyword_and => .{ .prec = 20, .tag = .bool_and }, + .invalid_ampersands => .{ .prec = 20, .tag = .bool_and }, + + .equal_equal => .{ .prec = 30, .tag = .equal_equal, .assoc = Assoc.none }, + .bang_equal => .{ .prec = 30, .tag = .bang_equal, .assoc = Assoc.none }, + .angle_bracket_left => .{ .prec = 30, .tag = .less_than, .assoc = Assoc.none }, + .angle_bracket_right => .{ .prec = 30, .tag = .greater_than, .assoc = Assoc.none }, + .angle_bracket_left_equal => .{ .prec = 30, .tag = .less_or_equal, .assoc = Assoc.none }, + .angle_bracket_right_equal => .{ .prec = 30, .tag = .greater_or_equal, .assoc = Assoc.none }, + + .ampersand => .{ .prec = 40, .tag = .bit_and }, + .caret => .{ .prec = 40, .tag = .bit_xor }, + .pipe => .{ .prec = 40, .tag = .bit_or }, + .keyword_orelse => .{ .prec = 40, .tag = .@"orelse" }, + .keyword_catch => .{ .prec = 40, .tag = .@"catch" }, + + .angle_bracket_angle_bracket_left => .{ .prec = 50, .tag = .bit_shift_left }, + .angle_bracket_angle_bracket_right => .{ .prec = 50, .tag = .bit_shift_right }, + + .plus => .{ .prec = 60, .tag = .add }, + .minus => .{ .prec = 60, .tag = .sub }, + .plus_plus => .{ .prec = 60, .tag = .array_cat }, + .plus_percent => .{ .prec = 60, .tag = .add_wrap }, + .minus_percent => .{ .prec = 60, .tag = .sub_wrap }, + + .pipe_pipe => .{ .prec = 70, .tag = .merge_error_sets }, + .asterisk => .{ .prec = 70, .tag = .mul }, + .slash => .{ .prec = 70, .tag = .div }, + .percent => .{ .prec = 70, .tag = .mod }, + .asterisk_asterisk => .{ .prec = 70, .tag = .array_mult }, + .asterisk_percent => .{ .prec = 70, .tag = .mul_wrap }, + + else => .{ .prec = -1, .tag = ast.Node.Tag.root }, // irrelevant + }; } - /// BoolAndExpr <- CompareExpr (KEYWORD_and CompareExpr)* - fn parseBoolAndExpr(p: *Parser) !Node.Index { - var res = try p.parseCompareExpr(); - if (res == 0) return null_node; + fn parseExprPrecedence(p: *Parser, min_prec: i32) Error!Node.Index { + var node = try p.parsePrefixExpr(); + if (node == 0) { + return null_node; + } + + var banned_prec: i8 = -1; while (true) { - switch (p.token_tags[p.tok_i]) { - .keyword_and => { - const and_token = p.nextToken(); - const rhs = try p.parseCompareExpr(); - if (rhs == 0) { - return p.fail(.invalid_token); - } - res = try p.addNode(.{ - .tag = .bool_and, - .main_token = and_token, - .data = .{ - .lhs = res, - .rhs = rhs, - }, - }); + const tok_tag = p.token_tags[p.tok_i]; + const info = operInfo(tok_tag); + if (info.prec < min_prec or info.prec == banned_prec) { + break; + } + const oper_token = p.nextToken(); + // Special-case handling for "catch" and "&&". + switch (tok_tag) { + .keyword_catch => { + _ = try p.parsePayload(); }, .invalid_ampersands => { try p.warn(.invalid_and); - p.tok_i += 1; - return p.parseCompareExpr(); }, - else => return res, + else => {}, + } + const rhs = try p.parseExprPrecedence(info.prec + 1); + if (rhs == 0) { + return p.fail(.invalid_token); + } + + node = try p.addNode(.{ + .tag = info.tag, + .main_token = oper_token, + .data = .{ + .lhs = node, + .rhs = rhs, + }, + }); + + if (info.assoc == Assoc.none) { + banned_prec = info.prec; } } - } - /// CompareExpr <- BitwiseExpr (CompareOp BitwiseExpr)? - /// CompareOp - /// <- EQUALEQUAL - /// / EXCLAMATIONMARKEQUAL - /// / LARROW - /// / RARROW - /// / LARROWEQUAL - /// / RARROWEQUAL - fn parseCompareExpr(p: *Parser) !Node.Index { - const expr = try p.parseBitwiseExpr(); - if (expr == 0) return null_node; - - const tag: Node.Tag = switch (p.token_tags[p.tok_i]) { - .equal_equal => .equal_equal, - .bang_equal => .bang_equal, - .angle_bracket_left => .less_than, - .angle_bracket_right => .greater_than, - .angle_bracket_left_equal => .less_or_equal, - .angle_bracket_right_equal => .greater_or_equal, - else => return expr, - }; - return p.addNode(.{ - .tag = tag, - .main_token = p.nextToken(), - .data = .{ - .lhs = expr, - .rhs = try p.expectBitwiseExpr(), - }, - }); - } - - /// BitwiseExpr <- BitShiftExpr (BitwiseOp BitShiftExpr)* - /// BitwiseOp - /// <- AMPERSAND - /// / CARET - /// / PIPE - /// / KEYWORD_orelse - /// / KEYWORD_catch Payload? - fn parseBitwiseExpr(p: *Parser) !Node.Index { - var res = try p.parseBitShiftExpr(); - if (res == 0) return null_node; - - while (true) { - const tag: Node.Tag = switch (p.token_tags[p.tok_i]) { - .ampersand => .bit_and, - .caret => .bit_xor, - .pipe => .bit_or, - .keyword_orelse => .@"orelse", - .keyword_catch => { - const catch_token = p.nextToken(); - _ = try p.parsePayload(); - const rhs = try p.parseBitShiftExpr(); - if (rhs == 0) { - return p.fail(.invalid_token); - } - res = try p.addNode(.{ - .tag = .@"catch", - .main_token = catch_token, - .data = .{ - .lhs = res, - .rhs = rhs, - }, - }); - continue; - }, - else => return res, - }; - res = try p.addNode(.{ - .tag = tag, - .main_token = p.nextToken(), - .data = .{ - .lhs = res, - .rhs = try p.expectBitShiftExpr(), - }, - }); - } - } - - fn expectBitwiseExpr(p: *Parser) Error!Node.Index { - const node = try p.parseBitwiseExpr(); - if (node == 0) { - return p.fail(.invalid_token); - } else { - return node; - } - } - - /// BitShiftExpr <- AdditionExpr (BitShiftOp AdditionExpr)* - /// BitShiftOp - /// <- LARROW2 - /// / RARROW2 - fn parseBitShiftExpr(p: *Parser) Error!Node.Index { - var res = try p.parseAdditionExpr(); - if (res == 0) return null_node; - - while (true) { - const tag: Node.Tag = switch (p.token_tags[p.tok_i]) { - .angle_bracket_angle_bracket_left => .bit_shift_left, - .angle_bracket_angle_bracket_right => .bit_shift_right, - else => return res, - }; - res = try p.addNode(.{ - .tag = tag, - .main_token = p.nextToken(), - .data = .{ - .lhs = res, - .rhs = try p.expectAdditionExpr(), - }, - }); - } - } - - fn expectBitShiftExpr(p: *Parser) Error!Node.Index { - const node = try p.parseBitShiftExpr(); - if (node == 0) { - return p.fail(.invalid_token); - } else { - return node; - } - } - - /// AdditionExpr <- MultiplyExpr (AdditionOp MultiplyExpr)* - /// AdditionOp - /// <- PLUS - /// / MINUS - /// / PLUS2 - /// / PLUSPERCENT - /// / MINUSPERCENT - fn parseAdditionExpr(p: *Parser) Error!Node.Index { - var res = try p.parseMultiplyExpr(); - if (res == 0) return null_node; - - while (true) { - const tag: Node.Tag = switch (p.token_tags[p.tok_i]) { - .plus => .add, - .minus => .sub, - .plus_plus => .array_cat, - .plus_percent => .add_wrap, - .minus_percent => .sub_wrap, - else => return res, - }; - res = try p.addNode(.{ - .tag = tag, - .main_token = p.nextToken(), - .data = .{ - .lhs = res, - .rhs = try p.expectMultiplyExpr(), - }, - }); - } - } - - fn expectAdditionExpr(p: *Parser) Error!Node.Index { - const node = try p.parseAdditionExpr(); - if (node == 0) { - return p.fail(.invalid_token); - } - return node; - } - - /// MultiplyExpr <- PrefixExpr (MultiplyOp PrefixExpr)* - /// MultiplyOp - /// <- PIPE2 - /// / ASTERISK - /// / SLASH - /// / PERCENT - /// / ASTERISK2 - /// / ASTERISKPERCENT - fn parseMultiplyExpr(p: *Parser) Error!Node.Index { - var res = try p.parsePrefixExpr(); - if (res == 0) return null_node; - - while (true) { - const tag: Node.Tag = switch (p.token_tags[p.tok_i]) { - .pipe_pipe => .merge_error_sets, - .asterisk => .mul, - .slash => .div, - .percent => .mod, - .asterisk_asterisk => .array_mult, - .asterisk_percent => .mul_wrap, - else => return res, - }; - res = try p.addNode(.{ - .tag = tag, - .main_token = p.nextToken(), - .data = .{ - .lhs = res, - .rhs = try p.expectPrefixExpr(), - }, - }); - } - } - - fn expectMultiplyExpr(p: *Parser) Error!Node.Index { - const node = try p.parseMultiplyExpr(); - if (node == 0) { - return p.fail(.invalid_token); - } return node; } diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index 2fa8baa185..b5f3b75482 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -2796,6 +2796,7 @@ test "zig fmt: precedence" { \\ a or b and c; \\ (a or b) and c; \\ (a or b) and c; + \\ a == b and c == d; \\} \\ ); @@ -4860,6 +4861,16 @@ test "recovery: missing comma" { }); } +test "recovery: non-associative operators" { + try testError( + \\const x = a == b == c; + \\const x = a == b != c; + , &[_]Error{ + .expected_token, + .expected_token, + }); +} + test "recovery: extra qualifier" { try testError( \\const a: *const const u8; From 33da465079d8d51bbe8d6e6c9976815a750872d0 Mon Sep 17 00:00:00 2001 From: Shachaf Ben-Kiki Date: Sat, 24 Apr 2021 22:35:37 -0700 Subject: [PATCH 02/42] Use a directEnumArray lookup table instead of a switch --- lib/std/zig/parse.zig | 64 ++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 34 deletions(-) diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index cc9a506e1c..047543e115 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -1336,45 +1336,41 @@ const Parser = struct { // A table of binary operator information. Higher precedence numbers are // stickier. All operators at the same precedence level should have the same // associativity. - fn operInfo(tag: Token.Tag) OperInfo { - return switch (tag) { - .keyword_or => .{ .prec = 10, .tag = .bool_or }, + const operTable = std.enums.directEnumArrayDefault(Token.Tag, OperInfo, .{ .prec = -1, .tag = Node.Tag.root }, 0, .{ + .keyword_or = .{ .prec = 10, .tag = .bool_or }, - .keyword_and => .{ .prec = 20, .tag = .bool_and }, - .invalid_ampersands => .{ .prec = 20, .tag = .bool_and }, + .keyword_and = .{ .prec = 20, .tag = .bool_and }, + .invalid_ampersands = .{ .prec = 20, .tag = .bool_and }, - .equal_equal => .{ .prec = 30, .tag = .equal_equal, .assoc = Assoc.none }, - .bang_equal => .{ .prec = 30, .tag = .bang_equal, .assoc = Assoc.none }, - .angle_bracket_left => .{ .prec = 30, .tag = .less_than, .assoc = Assoc.none }, - .angle_bracket_right => .{ .prec = 30, .tag = .greater_than, .assoc = Assoc.none }, - .angle_bracket_left_equal => .{ .prec = 30, .tag = .less_or_equal, .assoc = Assoc.none }, - .angle_bracket_right_equal => .{ .prec = 30, .tag = .greater_or_equal, .assoc = Assoc.none }, + .equal_equal = .{ .prec = 30, .tag = .equal_equal, .assoc = Assoc.none }, + .bang_equal = .{ .prec = 30, .tag = .bang_equal, .assoc = Assoc.none }, + .angle_bracket_left = .{ .prec = 30, .tag = .less_than, .assoc = Assoc.none }, + .angle_bracket_right = .{ .prec = 30, .tag = .greater_than, .assoc = Assoc.none }, + .angle_bracket_left_equal = .{ .prec = 30, .tag = .less_or_equal, .assoc = Assoc.none }, + .angle_bracket_right_equal = .{ .prec = 30, .tag = .greater_or_equal, .assoc = Assoc.none }, - .ampersand => .{ .prec = 40, .tag = .bit_and }, - .caret => .{ .prec = 40, .tag = .bit_xor }, - .pipe => .{ .prec = 40, .tag = .bit_or }, - .keyword_orelse => .{ .prec = 40, .tag = .@"orelse" }, - .keyword_catch => .{ .prec = 40, .tag = .@"catch" }, + .ampersand = .{ .prec = 40, .tag = .bit_and }, + .caret = .{ .prec = 40, .tag = .bit_xor }, + .pipe = .{ .prec = 40, .tag = .bit_or }, + .keyword_orelse = .{ .prec = 40, .tag = .@"orelse" }, + .keyword_catch = .{ .prec = 40, .tag = .@"catch" }, - .angle_bracket_angle_bracket_left => .{ .prec = 50, .tag = .bit_shift_left }, - .angle_bracket_angle_bracket_right => .{ .prec = 50, .tag = .bit_shift_right }, + .angle_bracket_angle_bracket_left = .{ .prec = 50, .tag = .bit_shift_left }, + .angle_bracket_angle_bracket_right = .{ .prec = 50, .tag = .bit_shift_right }, - .plus => .{ .prec = 60, .tag = .add }, - .minus => .{ .prec = 60, .tag = .sub }, - .plus_plus => .{ .prec = 60, .tag = .array_cat }, - .plus_percent => .{ .prec = 60, .tag = .add_wrap }, - .minus_percent => .{ .prec = 60, .tag = .sub_wrap }, + .plus = .{ .prec = 60, .tag = .add }, + .minus = .{ .prec = 60, .tag = .sub }, + .plus_plus = .{ .prec = 60, .tag = .array_cat }, + .plus_percent = .{ .prec = 60, .tag = .add_wrap }, + .minus_percent = .{ .prec = 60, .tag = .sub_wrap }, - .pipe_pipe => .{ .prec = 70, .tag = .merge_error_sets }, - .asterisk => .{ .prec = 70, .tag = .mul }, - .slash => .{ .prec = 70, .tag = .div }, - .percent => .{ .prec = 70, .tag = .mod }, - .asterisk_asterisk => .{ .prec = 70, .tag = .array_mult }, - .asterisk_percent => .{ .prec = 70, .tag = .mul_wrap }, - - else => .{ .prec = -1, .tag = ast.Node.Tag.root }, // irrelevant - }; - } + .pipe_pipe = .{ .prec = 70, .tag = .merge_error_sets }, + .asterisk = .{ .prec = 70, .tag = .mul }, + .slash = .{ .prec = 70, .tag = .div }, + .percent = .{ .prec = 70, .tag = .mod }, + .asterisk_asterisk = .{ .prec = 70, .tag = .array_mult }, + .asterisk_percent = .{ .prec = 70, .tag = .mul_wrap }, + }); fn parseExprPrecedence(p: *Parser, min_prec: i32) Error!Node.Index { var node = try p.parsePrefixExpr(); @@ -1386,7 +1382,7 @@ const Parser = struct { while (true) { const tok_tag = p.token_tags[p.tok_i]; - const info = operInfo(tok_tag); + const info = operTable[@intCast(usize, @enumToInt(tok_tag))]; if (info.prec < min_prec or info.prec == banned_prec) { break; } From 6e5330ee38cb10d4dec85ec36c7a1a0925ead8c9 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Wed, 12 May 2021 14:23:08 +0700 Subject: [PATCH 03/42] Define ENOTSUP for SPARC --- lib/std/os/bits/linux/errno-sparc.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/std/os/bits/linux/errno-sparc.zig b/lib/std/os/bits/linux/errno-sparc.zig index bbeabaaef0..94b696fc35 100644 --- a/lib/std/os/bits/linux/errno-sparc.zig +++ b/lib/std/os/bits/linux/errno-sparc.zig @@ -52,6 +52,7 @@ pub const ENOPROTOOPT = 42; pub const EPROTONOSUPPORT = 43; pub const ESOCKTNOSUPPORT = 44; pub const EOPNOTSUPP = 45; +pub const ENOTSUP = EOPNOTSUPP; pub const EPFNOSUPPORT = 46; pub const EAFNOSUPPORT = 47; pub const EADDRINUSE = 48; From 9c07d30ef5ccfa50c61b51f75c42cf37a09a5863 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Mon, 5 Apr 2021 21:22:20 +0700 Subject: [PATCH 04/42] Fix crti/crtn path for SPARC --- src/glibc.zig | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/glibc.zig b/src/glibc.zig index 6b288ac46d..04673e639b 100644 --- a/src/glibc.zig +++ b/src/glibc.zig @@ -446,10 +446,14 @@ fn start_asm_path(comp: *Compilation, arena: *Allocator, basename: []const u8) ! try result.appendSlice(comp.zig_lib_directory.path.?); try result.appendSlice(s ++ "libc" ++ s ++ "glibc" ++ s ++ "sysdeps" ++ s); if (is_sparc) { - if (is_64) { - try result.appendSlice("sparc" ++ s ++ "sparc64"); + if (mem.eql(u8, basename, "crti.S") or mem.eql(u8, basename, "crtn.S")) { + try result.appendSlice("sparc"); } else { - try result.appendSlice("sparc" ++ s ++ "sparc32"); + if (is_64) { + try result.appendSlice("sparc" ++ s ++ "sparc64"); + } else { + try result.appendSlice("sparc" ++ s ++ "sparc32"); + } } } else if (arch.isARM()) { try result.appendSlice("arm"); From 5dd6f9616e2adebed8e43a0c6bfb3ce8279b1c15 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Tue, 20 Apr 2021 18:35:50 +0700 Subject: [PATCH 05/42] Fix linux dirent64 buffer align directive --- lib/std/fs.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/fs.zig b/lib/std/fs.zig index 1a02cd5b6b..1b77c6657a 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -501,7 +501,7 @@ pub const Dir = struct { }, .linux => struct { dir: Dir, - buf: [8192]u8, // TODO align(@alignOf(os.dirent64)), + buf: [8192]u8 align(if (builtin.os.tag != .linux) 1 else @alignOf(os.linux.dirent64)), index: usize, end_index: usize, From f24d0fbb073e8c8795294bbc2727d6360b4bb4dd Mon Sep 17 00:00:00 2001 From: Koakuma Date: Tue, 13 Apr 2021 19:37:15 +0700 Subject: [PATCH 06/42] Remove SPARC pthreadtypes-arch.h to match upstream glibc This is only a temporary measure, we need to properly update the bundled glibc. --- .../sparc/nptl/bits/pthreadtypes-arch.h | 81 ------------------- 1 file changed, 81 deletions(-) delete mode 100644 lib/libc/glibc/sysdeps/sparc/nptl/bits/pthreadtypes-arch.h diff --git a/lib/libc/glibc/sysdeps/sparc/nptl/bits/pthreadtypes-arch.h b/lib/libc/glibc/sysdeps/sparc/nptl/bits/pthreadtypes-arch.h deleted file mode 100644 index d632a0c5c7..0000000000 --- a/lib/libc/glibc/sysdeps/sparc/nptl/bits/pthreadtypes-arch.h +++ /dev/null @@ -1,81 +0,0 @@ -/* Machine-specific pthread type layouts. SPARC version. - Copyright (C) 2003-2019 Free Software Foundation, Inc. - This file is part of the GNU C Library. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library; if not, see - . */ - -#ifndef _BITS_PTHREADTYPES_ARCH_H -#define _BITS_PTHREADTYPES_ARCH_H 1 - -#include - -#if __WORDSIZE == 64 -# define __SIZEOF_PTHREAD_ATTR_T 56 -# define __SIZEOF_PTHREAD_MUTEX_T 40 -# define __SIZEOF_PTHREAD_CONDATTR_T 4 -# define __SIZEOF_PTHREAD_RWLOCK_T 56 -# define __SIZEOF_PTHREAD_BARRIER_T 32 -#else -# define __SIZEOF_PTHREAD_ATTR_T 36 -# define __SIZEOF_PTHREAD_MUTEX_T 24 -# define __SIZEOF_PTHREAD_CONDATTR_T 4 -# define __SIZEOF_PTHREAD_RWLOCK_T 32 -# define __SIZEOF_PTHREAD_BARRIER_T 20 -#endif -#define __SIZEOF_PTHREAD_MUTEXATTR_T 4 -#define __SIZEOF_PTHREAD_COND_T 48 -#define __SIZEOF_PTHREAD_RWLOCKATTR_T 8 -#define __SIZEOF_PTHREAD_BARRIERATTR_T 4 - -/* Definitions for internal mutex struct. */ -#define __PTHREAD_COMPAT_PADDING_MID -#define __PTHREAD_COMPAT_PADDING_END -#define __PTHREAD_MUTEX_LOCK_ELISION 0 -#define __PTHREAD_MUTEX_NUSERS_AFTER_KIND (__WORDSIZE != 64) -#define __PTHREAD_MUTEX_USE_UNION (__WORDSIZE != 64) - -#define __LOCK_ALIGNMENT -#define __ONCE_ALIGNMENT - -struct __pthread_rwlock_arch_t -{ - unsigned int __readers; - unsigned int __writers; - unsigned int __wrphase_futex; - unsigned int __writers_futex; - unsigned int __pad3; - unsigned int __pad4; -#if __WORDSIZE == 64 - int __cur_writer; - int __shared; - unsigned long int __pad1; - unsigned long int __pad2; - /* FLAGS must stay at this position in the structure to maintain - binary compatibility. */ - unsigned int __flags; -#else - unsigned char __pad1; - unsigned char __pad2; - unsigned char __shared; - /* FLAGS must stay at this position in the structure to maintain - binary compatibility. */ - unsigned char __flags; - int __cur_writer; -#endif -}; - -#define __PTHREAD_RWLOCK_ELISION_EXTRA 0 - -#endif /* bits/pthreadtypes.h */ From 9e67c062c40292f02334111a4784bd60907b5d0e Mon Sep 17 00:00:00 2001 From: Koakuma Date: Wed, 12 May 2021 20:09:40 +0700 Subject: [PATCH 07/42] Add missing f128 compiler-rt shim --- lib/std/special/compiler_rt.zig | 10 +++++++ lib/std/special/compiler_rt/sparc.zig | 42 ++++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/lib/std/special/compiler_rt.zig b/lib/std/special/compiler_rt.zig index b0cf37553d..d5500d516c 100644 --- a/lib/std/special/compiler_rt.zig +++ b/lib/std/special/compiler_rt.zig @@ -294,7 +294,17 @@ comptime { @export(@import("compiler_rt/sparc.zig")._Qp_fgt, .{ .name = "_Qp_fgt", .linkage = linkage }); @export(@import("compiler_rt/sparc.zig")._Qp_fge, .{ .name = "_Qp_fge", .linkage = linkage }); + @export(@import("compiler_rt/sparc.zig")._Qp_itoq, .{ .name = "_Qp_itoq", .linkage = linkage }); + @export(@import("compiler_rt/sparc.zig")._Qp_uitoq, .{ .name = "_Qp_uitoq", .linkage = linkage }); + @export(@import("compiler_rt/sparc.zig")._Qp_xtoq, .{ .name = "_Qp_xtoq", .linkage = linkage }); + @export(@import("compiler_rt/sparc.zig")._Qp_uxtoq, .{ .name = "_Qp_uxtoq", .linkage = linkage }); + @export(@import("compiler_rt/sparc.zig")._Qp_stoq, .{ .name = "_Qp_stoq", .linkage = linkage }); @export(@import("compiler_rt/sparc.zig")._Qp_dtoq, .{ .name = "_Qp_dtoq", .linkage = linkage }); + @export(@import("compiler_rt/sparc.zig")._Qp_qtoi, .{ .name = "_Qp_qtoi", .linkage = linkage }); + @export(@import("compiler_rt/sparc.zig")._Qp_qtoui, .{ .name = "_Qp_qtoui", .linkage = linkage }); + @export(@import("compiler_rt/sparc.zig")._Qp_qtox, .{ .name = "_Qp_qtox", .linkage = linkage }); + @export(@import("compiler_rt/sparc.zig")._Qp_qtoux, .{ .name = "_Qp_qtoux", .linkage = linkage }); + @export(@import("compiler_rt/sparc.zig")._Qp_qtos, .{ .name = "_Qp_qtos", .linkage = linkage }); @export(@import("compiler_rt/sparc.zig")._Qp_qtod, .{ .name = "_Qp_qtod", .linkage = linkage }); } diff --git a/lib/std/special/compiler_rt/sparc.zig b/lib/std/special/compiler_rt/sparc.zig index e66bb25886..87eed4c967 100644 --- a/lib/std/special/compiler_rt/sparc.zig +++ b/lib/std/special/compiler_rt/sparc.zig @@ -68,12 +68,52 @@ pub fn _Qp_fge(a: *f128, b: *f128) callconv(.C) bool { return cmp == @enumToInt(FCMP.Greater) or cmp == @enumToInt(FCMP.Equal); } -// Casting +// Conversion + +pub fn _Qp_itoq(c: *f128, a: i32) callconv(.C) void { + c.* = @import("floatsiXf.zig").__floatsitf(a); +} + +pub fn _Qp_uitoq(c: *f128, a: u32) callconv(.C) void { + c.* = @import("floatunsitf.zig").__floatunsitf(a); +} + +pub fn _Qp_xtoq(c: *f128, a: i64) callconv(.C) void { + c.* = @import("floatditf.zig").__floatditf(a); +} + +pub fn _Qp_uxtoq(c: *f128, a: u64) callconv(.C) void { + c.* = @import("floatunditf.zig").__floatunditf(a); +} + +pub fn _Qp_stoq(c: *f128, a: f32) callconv(.C) void { + c.* = @import("extendXfYf2.zig").__extendsftf2(a); +} pub fn _Qp_dtoq(c: *f128, a: f64) callconv(.C) void { c.* = @import("extendXfYf2.zig").__extenddftf2(a); } +pub fn _Qp_qtoi(a: *f128) callconv(.C) i32 { + return @import("fixtfsi.zig").__fixtfsi(a.*); +} + +pub fn _Qp_qtoui(a: *f128) callconv(.C) u32 { + return @import("fixunstfsi.zig").__fixunstfsi(a.*); +} + +pub fn _Qp_qtox(a: *f128) callconv(.C) i64 { + return @import("fixtfdi.zig").__fixtfdi(a.*); +} + +pub fn _Qp_qtoux(a: *f128) callconv(.C) u64 { + return @import("fixunstfdi.zig").__fixunstfdi(a.*); +} + +pub fn _Qp_qtos(a: *f128) callconv(.C) f32 { + return @import("truncXfYf2.zig").__trunctfsf2(a.*); +} + pub fn _Qp_qtod(a: *f128) callconv(.C) f64 { return @import("truncXfYf2.zig").__trunctfdf2(a.*); } From 90a54f06e507e11ccf07c1378fe4a12b002ec2bd Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 13 May 2021 06:18:51 +0700 Subject: [PATCH 08/42] Add a comment to explain the alignment directive --- lib/std/fs.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/std/fs.zig b/lib/std/fs.zig index 1b77c6657a..02df5fae0f 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -501,6 +501,8 @@ pub const Dir = struct { }, .linux => struct { dir: Dir, + // The if guard is solely there to prevent compile errors from missing `os.linux.dirent64` + // definition when compiling for other OSes. It doesn't do anything when compiling for Linux. buf: [8192]u8 align(if (builtin.os.tag != .linux) 1 else @alignOf(os.linux.dirent64)), index: usize, end_index: usize, From 459c9f05359e3405f39e4c954c62c61a493e49ae Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Wed, 12 May 2021 22:39:10 +0200 Subject: [PATCH 09/42] stage2: fix build on OpenBSD/NetBSD Apparently these systems do not provide libdl or librt. --- src/link/Elf.zig | 10 +--------- src/target.zig | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 91ee6f3206..9a0aeabe6c 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1650,15 +1650,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { if (self.base.options.libc_installation != null) { const needs_grouping = self.base.options.link_mode == .Static; if (needs_grouping) try argv.append("--start-group"); - // This matches the order of glibc.libs - try argv.appendSlice(&[_][]const u8{ - "-lm", - "-lpthread", - "-lc", - "-ldl", - "-lrt", - "-lutil", - }); + try argv.appendSlice(target_util.libcFullLinkFlags(target)); if (needs_grouping) try argv.append("--end-group"); } else if (target.isGnuLibC()) { try argv.append(comp.libunwind_static_lib.?.full_object_path); diff --git a/src/target.zig b/src/target.zig index 1e31f99dc1..c2018db012 100644 --- a/src/target.zig +++ b/src/target.zig @@ -374,3 +374,24 @@ pub fn hasRedZone(target: std.Target) bool { else => false, }; } + +pub fn libcFullLinkFlags(target: std.Target) []const []const u8 { + // The linking order of these is significant and should match the order other + // c compilers such as gcc or clang use. + return switch (target.os.tag) { + .netbsd, .openbsd => &[_][]const u8{ + "-lm", + "-lpthread", + "-lc", + "-lutil", + }, + else => &[_][]const u8{ + "-lm", + "-lpthread", + "-lc", + "-ldl", + "-lrt", + "-lutil", + }, + }; +} From 4b59f564344598b4d1f1a51839a4dc5cbf357012 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 12 May 2021 22:25:20 -0700 Subject: [PATCH 10/42] stage2: build and provide libunwind when compiling for native libc 5ac91794cce8bd53916a378815be01e4365d53d9 made Zig link against the system libc when targeting the native C ABI. However this made it stop putting libunwind.a on the linker line when it needed to sometimes, causing undefined symbols when linking against C++ code. --- src/Compilation.zig | 1 - src/link/Elf.zig | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 6a9f75acf5..b14e598f71 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -3023,7 +3023,6 @@ fn wantBuildLibUnwindFromSource(comp: *Compilation) bool { .Exe => true, }; return comp.bin_file.options.link_libc and is_exe_or_dyn_lib and - comp.bin_file.options.libc_installation == null and target_util.libcNeedsLibUnwind(comp.getTarget()); } diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 9a0aeabe6c..fbbe40022a 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1648,6 +1648,9 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { // libc dep if (self.base.options.link_libc) { if (self.base.options.libc_installation != null) { + if (target_util.libcNeedsLibUnwind(target)) { + try argv.append(comp.libunwind_static_lib.?.full_object_path); + } const needs_grouping = self.base.options.link_mode == .Static; if (needs_grouping) try argv.append("--start-group"); try argv.appendSlice(target_util.libcFullLinkFlags(target)); From 6c3050e3c3eee19a9de2c912dc653062e5597b67 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 12 May 2021 20:17:43 +0200 Subject: [PATCH 11/42] macos: link static libc++ when building stage2 With this change, it is now possible to use zig-bootstrap as the initial C/C++ compiler, and then build stage2 with the resultant stage1 without the need to link any system libs! --- build.zig | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/build.zig b/build.zig index a0bc6a89c3..d31e218056 100644 --- a/build.zig +++ b/build.zig @@ -344,20 +344,7 @@ fn addCmakeCfgOptionsToExe( try addCxxKnownPath(b, cfg, exe, "libc++.a", null, need_cpp_includes); try addCxxKnownPath(b, cfg, exe, "libc++abi.a", null, need_cpp_includes); } else if (exe.target.isDarwin()) { - if (addCxxKnownPath(b, cfg, exe, "libgcc_eh.a", "", need_cpp_includes)) { - // Compiler is GCC. - try addCxxKnownPath(b, cfg, exe, "libstdc++.a", null, need_cpp_includes); - exe.linkSystemLibrary("pthread"); - // TODO LLD cannot perform this link. - // Set ZIG_SYSTEM_LINKER_HACK env var to use system linker ld instead. - // See https://github.com/ziglang/zig/issues/1535 - } else |err| switch (err) { - error.RequiredLibraryNotFound => { - // System compiler, not gcc. - exe.linkSystemLibrary("c++"); - }, - else => |e| return e, - } + exe.linkSystemLibrary("c++"); } if (cfg.dia_guids_lib.len != 0) { From e902c19c0e2bf7f0e9bc83b2c58d19ec024d56db Mon Sep 17 00:00:00 2001 From: Matthew Borkowski Date: Thu, 13 May 2021 05:11:28 -0400 Subject: [PATCH 12/42] std/json: Fix premature closing brace being considered valid JSON return error from StreamingParser when reading closing brace when expecting value for an object key --- lib/std/json.zig | 9 ++++++++- lib/std/json/test.zig | 6 ++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/std/json.zig b/lib/std/json.zig index ed26c13229..7515f8682d 100644 --- a/lib/std/json.zig +++ b/lib/std/json.zig @@ -623,7 +623,7 @@ pub const StreamingParser = struct { .ObjectSeparator => switch (c) { ':' => { - p.state = .ValueBegin; + p.state = .ValueBeginNoClosing; p.after_string_state = .ValueEnd; }, 0x09, 0x0A, 0x0D, 0x20 => { @@ -1205,6 +1205,13 @@ test "json.token mismatched close" { try testing.expectError(error.UnexpectedClosingBrace, p.next()); } +test "json.token premature object close" { + var p = TokenStream.init("{ \"key\": }"); + try checkNext(&p, .ObjectBegin); + try checkNext(&p, .String); + try testing.expectError(error.InvalidValueBegin, p.next()); +} + /// Validate a JSON string. This does not limit number precision so a decoder may not necessarily /// be able to decode the string even if this returns true. pub fn validate(s: []const u8) bool { diff --git a/lib/std/json/test.zig b/lib/std/json/test.zig index e37ba72113..027f6acaca 100644 --- a/lib/std/json/test.zig +++ b/lib/std/json/test.zig @@ -76,6 +76,12 @@ test "y_trailing_comma_after_empty" { ); } +test "n_object_closed_missing_value" { + try err( + \\{"a":} + ); +} + //////////////////////////////////////////////////////////////////////////////////////////////////// test "y_array_arraysWithSpaces" { From cadb84b3ac891a99f7740be056d6bf6063b266f1 Mon Sep 17 00:00:00 2001 From: Matthew Borkowski Date: Thu, 13 May 2021 09:43:55 -0400 Subject: [PATCH 13/42] fix duplicate_field_behavior UseFirst in json.zig --- lib/std/json.zig | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/std/json.zig b/lib/std/json.zig index 7515f8682d..8608990df6 100644 --- a/lib/std/json.zig +++ b/lib/std/json.zig @@ -1573,7 +1573,7 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options: // .UseLast => {}, // } if (options.duplicate_field_behavior == .UseFirst) { - break; + // do nothing, check of fields_seen[i] below will parse value without overwriting field } else if (options.duplicate_field_behavior == .Error) { return error.DuplicateJSONField; } else if (options.duplicate_field_behavior == .UseLast) { @@ -1586,7 +1586,11 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options: return error.UnexpectedValue; } } else { - @field(r, field.name) = try parse(field.field_type, tokens, options); + if (fields_seen[i]) { + parseFree(field.field_type, try parse(field.field_type, tokens, options), options); + } else { + @field(r, field.name) = try parse(field.field_type, tokens, options); + } } fields_seen[i] = true; found = true; @@ -2013,6 +2017,9 @@ test "parse into struct with duplicate field" { const T2 = struct { a: f64 }; try testing.expectEqual(T2{ .a = 0.25 }, try parse(T2, &TokenStream.init(str), options)); + try testing.expectEqual(T2{ .a = 1.0 }, try parse(T2, &TokenStream.init(str), + .{ .duplicate_field_behavior = .UseFirst } + )); } /// A non-stream JSON parser which constructs a tree of Value's. From 402264ab0e16dfdc98e861a440df9ef5db35c402 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 13 May 2021 11:03:44 +0200 Subject: [PATCH 14/42] Add experimental Darling support for cross testing macOS * for cross testing stage2 tests, we use `darling shell` command since the path to the tested binary is relative to cwd * for the `zig test` command, we simply use `darling` since the path to the binary is absolute --- build.zig | 72 +++++++++++++++++++++++++++++++++--- lib/std/build.zig | 8 ++++ lib/std/zig/cross_target.zig | 10 +++++ src/test.zig | 11 ++++++ test/tests.zig | 2 + 5 files changed, 98 insertions(+), 5 deletions(-) diff --git a/build.zig b/build.zig index d31e218056..d3e02bba5e 100644 --- a/build.zig +++ b/build.zig @@ -218,6 +218,7 @@ pub fn build(b: *Builder) !void { const is_wine_enabled = b.option(bool, "enable-wine", "Use Wine to run cross compiled Windows tests") orelse false; const is_qemu_enabled = b.option(bool, "enable-qemu", "Use QEMU to run cross compiled foreign architecture tests") orelse false; const is_wasmtime_enabled = b.option(bool, "enable-wasmtime", "Use Wasmtime to enable and run WASI libstd tests") orelse false; + const is_darling_enabled = b.option(bool, "enable-darling", "[Experimental] Use Darling to run cross compiled macOS tests") orelse false; const glibc_multi_dir = b.option([]const u8, "enable-foreign-glibc", "Provide directory with glibc installations to run cross compiled tests that link glibc"); test_stage2.addBuildOption(bool, "skip_non_native", skip_non_native); @@ -227,6 +228,7 @@ pub fn build(b: *Builder) !void { test_stage2.addBuildOption(bool, "enable_qemu", is_qemu_enabled); test_stage2.addBuildOption(bool, "enable_wine", is_wine_enabled); test_stage2.addBuildOption(bool, "enable_wasmtime", is_wasmtime_enabled); + test_stage2.addBuildOption(bool, "enable_darling", is_darling_enabled); test_stage2.addBuildOption(?[]const u8, "glibc_multi_install_dir", glibc_multi_dir); test_stage2.addBuildOption([]const u8, "version", version); @@ -261,11 +263,56 @@ pub fn build(b: *Builder) !void { const fmt_step = b.step("test-fmt", "Run zig fmt against build.zig to make sure it works"); fmt_step.dependOn(&fmt_build_zig.step); - // TODO for the moment, skip wasm32-wasi until bugs are sorted out. - toolchain_step.dependOn(tests.addPkgTests(b, test_filter, "test/stage1/behavior.zig", "behavior", "Run the behavior tests", modes, false, skip_non_native, skip_libc, is_wine_enabled, is_qemu_enabled, is_wasmtime_enabled, glibc_multi_dir)); + toolchain_step.dependOn(tests.addPkgTests( + b, + test_filter, + "test/stage1/behavior.zig", + "behavior", + "Run the behavior tests", + modes, + false, + skip_non_native, + skip_libc, + is_wine_enabled, + is_qemu_enabled, + is_wasmtime_enabled, + is_darling_enabled, + glibc_multi_dir, + )); - toolchain_step.dependOn(tests.addPkgTests(b, test_filter, "lib/std/special/compiler_rt.zig", "compiler-rt", "Run the compiler_rt tests", modes, true, skip_non_native, true, is_wine_enabled, is_qemu_enabled, is_wasmtime_enabled, glibc_multi_dir)); - toolchain_step.dependOn(tests.addPkgTests(b, test_filter, "lib/std/special/c.zig", "minilibc", "Run the mini libc tests", modes, true, skip_non_native, true, is_wine_enabled, is_qemu_enabled, is_wasmtime_enabled, glibc_multi_dir)); + toolchain_step.dependOn(tests.addPkgTests( + b, + test_filter, + "lib/std/special/compiler_rt.zig", + "compiler-rt", + "Run the compiler_rt tests", + modes, + true, + skip_non_native, + true, + is_wine_enabled, + is_qemu_enabled, + is_wasmtime_enabled, + is_darling_enabled, + glibc_multi_dir, + )); + + toolchain_step.dependOn(tests.addPkgTests( + b, + test_filter, + "lib/std/special/c.zig", + "minilibc", + "Run the mini libc tests", + modes, + true, + skip_non_native, + true, + is_wine_enabled, + is_qemu_enabled, + is_wasmtime_enabled, + is_darling_enabled, + glibc_multi_dir, + )); toolchain_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes)); toolchain_step.dependOn(tests.addStandaloneTests(b, test_filter, modes)); @@ -283,7 +330,22 @@ pub fn build(b: *Builder) !void { toolchain_step.dependOn(tests.addCompileErrorTests(b, test_filter, modes)); } - const std_step = tests.addPkgTests(b, test_filter, "lib/std/std.zig", "std", "Run the standard library tests", modes, false, skip_non_native, skip_libc, is_wine_enabled, is_qemu_enabled, is_wasmtime_enabled, glibc_multi_dir); + const std_step = tests.addPkgTests( + b, + test_filter, + "lib/std/std.zig", + "std", + "Run the standard library tests", + modes, + false, + skip_non_native, + skip_libc, + is_wine_enabled, + is_qemu_enabled, + is_wasmtime_enabled, + is_darling_enabled, + glibc_multi_dir, + ); const test_step = b.step("test", "Run all the tests"); test_step.dependOn(toolchain_step); diff --git a/lib/std/build.zig b/lib/std/build.zig index 9d12a4de51..808f3374d8 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1398,6 +1398,9 @@ pub const LibExeObjStep = struct { /// Uses system Wasmtime installation to run cross compiled wasm/wasi build artifacts. enable_wasmtime: bool = false, + /// Experimental. Uses system Darling installation to run cross compiled macOS build artifacts. + enable_darling: bool = false, + /// After following the steps in https://github.com/ziglang/zig/wiki/Updating-libc#glibc, /// this will be the directory $glibc-build-dir/install/glibcs /// Given the example of the aarch64 target, this is the directory @@ -2514,6 +2517,11 @@ pub const LibExeObjStep = struct { try zig_args.append("--dir=."); try zig_args.append("--test-cmd-bin"); }, + .darling => |bin_name| if (self.enable_darling) { + try zig_args.append("--test-cmd"); + try zig_args.append(bin_name); + try zig_args.append("--test-cmd-bin"); + }, } for (self.packages.items) |pkg| { diff --git a/lib/std/zig/cross_target.zig b/lib/std/zig/cross_target.zig index bf62a49ef6..e724be17ae 100644 --- a/lib/std/zig/cross_target.zig +++ b/lib/std/zig/cross_target.zig @@ -606,6 +606,7 @@ pub const CrossTarget = struct { qemu: []const u8, wine: []const u8, wasmtime: []const u8, + darling: []const u8, unavailable, }; @@ -667,6 +668,15 @@ pub const CrossTarget = struct { 32 => return Executor{ .wasmtime = "wasmtime" }, else => return .unavailable, }, + .macos => { + // TODO loosen this check once upstream adds QEMU-based emulation + // layer for non-host architectures: + // https://github.com/darlinghq/darling/issues/863 + if (cpu_arch != Target.current.cpu.arch) { + return .unavailable; + } + return Executor{ .darling = "darling" }; + }, else => return .unavailable, } } diff --git a/src/test.zig b/src/test.zig index f1cf543252..5a8adca3c7 100644 --- a/src/test.zig +++ b/src/test.zig @@ -9,6 +9,7 @@ const build_options = @import("build_options"); const enable_qemu: bool = build_options.enable_qemu; const enable_wine: bool = build_options.enable_wine; const enable_wasmtime: bool = build_options.enable_wasmtime; +const enable_darling: bool = build_options.enable_darling; const glibc_multi_install_dir: ?[]const u8 = build_options.glibc_multi_install_dir; const ThreadPool = @import("ThreadPool.zig"); const CrossTarget = std.zig.CrossTarget; @@ -899,6 +900,16 @@ pub const TestContext = struct { } else { return; // wasmtime not available; pass test. }, + + .darling => |darling_bin_name| if (enable_darling) { + try argv.append(darling_bin_name); + // Since we use relative to cwd here, we invoke darling with + // "shell" subcommand. + try argv.append("shell"); + try argv.append(exe_path); + } else { + return; // Darling not available; pass test. + }, } try comp.makeBinFileExecutable(); diff --git a/test/tests.zig b/test/tests.zig index 8bd1f3528e..eca81b591f 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -503,6 +503,7 @@ pub fn addPkgTests( is_wine_enabled: bool, is_qemu_enabled: bool, is_wasmtime_enabled: bool, + is_darling_enabled: bool, glibc_dir: ?[]const u8, ) *build.Step { const step = b.step(b.fmt("test-{s}", .{name}), desc); @@ -564,6 +565,7 @@ pub fn addPkgTests( these_tests.enable_wine = is_wine_enabled; these_tests.enable_qemu = is_qemu_enabled; these_tests.enable_wasmtime = is_wasmtime_enabled; + these_tests.enable_darling = is_darling_enabled; these_tests.glibc_multi_install_dir = glibc_dir; these_tests.addIncludeDir("test"); From 1b222cb1ff6b95be2f8b03a0ae872efcc7873aa6 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 13 May 2021 18:34:52 +0200 Subject: [PATCH 15/42] start.zig: export main with strong linkage Unmarks `_main` as weak symbol making it global (the entire linked program) in scope. --- lib/std/start.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/start.zig b/lib/std/start.zig index e1e331a682..e3983db8b4 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -40,7 +40,7 @@ comptime { } else if (builtin.output_mode == .Exe or @hasDecl(root, "main")) { if (builtin.link_libc and @hasDecl(root, "main")) { if (@typeInfo(@TypeOf(root.main)).Fn.calling_convention != .C) { - @export(main, .{ .name = "main", .linkage = .Weak }); + @export(main, .{ .name = "main" }); } } else if (builtin.os.tag == .windows) { if (!@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and From 76a259799d5bac3effabd1df44c0dec9e4fa16d4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 13 May 2021 11:52:58 -0700 Subject: [PATCH 16/42] stage2: only build and link libunwind when linking libc++ --- src/Compilation.zig | 1 + src/link/Elf.zig | 10 +++------- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index b14e598f71..0f04f02b7d 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -3023,6 +3023,7 @@ fn wantBuildLibUnwindFromSource(comp: *Compilation) bool { .Exe => true, }; return comp.bin_file.options.link_libc and is_exe_or_dyn_lib and + comp.bin_file.options.link_libcpp and target_util.libcNeedsLibUnwind(comp.getTarget()); } diff --git a/src/link/Elf.zig b/src/link/Elf.zig index fbbe40022a..b94e562eb7 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1643,20 +1643,19 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { if (self.base.options.link_libcpp) { try argv.append(comp.libcxxabi_static_lib.?.full_object_path); try argv.append(comp.libcxx_static_lib.?.full_object_path); + if (target_util.libcNeedsLibUnwind(target)) { + try argv.append(comp.libunwind_static_lib.?.full_object_path); + } } // libc dep if (self.base.options.link_libc) { if (self.base.options.libc_installation != null) { - if (target_util.libcNeedsLibUnwind(target)) { - try argv.append(comp.libunwind_static_lib.?.full_object_path); - } const needs_grouping = self.base.options.link_mode == .Static; if (needs_grouping) try argv.append("--start-group"); try argv.appendSlice(target_util.libcFullLinkFlags(target)); if (needs_grouping) try argv.append("--end-group"); } else if (target.isGnuLibC()) { - try argv.append(comp.libunwind_static_lib.?.full_object_path); for (glibc.libs) |lib| { const lib_path = try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{ comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover, @@ -1665,13 +1664,10 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { } try argv.append(try comp.get_libc_crt_file(arena, "libc_nonshared.a")); } else if (target.isMusl()) { - try argv.append(comp.libunwind_static_lib.?.full_object_path); try argv.append(try comp.get_libc_crt_file(arena, switch (self.base.options.link_mode) { .Static => "libc.a", .Dynamic => "libc.so", })); - } else if (self.base.options.link_libcpp) { - try argv.append(comp.libunwind_static_lib.?.full_object_path); } else { unreachable; // Compiler was supposed to emit an error for not being able to provide libc. } From 826179bff40fdbd8c3b11138897fcfbb3367def8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 13 May 2021 15:59:24 -0700 Subject: [PATCH 17/42] stage2: -lunwind is handled specially * `-lc++` now implies `-lc`. * `-lunwind` is now pulled out into a separate `link_libunwind` flag in the frontend driver code. This allows a project to request zig to provide libunwind even if the default situation that causes it to be implicitly added, is not active. * build.zig: ask for -lunwind when building the self-hosted compiler on Linux. Otherwise we get linker errors with unresolved symbols to libunwind. --- build.zig | 3 +-- src/Compilation.zig | 17 ++++++++++------- src/link.zig | 1 + src/link/Elf.zig | 8 +++++--- src/main.zig | 7 +++++++ 5 files changed, 24 insertions(+), 12 deletions(-) diff --git a/build.zig b/build.zig index d3e02bba5e..39c5257fae 100644 --- a/build.zig +++ b/build.zig @@ -397,8 +397,7 @@ fn addCmakeCfgOptionsToExe( }, else => |e| return e, }; - - exe.linkSystemLibrary("pthread"); + exe.linkSystemLibrary("unwind"); } else if (exe.target.isFreeBSD()) { try addCxxKnownPath(b, cfg, exe, "libc++.a", null, need_cpp_includes); exe.linkSystemLibrary("pthread"); diff --git a/src/Compilation.zig b/src/Compilation.zig index 0f04f02b7d..140e160b69 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -544,6 +544,7 @@ pub const InitOptions = struct { system_libs: []const []const u8 = &[0][]const u8{}, link_libc: bool = false, link_libcpp: bool = false, + link_libunwind: bool = false, want_pic: ?bool = null, /// This means that if the output mode is an executable it will be a /// Position Independent Executable. If the output mode is not an @@ -791,8 +792,13 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { }; const tsan = options.want_tsan orelse false; + // TSAN is implemented in C++ so it requires linking libc++. + const link_libcpp = options.link_libcpp or tsan; + const link_libc = link_libcpp or options.link_libc or + target_util.osRequiresLibC(options.target); - const link_libc = options.link_libc or target_util.osRequiresLibC(options.target) or tsan; + const link_libunwind = options.link_libunwind or + (link_libcpp and target_util.libcNeedsLibUnwind(options.target)); const must_dynamic_link = dl: { if (target_util.cannotDynamicLink(options.target)) @@ -878,9 +884,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { break :pic explicit; } else pie or must_pic; - // TSAN is implemented in C++ so it requires linking libc++. - const link_libcpp = options.link_libcpp or tsan; - // Make a decision on whether to use Clang for translate-c and compiling C files. const use_clang = if (options.use_clang) |explicit| explicit else blk: { if (build_options.have_llvm) { @@ -973,6 +976,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { cache.hash.add(strip); cache.hash.add(link_libc); cache.hash.add(link_libcpp); + cache.hash.add(link_libunwind); cache.hash.add(options.output_mode); cache.hash.add(options.machine_code_model); cache.hash.addOptionalEmitLoc(options.emit_bin); @@ -1157,6 +1161,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { .system_linker_hack = darwin_options.system_linker_hack, .link_libc = link_libc, .link_libcpp = link_libcpp, + .link_libunwind = link_libunwind, .objects = options.link_objects, .frameworks = options.frameworks, .framework_dirs = options.framework_dirs, @@ -3022,9 +3027,7 @@ fn wantBuildLibUnwindFromSource(comp: *Compilation) bool { .Lib => comp.bin_file.options.link_mode == .Dynamic, .Exe => true, }; - return comp.bin_file.options.link_libc and is_exe_or_dyn_lib and - comp.bin_file.options.link_libcpp and - target_util.libcNeedsLibUnwind(comp.getTarget()); + return is_exe_or_dyn_lib and comp.bin_file.options.link_libunwind; } fn updateBuiltinZigFile(comp: *Compilation, mod: *Module) Allocator.Error!void { diff --git a/src/link.zig b/src/link.zig index c0f9a50b2b..b30db06148 100644 --- a/src/link.zig +++ b/src/link.zig @@ -63,6 +63,7 @@ pub const Options = struct { system_linker_hack: bool, link_libc: bool, link_libcpp: bool, + link_libunwind: bool, function_sections: bool, eh_frame_hdr: bool, emit_relocs: bool, diff --git a/src/link/Elf.zig b/src/link/Elf.zig index b94e562eb7..0b2d800bd7 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1643,9 +1643,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { if (self.base.options.link_libcpp) { try argv.append(comp.libcxxabi_static_lib.?.full_object_path); try argv.append(comp.libcxx_static_lib.?.full_object_path); - if (target_util.libcNeedsLibUnwind(target)) { - try argv.append(comp.libunwind_static_lib.?.full_object_path); - } + } + + // libunwind dep + if (self.base.options.link_libunwind) { + try argv.append(comp.libunwind_static_lib.?.full_object_path); } // libc dep diff --git a/src/main.zig b/src/main.zig index bd57c1f14f..d435fb5ea4 100644 --- a/src/main.zig +++ b/src/main.zig @@ -520,6 +520,7 @@ fn buildOutputType( var ensure_libcpp_on_non_freestanding = false; var link_libc = false; var link_libcpp = false; + var link_libunwind = false; var want_native_include_dirs = false; var enable_cache: ?bool = null; var want_pic: ?bool = null; @@ -1532,6 +1533,11 @@ fn buildOutputType( _ = system_libs.orderedRemove(i); continue; } + if (mem.eql(u8, lib_name, "unwind")) { + link_libunwind = true; + _ = system_libs.orderedRemove(i); + continue; + } if (std.fs.path.isAbsolute(lib_name)) { fatal("cannot use absolute path as a system library: {s}", .{lib_name}); } @@ -1847,6 +1853,7 @@ fn buildOutputType( .system_libs = system_libs.items, .link_libc = link_libc, .link_libcpp = link_libcpp, + .link_libunwind = link_libunwind, .want_pic = want_pic, .want_pie = want_pie, .want_lto = want_lto, From 11fd2aa76770a0bef87a6ce304e1893b490bba83 Mon Sep 17 00:00:00 2001 From: Matthew Borkowski Date: Fri, 14 May 2021 01:51:39 -0400 Subject: [PATCH 18/42] fix logic for duplicate comptime fields and avoid freeing comptime fields in parseFree and parseInternal --- lib/std/json.zig | 57 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/lib/std/json.zig b/lib/std/json.zig index 8608990df6..a46ef274db 100644 --- a/lib/std/json.zig +++ b/lib/std/json.zig @@ -1573,11 +1573,16 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options: // .UseLast => {}, // } if (options.duplicate_field_behavior == .UseFirst) { - // do nothing, check of fields_seen[i] below will parse value without overwriting field + // unconditonally ignore value. for comptime fields, this skips check against default_value + parseFree(field.field_type, try parse(field.field_type, tokens, options), options); + found = true; + break; } else if (options.duplicate_field_behavior == .Error) { return error.DuplicateJSONField; } else if (options.duplicate_field_behavior == .UseLast) { - parseFree(field.field_type, @field(r, field.name), options); + if (!field.is_comptime) { + parseFree(field.field_type, @field(r, field.name), options); + } fields_seen[i] = false; } } @@ -1586,11 +1591,7 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options: return error.UnexpectedValue; } } else { - if (fields_seen[i]) { - parseFree(field.field_type, try parse(field.field_type, tokens, options), options); - } else { - @field(r, field.name) = try parse(field.field_type, tokens, options); - } + @field(r, field.name) = try parse(field.field_type, tokens, options); } fields_seen[i] = true; found = true; @@ -1735,7 +1736,9 @@ pub fn parseFree(comptime T: type, value: T, options: ParseOptions) void { }, .Struct => |structInfo| { inline for (structInfo.fields) |field| { - parseFree(field.field_type, @field(value, field.name), options); + if (!field.is_comptime) { + parseFree(field.field_type, @field(value, field.name), options); + } } }, .Array => |arrayInfo| { @@ -1912,14 +1915,19 @@ test "parse with comptime field" { }, }; - const r = try std.json.parse(T, &std.json.TokenStream.init( + const options = ParseOptions{ + .allocator = std.testing.allocator, + }; + + const r = try parse(T, &TokenStream.init( \\{ \\ "kind": "float", \\ "b": 1.0 \\} - ), .{ - .allocator = std.testing.allocator, - }); + ), options); + + // check that parseFree doesn't try to free comptime fields + parseFree(T, r, options); } } @@ -2006,20 +2014,33 @@ test "parse into struct with duplicate field" { const ballast = try testing.allocator.alloc(u64, 1); defer testing.allocator.free(ballast); - const options = ParseOptions{ + const options_first = ParseOptions{ + .allocator = testing.allocator, + .duplicate_field_behavior = .UseFirst + }; + + const options_last = ParseOptions{ .allocator = testing.allocator, .duplicate_field_behavior = .UseLast, }; + const str = "{ \"a\": 1, \"a\": 0.25 }"; const T1 = struct { a: *u64 }; - try testing.expectError(error.UnexpectedToken, parse(T1, &TokenStream.init(str), options)); + // both .UseFirst and .UseLast should fail because second "a" value isn't a u64 + try testing.expectError(error.UnexpectedToken, parse(T1, &TokenStream.init(str), options_first)); + try testing.expectError(error.UnexpectedToken, parse(T1, &TokenStream.init(str), options_last)); const T2 = struct { a: f64 }; - try testing.expectEqual(T2{ .a = 0.25 }, try parse(T2, &TokenStream.init(str), options)); - try testing.expectEqual(T2{ .a = 1.0 }, try parse(T2, &TokenStream.init(str), - .{ .duplicate_field_behavior = .UseFirst } - )); + try testing.expectEqual(T2{ .a = 1.0 }, try parse(T2, &TokenStream.init(str), options_first)); + try testing.expectEqual(T2{ .a = 0.25 }, try parse(T2, &TokenStream.init(str), options_last)); + + const T3 = struct { comptime a: f64 = 1.0 }; + // .UseFirst should succeed because second "a" value is unconditionally ignored (even though != 1.0) + const t3 = T3{ .a = 1.0 }; + try testing.expectEqual(t3, try parse(T3, &TokenStream.init(str), options_first)); + // .UseLast should fail because second "a" value is 0.25 which is not equal to default value of 1.0 + try testing.expectError(error.UnexpectedValue, parse(T3, &TokenStream.init(str), options_last)); } /// A non-stream JSON parser which constructs a tree of Value's. From 8eea5eddf773a8d1f9f883e71d00409e454ee0bd Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 14 May 2021 10:05:01 +0200 Subject: [PATCH 19/42] macho: fix bug with symbol growth and realloc --- src/link/MachO.zig | 8 +++++++- test/stage2/darwin.zig | 29 ++++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 2ae9575b7e..d8518c4894 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -1193,7 +1193,9 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { const need_realloc = code.len > capacity or !mem.isAlignedGeneric(u64, symbol.n_value, required_alignment); if (need_realloc) { const vaddr = try self.growTextBlock(&decl.link.macho, code.len, required_alignment); - log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl.name, symbol.n_value, vaddr }); + + log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ decl.name, symbol.n_value, vaddr }); + if (vaddr != symbol.n_value) { log.debug(" (writing new offset table entry)", .{}); self.offset_table.items[decl.link.macho.offset_table_index] = .{ @@ -1203,6 +1205,8 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { }; try self.writeOffsetTableEntry(decl.link.macho.offset_table_index); } + + symbol.n_value = vaddr; } else if (code.len < decl.link.macho.size) { self.shrinkTextBlock(&decl.link.macho, code.len); } @@ -1219,7 +1223,9 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { const decl_name = mem.spanZ(decl.name); const name_str_index = try self.makeString(decl_name); const addr = try self.allocateTextBlock(&decl.link.macho, code.len, required_alignment); + log.debug("allocated text block for {s} at 0x{x}", .{ decl_name, addr }); + errdefer self.freeTextBlock(&decl.link.macho); symbol.* = .{ diff --git a/test/stage2/darwin.zig b/test/stage2/darwin.zig index 232bc42d25..f8d82391cc 100644 --- a/test/stage2/darwin.zig +++ b/test/stage2/darwin.zig @@ -41,7 +41,34 @@ pub fn addCases(ctx: *TestContext) !void { "Hello, World!\n", ); - // Now change the message only + // Print it 4 times and force growth and realloc. + case.addCompareOutput( + \\extern "c" fn write(usize, usize, usize) usize; + \\extern "c" fn exit(usize) noreturn; + \\ + \\export fn _start() noreturn { + \\ print(); + \\ print(); + \\ print(); + \\ print(); + \\ + \\ exit(0); + \\} + \\ + \\fn print() void { + \\ const msg = @ptrToInt("Hello, World!\n"); + \\ const len = 14; + \\ _ = write(1, msg, len); + \\} + , + \\Hello, World! + \\Hello, World! + \\Hello, World! + \\Hello, World! + \\ + ); + + // Print it once, and change the message. case.addCompareOutput( \\extern "c" fn write(usize, usize, usize) usize; \\extern "c" fn exit(usize) noreturn; From 00ebbe6df2249ba8201c0e5472d95022bf73e782 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 14 May 2021 11:46:50 +0200 Subject: [PATCH 20/42] macho: require _main as global export in self-hosted Clean up type and description flags generation for exports in self-hosted MachO backend. --- src/link/MachO.zig | 33 +++++++++++++++++++++++++-------- test/stage2/darwin.zig | 14 +++++++------- 2 files changed, 32 insertions(+), 15 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index d8518c4894..5a0deb67bc 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -1369,15 +1369,32 @@ pub fn updateDeclExports( continue; } } - const n_desc = switch (exp.options.linkage) { - .Internal => macho.REFERENCE_FLAG_PRIVATE_DEFINED, - .Strong => blk: { - if (mem.eql(u8, exp.options.name, "_start")) { + + var n_type: u8 = macho.N_SECT | macho.N_EXT; + var n_desc: u16 = 0; + + switch (exp.options.linkage) { + .Internal => { + // Symbol should be hidden, or in MachO lingo, private extern. + // We should also mark the symbol as Weak: n_desc == N_WEAK_DEF. + // TODO work out when to add N_WEAK_REF. + n_type |= macho.N_PEXT; + n_desc |= macho.N_WEAK_DEF; + }, + .Strong => { + // Check if the export is _main, and note if os. + // Otherwise, don't do anything since we already have all the flags + // set that we need for global (strong) linkage. + // n_type == N_SECT | N_EXT + if (mem.eql(u8, exp.options.name, "_main")) { self.entry_addr = decl_sym.n_value; } - break :blk macho.REFERENCE_FLAG_DEFINED; }, - .Weak => macho.N_WEAK_REF, + .Weak => { + // Weak linkage is specified as part of n_desc field. + // Symbol's n_type is like for a symbol with strong linkage. + n_desc |= macho.N_WEAK_DEF; + }, .LinkOnce => { try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1); module.failed_exports.putAssumeCapacityNoClobber( @@ -1386,8 +1403,8 @@ pub fn updateDeclExports( ); continue; }, - }; - const n_type = decl_sym.n_type | macho.N_EXT; + } + if (exp.link.macho.sym_index) |i| { const sym = &self.globals.items[i]; sym.* = .{ diff --git a/test/stage2/darwin.zig b/test/stage2/darwin.zig index f8d82391cc..4f708729a2 100644 --- a/test/stage2/darwin.zig +++ b/test/stage2/darwin.zig @@ -17,7 +17,7 @@ pub fn addCases(ctx: *TestContext) !void { // Incorrect return type case.addError( - \\export fn _start() noreturn { + \\export fn _main() noreturn { \\} , &[_][]const u8{":2:1: error: expected noreturn, found void"}); @@ -26,7 +26,7 @@ pub fn addCases(ctx: *TestContext) !void { \\extern "c" fn write(usize, usize, usize) usize; \\extern "c" fn exit(usize) noreturn; \\ - \\export fn _start() noreturn { + \\export fn _main() noreturn { \\ print(); \\ \\ exit(0); @@ -46,7 +46,7 @@ pub fn addCases(ctx: *TestContext) !void { \\extern "c" fn write(usize, usize, usize) usize; \\extern "c" fn exit(usize) noreturn; \\ - \\export fn _start() noreturn { + \\export fn _main() noreturn { \\ print(); \\ print(); \\ print(); @@ -73,7 +73,7 @@ pub fn addCases(ctx: *TestContext) !void { \\extern "c" fn write(usize, usize, usize) usize; \\extern "c" fn exit(usize) noreturn; \\ - \\export fn _start() noreturn { + \\export fn _main() noreturn { \\ print(); \\ \\ exit(0); @@ -93,7 +93,7 @@ pub fn addCases(ctx: *TestContext) !void { \\extern "c" fn write(usize, usize, usize) usize; \\extern "c" fn exit(usize) noreturn; \\ - \\export fn _start() noreturn { + \\export fn _main() noreturn { \\ print(); \\ print(); \\ @@ -119,7 +119,7 @@ pub fn addCases(ctx: *TestContext) !void { case.addCompareOutput( \\extern "c" fn exit(usize) noreturn; \\ - \\export fn _start() noreturn { + \\export fn _main() noreturn { \\ exit(0); \\} , @@ -130,7 +130,7 @@ pub fn addCases(ctx: *TestContext) !void { \\extern "c" fn exit(usize) noreturn; \\extern "c" fn write(usize, usize, usize) usize; \\ - \\export fn _start() noreturn { + \\export fn _main() noreturn { \\ _ = write(1, @ptrToInt("Hey!\n"), 5); \\ exit(0); \\} From af9c6f2c696201e16d95f79a7275aae19b34cd59 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 14 May 2021 13:09:40 +0200 Subject: [PATCH 21/42] macho: put dSYM bundle in zig-cache Originally, I thought that the dSYM bundle has to reside side-by-side with the binary it carries the debugging information for. However, it turns out macOS is clever enough that it auto-searches for matching dSYM bundle based on the embedded UUID of the binary. To verify this, run this on your macOS: ``` mdfind "com_apple_xcode_dsym_uuids == " ``` See [here] for more info. [here]: https://developer.apple.com/documentation/xcode/adding-identifiable-symbol-names-to-a-crash-report --- src/link/MachO.zig | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 5a0deb67bc..0a4eb6da87 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -363,18 +363,30 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio self.base.file = file; // Create dSYM bundle. - const d_sym_path = try fmt.allocPrint(allocator, "{s}.dSYM/Contents/Resources/DWARF/", .{sub_path}); - defer allocator.free(d_sym_path); - var d_sym_bundle = try options.emit.?.directory.handle.makeOpenPath(d_sym_path, .{}); - defer d_sym_bundle.close(); - const d_sym_file = try d_sym_bundle.createFile(sub_path, .{ - .truncate = false, - .read = true, - }); - self.d_sym = .{ - .base = self, - .file = d_sym_file, - }; + if (options.module) |mod| { + const dir = mod.zig_cache_artifact_directory; + log.debug("creating {s}.dSYM bundle in {s}", .{ sub_path, dir.path }); + + const d_sym_path = try fmt.allocPrint( + allocator, + "{s}.dSYM" ++ fs.path.sep_str ++ "Contents" ++ fs.path.sep_str ++ "Resources" ++ fs.path.sep_str ++ "DWARF", + .{sub_path}, + ); + defer allocator.free(d_sym_path); + + var d_sym_bundle = try dir.handle.makeOpenPath(d_sym_path, .{}); + defer d_sym_bundle.close(); + + const d_sym_file = try d_sym_bundle.createFile(sub_path, .{ + .truncate = false, + .read = true, + }); + + self.d_sym = .{ + .base = self, + .file = d_sym_file, + }; + } // Index 0 is always a null symbol. try self.locals.append(allocator, .{ @@ -387,7 +399,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio switch (options.output_mode) { .Exe => {}, - .Obj => {}, + .Obj => return error.TODOImplementWritingObjFiles, .Lib => return error.TODOImplementWritingLibFiles, } From fa3afede5809cef6c1d5856c1f930344181c16c8 Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Tue, 4 May 2021 00:32:21 +0200 Subject: [PATCH 22/42] SPIR-V: Set default ofmt to spirv --- lib/std/target.zig | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/std/target.zig b/lib/std/target.zig index d7b00cf79e..d44eb4b064 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -822,6 +822,13 @@ pub const Target = struct { }; } + pub fn isSPIRV(arch: Arch) bool { + return switch (arch) { + .spirv32, .spirv64 => true, + else => false, + }; + } + pub fn parseCpuModel(arch: Arch, cpu_name: []const u8) !*const Cpu.Model { for (arch.allCpuModels()) |cpu| { if (mem.eql(u8, cpu_name, cpu.name)) { @@ -1324,6 +1331,9 @@ pub const Target = struct { if (cpu_arch.isWasm()) { return .wasm; } + if (cpu_arch.isSPIRV()) { + return .spirv; + } return .elf; } From d45e7dfc241f917946e057ad67d291bf1f0028e0 Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Wed, 5 May 2021 01:59:23 +0200 Subject: [PATCH 23/42] SPIR-V: Begin generating types --- src/codegen/spirv.zig | 101 +++++++++++++++++++++++++-------- src/link/SpirV.zig | 117 ++++++++++++++++++--------------------- tools/gen_spirv_spec.zig | 17 +++--- 3 files changed, 141 insertions(+), 94 deletions(-) diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index 23fc45616f..077e71d4e1 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -1,9 +1,13 @@ const std = @import("std"); const Allocator = std.mem.Allocator; +const log = std.log.scoped(.codegen); const spec = @import("spirv/spec.zig"); const Module = @import("../Module.zig"); const Decl = Module.Decl; +const Type = @import("../type.zig").Type; + +pub const TypeMap = std.HashMap(Type, u32, Type.hash, Type.eql, std.hash_map.default_max_load_percentage); pub fn writeInstruction(code: *std.ArrayList(u32), instr: spec.Opcode, args: []const u32) !void { const word_count = @intCast(u32, args.len + 1); @@ -12,38 +16,91 @@ pub fn writeInstruction(code: *std.ArrayList(u32), instr: spec.Opcode, args: []c } pub const SPIRVModule = struct { - next_id: u32 = 0, - free_id_list: std.ArrayList(u32), + next_result_id: u32 = 0, - pub fn init(allocator: *Allocator) SPIRVModule { + target: std.Target, + + types: TypeMap, + + types_and_globals: std.ArrayList(u32), + fn_decls: std.ArrayList(u32), + + pub fn init(target: std.Target, allocator: *Allocator) SPIRVModule { return .{ - .free_id_list = std.ArrayList(u32).init(allocator), + .target = target, + .types = TypeMap.init(allocator), + .types_and_globals = std.ArrayList(u32).init(allocator), + .fn_decls = std.ArrayList(u32).init(allocator), }; } pub fn deinit(self: *SPIRVModule) void { - self.free_id_list.deinit(); + self.fn_decls.deinit(); + self.types_and_globals.deinit(); + self.types.deinit(); + self.* = undefined; } - pub fn allocId(self: *SPIRVModule) u32 { - if (self.free_id_list.popOrNull()) |id| return id; - - defer self.next_id += 1; - return self.next_id; + pub fn allocResultId(self: *SPIRVModule) u32 { + defer self.next_result_id += 1; + return self.next_result_id; } - pub fn freeId(self: *SPIRVModule, id: u32) void { - if (id + 1 == self.next_id) { - self.next_id -= 1; - } else { - // If no more memory to append the id to the free list, just ignore it. - self.free_id_list.append(id) catch {}; + pub fn resultIdBound(self: *SPIRVModule) u32 { + return self.next_result_id; + } + + pub fn getOrGenType(self: *SPIRVModule, t: Type) !u32 { + // We can't use getOrPut here so we can recursively generate types. + if (self.types.get(t)) |already_generated| { + return already_generated; + } + + const result = self.allocResultId(); + + switch (t.zigTypeTag()) { + .Void => try writeInstruction(&self.types_and_globals, .OpTypeVoid, &[_]u32{ result }), + .Bool => try writeInstruction(&self.types_and_globals, .OpTypeBool, &[_]u32{ result }), + .Int => { + const int_info = t.intInfo(self.target); + try writeInstruction(&self.types_and_globals, .OpTypeInt, &[_]u32{ + result, + int_info.bits, + switch (int_info.signedness) { + .unsigned => 0, + .signed => 1, + }, + }); + }, + // TODO: Verify that floatBits() will be correct. + .Float => try writeInstruction(&self.types_and_globals, .OpTypeFloat, &[_]u32{ result, t.floatBits(self.target) }), + .Null, + .Undefined, + .EnumLiteral, + .ComptimeFloat, + .ComptimeInt, + .Type, + => unreachable, // Must be const or comptime. + + .BoundFn => unreachable, // this type will be deleted from the language. + + else => return error.TODO, + } + + try self.types.put(t, result); + return result; + } + + pub fn gen(self: *SPIRVModule, decl: *Decl) !void { + const typed_value = decl.typed_value.most_recent.typed_value; + + switch (typed_value.ty.zigTypeTag()) { + .Fn => { + log.debug("Generating code for function '{s}'", .{ std.mem.spanZ(decl.name) }); + + _ = try self.getOrGenType(typed_value.ty.fnReturnType()); + }, + else => return error.TODO, } } - - pub fn idBound(self: *SPIRVModule) u32 { - return self.next_id; - } - - pub fn genDecl(self: SPIRVModule, id: u32, code: *std.ArrayList(u32), decl: *Decl) !void {} }; diff --git a/src/link/SpirV.zig b/src/link/SpirV.zig index 7a35752f62..95c747c170 100644 --- a/src/link/SpirV.zig +++ b/src/link/SpirV.zig @@ -16,11 +16,16 @@ //! All function declarations without a body (extern functions presumably). //! All regular functions. +// Because SPIR-V requires re-compilation anyway, and so hot swapping will not work +// anyway, we simply generate all the code in flushModule. This keeps +// things considerably simpler. + const SpirV = @This(); const std = @import("std"); const Allocator = std.mem.Allocator; const assert = std.debug.assert; +const log = std.log.scoped(.link); const Module = @import("../Module.zig"); const Compilation = @import("../Compilation.zig"); @@ -30,16 +35,15 @@ const trace = @import("../tracy.zig").trace; const build_options = @import("build_options"); const spec = @import("../codegen/spirv/spec.zig"); +// TODO: Should this struct be used at all rather than just a hashmap of aux data for every decl? pub const FnData = struct { - id: ?u32 = null, - code: std.ArrayListUnmanaged(u32) = .{}, + // We're going to fill these in flushModule, and we're going to fill them unconditionally, + // so just set it to undefined. + id: u32 = undefined }; base: link.File, -// TODO: Does this file need to support multiple independent modules? -spirv_module: codegen.SPIRVModule, - pub fn createEmpty(gpa: *Allocator, options: link.Options) !*SpirV { const spirv = try gpa.create(SpirV); spirv.* = .{ @@ -49,7 +53,6 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*SpirV { .file = null, .allocator = gpa, }, - .spirv_module = codegen.SPIRVModule.init(gpa), }; // TODO: Figure out where to put all of these @@ -87,28 +90,9 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio return spirv; } -pub fn deinit(self: *SpirV) void { - self.spirv_module.deinit(); -} +pub fn deinit(self: *SpirV) void {} -pub fn updateDecl(self: *SpirV, module: *Module, decl: *Module.Decl) !void { - const tracy = trace(@src()); - defer tracy.end(); - - const fn_data = &decl.fn_link.spirv; - if (fn_data.id == null) { - fn_data.id = self.spirv_module.allocId(); - } - - var managed_code = fn_data.code.toManaged(self.base.allocator); - managed_code.items.len = 0; - - try self.spirv_module.genDecl(fn_data.id.?, &managed_code, decl); - fn_data.code = managed_code.toUnmanaged(); - - // Free excess allocated memory for this Decl. - fn_data.code.shrinkAndFree(self.base.allocator, fn_data.code.items.len); -} +pub fn updateDecl(self: *SpirV, module: *Module, decl: *Module.Decl) !void {} pub fn updateDeclExports( self: *SpirV, @@ -117,12 +101,7 @@ pub fn updateDeclExports( exports: []const *Module.Export, ) !void {} -pub fn freeDecl(self: *SpirV, decl: *Module.Decl) void { - var fn_data = decl.fn_link.spirv; - fn_data.code.deinit(self.base.allocator); - if (fn_data.id) |id| self.spirv_module.freeId(id); - decl.fn_link.spirv = undefined; -} +pub fn freeDecl(self: *SpirV, decl: *Module.Decl) void {} pub fn flush(self: *SpirV, comp: *Compilation) !void { if (build_options.have_llvm and self.base.options.use_lld) { @@ -139,55 +118,69 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void { const module = self.base.options.module.?; const target = comp.getTarget(); + var spirv_module = codegen.SPIRVModule.init(target, self.base.allocator); + defer spirv_module.deinit(); + + // Allocate an ID for every declaration before generating code, + // so that we can access them before processing them. + // TODO: We're allocating an ID unconditionally now, are there + // declarations which don't generate a result? + // TODO: fn_link is used here, but thats probably not the right field. It will work anyway though. + { + for (module.decl_table.items()) |entry| { + const decl = entry.value; + if (decl.typed_value != .most_recent) + continue; + + decl.fn_link.spirv.id = spirv_module.allocResultId(); + log.debug("Allocating id {} to '{s}'", .{ decl.fn_link.spirv.id, std.mem.spanZ(decl.name) }); + } + } + + // Now, actually generate the code for all declarations. + { + for (module.decl_table.items()) |entry| { + const decl = entry.value; + if (decl.typed_value != .most_recent) + continue; + + try spirv_module.gen(decl); + } + } + var binary = std.ArrayList(u32).init(self.base.allocator); defer binary.deinit(); - // Note: The order of adding sections to the final binary - // follows the SPIR-V logical module format! - try binary.appendSlice(&[_]u32{ spec.magic_number, (spec.version.major << 16) | (spec.version.minor << 8), 0, // TODO: Register Zig compiler magic number. - self.spirv_module.idBound(), + spirv_module.resultIdBound(), // ID bound. 0, // Schema (currently reserved for future use in the SPIR-V spec). }); try writeCapabilities(&binary, target); try writeMemoryModel(&binary, target); - // Collect list of buffers to write. - // SPIR-V files support both little and big endian words. The actual format is - // disambiguated by the magic number, and so theoretically we don't need to worry - // about endian-ness when writing the final binary. - var all_buffers = std.ArrayList(std.os.iovec_const).init(self.base.allocator); - defer all_buffers.deinit(); + // Note: The order of adding sections to the final binary + // follows the SPIR-V logical module format! + var all_buffers = [_]std.os.iovec_const{ + wordsToIovConst(binary.items), + wordsToIovConst(spirv_module.types_and_globals.items), + wordsToIovConst(spirv_module.fn_decls.items), + }; - // Pre-allocate enough for the binary info + all functions - try all_buffers.ensureCapacity(module.decl_table.count() + 1); - - all_buffers.appendAssumeCapacity(wordsToIovConst(binary.items)); - - for (module.decl_table.items()) |entry| { - const decl = entry.value; - switch (decl.typed_value) { - .most_recent => |tvm| { - const fn_data = &decl.fn_link.spirv; - all_buffers.appendAssumeCapacity(wordsToIovConst(fn_data.code.items)); - }, - .never_succeeded => continue, - } - } + const file = self.base.file.?; + const bytes = std.mem.sliceAsBytes(binary.items); var file_size: u64 = 0; - for (all_buffers.items) |iov| { + for (all_buffers) |iov| { file_size += iov.iov_len; } - const file = self.base.file.?; try file.seekTo(0); try file.setEndPos(file_size); - try file.pwritevAll(all_buffers.items, 0); + try file.pwritevAll(&all_buffers, 0); } fn writeCapabilities(binary: *std.ArrayList(u32), target: std.Target) !void { @@ -231,4 +224,4 @@ fn wordsToIovConst(words: []const u32) std.os.iovec_const { .iov_base = bytes.ptr, .iov_len = bytes.len, }; -} +} \ No newline at end of file diff --git a/tools/gen_spirv_spec.zig b/tools/gen_spirv_spec.zig index 22fe3443a5..7550f26346 100644 --- a/tools/gen_spirv_spec.zig +++ b/tools/gen_spirv_spec.zig @@ -118,11 +118,16 @@ pub fn main() !void { } fn render(writer: Writer, registry: Registry) !void { + try writer.writeAll( + \\//! This file is auto-generated by tools/gen_spirv_spec.zig. + \\ + \\const Version = @import("builtin").Version; + \\ + ); + switch (registry) { .core => |core_reg| { - try renderCopyRight(writer, core_reg.copyright); try writer.print( - \\const Version = @import("builtin").Version; \\pub const version = Version{{.major = {}, .minor = {}, .patch = {}}}; \\pub const magic_number: u32 = {s}; \\ @@ -132,9 +137,7 @@ fn render(writer: Writer, registry: Registry) !void { try renderOperandKinds(writer, core_reg.operand_kinds); }, .extension => |ext_reg| { - try renderCopyRight(writer, ext_reg.copyright); try writer.print( - \\const Version = @import("builtin").Version; \\pub const version = Version{{.major = {}, .minor = 0, .patch = {}}}; \\ , .{ ext_reg.version, ext_reg.revision }, @@ -145,12 +148,6 @@ fn render(writer: Writer, registry: Registry) !void { } } -fn renderCopyRight(writer: Writer, copyright: []const []const u8) !void { - for (copyright) |line| { - try writer.print("// {s}\n", .{ line }); - } -} - fn renderOpcodes(writer: Writer, instructions: []const Instruction) !void { try writer.writeAll("pub const Opcode = extern enum(u16) {\n"); for (instructions) |instr| { From 6d1a4c8b1ec66969f262a5469dd51223bcda720f Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Wed, 12 May 2021 00:00:44 +0200 Subject: [PATCH 24/42] SPIR-V: Adapt spec generator to new render api --- tools/gen_spirv_spec.zig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/gen_spirv_spec.zig b/tools/gen_spirv_spec.zig index 7550f26346..4ca69330c7 100644 --- a/tools/gen_spirv_spec.zig +++ b/tools/gen_spirv_spec.zig @@ -114,7 +114,8 @@ pub fn main() !void { try render(buf.writer(), registry); const tree = try std.zig.parse(allocator, buf.items); - _ = try std.zig.render(allocator, std.io.getStdOut().writer(), tree); + const formatted = try tree.render(allocator); + try std.io.getStdOut().writeAll(formatted); } fn render(writer: Writer, registry: Registry) !void { From 42f2ff6ec9ad370cc58d8b2d85f62af346e9b5b8 Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Wed, 12 May 2021 00:01:04 +0200 Subject: [PATCH 25/42] SPIR-V: Re-generate spec.zig --- src/codegen/spirv/spec.zig | 120 +++++++++++++++++++++++++++---------- 1 file changed, 89 insertions(+), 31 deletions(-) diff --git a/src/codegen/spirv/spec.zig b/src/codegen/spirv/spec.zig index ceb62f1e5d..7dc1e95ad1 100644 --- a/src/codegen/spirv/spec.zig +++ b/src/codegen/spirv/spec.zig @@ -1,26 +1,5 @@ -// Copyright (c) 2014-2020 The Khronos Group Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and/or associated documentation files (the "Materials"), -// to deal in the Materials without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Materials, and to permit persons to whom the -// Materials are furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Materials. -// -// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS -// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND -// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ -// -// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS -// IN THE MATERIALS. +//! This file is auto-generated by tools/gen_spirv_spec.zig. + const Version = @import("builtin").Version; pub const version = Version{ .major = 1, .minor = 5, .patch = 4 }; pub const magic_number: u32 = 0x07230203; @@ -443,8 +422,15 @@ pub const Opcode = extern enum(u16) { OpUSubSatINTEL = 5596, OpIMul32x16INTEL = 5597, OpUMul32x16INTEL = 5598, - OpFunctionPointerINTEL = 5600, + OpConstFunctionPointerINTEL = 5600, OpFunctionPointerCallINTEL = 5601, + OpAsmTargetINTEL = 5609, + OpAsmINTEL = 5610, + OpAsmCallINTEL = 5611, + OpAtomicFMinEXT = 5614, + OpAtomicFMaxEXT = 5615, + OpAssumeTrueKHR = 5630, + OpExpectKHR = 5631, OpDecorateString = 5632, OpDecorateStringGOOGLE = 5632, OpMemberDecorateString = 5633, @@ -567,7 +553,12 @@ pub const Opcode = extern enum(u16) { OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL = 5814, OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL = 5815, OpSubgroupAvcSicGetInterRawSadsINTEL = 5816, + OpVariableLengthArrayINTEL = 5818, + OpSaveMemoryINTEL = 5819, + OpRestoreMemoryINTEL = 5820, OpLoopControlINTEL = 5887, + OpPtrCastToCrossWorkgroupINTEL = 5934, + OpCrossWorkgroupCastToPtrINTEL = 5938, OpReadPipeBlockingINTEL = 5946, OpWritePipeBlockingINTEL = 5947, OpFPGARegINTEL = 5949, @@ -589,6 +580,10 @@ pub const Opcode = extern enum(u16) { OpRayQueryGetIntersectionObjectToWorldKHR = 6031, OpRayQueryGetIntersectionWorldToObjectKHR = 6032, OpAtomicFAddEXT = 6035, + OpTypeBufferSurfaceINTEL = 6086, + OpTypeStructContinuedINTEL = 6090, + OpConstantCompositeContinuedINTEL = 6091, + OpSpecConstantCompositeContinuedINTEL = 6092, _, }; pub const ImageOperands = packed struct { @@ -642,8 +637,8 @@ pub const FPFastMathMode = packed struct { _reserved_bit_13: bool = false, _reserved_bit_14: bool = false, _reserved_bit_15: bool = false, - _reserved_bit_16: bool = false, - _reserved_bit_17: bool = false, + AllowContractFastINTEL: bool = false, + AllowReassocINTEL: bool = false, _reserved_bit_18: bool = false, _reserved_bit_19: bool = false, _reserved_bit_20: bool = false, @@ -717,7 +712,7 @@ pub const LoopControl = packed struct { LoopCoalesceINTEL: bool = false, MaxInterleavingINTEL: bool = false, SpeculatedIterationsINTEL: bool = false, - _reserved_bit_23: bool = false, + NoFusionINTEL: bool = false, _reserved_bit_24: bool = false, _reserved_bit_25: bool = false, _reserved_bit_26: bool = false, @@ -1037,10 +1032,16 @@ pub const ExecutionMode = extern enum(u32) { SampleInterlockUnorderedEXT = 5369, ShadingRateInterlockOrderedEXT = 5370, ShadingRateInterlockUnorderedEXT = 5371, + SharedLocalMemorySizeINTEL = 5618, + RoundingModeRTPINTEL = 5620, + RoundingModeRTNINTEL = 5621, + FloatingPointModeALTINTEL = 5622, + FloatingPointModeIEEEINTEL = 5623, MaxWorkgroupSizeINTEL = 5893, MaxWorkDimINTEL = 5894, NoGlobalOffsetINTEL = 5895, NumSIMDWorkitemsINTEL = 5896, + SchedulerTargetFmaxMhzINTEL = 5903, _, }; pub const StorageClass = extern enum(u32) { @@ -1072,6 +1073,8 @@ pub const StorageClass = extern enum(u32) { PhysicalStorageBuffer = 5349, PhysicalStorageBufferEXT = 5349, CodeSectionINTEL = 5605, + DeviceOnlyINTEL = 5936, + HostOnlyINTEL = 5937, _, }; pub const Dim = extern enum(u32) { @@ -1192,9 +1195,20 @@ pub const FPRoundingMode = extern enum(u32) { RTN = 3, _, }; +pub const FPDenormMode = extern enum(u32) { + Preserve = 0, + FlushToZero = 1, + _, +}; +pub const FPOperationMode = extern enum(u32) { + IEEE = 0, + ALT = 1, + _, +}; pub const LinkageType = extern enum(u32) { Export = 0, Import = 1, + LinkOnceODR = 2, _, }; pub const AccessQualifier = extern enum(u32) { @@ -1279,12 +1293,22 @@ pub const Decoration = extern enum(u32) { RestrictPointerEXT = 5355, AliasedPointer = 5356, AliasedPointerEXT = 5356, + SIMTCallINTEL = 5599, ReferencedIndirectlyINTEL = 5602, + ClobberINTEL = 5607, + SideEffectsINTEL = 5608, + VectorComputeVariableINTEL = 5624, + FuncParamIOKindINTEL = 5625, + VectorComputeFunctionINTEL = 5626, + StackCallINTEL = 5627, + GlobalVariableOffsetINTEL = 5628, CounterBuffer = 5634, HlslCounterBufferGOOGLE = 5634, UserSemantic = 5635, HlslSemanticGOOGLE = 5635, UserTypeGOOGLE = 5636, + FunctionRoundingModeINTEL = 5822, + FunctionDenormModeINTEL = 5823, RegisterINTEL = 5825, MemoryINTEL = 5826, NumbanksINTEL = 5827, @@ -1297,6 +1321,17 @@ pub const Decoration = extern enum(u32) { MergeINTEL = 5834, BankBitsINTEL = 5835, ForcePow2DepthINTEL = 5836, + BurstCoalesceINTEL = 5899, + CacheSizeINTEL = 5900, + DontStaticallyCoalesceINTEL = 5901, + PrefetchINTEL = 5902, + StallEnableINTEL = 5905, + FuseLoopsInFunctionINTEL = 5907, + BufferLocationINTEL = 5921, + IOPipeStorageINTEL = 5944, + FunctionFloatingPointModeINTEL = 6080, + SingleElementVectorINTEL = 6085, + VectorComputeCallableFunctionINTEL = 6087, _, }; pub const BuiltIn = extern enum(u32) { @@ -1342,14 +1377,14 @@ pub const BuiltIn = extern enum(u32) { VertexIndex = 42, InstanceIndex = 43, SubgroupEqMask = 4416, - SubgroupGeMask = 4417, - SubgroupGtMask = 4418, - SubgroupLeMask = 4419, - SubgroupLtMask = 4420, SubgroupEqMaskKHR = 4416, + SubgroupGeMask = 4417, SubgroupGeMaskKHR = 4417, + SubgroupGtMask = 4418, SubgroupGtMaskKHR = 4418, + SubgroupLeMask = 4419, SubgroupLeMaskKHR = 4419, + SubgroupLtMask = 4420, SubgroupLtMaskKHR = 4420, BaseVertex = 4424, BaseInstance = 4425, @@ -1520,6 +1555,9 @@ pub const Capability = extern enum(u32) { FragmentShadingRateKHR = 4422, SubgroupBallotKHR = 4423, DrawParameters = 4427, + WorkgroupMemoryExplicitLayoutKHR = 4428, + WorkgroupMemoryExplicitLayout8BitAccessKHR = 4429, + WorkgroupMemoryExplicitLayout16BitAccessKHR = 4430, SubgroupVoteKHR = 4431, StorageBuffer16BitAccess = 4433, StorageUniformBufferBlock16 = 4433, @@ -1610,21 +1648,41 @@ pub const Capability = extern enum(u32) { SubgroupBufferBlockIOINTEL = 5569, SubgroupImageBlockIOINTEL = 5570, SubgroupImageMediaBlockIOINTEL = 5579, + RoundToInfinityINTEL = 5582, + FloatingPointModeINTEL = 5583, IntegerFunctions2INTEL = 5584, FunctionPointersINTEL = 5603, IndirectReferencesINTEL = 5604, + AsmINTEL = 5606, + AtomicFloat32MinMaxEXT = 5612, + AtomicFloat64MinMaxEXT = 5613, + AtomicFloat16MinMaxEXT = 5616, + VectorComputeINTEL = 5617, + VectorAnyINTEL = 5619, + ExpectAssumeKHR = 5629, SubgroupAvcMotionEstimationINTEL = 5696, SubgroupAvcMotionEstimationIntraINTEL = 5697, SubgroupAvcMotionEstimationChromaINTEL = 5698, + VariableLengthArrayINTEL = 5817, + FunctionFloatControlINTEL = 5821, FPGAMemoryAttributesINTEL = 5824, + FPFastMathModeINTEL = 5837, + ArbitraryPrecisionIntegersINTEL = 5844, UnstructuredLoopControlsINTEL = 5886, FPGALoopControlsINTEL = 5888, KernelAttributesINTEL = 5892, FPGAKernelAttributesINTEL = 5897, + FPGAMemoryAccessesINTEL = 5898, + FPGAClusterAttributesINTEL = 5904, + LoopFuseINTEL = 5906, + FPGABufferLocationINTEL = 5920, + USMStorageClassesINTEL = 5935, + IOPipesINTEL = 5943, BlockingPipesINTEL = 5945, FPGARegINTEL = 5948, AtomicFloat32AddEXT = 6033, AtomicFloat64AddEXT = 6034, + LongConstantCompositeINTEL = 6089, _, }; pub const RayQueryIntersection = extern enum(u32) { From 25329ca8521fc34a5cedc837a1142e1111d4e844 Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Wed, 12 May 2021 00:08:14 +0200 Subject: [PATCH 26/42] SPIR-V: Split out registry from gen_spirv_spec.zig --- tools/gen_spirv_spec.zig | 104 +++------------------------------------ tools/spirv/grammar.zig | 90 +++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 97 deletions(-) create mode 100644 tools/spirv/grammar.zig diff --git a/tools/gen_spirv_spec.zig b/tools/gen_spirv_spec.zig index 4ca69330c7..2176d1fa5e 100644 --- a/tools/gen_spirv_spec.zig +++ b/tools/gen_spirv_spec.zig @@ -1,97 +1,7 @@ const std = @import("std"); +const g = @import("spirv/grammar.zig"); const Writer = std.ArrayList(u8).Writer; -//! See https://www.khronos.org/registry/spir-v/specs/unified1/MachineReadableGrammar.html -//! and the files in https://github.com/KhronosGroup/SPIRV-Headers/blob/master/include/spirv/unified1/ -//! Note: Non-canonical casing in these structs used to match SPIR-V spec json. -const Registry = union(enum) { - core: CoreRegistry, - extension: ExtensionRegistry, -}; - -const CoreRegistry = struct { - copyright: [][]const u8, - /// Hexadecimal representation of the magic number - magic_number: []const u8, - major_version: u32, - minor_version: u32, - revision: u32, - instruction_printing_class: []InstructionPrintingClass, - instructions: []Instruction, - operand_kinds: []OperandKind, -}; - -const ExtensionRegistry = struct { - copyright: [][]const u8, - version: u32, - revision: u32, - instructions: []Instruction, - operand_kinds: []OperandKind = &[_]OperandKind{}, -}; - -const InstructionPrintingClass = struct { - tag: []const u8, - heading: ?[]const u8 = null, -}; - -const Instruction = struct { - opname: []const u8, - class: ?[]const u8 = null, // Note: Only available in the core registry. - opcode: u32, - operands: []Operand = &[_]Operand{}, - capabilities: [][]const u8 = &[_][]const u8{}, - extensions: [][]const u8 = &[_][]const u8{}, - version: ?[]const u8 = null, - - lastVersion: ?[]const u8 = null, -}; - -const Operand = struct { - kind: []const u8, - /// If this field is 'null', the operand is only expected once. - quantifier: ?Quantifier = null, - name: []const u8 = "", -}; - -const Quantifier = enum { - /// zero or once - @"?", - /// zero or more - @"*", -}; - -const OperandCategory = enum { - BitEnum, - ValueEnum, - Id, - Literal, - Composite, -}; - -const OperandKind = struct { - category: OperandCategory, - /// The name - kind: []const u8, - doc: ?[]const u8 = null, - enumerants: ?[]Enumerant = null, - bases: ?[]const []const u8 = null, -}; - -const Enumerant = struct { - enumerant: []const u8, - value: union(enum) { - bitflag: []const u8, // Hexadecimal representation of the value - int: u31, - }, - capabilities: [][]const u8 = &[_][]const u8{}, - /// Valid for .ValueEnum and .BitEnum - extensions: [][]const u8 = &[_][]const u8{}, - /// `quantifier` will always be `null`. - parameters: []Operand = &[_]Operand{}, - version: ?[]const u8 = null, - lastVersion: ?[]const u8 = null, -}; - pub fn main() !void { var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); defer arena.deinit(); @@ -106,7 +16,7 @@ pub fn main() !void { const spec = try std.fs.cwd().readFileAlloc(allocator, spec_path, std.math.maxInt(usize)); var tokens = std.json.TokenStream.init(spec); - var registry = try std.json.parse(Registry, &tokens, .{.allocator = allocator}); + var registry = try std.json.parse(g.Registry, &tokens, .{.allocator = allocator}); var buf = std.ArrayList(u8).init(allocator); defer buf.deinit(); @@ -118,7 +28,7 @@ pub fn main() !void { try std.io.getStdOut().writeAll(formatted); } -fn render(writer: Writer, registry: Registry) !void { +fn render(writer: Writer, registry: g.Registry) !void { try writer.writeAll( \\//! This file is auto-generated by tools/gen_spirv_spec.zig. \\ @@ -149,7 +59,7 @@ fn render(writer: Writer, registry: Registry) !void { } } -fn renderOpcodes(writer: Writer, instructions: []const Instruction) !void { +fn renderOpcodes(writer: Writer, instructions: []const g.Instruction) !void { try writer.writeAll("pub const Opcode = extern enum(u16) {\n"); for (instructions) |instr| { try writer.print("{} = {},\n", .{ std.zig.fmtId(instr.opname), instr.opcode }); @@ -157,7 +67,7 @@ fn renderOpcodes(writer: Writer, instructions: []const Instruction) !void { try writer.writeAll("_,\n};\n"); } -fn renderOperandKinds(writer: Writer, kinds: []const OperandKind) !void { +fn renderOperandKinds(writer: Writer, kinds: []const g.OperandKind) !void { for (kinds) |kind| { switch (kind.category) { .ValueEnum => try renderValueEnum(writer, kind), @@ -167,7 +77,7 @@ fn renderOperandKinds(writer: Writer, kinds: []const OperandKind) !void { } } -fn renderValueEnum(writer: Writer, enumeration: OperandKind) !void { +fn renderValueEnum(writer: Writer, enumeration: g.OperandKind) !void { try writer.print("pub const {s} = extern enum(u32) {{\n", .{ enumeration.kind }); const enumerants = enumeration.enumerants orelse return error.InvalidRegistry; @@ -180,7 +90,7 @@ fn renderValueEnum(writer: Writer, enumeration: OperandKind) !void { try writer.writeAll("_,\n};\n"); } -fn renderBitEnum(writer: Writer, enumeration: OperandKind) !void { +fn renderBitEnum(writer: Writer, enumeration: g.OperandKind) !void { try writer.print("pub const {s} = packed struct {{\n", .{ enumeration.kind }); var flags_by_bitpos = [_]?[]const u8{null} ** 32; diff --git a/tools/spirv/grammar.zig b/tools/spirv/grammar.zig new file mode 100644 index 0000000000..86a01641e4 --- /dev/null +++ b/tools/spirv/grammar.zig @@ -0,0 +1,90 @@ +//! See https://www.khronos.org/registry/spir-v/specs/unified1/MachineReadableGrammar.html +//! and the files in https://github.com/KhronosGroup/SPIRV-Headers/blob/master/include/spirv/unified1/ +//! Note: Non-canonical casing in these structs used to match SPIR-V spec json. +pub const Registry = union(enum) { + core: CoreRegistry, + extension: ExtensionRegistry, +}; + +pub const CoreRegistry = struct { + copyright: [][]const u8, + /// Hexadecimal representation of the magic number + magic_number: []const u8, + major_version: u32, + minor_version: u32, + revision: u32, + instruction_printing_class: []InstructionPrintingClass, + instructions: []Instruction, + operand_kinds: []OperandKind, +}; + +pub const ExtensionRegistry = struct { + copyright: [][]const u8, + version: u32, + revision: u32, + instructions: []Instruction, + operand_kinds: []OperandKind = &[_]OperandKind{}, +}; + +pub const InstructionPrintingClass = struct { + tag: []const u8, + heading: ?[]const u8 = null, +}; + +pub const Instruction = struct { + opname: []const u8, + class: ?[]const u8 = null, // Note: Only available in the core registry. + opcode: u32, + operands: []Operand = &[_]Operand{}, + capabilities: [][]const u8 = &[_][]const u8{}, + extensions: [][]const u8 = &[_][]const u8{}, + version: ?[]const u8 = null, + + lastVersion: ?[]const u8 = null, +}; + +pub const Operand = struct { + kind: []const u8, + /// If this field is 'null', the operand is only expected once. + quantifier: ?Quantifier = null, + name: []const u8 = "", +}; + +pub const Quantifier = enum { + /// zero or once + @"?", + /// zero or more + @"*", +}; + +pub const OperandCategory = enum { + BitEnum, + ValueEnum, + Id, + Literal, + Composite, +}; + +pub const OperandKind = struct { + category: OperandCategory, + /// The name + kind: []const u8, + doc: ?[]const u8 = null, + enumerants: ?[]Enumerant = null, + bases: ?[]const []const u8 = null, +}; + +pub const Enumerant = struct { + enumerant: []const u8, + value: union(enum) { + bitflag: []const u8, // Hexadecimal representation of the value + int: u31, + }, + capabilities: [][]const u8 = &[_][]const u8{}, + /// Valid for .ValueEnum and .BitEnum + extensions: [][]const u8 = &[_][]const u8{}, + /// `quantifier` will always be `null`. + parameters: []Operand = &[_]Operand{}, + version: ?[]const u8 = null, + lastVersion: ?[]const u8 = null, +}; From 4fe0a0b82b531d4ac44ed9a249137e9f6c186d0b Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Thu, 13 May 2021 01:19:32 +0200 Subject: [PATCH 27/42] SPIR-V: SPIR-V feature generation tool There is a lot left to be desired for this tool, as currently dependencies of extensions and dependencies of capabilities on extensions are not included: - There is no machine-readable definition of dependencies of extensions. - A capability may depend on either of a multitude of extensions, which as of yet cannot be properly modelled in the target system. --- tools/update_spirv_features.zig | 321 ++++++++++++++++++++++++++++++++ 1 file changed, 321 insertions(+) create mode 100644 tools/update_spirv_features.zig diff --git a/tools/update_spirv_features.zig b/tools/update_spirv_features.zig new file mode 100644 index 0000000000..94fdb38229 --- /dev/null +++ b/tools/update_spirv_features.zig @@ -0,0 +1,321 @@ +const std = @import("std"); +const fs = std.fs; +const Allocator = std.mem.Allocator; +const g = @import("spirv/grammar.zig"); + +//! This tool generates SPIR-V features from the grammar files in the SPIRV-Headers +//! (https://github.com/KhronosGroup/SPIRV-Headers/) and SPIRV-Registry (https://github.com/KhronosGroup/SPIRV-Registry/) +//! repositories. Currently it only generates a basic feature set definition consisting of versions, extensions and capabilities. +//! There is a lot left to be desired, as currently dependencies of extensions and dependencies on extensions aren't generated. +//! This is because there are some peculiarities in the SPIR-V registries: +//! - Capabilities may depend on multiple extensions, which cannot be modelled yet by std.Target. +//! - Extension dependencies are not documented in a machine-readable manner. +//! - Note that the grammar spec also contains definitions from extensions which aren't actually official. Most of these seem to be +//! from an intel project (https://github.com/intel/llvm/, https://github.com/intel/llvm/tree/sycl/sycl/doc/extensions/SPIRV), +//! and so ONLY extensions in the SPIRV-Registry should be included. + +const Version = struct { + major: u32, + minor: u32, + + fn parse(str: []const u8) !Version { + var it = std.mem.split(str, "."); + + const major = it.next() orelse return error.InvalidVersion; + const minor = it.next() orelse return error.InvalidVersion; + + if (it.next() != null) return error.InvalidVersion; + + return Version{ + .major = std.fmt.parseInt(u32, major, 10) catch return error.InvalidVersion, + .minor = std.fmt.parseInt(u32, minor, 10) catch return error.InvalidVersion, + }; + } + + fn eql(a: Version, b: Version) bool { + return a.major == b.major and a.minor == b.minor; + } + + fn lessThan(ctx: void, a: Version, b: Version) bool { + return if (a.major == b.major) + a.minor < b.minor + else + a.major < b.major; + } +}; + +pub fn main() !void { + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const allocator = &arena.allocator; + + const args = try std.process.argsAlloc(allocator); + + if (args.len <= 1) { + usageAndExit(std.io.getStdErr(), args[0], 1); + } + if (std.mem.eql(u8, args[1], "--help")) { + usageAndExit(std.io.getStdErr(), args[0], 0); + } + if (args.len != 3) { + usageAndExit(std.io.getStdErr(), args[0], 1); + } + + const spirv_headers_root = args[1]; + const spirv_registry_root = args[2]; + + if (std.mem.startsWith(u8, spirv_headers_root, "-") or std.mem.startsWith(u8, spirv_registry_root, "-")) { + usageAndExit(std.io.getStdErr(), args[0], 1); + } + + const registry_path = try fs.path.join(allocator, &.{ spirv_headers_root, "include", "spirv", "unified1", "spirv.core.grammar.json" }); + const registry_json = try std.fs.cwd().readFileAlloc(allocator, registry_path, std.math.maxInt(usize)); + var tokens = std.json.TokenStream.init(registry_json); + const registry = try std.json.parse(g.CoreRegistry, &tokens, .{ .allocator = allocator }); + + const capabilities = for (registry.operand_kinds) |opkind| { + if (std.mem.eql(u8, opkind.kind, "Capability")) + break opkind.enumerants orelse return error.InvalidRegistry; + } else return error.InvalidRegistry; + + const extensions = try gather_extensions(allocator, spirv_registry_root); + const versions = try gatherVersions(allocator, registry); + + var bw = std.io.bufferedWriter(std.io.getStdOut().writer()); + const w = bw.writer(); + + try w.writeAll( + \\//! This file is auto-generated by tools/update_spirv_features.zig. + \\//! TODO: Dependencies of capabilities on extensions. + \\//! TODO: Dependencies of extensions on extensions. + \\//! TODO: Dependencies of extensions on versions. + \\ + \\const std = @import("../std.zig"); + \\const CpuFeature = std.Target.Cpu.Feature; + \\const CpuModel = std.Target.Cpu.Model; + \\ + \\pub const Feature = enum { + \\ + ); + + for (versions) |ver| { + try w.print(" v{}_{},\n", .{ ver.major, ver.minor }); + } + + for (extensions) |ext| { + try w.print(" {},\n", .{ std.zig.fmtId(ext) }); + } + + for (capabilities) |cap| { + try w.print(" {},\n", .{ std.zig.fmtId(cap.enumerant) }); + } + + try w.writeAll( + \\}; + \\ + \\pub usingnamespace CpuFeature.feature_set_fns(Feature); + \\ + \\pub const all_features = blk: { + \\ @setEvalBranchQuota(2000); + \\ const len = @typeInfo(Feature).Enum.fields.len; + \\ std.debug.assert(len <= CpuFeature.Set.needed_bit_count); + \\ var result: [len]CpuFeature = undefined; + \\ + ); + + for (versions) |ver, i| { + try w.print( + \\ result[@enumToInt(Feature.v{0}_{1})] = .{{ + \\ .llvm_name = null, + \\ .description = "SPIR-V version {0}.{1}", + \\ + , .{ ver.major, ver.minor } + ); + + if (i == 0) { + try w.writeAll( + \\ .dependencies = featureSet(&[_]Feature{}), + \\ }; + \\ + ); + } else { + try w.print( + \\ .dependencies = featureSet(&[_]Feature{{ + \\ .v{}_{}, + \\ }}), + \\ }}; + \\ + , .{ versions[i - 1].major, versions[i - 1].minor } + ); + } + } + + // TODO: Extension dependencies. + for (extensions) |ext| { + try w.print( + \\ result[@enumToInt(Feature.{s})] = .{{ + \\ .llvm_name = null, + \\ .description = "SPIR-V extension {s}", + \\ .dependencies = featureSet(&[_]Feature{{}}), + \\ }}; + \\ + , .{ + std.zig.fmtId(ext), + ext, + } + ); + } + + // TODO: Capability extension dependencies. + for (capabilities) |cap| { + try w.print( + \\ result[@enumToInt(Feature.{s})] = .{{ + \\ .llvm_name = null, + \\ .description = "Enable SPIR-V capability {s}", + \\ .dependencies = featureSet(&[_]Feature{{ + \\ + , .{ + std.zig.fmtId(cap.enumerant), + cap.enumerant, + } + ); + + if (cap.version) |ver_str| { + if (!std.mem.eql(u8, ver_str, "None")) { + const ver = try Version.parse(ver_str); + try w.print(" .v{}_{},\n", .{ ver.major, ver.minor }); + } + } + + for (cap.capabilities) |cap_dep| { + try w.print(" .{},\n", .{ std.zig.fmtId(cap_dep) }); + } + + try w.writeAll( + \\ }), + \\ }; + \\ + ); + } + + try w.writeAll( + \\ const ti = @typeInfo(Feature); + \\ for (result) |*elem, i| { + \\ elem.index = i; + \\ elem.name = ti.Enum.fields[i].name; + \\ } + \\ break :blk result; + \\}; + \\ + ); + + try bw.flush(); +} + +/// SPIRV-Registry should hold all extensions currently registered for SPIR-V. +/// The *.grammar.json in SPIRV-Headers should have most of these as well, but with this we're sure to get only the actually +/// registered ones. +/// TODO: Unfortunately, neither repository contains a machine-readable list of extension dependencies. +fn gather_extensions(allocator: *Allocator, spirv_registry_root: []const u8) ![]const []const u8 { + const extensions_path = try fs.path.join(allocator, &.{spirv_registry_root, "extensions"}); + var extensions_dir = try fs.cwd().openDir(extensions_path, .{ .iterate = true }); + defer extensions_dir.close(); + + var extensions = std.ArrayList([]const u8).init(allocator); + + var vendor_it = extensions_dir.iterate(); + while (try vendor_it.next()) |vendor_entry| { + std.debug.assert(vendor_entry.kind == .Directory); // If this fails, the structure of SPIRV-Registry has changed. + + const vendor_dir = try extensions_dir.openDir(vendor_entry.name, .{ .iterate = true }); + var ext_it = vendor_dir.iterate(); + while (try ext_it.next()) |ext_entry| { + // There is both a HTML and asciidoc version of every spec (as well as some other directories), + // we need just the name, but to avoid duplicates here we will just skip anything thats not asciidoc. + if (!std.mem.endsWith(u8, ext_entry.name, ".asciidoc")) + continue; + + // Unfortunately, some extension filenames are incorrect, so we need to look for the string in tne 'Name Strings' section. + // This has the following format: + // ``` + // Name Strings + // ------------ + // + // SPV_EXT_name + // ``` + // OR + // ``` + // == Name Strings + // + // SPV_EXT_name + // ``` + + const ext_spec = try vendor_dir.readFileAlloc(allocator, ext_entry.name, std.math.maxInt(usize)); + const name_strings = "Name Strings"; + + const name_strings_offset = std.mem.indexOf(u8, ext_spec, name_strings) orelse return error.InvalidRegistry; + + // As the specs are inconsistent on this next part, just skip any newlines/minuses + var ext_start = name_strings_offset + name_strings.len + 1; + while (ext_spec[ext_start] == '\n' or ext_spec[ext_start] == '-') { + ext_start += 1; + } + + const ext_end = std.mem.indexOfScalarPos(u8, ext_spec, ext_start, '\n') orelse return error.InvalidRegistry; + const ext = ext_spec[ext_start .. ext_end]; + + std.debug.assert(std.mem.startsWith(u8, ext, "SPV_")); // Sanity check, all extensions should have a name like SPV_VENDOR_extension. + + try extensions.append(try allocator.dupe(u8, ext)); + } + } + + return extensions.items; +} + +fn insertVersion(versions: *std.ArrayList(Version), version: ?[]const u8) !void { + const ver_str = version orelse return; + if (std.mem.eql(u8, ver_str, "None")) + return; + + const ver = try Version.parse(ver_str); + for (versions.items) |existing_ver| { + if (ver.eql(existing_ver)) return; + } + + try versions.append(ver); +} + +fn gatherVersions(allocator: *Allocator, registry: g.CoreRegistry) ![]const Version { + // Expected number of versions is small + var versions = std.ArrayList(Version).init(allocator); + + for (registry.instructions) |inst| { + try insertVersion(&versions, inst.version); + } + + for (registry.operand_kinds) |opkind| { + const enumerants = opkind.enumerants orelse continue; + for (enumerants) |enumerant| { + try insertVersion(&versions, enumerant.version); + } + } + + std.sort.sort(Version, versions.items, {}, Version.lessThan); + + return versions.items; +} + +fn usageAndExit(file: fs.File, arg0: []const u8, code: u8) noreturn { + file.writer().print( + \\Usage: {s} /path/git/SPIRV-Headers /path/git/SPIRV-Registry + \\ + \\Prints to stdout Zig code which can be used to replace the file lib/std/target/spirv.zig. + \\ + \\SPIRV-Headers can be cloned from https://github.com/KhronosGroup/SPIRV-Headers, + \\SPIRV-Registry can be cloned from https://github.com/KhronosGroup/SPIRV-Registry. + \\ + , .{arg0} + ) catch std.process.exit(1); + std.process.exit(code); +} From 98dc8eb7d97bd05ced6908c4da3c5e0a55e2c41b Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Thu, 13 May 2021 01:30:54 +0200 Subject: [PATCH 28/42] SPIR-V: Add generated SPIR-V features --- lib/std/target.zig | 4 +- lib/std/target/spirv.zig | 2135 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 2138 insertions(+), 1 deletion(-) create mode 100644 lib/std/target/spirv.zig diff --git a/lib/std/target.zig b/lib/std/target.zig index d44eb4b064..512021bab5 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -431,6 +431,7 @@ pub const Target = struct { pub const powerpc = @import("target/powerpc.zig"); pub const riscv = @import("target/riscv.zig"); pub const sparc = @import("target/sparc.zig"); + pub const spirv = @import("target/spirv.zig"); pub const systemz = @import("target/systemz.zig"); pub const ve = @import("target/ve.zig"); pub const wasm = @import("target/wasm.zig"); @@ -594,7 +595,7 @@ pub const Target = struct { pub const Set = struct { ints: [usize_count]usize, - pub const needed_bit_count = 172; + pub const needed_bit_count = 288; pub const byte_count = (needed_bit_count + 7) / 8; pub const usize_count = (byte_count + (@sizeOf(usize) - 1)) / @sizeOf(usize); pub const Index = std.math.Log2Int(std.meta.Int(.unsigned, usize_count * @bitSizeOf(usize))); @@ -1123,6 +1124,7 @@ pub const Target = struct { .amdgcn => &amdgpu.all_features, .riscv32, .riscv64 => &riscv.all_features, .sparc, .sparcv9, .sparcel => &sparc.all_features, + .spirv32, .spirv64 => &spirv.all_features, .s390x => &systemz.all_features, .i386, .x86_64 => &x86.all_features, .nvptx, .nvptx64 => &nvptx.all_features, diff --git a/lib/std/target/spirv.zig b/lib/std/target/spirv.zig new file mode 100644 index 0000000000..690501f731 --- /dev/null +++ b/lib/std/target/spirv.zig @@ -0,0 +1,2135 @@ +//! This file is auto-generated by tools/update_spirv_features.zig. +//! TODO: Dependencies of capabilities on extensions. +//! TODO: Dependencies of extensions on extensions. +//! TODO: Dependencies of extensions on versions. + +const std = @import("../std.zig"); +const CpuFeature = std.Target.Cpu.Feature; +const CpuModel = std.Target.Cpu.Model; + +pub const Feature = enum { + v1_1, + v1_2, + v1_3, + v1_4, + v1_5, + SPV_AMD_shader_fragment_mask, + SPV_AMD_gpu_shader_int16, + SPV_AMD_gpu_shader_half_float, + SPV_AMD_texture_gather_bias_lod, + SPV_AMD_shader_ballot, + SPV_AMD_gcn_shader, + SPV_AMD_shader_image_load_store_lod, + SPV_AMD_shader_explicit_vertex_parameter, + SPV_AMD_shader_trinary_minmax, + SPV_AMD_gpu_shader_half_float_fetch, + SPV_GOOGLE_hlsl_functionality1, + SPV_GOOGLE_user_type, + SPV_GOOGLE_decorate_string, + SPV_EXT_demote_to_helper_invocation, + SPV_EXT_descriptor_indexing, + SPV_EXT_fragment_fully_covered, + SPV_EXT_shader_stencil_export, + SPV_EXT_physical_storage_buffer, + SPV_EXT_shader_atomic_float_add, + SPV_EXT_shader_atomic_float_min_max, + SPV_EXT_shader_image_int64, + SPV_EXT_fragment_shader_interlock, + SPV_EXT_fragment_invocation_density, + SPV_EXT_shader_viewport_index_layer, + SPV_INTEL_loop_fuse, + SPV_INTEL_fpga_dsp_control, + SPV_INTEL_fpga_reg, + SPV_INTEL_fpga_memory_accesses, + SPV_INTEL_fpga_loop_controls, + SPV_INTEL_io_pipes, + SPV_INTEL_unstructured_loop_controls, + SPV_INTEL_blocking_pipes, + SPV_INTEL_device_side_avc_motion_estimation, + SPV_INTEL_fpga_memory_attributes, + SPV_INTEL_fp_fast_math_mode, + SPV_INTEL_media_block_io, + SPV_INTEL_shader_integer_functions2, + SPV_INTEL_subgroups, + SPV_INTEL_fpga_cluster_attributes, + SPV_INTEL_kernel_attributes, + SPV_INTEL_arbitrary_precision_integers, + SPV_KHR_8bit_storage, + SPV_KHR_shader_clock, + SPV_KHR_device_group, + SPV_KHR_16bit_storage, + SPV_KHR_variable_pointers, + SPV_KHR_no_integer_wrap_decoration, + SPV_KHR_subgroup_vote, + SPV_KHR_multiview, + SPV_KHR_shader_ballot, + SPV_KHR_vulkan_memory_model, + SPV_KHR_physical_storage_buffer, + SPV_KHR_workgroup_memory_explicit_layout, + SPV_KHR_fragment_shading_rate, + SPV_KHR_shader_atomic_counter_ops, + SPV_KHR_shader_draw_parameters, + SPV_KHR_storage_buffer_storage_class, + SPV_KHR_linkonce_odr, + SPV_KHR_terminate_invocation, + SPV_KHR_non_semantic_info, + SPV_KHR_post_depth_coverage, + SPV_KHR_expect_assume, + SPV_KHR_ray_tracing, + SPV_KHR_ray_query, + SPV_KHR_float_controls, + SPV_NV_viewport_array2, + SPV_NV_shader_subgroup_partitioned, + SPV_NVX_multiview_per_view_attributes, + SPV_NV_ray_tracing, + SPV_NV_shader_image_footprint, + SPV_NV_shading_rate, + SPV_NV_stereo_view_rendering, + SPV_NV_compute_shader_derivatives, + SPV_NV_shader_sm_builtins, + SPV_NV_mesh_shader, + SPV_NV_geometry_shader_passthrough, + SPV_NV_fragment_shader_barycentric, + SPV_NV_cooperative_matrix, + SPV_NV_sample_mask_override_coverage, + Matrix, + Shader, + Geometry, + Tessellation, + Addresses, + Linkage, + Kernel, + Vector16, + Float16Buffer, + Float16, + Float64, + Int64, + Int64Atomics, + ImageBasic, + ImageReadWrite, + ImageMipmap, + Pipes, + Groups, + DeviceEnqueue, + LiteralSampler, + AtomicStorage, + Int16, + TessellationPointSize, + GeometryPointSize, + ImageGatherExtended, + StorageImageMultisample, + UniformBufferArrayDynamicIndexing, + SampledImageArrayDynamicIndexing, + StorageBufferArrayDynamicIndexing, + StorageImageArrayDynamicIndexing, + ClipDistance, + CullDistance, + ImageCubeArray, + SampleRateShading, + ImageRect, + SampledRect, + GenericPointer, + Int8, + InputAttachment, + SparseResidency, + MinLod, + Sampled1D, + Image1D, + SampledCubeArray, + SampledBuffer, + ImageBuffer, + ImageMSArray, + StorageImageExtendedFormats, + ImageQuery, + DerivativeControl, + InterpolationFunction, + TransformFeedback, + GeometryStreams, + StorageImageReadWithoutFormat, + StorageImageWriteWithoutFormat, + MultiViewport, + SubgroupDispatch, + NamedBarrier, + PipeStorage, + GroupNonUniform, + GroupNonUniformVote, + GroupNonUniformArithmetic, + GroupNonUniformBallot, + GroupNonUniformShuffle, + GroupNonUniformShuffleRelative, + GroupNonUniformClustered, + GroupNonUniformQuad, + ShaderLayer, + ShaderViewportIndex, + FragmentShadingRateKHR, + SubgroupBallotKHR, + DrawParameters, + WorkgroupMemoryExplicitLayoutKHR, + WorkgroupMemoryExplicitLayout8BitAccessKHR, + WorkgroupMemoryExplicitLayout16BitAccessKHR, + SubgroupVoteKHR, + StorageBuffer16BitAccess, + StorageUniformBufferBlock16, + UniformAndStorageBuffer16BitAccess, + StorageUniform16, + StoragePushConstant16, + StorageInputOutput16, + DeviceGroup, + MultiView, + VariablePointersStorageBuffer, + VariablePointers, + AtomicStorageOps, + SampleMaskPostDepthCoverage, + StorageBuffer8BitAccess, + UniformAndStorageBuffer8BitAccess, + StoragePushConstant8, + DenormPreserve, + DenormFlushToZero, + SignedZeroInfNanPreserve, + RoundingModeRTE, + RoundingModeRTZ, + RayQueryProvisionalKHR, + RayQueryKHR, + RayTraversalPrimitiveCullingKHR, + RayTracingKHR, + Float16ImageAMD, + ImageGatherBiasLodAMD, + FragmentMaskAMD, + StencilExportEXT, + ImageReadWriteLodAMD, + Int64ImageEXT, + ShaderClockKHR, + SampleMaskOverrideCoverageNV, + GeometryShaderPassthroughNV, + ShaderViewportIndexLayerEXT, + ShaderViewportIndexLayerNV, + ShaderViewportMaskNV, + ShaderStereoViewNV, + PerViewAttributesNV, + FragmentFullyCoveredEXT, + MeshShadingNV, + ImageFootprintNV, + FragmentBarycentricNV, + ComputeDerivativeGroupQuadsNV, + FragmentDensityEXT, + ShadingRateNV, + GroupNonUniformPartitionedNV, + ShaderNonUniform, + ShaderNonUniformEXT, + RuntimeDescriptorArray, + RuntimeDescriptorArrayEXT, + InputAttachmentArrayDynamicIndexing, + InputAttachmentArrayDynamicIndexingEXT, + UniformTexelBufferArrayDynamicIndexing, + UniformTexelBufferArrayDynamicIndexingEXT, + StorageTexelBufferArrayDynamicIndexing, + StorageTexelBufferArrayDynamicIndexingEXT, + UniformBufferArrayNonUniformIndexing, + UniformBufferArrayNonUniformIndexingEXT, + SampledImageArrayNonUniformIndexing, + SampledImageArrayNonUniformIndexingEXT, + StorageBufferArrayNonUniformIndexing, + StorageBufferArrayNonUniformIndexingEXT, + StorageImageArrayNonUniformIndexing, + StorageImageArrayNonUniformIndexingEXT, + InputAttachmentArrayNonUniformIndexing, + InputAttachmentArrayNonUniformIndexingEXT, + UniformTexelBufferArrayNonUniformIndexing, + UniformTexelBufferArrayNonUniformIndexingEXT, + StorageTexelBufferArrayNonUniformIndexing, + StorageTexelBufferArrayNonUniformIndexingEXT, + RayTracingNV, + VulkanMemoryModel, + VulkanMemoryModelKHR, + VulkanMemoryModelDeviceScope, + VulkanMemoryModelDeviceScopeKHR, + PhysicalStorageBufferAddresses, + PhysicalStorageBufferAddressesEXT, + ComputeDerivativeGroupLinearNV, + RayTracingProvisionalKHR, + CooperativeMatrixNV, + FragmentShaderSampleInterlockEXT, + FragmentShaderShadingRateInterlockEXT, + ShaderSMBuiltinsNV, + FragmentShaderPixelInterlockEXT, + DemoteToHelperInvocationEXT, + SubgroupShuffleINTEL, + SubgroupBufferBlockIOINTEL, + SubgroupImageBlockIOINTEL, + SubgroupImageMediaBlockIOINTEL, + RoundToInfinityINTEL, + FloatingPointModeINTEL, + IntegerFunctions2INTEL, + FunctionPointersINTEL, + IndirectReferencesINTEL, + AsmINTEL, + AtomicFloat32MinMaxEXT, + AtomicFloat64MinMaxEXT, + AtomicFloat16MinMaxEXT, + VectorComputeINTEL, + VectorAnyINTEL, + ExpectAssumeKHR, + SubgroupAvcMotionEstimationINTEL, + SubgroupAvcMotionEstimationIntraINTEL, + SubgroupAvcMotionEstimationChromaINTEL, + VariableLengthArrayINTEL, + FunctionFloatControlINTEL, + FPGAMemoryAttributesINTEL, + FPFastMathModeINTEL, + ArbitraryPrecisionIntegersINTEL, + UnstructuredLoopControlsINTEL, + FPGALoopControlsINTEL, + KernelAttributesINTEL, + FPGAKernelAttributesINTEL, + FPGAMemoryAccessesINTEL, + FPGAClusterAttributesINTEL, + LoopFuseINTEL, + FPGABufferLocationINTEL, + USMStorageClassesINTEL, + IOPipesINTEL, + BlockingPipesINTEL, + FPGARegINTEL, + AtomicFloat32AddEXT, + AtomicFloat64AddEXT, + LongConstantCompositeINTEL, +}; + +pub usingnamespace CpuFeature.feature_set_fns(Feature); + +pub const all_features = blk: { + @setEvalBranchQuota(2000); + const len = @typeInfo(Feature).Enum.fields.len; + std.debug.assert(len <= CpuFeature.Set.needed_bit_count); + var result: [len]CpuFeature = undefined; + result[@enumToInt(Feature.v1_1)] = .{ + .llvm_name = null, + .description = "SPIR-V version 1.1", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.v1_2)] = .{ + .llvm_name = null, + .description = "SPIR-V version 1.2", + .dependencies = featureSet(&[_]Feature{ + .v1_1, + }), + }; + result[@enumToInt(Feature.v1_3)] = .{ + .llvm_name = null, + .description = "SPIR-V version 1.3", + .dependencies = featureSet(&[_]Feature{ + .v1_2, + }), + }; + result[@enumToInt(Feature.v1_4)] = .{ + .llvm_name = null, + .description = "SPIR-V version 1.4", + .dependencies = featureSet(&[_]Feature{ + .v1_3, + }), + }; + result[@enumToInt(Feature.v1_5)] = .{ + .llvm_name = null, + .description = "SPIR-V version 1.5", + .dependencies = featureSet(&[_]Feature{ + .v1_4, + }), + }; + result[@enumToInt(Feature.SPV_AMD_shader_fragment_mask)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_AMD_shader_fragment_mask", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_AMD_gpu_shader_int16)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_AMD_gpu_shader_int16", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_AMD_gpu_shader_half_float)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_AMD_gpu_shader_half_float", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_AMD_texture_gather_bias_lod)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_AMD_texture_gather_bias_lod", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_AMD_shader_ballot)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_AMD_shader_ballot", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_AMD_gcn_shader)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_AMD_gcn_shader", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_AMD_shader_image_load_store_lod)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_AMD_shader_image_load_store_lod", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_AMD_shader_explicit_vertex_parameter)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_AMD_shader_explicit_vertex_parameter", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_AMD_shader_trinary_minmax)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_AMD_shader_trinary_minmax", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_AMD_gpu_shader_half_float_fetch)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_AMD_gpu_shader_half_float_fetch", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_GOOGLE_hlsl_functionality1)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_GOOGLE_hlsl_functionality1", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_GOOGLE_user_type)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_GOOGLE_user_type", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_GOOGLE_decorate_string)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_GOOGLE_decorate_string", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_EXT_demote_to_helper_invocation)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_EXT_demote_to_helper_invocation", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_EXT_descriptor_indexing)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_EXT_descriptor_indexing", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_EXT_fragment_fully_covered)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_EXT_fragment_fully_covered", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_EXT_shader_stencil_export)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_EXT_shader_stencil_export", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_EXT_physical_storage_buffer)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_EXT_physical_storage_buffer", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_EXT_shader_atomic_float_add)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_EXT_shader_atomic_float_add", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_EXT_shader_atomic_float_min_max)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_EXT_shader_atomic_float_min_max", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_EXT_shader_image_int64)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_EXT_shader_image_int64", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_EXT_fragment_shader_interlock)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_EXT_fragment_shader_interlock", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_EXT_fragment_invocation_density)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_EXT_fragment_invocation_density", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_EXT_shader_viewport_index_layer)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_EXT_shader_viewport_index_layer", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_INTEL_loop_fuse)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_INTEL_loop_fuse", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_INTEL_fpga_dsp_control)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_INTEL_fpga_dsp_control", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_INTEL_fpga_reg)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_INTEL_fpga_reg", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_INTEL_fpga_memory_accesses)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_INTEL_fpga_memory_accesses", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_INTEL_fpga_loop_controls)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_INTEL_fpga_loop_controls", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_INTEL_io_pipes)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_INTEL_io_pipes", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_INTEL_unstructured_loop_controls)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_INTEL_unstructured_loop_controls", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_INTEL_blocking_pipes)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_INTEL_blocking_pipes", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_INTEL_device_side_avc_motion_estimation)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_INTEL_device_side_avc_motion_estimation", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_INTEL_fpga_memory_attributes)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_INTEL_fpga_memory_attributes", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_INTEL_fp_fast_math_mode)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_INTEL_fp_fast_math_mode", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_INTEL_media_block_io)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_INTEL_media_block_io", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_INTEL_shader_integer_functions2)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_INTEL_shader_integer_functions2", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_INTEL_subgroups)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_INTEL_subgroups", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_INTEL_fpga_cluster_attributes)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_INTEL_fpga_cluster_attributes", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_INTEL_kernel_attributes)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_INTEL_kernel_attributes", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_INTEL_arbitrary_precision_integers)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_INTEL_arbitrary_precision_integers", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_KHR_8bit_storage)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_KHR_8bit_storage", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_KHR_shader_clock)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_KHR_shader_clock", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_KHR_device_group)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_KHR_device_group", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_KHR_16bit_storage)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_KHR_16bit_storage", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_KHR_variable_pointers)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_KHR_variable_pointers", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_KHR_no_integer_wrap_decoration)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_KHR_no_integer_wrap_decoration", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_KHR_subgroup_vote)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_KHR_subgroup_vote", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_KHR_multiview)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_KHR_multiview", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_KHR_shader_ballot)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_KHR_shader_ballot", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_KHR_vulkan_memory_model)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_KHR_vulkan_memory_model", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_KHR_physical_storage_buffer)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_KHR_physical_storage_buffer", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_KHR_workgroup_memory_explicit_layout)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_KHR_workgroup_memory_explicit_layout", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_KHR_fragment_shading_rate)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_KHR_fragment_shading_rate", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_KHR_shader_atomic_counter_ops)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_KHR_shader_atomic_counter_ops", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_KHR_shader_draw_parameters)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_KHR_shader_draw_parameters", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_KHR_storage_buffer_storage_class)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_KHR_storage_buffer_storage_class", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_KHR_linkonce_odr)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_KHR_linkonce_odr", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_KHR_terminate_invocation)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_KHR_terminate_invocation", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_KHR_non_semantic_info)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_KHR_non_semantic_info", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_KHR_post_depth_coverage)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_KHR_post_depth_coverage", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_KHR_expect_assume)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_KHR_expect_assume", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_KHR_ray_tracing)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_KHR_ray_tracing", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_KHR_ray_query)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_KHR_ray_query", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_KHR_float_controls)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_KHR_float_controls", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_NV_viewport_array2)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_NV_viewport_array2", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_NV_shader_subgroup_partitioned)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_NV_shader_subgroup_partitioned", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_NVX_multiview_per_view_attributes)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_NVX_multiview_per_view_attributes", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_NV_ray_tracing)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_NV_ray_tracing", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_NV_shader_image_footprint)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_NV_shader_image_footprint", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_NV_shading_rate)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_NV_shading_rate", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_NV_stereo_view_rendering)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_NV_stereo_view_rendering", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_NV_compute_shader_derivatives)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_NV_compute_shader_derivatives", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_NV_shader_sm_builtins)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_NV_shader_sm_builtins", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_NV_mesh_shader)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_NV_mesh_shader", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_NV_geometry_shader_passthrough)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_NV_geometry_shader_passthrough", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_NV_fragment_shader_barycentric)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_NV_fragment_shader_barycentric", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_NV_cooperative_matrix)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_NV_cooperative_matrix", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.SPV_NV_sample_mask_override_coverage)] = .{ + .llvm_name = null, + .description = "SPIR-V extension SPV_NV_sample_mask_override_coverage", + .dependencies = featureSet(&[_]Feature{}), + }; + result[@enumToInt(Feature.Matrix)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability Matrix", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.Shader)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability Shader", + .dependencies = featureSet(&[_]Feature{ + .Matrix, + }), + }; + result[@enumToInt(Feature.Geometry)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability Geometry", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.Tessellation)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability Tessellation", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.Addresses)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability Addresses", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.Linkage)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability Linkage", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.Kernel)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability Kernel", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.Vector16)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability Vector16", + .dependencies = featureSet(&[_]Feature{ + .Kernel, + }), + }; + result[@enumToInt(Feature.Float16Buffer)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability Float16Buffer", + .dependencies = featureSet(&[_]Feature{ + .Kernel, + }), + }; + result[@enumToInt(Feature.Float16)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability Float16", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.Float64)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability Float64", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.Int64)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability Int64", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.Int64Atomics)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability Int64Atomics", + .dependencies = featureSet(&[_]Feature{ + .Int64, + }), + }; + result[@enumToInt(Feature.ImageBasic)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability ImageBasic", + .dependencies = featureSet(&[_]Feature{ + .Kernel, + }), + }; + result[@enumToInt(Feature.ImageReadWrite)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability ImageReadWrite", + .dependencies = featureSet(&[_]Feature{ + .ImageBasic, + }), + }; + result[@enumToInt(Feature.ImageMipmap)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability ImageMipmap", + .dependencies = featureSet(&[_]Feature{ + .ImageBasic, + }), + }; + result[@enumToInt(Feature.Pipes)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability Pipes", + .dependencies = featureSet(&[_]Feature{ + .Kernel, + }), + }; + result[@enumToInt(Feature.Groups)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability Groups", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.DeviceEnqueue)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability DeviceEnqueue", + .dependencies = featureSet(&[_]Feature{ + .Kernel, + }), + }; + result[@enumToInt(Feature.LiteralSampler)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability LiteralSampler", + .dependencies = featureSet(&[_]Feature{ + .Kernel, + }), + }; + result[@enumToInt(Feature.AtomicStorage)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability AtomicStorage", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.Int16)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability Int16", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.TessellationPointSize)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability TessellationPointSize", + .dependencies = featureSet(&[_]Feature{ + .Tessellation, + }), + }; + result[@enumToInt(Feature.GeometryPointSize)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability GeometryPointSize", + .dependencies = featureSet(&[_]Feature{ + .Geometry, + }), + }; + result[@enumToInt(Feature.ImageGatherExtended)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability ImageGatherExtended", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.StorageImageMultisample)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability StorageImageMultisample", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.UniformBufferArrayDynamicIndexing)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability UniformBufferArrayDynamicIndexing", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.SampledImageArrayDynamicIndexing)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability SampledImageArrayDynamicIndexing", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.StorageBufferArrayDynamicIndexing)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability StorageBufferArrayDynamicIndexing", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.StorageImageArrayDynamicIndexing)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability StorageImageArrayDynamicIndexing", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.ClipDistance)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability ClipDistance", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.CullDistance)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability CullDistance", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.ImageCubeArray)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability ImageCubeArray", + .dependencies = featureSet(&[_]Feature{ + .SampledCubeArray, + }), + }; + result[@enumToInt(Feature.SampleRateShading)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability SampleRateShading", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.ImageRect)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability ImageRect", + .dependencies = featureSet(&[_]Feature{ + .SampledRect, + }), + }; + result[@enumToInt(Feature.SampledRect)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability SampledRect", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.GenericPointer)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability GenericPointer", + .dependencies = featureSet(&[_]Feature{ + .Addresses, + }), + }; + result[@enumToInt(Feature.Int8)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability Int8", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.InputAttachment)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability InputAttachment", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.SparseResidency)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability SparseResidency", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.MinLod)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability MinLod", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.Sampled1D)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability Sampled1D", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.Image1D)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability Image1D", + .dependencies = featureSet(&[_]Feature{ + .Sampled1D, + }), + }; + result[@enumToInt(Feature.SampledCubeArray)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability SampledCubeArray", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.SampledBuffer)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability SampledBuffer", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.ImageBuffer)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability ImageBuffer", + .dependencies = featureSet(&[_]Feature{ + .SampledBuffer, + }), + }; + result[@enumToInt(Feature.ImageMSArray)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability ImageMSArray", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.StorageImageExtendedFormats)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability StorageImageExtendedFormats", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.ImageQuery)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability ImageQuery", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.DerivativeControl)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability DerivativeControl", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.InterpolationFunction)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability InterpolationFunction", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.TransformFeedback)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability TransformFeedback", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.GeometryStreams)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability GeometryStreams", + .dependencies = featureSet(&[_]Feature{ + .Geometry, + }), + }; + result[@enumToInt(Feature.StorageImageReadWithoutFormat)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability StorageImageReadWithoutFormat", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.StorageImageWriteWithoutFormat)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability StorageImageWriteWithoutFormat", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.MultiViewport)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability MultiViewport", + .dependencies = featureSet(&[_]Feature{ + .Geometry, + }), + }; + result[@enumToInt(Feature.SubgroupDispatch)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability SubgroupDispatch", + .dependencies = featureSet(&[_]Feature{ + .v1_1, + .DeviceEnqueue, + }), + }; + result[@enumToInt(Feature.NamedBarrier)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability NamedBarrier", + .dependencies = featureSet(&[_]Feature{ + .v1_1, + .Kernel, + }), + }; + result[@enumToInt(Feature.PipeStorage)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability PipeStorage", + .dependencies = featureSet(&[_]Feature{ + .v1_1, + .Pipes, + }), + }; + result[@enumToInt(Feature.GroupNonUniform)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability GroupNonUniform", + .dependencies = featureSet(&[_]Feature{ + .v1_3, + }), + }; + result[@enumToInt(Feature.GroupNonUniformVote)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability GroupNonUniformVote", + .dependencies = featureSet(&[_]Feature{ + .v1_3, + .GroupNonUniform, + }), + }; + result[@enumToInt(Feature.GroupNonUniformArithmetic)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability GroupNonUniformArithmetic", + .dependencies = featureSet(&[_]Feature{ + .v1_3, + .GroupNonUniform, + }), + }; + result[@enumToInt(Feature.GroupNonUniformBallot)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability GroupNonUniformBallot", + .dependencies = featureSet(&[_]Feature{ + .v1_3, + .GroupNonUniform, + }), + }; + result[@enumToInt(Feature.GroupNonUniformShuffle)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability GroupNonUniformShuffle", + .dependencies = featureSet(&[_]Feature{ + .v1_3, + .GroupNonUniform, + }), + }; + result[@enumToInt(Feature.GroupNonUniformShuffleRelative)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability GroupNonUniformShuffleRelative", + .dependencies = featureSet(&[_]Feature{ + .v1_3, + .GroupNonUniform, + }), + }; + result[@enumToInt(Feature.GroupNonUniformClustered)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability GroupNonUniformClustered", + .dependencies = featureSet(&[_]Feature{ + .v1_3, + .GroupNonUniform, + }), + }; + result[@enumToInt(Feature.GroupNonUniformQuad)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability GroupNonUniformQuad", + .dependencies = featureSet(&[_]Feature{ + .v1_3, + .GroupNonUniform, + }), + }; + result[@enumToInt(Feature.ShaderLayer)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability ShaderLayer", + .dependencies = featureSet(&[_]Feature{ + .v1_5, + }), + }; + result[@enumToInt(Feature.ShaderViewportIndex)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability ShaderViewportIndex", + .dependencies = featureSet(&[_]Feature{ + .v1_5, + }), + }; + result[@enumToInt(Feature.FragmentShadingRateKHR)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability FragmentShadingRateKHR", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.SubgroupBallotKHR)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability SubgroupBallotKHR", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.DrawParameters)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability DrawParameters", + .dependencies = featureSet(&[_]Feature{ + .v1_3, + .Shader, + }), + }; + result[@enumToInt(Feature.WorkgroupMemoryExplicitLayoutKHR)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability WorkgroupMemoryExplicitLayoutKHR", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.WorkgroupMemoryExplicitLayout8BitAccessKHR)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability WorkgroupMemoryExplicitLayout8BitAccessKHR", + .dependencies = featureSet(&[_]Feature{ + .WorkgroupMemoryExplicitLayoutKHR, + }), + }; + result[@enumToInt(Feature.WorkgroupMemoryExplicitLayout16BitAccessKHR)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability WorkgroupMemoryExplicitLayout16BitAccessKHR", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.SubgroupVoteKHR)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability SubgroupVoteKHR", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.StorageBuffer16BitAccess)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability StorageBuffer16BitAccess", + .dependencies = featureSet(&[_]Feature{ + .v1_3, + }), + }; + result[@enumToInt(Feature.StorageUniformBufferBlock16)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability StorageUniformBufferBlock16", + .dependencies = featureSet(&[_]Feature{ + .v1_3, + }), + }; + result[@enumToInt(Feature.UniformAndStorageBuffer16BitAccess)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability UniformAndStorageBuffer16BitAccess", + .dependencies = featureSet(&[_]Feature{ + .v1_3, + .StorageBuffer16BitAccess, + .StorageUniformBufferBlock16, + }), + }; + result[@enumToInt(Feature.StorageUniform16)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability StorageUniform16", + .dependencies = featureSet(&[_]Feature{ + .v1_3, + .StorageBuffer16BitAccess, + .StorageUniformBufferBlock16, + }), + }; + result[@enumToInt(Feature.StoragePushConstant16)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability StoragePushConstant16", + .dependencies = featureSet(&[_]Feature{ + .v1_3, + }), + }; + result[@enumToInt(Feature.StorageInputOutput16)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability StorageInputOutput16", + .dependencies = featureSet(&[_]Feature{ + .v1_3, + }), + }; + result[@enumToInt(Feature.DeviceGroup)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability DeviceGroup", + .dependencies = featureSet(&[_]Feature{ + .v1_3, + }), + }; + result[@enumToInt(Feature.MultiView)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability MultiView", + .dependencies = featureSet(&[_]Feature{ + .v1_3, + .Shader, + }), + }; + result[@enumToInt(Feature.VariablePointersStorageBuffer)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability VariablePointersStorageBuffer", + .dependencies = featureSet(&[_]Feature{ + .v1_3, + .Shader, + }), + }; + result[@enumToInt(Feature.VariablePointers)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability VariablePointers", + .dependencies = featureSet(&[_]Feature{ + .v1_3, + .VariablePointersStorageBuffer, + }), + }; + result[@enumToInt(Feature.AtomicStorageOps)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability AtomicStorageOps", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.SampleMaskPostDepthCoverage)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability SampleMaskPostDepthCoverage", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.StorageBuffer8BitAccess)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability StorageBuffer8BitAccess", + .dependencies = featureSet(&[_]Feature{ + .v1_5, + }), + }; + result[@enumToInt(Feature.UniformAndStorageBuffer8BitAccess)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability UniformAndStorageBuffer8BitAccess", + .dependencies = featureSet(&[_]Feature{ + .v1_5, + .StorageBuffer8BitAccess, + }), + }; + result[@enumToInt(Feature.StoragePushConstant8)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability StoragePushConstant8", + .dependencies = featureSet(&[_]Feature{ + .v1_5, + }), + }; + result[@enumToInt(Feature.DenormPreserve)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability DenormPreserve", + .dependencies = featureSet(&[_]Feature{ + .v1_4, + }), + }; + result[@enumToInt(Feature.DenormFlushToZero)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability DenormFlushToZero", + .dependencies = featureSet(&[_]Feature{ + .v1_4, + }), + }; + result[@enumToInt(Feature.SignedZeroInfNanPreserve)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability SignedZeroInfNanPreserve", + .dependencies = featureSet(&[_]Feature{ + .v1_4, + }), + }; + result[@enumToInt(Feature.RoundingModeRTE)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability RoundingModeRTE", + .dependencies = featureSet(&[_]Feature{ + .v1_4, + }), + }; + result[@enumToInt(Feature.RoundingModeRTZ)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability RoundingModeRTZ", + .dependencies = featureSet(&[_]Feature{ + .v1_4, + }), + }; + result[@enumToInt(Feature.RayQueryProvisionalKHR)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability RayQueryProvisionalKHR", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.RayQueryKHR)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability RayQueryKHR", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.RayTraversalPrimitiveCullingKHR)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability RayTraversalPrimitiveCullingKHR", + .dependencies = featureSet(&[_]Feature{ + .RayQueryKHR, + .RayTracingKHR, + }), + }; + result[@enumToInt(Feature.RayTracingKHR)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability RayTracingKHR", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.Float16ImageAMD)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability Float16ImageAMD", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.ImageGatherBiasLodAMD)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability ImageGatherBiasLodAMD", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.FragmentMaskAMD)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability FragmentMaskAMD", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.StencilExportEXT)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability StencilExportEXT", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.ImageReadWriteLodAMD)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability ImageReadWriteLodAMD", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.Int64ImageEXT)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability Int64ImageEXT", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.ShaderClockKHR)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability ShaderClockKHR", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.SampleMaskOverrideCoverageNV)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability SampleMaskOverrideCoverageNV", + .dependencies = featureSet(&[_]Feature{ + .SampleRateShading, + }), + }; + result[@enumToInt(Feature.GeometryShaderPassthroughNV)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability GeometryShaderPassthroughNV", + .dependencies = featureSet(&[_]Feature{ + .Geometry, + }), + }; + result[@enumToInt(Feature.ShaderViewportIndexLayerEXT)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability ShaderViewportIndexLayerEXT", + .dependencies = featureSet(&[_]Feature{ + .MultiViewport, + }), + }; + result[@enumToInt(Feature.ShaderViewportIndexLayerNV)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability ShaderViewportIndexLayerNV", + .dependencies = featureSet(&[_]Feature{ + .MultiViewport, + }), + }; + result[@enumToInt(Feature.ShaderViewportMaskNV)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability ShaderViewportMaskNV", + .dependencies = featureSet(&[_]Feature{ + .ShaderViewportIndexLayerNV, + }), + }; + result[@enumToInt(Feature.ShaderStereoViewNV)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability ShaderStereoViewNV", + .dependencies = featureSet(&[_]Feature{ + .ShaderViewportMaskNV, + }), + }; + result[@enumToInt(Feature.PerViewAttributesNV)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability PerViewAttributesNV", + .dependencies = featureSet(&[_]Feature{ + .MultiView, + }), + }; + result[@enumToInt(Feature.FragmentFullyCoveredEXT)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability FragmentFullyCoveredEXT", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.MeshShadingNV)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability MeshShadingNV", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.ImageFootprintNV)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability ImageFootprintNV", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.FragmentBarycentricNV)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability FragmentBarycentricNV", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.ComputeDerivativeGroupQuadsNV)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability ComputeDerivativeGroupQuadsNV", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.FragmentDensityEXT)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability FragmentDensityEXT", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.ShadingRateNV)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability ShadingRateNV", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.GroupNonUniformPartitionedNV)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability GroupNonUniformPartitionedNV", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.ShaderNonUniform)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability ShaderNonUniform", + .dependencies = featureSet(&[_]Feature{ + .v1_5, + .Shader, + }), + }; + result[@enumToInt(Feature.ShaderNonUniformEXT)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability ShaderNonUniformEXT", + .dependencies = featureSet(&[_]Feature{ + .v1_5, + .Shader, + }), + }; + result[@enumToInt(Feature.RuntimeDescriptorArray)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability RuntimeDescriptorArray", + .dependencies = featureSet(&[_]Feature{ + .v1_5, + .Shader, + }), + }; + result[@enumToInt(Feature.RuntimeDescriptorArrayEXT)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability RuntimeDescriptorArrayEXT", + .dependencies = featureSet(&[_]Feature{ + .v1_5, + .Shader, + }), + }; + result[@enumToInt(Feature.InputAttachmentArrayDynamicIndexing)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability InputAttachmentArrayDynamicIndexing", + .dependencies = featureSet(&[_]Feature{ + .v1_5, + .InputAttachment, + }), + }; + result[@enumToInt(Feature.InputAttachmentArrayDynamicIndexingEXT)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability InputAttachmentArrayDynamicIndexingEXT", + .dependencies = featureSet(&[_]Feature{ + .v1_5, + .InputAttachment, + }), + }; + result[@enumToInt(Feature.UniformTexelBufferArrayDynamicIndexing)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability UniformTexelBufferArrayDynamicIndexing", + .dependencies = featureSet(&[_]Feature{ + .v1_5, + .SampledBuffer, + }), + }; + result[@enumToInt(Feature.UniformTexelBufferArrayDynamicIndexingEXT)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability UniformTexelBufferArrayDynamicIndexingEXT", + .dependencies = featureSet(&[_]Feature{ + .v1_5, + .SampledBuffer, + }), + }; + result[@enumToInt(Feature.StorageTexelBufferArrayDynamicIndexing)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability StorageTexelBufferArrayDynamicIndexing", + .dependencies = featureSet(&[_]Feature{ + .v1_5, + .ImageBuffer, + }), + }; + result[@enumToInt(Feature.StorageTexelBufferArrayDynamicIndexingEXT)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability StorageTexelBufferArrayDynamicIndexingEXT", + .dependencies = featureSet(&[_]Feature{ + .v1_5, + .ImageBuffer, + }), + }; + result[@enumToInt(Feature.UniformBufferArrayNonUniformIndexing)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability UniformBufferArrayNonUniformIndexing", + .dependencies = featureSet(&[_]Feature{ + .v1_5, + .ShaderNonUniform, + }), + }; + result[@enumToInt(Feature.UniformBufferArrayNonUniformIndexingEXT)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability UniformBufferArrayNonUniformIndexingEXT", + .dependencies = featureSet(&[_]Feature{ + .v1_5, + .ShaderNonUniform, + }), + }; + result[@enumToInt(Feature.SampledImageArrayNonUniformIndexing)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability SampledImageArrayNonUniformIndexing", + .dependencies = featureSet(&[_]Feature{ + .v1_5, + .ShaderNonUniform, + }), + }; + result[@enumToInt(Feature.SampledImageArrayNonUniformIndexingEXT)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability SampledImageArrayNonUniformIndexingEXT", + .dependencies = featureSet(&[_]Feature{ + .v1_5, + .ShaderNonUniform, + }), + }; + result[@enumToInt(Feature.StorageBufferArrayNonUniformIndexing)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability StorageBufferArrayNonUniformIndexing", + .dependencies = featureSet(&[_]Feature{ + .v1_5, + .ShaderNonUniform, + }), + }; + result[@enumToInt(Feature.StorageBufferArrayNonUniformIndexingEXT)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability StorageBufferArrayNonUniformIndexingEXT", + .dependencies = featureSet(&[_]Feature{ + .v1_5, + .ShaderNonUniform, + }), + }; + result[@enumToInt(Feature.StorageImageArrayNonUniformIndexing)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability StorageImageArrayNonUniformIndexing", + .dependencies = featureSet(&[_]Feature{ + .v1_5, + .ShaderNonUniform, + }), + }; + result[@enumToInt(Feature.StorageImageArrayNonUniformIndexingEXT)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability StorageImageArrayNonUniformIndexingEXT", + .dependencies = featureSet(&[_]Feature{ + .v1_5, + .ShaderNonUniform, + }), + }; + result[@enumToInt(Feature.InputAttachmentArrayNonUniformIndexing)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability InputAttachmentArrayNonUniformIndexing", + .dependencies = featureSet(&[_]Feature{ + .v1_5, + .InputAttachment, + .ShaderNonUniform, + }), + }; + result[@enumToInt(Feature.InputAttachmentArrayNonUniformIndexingEXT)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability InputAttachmentArrayNonUniformIndexingEXT", + .dependencies = featureSet(&[_]Feature{ + .v1_5, + .InputAttachment, + .ShaderNonUniform, + }), + }; + result[@enumToInt(Feature.UniformTexelBufferArrayNonUniformIndexing)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability UniformTexelBufferArrayNonUniformIndexing", + .dependencies = featureSet(&[_]Feature{ + .v1_5, + .SampledBuffer, + .ShaderNonUniform, + }), + }; + result[@enumToInt(Feature.UniformTexelBufferArrayNonUniformIndexingEXT)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability UniformTexelBufferArrayNonUniformIndexingEXT", + .dependencies = featureSet(&[_]Feature{ + .v1_5, + .SampledBuffer, + .ShaderNonUniform, + }), + }; + result[@enumToInt(Feature.StorageTexelBufferArrayNonUniformIndexing)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability StorageTexelBufferArrayNonUniformIndexing", + .dependencies = featureSet(&[_]Feature{ + .v1_5, + .ImageBuffer, + .ShaderNonUniform, + }), + }; + result[@enumToInt(Feature.StorageTexelBufferArrayNonUniformIndexingEXT)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability StorageTexelBufferArrayNonUniformIndexingEXT", + .dependencies = featureSet(&[_]Feature{ + .v1_5, + .ImageBuffer, + .ShaderNonUniform, + }), + }; + result[@enumToInt(Feature.RayTracingNV)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability RayTracingNV", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.VulkanMemoryModel)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability VulkanMemoryModel", + .dependencies = featureSet(&[_]Feature{ + .v1_5, + }), + }; + result[@enumToInt(Feature.VulkanMemoryModelKHR)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability VulkanMemoryModelKHR", + .dependencies = featureSet(&[_]Feature{ + .v1_5, + }), + }; + result[@enumToInt(Feature.VulkanMemoryModelDeviceScope)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability VulkanMemoryModelDeviceScope", + .dependencies = featureSet(&[_]Feature{ + .v1_5, + }), + }; + result[@enumToInt(Feature.VulkanMemoryModelDeviceScopeKHR)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability VulkanMemoryModelDeviceScopeKHR", + .dependencies = featureSet(&[_]Feature{ + .v1_5, + }), + }; + result[@enumToInt(Feature.PhysicalStorageBufferAddresses)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability PhysicalStorageBufferAddresses", + .dependencies = featureSet(&[_]Feature{ + .v1_5, + .Shader, + }), + }; + result[@enumToInt(Feature.PhysicalStorageBufferAddressesEXT)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability PhysicalStorageBufferAddressesEXT", + .dependencies = featureSet(&[_]Feature{ + .v1_5, + .Shader, + }), + }; + result[@enumToInt(Feature.ComputeDerivativeGroupLinearNV)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability ComputeDerivativeGroupLinearNV", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.RayTracingProvisionalKHR)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability RayTracingProvisionalKHR", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.CooperativeMatrixNV)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability CooperativeMatrixNV", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.FragmentShaderSampleInterlockEXT)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability FragmentShaderSampleInterlockEXT", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.FragmentShaderShadingRateInterlockEXT)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability FragmentShaderShadingRateInterlockEXT", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.ShaderSMBuiltinsNV)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability ShaderSMBuiltinsNV", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.FragmentShaderPixelInterlockEXT)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability FragmentShaderPixelInterlockEXT", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.DemoteToHelperInvocationEXT)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability DemoteToHelperInvocationEXT", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.SubgroupShuffleINTEL)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability SubgroupShuffleINTEL", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.SubgroupBufferBlockIOINTEL)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability SubgroupBufferBlockIOINTEL", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.SubgroupImageBlockIOINTEL)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability SubgroupImageBlockIOINTEL", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.SubgroupImageMediaBlockIOINTEL)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability SubgroupImageMediaBlockIOINTEL", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.RoundToInfinityINTEL)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability RoundToInfinityINTEL", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.FloatingPointModeINTEL)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability FloatingPointModeINTEL", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.IntegerFunctions2INTEL)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability IntegerFunctions2INTEL", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.FunctionPointersINTEL)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability FunctionPointersINTEL", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.IndirectReferencesINTEL)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability IndirectReferencesINTEL", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.AsmINTEL)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability AsmINTEL", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.AtomicFloat32MinMaxEXT)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability AtomicFloat32MinMaxEXT", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.AtomicFloat64MinMaxEXT)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability AtomicFloat64MinMaxEXT", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.AtomicFloat16MinMaxEXT)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability AtomicFloat16MinMaxEXT", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.VectorComputeINTEL)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability VectorComputeINTEL", + .dependencies = featureSet(&[_]Feature{ + .VectorAnyINTEL, + }), + }; + result[@enumToInt(Feature.VectorAnyINTEL)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability VectorAnyINTEL", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.ExpectAssumeKHR)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability ExpectAssumeKHR", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.SubgroupAvcMotionEstimationINTEL)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability SubgroupAvcMotionEstimationINTEL", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.SubgroupAvcMotionEstimationIntraINTEL)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability SubgroupAvcMotionEstimationIntraINTEL", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.SubgroupAvcMotionEstimationChromaINTEL)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability SubgroupAvcMotionEstimationChromaINTEL", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.VariableLengthArrayINTEL)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability VariableLengthArrayINTEL", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.FunctionFloatControlINTEL)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability FunctionFloatControlINTEL", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.FPGAMemoryAttributesINTEL)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability FPGAMemoryAttributesINTEL", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.FPFastMathModeINTEL)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability FPFastMathModeINTEL", + .dependencies = featureSet(&[_]Feature{ + .Kernel, + }), + }; + result[@enumToInt(Feature.ArbitraryPrecisionIntegersINTEL)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability ArbitraryPrecisionIntegersINTEL", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.UnstructuredLoopControlsINTEL)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability UnstructuredLoopControlsINTEL", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.FPGALoopControlsINTEL)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability FPGALoopControlsINTEL", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.KernelAttributesINTEL)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability KernelAttributesINTEL", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.FPGAKernelAttributesINTEL)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability FPGAKernelAttributesINTEL", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.FPGAMemoryAccessesINTEL)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability FPGAMemoryAccessesINTEL", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.FPGAClusterAttributesINTEL)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability FPGAClusterAttributesINTEL", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.LoopFuseINTEL)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability LoopFuseINTEL", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.FPGABufferLocationINTEL)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability FPGABufferLocationINTEL", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.USMStorageClassesINTEL)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability USMStorageClassesINTEL", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.IOPipesINTEL)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability IOPipesINTEL", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.BlockingPipesINTEL)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability BlockingPipesINTEL", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.FPGARegINTEL)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability FPGARegINTEL", + .dependencies = featureSet(&[_]Feature{ + }), + }; + result[@enumToInt(Feature.AtomicFloat32AddEXT)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability AtomicFloat32AddEXT", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.AtomicFloat64AddEXT)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability AtomicFloat64AddEXT", + .dependencies = featureSet(&[_]Feature{ + .Shader, + }), + }; + result[@enumToInt(Feature.LongConstantCompositeINTEL)] = .{ + .llvm_name = null, + .description = "Enable SPIR-V capability LongConstantCompositeINTEL", + .dependencies = featureSet(&[_]Feature{ + }), + }; + const ti = @typeInfo(Feature); + for (result) |*elem, i| { + elem.index = i; + elem.name = ti.Enum.fields[i].name; + } + break :blk result; +}; From 00428ac11b4d5b71ded1f9e572652ff52814f7b7 Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Fri, 14 May 2021 19:46:38 +0200 Subject: [PATCH 29/42] SPIR-V: Don't parse/render in gen_spirv_spec.zig, just emit in the right format --- tools/gen_spirv_spec.zig | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/tools/gen_spirv_spec.zig b/tools/gen_spirv_spec.zig index 2176d1fa5e..4e6d559c41 100644 --- a/tools/gen_spirv_spec.zig +++ b/tools/gen_spirv_spec.zig @@ -1,6 +1,5 @@ const std = @import("std"); const g = @import("spirv/grammar.zig"); -const Writer = std.ArrayList(u8).Writer; pub fn main() !void { var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); @@ -18,17 +17,12 @@ pub fn main() !void { var tokens = std.json.TokenStream.init(spec); var registry = try std.json.parse(g.Registry, &tokens, .{.allocator = allocator}); - var buf = std.ArrayList(u8).init(allocator); - defer buf.deinit(); - - try render(buf.writer(), registry); - - const tree = try std.zig.parse(allocator, buf.items); - const formatted = try tree.render(allocator); - try std.io.getStdOut().writeAll(formatted); + var bw = std.io.bufferedWriter(std.io.getStdOut().writer()); + try render(bw.writer(), registry); + try bw.flush(); } -fn render(writer: Writer, registry: g.Registry) !void { +fn render(writer: anytype, registry: g.Registry) !void { try writer.writeAll( \\//! This file is auto-generated by tools/gen_spirv_spec.zig. \\ @@ -39,7 +33,7 @@ fn render(writer: Writer, registry: g.Registry) !void { switch (registry) { .core => |core_reg| { try writer.print( - \\pub const version = Version{{.major = {}, .minor = {}, .patch = {}}}; + \\pub const version = Version{{ .major = {}, .minor = {}, .patch = {} }}; \\pub const magic_number: u32 = {s}; \\ , .{ core_reg.major_version, core_reg.minor_version, core_reg.revision, core_reg.magic_number }, @@ -49,7 +43,7 @@ fn render(writer: Writer, registry: g.Registry) !void { }, .extension => |ext_reg| { try writer.print( - \\pub const version = Version{{.major = {}, .minor = 0, .patch = {}}}; + \\pub const version = Version{{ .major = {}, .minor = 0, .patch = {} }}; \\ , .{ ext_reg.version, ext_reg.revision }, ); @@ -59,15 +53,15 @@ fn render(writer: Writer, registry: g.Registry) !void { } } -fn renderOpcodes(writer: Writer, instructions: []const g.Instruction) !void { +fn renderOpcodes(writer: anytype, instructions: []const g.Instruction) !void { try writer.writeAll("pub const Opcode = extern enum(u16) {\n"); for (instructions) |instr| { - try writer.print("{} = {},\n", .{ std.zig.fmtId(instr.opname), instr.opcode }); + try writer.print(" {} = {},\n", .{ std.zig.fmtId(instr.opname), instr.opcode }); } - try writer.writeAll("_,\n};\n"); + try writer.writeAll(" _,\n};\n"); } -fn renderOperandKinds(writer: Writer, kinds: []const g.OperandKind) !void { +fn renderOperandKinds(writer: anytype, kinds: []const g.OperandKind) !void { for (kinds) |kind| { switch (kind.category) { .ValueEnum => try renderValueEnum(writer, kind), @@ -77,20 +71,20 @@ fn renderOperandKinds(writer: Writer, kinds: []const g.OperandKind) !void { } } -fn renderValueEnum(writer: Writer, enumeration: g.OperandKind) !void { +fn renderValueEnum(writer: anytype, enumeration: g.OperandKind) !void { try writer.print("pub const {s} = extern enum(u32) {{\n", .{ enumeration.kind }); const enumerants = enumeration.enumerants orelse return error.InvalidRegistry; for (enumerants) |enumerant| { if (enumerant.value != .int) return error.InvalidRegistry; - try writer.print("{} = {},\n", .{ std.zig.fmtId(enumerant.enumerant), enumerant.value.int }); + try writer.print(" {} = {},\n", .{ std.zig.fmtId(enumerant.enumerant), enumerant.value.int }); } - try writer.writeAll("_,\n};\n"); + try writer.writeAll(" _,\n};\n"); } -fn renderBitEnum(writer: Writer, enumeration: g.OperandKind) !void { +fn renderBitEnum(writer: anytype, enumeration: g.OperandKind) !void { try writer.print("pub const {s} = packed struct {{\n", .{ enumeration.kind }); var flags_by_bitpos = [_]?[]const u8{null} ** 32; @@ -113,6 +107,7 @@ fn renderBitEnum(writer: Writer, enumeration: g.OperandKind) !void { } for (flags_by_bitpos) |maybe_flag_name, bitpos| { + try writer.writeAll(" "); if (maybe_flag_name) |flag_name| { try writer.writeAll(flag_name); } else { @@ -123,7 +118,7 @@ fn renderBitEnum(writer: Writer, enumeration: g.OperandKind) !void { if (bitpos == 0) { // Force alignment to integer boundaries try writer.writeAll("align(@alignOf(u32)) "); } - try writer.writeAll("= false, "); + try writer.writeAll("= false,\n"); } try writer.writeAll("};\n"); From 5185b5619ac703755849da70d158b5a45e5673a7 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Fri, 14 May 2021 12:17:58 +0200 Subject: [PATCH 30/42] compiler-rt: Fix signedness mismatch in f128 mul impl The `1 - shift` expression was computed using small unsigned types and then casted to i32, producing either an underflow error or an incorrect result. Reported by `@notviri` in #8733 --- lib/std/special/compiler_rt/mulXf3.zig | 12 ++++++------ lib/std/special/compiler_rt/mulXf3_test.zig | 14 ++++++++++++++ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/lib/std/special/compiler_rt/mulXf3.zig b/lib/std/special/compiler_rt/mulXf3.zig index a4c71529d1..20828badd3 100644 --- a/lib/std/special/compiler_rt/mulXf3.zig +++ b/lib/std/special/compiler_rt/mulXf3.zig @@ -98,8 +98,8 @@ fn mulXf3(comptime T: type, a: T, b: T) T { // one or both of a or b is denormal, the other (if applicable) is a // normal number. Renormalize one or both of a and b, and set scale to // include the necessary exponent adjustment. - if (aAbs < implicitBit) scale +%= normalize(T, &aSignificand); - if (bAbs < implicitBit) scale +%= normalize(T, &bSignificand); + if (aAbs < implicitBit) scale += normalize(T, &aSignificand); + if (bAbs < implicitBit) scale += normalize(T, &bSignificand); } // Or in the implicit significand bit. (If we fell through from the @@ -277,7 +277,7 @@ fn normalize(comptime T: type, significand: *std.meta.Int(.unsigned, @typeInfo(T const shift = @clz(Z, significand.*) - @clz(Z, implicitBit); significand.* <<= @intCast(std.math.Log2Int(Z), shift); - return 1 - shift; + return @as(i32, 1) - shift; } fn wideRightShiftWithSticky(comptime Z: type, hi: *Z, lo: *Z, count: u32) void { @@ -285,15 +285,15 @@ fn wideRightShiftWithSticky(comptime Z: type, hi: *Z, lo: *Z, count: u32) void { const typeWidth = @typeInfo(Z).Int.bits; const S = std.math.Log2Int(Z); if (count < typeWidth) { - const sticky = @truncate(u8, lo.* << @intCast(S, typeWidth -% count)); + const sticky = @boolToInt((lo.* << @intCast(S, typeWidth -% count)) != 0); lo.* = (hi.* << @intCast(S, typeWidth -% count)) | (lo.* >> @intCast(S, count)) | sticky; hi.* = hi.* >> @intCast(S, count); } else if (count < 2 * typeWidth) { - const sticky = @truncate(u8, hi.* << @intCast(S, 2 * typeWidth -% count) | lo.*); + const sticky = @boolToInt((hi.* << @intCast(S, 2 * typeWidth -% count) | lo.*) != 0); lo.* = hi.* >> @intCast(S, count -% typeWidth) | sticky; hi.* = 0; } else { - const sticky = @truncate(u8, hi.* | lo.*); + const sticky = @boolToInt((hi.* | lo.*) != 0); lo.* = sticky; hi.* = 0; } diff --git a/lib/std/special/compiler_rt/mulXf3_test.zig b/lib/std/special/compiler_rt/mulXf3_test.zig index b73f03d6c1..d3a61710a1 100644 --- a/lib/std/special/compiler_rt/mulXf3_test.zig +++ b/lib/std/special/compiler_rt/mulXf3_test.zig @@ -88,4 +88,18 @@ test "multf3" { ); try test__multf3(0x1.23456734245345p-10000, 0x1.edcba524498724p-6497, 0x0, 0x0); + + // Denormal operands. + try test__multf3( + 0x0.0000000000000000000000000001p-16382, + 0x1.p16383, + 0x3f90000000000000, + 0x0, + ); + try test__multf3( + 0x1.p16383, + 0x0.0000000000000000000000000001p-16382, + 0x3f90000000000000, + 0x0, + ); } From 8b4e91e18ce425902bef726ec3731d266867a6f7 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Fri, 14 May 2021 17:09:11 +0800 Subject: [PATCH 31/42] stage2 register manager: clean up API and add more unit tests --- src/codegen.zig | 18 +- src/register_manager.zig | 375 +++++++++++++++++++++++---------------- 2 files changed, 228 insertions(+), 165 deletions(-) diff --git a/src/codegen.zig b/src/codegen.zig index c41802ca00..9d344bf1d0 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -952,7 +952,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { /// allocated. A second call to `copyToTmpRegister` may return the same register. /// This can have a side effect of spilling instructions to the stack to free up a register. fn copyToTmpRegister(self: *Self, src: LazySrcLoc, ty: Type, mcv: MCValue) !Register { - const reg = try self.register_manager.allocRegWithoutTracking(&.{}); + const reg = try self.register_manager.allocReg(null, &.{}); try self.genSetReg(src, ty, reg, mcv); return reg; } @@ -2228,7 +2228,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { switch (mc_arg) { .none => continue, .register => |reg| { - try self.register_manager.getRegWithoutTracking(reg); + try self.register_manager.getReg(reg, null); try self.genSetReg(arg.src, arg.ty, reg, arg_mcv); }, .stack_offset => { @@ -2370,7 +2370,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .compare_flags_signed => unreachable, .compare_flags_unsigned => unreachable, .register => |reg| { - try self.register_manager.getRegWithoutTracking(reg); + try self.register_manager.getReg(reg, null); try self.genSetReg(arg.src, arg.ty, reg, arg_mcv); }, .stack_offset => { @@ -2433,7 +2433,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .compare_flags_signed => unreachable, .compare_flags_unsigned => unreachable, .register => |reg| { - try self.register_manager.getRegWithoutTracking(reg); + try self.register_manager.getReg(reg, null); try self.genSetReg(arg.src, arg.ty, reg, arg_mcv); }, .stack_offset => { @@ -2486,7 +2486,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .register => |reg| { // TODO prevent this macho if block to be generated for all archs switch (arch) { - .x86_64, .aarch64 => try self.register_manager.getRegWithoutTracking(reg), + .x86_64, .aarch64 => try self.register_manager.getReg(reg, null), else => unreachable, } try self.genSetReg(arg.src, arg.ty, reg, arg_mcv); @@ -3190,7 +3190,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { const arg = inst.args[i]; const arg_mcv = try self.resolveInst(arg); - try self.register_manager.getRegWithoutTracking(reg); + try self.register_manager.getReg(reg, null); try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv); } @@ -3223,7 +3223,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { const arg = inst.args[i]; const arg_mcv = try self.resolveInst(arg); - try self.register_manager.getRegWithoutTracking(reg); + try self.register_manager.getReg(reg, null); try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv); } @@ -3258,7 +3258,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { const arg = inst.args[i]; const arg_mcv = try self.resolveInst(arg); - try self.register_manager.getRegWithoutTracking(reg); + try self.register_manager.getReg(reg, null); try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv); } @@ -3291,7 +3291,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { const arg = inst.args[i]; const arg_mcv = try self.resolveInst(arg); - try self.register_manager.getRegWithoutTracking(reg); + try self.register_manager.getReg(reg, null); try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv); } diff --git a/src/register_manager.zig b/src/register_manager.zig index c9235b88c1..5f7cccd60f 100644 --- a/src/register_manager.zig +++ b/src/register_manager.zig @@ -7,6 +7,9 @@ const ir = @import("ir.zig"); const Type = @import("type.zig").Type; const Module = @import("Module.zig"); const LazySrcLoc = Module.LazySrcLoc; +const expect = std.testing.expect; +const expectEqual = std.testing.expectEqual; +const expectEqualSlices = std.testing.expectEqualSlices; const log = std.log.scoped(.register_manager); @@ -66,77 +69,14 @@ pub fn RegisterManager( return self.allocated_registers & @as(FreeRegInt, 1) << shift != 0; } - /// Returns `null` if all registers are allocated. + /// Allocates a specified number of registers, optionally + /// tracking them. Returns `null` if not enough registers are + /// free. pub fn tryAllocRegs( self: *Self, comptime count: comptime_int, - insts: [count]*ir.Inst, - exceptions: []Register, - ) ?[count]Register { - if (self.tryAllocRegsWithoutTracking(count, exceptions)) |regs| { - for (regs) |reg, i| { - const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null - self.registers[index] = insts[i]; - self.markRegUsed(reg); - } - - return regs; - } else { - return null; - } - } - - /// Returns `null` if all registers are allocated. - pub fn tryAllocReg(self: *Self, inst: *ir.Inst, exceptions: []Register) ?Register { - return if (tryAllocRegs(self, 1, .{inst}, exceptions)) |regs| regs[0] else null; - } - - pub fn allocRegs( - self: *Self, - comptime count: comptime_int, - insts: [count]*ir.Inst, - exceptions: []Register, - ) ![count]Register { - comptime assert(count > 0 and count <= callee_preserved_regs.len); - assert(count + exceptions.len <= callee_preserved_regs.len); - - return self.tryAllocRegs(count, insts, exceptions) orelse blk: { - // We'll take over the first count registers. Spill - // the instructions that were previously there to a - // stack allocations. - var regs: [count]Register = undefined; - var i: usize = 0; - for (callee_preserved_regs) |reg| { - if (i >= count) break; - if (mem.indexOfScalar(Register, exceptions, reg) != null) continue; - regs[i] = reg; - - const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null - if (self.isRegFree(reg)) { - self.markRegUsed(reg); - } else { - const spilled_inst = self.registers[index].?; - try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); - } - self.registers[index] = insts[i]; - - i += 1; - } - - break :blk regs; - }; - } - - pub fn allocReg(self: *Self, inst: *ir.Inst, exceptions: []Register) !Register { - return (try self.allocRegs(1, .{inst}, exceptions))[0]; - } - - /// Does not track the registers. - /// Returns `null` if not enough registers are free. - pub fn tryAllocRegsWithoutTracking( - self: *Self, - comptime count: comptime_int, - exceptions: []Register, + insts: [count]?*ir.Inst, + exceptions: []const Register, ) ?[count]Register { comptime if (callee_preserved_regs.len == 0) return null; comptime assert(count > 0 and count <= callee_preserved_regs.len); @@ -156,18 +96,40 @@ pub fn RegisterManager( } } - return if (i < count) null else regs; + if (i == count) { + for (regs) |reg, j| { + if (insts[j]) |inst| { + // Track the register + const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null + self.registers[index] = inst; + self.markRegUsed(reg); + } + } + + return regs; + } else return null; } - /// Does not track the register. - /// Returns `null` if all registers are allocated. - pub fn tryAllocRegWithoutTracking(self: *Self, exceptions: []Register) ?Register { - return if (self.tryAllocRegsWithoutTracking(1, exceptions)) |regs| regs[0] else null; + /// Allocates a register and optionally tracks it with a + /// corresponding instruction. Returns `null` if all registers + /// are allocated. + pub fn tryAllocReg(self: *Self, inst: ?*ir.Inst, exceptions: []const Register) ?Register { + return if (tryAllocRegs(self, 1, .{inst}, exceptions)) |regs| regs[0] else null; } - /// Does not track the registers - pub fn allocRegsWithoutTracking(self: *Self, comptime count: comptime_int, exceptions: []Register) ![count]Register { - return self.tryAllocRegsWithoutTracking(count, exceptions) orelse blk: { + /// Allocates a specified number of registers, optionally + /// tracking them. Asserts that count + exceptions.len is not + /// larger than the total number of registers available. + pub fn allocRegs( + self: *Self, + comptime count: comptime_int, + insts: [count]?*ir.Inst, + exceptions: []const Register, + ) ![count]Register { + comptime assert(count > 0 and count <= callee_preserved_regs.len); + assert(count + exceptions.len <= callee_preserved_regs.len); + + return self.tryAllocRegs(count, insts, exceptions) orelse blk: { // We'll take over the first count registers. Spill // the instructions that were previously there to a // stack allocations. @@ -179,11 +141,22 @@ pub fn RegisterManager( regs[i] = reg; const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null - if (!self.isRegFree(reg)) { - const spilled_inst = self.registers[index].?; - try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); - self.registers[index] = null; - self.markRegFree(reg); + if (insts[i]) |inst| { + // Track the register + if (self.isRegFree(reg)) { + self.markRegUsed(reg); + } else { + const spilled_inst = self.registers[index].?; + try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); + } + self.registers[index] = inst; + } else { + // Don't track the register + if (!self.isRegFree(reg)) { + const spilled_inst = self.registers[index].?; + try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); + self.freeReg(reg); + } } i += 1; @@ -193,39 +166,36 @@ pub fn RegisterManager( }; } - /// Does not track the register. - pub fn allocRegWithoutTracking(self: *Self, exceptions: []Register) !Register { - return (try self.allocRegsWithoutTracking(1, exceptions))[0]; + /// Allocates a register and optionally tracks it with a + /// corresponding instruction. + pub fn allocReg(self: *Self, inst: ?*ir.Inst, exceptions: []const Register) !Register { + return (try self.allocRegs(1, .{inst}, exceptions))[0]; } - /// Allocates the specified register with the specified - /// instruction. Spills the register if it is currently - /// allocated. - pub fn getReg(self: *Self, reg: Register, inst: *ir.Inst) !void { + /// Spills the register if it is currently allocated. If a + /// corresponding instruction is passed, will also track this + /// register. + pub fn getReg(self: *Self, reg: Register, inst: ?*ir.Inst) !void { const index = reg.allocIndex() orelse return; - if (!self.isRegFree(reg)) { - // Move the instruction that was previously there to a - // stack allocation. - const spilled_inst = self.registers[index].?; - self.registers[index] = inst; - try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); - } else { - self.getRegAssumeFree(reg, inst); - } - } - - /// Spills the register if it is currently allocated. - /// Does not track the register. - pub fn getRegWithoutTracking(self: *Self, reg: Register) !void { - const index = reg.allocIndex() orelse return; - - if (!self.isRegFree(reg)) { - // Move the instruction that was previously there to a - // stack allocation. - const spilled_inst = self.registers[index].?; - try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); - self.markRegFree(reg); + if (inst) |tracked_inst| + if (!self.isRegFree(reg)) { + // Move the instruction that was previously there to a + // stack allocation. + const spilled_inst = self.registers[index].?; + self.registers[index] = tracked_inst; + try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); + } else { + self.getRegAssumeFree(reg, tracked_inst); + } + else { + if (!self.isRegFree(reg)) { + // Move the instruction that was previously there to a + // stack allocation. + const spilled_inst = self.registers[index].?; + try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); + self.freeReg(reg); + } } } @@ -250,42 +220,63 @@ pub fn RegisterManager( }; } -const MockRegister = enum(u2) { +const MockRegister1 = enum(u2) { r0, r1, r2, r3, - pub fn allocIndex(self: MockRegister) ?u2 { - inline for (mock_callee_preserved_regs) |cpreg, i| { + pub fn allocIndex(self: MockRegister1) ?u2 { + inline for (callee_preserved_regs) |cpreg, i| { if (self == cpreg) return i; } return null; } + + const callee_preserved_regs = [_]MockRegister1{ .r2, .r3 }; }; -const mock_callee_preserved_regs = [_]MockRegister{ .r2, .r3 }; +const MockRegister2 = enum(u2) { + r0, + r1, + r2, + r3, -const MockFunction = struct { - allocator: *Allocator, - register_manager: RegisterManager(Self, MockRegister, &mock_callee_preserved_regs) = .{}, - spilled: std.ArrayListUnmanaged(MockRegister) = .{}, - - const Self = @This(); - - pub fn deinit(self: *Self) void { - self.spilled.deinit(self.allocator); + pub fn allocIndex(self: MockRegister2) ?u2 { + inline for (callee_preserved_regs) |cpreg, i| { + if (self == cpreg) return i; + } + return null; } - pub fn spillInstruction(self: *Self, src: LazySrcLoc, reg: MockRegister, inst: *ir.Inst) !void { - try self.spilled.append(self.allocator, reg); - } + const callee_preserved_regs = [_]MockRegister2{ .r0, .r1, .r2, .r3 }; }; -test "tryAllocReg: no spilling" { +fn MockFunction(comptime Register: type) type { + return struct { + allocator: *Allocator, + register_manager: RegisterManager(Self, Register, &Register.callee_preserved_regs) = .{}, + spilled: std.ArrayListUnmanaged(Register) = .{}, + + const Self = @This(); + + pub fn deinit(self: *Self) void { + self.spilled.deinit(self.allocator); + } + + pub fn spillInstruction(self: *Self, src: LazySrcLoc, reg: Register, inst: *ir.Inst) !void { + try self.spilled.append(self.allocator, reg); + } + }; +} + +const MockFunction1 = MockFunction(MockRegister1); +const MockFunction2 = MockFunction(MockRegister2); + +test "default state" { const allocator = std.testing.allocator; - var function = MockFunction{ + var function = MockFunction1{ .allocator = allocator, }; defer function.deinit(); @@ -296,27 +287,48 @@ test "tryAllocReg: no spilling" { .src = .unneeded, }; - try std.testing.expect(!function.register_manager.isRegAllocated(.r2)); - try std.testing.expect(!function.register_manager.isRegAllocated(.r3)); + try expect(!function.register_manager.isRegAllocated(.r2)); + try expect(!function.register_manager.isRegAllocated(.r3)); + try expect(function.register_manager.isRegFree(.r2)); + try expect(function.register_manager.isRegFree(.r3)); +} - try std.testing.expectEqual(@as(?MockRegister, .r2), function.register_manager.tryAllocReg(&mock_instruction, &.{})); - try std.testing.expectEqual(@as(?MockRegister, .r3), function.register_manager.tryAllocReg(&mock_instruction, &.{})); - try std.testing.expectEqual(@as(?MockRegister, null), function.register_manager.tryAllocReg(&mock_instruction, &.{})); +test "tryAllocReg: no spilling" { + const allocator = std.testing.allocator; - try std.testing.expect(function.register_manager.isRegAllocated(.r2)); - try std.testing.expect(function.register_manager.isRegAllocated(.r3)); + var function = MockFunction1{ + .allocator = allocator, + }; + defer function.deinit(); + + var mock_instruction = ir.Inst{ + .tag = .breakpoint, + .ty = Type.initTag(.void), + .src = .unneeded, + }; + + try expectEqual(@as(?MockRegister1, .r2), function.register_manager.tryAllocReg(&mock_instruction, &.{})); + try expectEqual(@as(?MockRegister1, .r3), function.register_manager.tryAllocReg(&mock_instruction, &.{})); + try expectEqual(@as(?MockRegister1, null), function.register_manager.tryAllocReg(&mock_instruction, &.{})); + + try expect(function.register_manager.isRegAllocated(.r2)); + try expect(function.register_manager.isRegAllocated(.r3)); + try expect(!function.register_manager.isRegFree(.r2)); + try expect(!function.register_manager.isRegFree(.r3)); function.register_manager.freeReg(.r2); function.register_manager.freeReg(.r3); - try std.testing.expect(function.register_manager.isRegAllocated(.r2)); - try std.testing.expect(function.register_manager.isRegAllocated(.r3)); + try expect(function.register_manager.isRegAllocated(.r2)); + try expect(function.register_manager.isRegAllocated(.r3)); + try expect(function.register_manager.isRegFree(.r2)); + try expect(function.register_manager.isRegFree(.r3)); } test "allocReg: spilling" { const allocator = std.testing.allocator; - var function = MockFunction{ + var function = MockFunction1{ .allocator = allocator, }; defer function.deinit(); @@ -327,26 +339,76 @@ test "allocReg: spilling" { .src = .unneeded, }; - try std.testing.expect(!function.register_manager.isRegAllocated(.r2)); - try std.testing.expect(!function.register_manager.isRegAllocated(.r3)); - - try std.testing.expectEqual(@as(?MockRegister, .r2), try function.register_manager.allocReg(&mock_instruction, &.{})); - try std.testing.expectEqual(@as(?MockRegister, .r3), try function.register_manager.allocReg(&mock_instruction, &.{})); + try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(&mock_instruction, &.{})); + try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(&mock_instruction, &.{})); // Spill a register - try std.testing.expectEqual(@as(?MockRegister, .r2), try function.register_manager.allocReg(&mock_instruction, &.{})); - try std.testing.expectEqualSlices(MockRegister, &[_]MockRegister{.r2}, function.spilled.items); + try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(&mock_instruction, &.{})); + try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items); // No spilling necessary function.register_manager.freeReg(.r3); - try std.testing.expectEqual(@as(?MockRegister, .r3), try function.register_manager.allocReg(&mock_instruction, &.{})); - try std.testing.expectEqualSlices(MockRegister, &[_]MockRegister{.r2}, function.spilled.items); + try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(&mock_instruction, &.{})); + try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items); + + // Exceptions + function.register_manager.freeReg(.r2); + function.register_manager.freeReg(.r3); + try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(&mock_instruction, &.{.r2})); +} + +test "tryAllocRegs" { + const allocator = std.testing.allocator; + + var function = MockFunction2{ + .allocator = allocator, + }; + defer function.deinit(); + + var mock_instruction = ir.Inst{ + .tag = .breakpoint, + .ty = Type.initTag(.void), + .src = .unneeded, + }; + + try expectEqual([_]MockRegister2{ .r0, .r1, .r2 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }, &.{}).?); + + // Exceptions + function.register_manager.freeReg(.r0); + function.register_manager.freeReg(.r1); + function.register_manager.freeReg(.r2); + try expectEqual([_]MockRegister2{ .r0, .r2, .r3 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }, &.{.r1}).?); +} + +test "allocRegs" { + const allocator = std.testing.allocator; + + var function = MockFunction2{ + .allocator = allocator, + }; + defer function.deinit(); + + var mock_instruction = ir.Inst{ + .tag = .breakpoint, + .ty = Type.initTag(.void), + .src = .unneeded, + }; + + try expectEqual([_]MockRegister2{ .r0, .r1, .r2 }, try function.register_manager.allocRegs(3, .{ + &mock_instruction, + &mock_instruction, + &mock_instruction, + }, &.{})); + + // Exceptions + try expectEqual([_]MockRegister2{ .r0, .r2, .r3 }, try function.register_manager.allocRegs(3, .{ null, null, null }, &.{.r1})); + try expectEqualSlices(MockRegister2, &[_]MockRegister2{ .r0, .r2 }, function.spilled.items); } test "getReg" { const allocator = std.testing.allocator; - var function = MockFunction{ + var function = MockFunction1{ .allocator = allocator, }; defer function.deinit(); @@ -357,18 +419,19 @@ test "getReg" { .src = .unneeded, }; - try std.testing.expect(!function.register_manager.isRegAllocated(.r2)); - try std.testing.expect(!function.register_manager.isRegAllocated(.r3)); - try function.register_manager.getReg(.r3, &mock_instruction); - try std.testing.expect(!function.register_manager.isRegAllocated(.r2)); - try std.testing.expect(function.register_manager.isRegAllocated(.r3)); + try expect(!function.register_manager.isRegAllocated(.r2)); + try expect(function.register_manager.isRegAllocated(.r3)); + try expect(function.register_manager.isRegFree(.r2)); + try expect(!function.register_manager.isRegFree(.r3)); // Spill r3 try function.register_manager.getReg(.r3, &mock_instruction); - try std.testing.expect(!function.register_manager.isRegAllocated(.r2)); - try std.testing.expect(function.register_manager.isRegAllocated(.r3)); - try std.testing.expectEqualSlices(MockRegister, &[_]MockRegister{.r3}, function.spilled.items); + try expect(!function.register_manager.isRegAllocated(.r2)); + try expect(function.register_manager.isRegAllocated(.r3)); + try expect(function.register_manager.isRegFree(.r2)); + try expect(!function.register_manager.isRegFree(.r3)); + try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r3}, function.spilled.items); } From 2d4d4baa42590803cff8581add325ea3f102ac2a Mon Sep 17 00:00:00 2001 From: Sahnvour Date: Wed, 12 May 2021 19:39:36 +0200 Subject: [PATCH 32/42] std.hash_map: use 7 bits of metadata instead of 6 we only effectively need 1 control bit to represent 2 special states for the metadata (free and tombstone) this should reduce the number of actual element equality tests, but since it's very low already, the impact is negligible --- lib/std/hash_map.zig | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/lib/std/hash_map.zig b/lib/std/hash_map.zig index 0523ad7376..7dd8b0f664 100644 --- a/lib/std/hash_map.zig +++ b/lib/std/hash_map.zig @@ -304,29 +304,32 @@ pub fn HashMapUnmanaged( /// Metadata for a slot. It can be in three states: empty, used or /// tombstone. Tombstones indicate that an entry was previously used, /// they are a simple way to handle removal. - /// To this state, we add 6 bits from the slot's key hash. These are + /// To this state, we add 7 bits from the slot's key hash. These are /// used as a fast way to disambiguate between entries without /// having to use the equality function. If two fingerprints are /// different, we know that we don't have to compare the keys at all. - /// The 6 bits are the highest ones from a 64 bit hash. This way, not + /// The 7 bits are the highest ones from a 64 bit hash. This way, not /// only we use the `log2(capacity)` lowest bits from the hash to determine - /// a slot index, but we use 6 more bits to quickly resolve collisions - /// when multiple elements with different hashes end up wanting to be in / the same slot. + /// a slot index, but we use 7 more bits to quickly resolve collisions + /// when multiple elements with different hashes end up wanting to be in the same slot. /// Not using the equality function means we don't have to read into - /// the entries array, avoiding a likely cache miss. + /// the entries array, likely avoiding a cache miss and a potentially + /// costly function call. const Metadata = packed struct { - const FingerPrint = u6; + const FingerPrint = u7; + const free: FingerPrint = 0; + const tombstone: FingerPrint = 1; + + fingerprint: FingerPrint = free, used: u1 = 0, - tombstone: u1 = 0, - fingerprint: FingerPrint = 0, pub fn isUsed(self: Metadata) bool { return self.used == 1; } pub fn isTombstone(self: Metadata) bool { - return self.tombstone == 1; + return !self.isUsed() and self.fingerprint == tombstone; } pub fn takeFingerprint(hash: Hash) FingerPrint { @@ -337,14 +340,12 @@ pub fn HashMapUnmanaged( pub fn fill(self: *Metadata, fp: FingerPrint) void { self.used = 1; - self.tombstone = 0; self.fingerprint = fp; } pub fn remove(self: *Metadata) void { self.used = 0; - self.tombstone = 1; - self.fingerprint = 0; + self.fingerprint = tombstone; } }; From a52e47230718c6e38dfd84ac0571a597ecd9719f Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Fri, 14 May 2021 08:31:22 +0200 Subject: [PATCH 33/42] stage1: Widen non byte-sized atomic loads/stores Checking if the size is a power of two is not enough, should also check if it's a multiple of 8. Closes #7976 --- src/stage1/codegen.cpp | 6 +++--- test/stage1/behavior/atomics.zig | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index 015a64f68a..d70b2d498c 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -5485,8 +5485,8 @@ static enum ZigLLVM_AtomicRMWBinOp to_ZigLLVMAtomicRMWBinOp(AtomicRmwOp op, bool } static LLVMTypeRef get_atomic_abi_type(CodeGen *g, IrInstGen *instruction) { - // If the operand type of an atomic operation is not a power of two sized - // we need to widen it before using it and then truncate the result. + // If the operand type of an atomic operation is not byte sized we need to + // widen it before using it and then truncate the result. ir_assert(instruction->value->type->id == ZigTypeIdPointer, instruction); ZigType *operand_type = instruction->value->type->data.pointer.child_type; @@ -5498,7 +5498,7 @@ static LLVMTypeRef get_atomic_abi_type(CodeGen *g, IrInstGen *instruction) { bool is_signed = operand_type->data.integral.is_signed; ir_assert(bit_count != 0, instruction); - if (bit_count == 1 || !is_power_of_2(bit_count)) { + if (!is_power_of_2(bit_count) || bit_count % 8) { return get_llvm_type(g, get_int_type(g, is_signed, operand_type->abi_size * 8)); } else { return nullptr; diff --git a/test/stage1/behavior/atomics.zig b/test/stage1/behavior/atomics.zig index 74a161b6fc..f965f53b05 100644 --- a/test/stage1/behavior/atomics.zig +++ b/test/stage1/behavior/atomics.zig @@ -199,7 +199,7 @@ fn testAtomicRmwInt() !void { test "atomics with different types" { try testAtomicsWithType(bool, true, false); - inline for (.{ u1, i5, u15 }) |T| { + inline for (.{ u1, i4, u5, i15, u24 }) |T| { var x: T = 0; try testAtomicsWithType(T, 0, 1); } From 193a92630170582c8c7b28432c8a65a80e6ef0b3 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Fri, 14 May 2021 18:41:16 +0200 Subject: [PATCH 34/42] stage2: Force Clang to use LLVM's assembler for SPARC targets This change allows cross-compiling C code for SPARC, if you hit any error with the internal assembler please open a ticket. --- src/Compilation.zig | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Compilation.zig b/src/Compilation.zig index 140e160b69..6a73bd9ca9 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -2695,6 +2695,12 @@ pub fn addCCArgs( try argv.appendSlice(&[_][]const u8{ "-MD", "-MV", "-MF", p }); } + if (target.cpu.arch.isSPARC()) { + // Clang defaults to using the system assembler over the internal one + // when targeting a non-BSD OS. + try argv.append("-integrated-as"); + } + if (target.os.tag == .freestanding) { try argv.append("-ffreestanding"); } From 8f35c60b39dad1ecb1609f6105f1e41a52df87ae Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 14 May 2021 12:24:47 -0700 Subject: [PATCH 35/42] stage2: clarify logic of passing `-integrated-as` to clang make it clear that the logic is only there to lower the amount of noise on the clang command line. --- src/Compilation.zig | 4 +--- src/target.zig | 6 ++++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 6a73bd9ca9..f8ef492d78 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -2695,9 +2695,7 @@ pub fn addCCArgs( try argv.appendSlice(&[_][]const u8{ "-MD", "-MV", "-MF", p }); } - if (target.cpu.arch.isSPARC()) { - // Clang defaults to using the system assembler over the internal one - // when targeting a non-BSD OS. + if (target_util.clangMightShellOutForAssembly(target)) { try argv.append("-integrated-as"); } diff --git a/src/target.zig b/src/target.zig index c2018db012..28b4771d0a 100644 --- a/src/target.zig +++ b/src/target.zig @@ -395,3 +395,9 @@ pub fn libcFullLinkFlags(target: std.Target) []const []const u8 { }, }; } + +pub fn clangMightShellOutForAssembly(target: std.Target) bool { + // Clang defaults to using the system assembler over the internal one + // when targeting a non-BSD OS. + return target.cpu.arch.isSPARC(); +} From 612ad779bb2df8e7e15ab20dd3df3a2900ff82f0 Mon Sep 17 00:00:00 2001 From: viri Date: Fri, 14 May 2021 14:15:53 -0600 Subject: [PATCH 36/42] std: rework math.scalbn (#8733) * std: fix overflow in math.scalbn32 * std: rewrite math.scalbn to be generic * std: support f128 in math.isNormal * std: enable f128 tests in math.scalbn --- lib/std/math/isnormal.zig | 11 ++- lib/std/math/scalbn.zig | 138 +++++++++++++++++++------------------- 2 files changed, 78 insertions(+), 71 deletions(-) diff --git a/lib/std/math/isnormal.zig b/lib/std/math/isnormal.zig index b10a6e2286..f3942d121c 100644 --- a/lib/std/math/isnormal.zig +++ b/lib/std/math/isnormal.zig @@ -14,16 +14,20 @@ pub fn isNormal(x: anytype) bool { switch (T) { f16 => { const bits = @bitCast(u16, x); - return (bits + 1024) & 0x7FFF >= 2048; + return (bits + (1 << 10)) & (maxInt(u16) >> 1) >= (1 << 11); }, f32 => { const bits = @bitCast(u32, x); - return (bits + 0x00800000) & 0x7FFFFFFF >= 0x01000000; + return (bits + (1 << 23)) & (maxInt(u32) >> 1) >= (1 << 24); }, f64 => { const bits = @bitCast(u64, x); return (bits + (1 << 52)) & (maxInt(u64) >> 1) >= (1 << 53); }, + f128 => { + const bits = @bitCast(u128, x); + return (bits + (1 << 112)) & (maxInt(u128) >> 1) >= (1 << 113); + }, else => { @compileError("isNormal not implemented for " ++ @typeName(T)); }, @@ -34,10 +38,13 @@ test "math.isNormal" { try expect(!isNormal(math.nan(f16))); try expect(!isNormal(math.nan(f32))); try expect(!isNormal(math.nan(f64))); + try expect(!isNormal(math.nan(f128))); try expect(!isNormal(@as(f16, 0))); try expect(!isNormal(@as(f32, 0))); try expect(!isNormal(@as(f64, 0))); + try expect(!isNormal(@as(f128, 0))); try expect(isNormal(@as(f16, 1.0))); try expect(isNormal(@as(f32, 1.0))); try expect(isNormal(@as(f64, 1.0))); + try expect(isNormal(@as(f128, 1.0))); } diff --git a/lib/std/math/scalbn.zig b/lib/std/math/scalbn.zig index 49aea08931..bdf382a303 100644 --- a/lib/std/math/scalbn.zig +++ b/lib/std/math/scalbn.zig @@ -9,89 +9,89 @@ // https://git.musl-libc.org/cgit/musl/tree/src/math/scalbnf.c // https://git.musl-libc.org/cgit/musl/tree/src/math/scalbn.c -const std = @import("../std.zig"); +const std = @import("std"); const math = std.math; +const assert = std.debug.assert; const expect = std.testing.expect; /// Returns x * 2^n. pub fn scalbn(x: anytype, n: i32) @TypeOf(x) { - const T = @TypeOf(x); - return switch (T) { - f32 => scalbn32(x, n), - f64 => scalbn64(x, n), - else => @compileError("scalbn not implemented for " ++ @typeName(T)), - }; -} + var base = x; + var shift = n; -fn scalbn32(x: f32, n_: i32) f32 { - var y = x; - var n = n_; + const T = @TypeOf(base); + const IntT = std.meta.Int(.unsigned, @bitSizeOf(T)); + if (@typeInfo(T) != .Float) { + @compileError("scalbn not implemented for " ++ @typeName(T)); + } - if (n > 127) { - y *= 0x1.0p127; - n -= 127; - if (n > 1023) { - y *= 0x1.0p127; - n -= 127; - if (n > 127) { - n = 127; - } + const mantissa_bits = math.floatMantissaBits(T); + const exponent_bits = math.floatExponentBits(T); + const exponent_bias = (1 << (exponent_bits - 1)) - 1; + const exponent_min = 1 - exponent_bias; + const exponent_max = exponent_bias; + + // fix double rounding errors in subnormal ranges + // https://git.musl-libc.org/cgit/musl/commit/src/math/scalbn.c?id=8c44a060243f04283ca68dad199aab90336141db + const scale_min_expo = exponent_min + mantissa_bits + 1; + const scale_min = @bitCast(T, @as(IntT, scale_min_expo + exponent_bias) << mantissa_bits); + const scale_max = @bitCast(T, @intCast(IntT, exponent_max + exponent_bias) << mantissa_bits); + + // scale `shift` within floating point limits, if possible + // second pass is possible due to subnormal range + // third pass always results in +/-0.0 or +/-inf + if (shift > exponent_max) { + base *= scale_max; + shift -= exponent_max; + if (shift > exponent_max) { + base *= scale_max; + shift -= exponent_max; + if (shift > exponent_max) shift = exponent_max; } - } else if (n < -126) { - y *= 0x1.0p-126 * 0x1.0p24; - n += 126 - 24; - if (n < -126) { - y *= 0x1.0p-126 * 0x1.0p24; - n += 126 - 24; - if (n < -126) { - n = -126; - } + } else if (shift < exponent_min) { + base *= scale_min; + shift -= scale_min_expo; + if (shift < exponent_min) { + base *= scale_min; + shift -= scale_min_expo; + if (shift < exponent_min) shift = exponent_min; } } - const u = @intCast(u32, n +% 0x7F) << 23; - return y * @bitCast(f32, u); -} - -fn scalbn64(x: f64, n_: i32) f64 { - var y = x; - var n = n_; - - if (n > 1023) { - y *= 0x1.0p1023; - n -= 1023; - if (n > 1023) { - y *= 0x1.0p1023; - n -= 1023; - if (n > 1023) { - n = 1023; - } - } - } else if (n < -1022) { - y *= 0x1.0p-1022 * 0x1.0p53; - n += 1022 - 53; - if (n < -1022) { - y *= 0x1.0p-1022 * 0x1.0p53; - n += 1022 - 53; - if (n < -1022) { - n = -1022; - } - } - } - - const u = @intCast(u64, n +% 0x3FF) << 52; - return y * @bitCast(f64, u); + return base * @bitCast(T, @intCast(IntT, shift + exponent_bias) << mantissa_bits); } test "math.scalbn" { - try expect(scalbn(@as(f32, 1.5), 4) == scalbn32(1.5, 4)); - try expect(scalbn(@as(f64, 1.5), 4) == scalbn64(1.5, 4)); -} + // basic usage + try expect(scalbn(@as(f16, 1.5), 4) == 24.0); + try expect(scalbn(@as(f32, 1.5), 4) == 24.0); + try expect(scalbn(@as(f64, 1.5), 4) == 24.0); + try expect(scalbn(@as(f128, 1.5), 4) == 24.0); -test "math.scalbn32" { - try expect(scalbn32(1.5, 4) == 24.0); -} + // subnormals + try expect(math.isNormal(scalbn(@as(f16, 1.0), -14))); + try expect(!math.isNormal(scalbn(@as(f16, 1.0), -15))); + try expect(math.isNormal(scalbn(@as(f32, 1.0), -126))); + try expect(!math.isNormal(scalbn(@as(f32, 1.0), -127))); + try expect(math.isNormal(scalbn(@as(f64, 1.0), -1022))); + try expect(!math.isNormal(scalbn(@as(f64, 1.0), -1023))); + try expect(math.isNormal(scalbn(@as(f128, 1.0), -16382))); + try expect(!math.isNormal(scalbn(@as(f128, 1.0), -16383))); + // unreliable due to lack of native f16 support, see talk on PR #8733 + // try expect(scalbn(@as(f16, 0x1.1FFp-1), -14 - 9) == math.f16_true_min); + try expect(scalbn(@as(f32, 0x1.3FFFFFp-1), -126 - 22) == math.f32_true_min); + try expect(scalbn(@as(f64, 0x1.7FFFFFFFFFFFFp-1), -1022 - 51) == math.f64_true_min); + try expect(scalbn(@as(f128, 0x1.7FFFFFFFFFFFFFFFFFFFFFFFFFFFp-1), -16382 - 111) == math.f128_true_min); -test "math.scalbn64" { - try expect(scalbn64(1.5, 4) == 24.0); + // float limits + try expect(scalbn(@as(f32, math.f32_max), -128 - 149) > 0.0); + try expect(scalbn(@as(f32, math.f32_max), -128 - 149 - 1) == 0.0); + try expect(!math.isPositiveInf(scalbn(@as(f16, math.f16_true_min), 15 + 24))); + try expect(math.isPositiveInf(scalbn(@as(f16, math.f16_true_min), 15 + 24 + 1))); + try expect(!math.isPositiveInf(scalbn(@as(f32, math.f32_true_min), 127 + 149))); + try expect(math.isPositiveInf(scalbn(@as(f32, math.f32_true_min), 127 + 149 + 1))); + try expect(!math.isPositiveInf(scalbn(@as(f64, math.f64_true_min), 1023 + 1074))); + try expect(math.isPositiveInf(scalbn(@as(f64, math.f64_true_min), 1023 + 1074 + 1))); + try expect(!math.isPositiveInf(scalbn(@as(f128, math.f128_true_min), 16383 + 16494))); + try expect(math.isPositiveInf(scalbn(@as(f128, math.f128_true_min), 16383 + 16494 + 1))); } From e93254412b2bc516c3add5b46843b94b3a0f9239 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 14 May 2021 22:27:26 +0200 Subject: [PATCH 37/42] macho: if strip is true, do not generate dSYM --- src/link/MachO.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 0a4eb6da87..c6628149c3 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -363,8 +363,8 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio self.base.file = file; // Create dSYM bundle. - if (options.module) |mod| { - const dir = mod.zig_cache_artifact_directory; + if (!options.strip and options.module != null) { + const dir = options.module.?.zig_cache_artifact_directory; log.debug("creating {s}.dSYM bundle in {s}", .{ sub_path, dir.path }); const d_sym_path = try fmt.allocPrint( @@ -399,7 +399,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio switch (options.output_mode) { .Exe => {}, - .Obj => return error.TODOImplementWritingObjFiles, + .Obj => {}, .Lib => return error.TODOImplementWritingLibFiles, } From e94bbc22375700c0e4bf5302056d544d7e8e2bd9 Mon Sep 17 00:00:00 2001 From: ZapAnton Date: Sat, 15 May 2021 01:41:12 +0300 Subject: [PATCH 38/42] std.testing: Added newline to the error messages Added newline to every `std.debug.print` message. Closes https://github.com/ziglang/zig/issues/8777 --- lib/std/testing.zig | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/std/testing.zig b/lib/std/testing.zig index 22df9c9b66..d317e5115e 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -29,11 +29,11 @@ pub var zig_exe_path: []const u8 = undefined; /// and then aborts when actual_error_union is not expected_error. pub fn expectError(expected_error: anyerror, actual_error_union: anytype) !void { if (actual_error_union) |actual_payload| { - std.debug.print("expected error.{s}, found {any}", .{ @errorName(expected_error), actual_payload }); + std.debug.print("expected error.{s}, found {any}\n", .{ @errorName(expected_error), actual_payload }); return error.TestUnexpectedError; } else |actual_error| { if (expected_error != actual_error) { - std.debug.print("expected error.{s}, found error.{s}", .{ + std.debug.print("expected error.{s}, found error.{s}\n", .{ @errorName(expected_error), @errorName(actual_error), }); @@ -62,7 +62,7 @@ pub fn expectEqual(expected: anytype, actual: @TypeOf(expected)) !void { .Type => { if (actual != expected) { - std.debug.print("expected type {s}, found type {s}", .{ @typeName(expected), @typeName(actual) }); + std.debug.print("expected type {s}, found type {s}\n", .{ @typeName(expected), @typeName(actual) }); return error.TestExpectedEqual; } }, @@ -78,7 +78,7 @@ pub fn expectEqual(expected: anytype, actual: @TypeOf(expected)) !void { .ErrorSet, => { if (actual != expected) { - std.debug.print("expected {}, found {}", .{ expected, actual }); + std.debug.print("expected {}, found {}\n", .{ expected, actual }); return error.TestExpectedEqual; } }, @@ -87,17 +87,17 @@ pub fn expectEqual(expected: anytype, actual: @TypeOf(expected)) !void { switch (pointer.size) { .One, .Many, .C => { if (actual != expected) { - std.debug.print("expected {*}, found {*}", .{ expected, actual }); + std.debug.print("expected {*}, found {*}\n", .{ expected, actual }); return error.TestExpectedEqual; } }, .Slice => { if (actual.ptr != expected.ptr) { - std.debug.print("expected slice ptr {*}, found {*}", .{ expected.ptr, actual.ptr }); + std.debug.print("expected slice ptr {*}, found {*}\n", .{ expected.ptr, actual.ptr }); return error.TestExpectedEqual; } if (actual.len != expected.len) { - std.debug.print("expected slice len {}, found {}", .{ expected.len, actual.len }); + std.debug.print("expected slice len {}, found {}\n", .{ expected.len, actual.len }); return error.TestExpectedEqual; } }, @@ -110,7 +110,7 @@ pub fn expectEqual(expected: anytype, actual: @TypeOf(expected)) !void { var i: usize = 0; while (i < vectorType.len) : (i += 1) { if (!std.meta.eql(expected[i], actual[i])) { - std.debug.print("index {} incorrect. expected {}, found {}", .{ i, expected[i], actual[i] }); + std.debug.print("index {} incorrect. expected {}, found {}\n", .{ i, expected[i], actual[i] }); return error.TestExpectedEqual; } } @@ -153,12 +153,12 @@ pub fn expectEqual(expected: anytype, actual: @TypeOf(expected)) !void { if (actual) |actual_payload| { try expectEqual(expected_payload, actual_payload); } else { - std.debug.print("expected {any}, found null", .{expected_payload}); + std.debug.print("expected {any}, found null\n", .{expected_payload}); return error.TestExpectedEqual; } } else { if (actual) |actual_payload| { - std.debug.print("expected null, found {any}", .{actual_payload}); + std.debug.print("expected null, found {any}\n", .{actual_payload}); return error.TestExpectedEqual; } } @@ -169,12 +169,12 @@ pub fn expectEqual(expected: anytype, actual: @TypeOf(expected)) !void { if (actual) |actual_payload| { try expectEqual(expected_payload, actual_payload); } else |actual_err| { - std.debug.print("expected {any}, found {}", .{ expected_payload, actual_err }); + std.debug.print("expected {any}, found {}\n", .{ expected_payload, actual_err }); return error.TestExpectedEqual; } } else |expected_err| { if (actual) |actual_payload| { - std.debug.print("expected {}, found {any}", .{ expected_err, actual_payload }); + std.debug.print("expected {}, found {any}\n", .{ expected_err, actual_payload }); return error.TestExpectedEqual; } else |actual_err| { try expectEqual(expected_err, actual_err); @@ -225,7 +225,7 @@ pub fn expectApproxEqAbs(expected: anytype, actual: @TypeOf(expected), tolerance switch (@typeInfo(T)) { .Float => if (!math.approxEqAbs(T, expected, actual, tolerance)) { - std.debug.print("actual {}, not within absolute tolerance {} of expected {}", .{ actual, tolerance, expected }); + std.debug.print("actual {}, not within absolute tolerance {} of expected {}\n", .{ actual, tolerance, expected }); return error.TestExpectedApproxEqAbs; }, @@ -257,7 +257,7 @@ pub fn expectApproxEqRel(expected: anytype, actual: @TypeOf(expected), tolerance switch (@typeInfo(T)) { .Float => if (!math.approxEqRel(T, expected, actual, tolerance)) { - std.debug.print("actual {}, not within relative tolerance {} of expected {}", .{ actual, tolerance, expected }); + std.debug.print("actual {}, not within relative tolerance {} of expected {}\n", .{ actual, tolerance, expected }); return error.TestExpectedApproxEqRel; }, @@ -292,13 +292,13 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const // If the child type is u8 and no weird bytes, we could print it as strings // Even for the length difference, it would be useful to see the values of the slices probably. if (expected.len != actual.len) { - std.debug.print("slice lengths differ. expected {d}, found {d}", .{ expected.len, actual.len }); + std.debug.print("slice lengths differ. expected {d}, found {d}\n", .{ expected.len, actual.len }); return error.TestExpectedEqual; } var i: usize = 0; while (i < expected.len) : (i += 1) { if (!std.meta.eql(expected[i], actual[i])) { - std.debug.print("index {} incorrect. expected {any}, found {any}", .{ i, expected[i], actual[i] }); + std.debug.print("index {} incorrect. expected {any}, found {any}\n", .{ i, expected[i], actual[i] }); return error.TestExpectedEqual; } } From 607abb5b1484a17abd0ce98de5260c877fd08e49 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 13 May 2021 23:50:39 -0700 Subject: [PATCH 39/42] std: dragonfly: fix duplicate definition of sockaddr_storage --- lib/std/os/bits/dragonfly.zig | 7 ------- 1 file changed, 7 deletions(-) diff --git a/lib/std/os/bits/dragonfly.zig b/lib/std/os/bits/dragonfly.zig index e0cfa387bd..1f77d2e73b 100644 --- a/lib/std/os/bits/dragonfly.zig +++ b/lib/std/os/bits/dragonfly.zig @@ -645,13 +645,6 @@ pub const AF_MAX = 36; pub const sa_family_t = u8; pub const socklen_t = c_uint; -pub const sockaddr_storage = extern struct { - ss_len: u8, - ss_family: sa_family_t, - __ss_pad1: [5]u8, - __ss_align: i64, - __ss_pad2: [112]u8, -}; pub const dl_phdr_info = extern struct { dlpi_addr: usize, dlpi_name: ?[*:0]const u8, From 36da8d02b56e06c92279b4df21fbfaa0273e6c7b Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Wed, 12 May 2021 23:06:28 -0700 Subject: [PATCH 40/42] translate-c: translate global (file scope) assembly --- src/clang.zig | 5 +++++ src/translate_c.zig | 18 ++++++++++++++++++ src/translate_c/ast.zig | 17 +++++++++++++++++ src/zig_clang.cpp | 5 +++++ src/zig_clang.h | 3 +++ test/translate_c.zig | 14 ++++++++++++++ 6 files changed, 62 insertions(+) diff --git a/src/clang.zig b/src/clang.zig index e00271968b..256dbda1e1 100644 --- a/src/clang.zig +++ b/src/clang.zig @@ -905,6 +905,11 @@ pub const TypedefNameDecl = opaque { extern fn ZigClangTypedefNameDecl_getLocation(*const TypedefNameDecl) SourceLocation; }; +pub const FileScopeAsmDecl = opaque { + pub const getAsmString = ZigClangFileScopeAsmDecl_getAsmString; + extern fn ZigClangFileScopeAsmDecl_getAsmString(*const FileScopeAsmDecl) *const StringLiteral; +}; + pub const TypedefType = opaque { pub const getDecl = ZigClangTypedefType_getDecl; extern fn ZigClangTypedefType_getDecl(*const TypedefType) *const TypedefNameDecl; diff --git a/src/translate_c.zig b/src/translate_c.zig index 1f8d818f0b..348e284db3 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -480,6 +480,9 @@ fn declVisitor(c: *Context, decl: *const clang.Decl) Error!void { .Empty => { // Do nothing }, + .FileScopeAsm => { + try transFileScopeAsm(c, &c.global_scope.base, @ptrCast(*const clang.FileScopeAsmDecl, decl)); + }, else => { const decl_name = try c.str(decl.getDeclKindName()); try warn(c, &c.global_scope.base, decl.getLocation(), "ignoring {s} declaration", .{decl_name}); @@ -487,6 +490,21 @@ fn declVisitor(c: *Context, decl: *const clang.Decl) Error!void { } } +fn transFileScopeAsm(c: *Context, scope: *Scope, file_scope_asm: *const clang.FileScopeAsmDecl) Error!void { + const asm_string = file_scope_asm.getAsmString(); + var len: usize = undefined; + const bytes_ptr = asm_string.getString_bytes_begin_size(&len); + + const str = try std.fmt.allocPrint(c.arena, "\"{}\"", .{std.zig.fmtEscapes(bytes_ptr[0..len])}); + const str_node = try Tag.string_literal.create(c.arena, str); + + const asm_node = try Tag.asm_simple.create(c.arena, str_node); + const block = try Tag.block_single.create(c.arena, asm_node); + const comptime_node = try Tag.@"comptime".create(c.arena, block); + + try scope.appendNode(comptime_node); +} + fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { const fn_name = try c.str(@ptrCast(*const clang.NamedDecl, fn_decl).getName_bytes_begin()); if (c.global_scope.sym_table.contains(fn_name)) diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index 2e75bf1182..1c7e7de1ac 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -161,6 +161,8 @@ pub const Node = extern union { /// @shuffle(type, a, b, mask) shuffle, + asm_simple, + negate, negate_wrap, bit_not, @@ -245,6 +247,7 @@ pub const Node = extern union { .std_mem_zeroes, .@"return", .@"comptime", + .asm_simple, .discard, .std_math_Log2Int, .negate, @@ -1017,6 +1020,19 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { }, }); }, + .asm_simple => { + const payload = node.castTag(.asm_simple).?.data; + const asm_token = try c.addToken(.keyword_asm, "asm"); + _ = try c.addToken(.l_paren, "("); + return c.addNode(.{ + .tag = .asm_simple, + .main_token = asm_token, + .data = .{ + .lhs = try renderNode(c, payload), + .rhs = try c.addToken(.r_paren, ")"), + }, + }); + }, .type => { const payload = node.castTag(.type).?.data; return c.addNode(.{ @@ -2257,6 +2273,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { .@"continue", .@"return", .@"comptime", + .asm_simple, .usingnamespace_builtins, .while_true, .if_not_break, diff --git a/src/zig_clang.cpp b/src/zig_clang.cpp index efdef1e26b..bf6e09d8c0 100644 --- a/src/zig_clang.cpp +++ b/src/zig_clang.cpp @@ -1820,6 +1820,11 @@ const ZigClangEnumDecl *ZigClangEnumDecl_getDefinition(const ZigClangEnumDecl *z return reinterpret_cast(definition); } +const ZigClangStringLiteral *ZigClangFileScopeAsmDecl_getAsmString(const ZigClangFileScopeAsmDecl *self) { + const clang::StringLiteral *result = reinterpret_cast(self)->getAsmString(); + return reinterpret_cast(result); +} + bool ZigClangRecordDecl_isUnion(const ZigClangRecordDecl *record_decl) { return reinterpret_cast(record_decl)->isUnion(); } diff --git a/src/zig_clang.h b/src/zig_clang.h index 4ea8cc731b..b015340c29 100644 --- a/src/zig_clang.h +++ b/src/zig_clang.h @@ -124,6 +124,7 @@ struct ZigClangEnumType; struct ZigClangExpr; struct ZigClangFieldDecl; struct ZigClangFileID; +struct ZigClangFileScopeAsmDecl; struct ZigClangFloatingLiteral; struct ZigClangForStmt; struct ZigClangFullSourceLoc; @@ -1000,6 +1001,8 @@ ZIG_EXTERN_C unsigned ZigClangVarDecl_getAlignedAttribute(const struct ZigClangV ZIG_EXTERN_C unsigned ZigClangFunctionDecl_getAlignedAttribute(const struct ZigClangFunctionDecl *self, const ZigClangASTContext* ctx); ZIG_EXTERN_C unsigned ZigClangFieldDecl_getAlignedAttribute(const struct ZigClangFieldDecl *self, const ZigClangASTContext* ctx); +ZIG_EXTERN_C const struct ZigClangStringLiteral *ZigClangFileScopeAsmDecl_getAsmString(const struct ZigClangFileScopeAsmDecl *self); + ZIG_EXTERN_C struct ZigClangQualType ZigClangParmVarDecl_getOriginalType(const struct ZigClangParmVarDecl *self); ZIG_EXTERN_C bool ZigClangRecordDecl_getPackedAttribute(const struct ZigClangRecordDecl *); diff --git a/test/translate_c.zig b/test/translate_c.zig index 4021210b29..5918dbb37b 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -3499,4 +3499,18 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ '\u{1f4af}', \\}; }); + + cases.add("global assembly", + \\__asm__(".globl func\n\t" + \\ ".type func, @function\n\t" + \\ "func:\n\t" + \\ ".cfi_startproc\n\t" + \\ "movl $42, %eax\n\t" + \\ "ret\n\t" + \\ ".cfi_endproc"); + , &[_][]const u8{ + \\comptime { + \\ asm (".globl func\n\t.type func, @function\n\tfunc:\n\t.cfi_startproc\n\tmovl $42, %eax\n\tret\n\t.cfi_endproc"); + \\} + }); } From 7a4b53fdee89f2d61cedbb3cef3bda24dacf2a57 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sat, 15 May 2021 09:59:22 +0200 Subject: [PATCH 41/42] std: Avoid using white color when printing stacktraces Use .bold instead of .white, the effect is the same for light-on-dark terminals but greatly improves the readability for dark-on-light ones. Closes #8761 --- lib/std/debug.zig | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 6cca67f302..3c8423c388 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -306,6 +306,7 @@ const RED = "\x1b[31;1m"; const GREEN = "\x1b[32;1m"; const CYAN = "\x1b[36;1m"; const WHITE = "\x1b[37;1m"; +const BOLD = "\x1b[1m"; const DIM = "\x1b[2m"; const RESET = "\x1b[0m"; @@ -476,8 +477,9 @@ pub const TTY = struct { .Red => out_stream.writeAll(RED) catch return, .Green => out_stream.writeAll(GREEN) catch return, .Cyan => out_stream.writeAll(CYAN) catch return, - .White, .Bold => out_stream.writeAll(WHITE) catch return, + .White => out_stream.writeAll(WHITE) catch return, .Dim => out_stream.writeAll(DIM) catch return, + .Bold => out_stream.writeAll(BOLD) catch return, .Reset => out_stream.writeAll(RESET) catch return, }, .windows_api => if (builtin.os.tag == .windows) { @@ -629,7 +631,7 @@ fn printLineInfo( comptime printLineFromFile: anytype, ) !void { nosuspend { - tty_config.setColor(out_stream, .White); + tty_config.setColor(out_stream, .Bold); if (line_info) |*li| { try out_stream.print("{s}:{d}:{d}", .{ li.file_name, li.line, li.column }); From d98e39fa6864f287bc50f265f98b7195849afa68 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Sat, 15 May 2021 16:59:29 +0200 Subject: [PATCH 42/42] stage2: make failure to find native libc verbose --- src/Compilation.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index f8ef492d78..ea61914ee2 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -2895,7 +2895,7 @@ fn detectLibCIncludeDirs( if (is_native_abi) { const libc = try arena.create(LibCInstallation); - libc.* = try LibCInstallation.findNative(.{ .allocator = arena }); + libc.* = try LibCInstallation.findNative(.{ .allocator = arena, .verbose = true }); return detectLibCFromLibCInstallation(arena, target, libc); }