From 434fce2146a9f1dc096fdae6827e0855799351ca Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 4 Mar 2021 20:54:09 -0700 Subject: [PATCH 01/49] zig fmt: recovery: missing while rbrace Previously, this test case resulted in zig fmt entering an endless loop. --- lib/std/zig/parse.zig | 9 ++++++--- lib/std/zig/parser_test.zig | 10 ++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index 9b755c2033..90e5c4fbff 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -937,14 +937,17 @@ const Parser = struct { /// If a parse error occurs, reports an error, but then finds the next statement /// and returns that one instead. If a parse error occurs but there is no following /// statement, returns 0. - fn expectStatementRecoverable(p: *Parser) error{OutOfMemory}!Node.Index { + fn expectStatementRecoverable(p: *Parser) Error!Node.Index { while (true) { return p.expectStatement() catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.ParseError => { p.findNextStmt(); // Try to skip to the next statement. - if (p.token_tags[p.tok_i] == .r_brace) return null_node; - continue; + switch (p.token_tags[p.tok_i]) { + .r_brace => return null_node, + .eof => return error.ParseError, + else => continue, + } }, }; } diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index c083d23932..42849166eb 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -4580,6 +4580,16 @@ test "recovery: missing comma in params" { }); } +test "recovery: missing while rbrace" { + try testError( + \\fn a() b { + \\ while (d) { + \\} + , &[_]Error{ + .expected_statement, + }); +} + const std = @import("std"); const mem = std.mem; const warn = std.debug.warn; From 02737d535ac5bda4e9a00ec0ae11ae313065dfbb Mon Sep 17 00:00:00 2001 From: cryptocode Date: Thu, 4 Mar 2021 18:42:56 +0100 Subject: [PATCH 02/49] Reject bare +/- input when parsing floats --- lib/std/fmt/parse_float.zig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/std/fmt/parse_float.zig b/lib/std/fmt/parse_float.zig index 324b06898e..19e17ef5a8 100644 --- a/lib/std/fmt/parse_float.zig +++ b/lib/std/fmt/parse_float.zig @@ -339,7 +339,7 @@ fn caseInEql(a: []const u8, b: []const u8) bool { } pub fn parseFloat(comptime T: type, s: []const u8) !T { - if (s.len == 0) { + if (s.len == 0 or (s.len == 1 and (s[0] == '+' or s[0] == '-'))) { return error.InvalidCharacter; } @@ -379,6 +379,8 @@ test "fmt.parseFloat" { testing.expectError(error.InvalidCharacter, parseFloat(T, "")); testing.expectError(error.InvalidCharacter, parseFloat(T, " 1")); testing.expectError(error.InvalidCharacter, parseFloat(T, "1abc")); + testing.expectError(error.InvalidCharacter, parseFloat(T, "+")); + testing.expectError(error.InvalidCharacter, parseFloat(T, "-")); expectEqual(try parseFloat(T, "0"), 0.0); expectEqual(try parseFloat(T, "0"), 0.0); From 291edafa1b3e6f56f88c3d1c542bdb25e99e45d1 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Wed, 3 Mar 2021 19:53:48 -0800 Subject: [PATCH 03/49] translate-c: enable pointer arithmetic with signed integer operand Given a pointer operand `ptr` and a signed integer operand `idx` `ptr + idx` and `idx + ptr` -> ptr + @bitCast(usize, @intCast(isize, idx)) `ptr - idx` -> ptr - @bitCast(usize, @intCast(isize, idx)) Thanks @LemonBoy for pointing out that we can take advantage of wraparound to dramatically simplify the code. --- src/translate_c.zig | 44 +++++++++++++++++++++++++++++++++++++++ test/run_translated_c.zig | 26 +++++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/src/translate_c.zig b/src/translate_c.zig index ed80a13058..2770ffb4cb 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -1127,6 +1127,44 @@ fn transOffsetOfExpr( return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "TODO: implement complex OffsetOfExpr translation", .{}); } +/// Translate an arithmetic expression with a pointer operand and a signed-integer operand. +/// Zig requires a usize argument for pointer arithmetic, so we intCast to isize and then +/// bitcast to usize; pointer wraparound make the math work. +/// Zig pointer addition is not commutative (unlike C); the pointer operand needs to be on the left. +/// The + operator in C is not a sequence point so it should be safe to switch the order if necessary. +fn transCreatePointerArithmeticSignedOp( + c: *Context, + scope: *Scope, + stmt: *const clang.BinaryOperator, + result_used: ResultUsed, +) TransError!Node { + const is_add = stmt.getOpcode() == .Add; + const lhs = stmt.getLHS(); + const rhs = stmt.getRHS(); + const swap_operands = is_add and cIsSignedInteger(getExprQualType(c, lhs)); + + const swizzled_lhs = if (swap_operands) rhs else lhs; + const swizzled_rhs = if (swap_operands) lhs else rhs; + + const lhs_node = try transExpr(c, scope, swizzled_lhs, .used); + const rhs_node = try transExpr(c, scope, swizzled_rhs, .used); + + const intcast_node = try Tag.int_cast.create(c.arena, .{ + .lhs = try Tag.identifier.create(c.arena, "isize"), + .rhs = rhs_node, + }); + + const bitcast_node = try Tag.bit_cast.create(c.arena, .{ + .lhs = try Tag.identifier.create(c.arena, "usize"), + .rhs = intcast_node, + }); + + const arith_args = .{ .lhs = lhs_node, .rhs = bitcast_node }; + const arith_node = try if (is_add) Tag.add.create(c.arena, arith_args) else Tag.sub.create(c.arena, arith_args); + + return maybeSuppressResult(c, scope, result_used, arith_node); +} + fn transBinaryOperator( c: *Context, scope: *Scope, @@ -1184,6 +1222,12 @@ fn transBinaryOperator( .LOr => { return transCreateNodeBoolInfixOp(c, scope, stmt, .@"or", result_used); }, + .Add, .Sub => { + // `ptr + idx` and `idx + ptr` -> ptr + @bitCast(usize, @intCast(isize, idx)) + // `ptr - idx` -> ptr - @bitCast(usize, @intCast(isize, idx)) + if (qualTypeIsPtr(qt) and (cIsSignedInteger(getExprQualType(c, stmt.getLHS())) or + cIsSignedInteger(getExprQualType(c, stmt.getRHS())))) return transCreatePointerArithmeticSignedOp(c, scope, stmt, result_used); + }, else => {}, } var op_id: Tag = undefined; diff --git a/test/run_translated_c.zig b/test/run_translated_c.zig index 07e449733f..977400be82 100644 --- a/test/run_translated_c.zig +++ b/test/run_translated_c.zig @@ -1131,4 +1131,30 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { \\ return 0; \\} , ""); + + cases.add("pointer arithmetic with signed operand", + \\#include + \\int main() { + \\ int array[10]; + \\ int *x = &array[5]; + \\ int *y; + \\ int idx = 0; + \\ y = x + ++idx; + \\ if (y != x + 1 || y != &array[6]) abort(); + \\ y = idx + x; + \\ if (y != x + 1 || y != &array[6]) abort(); + \\ y = x - idx; + \\ if (y != x - 1 || y != &array[4]) abort(); + \\ + \\ idx = 0; + \\ y = --idx + x; + \\ if (y != x - 1 || y != &array[4]) abort(); + \\ y = idx + x; + \\ if (y != x - 1 || y != &array[4]) abort(); + \\ y = x - idx; + \\ if (y != x + 1 || y != &array[6]) abort(); + \\ + \\ return 0; + \\} + , ""); } From 9cd038d73a174706ec0a51ab9db0c04b095e019d Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 5 Mar 2021 20:04:33 +0200 Subject: [PATCH 04/49] std: fix memory leak in MultiArrayList --- lib/std/multi_array_list.zig | 102 +++++++++++++++++++++++------------ 1 file changed, 69 insertions(+), 33 deletions(-) diff --git a/lib/std/multi_array_list.zig b/lib/std/multi_array_list.zig index 99a9fff7f0..37c4456d22 100644 --- a/lib/std/multi_array_list.zig +++ b/lib/std/multi_array_list.zig @@ -8,6 +8,7 @@ const assert = std.debug.assert; const meta = std.meta; const mem = std.mem; const Allocator = mem.Allocator; +const testing = std.testing; pub fn MultiArrayList(comptime S: type) type { return struct { @@ -247,6 +248,7 @@ pub fn MultiArrayList(comptime S: type) type { .exact, ); if (self.len == 0) { + gpa.free(self.allocatedBytes()); self.bytes = new_bytes.ptr; self.capacity = new_capacity; return; @@ -287,7 +289,6 @@ pub fn MultiArrayList(comptime S: type) type { } test "basic usage" { - const testing = std.testing; const ally = testing.allocator; const Foo = struct { @@ -369,7 +370,7 @@ test "basic usage" { // This was observed to fail on aarch64 with LLVM 11, when the capacityInBytes // function used the @reduce code path. test "regression test for @reduce bug" { - const ally = std.testing.allocator; + const ally = testing.allocator; var list = MultiArrayList(struct { tag: std.zig.Token.Tag, start: u32, @@ -412,35 +413,70 @@ test "regression test for @reduce bug" { try list.append(ally, .{ .tag = .eof, .start = 123 }); const tags = list.items(.tag); - std.testing.expectEqual(tags[1], .identifier); - std.testing.expectEqual(tags[2], .equal); - std.testing.expectEqual(tags[3], .builtin); - std.testing.expectEqual(tags[4], .l_paren); - std.testing.expectEqual(tags[5], .string_literal); - std.testing.expectEqual(tags[6], .r_paren); - std.testing.expectEqual(tags[7], .semicolon); - std.testing.expectEqual(tags[8], .keyword_pub); - std.testing.expectEqual(tags[9], .keyword_fn); - std.testing.expectEqual(tags[10], .identifier); - std.testing.expectEqual(tags[11], .l_paren); - std.testing.expectEqual(tags[12], .r_paren); - std.testing.expectEqual(tags[13], .identifier); - std.testing.expectEqual(tags[14], .bang); - std.testing.expectEqual(tags[15], .identifier); - std.testing.expectEqual(tags[16], .l_brace); - std.testing.expectEqual(tags[17], .identifier); - std.testing.expectEqual(tags[18], .period); - std.testing.expectEqual(tags[19], .identifier); - std.testing.expectEqual(tags[20], .period); - std.testing.expectEqual(tags[21], .identifier); - std.testing.expectEqual(tags[22], .l_paren); - std.testing.expectEqual(tags[23], .string_literal); - std.testing.expectEqual(tags[24], .comma); - std.testing.expectEqual(tags[25], .period); - std.testing.expectEqual(tags[26], .l_brace); - std.testing.expectEqual(tags[27], .r_brace); - std.testing.expectEqual(tags[28], .r_paren); - std.testing.expectEqual(tags[29], .semicolon); - std.testing.expectEqual(tags[30], .r_brace); - std.testing.expectEqual(tags[31], .eof); + testing.expectEqual(tags[1], .identifier); + testing.expectEqual(tags[2], .equal); + testing.expectEqual(tags[3], .builtin); + testing.expectEqual(tags[4], .l_paren); + testing.expectEqual(tags[5], .string_literal); + testing.expectEqual(tags[6], .r_paren); + testing.expectEqual(tags[7], .semicolon); + testing.expectEqual(tags[8], .keyword_pub); + testing.expectEqual(tags[9], .keyword_fn); + testing.expectEqual(tags[10], .identifier); + testing.expectEqual(tags[11], .l_paren); + testing.expectEqual(tags[12], .r_paren); + testing.expectEqual(tags[13], .identifier); + testing.expectEqual(tags[14], .bang); + testing.expectEqual(tags[15], .identifier); + testing.expectEqual(tags[16], .l_brace); + testing.expectEqual(tags[17], .identifier); + testing.expectEqual(tags[18], .period); + testing.expectEqual(tags[19], .identifier); + testing.expectEqual(tags[20], .period); + testing.expectEqual(tags[21], .identifier); + testing.expectEqual(tags[22], .l_paren); + testing.expectEqual(tags[23], .string_literal); + testing.expectEqual(tags[24], .comma); + testing.expectEqual(tags[25], .period); + testing.expectEqual(tags[26], .l_brace); + testing.expectEqual(tags[27], .r_brace); + testing.expectEqual(tags[28], .r_paren); + testing.expectEqual(tags[29], .semicolon); + testing.expectEqual(tags[30], .r_brace); + testing.expectEqual(tags[31], .eof); +} + +test "ensure capacity on empty list" { + const ally = testing.allocator; + + const Foo = struct { + a: u32, + b: u8, + }; + + var list = MultiArrayList(Foo){}; + defer list.deinit(ally); + + try list.ensureCapacity(ally, 2); + list.appendAssumeCapacity(.{ .a = 1, .b = 2 }); + list.appendAssumeCapacity(.{ .a = 3, .b = 4 }); + + testing.expectEqualSlices(u32, &[_]u32{ 1, 3 }, list.items(.a)); + testing.expectEqualSlices(u8, &[_]u8{ 2, 4 }, list.items(.b)); + + list.len = 0; + list.appendAssumeCapacity(.{ .a = 5, .b = 6 }); + list.appendAssumeCapacity(.{ .a = 7, .b = 8 }); + + testing.expectEqualSlices(u32, &[_]u32{ 5, 7 }, list.items(.a)); + testing.expectEqualSlices(u8, &[_]u8{ 6, 8 }, list.items(.b)); + + list.len = 0; + try list.ensureCapacity(ally, 16); + + list.appendAssumeCapacity(.{ .a = 9, .b = 10 }); + list.appendAssumeCapacity(.{ .a = 11, .b = 12 }); + + testing.expectEqualSlices(u32, &[_]u32{ 9, 11 }, list.items(.a)); + testing.expectEqualSlices(u8, &[_]u8{ 10, 12 }, list.items(.b)); } From 679910ecec5cb8d77cbb599ce5df9459615e2d50 Mon Sep 17 00:00:00 2001 From: xackus <14938807+xackus@users.noreply.github.com> Date: Tue, 2 Mar 2021 17:40:34 +0100 Subject: [PATCH 05/49] translate-c: promote int literals to bigger types --- lib/std/meta.zig | 30 +++++++++++++++++++++++++++ src/translate_c.zig | 46 +++++++++++++++++++++++++++++++++-------- src/translate_c/ast.zig | 29 ++++++++++++++++++++++++++ 3 files changed, 96 insertions(+), 9 deletions(-) diff --git a/lib/std/meta.zig b/lib/std/meta.zig index 7ec29dcd0e..7fe0df3dea 100644 --- a/lib/std/meta.zig +++ b/lib/std/meta.zig @@ -1094,6 +1094,36 @@ test "sizeof" { testing.expect(sizeof(c_void) == 1); } +pub const CIntLiteralRadix = enum { decimal, octal, hexadecimal }; + +fn PromoteIntLiteralReturnType(comptime SuffixType: type, comptime target: comptime_int, comptime radix: CIntLiteralRadix) type { + const signed_decimal = [_]type{ c_int, c_long, c_longlong }; + const signed_oct_hex = [_]type{ c_int, c_uint, c_long, c_ulong, c_longlong, c_ulonglong }; + const unsigned = [_]type{ c_uint, c_ulong, c_ulonglong }; + + const list: []const type = if (@typeInfo(SuffixType).Int.signedness == .unsigned) + &unsigned + else if (radix == .decimal) + &signed_decimal + else + &signed_oct_hex; + + var pos = mem.indexOfScalar(type, list, SuffixType).?; + + while (pos < list.len) : (pos += 1) { + if (target >= math.minInt(list[pos]) and target <= math.maxInt(list[pos])) { + return list[pos]; + } + } + @compileError("Integer literal does not fit in compatible types"); +} + +/// Promote the type of an integer literal until it fits as C would. +/// This is for translate-c and is not intended for general use. +pub fn promoteIntLiteral(comptime SuffixType: type, comptime target: comptime_int, comptime radix: CIntLiteralRadix) PromoteIntLiteralReturnType(SuffixType, target, radix) { + return @as(PromoteIntLiteralReturnType(SuffixType, target, radix), target); +} + /// For a given function type, returns a tuple type which fields will /// correspond to the argument types. /// diff --git a/src/translate_c.zig b/src/translate_c.zig index 2770ffb4cb..34655d3bb5 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -4431,40 +4431,68 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!Node { switch (m.list[m.i].id) { .IntegerLiteral => |suffix| { + var radix: []const u8 = "decimal"; if (lit_bytes.len > 2 and lit_bytes[0] == '0') { switch (lit_bytes[1]) { '0'...'7' => { // Octal lit_bytes = try std.fmt.allocPrint(c.arena, "0o{s}", .{lit_bytes}); + radix = "octal"; }, 'X' => { // Hexadecimal with capital X, valid in C but not in Zig lit_bytes = try std.fmt.allocPrint(c.arena, "0x{s}", .{lit_bytes[2..]}); + radix = "hexadecimal"; + }, + 'x' => { + radix = "hexadecimal"; }, else => {}, } } - if (suffix == .none) { - return transCreateNodeNumber(c, lit_bytes, .int); - } - const type_node = try Tag.type.create(c.arena, switch (suffix) { + .none => "c_int", .u => "c_uint", .l => "c_long", .lu => "c_ulong", .ll => "c_longlong", .llu => "c_ulonglong", - else => unreachable, + .f => unreachable, }); lit_bytes = lit_bytes[0 .. lit_bytes.len - switch (suffix) { - .u, .l => @as(u8, 1), + .none => @as(u8, 0), + .u, .l => 1, .lu, .ll => 2, .llu => 3, - else => unreachable, + .f => unreachable, }]; - const rhs = try transCreateNodeNumber(c, lit_bytes, .int); - return Tag.as.create(c.arena, .{ .lhs = type_node, .rhs = rhs }); + + const value = std.fmt.parseInt(i128, lit_bytes, 0) catch math.maxInt(i128); + + // make the output less noisy by skipping promoteIntLiteral where + // it's guaranteed to not be required because of C standard type constraints + const guaranteed_to_fit = switch (suffix) { + .none => if (math.cast(i16, value)) |_| true else |_| false, + .u => if (math.cast(u16, value)) |_| true else |_| false, + .l => if (math.cast(i32, value)) |_| true else |_| false, + .lu => if (math.cast(u32, value)) |_| true else |_| false, + .ll => if (math.cast(i64, value)) |_| true else |_| false, + .llu => if (math.cast(u64, value)) |_| true else |_| false, + .f => unreachable, + }; + + const literal_node = try transCreateNodeNumber(c, lit_bytes, .int); + + if (guaranteed_to_fit) { + return Tag.as.create(c.arena, .{ .lhs = type_node, .rhs = literal_node }); + } else { + return Tag.std_meta_promoteIntLiteral.create(c.arena, .{ + .type = type_node, + .value = literal_node, + .radix = try Tag.enum_literal.create(c.arena, radix), + }); + } }, .FloatLiteral => |suffix| { if (lit_bytes[0] == '.') diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index e984274c75..e5f76cc1de 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -39,6 +39,7 @@ pub const Node = extern union { float_literal, string_literal, char_literal, + enum_literal, identifier, @"if", /// if (!operand) break; @@ -117,6 +118,7 @@ pub const Node = extern union { /// @intCast(lhs, rhs) int_cast, /// @rem(lhs, rhs) + std_meta_promoteIntLiteral, rem, /// @divTrunc(lhs, rhs) div_trunc, @@ -312,6 +314,7 @@ pub const Node = extern union { .float_literal, .string_literal, .char_literal, + .enum_literal, .identifier, .warning, .type, @@ -328,6 +331,7 @@ pub const Node = extern union { .tuple => Payload.TupleInit, .container_init => Payload.ContainerInit, .std_meta_cast => Payload.Infix, + .std_meta_promoteIntLiteral => Payload.PromoteIntLiteral, .block => Payload.Block, .c_pointer, .single_pointer => Payload.Pointer, .array_type => Payload.Array, @@ -651,6 +655,15 @@ pub const Payload = struct { field_name: []const u8, }, }; + + pub const PromoteIntLiteral = struct { + base: Payload, + data: struct { + value: Node, + type: Node, + radix: Node, + }, + }; }; /// Converts the nodes into a Zig ast. @@ -821,6 +834,11 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { const import_node = try renderStdImport(c, "meta", "cast"); return renderCall(c, import_node, &.{ payload.lhs, payload.rhs }); }, + .std_meta_promoteIntLiteral => { + const payload = node.castTag(.std_meta_promoteIntLiteral).?.data; + const import_node = try renderStdImport(c, "meta", "promoteIntLiteral"); + return renderCall(c, import_node, &.{ payload.type, payload.value, payload.radix }); + }, .std_meta_sizeof => { const payload = node.castTag(.std_meta_sizeof).?.data; const import_node = try renderStdImport(c, "meta", "sizeof"); @@ -988,6 +1006,15 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { .data = undefined, }); }, + .enum_literal => { + const payload = node.castTag(.enum_literal).?.data; + _ = try c.addToken(.period, "."); + return c.addNode(.{ + .tag = .enum_literal, + .main_token = try c.addToken(.identifier, payload), + .data = undefined, + }); + }, .fail_decl => { const payload = node.castTag(.fail_decl).?.data; // pub const name = @compileError(msg); @@ -1982,11 +2009,13 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { .typeof, .std_meta_sizeof, .std_meta_cast, + .std_meta_promoteIntLiteral, .std_mem_zeroinit, .integer_literal, .float_literal, .string_literal, .char_literal, + .enum_literal, .identifier, .field_access, .ptr_cast, From 5f53b77c2bed56e53717315eef2c92bfbf0a3ee0 Mon Sep 17 00:00:00 2001 From: Maciej Walczak <14938807+xackus@users.noreply.github.com> Date: Fri, 5 Mar 2021 15:32:18 +0100 Subject: [PATCH 06/49] remove redundant cast Co-authored-by: Veikka Tuominen --- lib/std/meta.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/meta.zig b/lib/std/meta.zig index 7fe0df3dea..46d31a87c3 100644 --- a/lib/std/meta.zig +++ b/lib/std/meta.zig @@ -1121,7 +1121,7 @@ fn PromoteIntLiteralReturnType(comptime SuffixType: type, comptime target: compt /// Promote the type of an integer literal until it fits as C would. /// This is for translate-c and is not intended for general use. pub fn promoteIntLiteral(comptime SuffixType: type, comptime target: comptime_int, comptime radix: CIntLiteralRadix) PromoteIntLiteralReturnType(SuffixType, target, radix) { - return @as(PromoteIntLiteralReturnType(SuffixType, target, radix), target); + return target; } /// For a given function type, returns a tuple type which fields will From b4ef6fa09d945c6e7d0f8a73e77c4c03ba262fd7 Mon Sep 17 00:00:00 2001 From: xackus <14938807+xackus@users.noreply.github.com> Date: Fri, 5 Mar 2021 19:42:21 +0100 Subject: [PATCH 07/49] fix test-translate-c --- test/translate_c.zig | 78 ++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/test/translate_c.zig b/test/translate_c.zig index 367f69745b..6134190efb 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -232,12 +232,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ | (*((unsigned char *)(p) + 1) << 8) \ \\ | (*((unsigned char *)(p) + 2) << 16)) , &[_][]const u8{ - \\pub const FOO = (foo + 2).*; + \\pub const FOO = (foo + @as(c_int, 2)).*; , - \\pub const VALUE = ((((1 + (2 * 3)) + (4 * 5)) + 6) << 7) | @boolToInt(8 == 9); + \\pub const VALUE = ((((@as(c_int, 1) + (@as(c_int, 2) * @as(c_int, 3))) + (@as(c_int, 4) * @as(c_int, 5))) + @as(c_int, 6)) << @as(c_int, 7)) | @boolToInt(@as(c_int, 8) == @as(c_int, 9)); , - \\pub fn _AL_READ3BYTES(p: anytype) callconv(.Inline) @TypeOf((@import("std").meta.cast([*c]u8, p).* | ((@import("std").meta.cast([*c]u8, p) + 1).* << 8)) | ((@import("std").meta.cast([*c]u8, p) + 2).* << 16)) { - \\ return (@import("std").meta.cast([*c]u8, p).* | ((@import("std").meta.cast([*c]u8, p) + 1).* << 8)) | ((@import("std").meta.cast([*c]u8, p) + 2).* << 16); + \\pub fn _AL_READ3BYTES(p: anytype) callconv(.Inline) @TypeOf((@import("std").meta.cast([*c]u8, p).* | ((@import("std").meta.cast([*c]u8, p) + @as(c_int, 1)).* << @as(c_int, 8))) | ((@import("std").meta.cast([*c]u8, p) + @as(c_int, 2)).* << @as(c_int, 16))) { + \\ return (@import("std").meta.cast([*c]u8, p).* | ((@import("std").meta.cast([*c]u8, p) + @as(c_int, 1)).* << @as(c_int, 8))) | ((@import("std").meta.cast([*c]u8, p) + @as(c_int, 2)).* << @as(c_int, 16)); \\} }); @@ -312,14 +312,14 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return type_1; \\} , - \\pub const LIGHTGRAY = @import("std").mem.zeroInit(CLITERAL(Color), .{ 200, 200, 200, 255 }); + \\pub const LIGHTGRAY = @import("std").mem.zeroInit(CLITERAL(Color), .{ @as(c_int, 200), @as(c_int, 200), @as(c_int, 200), @as(c_int, 255) }); , \\pub const struct_boom_t = extern struct { \\ i1: c_int, \\}; \\pub const boom_t = struct_boom_t; , - \\pub const FOO = @import("std").mem.zeroInit(boom_t, .{1}); + \\pub const FOO = @import("std").mem.zeroInit(boom_t, .{@as(c_int, 1)}); }); cases.add("complex switch", @@ -343,8 +343,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { cases.add("correct semicolon after infixop", \\#define __ferror_unlocked_body(_fp) (((_fp)->_flags & _IO_ERR_SEEN) != 0) , &[_][]const u8{ - \\pub fn __ferror_unlocked_body(_fp: anytype) callconv(.Inline) @TypeOf((_fp.*._flags & _IO_ERR_SEEN) != 0) { - \\ return (_fp.*._flags & _IO_ERR_SEEN) != 0; + \\pub fn __ferror_unlocked_body(_fp: anytype) callconv(.Inline) @TypeOf((_fp.*._flags & _IO_ERR_SEEN) != @as(c_int, 0)) { + \\ return (_fp.*._flags & _IO_ERR_SEEN) != @as(c_int, 0); \\} }); @@ -352,11 +352,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\#define FOO(x) ((x >= 0) + (x >= 0)) \\#define BAR 1 && 2 > 4 , &[_][]const u8{ - \\pub fn FOO(x: anytype) callconv(.Inline) @TypeOf(@boolToInt(x >= 0) + @boolToInt(x >= 0)) { - \\ return @boolToInt(x >= 0) + @boolToInt(x >= 0); + \\pub fn FOO(x: anytype) callconv(.Inline) @TypeOf(@boolToInt(x >= @as(c_int, 0)) + @boolToInt(x >= @as(c_int, 0))) { + \\ return @boolToInt(x >= @as(c_int, 0)) + @boolToInt(x >= @as(c_int, 0)); \\} , - \\pub const BAR = (1 != 0) and (2 > 4); + \\pub const BAR = (@as(c_int, 1) != 0) and (@as(c_int, 2) > @as(c_int, 4)); }); cases.add("struct with aligned fields", @@ -401,15 +401,15 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ break :blk bar; \\}; , - \\pub fn bar(x: anytype) callconv(.Inline) @TypeOf(baz(1, 2)) { + \\pub fn bar(x: anytype) callconv(.Inline) @TypeOf(baz(@as(c_int, 1), @as(c_int, 2))) { \\ return blk: { \\ _ = &x; - \\ _ = 3; - \\ _ = 4 == 4; - \\ _ = 5 * 6; - \\ _ = baz(1, 2); - \\ _ = 2 % 2; - \\ break :blk baz(1, 2); + \\ _ = @as(c_int, 3); + \\ _ = @as(c_int, 4) == @as(c_int, 4); + \\ _ = @as(c_int, 5) * @as(c_int, 6); + \\ _ = baz(@as(c_int, 1), @as(c_int, 2)); + \\ _ = @as(c_int, 2) % @as(c_int, 2); + \\ break :blk baz(@as(c_int, 1), @as(c_int, 2)); \\ }; \\} }); @@ -418,9 +418,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\#define foo 1 \\#define inline 2 , &[_][]const u8{ - \\pub const foo = 1; + \\pub const foo = @as(c_int, 1); , - \\pub const @"inline" = 2; + \\pub const @"inline" = @as(c_int, 2); }); cases.add("macro line continuation", @@ -507,7 +507,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { cases.add("#define hex literal with capital X", \\#define VAL 0XF00D , &[_][]const u8{ - \\pub const VAL = 0xF00D; + \\pub const VAL = @import("std").meta.promoteIntLiteral(c_int, 0xF00D, .hexadecimal); }); cases.add("anonymous struct & unions", @@ -872,7 +872,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { cases.add("macro with left shift", \\#define REDISMODULE_READ (1<<0) , &[_][]const u8{ - \\pub const REDISMODULE_READ = 1 << 0; + \\pub const REDISMODULE_READ = @as(c_int, 1) << @as(c_int, 0); }); cases.add("macro with right shift", @@ -881,7 +881,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , &[_][]const u8{ \\pub const FLASH_SIZE = @as(c_ulong, 0x200000); , - \\pub const FLASH_BANK_SIZE = FLASH_SIZE >> 1; + \\pub const FLASH_BANK_SIZE = FLASH_SIZE >> @as(c_int, 1); }); cases.add("double define struct", @@ -949,14 +949,14 @@ pub fn addCases(cases: *tests.TranslateCContext) void { cases.add("#define an unsigned integer literal", \\#define CHANNEL_COUNT 24 , &[_][]const u8{ - \\pub const CHANNEL_COUNT = 24; + \\pub const CHANNEL_COUNT = @as(c_int, 24); }); cases.add("#define referencing another #define", \\#define THING2 THING1 \\#define THING1 1234 , &[_][]const u8{ - \\pub const THING1 = 1234; + \\pub const THING1 = @as(c_int, 1234); , \\pub const THING2 = THING1; }); @@ -1002,7 +1002,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { cases.add("macro with parens around negative number", \\#define LUA_GLOBALSINDEX (-10002) , &[_][]const u8{ - \\pub const LUA_GLOBALSINDEX = -10002; + \\pub const LUA_GLOBALSINDEX = -@as(c_int, 10002); }); cases.add( @@ -1085,8 +1085,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\#define foo 1 //foo \\#define bar /* bar */ 2 , &[_][]const u8{ - "pub const foo = 1;", - "pub const bar = 2;", + "pub const foo = @as(c_int, 1);", + "pub const bar = @as(c_int, 2);", }); cases.add("string prefix", @@ -1716,7 +1716,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { cases.add("comment after integer literal", \\#define SDL_INIT_VIDEO 0x00000020 /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */ , &[_][]const u8{ - \\pub const SDL_INIT_VIDEO = 0x00000020; + \\pub const SDL_INIT_VIDEO = @as(c_int, 0x00000020); }); cases.add("u integer suffix after hex literal", @@ -1830,8 +1830,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , &[_][]const u8{ \\pub extern var c: c_int; , - \\pub fn BASIC(c_1: anytype) callconv(.Inline) @TypeOf(c_1 * 2) { - \\ return c_1 * 2; + \\pub fn BASIC(c_1: anytype) callconv(.Inline) @TypeOf(c_1 * @as(c_int, 2)) { + \\ return c_1 * @as(c_int, 2); \\} , \\pub fn FOO(L: anytype, b: anytype) callconv(.Inline) @TypeOf(L + b) { @@ -2475,7 +2475,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return array[@intCast(c_uint, index)]; \\} , - \\pub const ACCESS = array[2]; + \\pub const ACCESS = array[@as(c_int, 2)]; }); cases.add("cast signed array index to unsigned", @@ -3091,7 +3091,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , \\pub const BAR = @import("std").meta.cast(?*c_void, a); , - \\pub const BAZ = @import("std").meta.cast(u32, 2); + \\pub const BAZ = @import("std").meta.cast(u32, @as(c_int, 2)); }); cases.add("macro with cast to unsigned short, long, and long long", @@ -3099,9 +3099,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\#define CURLAUTH_BASIC ((unsigned long) 1) \\#define CURLAUTH_BASIC_BUT_ULONGLONG ((unsigned long long) 1) , &[_][]const u8{ - \\pub const CURLAUTH_BASIC_BUT_USHORT = @import("std").meta.cast(c_ushort, 1); - \\pub const CURLAUTH_BASIC = @import("std").meta.cast(c_ulong, 1); - \\pub const CURLAUTH_BASIC_BUT_ULONGLONG = @import("std").meta.cast(c_ulonglong, 1); + \\pub const CURLAUTH_BASIC_BUT_USHORT = @import("std").meta.cast(c_ushort, @as(c_int, 1)); + \\pub const CURLAUTH_BASIC = @import("std").meta.cast(c_ulong, @as(c_int, 1)); + \\pub const CURLAUTH_BASIC_BUT_ULONGLONG = @import("std").meta.cast(c_ulonglong, @as(c_int, 1)); }); cases.add("macro conditional operator", @@ -3196,7 +3196,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ bar_1 = 2; \\} , - \\pub const bar = 4; + \\pub const bar = @as(c_int, 4); }); cases.add("don't export inline functions", @@ -3325,9 +3325,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\#define NULL ((void*)0) \\#define FOO ((int)0x8000) , &[_][]const u8{ - \\pub const NULL = @import("std").meta.cast(?*c_void, 0); + \\pub const NULL = @import("std").meta.cast(?*c_void, @as(c_int, 0)); , - \\pub const FOO = @import("std").meta.cast(c_int, 0x8000); + \\pub const FOO = @import("std").meta.cast(c_int, @import("std").meta.promoteIntLiteral(c_int, 0x8000, .hexadecimal)); }); if (std.Target.current.abi == .msvc) { From eee43a65aec2e2b104ff64cb23488a86437578e4 Mon Sep 17 00:00:00 2001 From: xackus <14938807+xackus@users.noreply.github.com> Date: Fri, 5 Mar 2021 20:51:19 +0100 Subject: [PATCH 08/49] add tests --- lib/std/meta.zig | 32 +++++++++++++++++++++++++++----- test/translate_c.zig | 20 ++++++++++++++++++++ 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/lib/std/meta.zig b/lib/std/meta.zig index 46d31a87c3..fd3e03bdbd 100644 --- a/lib/std/meta.zig +++ b/lib/std/meta.zig @@ -1096,7 +1096,7 @@ test "sizeof" { pub const CIntLiteralRadix = enum { decimal, octal, hexadecimal }; -fn PromoteIntLiteralReturnType(comptime SuffixType: type, comptime target: comptime_int, comptime radix: CIntLiteralRadix) type { +fn PromoteIntLiteralReturnType(comptime SuffixType: type, comptime number: comptime_int, comptime radix: CIntLiteralRadix) type { const signed_decimal = [_]type{ c_int, c_long, c_longlong }; const signed_oct_hex = [_]type{ c_int, c_uint, c_long, c_ulong, c_longlong, c_ulonglong }; const unsigned = [_]type{ c_uint, c_ulong, c_ulonglong }; @@ -1111,17 +1111,39 @@ fn PromoteIntLiteralReturnType(comptime SuffixType: type, comptime target: compt var pos = mem.indexOfScalar(type, list, SuffixType).?; while (pos < list.len) : (pos += 1) { - if (target >= math.minInt(list[pos]) and target <= math.maxInt(list[pos])) { + if (number >= math.minInt(list[pos]) and number <= math.maxInt(list[pos])) { return list[pos]; } } - @compileError("Integer literal does not fit in compatible types"); + @compileError("Integer literal is too large"); } /// Promote the type of an integer literal until it fits as C would. /// This is for translate-c and is not intended for general use. -pub fn promoteIntLiteral(comptime SuffixType: type, comptime target: comptime_int, comptime radix: CIntLiteralRadix) PromoteIntLiteralReturnType(SuffixType, target, radix) { - return target; +pub fn promoteIntLiteral( + comptime SuffixType: type, + comptime number: comptime_int, + comptime radix: CIntLiteralRadix, +) PromoteIntLiteralReturnType(SuffixType, number, radix) { + return number; +} + +test "promoteIntLiteral" { + const signed_hex = promoteIntLiteral(c_int, math.maxInt(c_int) + 1, .hexadecimal); + testing.expectEqual(c_uint, @TypeOf(signed_hex)); + + if (math.maxInt(c_longlong) == math.maxInt(c_int)) return; + + const signed_decimal = promoteIntLiteral(c_int, math.maxInt(c_int) + 1, .decimal); + const unsigned = promoteIntLiteral(c_uint, math.maxInt(c_uint) + 1, .hexadecimal); + + if (math.maxInt(c_long) > math.maxInt(c_int)) { + testing.expectEqual(c_long, @TypeOf(signed_decimal)); + testing.expectEqual(c_ulong, @TypeOf(unsigned)); + } else { + testing.expectEqual(c_longlong, @TypeOf(signed_decimal)); + testing.expectEqual(c_ulonglong, @TypeOf(unsigned)); + } } /// For a given function type, returns a tuple type which fields will diff --git a/test/translate_c.zig b/test/translate_c.zig index 6134190efb..5785d07311 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -3392,4 +3392,24 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ unnamed_0: struct_unnamed_2, \\}; }); + + cases.add("integer literal promotion", + \\#define GUARANTEED_TO_FIT_1 1024 + \\#define GUARANTEED_TO_FIT_2 10241024L + \\#define GUARANTEED_TO_FIT_3 20482048LU + \\#define MAY_NEED_PROMOTION_1 10241024 + \\#define MAY_NEED_PROMOTION_2 307230723072L + \\#define MAY_NEED_PROMOTION_3 819281928192LU + \\#define MAY_NEED_PROMOTION_HEX 0x80000000 + \\#define MAY_NEED_PROMOTION_OCT 020000000000 + , &[_][]const u8{ + \\pub const GUARANTEED_TO_FIT_1 = @as(c_int, 1024); + \\pub const GUARANTEED_TO_FIT_2 = @as(c_long, 10241024); + \\pub const GUARANTEED_TO_FIT_3 = @as(c_ulong, 20482048); + \\pub const MAY_NEED_PROMOTION_1 = @import("std").meta.promoteIntLiteral(c_int, 10241024, .decimal); + \\pub const MAY_NEED_PROMOTION_2 = @import("std").meta.promoteIntLiteral(c_long, 307230723072, .decimal); + \\pub const MAY_NEED_PROMOTION_3 = @import("std").meta.promoteIntLiteral(c_ulong, 819281928192, .decimal); + \\pub const MAY_NEED_PROMOTION_HEX = @import("std").meta.promoteIntLiteral(c_int, 0x80000000, .hexadecimal); + \\pub const MAY_NEED_PROMOTION_OCT = @import("std").meta.promoteIntLiteral(c_int, 0o020000000000, .octal); + }); } From eda1b53723447c0fe0bc15bf29abcee2abe90153 Mon Sep 17 00:00:00 2001 From: xackus <14938807+xackus@users.noreply.github.com> Date: Fri, 5 Mar 2021 21:03:25 +0100 Subject: [PATCH 09/49] strip the leading zero from octal literals --- src/translate_c.zig | 2 +- test/translate_c.zig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/translate_c.zig b/src/translate_c.zig index 34655d3bb5..3656c1e244 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -4436,7 +4436,7 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!Node { switch (lit_bytes[1]) { '0'...'7' => { // Octal - lit_bytes = try std.fmt.allocPrint(c.arena, "0o{s}", .{lit_bytes}); + lit_bytes = try std.fmt.allocPrint(c.arena, "0o{s}", .{lit_bytes[1..]}); radix = "octal"; }, 'X' => { diff --git a/test/translate_c.zig b/test/translate_c.zig index 5785d07311..e8054c87ad 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -3410,6 +3410,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub const MAY_NEED_PROMOTION_2 = @import("std").meta.promoteIntLiteral(c_long, 307230723072, .decimal); \\pub const MAY_NEED_PROMOTION_3 = @import("std").meta.promoteIntLiteral(c_ulong, 819281928192, .decimal); \\pub const MAY_NEED_PROMOTION_HEX = @import("std").meta.promoteIntLiteral(c_int, 0x80000000, .hexadecimal); - \\pub const MAY_NEED_PROMOTION_OCT = @import("std").meta.promoteIntLiteral(c_int, 0o020000000000, .octal); + \\pub const MAY_NEED_PROMOTION_OCT = @import("std").meta.promoteIntLiteral(c_int, 0o20000000000, .octal); }); } From ef3adbdb362d5511b05543567c58c5bdfb0cf093 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Fri, 5 Mar 2021 12:29:02 +0100 Subject: [PATCH 10/49] zig fmt: fix lastToken() for container_decl_arg --- lib/std/zig/ast.zig | 2 +- lib/std/zig/parser_test.zig | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/std/zig/ast.zig b/lib/std/zig/ast.zig index 46b58e9465..1a0515b475 100644 --- a/lib/std/zig/ast.zig +++ b/lib/std/zig/ast.zig @@ -766,7 +766,7 @@ pub const Tree = struct { .container_decl_arg => { const members = tree.extraData(datas[n].rhs, Node.SubRange); if (members.end - members.start == 0) { - end_offset += 1; // for the rparen + end_offset += 3; // for the rparen + lbrace + rbrace n = datas[n].lhs; } else { end_offset += 1; // for the rbrace diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index 42849166eb..f4b95f2362 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -963,6 +963,27 @@ test "zig fmt: allowzero pointer" { ); } +test "zig fmt: empty enum decls" { + try testCanonical( + \\const A = enum {}; + \\const B = enum(u32) {}; + \\const C = extern enum(c_int) {}; + \\const D = packed enum(u8) {}; + \\ + ); +} + +test "zig fmt: empty union decls" { + try testCanonical( + \\const A = union {}; + \\const B = union(enum) {}; + \\const C = union(Foo) {}; + \\const D = extern union {}; + \\const E = packed union {}; + \\ + ); +} + test "zig fmt: enum literal" { try testCanonical( \\const x = .hi; From 9f722f43ac449caa0e09c1b7bb1bad0581ef323d Mon Sep 17 00:00:00 2001 From: Meghan Date: Fri, 5 Mar 2021 19:13:05 -0800 Subject: [PATCH 11/49] std/special: init-exe,lib make import(std) its own decl (#8160) std/special: init-exe,lib make import(std) its own decl --- lib/std/special/init-exe/build.zig | 4 ++-- lib/std/special/init-lib/build.zig | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/std/special/init-exe/build.zig b/lib/std/special/init-exe/build.zig index 3a66254b02..714c97e008 100644 --- a/lib/std/special/init-exe/build.zig +++ b/lib/std/special/init-exe/build.zig @@ -1,6 +1,6 @@ -const Builder = @import("std").build.Builder; +const std = @import("std"); -pub fn build(b: *Builder) void { +pub fn build(b: *std.build.Builder) void { // Standard target options allows the person running `zig build` to choose // what target to build for. Here we do not override the defaults, which // means any target is allowed, and the default is native. Other options diff --git a/lib/std/special/init-lib/build.zig b/lib/std/special/init-lib/build.zig index 558e447c15..f7d261f152 100644 --- a/lib/std/special/init-lib/build.zig +++ b/lib/std/special/init-lib/build.zig @@ -1,7 +1,10 @@ -const Builder = @import("std").build.Builder; +const std = @import("std"); -pub fn build(b: *Builder) void { +pub fn build(b: *std.build.Builder) void { + // Standard release options allow the person running `zig build` to select + // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. const mode = b.standardReleaseOptions(); + const lib = b.addStaticLibrary("$", "src/main.zig"); lib.setBuildMode(mode); lib.install(); From 17e6e09285ed29ead1a3de5d5bfeb4d287f23215 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Thu, 28 Jan 2021 22:38:25 +0200 Subject: [PATCH 12/49] stage2: astgen async --- src/Module.zig | 71 ++++++++++++++++++++++++++++- src/astgen.zig | 115 ++++++++++++++++++++++++++++++++++++++++++++--- src/zir.zig | 21 +++++++++ src/zir_sema.zig | 25 ++++++++++- 4 files changed, 223 insertions(+), 9 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index e6d509ace5..c5cdbb3890 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -370,6 +370,8 @@ pub const Scope = struct { .gen_zir => return self.cast(GenZIR).?.arena, .local_val => return self.cast(LocalVal).?.gen_zir.arena, .local_ptr => return self.cast(LocalPtr).?.gen_zir.arena, + .gen_suspend => return self.cast(GenZIR).?.arena, + .gen_nosuspend => return self.cast(Nosuspend).?.gen_zir.arena, .file => unreachable, .container => unreachable, } @@ -385,6 +387,8 @@ pub const Scope = struct { .gen_zir => self.cast(GenZIR).?.decl, .local_val => self.cast(LocalVal).?.gen_zir.decl, .local_ptr => self.cast(LocalPtr).?.gen_zir.decl, + .gen_suspend => return self.cast(GenZIR).?.decl, + .gen_nosuspend => return self.cast(Nosuspend).?.gen_zir.decl, .file => null, .container => null, }; @@ -396,6 +400,8 @@ pub const Scope = struct { .gen_zir => self.cast(GenZIR).?.decl, .local_val => self.cast(LocalVal).?.gen_zir.decl, .local_ptr => self.cast(LocalPtr).?.gen_zir.decl, + .gen_suspend => return self.cast(GenZIR).?.decl, + .gen_nosuspend => return self.cast(Nosuspend).?.gen_zir.decl, .file => null, .container => null, }; @@ -410,6 +416,8 @@ pub const Scope = struct { .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.container, .file => return &self.cast(File).?.root_container, .container => return self.cast(Container).?, + .gen_suspend => return self.cast(GenZIR).?.decl.container, + .gen_nosuspend => return self.cast(Nosuspend).?.gen_zir.decl.container, } } @@ -422,6 +430,8 @@ pub const Scope = struct { .gen_zir => unreachable, .local_val => unreachable, .local_ptr => unreachable, + .gen_suspend => unreachable, + .gen_nosuspend => unreachable, .file => unreachable, .container => return self.cast(Container).?.fullyQualifiedNameHash(name), } @@ -436,6 +446,8 @@ pub const Scope = struct { .local_val => return &self.cast(LocalVal).?.gen_zir.decl.container.file_scope.tree, .local_ptr => return &self.cast(LocalPtr).?.gen_zir.decl.container.file_scope.tree, .container => return &self.cast(Container).?.file_scope.tree, + .gen_suspend => return &self.cast(GenZIR).?.decl.container.file_scope.tree, + .gen_nosuspend => return &self.cast(Nosuspend).?.gen_zir.decl.container.file_scope.tree, } } @@ -443,9 +455,10 @@ pub const Scope = struct { pub fn getGenZIR(self: *Scope) *GenZIR { return switch (self.tag) { .block => unreachable, - .gen_zir => self.cast(GenZIR).?, + .gen_zir, .gen_suspend => self.cast(GenZIR).?, .local_val => return self.cast(LocalVal).?.gen_zir, .local_ptr => return self.cast(LocalPtr).?.gen_zir, + .gen_nosuspend => return self.cast(Nosuspend).?.gen_zir, .file => unreachable, .container => unreachable, }; @@ -461,6 +474,8 @@ pub const Scope = struct { .gen_zir => unreachable, .local_val => unreachable, .local_ptr => unreachable, + .gen_suspend => unreachable, + .gen_nosuspend => unreachable, } } @@ -472,6 +487,8 @@ pub const Scope = struct { .local_val => unreachable, .local_ptr => unreachable, .block => unreachable, + .gen_suspend => unreachable, + .gen_nosuspend => unreachable, } } @@ -486,6 +503,36 @@ pub const Scope = struct { .local_val => @fieldParentPtr(LocalVal, "base", cur).parent, .local_ptr => @fieldParentPtr(LocalPtr, "base", cur).parent, .block => return @fieldParentPtr(Block, "base", cur).src_decl.container.file_scope, + .gen_suspend => @fieldParentPtr(GenZIR, "base", cur).parent, + .gen_nosuspend => @fieldParentPtr(Nosuspend, "base", cur).parent, + }; + } + } + + pub fn getSuspend(base: *Scope) ?*Scope.GenZIR { + var cur = base; + while (true) { + cur = switch (cur.tag) { + .gen_zir => @fieldParentPtr(GenZIR, "base", cur).parent, + .local_val => @fieldParentPtr(LocalVal, "base", cur).parent, + .local_ptr => @fieldParentPtr(LocalPtr, "base", cur).parent, + .gen_nosuspend => @fieldParentPtr(Nosuspend, "base", cur).parent, + .gen_suspend => return @fieldParentPtr(GenZIR, "base", cur), + else => return null, + }; + } + } + + pub fn getNosuspend(base: *Scope) ?*Scope.Nosuspend { + var cur = base; + while (true) { + cur = switch (cur.tag) { + .gen_zir => @fieldParentPtr(GenZIR, "base", cur).parent, + .local_val => @fieldParentPtr(LocalVal, "base", cur).parent, + .local_ptr => @fieldParentPtr(LocalPtr, "base", cur).parent, + .gen_suspend => @fieldParentPtr(GenZIR, "base", cur).parent, + .gen_nosuspend => return @fieldParentPtr(Nosuspend, "base", cur), + else => return null, }; } } @@ -507,6 +554,8 @@ pub const Scope = struct { gen_zir, local_val, local_ptr, + gen_suspend, + gen_nosuspend, }; pub const Container = struct { @@ -740,6 +789,8 @@ pub const Scope = struct { /// so they can possibly be elided later if the labeled block ends up not needing /// a result location pointer. labeled_store_to_block_ptr_list: std.ArrayListUnmanaged(*zir.Inst.BinOp) = .{}, + /// for suspend error notes + src: usize = 0, pub const Label = struct { token: ast.TokenIndex, @@ -773,6 +824,16 @@ pub const Scope = struct { name: []const u8, ptr: *zir.Inst, }; + + pub const Nosuspend = struct { + pub const base_tag: Tag = .gen_nosuspend; + + base: Scope = Scope{ .tag = base_tag }, + /// Parents can be: `LocalVal`, `LocalPtr`, `GenZIR`. + parent: *Scope, + gen_zir: *GenZIR, + src: usize, + }; }; /// This struct holds data necessary to construct API-facing `AllErrors.Message`. @@ -3586,7 +3647,7 @@ pub fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, err_msg: *ErrorMsg) I } self.failed_decls.putAssumeCapacityNoClobber(block.owner_decl, err_msg); }, - .gen_zir => { + .gen_zir, .gen_suspend => { const gen_zir = scope.cast(Scope.GenZIR).?; gen_zir.decl.analysis = .sema_failure; gen_zir.decl.generation = self.generation; @@ -3604,6 +3665,12 @@ pub fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, err_msg: *ErrorMsg) I gen_zir.decl.generation = self.generation; self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg); }, + .gen_nosuspend => { + const gen_zir = scope.cast(Scope.Nosuspend).?.gen_zir; + gen_zir.decl.analysis = .sema_failure; + gen_zir.decl.generation = self.generation; + self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg); + }, .file => unreachable, .container => unreachable, } diff --git a/src/astgen.zig b/src/astgen.zig index aaf38ed1ea..939fba2ff3 100644 --- a/src/astgen.zig +++ b/src/astgen.zig @@ -626,10 +626,13 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) In .@"comptime" => return comptimeExpr(mod, scope, rl, node_datas[node].lhs), .@"switch", .switch_comma => return switchExpr(mod, scope, rl, node), + .@"nosuspend" => return nosuspendExpr(mod, scope, rl, node), + .@"suspend" => return rvalue(mod, scope, rl, try suspendExpr(mod, scope, node)), + .@"await" => return awaitExpr(mod, scope, rl, node), + .@"resume" => return rvalue(mod, scope, rl, try resumeExpr(mod, scope, node)), + .@"defer" => return mod.failNode(scope, node, "TODO implement astgen.expr for .defer", .{}), .@"errdefer" => return mod.failNode(scope, node, "TODO implement astgen.expr for .errdefer", .{}), - .@"await" => return mod.failNode(scope, node, "TODO implement astgen.expr for .await", .{}), - .@"resume" => return mod.failNode(scope, node, "TODO implement astgen.expr for .resume", .{}), .@"try" => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}), .array_init_one, @@ -652,15 +655,12 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) In .struct_init_comma, => return mod.failNode(scope, node, "TODO implement astgen.expr for struct literals", .{}), - .@"suspend" => return mod.failNode(scope, node, "TODO implement astgen.expr for .suspend", .{}), .@"anytype" => return mod.failNode(scope, node, "TODO implement astgen.expr for .anytype", .{}), .fn_proto_simple, .fn_proto_multi, .fn_proto_one, .fn_proto, => return mod.failNode(scope, node, "TODO implement astgen.expr for function prototypes", .{}), - - .@"nosuspend" => return mod.failNode(scope, node, "TODO implement astgen.expr for .nosuspend", .{}), } } @@ -766,6 +766,8 @@ fn breakExpr( }, .local_val => scope = scope.cast(Scope.LocalVal).?.parent, .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, + .gen_suspend => scope = scope.cast(Scope.GenZIR).?.parent, + .gen_nosuspend => scope = scope.cast(Scope.Nosuspend).?.parent, else => if (break_label != 0) { const label_name = try mod.identifierTokenString(parent_scope, break_label); return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name}); @@ -819,6 +821,8 @@ fn continueExpr( }, .local_val => scope = scope.cast(Scope.LocalVal).?.parent, .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, + .gen_suspend => scope = scope.cast(Scope.GenZIR).?.parent, + .gen_nosuspend => scope = scope.cast(Scope.Nosuspend).?.parent, else => if (break_label != 0) { const label_name = try mod.identifierTokenString(parent_scope, break_label); return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name}); @@ -893,6 +897,8 @@ fn checkLabelRedefinition(mod: *Module, parent_scope: *Scope, label: ast.TokenIn }, .local_val => scope = scope.cast(Scope.LocalVal).?.parent, .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, + .gen_suspend => scope = scope.cast(Scope.GenZIR).?.parent, + .gen_nosuspend => scope = scope.cast(Scope.Nosuspend).?.parent, else => return, } } @@ -1100,6 +1106,8 @@ fn varDecl( s = local_ptr.parent; }, .gen_zir => s = s.cast(Scope.GenZIR).?.parent, + .gen_suspend => s = s.cast(Scope.GenZIR).?.parent, + .gen_nosuspend => s = s.cast(Scope.Nosuspend).?.parent, else => break, }; } @@ -3021,6 +3029,8 @@ fn identifier( s = local_ptr.parent; }, .gen_zir => s = s.cast(Scope.GenZIR).?.parent, + .gen_suspend => s = s.cast(Scope.GenZIR).?.parent, + .gen_nosuspend => s = s.cast(Scope.Nosuspend).?.parent, else => break, }; } @@ -3633,14 +3643,109 @@ fn callExpr( } const src = token_starts[call.ast.lparen]; + var modifier: std.builtin.CallOptions.Modifier = .auto; + if (call.async_token) |_| modifier = .async_kw; + const result = try addZIRInst(mod, scope, src, zir.Inst.Call, .{ .func = lhs, .args = args, + .modifier = modifier, }, .{}); // TODO function call with result location return rvalue(mod, scope, rl, result); } +fn suspendExpr(mod: *Module, scope: *Scope, node: ast.Node.Index) InnerError!*zir.Inst { + const tree = scope.tree(); + const src = tree.tokens.items(.start)[tree.nodes.items(.main_token)[node]]; + + if (scope.getNosuspend()) |some| { + const msg = msg: { + const msg = try mod.errMsg(scope, src, "suspend in nosuspend block", .{}); + errdefer msg.destroy(mod.gpa); + try mod.errNote(scope, some.src, msg, "nosuspend block here", .{}); + break :msg msg; + }; + return mod.failWithOwnedErrorMsg(scope, msg); + } + + if (scope.getSuspend()) |some| { + const msg = msg: { + const msg = try mod.errMsg(scope, src, "cannot suspend inside suspend block", .{}); + errdefer msg.destroy(mod.gpa); + try mod.errNote(scope, some.src, msg, "other suspend block here", .{}); + break :msg msg; + }; + return mod.failWithOwnedErrorMsg(scope, msg); + } + + var suspend_scope: Scope.GenZIR = .{ + .base = .{ .tag = .gen_suspend }, + .parent = scope, + .decl = scope.ownerDecl().?, + .arena = scope.arena(), + .force_comptime = scope.isComptime(), + .instructions = .{}, + }; + defer suspend_scope.instructions.deinit(mod.gpa); + + const operand = tree.nodes.items(.data)[node].lhs; + if (operand != 0) { + const possibly_unused_result = try expr(mod, &suspend_scope.base, .none, operand); + if (!possibly_unused_result.tag.isNoReturn()) { + _ = try addZIRUnOp(mod, &suspend_scope.base, src, .ensure_result_used, possibly_unused_result); + } + } else { + return addZIRNoOp(mod, scope, src, .@"suspend"); + } + + const block = try addZIRInstBlock(mod, scope, src, .suspend_block, .{ + .instructions = try scope.arena().dupe(*zir.Inst, suspend_scope.instructions.items), + }); + return &block.base; +} + +fn nosuspendExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!*zir.Inst { + const tree = scope.tree(); + var child_scope = Scope.Nosuspend{ + .parent = scope, + .gen_zir = scope.getGenZIR(), + .src = tree.tokens.items(.start)[tree.nodes.items(.main_token)[node]], + }; + + return expr(mod, &child_scope.base, rl, tree.nodes.items(.data)[node].lhs); +} + +fn awaitExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!*zir.Inst { + const tree = scope.tree(); + const src = tree.tokens.items(.start)[tree.nodes.items(.main_token)[node]]; + const is_nosuspend = scope.getNosuspend() != null; + + // TODO some @asyncCall stuff + + if (scope.getSuspend()) |some| { + const msg = msg: { + const msg = try mod.errMsg(scope, src, "cannot await inside suspend block", .{}); + errdefer msg.destroy(mod.gpa); + try mod.errNote(scope, some.src, msg, "suspend block here", .{}); + break :msg msg; + }; + return mod.failWithOwnedErrorMsg(scope, msg); + } + + const operand = try expr(mod, scope, .ref, tree.nodes.items(.data)[node].lhs); + // TODO pass result location + return addZIRUnOp(mod, scope, src, if (is_nosuspend) .nosuspend_await else .@"await", operand); +} + +fn resumeExpr(mod: *Module, scope: *Scope, node: ast.Node.Index) InnerError!*zir.Inst { + const tree = scope.tree(); + const src = tree.tokens.items(.start)[tree.nodes.items(.main_token)[node]]; + + const operand = try expr(mod, scope, .ref, tree.nodes.items(.data)[node].lhs); + return addZIRUnOp(mod, scope, src, .@"resume", operand); +} + pub const simple_types = std.ComptimeStringMap(Value.Tag, .{ .{ "u8", .u8_type }, .{ "i8", .i8_type }, diff --git a/src/zir.zig b/src/zir.zig index 1331f26dc7..85ecf99f20 100644 --- a/src/zir.zig +++ b/src/zir.zig @@ -61,6 +61,8 @@ pub const Inst = struct { as, /// Inline assembly. @"asm", + /// Await an async function. + @"await", /// Bitwise AND. `&` bit_and, /// TODO delete this instruction, it has no purpose. @@ -212,6 +214,8 @@ pub const Inst = struct { mul, /// Twos complement wrapping integer multiplication. mulwrap, + /// An await inside a nosuspend scope. + nosuspend_await, /// Given a reference to a function and a parameter index, returns the /// type of the parameter. TODO what happens when the parameter is `anytype`? param_type, @@ -226,6 +230,8 @@ pub const Inst = struct { /// the memory location is in the stack frame, local to the scope containing the /// instruction. ref, + /// Resume an async function. + @"resume", /// Obtains a pointer to the return value. ret_ptr, /// Obtains the return type of the in-scope function. @@ -348,6 +354,11 @@ pub const Inst = struct { enum_type, /// Does nothing; returns a void value. void_value, + /// Suspend an async function. + @"suspend", + /// Suspend an async function. + /// Same as .suspend but with a block. + suspend_block, /// A switch expression. switchbr, /// Same as `switchbr` but the target is a pointer to the value being switched on. @@ -369,6 +380,7 @@ pub const Inst = struct { .unreachable_unsafe, .unreachable_safe, .void_value, + .@"suspend", => NoOp, .alloc, @@ -417,6 +429,9 @@ pub const Inst = struct { .import, .set_eval_branch_quota, .indexable_ptr_len, + .@"resume", + .@"await", + .nosuspend_await, => UnOp, .add, @@ -461,6 +476,7 @@ pub const Inst = struct { .block_flat, .block_comptime, .block_comptime_flat, + .suspend_block, => Block, .switchbr, .switchbr_ref => SwitchBr, @@ -633,6 +649,9 @@ pub const Inst = struct { .struct_type, .void_value, .switch_range, + .@"resume", + .@"await", + .nosuspend_await, => false, .@"break", @@ -649,6 +668,8 @@ pub const Inst = struct { .container_field, .switchbr, .switchbr_ref, + .@"suspend", + .suspend_block, => true, }; } diff --git a/src/zir_sema.zig b/src/zir_sema.zig index 27e31c6197..735defa323 100644 --- a/src/zir_sema.zig +++ b/src/zir_sema.zig @@ -160,6 +160,11 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! .switchbr => return zirSwitchBr(mod, scope, old_inst.castTag(.switchbr).?, false), .switchbr_ref => return zirSwitchBr(mod, scope, old_inst.castTag(.switchbr_ref).?, true), .switch_range => return zirSwitchRange(mod, scope, old_inst.castTag(.switch_range).?), + .@"await" => return zirAwait(mod, scope, old_inst.castTag(.@"await").?), + .nosuspend_await => return zirAwait(mod, scope, old_inst.castTag(.nosuspend_await).?), + .@"resume" => return zirResume(mod, scope, old_inst.castTag(.@"resume").?), + .@"suspend" => return zirSuspend(mod, scope, old_inst.castTag(.@"suspend").?), + .suspend_block => return zirSuspendBlock(mod, scope, old_inst.castTag(.suspend_block).?), .container_field_named, .container_field_typed, @@ -1080,6 +1085,22 @@ fn zirFn(mod: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError!*Inst { }); } +fn zirAwait(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + return mod.fail(scope, inst.base.src, "TODO implement await", .{}); +} + +fn zirResume(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + return mod.fail(scope, inst.base.src, "TODO implement resume", .{}); +} + +fn zirSuspend(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { + return mod.fail(scope, inst.base.src, "TODO implement suspend", .{}); +} + +fn zirSuspendBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerError!*Inst { + return mod.fail(scope, inst.base.src, "TODO implement suspend", .{}); +} + fn zirIntType(mod: *Module, scope: *Scope, inttype: *zir.Inst.IntType) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); @@ -2046,7 +2067,7 @@ fn zirBitwise(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*In rhs.ty.arrayLen(), }); } - return mod.fail(scope, inst.base.src, "TODO implement support for vectors in analyzeInstBitwise", .{}); + return mod.fail(scope, inst.base.src, "TODO implement support for vectors in zirBitwise", .{}); } else if (lhs.ty.zigTypeTag() == .Vector or rhs.ty.zigTypeTag() == .Vector) { return mod.fail(scope, inst.base.src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{ lhs.ty, @@ -2127,7 +2148,7 @@ fn zirArithmetic(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError! rhs.ty.arrayLen(), }); } - return mod.fail(scope, inst.base.src, "TODO implement support for vectors in analyzeInstBinOp", .{}); + return mod.fail(scope, inst.base.src, "TODO implement support for vectors in zirBinOp", .{}); } else if (lhs.ty.zigTypeTag() == .Vector or rhs.ty.zigTypeTag() == .Vector) { return mod.fail(scope, inst.base.src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{ lhs.ty, From 8c6e7fb2c7488faab0c41a4ea241f0110237ce91 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 29 Jan 2021 12:19:10 +0200 Subject: [PATCH 13/49] stage2: implement var args --- src/Module.zig | 68 +++++++++++++++++++++++++++++++++++---------- src/codegen/c.zig | 9 ++++-- src/test.zig | 1 + src/type.zig | 47 ++++++++++++++++++++++++++++++- src/zir.zig | 10 +++++-- src/zir_sema.zig | 33 ++++++++++++---------- test/stage2/cbe.zig | 13 +++++++++ 7 files changed, 147 insertions(+), 34 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index c5cdbb3890..585925c4a0 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -1183,7 +1183,8 @@ fn astgenAndSemaFn( const param_count = blk: { var count: usize = 0; var it = fn_proto.iterate(tree); - while (it.next()) |_| { + while (it.next()) |param| { + if (param.anytype_ellipsis3) |some| if (token_tags[some] == .ellipsis3) break; count += 1; } break :blk count; @@ -1196,6 +1197,7 @@ fn astgenAndSemaFn( }); const type_type_rl: astgen.ResultLoc = .{ .ty = type_type }; + var is_var_args = false; { var param_type_i: usize = 0; var it = fn_proto.iterate(tree); @@ -1208,12 +1210,10 @@ fn astgenAndSemaFn( "TODO implement anytype parameter", .{}, ), - .ellipsis3 => return mod.failTok( - &fn_type_scope.base, - token, - "TODO implement var args", - .{}, - ), + .ellipsis3 => { + is_var_args = true; + break; + }, else => unreachable, } } @@ -1295,7 +1295,13 @@ fn astgenAndSemaFn( type_type_rl, fn_proto.ast.return_type, ); - const fn_type_inst = if (fn_proto.ast.callconv_expr != 0) cc: { + + const is_extern = if (fn_proto.extern_export_token) |maybe_export_token| + token_tags[maybe_export_token] == .keyword_extern + else + false; + + const cc_inst = if (fn_proto.ast.callconv_expr != 0) cc: { // TODO instead of enum literal type, this needs to be the // std.builtin.CallingConvention enum. We need to implement importing other files // and enums in order to fix this. @@ -1304,18 +1310,31 @@ fn astgenAndSemaFn( .ty = Type.initTag(.type), .val = Value.initTag(.enum_literal_type), }); - const cc = try astgen.comptimeExpr(mod, &fn_type_scope.base, .{ + break :cc try astgen.comptimeExpr(mod, &fn_type_scope.base, .{ .ty = enum_lit_ty, }, fn_proto.ast.callconv_expr); - break :cc try astgen.addZirInstTag(mod, &fn_type_scope.base, fn_src, .fn_type_cc, .{ + } else if (is_extern) cc: { + // note: https://github.com/ziglang/zig/issues/5269 + const src = token_starts[fn_proto.extern_export_token.?]; + break :cc try astgen.addZIRInst(mod, &fn_type_scope.base, src, zir.Inst.EnumLiteral, .{ .name = "C" }, .{}); + } else null; + + const fn_type_inst = if (cc_inst) |cc| fn_type: { + var fn_type = try astgen.addZirInstTag(mod, &fn_type_scope.base, fn_src, .fn_type_cc, .{ .return_type = return_type_inst, .param_types = param_types, .cc = cc, }); - } else try astgen.addZirInstTag(mod, &fn_type_scope.base, fn_src, .fn_type, .{ - .return_type = return_type_inst, - .param_types = param_types, - }); + if (is_var_args) fn_type.tag = .fn_type_cc_var_args; + break :fn_type fn_type; + } else fn_type: { + var fn_type = try astgen.addZirInstTag(mod, &fn_type_scope.base, fn_src, .fn_type, .{ + .return_type = return_type_inst, + .param_types = param_types, + }); + if (is_var_args) fn_type.tag = .fn_type_var_args; + break :fn_type fn_type; + }; if (std.builtin.mode == .Debug and mod.comp.verbose_ir) { zir.dumpZir(mod.gpa, "fn_type", decl.name, fn_type_scope.instructions.items) catch {}; @@ -1348,7 +1367,12 @@ fn astgenAndSemaFn( const fn_type = try zir_sema.analyzeBodyValueAsType(mod, &block_scope, fn_type_inst, .{ .instructions = fn_type_scope.instructions.items, }); + if (body_node == 0) { + if (!is_extern) { + return mod.failNode(&block_scope.base, fn_proto.ast.fn_token, "non-extern function has no body", .{}); + } + // Extern function. var type_changed = true; if (decl.typedValueManaged()) |tvm| { @@ -1378,6 +1402,10 @@ fn astgenAndSemaFn( return type_changed; } + if (fn_type.fnIsVarArgs()) { + return mod.failNode(&block_scope.base, fn_proto.ast.fn_token, "non-extern function is variadic", .{}); + } + const new_func = try decl_arena.allocator.create(Fn); const fn_payload = try decl_arena.allocator.create(Value.Payload.Function); @@ -3356,6 +3384,9 @@ pub fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Ty } pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) InnerError!*Inst { + if (dest_type.tag() == .var_args_param) { + return self.coerceVarArgParam(scope, inst); + } // If the types are the same, we can return the operand. if (dest_type.eql(inst.ty)) return inst; @@ -3508,6 +3539,15 @@ pub fn coerceNum(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) Inn return null; } +pub fn coerceVarArgParam(mod: *Module, scope: *Scope, inst: *Inst) !*Inst { + switch (inst.ty.zigTypeTag()) { + .ComptimeInt, .ComptimeFloat => return mod.fail(scope, inst.src, "integer and float literals in var args function must be casted", .{}), + else => {}, + } + // TODO implement more of this function. + return inst; +} + pub fn storePtr(self: *Module, scope: *Scope, src: usize, ptr: *Inst, uncasted_value: *Inst) !*Inst { if (ptr.ty.isConstPtr()) return self.fail(scope, src, "cannot assign to constant", .{}); diff --git a/src/codegen/c.zig b/src/codegen/c.zig index a885b984ac..1a323441d9 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -215,8 +215,9 @@ pub const DeclGen = struct { try dg.renderType(w, tv.ty.fnReturnType()); const decl_name = mem.span(dg.decl.name); try w.print(" {s}(", .{decl_name}); - var param_len = tv.ty.fnParamLen(); - if (param_len == 0) + const param_len = tv.ty.fnParamLen(); + const is_var_args = tv.ty.fnIsVarArgs(); + if (param_len == 0 and !is_var_args) try w.writeAll("void") else { var index: usize = 0; @@ -228,6 +229,10 @@ pub const DeclGen = struct { try w.print(" a{d}", .{index}); } } + if (is_var_args) { + if (param_len != 0) try w.writeAll(", "); + try w.writeAll("..."); + } try w.writeByte(')'); } diff --git a/src/test.zig b/src/test.zig index d2c368d8ea..47aeef3385 100644 --- a/src/test.zig +++ b/src/test.zig @@ -871,6 +871,7 @@ pub const TestContext = struct { "-std=c89", "-pedantic", "-Werror", + "-Wno-incompatible-library-redeclaration", // https://github.com/ziglang/zig/issues/875 "-Wno-declaration-after-statement", "--", "-lc", diff --git a/src/type.zig b/src/type.zig index 38fe6dd3e6..bb8dcea390 100644 --- a/src/type.zig +++ b/src/type.zig @@ -97,6 +97,8 @@ pub const Type = extern union { .@"struct", .empty_struct => return .Struct, .@"enum" => return .Enum, .@"union" => return .Union, + + .var_args_param => unreachable, // can be any type } } @@ -258,6 +260,8 @@ pub const Type = extern union { if (!a.fnParamType(i).eql(b.fnParamType(i))) return false; } + if (a.fnIsVarArgs() != b.fnIsVarArgs()) + return false; return true; }, .Optional => { @@ -323,6 +327,7 @@ pub const Type = extern union { while (i < params_len) : (i += 1) { std.hash.autoHash(&hasher, self.fnParamType(i).hash()); } + std.hash.autoHash(&hasher, self.fnIsVarArgs()); }, .Optional => { var buf: Payload.ElemType = undefined; @@ -397,6 +402,7 @@ pub const Type = extern union { .@"anyframe", .inferred_alloc_const, .inferred_alloc_mut, + .var_args_param, => unreachable, .array_u8, @@ -446,6 +452,7 @@ pub const Type = extern union { .return_type = try payload.return_type.copy(allocator), .param_types = param_types, .cc = payload.cc, + .is_var_args = payload.is_var_args, }); }, .pointer => { @@ -535,6 +542,7 @@ pub const Type = extern union { .comptime_int, .comptime_float, .noreturn, + .var_args_param, => return out_stream.writeAll(@tagName(t)), .enum_literal => return out_stream.writeAll("@Type(.EnumLiteral)"), @@ -558,6 +566,12 @@ pub const Type = extern union { if (i != 0) try out_stream.writeAll(", "); try param_type.format("", .{}, out_stream); } + if (payload.is_var_args) { + if (payload.param_types.len != 0) { + try out_stream.writeAll(", "); + } + try out_stream.writeAll("..."); + } try out_stream.writeAll(") callconv(."); try out_stream.writeAll(@tagName(payload.cc)); try out_stream.writeAll(")"); @@ -844,6 +858,7 @@ pub const Type = extern union { .inferred_alloc_const => unreachable, .inferred_alloc_mut => unreachable, + .var_args_param => unreachable, }; } @@ -969,6 +984,7 @@ pub const Type = extern union { .inferred_alloc_const, .inferred_alloc_mut, .@"opaque", + .var_args_param, => unreachable, }; } @@ -995,6 +1011,7 @@ pub const Type = extern union { .inferred_alloc_const => unreachable, .inferred_alloc_mut => unreachable, .@"opaque" => unreachable, + .var_args_param => unreachable, .u8, .i8, @@ -1179,6 +1196,7 @@ pub const Type = extern union { .@"struct", .@"union", .@"opaque", + .var_args_param, => false, .single_const_pointer, @@ -1256,6 +1274,7 @@ pub const Type = extern union { .@"struct", .@"union", .@"opaque", + .var_args_param, => unreachable, .const_slice, @@ -1354,6 +1373,7 @@ pub const Type = extern union { .@"struct", .@"union", .@"opaque", + .var_args_param, => false, .const_slice, @@ -1434,6 +1454,7 @@ pub const Type = extern union { .@"struct", .@"union", .@"opaque", + .var_args_param, => false, .single_const_pointer, @@ -1523,6 +1544,7 @@ pub const Type = extern union { .@"struct", .@"union", .@"opaque", + .var_args_param, => false, .pointer => { @@ -1607,6 +1629,7 @@ pub const Type = extern union { .@"struct", .@"union", .@"opaque", + .var_args_param, => false, .pointer => { @@ -1733,6 +1756,7 @@ pub const Type = extern union { .@"struct" => unreachable, .@"union" => unreachable, .@"opaque" => unreachable, + .var_args_param => unreachable, .array => self.castTag(.array).?.data.elem_type, .array_sentinel => self.castTag(.array_sentinel).?.data.elem_type, @@ -1862,6 +1886,7 @@ pub const Type = extern union { .@"struct", .@"union", .@"opaque", + .var_args_param, => unreachable, .array => self.castTag(.array).?.data.len, @@ -1936,6 +1961,7 @@ pub const Type = extern union { .@"struct", .@"union", .@"opaque", + .var_args_param, => unreachable, .single_const_pointer, @@ -2025,6 +2051,7 @@ pub const Type = extern union { .@"struct", .@"union", .@"opaque", + .var_args_param, => false, .int_signed, @@ -2110,6 +2137,7 @@ pub const Type = extern union { .@"struct", .@"union", .@"opaque", + .var_args_param, => false, .int_unsigned, @@ -2181,6 +2209,7 @@ pub const Type = extern union { .@"struct", .@"union", .@"opaque", + .var_args_param, => unreachable, .int_unsigned => .{ @@ -2280,6 +2309,7 @@ pub const Type = extern union { .@"struct", .@"union", .@"opaque", + .var_args_param, => false, .usize, @@ -2400,6 +2430,7 @@ pub const Type = extern union { .@"struct", .@"union", .@"opaque", + .var_args_param, => unreachable, }; } @@ -2486,6 +2517,7 @@ pub const Type = extern union { .@"struct", .@"union", .@"opaque", + .var_args_param, => unreachable, } } @@ -2571,6 +2603,7 @@ pub const Type = extern union { .@"struct", .@"union", .@"opaque", + .var_args_param, => unreachable, } } @@ -2656,6 +2689,7 @@ pub const Type = extern union { .@"struct", .@"union", .@"opaque", + .var_args_param, => unreachable, }; } @@ -2738,6 +2772,7 @@ pub const Type = extern union { .@"struct", .@"union", .@"opaque", + .var_args_param, => unreachable, }; } @@ -2749,7 +2784,7 @@ pub const Type = extern union { .fn_void_no_args => false, .fn_naked_noreturn_no_args => false, .fn_ccc_void_no_args => false, - .function => false, + .function => self.castTag(.function).?.data.is_var_args, .f16, .f32, @@ -2820,6 +2855,7 @@ pub const Type = extern union { .@"struct", .@"union", .@"opaque", + .var_args_param, => unreachable, }; } @@ -2902,6 +2938,7 @@ pub const Type = extern union { .@"struct", .@"union", .@"opaque", + .var_args_param, => false, }; } @@ -2962,6 +2999,7 @@ pub const Type = extern union { .error_set, .error_set_single, .@"opaque", + .var_args_param, => return null, .@"enum" => @panic("TODO onePossibleValue enum"), @@ -3079,6 +3117,7 @@ pub const Type = extern union { .@"struct", .@"union", .@"opaque", + .var_args_param, => return false, .c_const_pointer, @@ -3168,6 +3207,7 @@ pub const Type = extern union { .pointer, .inferred_alloc_const, .inferred_alloc_mut, + .var_args_param, => unreachable, .empty_struct => self.castTag(.empty_struct).?.data, @@ -3285,6 +3325,9 @@ pub const Type = extern union { anyerror_void_error_union, @"anyframe", const_slice_u8, + /// This is a special type for variadic parameters of a function call. + /// Casts to it will validate that the type can be passed to a c calling convetion function. + var_args_param, /// This is a special value that tracks a set of types that have been stored /// to an inferred allocation. It does not support most of the normal type queries. /// However it does respond to `isConstPtr`, `ptrSize`, `zigTypeTag`, etc. @@ -3373,6 +3416,7 @@ pub const Type = extern union { .const_slice_u8, .inferred_alloc_const, .inferred_alloc_mut, + .var_args_param, => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"), .array_u8, @@ -3479,6 +3523,7 @@ pub const Type = extern union { param_types: []Type, return_type: Type, cc: std.builtin.CallingConvention, + is_var_args: bool, }, }; diff --git a/src/zir.zig b/src/zir.zig index 85ecf99f20..cb1f4561bf 100644 --- a/src/zir.zig +++ b/src/zir.zig @@ -178,8 +178,12 @@ pub const Inst = struct { @"fn", /// Returns a function type, assuming unspecified calling convention. fn_type, + /// Same as `fn_type` but the function is variadic. + fn_type_var_args, /// Returns a function type, with a calling convention instruction operand. fn_type_cc, + /// Same as `fn_type_cc` but the function is variadic. + fn_type_cc_var_args, /// @import(operand) import, /// Integer literal. @@ -502,8 +506,8 @@ pub const Inst = struct { .@"export" => Export, .param_type => ParamType, .primitive => Primitive, - .fn_type => FnType, - .fn_type_cc => FnTypeCc, + .fn_type, .fn_type_var_args => FnType, + .fn_type_cc, .fn_type_cc_var_args => FnTypeCc, .elem_ptr, .elem_val => Elem, .condbr => CondBr, .ptr_type => PtrType, @@ -579,7 +583,9 @@ pub const Inst = struct { .field_val_named, .@"fn", .fn_type, + .fn_type_var_args, .fn_type_cc, + .fn_type_cc_var_args, .int, .intcast, .int_type, diff --git a/src/zir_sema.zig b/src/zir_sema.zig index 735defa323..9cbdfd07dd 100644 --- a/src/zir_sema.zig +++ b/src/zir_sema.zig @@ -91,8 +91,10 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! .@"fn" => return zirFn(mod, scope, old_inst.castTag(.@"fn").?), .@"export" => return zirExport(mod, scope, old_inst.castTag(.@"export").?), .primitive => return zirPrimitive(mod, scope, old_inst.castTag(.primitive).?), - .fn_type => return zirFnType(mod, scope, old_inst.castTag(.fn_type).?), - .fn_type_cc => return zirFnTypeCc(mod, scope, old_inst.castTag(.fn_type_cc).?), + .fn_type => return zirFnType(mod, scope, old_inst.castTag(.fn_type).?, false), + .fn_type_cc => return zirFnTypeCc(mod, scope, old_inst.castTag(.fn_type_cc).?, false), + .fn_type_var_args => return zirFnType(mod, scope, old_inst.castTag(.fn_type_var_args).?, true), + .fn_type_cc_var_args => return zirFnTypeCc(mod, scope, old_inst.castTag(.fn_type_cc_var_args).?, true), .intcast => return zirIntcast(mod, scope, old_inst.castTag(.intcast).?), .bitcast => return zirBitcast(mod, scope, old_inst.castTag(.bitcast).?), .floatcast => return zirFloatcast(mod, scope, old_inst.castTag(.floatcast).?), @@ -522,9 +524,11 @@ fn zirParamType(mod: *Module, scope: *Scope, inst: *zir.Inst.ParamType) InnerErr }, }; - // TODO support C-style var args const param_count = fn_ty.fnParamLen(); if (arg_index >= param_count) { + if (fn_ty.fnIsVarArgs()) { + return mod.constType(scope, inst.base.src, Type.initTag(.var_args_param)); + } return mod.fail(scope, inst.base.src, "arg index {d} out of bounds; '{}' has {d} argument(s)", .{ arg_index, fn_ty, @@ -946,6 +950,7 @@ fn zirCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst { const call_params_len = inst.positionals.args.len; const fn_params_len = func.ty.fnParamLen(); if (func.ty.fnIsVarArgs()) { + assert(cc == .C); if (call_params_len < fn_params_len) { // TODO add error note: declared here return mod.fail( @@ -955,7 +960,6 @@ fn zirCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst { .{ fn_params_len, call_params_len }, ); } - return mod.fail(scope, inst.base.src, "TODO implement support for calling var args functions", .{}); } else if (fn_params_len != call_params_len) { // TODO add error note: declared here return mod.fail( @@ -974,15 +978,10 @@ fn zirCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst { } // TODO handle function calls of generic functions - - const fn_param_types = try mod.gpa.alloc(Type, fn_params_len); - defer mod.gpa.free(fn_param_types); - func.ty.fnParamTypes(fn_param_types); - - const casted_args = try scope.arena().alloc(*Inst, fn_params_len); + const casted_args = try scope.arena().alloc(*Inst, call_params_len); for (inst.positionals.args) |src_arg, i| { - const uncasted_arg = try resolveInst(mod, scope, src_arg); - casted_args[i] = try mod.coerce(scope, fn_param_types[i], uncasted_arg); + // the args are already casted to the result of a param type instruction. + casted_args[i] = try resolveInst(mod, scope, src_arg); } const ret_type = func.ty.fnReturnType(); @@ -1503,7 +1502,7 @@ fn zirEnsureErrPayloadVoid(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) return mod.constVoid(scope, unwrap.base.src); } -fn zirFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst { +fn zirFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType, var_args: bool) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); @@ -1514,10 +1513,11 @@ fn zirFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!* fntype.positionals.param_types, fntype.positionals.return_type, .Unspecified, + var_args, ); } -fn zirFnTypeCc(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnTypeCc) InnerError!*Inst { +fn zirFnTypeCc(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnTypeCc, var_args: bool) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); @@ -1534,6 +1534,7 @@ fn zirFnTypeCc(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnTypeCc) InnerErr fntype.positionals.param_types, fntype.positionals.return_type, cc, + var_args, ); } @@ -1544,11 +1545,12 @@ fn fnTypeCommon( zir_param_types: []*zir.Inst, zir_return_type: *zir.Inst, cc: std.builtin.CallingConvention, + var_args: bool, ) InnerError!*Inst { const return_type = try resolveType(mod, scope, zir_return_type); // Hot path for some common function types. - if (zir_param_types.len == 0) { + if (zir_param_types.len == 0 and !var_args) { if (return_type.zigTypeTag() == .NoReturn and cc == .Unspecified) { return mod.constType(scope, zir_inst.src, Type.initTag(.fn_noreturn_no_args)); } @@ -1581,6 +1583,7 @@ fn fnTypeCommon( .param_types = param_types, .return_type = return_type, .cc = cc, + .is_var_args = var_args, }); return mod.constType(scope, zir_inst.src, fn_ty); } diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig index 35ae1dbf12..873f0e1198 100644 --- a/test/stage2/cbe.zig +++ b/test/stage2/cbe.zig @@ -41,6 +41,19 @@ pub fn addCases(ctx: *TestContext) !void { , "yo!" ++ std.cstr.line_sep); } + { + var case = ctx.exeFromCompiledC("var args", .{}); + + case.addCompareOutput( + \\extern fn printf(format: [*:0]const u8, ...) c_int; + \\ + \\export fn main() c_int { + \\ _ = printf("Hello, %s!\n", "world"); + \\ return 0; + \\} + , "Hello, world!\n"); + } + { var case = ctx.exeFromCompiledC("x86_64-linux inline assembly", linux_x64); From 272ae0ca0dff89689ad9715a4d8fa98ed86440b9 Mon Sep 17 00:00:00 2001 From: Vincent Rischmann Date: Sat, 6 Mar 2021 19:37:33 +0100 Subject: [PATCH 14/49] fix parsing of assignment with 'inline for' and 'inline while' --- lib/std/zig/parse.zig | 2 +- lib/std/zig/parser_test.zig | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index 90e5c4fbff..ad32d3182d 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -1981,7 +1981,7 @@ const Parser = struct { } }, .keyword_inline => { - p.tok_i += 2; + p.tok_i += 1; switch (p.token_tags[p.tok_i]) { .keyword_for => return p.parseForExpr(), .keyword_while => return p.parseWhileExpr(), diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index f4b95f2362..5c0f4b3bda 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -4297,6 +4297,18 @@ test "zig fmt: respect extra newline between switch items" { ); } +test "zig fmt: assignment with inline for and inline while" { + try testCanonical( + \\const tmp = inline for (items) |item| {}; + \\ + ); + + try testCanonical( + \\const tmp2 = inline while (true) {}; + \\ + ); +} + test "zig fmt: insert trailing comma if there are comments between switch values" { try testTransform( \\const a = switch (b) { From a7c0234eee97c11b1dd6879a3cf313ac9a7bdbd9 Mon Sep 17 00:00:00 2001 From: Josh Wolfe Date: Thu, 4 Mar 2021 09:05:43 -0500 Subject: [PATCH 15/49] docgen --skip-code-tests --- doc/docgen.zig | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/doc/docgen.zig b/doc/docgen.zig index 4f06b63c2c..1a9744698f 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -34,6 +34,15 @@ pub fn main() !void { const out_file_name = try (args_it.next(allocator) orelse @panic("expected output arg")); defer allocator.free(out_file_name); + var do_code_tests = true; + if (args_it.next(allocator)) |arg| { + if (mem.eql(u8, try arg, "--skip-code-tests")) { + do_code_tests = false; + } else { + @panic("unrecognized arg"); + } + } + var in_file = try fs.cwd().openFile(in_file_name, .{ .read = true }); defer in_file.close(); @@ -50,7 +59,7 @@ pub fn main() !void { try fs.cwd().makePath(tmp_dir_name); defer fs.cwd().deleteTree(tmp_dir_name) catch {}; - try genHtml(allocator, &tokenizer, &toc, buffered_writer.writer(), zig_exe); + try genHtml(allocator, &tokenizer, &toc, buffered_writer.writer(), zig_exe, do_code_tests); try buffered_writer.flush(); } @@ -564,8 +573,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc { ); } _ = try eatToken(tokenizer, Token.Id.BracketClose); - } else - unreachable; // TODO issue #707 + } else unreachable; // TODO issue #707 try nodes.append(Node{ .Code = Code{ .id = code_kind_id, @@ -784,12 +792,12 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: anytype, source_token: if (mem.indexOf(u8, src[index..token.loc.start], "//")) |comment_start_off| { // render one comment const comment_start = index + comment_start_off; - const comment_end_off = mem.indexOf(u8, src[comment_start .. token.loc.start], "\n"); + const comment_end_off = mem.indexOf(u8, src[comment_start..token.loc.start], "\n"); const comment_end = if (comment_end_off) |o| comment_start + o else token.loc.start; try writeEscaped(out, src[index..comment_start]); try out.writeAll(""); - try writeEscaped(out, src[comment_start .. comment_end]); + try writeEscaped(out, src[comment_start..comment_end]); try out.writeAll(""); index = comment_end; tokenizer.index = index; @@ -1002,7 +1010,7 @@ fn tokenizeAndPrint(docgen_tokenizer: *Tokenizer, out: anytype, source_token: To return tokenizeAndPrintRaw(docgen_tokenizer, out, source_token, raw_src); } -fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: anytype, zig_exe: []const u8) !void { +fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: anytype, zig_exe: []const u8, do_code_tests: bool) !void { var code_progress_index: usize = 0; var env_map = try process.getEnvMap(allocator); @@ -1061,6 +1069,12 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any try out.writeAll("
");
                 try tokenizeAndPrint(tokenizer, out, code.source_token);
                 try out.writeAll("
"); + + if (!do_code_tests) { + print("SKIP\n", .{}); + continue; + } + const name_plus_ext = try std.fmt.allocPrint(allocator, "{s}.zig", .{code.name}); const tmp_source_file_name = try fs.path.join( allocator, From 874c63f89cb6174d2dba32f4d08c140b5eed6744 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Sat, 6 Mar 2021 09:17:17 -0800 Subject: [PATCH 16/49] translate-c: translate align attribute for block scoped variables --- src/translate_c.zig | 41 ++++++++++++----------------------------- test/translate_c.zig | 6 ++++++ 2 files changed, 18 insertions(+), 29 deletions(-) diff --git a/src/translate_c.zig b/src/translate_c.zig index 2770ffb4cb..0fec83e3bb 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -671,15 +671,6 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co break :blk null; }; - const alignment = blk: { - const alignment = var_decl.getAlignedAttribute(c.clang_context); - if (alignment != 0) { - // Clang reports the alignment in bits - break :blk alignment / 8; - } - break :blk null; - }; - const node = try Tag.var_decl.create(c.arena, .{ .is_pub = is_pub, .is_const = is_const, @@ -687,7 +678,7 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co .is_export = is_export, .is_threadlocal = is_threadlocal, .linksection_string = linksection_string, - .alignment = alignment, + .alignment = zigAlignment(var_decl.getAlignedAttribute(c.clang_context)), .name = checked_name, .type = type_node, .init = init_node, @@ -833,14 +824,7 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD else => |e| return e, }; - const alignment = blk_2: { - const alignment = field_decl.getAlignedAttribute(c.clang_context); - if (alignment != 0) { - // Clang reports the alignment in bits - break :blk_2 alignment / 8; - } - break :blk_2 null; - }; + const alignment = zigAlignment(field_decl.getAlignedAttribute(c.clang_context)); if (is_anon) { try c.decl_table.putNoClobber(c.gpa, @ptrToInt(field_decl.getCanonicalDecl()), field_name); @@ -1373,6 +1357,13 @@ fn transCStyleCastExprClass( return maybeSuppressResult(c, scope, result_used, cast_node); } +/// Clang reports the alignment in bits, we use bytes +/// Clang uses 0 for "no alignment specified", we use null +fn zigAlignment(bit_alignment: c_uint) ?c_uint { + if (bit_alignment == 0) return null; + return bit_alignment / 8; +} + fn transDeclStmtOne( c: *Context, scope: *Scope, @@ -1412,6 +1403,7 @@ fn transDeclStmtOne( if (!qualTypeIsBoolean(qual_type) and isBoolRes(init_node)) { init_node = try Tag.bool_to_int.create(c.arena, init_node); } + const node = try Tag.var_decl.create(c.arena, .{ .is_pub = false, .is_const = is_const, @@ -1419,7 +1411,7 @@ fn transDeclStmtOne( .is_export = false, .is_threadlocal = false, .linksection_string = null, - .alignment = null, + .alignment = zigAlignment(var_decl.getAlignedAttribute(c.clang_context)), .name = mangled_name, .type = type_node, .init = init_node, @@ -4111,16 +4103,7 @@ fn finishTransFnProto( break :blk null; }; - const alignment = blk: { - if (fn_decl) |decl| { - const alignment = decl.getAlignedAttribute(c.clang_context); - if (alignment != 0) { - // Clang reports the alignment in bits - break :blk alignment / 8; - } - } - break :blk null; - }; + const alignment = if (fn_decl) |decl| zigAlignment(decl.getAlignedAttribute(c.clang_context)) else null; const explicit_callconv = if ((is_export or is_extern) and cc == .C) null else cc; diff --git a/test/translate_c.zig b/test/translate_c.zig index 367f69745b..e74af41f80 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -643,9 +643,15 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\extern char my_array[16]; \\__attribute__ ((aligned(128))) \\void my_fn(void) { } + \\void other_fn(void) { + \\ char ARR[16] __attribute__ ((aligned (16))); + \\} , &[_][]const u8{ \\pub extern var my_array: [16]u8 align(128); \\pub export fn my_fn() align(128) void {} + \\pub export fn other_fn() void { + \\ var ARR: [16]u8 align(16) = undefined; + \\} }); cases.add("linksection() attribute", From 6d69a29d753de8680ff1220b8d9127e890e7d965 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Fri, 5 Mar 2021 09:54:59 -0800 Subject: [PATCH 17/49] translate-c: Support compound assignment of pointer and signed int This handles `ptr += idx` and `ptr -= idx` when `idx` is a signed integer expression. --- src/translate_c.zig | 34 +++++++++++++++++++++++++--------- test/run_translated_c.zig | 10 ++++++++++ 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/src/translate_c.zig b/src/translate_c.zig index 0fec83e3bb..896f89283d 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -1111,6 +1111,22 @@ fn transOffsetOfExpr( return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "TODO: implement complex OffsetOfExpr translation", .{}); } +/// Cast a signed integer node to a usize, for use in pointer arithmetic. Negative numbers +/// will become very large positive numbers but that is ok since we only use this in +/// pointer arithmetic expressions, where wraparound will ensure we get the correct value. +/// node -> @bitCast(usize, @intCast(isize, node)) +fn usizeCastForWrappingPtrArithmetic(gpa: *mem.Allocator, node: Node) TransError!Node { + const intcast_node = try Tag.int_cast.create(gpa, .{ + .lhs = try Tag.identifier.create(gpa, "isize"), + .rhs = node, + }); + + return Tag.bit_cast.create(gpa, .{ + .lhs = try Tag.identifier.create(gpa, "usize"), + .rhs = intcast_node, + }); +} + /// Translate an arithmetic expression with a pointer operand and a signed-integer operand. /// Zig requires a usize argument for pointer arithmetic, so we intCast to isize and then /// bitcast to usize; pointer wraparound make the math work. @@ -1133,15 +1149,7 @@ fn transCreatePointerArithmeticSignedOp( const lhs_node = try transExpr(c, scope, swizzled_lhs, .used); const rhs_node = try transExpr(c, scope, swizzled_rhs, .used); - const intcast_node = try Tag.int_cast.create(c.arena, .{ - .lhs = try Tag.identifier.create(c.arena, "isize"), - .rhs = rhs_node, - }); - - const bitcast_node = try Tag.bit_cast.create(c.arena, .{ - .lhs = try Tag.identifier.create(c.arena, "usize"), - .rhs = intcast_node, - }); + const bitcast_node = try usizeCastForWrappingPtrArithmetic(c.arena, rhs_node); const arith_args = .{ .lhs = lhs_node, .rhs = bitcast_node }; const arith_node = try if (is_add) Tag.add.create(c.arena, arith_args) else Tag.sub.create(c.arena, arith_args); @@ -3035,6 +3043,7 @@ fn transCreateCompoundAssign( const lhs_qt = getExprQualType(c, lhs); const rhs_qt = getExprQualType(c, rhs); const is_signed = cIsSignedInteger(lhs_qt); + const is_ptr_op_signed = qualTypeIsPtr(lhs_qt) and cIsSignedInteger(rhs_qt); const requires_int_cast = blk: { const are_integers = cIsInteger(lhs_qt) and cIsInteger(rhs_qt); const are_same_sign = cIsSignedInteger(lhs_qt) == cIsSignedInteger(rhs_qt); @@ -3061,6 +3070,10 @@ fn transCreateCompoundAssign( else try transExpr(c, scope, rhs, .used); + if (is_ptr_op_signed) { + rhs_node = try usizeCastForWrappingPtrArithmetic(c.arena, rhs_node); + } + if (is_shift or requires_int_cast) { // @intCast(rhs) const cast_to_type = if (is_shift) @@ -3113,6 +3126,9 @@ fn transCreateCompoundAssign( rhs_node = try Tag.int_cast.create(c.arena, .{ .lhs = cast_to_type, .rhs = rhs_node }); } + if (is_ptr_op_signed) { + rhs_node = try usizeCastForWrappingPtrArithmetic(c.arena, rhs_node); + } const assign = try transCreateNodeInfixOp(c, &block_scope.base, op, ref_node, rhs_node, .used); try block_scope.statements.append(assign); diff --git a/test/run_translated_c.zig b/test/run_translated_c.zig index 977400be82..abefe46459 100644 --- a/test/run_translated_c.zig +++ b/test/run_translated_c.zig @@ -1154,6 +1154,16 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { \\ y = x - idx; \\ if (y != x + 1 || y != &array[6]) abort(); \\ + \\ idx = 1; + \\ x += idx; + \\ if (x != &array[6]) abort(); + \\ x -= idx; + \\ if (x != &array[5]) abort(); + \\ y = (x += idx); + \\ if (y != x || y != &array[6]) abort(); + \\ y = (x -= idx); + \\ if (y != x || y != &array[5]) abort(); + \\ \\ return 0; \\} , ""); From e47b754b28d658f05b72811207b9f953c1ab83e9 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Fri, 5 Mar 2021 22:08:34 +0100 Subject: [PATCH 18/49] std: Prevent null pointer deref in mem.len{,Z} Closes #8140 --- lib/std/mem.zig | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/std/mem.zig b/lib/std/mem.zig index 581fb16e6c..2bd5fdac7b 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -647,7 +647,10 @@ pub fn len(value: anytype) usize { indexOfSentinel(info.child, sentinel, value) else @compileError("length of pointer with no sentinel"), - .C => indexOfSentinel(info.child, 0, value), + .C => { + assert(value != null); + return indexOfSentinel(info.child, 0, value); + }, .Slice => value.len, }, .Struct => |info| if (info.is_tuple) { @@ -708,7 +711,10 @@ pub fn lenZ(ptr: anytype) usize { indexOfSentinel(info.child, sentinel, ptr) else @compileError("length of pointer with no sentinel"), - .C => indexOfSentinel(info.child, 0, ptr), + .C => { + assert(ptr != null); + return indexOfSentinel(info.child, 0, ptr); + }, .Slice => if (info.sentinel) |sentinel| indexOfSentinel(info.child, sentinel, ptr.ptr) else From 72664df4911f5d5bddead48acde6275f1d5f2a5e Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sat, 6 Mar 2021 09:57:21 +0100 Subject: [PATCH 19/49] std: Deprecate the B and Bi format specifiers Following #8007 and #8137 let's get rid of the last weird format. --- ci/srht/update-download-page.zig | 3 +- lib/std/fmt.zig | 128 +++++++++++++++++-------------- src/link/Coff.zig | 6 +- tools/process_headers.zig | 30 +++++--- 4 files changed, 97 insertions(+), 70 deletions(-) diff --git a/ci/srht/update-download-page.zig b/ci/srht/update-download-page.zig index 0fb1f3913c..d9dadc9ff2 100644 --- a/ci/srht/update-download-page.zig +++ b/ci/srht/update-download-page.zig @@ -73,7 +73,8 @@ fn render( if (vars.get(var_name)) |value| { const trimmed = mem.trim(u8, value, " \r\n"); if (fmt == .html and mem.endsWith(u8, var_name, "BYTESIZE")) { - try writer.print("{Bi:.1}", .{try std.fmt.parseInt(u64, trimmed, 10)}); + const size = try std.fmt.parseInt(u64, trimmed, 10); + try writer.print("{:.1}", .{std.fmt.fmtIntSizeDec(size)}); } else { try writer.writeAll(trimmed); } diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 1f924bf00c..2d176e761a 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -35,11 +35,11 @@ pub const FormatOptions = struct { /// /// The format string must be comptime known and may contain placeholders following /// this format: -/// `{[position][specifier]:[fill][alignment][width].[precision]}` +/// `{[argument][specifier]:[fill][alignment][width].[precision]}` /// /// Each word between `[` and `]` is a parameter you have to replace with something: /// -/// - *position* is the index of the argument that should be inserted +/// - *argument* is either the index or the name of the argument that should be inserted /// - *specifier* is a type-dependent formatting option that determines how a type should formatted (see below) /// - *fill* is a single character which is used to pad the formatted text /// - *alignment* is one of the three characters `<`, `^` or `>`. they define if the text is *left*, *center*, or *right* aligned @@ -52,16 +52,10 @@ pub const FormatOptions = struct { /// the digits after `:` is interpreted as *width*, not *fill*. /// /// The *specifier* has several options for types: -/// - `x` and `X`: -/// - format the non-numeric value as a string of bytes in hexadecimal notation ("binary dump") in either lower case or upper case -/// - output numeric value in hexadecimal notation +/// - `x` and `X`: output numeric value in hexadecimal notation /// - `s`: /// - for pointer-to-many and C pointers of u8, print as a C-string using zero-termination /// - for slices of u8, print the entire slice as a string without zero-termination -/// - `z`: escape the string with @"" syntax if it is not a valid Zig identifier. -/// - `Z`: print the string escaping non-printable characters using Zig escape sequences. -/// - `B` and `Bi`: output a memory size in either metric (1000) or power-of-two (1024) based notation. works for both float and integer values. -/// - `e` and `E`: if printing a string, escape non-printable characters /// - `e`: output floating point value in scientific notation /// - `d`: output numeric value in decimal notation /// - `b`: output integer value in binary notation @@ -620,9 +614,9 @@ fn formatValue( writer: anytype, ) !void { if (comptime std.mem.eql(u8, fmt, "B")) { - return formatBytes(value, options, 1000, writer); + @compileError("specifier 'B' has been deprecated, wrap your argument in std.fmt.fmtIntSizeDec instead"); } else if (comptime std.mem.eql(u8, fmt, "Bi")) { - return formatBytes(value, options, 1024, writer); + @compileError("specifier 'Bi' has been deprecated, wrap your argument in std.fmt.fmtIntSizeBin instead"); } const T = @TypeOf(value); @@ -790,6 +784,67 @@ pub fn fmtSliceEscapeUpper(bytes: []const u8) std.fmt.Formatter(formatSliceEscap return .{ .data = bytes }; } +fn formatSizeImpl(comptime radix: comptime_int) type { + return struct { + fn f( + value: u64, + comptime fmt: []const u8, + options: FormatOptions, + writer: anytype, + ) !void { + if (value == 0) { + return writer.writeAll("0B"); + } + + const mags_si = " kMGTPEZY"; + const mags_iec = " KMGTPEZY"; + + const log2 = math.log2(value); + const magnitude = switch (radix) { + 1000 => math.min(log2 / comptime math.log2(1000), mags_si.len - 1), + 1024 => math.min(log2 / 10, mags_iec.len - 1), + else => unreachable, + }; + const new_value = lossyCast(f64, value) / math.pow(f64, lossyCast(f64, radix), lossyCast(f64, magnitude)); + const suffix = switch (radix) { + 1000 => mags_si[magnitude], + 1024 => mags_iec[magnitude], + else => unreachable, + }; + + try formatFloatDecimal(new_value, options, writer); + + if (suffix == ' ') { + return writer.writeAll("B"); + } + + const buf = switch (radix) { + 1000 => &[_]u8{ suffix, 'B' }, + 1024 => &[_]u8{ suffix, 'i', 'B' }, + else => unreachable, + }; + return writer.writeAll(buf); + } + }; +} + +const formatSizeDec = formatSizeImpl(1000).f; +const formatSizeBin = formatSizeImpl(1024).f; + +/// Return a Formatter for a u64 value representing a file size. +/// This formatter represents the number as multiple of 1000 and uses the SI +/// measurement units (kB, MB, GB, ...). +pub fn fmtIntSizeDec(value: u64) std.fmt.Formatter(formatSizeDec) { + return .{ .data = value }; +} + +/// Return a Formatter for a u64 value representing a file size. +/// This formatter represents the number as multiple of 1024 and uses the IEC +/// measurement units (KiB, MiB, GiB, ...). +pub fn fmtIntSizeBin(value: u64) std.fmt.Formatter(formatSizeBin) { + return .{ .data = value }; +} + pub fn formatText( bytes: []const u8, comptime fmt: []const u8, @@ -1111,47 +1166,6 @@ pub fn formatFloatDecimal( } } -pub fn formatBytes( - value: anytype, - options: FormatOptions, - comptime radix: usize, - writer: anytype, -) !void { - if (value == 0) { - return writer.writeAll("0B"); - } - - const is_float = comptime std.meta.trait.is(.Float)(@TypeOf(value)); - const mags_si = " kMGTPEZY"; - const mags_iec = " KMGTPEZY"; - - const log2 = if (is_float) @floatToInt(usize, math.log2(value)) else math.log2(value); - const magnitude = switch (radix) { - 1000 => math.min(log2 / comptime math.log2(1000), mags_si.len - 1), - 1024 => math.min(log2 / 10, mags_iec.len - 1), - else => unreachable, - }; - const new_value = lossyCast(f64, value) / math.pow(f64, lossyCast(f64, radix), lossyCast(f64, magnitude)); - const suffix = switch (radix) { - 1000 => mags_si[magnitude], - 1024 => mags_iec[magnitude], - else => unreachable, - }; - - try formatFloatDecimal(new_value, options, writer); - - if (suffix == ' ') { - return writer.writeAll("B"); - } - - const buf = switch (radix) { - 1000 => &[_]u8{ suffix, 'B' }, - 1024 => &[_]u8{ suffix, 'i', 'B' }, - else => unreachable, - }; - return writer.writeAll(buf); -} - pub fn formatInt( value: anytype, base: u8, @@ -1806,8 +1820,12 @@ test "cstr" { } test "filesize" { - try expectFmt("file size: 63MiB\n", "file size: {Bi}\n", .{@as(usize, 63 * 1024 * 1024)}); - try expectFmt("file size: 66.06MB\n", "file size: {B:.2}\n", .{@as(usize, 63 * 1024 * 1024)}); + try expectFmt("file size: 42B\n", "file size: {}\n", .{fmtIntSizeDec(42)}); + try expectFmt("file size: 42B\n", "file size: {}\n", .{fmtIntSizeBin(42)}); + try expectFmt("file size: 63MB\n", "file size: {}\n", .{fmtIntSizeDec(63 * 1000 * 1000)}); + try expectFmt("file size: 63MiB\n", "file size: {}\n", .{fmtIntSizeBin(63 * 1024 * 1024)}); + try expectFmt("file size: 66.06MB\n", "file size: {:.2}\n", .{fmtIntSizeDec(63 * 1024 * 1024)}); + try expectFmt("file size: 60.08MiB\n", "file size: {:.2}\n", .{fmtIntSizeBin(63 * 1000 * 1000)}); } test "struct" { @@ -2213,8 +2231,6 @@ test "vector" { try expectFmt("{ -2, -1, +0, +1 }", "{d:5}", .{vi64}); try expectFmt("{ 1000, 2000, 3000, 4000 }", "{}", .{vu64}); try expectFmt("{ 3e8, 7d0, bb8, fa0 }", "{x}", .{vu64}); - try expectFmt("{ 1kB, 2kB, 3kB, 4kB }", "{B}", .{vu64}); - try expectFmt("{ 1000B, 1.953125KiB, 2.9296875KiB, 3.90625KiB }", "{Bi}", .{vu64}); } test "enum-literal" { diff --git a/src/link/Coff.zig b/src/link/Coff.zig index a73b8aaf9c..8b2b13eb71 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -701,7 +701,11 @@ pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void { } } else { const vaddr = try self.allocateTextBlock(&decl.link.coff, code.len, required_alignment); - log.debug("allocated text block for {s} at 0x{x} (size: {Bi})\n", .{ mem.spanZ(decl.name), vaddr, code.len }); + log.debug("allocated text block for {s} at 0x{x} (size: {Bi})\n", .{ + mem.spanZ(decl.name), + vaddr, + std.fmt.fmtIntSizeDec(code.len), + }); errdefer self.freeTextBlock(&decl.link.coff); self.offset_table.items[decl.link.coff.offset_table_index] = vaddr; try self.writeOffsetTableEntry(decl.link.coff.offset_table_index); diff --git a/tools/process_headers.zig b/tools/process_headers.zig index c5279ba74f..9c0822a9f3 100644 --- a/tools/process_headers.zig +++ b/tools/process_headers.zig @@ -270,7 +270,7 @@ pub fn main() !void { if (std.mem.eql(u8, args[arg_i], "--help")) usageAndExit(args[0]); if (arg_i + 1 >= args.len) { - std.debug.warn("expected argument after '{}'\n", .{args[arg_i]}); + std.debug.warn("expected argument after '{s}'\n", .{args[arg_i]}); usageAndExit(args[0]); } @@ -283,7 +283,7 @@ pub fn main() !void { assert(opt_abi == null); opt_abi = args[arg_i + 1]; } else { - std.debug.warn("unrecognized argument: {}\n", .{args[arg_i]}); + std.debug.warn("unrecognized argument: {s}\n", .{args[arg_i]}); usageAndExit(args[0]); } @@ -297,10 +297,10 @@ pub fn main() !void { else if (std.mem.eql(u8, abi_name, "glibc")) LibCVendor.glibc else { - std.debug.warn("unrecognized C ABI: {}\n", .{abi_name}); + std.debug.warn("unrecognized C ABI: {s}\n", .{abi_name}); usageAndExit(args[0]); }; - const generic_name = try std.fmt.allocPrint(allocator, "generic-{}", .{abi_name}); + const generic_name = try std.fmt.allocPrint(allocator, "generic-{s}", .{abi_name}); // TODO compiler crashed when I wrote this the canonical way var libc_targets: []const LibCTarget = undefined; @@ -368,10 +368,10 @@ pub fn main() !void { if (gop.found_existing) { max_bytes_saved += raw_bytes.len; gop.entry.value.hit_count += 1; - std.debug.warn("duplicate: {} {} ({Bi:2})\n", .{ + std.debug.warn("duplicate: {s} {s} ({:2})\n", .{ libc_target.name, rel_path, - raw_bytes.len, + std.fmt.fmtIntSizeDec(raw_bytes.len), }); } else { gop.entry.value = Contents{ @@ -390,16 +390,19 @@ pub fn main() !void { }; try target_to_hash.putNoClobber(dest_target, hash); }, - else => std.debug.warn("warning: weird file: {}\n", .{full_path}), + else => std.debug.warn("warning: weird file: {s}\n", .{full_path}), } } } break; } else { - std.debug.warn("warning: libc target not found: {}\n", .{libc_target.name}); + std.debug.warn("warning: libc target not found: {s}\n", .{libc_target.name}); } } - std.debug.warn("summary: {Bi:2} could be reduced to {Bi:2}\n", .{ total_bytes, total_bytes - max_bytes_saved }); + std.debug.warn("summary: {:2} could be reduced to {:2}\n", .{ + std.fmt.fmtIntSizeDec(total_bytes), + std.fmt.fmtIntSizeDec(total_bytes - max_bytes_saved), + }); try std.fs.cwd().makePath(out_dir); var missed_opportunity_bytes: usize = 0; @@ -428,7 +431,10 @@ pub fn main() !void { if (contender.hit_count > 1) { const this_missed_bytes = contender.hit_count * contender.bytes.len; missed_opportunity_bytes += this_missed_bytes; - std.debug.warn("Missed opportunity ({Bi:2}): {}\n", .{ this_missed_bytes, path_kv.key }); + std.debug.warn("Missed opportunity ({:2}): {s}\n", .{ + std.fmt.fmtIntSizeDec(this_missed_bytes), + path_kv.key, + }); } else break; } } @@ -442,7 +448,7 @@ pub fn main() !void { .specific => |a| @tagName(a), else => @tagName(dest_target.arch), }; - const out_subpath = try std.fmt.allocPrint(allocator, "{}-{}-{}", .{ + const out_subpath = try std.fmt.allocPrint(allocator, "{s}-{s}-{s}", .{ arch_name, @tagName(dest_target.os), @tagName(dest_target.abi), @@ -455,7 +461,7 @@ pub fn main() !void { } fn usageAndExit(arg0: []const u8) noreturn { - std.debug.warn("Usage: {} [--search-path ] --out --abi \n", .{arg0}); + std.debug.warn("Usage: {s} [--search-path ] --out --abi \n", .{arg0}); std.debug.warn("--search-path can be used any number of times.\n", .{}); std.debug.warn(" subdirectories of search paths look like, e.g. x86_64-linux-gnu\n", .{}); std.debug.warn("--out is a dir that will be created, and populated with the results\n", .{}); From 0447a2c041a4be843251396e668e074186aa49a2 Mon Sep 17 00:00:00 2001 From: Jonathan Knezek Date: Sun, 7 Mar 2021 07:00:15 -0600 Subject: [PATCH 20/49] Implement fmtDuration using Formatter (#8137) * Implement fmtDuration using Formatter Deprecate Duration, duration * fmtDuration: Fixed ns remainder * fmtDuration: Fixed visibility; removed [Dd]uration --- lib/std/fmt.zig | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 2d176e761a..90c0d98539 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -1224,11 +1224,7 @@ pub fn formatIntBuf(out_buf: []u8, value: anytype, base: u8, uppercase: bool, op return fbs.pos; } -/// Formats a number of nanoseconds according to its magnitude: -/// -/// - #ns -/// - [#y][#w][#d][#h][#m]#[.###][u|m]s -pub fn formatDuration(ns: u64, writer: anytype) !void { +fn formatDuration(ns: u64, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { var ns_remaining = ns; inline for (.{ .{ .ns = 365 * std.time.ns_per_day, .sep = 'y' }, @@ -1270,12 +1266,18 @@ pub fn formatDuration(ns: u64, writer: anytype) !void { } } - try formatInt(ns, 10, false, .{}, writer); + try formatInt(ns_remaining, 10, false, .{}, writer); try writer.writeAll("ns"); return; } -test "formatDuration" { +/// Return a Formatter for number of nanoseconds according to its magnitude: +/// [#y][#w][#d][#h][#m]#[.###][n|u|m]s +pub fn fmtDuration(ns: u64) Formatter(formatDuration) { + return .{ .data = ns }; +} + +test "fmtDuration" { var buf: [24]u8 = undefined; inline for (.{ .{ .s = "0ns", .d = 0 }, @@ -1301,26 +1303,13 @@ test "formatDuration" { .{ .s = "1y1h999.999us", .d = 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms - 1 }, .{ .s = "1y1h1ms", .d = 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms }, .{ .s = "1y1h1ms", .d = 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms + 1 }, + .{ .s = "1y1m999ns", .d = 365 * std.time.ns_per_day + std.time.ns_per_min + 999 }, }) |tc| { - const slice = try bufPrint(&buf, "{}", .{duration(tc.d)}); + const slice = try bufPrint(&buf, "{}", .{fmtDuration(tc.d)}); std.testing.expectEqualStrings(tc.s, slice); } } -/// Wraps a `u64` to format with `formatDuration`. -const Duration = struct { - ns: u64, - - pub fn format(self: Duration, comptime fmt: []const u8, options: FormatOptions, writer: anytype) !void { - return formatDuration(self.ns, writer); - } -}; - -/// Formats a number of nanoseconds according to its magnitude. See `formatDuration`. -pub fn duration(ns: u64) Duration { - return Duration{ .ns = ns }; -} - pub const ParseIntError = error{ /// The result cannot fit in the type specified Overflow, From 4fc2e92876d8aafd087a5f0bdb6ea7a54f195704 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sun, 7 Mar 2021 15:23:20 +0100 Subject: [PATCH 21/49] std: Better handling of line-wrapping in Progress In order to update the printed progress string the code tried to move the cursor N cells to the left, where N is the number of written bytes, and then clear the remaining part of the line. This strategy has two main issues: - Is only valid if the number of characters is equal to the number of written bytes, - Is only valid if the line doesn't get too long. The second point is the main motivation for this change, when the line becomes too long the terminal wraps it to a new physical line. This means that moving the cursor to the left won't be enough anymore as once the left border is reached it cannot move anymore. The wrapped line is still stored by the terminal as a single line, despite now taking more than a single one when displayed. If you try to resize the terminal you'll notice how the contents are reflowed and are essentially illegible. Querying the cursor position on non-Windows systems (plot twist, Microsoft suggests using VT escape sequences on newer systems) is extremely cumbersome so let's do something different. Before printing anything let's save the cursor position and clear the screen below the cursor, this way we ensure there's absolutely no trace of stale data on screen, and after the message is printed we simply restore it. --- lib/std/Progress.zig | 113 +++++++++++++++++++++---------------------- 1 file changed, 55 insertions(+), 58 deletions(-) diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig index 4afd191b93..93e816feec 100644 --- a/lib/std/Progress.zig +++ b/lib/std/Progress.zig @@ -59,10 +59,6 @@ done: bool = true, /// while it was still being accessed by the `refresh` function. update_lock: std.Thread.Mutex = .{}, -/// Keeps track of how many columns in the terminal have been output, so that -/// we can move the cursor back later. -columns_written: usize = undefined, - /// Represents one unit of progress. Each node can have children nodes, or /// one can use integers with `update`. pub const Node = struct { @@ -159,7 +155,6 @@ pub fn start(self: *Progress, name: []const u8, estimated_total_items: usize) !* .unprotected_estimated_total_items = estimated_total_items, .unprotected_completed_items = 0, }; - self.columns_written = 0; self.prev_refresh_timestamp = 0; self.timer = try std.time.Timer.start(); self.done = false; @@ -187,64 +182,56 @@ pub fn refresh(self: *Progress) void { return self.refreshWithHeldLock(); } +// ED -- Clear screen +const ED = "\x1b[J"; +// DECSC -- Save cursor position +const DECSC = "\x1b[s"; +// DECRC -- Restore cursor position +const DECRC = "\x1b[u"; + fn refreshWithHeldLock(self: *Progress) void { const is_dumb = !self.supports_ansi_escape_codes and !(std.builtin.os.tag == .windows); if (is_dumb and self.dont_print_on_dumb) return; const file = self.terminal orelse return; - const prev_columns_written = self.columns_written; var end: usize = 0; - if (self.columns_written > 0) { - // restore the cursor position by moving the cursor - // `columns_written` cells to the left, then clear the rest of the - // line - if (self.supports_ansi_escape_codes) { - end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[{d}D", .{self.columns_written}) catch unreachable).len; - end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[0K", .{}) catch unreachable).len; - } else if (std.builtin.os.tag == .windows) winapi: { - var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined; - if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE) - unreachable; + // Save the cursor position and clear the part of the screen below. + // Clearing only the line is not enough as the terminal may wrap the line + // when it becomes too long. + var saved_cursor_pos: windows.COORD = undefined; + if (self.supports_ansi_escape_codes) { + const seq_before = DECSC ++ ED; + std.mem.copy(u8, self.output_buffer[end..], seq_before); + end += seq_before.len; + } else if (std.builtin.os.tag == .windows) winapi: { + var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined; + if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE) + unreachable; - var cursor_pos = windows.COORD{ - .X = info.dwCursorPosition.X - @intCast(windows.SHORT, self.columns_written), - .Y = info.dwCursorPosition.Y, - }; + saved_cursor_pos = info.dwCursorPosition; + const fill_chars = @intCast(windows.DWORD, info.dwSize.X * (info.dwSize.Y - info.dwCursorPosition.Y) - info.dwCursorPosition.X); - if (cursor_pos.X < 0) - cursor_pos.X = 0; - - const fill_chars = @intCast(windows.DWORD, info.dwSize.X - cursor_pos.X); - - var written: windows.DWORD = undefined; - if (windows.kernel32.FillConsoleOutputAttribute( - file.handle, - info.wAttributes, - fill_chars, - cursor_pos, - &written, - ) != windows.TRUE) { - // Stop trying to write to this file. - self.terminal = null; - break :winapi; - } - if (windows.kernel32.FillConsoleOutputCharacterA( - file.handle, - ' ', - fill_chars, - cursor_pos, - &written, - ) != windows.TRUE) unreachable; - - if (windows.kernel32.SetConsoleCursorPosition(file.handle, cursor_pos) != windows.TRUE) - unreachable; - } else { - // we are in a "dumb" terminal like in acme or writing to a file - self.output_buffer[end] = '\n'; - end += 1; + var written: windows.DWORD = undefined; + if (windows.kernel32.FillConsoleOutputAttribute( + file.handle, + info.wAttributes, + fill_chars, + saved_cursor_pos, + &written, + ) != windows.TRUE) { + // Stop trying to write to this file. + self.terminal = null; + break :winapi; + } + if (windows.kernel32.FillConsoleOutputCharacterA( + file.handle, + ' ', + fill_chars, + saved_cursor_pos, + &written, + ) != windows.TRUE) { + unreachable; } - - self.columns_written = 0; } if (!self.done) { @@ -279,6 +266,20 @@ fn refreshWithHeldLock(self: *Progress) void { } } + // We're done printing the updated message, restore the cursor position. + if (self.supports_ansi_escape_codes) { + const seq_after = DECRC; + std.mem.copy(u8, self.output_buffer[end..], seq_after); + end += seq_after.len; + } else if (std.builtin.os.tag == .windows) { + if (windows.kernel32.SetConsoleCursorPosition(file.handle, saved_cursor_pos) != windows.TRUE) { + unreachable; + } + } else { + self.output_buffer[end] = '\n'; + end += 1; + } + _ = file.write(self.output_buffer[0..end]) catch |e| { // Stop trying to write to this file once it errors. self.terminal = null; @@ -293,17 +294,14 @@ pub fn log(self: *Progress, comptime format: []const u8, args: anytype) void { self.terminal = null; return; }; - self.columns_written = 0; } fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: anytype) void { if (std.fmt.bufPrint(self.output_buffer[end.*..], format, args)) |written| { const amt = written.len; end.* += amt; - self.columns_written += amt; } else |err| switch (err) { error.NoSpaceLeft => { - self.columns_written += self.output_buffer.len - end.*; end.* = self.output_buffer.len; }, } @@ -311,7 +309,6 @@ fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: any const max_end = self.output_buffer.len - bytes_needed_for_esc_codes_at_end; if (end.* > max_end) { const suffix = "... "; - self.columns_written = self.columns_written - (end.* - max_end) + suffix.len; std.mem.copy(u8, self.output_buffer[max_end..], suffix); end.* = max_end + suffix.len; } From 5d215cc76bcac12afbda7b10ec04bbf6c5b47910 Mon Sep 17 00:00:00 2001 From: jacob gw Date: Thu, 4 Mar 2021 16:13:49 -0500 Subject: [PATCH 22/49] stage2 Elf linker: fill in bswapAllFields I moved it to std.elf since it could be used there and added test --- lib/std/elf.zig | 43 +++++++++++++++++++++++-------------------- src/link/Elf.zig | 28 ++++++++++++---------------- 2 files changed, 35 insertions(+), 36 deletions(-) diff --git a/lib/std/elf.zig b/lib/std/elf.zig index e644c6631a..26674288a4 100644 --- a/lib/std/elf.zig +++ b/lib/std/elf.zig @@ -422,16 +422,8 @@ pub fn ProgramHeaderIterator(ParseSource: anytype) type { if (self.elf_header.endian == std.builtin.endian) return phdr; // Convert fields to native endianness. - return Elf64_Phdr{ - .p_type = @byteSwap(@TypeOf(phdr.p_type), phdr.p_type), - .p_offset = @byteSwap(@TypeOf(phdr.p_offset), phdr.p_offset), - .p_vaddr = @byteSwap(@TypeOf(phdr.p_vaddr), phdr.p_vaddr), - .p_paddr = @byteSwap(@TypeOf(phdr.p_paddr), phdr.p_paddr), - .p_filesz = @byteSwap(@TypeOf(phdr.p_filesz), phdr.p_filesz), - .p_memsz = @byteSwap(@TypeOf(phdr.p_memsz), phdr.p_memsz), - .p_flags = @byteSwap(@TypeOf(phdr.p_flags), phdr.p_flags), - .p_align = @byteSwap(@TypeOf(phdr.p_align), phdr.p_align), - }; + bswapAllFields(Elf64_Phdr, &phdr); + return phdr; } var phdr: Elf32_Phdr = undefined; @@ -442,16 +434,7 @@ pub fn ProgramHeaderIterator(ParseSource: anytype) type { // ELF endianness does NOT match native endianness. if (self.elf_header.endian != std.builtin.endian) { // Convert fields to native endianness. - phdr = .{ - .p_type = @byteSwap(@TypeOf(phdr.p_type), phdr.p_type), - .p_offset = @byteSwap(@TypeOf(phdr.p_offset), phdr.p_offset), - .p_vaddr = @byteSwap(@TypeOf(phdr.p_vaddr), phdr.p_vaddr), - .p_paddr = @byteSwap(@TypeOf(phdr.p_paddr), phdr.p_paddr), - .p_filesz = @byteSwap(@TypeOf(phdr.p_filesz), phdr.p_filesz), - .p_memsz = @byteSwap(@TypeOf(phdr.p_memsz), phdr.p_memsz), - .p_flags = @byteSwap(@TypeOf(phdr.p_flags), phdr.p_flags), - .p_align = @byteSwap(@TypeOf(phdr.p_align), phdr.p_align), - }; + bswapAllFields(Elf32_Phdr, &phdr); } // Convert 32-bit header to 64-bit. @@ -562,6 +545,26 @@ pub fn int32(need_bswap: bool, int_32: anytype, comptime Int64: anytype) Int64 { } } +pub fn bswapAllFields(comptime S: type, ptr: *S) void { + if (@typeInfo(S) != .Struct) @compileError("bswapAllFields expects a struct as the first argument"); + inline for (std.meta.fields(S)) |f| { + @field(ptr, f.name) = @byteSwap(f.field_type, @field(ptr, f.name)); + } +} +test "bswapAllFields" { + var s: Elf32_Chdr = .{ + .ch_type = 0x12341234, + .ch_size = 0x56785678, + .ch_addralign = 0x12124242, + }; + bswapAllFields(Elf32_Chdr, &s); + std.testing.expectEqual(Elf32_Chdr{ + .ch_type = 0x34123412, + .ch_size = 0x78567856, + .ch_addralign = 0x42421212, + }, s); +} + pub const EI_NIDENT = 16; pub const EI_CLASS = 4; diff --git a/src/link/Elf.zig b/src/link/Elf.zig index bfef2cd12c..bbbc9e25ed 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1107,7 +1107,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { for (buf) |*phdr, i| { phdr.* = progHeaderTo32(self.program_headers.items[i]); if (foreign_endian) { - bswapAllFields(elf.Elf32_Phdr, phdr); + std.elf.bswapAllFields(elf.Elf32_Phdr, phdr); } } try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?); @@ -1119,7 +1119,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { for (buf) |*phdr, i| { phdr.* = self.program_headers.items[i]; if (foreign_endian) { - bswapAllFields(elf.Elf64_Phdr, phdr); + std.elf.bswapAllFields(elf.Elf64_Phdr, phdr); } } try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?); @@ -1196,7 +1196,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { shdr.* = sectHeaderTo32(self.sections.items[i]); log.debug("writing section {}\n", .{shdr.*}); if (foreign_endian) { - bswapAllFields(elf.Elf32_Shdr, shdr); + std.elf.bswapAllFields(elf.Elf32_Shdr, shdr); } } try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?); @@ -1209,7 +1209,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { shdr.* = self.sections.items[i]; log.debug("writing section {}\n", .{shdr.*}); if (foreign_endian) { - bswapAllFields(elf.Elf64_Shdr, shdr); + std.elf.bswapAllFields(elf.Elf64_Shdr, shdr); } } try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?); @@ -2787,14 +2787,14 @@ fn writeProgHeader(self: *Elf, index: usize) !void { .p32 => { var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.program_headers.items[index])}; if (foreign_endian) { - bswapAllFields(elf.Elf32_Phdr, &phdr[0]); + std.elf.bswapAllFields(elf.Elf32_Phdr, &phdr[0]); } return self.base.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset); }, .p64 => { var phdr = [1]elf.Elf64_Phdr{self.program_headers.items[index]}; if (foreign_endian) { - bswapAllFields(elf.Elf64_Phdr, &phdr[0]); + std.elf.bswapAllFields(elf.Elf64_Phdr, &phdr[0]); } return self.base.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset); }, @@ -2808,7 +2808,7 @@ fn writeSectHeader(self: *Elf, index: usize) !void { var shdr: [1]elf.Elf32_Shdr = undefined; shdr[0] = sectHeaderTo32(self.sections.items[index]); if (foreign_endian) { - bswapAllFields(elf.Elf32_Shdr, &shdr[0]); + std.elf.bswapAllFields(elf.Elf32_Shdr, &shdr[0]); } const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf32_Shdr); return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset); @@ -2816,7 +2816,7 @@ fn writeSectHeader(self: *Elf, index: usize) !void { .p64 => { var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]}; if (foreign_endian) { - bswapAllFields(elf.Elf64_Shdr, &shdr[0]); + std.elf.bswapAllFields(elf.Elf64_Shdr, &shdr[0]); } const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf64_Shdr); return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset); @@ -2914,7 +2914,7 @@ fn writeSymbol(self: *Elf, index: usize) !void { }, }; if (foreign_endian) { - bswapAllFields(elf.Elf32_Sym, &sym[0]); + std.elf.bswapAllFields(elf.Elf32_Sym, &sym[0]); } const off = syms_sect.sh_offset + @sizeOf(elf.Elf32_Sym) * index; try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off); @@ -2922,7 +2922,7 @@ fn writeSymbol(self: *Elf, index: usize) !void { .p64 => { var sym = [1]elf.Elf64_Sym{self.local_symbols.items[index]}; if (foreign_endian) { - bswapAllFields(elf.Elf64_Sym, &sym[0]); + std.elf.bswapAllFields(elf.Elf64_Sym, &sym[0]); } const off = syms_sect.sh_offset + @sizeOf(elf.Elf64_Sym) * index; try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off); @@ -2953,7 +2953,7 @@ fn writeAllGlobalSymbols(self: *Elf) !void { .st_shndx = self.global_symbols.items[i].st_shndx, }; if (foreign_endian) { - bswapAllFields(elf.Elf32_Sym, sym); + std.elf.bswapAllFields(elf.Elf32_Sym, sym); } } try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off); @@ -2972,7 +2972,7 @@ fn writeAllGlobalSymbols(self: *Elf) !void { .st_shndx = self.global_symbols.items[i].st_shndx, }; if (foreign_endian) { - bswapAllFields(elf.Elf64_Sym, sym); + std.elf.bswapAllFields(elf.Elf64_Sym, sym); } } try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off); @@ -3188,10 +3188,6 @@ fn pwriteDbgInfoNops( try self.base.file.?.pwritevAll(vecs[0..vec_index], offset - prev_padding_size); } -fn bswapAllFields(comptime S: type, ptr: *S) void { - @panic("TODO implement bswapAllFields"); -} - fn progHeaderTo32(phdr: elf.Elf64_Phdr) elf.Elf32_Phdr { return .{ .p_type = phdr.p_type, From ca20d0ea26bba603229377f4843eb8772aeb749d Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Thu, 28 Jan 2021 19:40:53 +0200 Subject: [PATCH 23/49] stage2 cbe: pointer like optionals --- src/codegen/c.zig | 81 ++++++++++++++++++++++++++++++++++++++++++++- test/stage2/cbe.zig | 15 +++++++++ 2 files changed, 95 insertions(+), 1 deletion(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 1a323441d9..ac3ddca990 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -140,7 +140,7 @@ pub const DeclGen = struct { return writer.print("{d}", .{val.toUnsignedInt()}); }, .Pointer => switch (val.tag()) { - .undef, .zero => try writer.writeAll("0"), + .null_value, .zero => try writer.writeAll("NULL"), .one => try writer.writeAll("1"), .decl_ref => { const decl = val.castTag(.decl_ref).?.data; @@ -201,6 +201,20 @@ pub const DeclGen = struct { } }, .Bool => return writer.print("{}", .{val.toBool()}), + .Optional => { + var opt_buf: Type.Payload.ElemType = undefined; + const child_type = t.optionalChild(&opt_buf); + if (t.isPtrLikeOptional()) { + return dg.renderValue(writer, child_type, val); + } + if (val.tag() == .null_value) { + try writer.writeAll("{ .is_null = true }"); + } else { + try writer.writeAll("{ .is_null = false, .payload = "); + try dg.renderValue(writer, child_type, val); + try writer.writeAll(" }"); + } + }, else => |e| return dg.fail(dg.decl.src(), "TODO: C backend: implement value {s}", .{ @tagName(e), }), @@ -299,6 +313,14 @@ pub const DeclGen = struct { try dg.renderType(w, t.elemType()); try w.writeAll(" *"); }, + .Optional => { + var opt_buf: Type.Payload.ElemType = undefined; + const child_type = t.optionalChild(&opt_buf); + if (t.isPtrLikeOptional()) { + return dg.renderType(w, child_type); + } + return dg.fail(dg.decl.src(), "TODO: C backend: more optional types", .{}); + }, .Null, .Undefined => unreachable, // must be const or comptime else => |e| return dg.fail(dg.decl.src(), "TODO: C backend: implement type {s}", .{ @tagName(e), @@ -429,6 +451,13 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi .bit_or => try genBinOp(o, inst.castTag(.bit_or).?, " | "), .xor => try genBinOp(o, inst.castTag(.xor).?, " ^ "), .not => try genUnOp(o, inst.castTag(.not).?, "!"), + .is_null => try genIsNull(o, inst.castTag(.is_null).?), + .is_non_null => try genIsNull(o, inst.castTag(.is_non_null).?), + .is_null_ptr => try genIsNull(o, inst.castTag(.is_null_ptr).?), + .is_non_null_ptr => try genIsNull(o, inst.castTag(.is_non_null_ptr).?), + .wrap_optional => try genWrapOptional(o, inst.castTag(.wrap_optional).?), + .optional_payload => try genOptionalPayload(o, inst.castTag(.optional_payload).?), + .optional_payload_ptr => try genOptionalPayload(o, inst.castTag(.optional_payload).?), else => |e| return o.dg.fail(o.dg.decl.src(), "TODO: C backend: implement codegen for {}", .{e}), }; switch (result_value) { @@ -802,6 +831,56 @@ fn genAsm(o: *Object, as: *Inst.Assembly) !CValue { return o.dg.fail(o.dg.decl.src(), "TODO: C backend: inline asm expression result used", .{}); } +fn genIsNull(o: *Object, inst: *Inst.UnOp) !CValue { + const writer = o.writer(); + const invert_logic = inst.base.tag == .is_non_null or inst.base.tag == .is_non_null_ptr; + const maybe_deref = if (inst.base.tag == .is_null_ptr or inst.base.tag == .is_non_null_ptr) "[0]" else ""; + const operand = try o.resolveInst(inst.operand); + + const local = try o.allocLocal(Type.initTag(.bool), .Const); + try writer.writeAll(" = ("); + try o.writeCValue(writer, operand); + + if (inst.operand.ty.isPtrLikeOptional()) { + // operand is a regular pointer, test `operand !=/== NULL` + const operator = if (invert_logic) "!=" else "=="; + try writer.print("){s} {s} NULL;\n", .{ maybe_deref, operator }); + } else { + const operator = if (invert_logic) "!=" else "=="; + try writer.print("){s}.is_null {s} true;\n", .{ maybe_deref, operator }); + } + return local; +} + +fn genOptionalPayload(o: *Object, inst: *Inst.UnOp) !CValue { + const writer = o.writer(); + const operand = try o.resolveInst(inst.operand); + + if (opt_ty.isPtrLikeOptional()) { + // the operand is just a regular pointer, no need to do anything special. + return operand; + } + + const opt_ty = if (inst.operand.ty.zigTypeTag() == .Pointer) + inst.operand.ty.elemType() + else + inst.operand.ty; + + return o.dg.fail(o.dg.decl.src(), "TODO: C backend: genOptionalPayload non ptr-like optionals", .{}); +} + +fn genWrapOptional(o: *Object, inst: *Inst.UnOp) !CValue { + const writer = o.writer(); + const operand = try o.resolveInst(inst.operand); + + if (inst.base.ty.isPtrLikeOptional()) { + // the operand is just a regular pointer, no need to do anything special. + return operand; + } + + return o.dg.fail(o.dg.decl.src(), "TODO: C backend: genWrapOptional non ptr-like optionals", .{}); +} + fn IndentWriter(comptime UnderlyingWriter: type) type { return struct { const Self = @This(); diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig index 873f0e1198..b22ef44148 100644 --- a/test/stage2/cbe.zig +++ b/test/stage2/cbe.zig @@ -244,6 +244,21 @@ pub fn addCases(ctx: *TestContext) !void { \\} , ""); } + { + var case = ctx.exeFromCompiledC("optionals", .{}); + + // Simple while loop + case.addCompareOutput( + \\export fn main() c_int { + \\ var count: c_int = 0; + \\ var opt_ptr: ?*c_int = &count; + \\ while (opt_ptr) |_| : (count += 1) { + \\ if (count == 4) opt_ptr = null; + \\ } + \\ return count - 5; + \\} + , ""); + } ctx.c("empty start function", linux_x64, \\export fn _start() noreturn { \\ unreachable; From c22f010fdd2c5ead80cbdc902738a9d023322436 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Thu, 28 Jan 2021 20:46:15 +0200 Subject: [PATCH 24/49] stage2 cbe: regular optional types --- src/codegen/c.zig | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index ac3ddca990..642166092e 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -207,10 +207,12 @@ pub const DeclGen = struct { if (t.isPtrLikeOptional()) { return dg.renderValue(writer, child_type, val); } + try writer.writeByte('('); + try dg.renderType(writer, t); if (val.tag() == .null_value) { - try writer.writeAll("{ .is_null = true }"); + try writer.writeAll("){ .is_null = true }"); } else { - try writer.writeAll("{ .is_null = false, .payload = "); + try writer.writeAll("){ .is_null = false, .payload = "); try dg.renderValue(writer, child_type, val); try writer.writeAll(" }"); } @@ -319,7 +321,11 @@ pub const DeclGen = struct { if (t.isPtrLikeOptional()) { return dg.renderType(w, child_type); } - return dg.fail(dg.decl.src(), "TODO: C backend: more optional types", .{}); + + // TODO this needs to be typedeffed since different structs are different types. + try w.writeAll("struct { "); + try dg.renderType(w, child_type); + try w.writeAll(" payload; bool is_null; }"); }, .Null, .Undefined => unreachable, // must be const or comptime else => |e| return dg.fail(dg.decl.src(), "TODO: C backend: implement type {s}", .{ @@ -834,6 +840,7 @@ fn genAsm(o: *Object, as: *Inst.Assembly) !CValue { fn genIsNull(o: *Object, inst: *Inst.UnOp) !CValue { const writer = o.writer(); const invert_logic = inst.base.tag == .is_non_null or inst.base.tag == .is_non_null_ptr; + const operator = if (invert_logic) "!=" else "=="; const maybe_deref = if (inst.base.tag == .is_null_ptr or inst.base.tag == .is_non_null_ptr) "[0]" else ""; const operand = try o.resolveInst(inst.operand); @@ -843,10 +850,8 @@ fn genIsNull(o: *Object, inst: *Inst.UnOp) !CValue { if (inst.operand.ty.isPtrLikeOptional()) { // operand is a regular pointer, test `operand !=/== NULL` - const operator = if (invert_logic) "!=" else "=="; try writer.print("){s} {s} NULL;\n", .{ maybe_deref, operator }); } else { - const operator = if (invert_logic) "!=" else "=="; try writer.print("){s}.is_null {s} true;\n", .{ maybe_deref, operator }); } return local; @@ -856,17 +861,26 @@ fn genOptionalPayload(o: *Object, inst: *Inst.UnOp) !CValue { const writer = o.writer(); const operand = try o.resolveInst(inst.operand); - if (opt_ty.isPtrLikeOptional()) { - // the operand is just a regular pointer, no need to do anything special. - return operand; - } - const opt_ty = if (inst.operand.ty.zigTypeTag() == .Pointer) inst.operand.ty.elemType() else inst.operand.ty; - return o.dg.fail(o.dg.decl.src(), "TODO: C backend: genOptionalPayload non ptr-like optionals", .{}); + if (opt_ty.isPtrLikeOptional()) { + // the operand is just a regular pointer, no need to do anything special. + // *?*T -> **T and ?*T -> *T are **T -> **T and *T -> *T in C + return operand; + } + + const maybe_deref = if (inst.operand.ty.zigTypeTag() == .Pointer) "->" else "."; + const maybe_addrof = if (inst.base.ty.zigTypeTag() == .Pointer) "&" else ""; + + const local = try o.allocLocal(inst.base.ty, .Const); + try writer.print(" = {s}(", .{maybe_addrof}); + try o.writeCValue(writer, operand); + + try writer.print("){s}payload;\n", .{maybe_deref}); + return local; } fn genWrapOptional(o: *Object, inst: *Inst.UnOp) !CValue { @@ -878,7 +892,12 @@ fn genWrapOptional(o: *Object, inst: *Inst.UnOp) !CValue { return operand; } - return o.dg.fail(o.dg.decl.src(), "TODO: C backend: genWrapOptional non ptr-like optionals", .{}); + // .wrap_optional is used to convert non-optionals into optionals so it can never be null. + const local = try o.allocLocal(inst.base.ty, .Const); + try writer.writeAll(" = { .is_null = false, .payload ="); + try o.writeCValue(writer, operand); + try writer.writeAll("};\n"); + return local; } fn IndentWriter(comptime UnderlyingWriter: type) type { From cfc19eace71c92ecd7e138db6d961271a1b6c126 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 29 Jan 2021 18:20:35 +0200 Subject: [PATCH 25/49] stage2 cbe: errors --- src/codegen/c.zig | 29 +++++++++++++++++++++++++++++ src/link/C.zig | 13 +++++++++++++ src/type.zig | 12 ++++++++++++ 3 files changed, 54 insertions(+) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 642166092e..fe87a5994a 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -217,6 +217,11 @@ pub const DeclGen = struct { try writer.writeAll(" }"); } }, + .ErrorSet => { + const payload = val.castTag(.@"error").?; + // error values will be #defined at the top of the file + return writer.print("zig_error_{s}", .{payload.data.name}); + }, else => |e| return dg.fail(dg.decl.src(), "TODO: C backend: implement value {s}", .{ @tagName(e), }), @@ -327,6 +332,16 @@ pub const DeclGen = struct { try dg.renderType(w, child_type); try w.writeAll(" payload; bool is_null; }"); }, + .ErrorSet => { + comptime std.debug.assert(Type.initTag(.anyerror).abiSize(std.Target.current) == 2); + try w.writeAll("uint16_t"); + }, + .ErrorUnion => { + // TODO this needs to be typedeffed since different structs are different types. + try w.writeAll("struct { "); + try dg.renderType(w, t.errorUnionChild()); + try w.writeAll(" payload; uint16_t error; }"); + }, .Null, .Undefined => unreachable, // must be const or comptime else => |e| return dg.fail(dg.decl.src(), "TODO: C backend: implement type {s}", .{ @tagName(e), @@ -464,6 +479,8 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi .wrap_optional => try genWrapOptional(o, inst.castTag(.wrap_optional).?), .optional_payload => try genOptionalPayload(o, inst.castTag(.optional_payload).?), .optional_payload_ptr => try genOptionalPayload(o, inst.castTag(.optional_payload).?), + .is_err => try genIsErr(o, inst.castTag(.is_err).?), + .is_err_ptr => try genIsErr(o, inst.castTag(.is_err_ptr).?), else => |e| return o.dg.fail(o.dg.decl.src(), "TODO: C backend: implement codegen for {}", .{e}), }; switch (result_value) { @@ -900,6 +917,18 @@ fn genWrapOptional(o: *Object, inst: *Inst.UnOp) !CValue { return local; } +fn genIsErr(o: *Object, inst: *Inst.UnOp) !CValue { + const writer = o.writer(); + const maybe_deref = if (inst.base.tag == .is_err_ptr) "[0]" else ""; + const operand = try o.resolveInst(inst.operand); + + const local = try o.allocLocal(Type.initTag(.bool), .Const); + try writer.writeAll(" = ("); + try o.writeCValue(writer, operand); + try writer.print("){s}.error != 0;\n", .{maybe_deref}); + return local; +} + fn IndentWriter(comptime UnderlyingWriter: type) type { return struct { const Self = @This(); diff --git a/src/link/C.zig b/src/link/C.zig index 8fb3637cbe..1860fdb051 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -150,6 +150,19 @@ pub fn flushModule(self: *C, comp: *Compilation) !void { .iov_len = zig_h.len, }); + var error_defs_buf = std.ArrayList(u8).init(comp.gpa); + defer error_defs_buf.deinit(); + + var it = module.global_error_set.iterator(); + while (it.next()) |entry| { + try error_defs_buf.writer().print("#define zig_error_{s} {d}\n", .{ entry.key, entry.value }); + } + try error_defs_buf.writer().writeByte('\n'); + all_buffers.appendAssumeCapacity(.{ + .iov_base = error_defs_buf.items.ptr, + .iov_len = error_defs_buf.items.len, + }); + var fn_count: usize = 0; // Forward decls and non-functions first. diff --git a/src/type.zig b/src/type.zig index bb8dcea390..50d55a0c54 100644 --- a/src/type.zig +++ b/src/type.zig @@ -1813,6 +1813,18 @@ pub const Type = extern union { } } + /// Asserts that the type is an error union. + pub fn errorUnionChild(self: Type) Type { + return switch (self.tag()) { + .anyerror_void_error_union => Type.initTag(.anyerror), + .error_union => { + const payload = self.castTag(.error_union).?; + return payload.data.payload; + }, + else => unreachable, + }; + } + /// Asserts the type is an array or vector. pub fn arrayLen(self: Type) u64 { return switch (self.tag()) { From 0a7be71bc2e58a5375ceed0b1b9850bd33717a0b Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sat, 30 Jan 2021 14:56:36 +0200 Subject: [PATCH 26/49] stage2 cbe: non pointer optionals --- lib/std/hash_map.zig | 45 +++++++++++++------------ src/Compilation.zig | 2 ++ src/codegen/c.zig | 78 +++++++++++++++++++++++++++++++++++++++----- src/link/C.zig | 69 ++++++++++++++++++++++++++++++--------- src/test.zig | 3 +- test/stage2/cbe.zig | 12 +++++++ 6 files changed, 163 insertions(+), 46 deletions(-) diff --git a/lib/std/hash_map.zig b/lib/std/hash_map.zig index e2e0f056e1..62f430a672 100644 --- a/lib/std/hash_map.zig +++ b/lib/std/hash_map.zig @@ -50,20 +50,20 @@ pub fn getAutoEqlFn(comptime K: type) (fn (K, K) bool) { } pub fn AutoHashMap(comptime K: type, comptime V: type) type { - return HashMap(K, V, getAutoHashFn(K), getAutoEqlFn(K), DefaultMaxLoadPercentage); + return HashMap(K, V, getAutoHashFn(K), getAutoEqlFn(K), default_max_load_percentage); } pub fn AutoHashMapUnmanaged(comptime K: type, comptime V: type) type { - return HashMapUnmanaged(K, V, getAutoHashFn(K), getAutoEqlFn(K), DefaultMaxLoadPercentage); + return HashMapUnmanaged(K, V, getAutoHashFn(K), getAutoEqlFn(K), default_max_load_percentage); } /// Builtin hashmap for strings as keys. pub fn StringHashMap(comptime V: type) type { - return HashMap([]const u8, V, hashString, eqlString, DefaultMaxLoadPercentage); + return HashMap([]const u8, V, hashString, eqlString, default_max_load_percentage); } pub fn StringHashMapUnmanaged(comptime V: type) type { - return HashMapUnmanaged([]const u8, V, hashString, eqlString, DefaultMaxLoadPercentage); + return HashMapUnmanaged([]const u8, V, hashString, eqlString, default_max_load_percentage); } pub fn eqlString(a: []const u8, b: []const u8) bool { @@ -74,7 +74,10 @@ pub fn hashString(s: []const u8) u64 { return std.hash.Wyhash.hash(0, s); } -pub const DefaultMaxLoadPercentage = 80; +/// Deprecated use `default_max_load_percentage` +pub const DefaultMaxLoadPercentage = default_max_load_percentage; + +pub const default_max_load_percentage = 80; /// General purpose hash table. /// No order is guaranteed and any modification invalidates live iterators. @@ -89,13 +92,13 @@ pub fn HashMap( comptime V: type, comptime hashFn: fn (key: K) u64, comptime eqlFn: fn (a: K, b: K) bool, - comptime MaxLoadPercentage: u64, + comptime max_load_percentage: u64, ) type { return struct { unmanaged: Unmanaged, allocator: *Allocator, - pub const Unmanaged = HashMapUnmanaged(K, V, hashFn, eqlFn, MaxLoadPercentage); + pub const Unmanaged = HashMapUnmanaged(K, V, hashFn, eqlFn, max_load_percentage); pub const Entry = Unmanaged.Entry; pub const Hash = Unmanaged.Hash; pub const Iterator = Unmanaged.Iterator; @@ -251,9 +254,9 @@ pub fn HashMapUnmanaged( comptime V: type, hashFn: fn (key: K) u64, eqlFn: fn (a: K, b: K) bool, - comptime MaxLoadPercentage: u64, + comptime max_load_percentage: u64, ) type { - comptime assert(MaxLoadPercentage > 0 and MaxLoadPercentage < 100); + comptime assert(max_load_percentage > 0 and max_load_percentage < 100); return struct { const Self = @This(); @@ -274,12 +277,12 @@ pub fn HashMapUnmanaged( // Having a countdown to grow reduces the number of instructions to // execute when determining if the hashmap has enough capacity already. /// Number of available slots before a grow is needed to satisfy the - /// `MaxLoadPercentage`. + /// `max_load_percentage`. available: Size = 0, // This is purely empirical and not a /very smart magic constantâ„¢/. /// Capacity of the first grow when bootstrapping the hashmap. - const MinimalCapacity = 8; + const minimal_capacity = 8; // This hashmap is specially designed for sizes that fit in a u32. const Size = u32; @@ -382,7 +385,7 @@ pub fn HashMapUnmanaged( found_existing: bool, }; - pub const Managed = HashMap(K, V, hashFn, eqlFn, MaxLoadPercentage); + pub const Managed = HashMap(K, V, hashFn, eqlFn, max_load_percentage); pub fn promote(self: Self, allocator: *Allocator) Managed { return .{ @@ -392,7 +395,7 @@ pub fn HashMapUnmanaged( } fn isUnderMaxLoadPercentage(size: Size, cap: Size) bool { - return size * 100 < MaxLoadPercentage * cap; + return size * 100 < max_load_percentage * cap; } pub fn init(allocator: *Allocator) Self { @@ -425,7 +428,7 @@ pub fn HashMapUnmanaged( } fn capacityForSize(size: Size) Size { - var new_cap = @truncate(u32, (@as(u64, size) * 100) / MaxLoadPercentage + 1); + var new_cap = @truncate(u32, (@as(u64, size) * 100) / max_load_percentage + 1); new_cap = math.ceilPowerOfTwo(u32, new_cap) catch unreachable; return new_cap; } @@ -439,7 +442,7 @@ pub fn HashMapUnmanaged( if (self.metadata) |_| { self.initMetadatas(); self.size = 0; - self.available = @truncate(u32, (self.capacity() * MaxLoadPercentage) / 100); + self.available = @truncate(u32, (self.capacity() * max_load_percentage) / 100); } } @@ -712,9 +715,9 @@ pub fn HashMapUnmanaged( } // This counts the number of occupied slots, used + tombstones, which is - // what has to stay under the MaxLoadPercentage of capacity. + // what has to stay under the max_load_percentage of capacity. fn load(self: *const Self) Size { - const max_load = (self.capacity() * MaxLoadPercentage) / 100; + const max_load = (self.capacity() * max_load_percentage) / 100; assert(max_load >= self.available); return @truncate(Size, max_load - self.available); } @@ -733,7 +736,7 @@ pub fn HashMapUnmanaged( const new_cap = capacityForSize(self.size); try other.allocate(allocator, new_cap); other.initMetadatas(); - other.available = @truncate(u32, (new_cap * MaxLoadPercentage) / 100); + other.available = @truncate(u32, (new_cap * max_load_percentage) / 100); var i: Size = 0; var metadata = self.metadata.?; @@ -751,7 +754,7 @@ pub fn HashMapUnmanaged( } fn grow(self: *Self, allocator: *Allocator, new_capacity: Size) !void { - const new_cap = std.math.max(new_capacity, MinimalCapacity); + const new_cap = std.math.max(new_capacity, minimal_capacity); assert(new_cap > self.capacity()); assert(std.math.isPowerOfTwo(new_cap)); @@ -759,7 +762,7 @@ pub fn HashMapUnmanaged( defer map.deinit(allocator); try map.allocate(allocator, new_cap); map.initMetadatas(); - map.available = @truncate(u32, (new_cap * MaxLoadPercentage) / 100); + map.available = @truncate(u32, (new_cap * max_load_percentage) / 100); if (self.size != 0) { const old_capacity = self.capacity(); @@ -943,7 +946,7 @@ test "std.hash_map ensureCapacity with existing elements" { try map.put(0, 0); expectEqual(map.count(), 1); - expectEqual(map.capacity(), @TypeOf(map).Unmanaged.MinimalCapacity); + expectEqual(map.capacity(), @TypeOf(map).Unmanaged.minimal_capacity); try map.ensureCapacity(65); expectEqual(map.count(), 1); diff --git a/src/Compilation.zig b/src/Compilation.zig index 39e10becec..786280f9ef 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1653,6 +1653,8 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor .error_msg = null, .decl = decl, .fwd_decl = fwd_decl.toManaged(module.gpa), + // we don't want to emit optionals and error unions to headers since they have no ABI + .typedefs = undefined, }; defer dg.fwd_decl.deinit(); diff --git a/src/codegen/c.zig b/src/codegen/c.zig index fe87a5994a..084c653d1c 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -32,6 +32,34 @@ pub const CValue = union(enum) { }; pub const CValueMap = std.AutoHashMap(*Inst, CValue); +pub const TypedefMap = std.HashMap(Type, struct { name: []const u8, rendered: []u8 }, Type.hash, Type.eql, std.hash_map.default_max_load_percentage); + +fn formatTypeAsCIdentifier( + data: Type, + comptime fmt: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, +) !void { + var buffer = [1]u8{0} ** 128; + // We don't care if it gets cut off, it's still more unique than a number + var buf = std.fmt.bufPrint(&buffer, "{}", .{data}) catch &buffer; + + for (buf) |c, i| { + switch (c) { + 0 => return writer.writeAll(buf[0..i]), + 'a'...'z', 'A'...'Z', '_', '$' => {}, + '0'...'9' => if (i == 0) { + buf[i] = '_'; + }, + else => buf[i] = '_', + } + } + return writer.writeAll(buf); +} + +pub fn typeToCIdentifier(t: Type) std.fmt.Formatter(formatTypeAsCIdentifier) { + return .{ .data = t }; +} /// This data is available when outputting .c code for a Module. /// It is not available when generating .h file. @@ -115,6 +143,7 @@ pub const DeclGen = struct { decl: *Decl, fwd_decl: std.ArrayList(u8), error_msg: ?*Module.ErrorMsg, + typedefs: TypedefMap, fn fail(dg: *DeclGen, src: usize, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } { dg.error_msg = try Module.ErrorMsg.create(dg.module.gpa, .{ @@ -325,22 +354,55 @@ pub const DeclGen = struct { const child_type = t.optionalChild(&opt_buf); if (t.isPtrLikeOptional()) { return dg.renderType(w, child_type); + } else if (dg.typedefs.get(t)) |some| { + return w.writeAll(some.name); } - // TODO this needs to be typedeffed since different structs are different types. - try w.writeAll("struct { "); - try dg.renderType(w, child_type); - try w.writeAll(" payload; bool is_null; }"); + var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); + defer buffer.deinit(); + const bw = buffer.writer(); + + try bw.writeAll("typedef struct { "); + try dg.renderType(bw, child_type); + try bw.writeAll(" payload; bool is_null; } "); + const name_index = buffer.items.len; + try bw.print("zig_opt_{s}_t;\n", .{typeToCIdentifier(child_type)}); + + const rendered = buffer.toOwnedSlice(); + errdefer dg.typedefs.allocator.free(rendered); + const name = rendered[name_index .. rendered.len - 2]; + + try dg.typedefs.ensureCapacity(dg.typedefs.capacity() + 1); + try w.writeAll(name); + dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered }); }, .ErrorSet => { comptime std.debug.assert(Type.initTag(.anyerror).abiSize(std.Target.current) == 2); try w.writeAll("uint16_t"); }, .ErrorUnion => { - // TODO this needs to be typedeffed since different structs are different types. - try w.writeAll("struct { "); - try dg.renderType(w, t.errorUnionChild()); - try w.writeAll(" payload; uint16_t error; }"); + if (dg.typedefs.get(t)) |some| { + return w.writeAll(some.name); + } + const child_type = t.errorUnionChild(); + + var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); + defer buffer.deinit(); + const bw = buffer.writer(); + + try bw.writeAll("typedef struct { "); + try dg.renderType(bw, t.errorUnionChild()); + try bw.writeAll(" payload; uint16_t error; } "); + const name_index = buffer.items.len; + try bw.print("zig_err_union_{s}_t;\n", .{typeToCIdentifier(child_type)}); + + const rendered = buffer.toOwnedSlice(); + errdefer dg.typedefs.allocator.free(rendered); + const name = rendered[name_index .. rendered.len - 2]; + + try dg.typedefs.ensureCapacity(dg.typedefs.capacity() + 1); + try w.writeAll(name); + dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered }); }, .Null, .Undefined => unreachable, // must be const or comptime else => |e| return dg.fail(dg.decl.src(), "TODO: C backend: implement type {s}", .{ diff --git a/src/link/C.zig b/src/link/C.zig index 1860fdb051..60844ce43b 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -9,6 +9,7 @@ const codegen = @import("../codegen/c.zig"); const link = @import("../link.zig"); const trace = @import("../tracy.zig").trace; const C = @This(); +const Type = @import("../type.zig").Type; pub const base_tag: link.File.Tag = .c; pub const zig_h = @embedFile("C/zig.h"); @@ -28,9 +29,11 @@ pub const DeclBlock = struct { /// Per-function data. pub const FnBlock = struct { fwd_decl: std.ArrayListUnmanaged(u8), + typedefs: codegen.TypedefMap.Unmanaged, pub const empty: FnBlock = .{ .fwd_decl = .{}, + .typedefs = .{}, }; }; @@ -74,6 +77,11 @@ pub fn allocateDeclIndexes(self: *C, decl: *Module.Decl) !void {} pub fn freeDecl(self: *C, decl: *Module.Decl) void { decl.link.c.code.deinit(self.base.allocator); decl.fn_link.c.fwd_decl.deinit(self.base.allocator); + var it = decl.fn_link.c.typedefs.iterator(); + while (it.next()) |some| { + self.base.allocator.free(some.value.rendered); + } + decl.fn_link.c.typedefs.deinit(self.base.allocator); } pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void { @@ -81,8 +89,10 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void { defer tracy.end(); const fwd_decl = &decl.fn_link.c.fwd_decl; + const typedefs = &decl.fn_link.c.typedefs; const code = &decl.link.c.code; fwd_decl.shrinkRetainingCapacity(0); + typedefs.clearRetainingCapacity(); code.shrinkRetainingCapacity(0); var object: codegen.Object = .{ @@ -91,6 +101,7 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void { .error_msg = null, .decl = decl, .fwd_decl = fwd_decl.toManaged(module.gpa), + .typedefs = typedefs.promote(module.gpa), }, .gpa = module.gpa, .code = code.toManaged(module.gpa), @@ -98,9 +109,16 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void { .indent_writer = undefined, // set later so we can get a pointer to object.code }; object.indent_writer = .{ .underlying_writer = object.code.writer() }; - defer object.value_map.deinit(); - defer object.code.deinit(); - defer object.dg.fwd_decl.deinit(); + defer { + object.value_map.deinit(); + object.code.deinit(); + object.dg.fwd_decl.deinit(); + var it = object.dg.typedefs.iterator(); + while (it.next()) |some| { + module.gpa.free(some.value.rendered); + } + object.dg.typedefs.deinit(); + } codegen.genDecl(&object) catch |err| switch (err) { error.AnalysisFail => { @@ -111,6 +129,8 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void { }; fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged(); + typedefs.* = object.dg.typedefs.unmanaged; + object.dg.typedefs.unmanaged = .{}; code.* = object.code.moveToUnmanaged(); // Free excess allocated memory for this Decl. @@ -142,7 +162,7 @@ pub fn flushModule(self: *C, comp: *Compilation) !void { defer all_buffers.deinit(); // This is at least enough until we get to the function bodies without error handling. - try all_buffers.ensureCapacity(module.decl_table.count() + 1); + try all_buffers.ensureCapacity(module.decl_table.count() + 2); var file_size: u64 = zig_h.len; all_buffers.appendAssumeCapacity(.{ @@ -150,22 +170,25 @@ pub fn flushModule(self: *C, comp: *Compilation) !void { .iov_len = zig_h.len, }); - var error_defs_buf = std.ArrayList(u8).init(comp.gpa); - defer error_defs_buf.deinit(); + var err_typedef_buf = std.ArrayList(u8).init(comp.gpa); + defer err_typedef_buf.deinit(); + const err_typedef_writer = err_typedef_buf.writer(); + const err_typedef_item = all_buffers.addOneAssumeCapacity(); - var it = module.global_error_set.iterator(); - while (it.next()) |entry| { - try error_defs_buf.writer().print("#define zig_error_{s} {d}\n", .{ entry.key, entry.value }); + render_errors: { + if (module.global_error_set.size == 0) break :render_errors; + var it = module.global_error_set.iterator(); + while (it.next()) |entry| { + try err_typedef_writer.print("#define zig_error_{s} {d}\n", .{ entry.key, entry.value }); + } + try err_typedef_writer.writeByte('\n'); } - try error_defs_buf.writer().writeByte('\n'); - all_buffers.appendAssumeCapacity(.{ - .iov_base = error_defs_buf.items.ptr, - .iov_len = error_defs_buf.items.len, - }); var fn_count: usize = 0; + var typedefs = std.HashMap(Type, []const u8, Type.hash, Type.eql, std.hash_map.default_max_load_percentage).init(comp.gpa); + defer typedefs.deinit(); - // Forward decls and non-functions first. + // Typedefs, forward decls and non-functions first. // TODO: performance investigation: would keeping a list of Decls that we should // generate, rather than querying here, be faster? for (module.decl_table.items()) |kv| { @@ -174,6 +197,16 @@ pub fn flushModule(self: *C, comp: *Compilation) !void { .most_recent => |tvm| { const buf = buf: { if (tvm.typed_value.val.castTag(.function)) |_| { + var it = decl.fn_link.c.typedefs.iterator(); + while (it.next()) |new| { + if (typedefs.get(new.key)) |previous| { + try err_typedef_writer.print("typedef {s} {s};\n", .{ previous, new.value.name }); + } else { + try typedefs.ensureCapacity(typedefs.capacity() + 1); + try err_typedef_writer.writeAll(new.value.rendered); + typedefs.putAssumeCapacityNoClobber(new.key, new.value.name); + } + } fn_count += 1; break :buf decl.fn_link.c.fwd_decl.items; } else { @@ -190,6 +223,12 @@ pub fn flushModule(self: *C, comp: *Compilation) !void { } } + err_typedef_item.* = .{ + .iov_base = err_typedef_buf.items.ptr, + .iov_len = err_typedef_buf.items.len, + }; + file_size += err_typedef_buf.items.len; + // Now the function bodies. try all_buffers.ensureCapacity(all_buffers.items.len + fn_count); for (module.decl_table.items()) |kv| { diff --git a/src/test.zig b/src/test.zig index 47aeef3385..d6c78281dc 100644 --- a/src/test.zig +++ b/src/test.zig @@ -868,11 +868,10 @@ pub const TestContext = struct { std.testing.zig_exe_path, "run", "-cflags", - "-std=c89", + "-std=c99", "-pedantic", "-Werror", "-Wno-incompatible-library-redeclaration", // https://github.com/ziglang/zig/issues/875 - "-Wno-declaration-after-statement", "--", "-lc", exe_path, diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig index b22ef44148..ba679f49a0 100644 --- a/test/stage2/cbe.zig +++ b/test/stage2/cbe.zig @@ -258,6 +258,18 @@ pub fn addCases(ctx: *TestContext) !void { \\ return count - 5; \\} , ""); + + // Same with non pointer optionals + case.addCompareOutput( + \\export fn main() c_int { + \\ var count: c_int = 0; + \\ var opt_ptr: ?c_int = count; + \\ while (opt_ptr) |_| : (count += 1) { + \\ if (count == 4) opt_ptr = null; + \\ } + \\ return count - 5; + \\} + , ""); } ctx.c("empty start function", linux_x64, \\export fn _start() noreturn { From 6467ef6d3b5648d47c13b877d3d4fe6a5b5efb7d Mon Sep 17 00:00:00 2001 From: jacob gw Date: Mon, 1 Mar 2021 11:25:50 -0500 Subject: [PATCH 27/49] cbe: add error comparison support --- src/codegen.zig | 2 ++ src/type.zig | 2 +- src/zir_sema.zig | 3 ++- test/stage2/cbe.zig | 56 +++++++++++++++++++++++++++++---------------- 4 files changed, 41 insertions(+), 22 deletions(-) diff --git a/src/codegen.zig b/src/codegen.zig index c3cd64cf73..cfe605b567 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -2237,6 +2237,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { // No side effects, so if it's unreferenced, do nothing. if (inst.base.isUnused()) return MCValue{ .dead = {} }; + if (inst.lhs.ty.zigTypeTag() == .ErrorSet or inst.rhs.ty.zigTypeTag() == .ErrorSet) + return self.fail(inst.base.src, "TODO implement cmp for errors", .{}); switch (arch) { .x86_64 => { try self.code.ensureCapacity(self.code.items.len + 8); diff --git a/src/type.zig b/src/type.zig index 50d55a0c54..c3a99bb184 100644 --- a/src/type.zig +++ b/src/type.zig @@ -1663,6 +1663,7 @@ pub const Type = extern union { .Int, .Float, .ErrorSet, + .ErrorUnion, .Enum, .Frame, .AnyFrame, @@ -1687,7 +1688,6 @@ pub const Type = extern union { }, .Pointer, .Array => ty = ty.elemType(), - .ErrorUnion => @panic("TODO fn isValidVarType"), .Fn => @panic("TODO fn isValidVarType"), .Struct => @panic("TODO struct isValidVarType"), .Union => @panic("TODO union isValidVarType"), diff --git a/src/zir_sema.zig b/src/zir_sema.zig index 9cbdfd07dd..a1b79879b8 100644 --- a/src/zir_sema.zig +++ b/src/zir_sema.zig @@ -2326,7 +2326,8 @@ fn zirCmp( return mod.constBool(scope, inst.base.src, std.mem.eql(u8, lval.castTag(.@"error").?.data.name, rval.castTag(.@"error").?.data.name) == (op == .eq)); } } - return mod.fail(scope, inst.base.src, "TODO implement equality comparison between runtime errors", .{}); + const b = try mod.requireRuntimeBlock(scope, inst.base.src); + return mod.addBinOp(b, inst.base.src, Type.initTag(.bool), if (op == .eq) .cmp_eq else .cmp_neq, lhs, rhs); } else if (lhs.ty.isNumeric() and rhs.ty.isNumeric()) { // This operation allows any combination of integer and float types, regardless of the // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig index ba679f49a0..1487b15e12 100644 --- a/test/stage2/cbe.zig +++ b/test/stage2/cbe.zig @@ -244,30 +244,46 @@ pub fn addCases(ctx: *TestContext) !void { \\} , ""); } + //{ + // var case = ctx.exeFromCompiledC("optionals", .{}); + + // // Simple while loop + // case.addCompareOutput( + // \\export fn main() c_int { + // \\ var count: c_int = 0; + // \\ var opt_ptr: ?*c_int = &count; + // \\ while (opt_ptr) |_| : (count += 1) { + // \\ if (count == 4) opt_ptr = null; + // \\ } + // \\ return count - 5; + // \\} + // , ""); + + // // Same with non pointer optionals + // case.addCompareOutput( + // \\export fn main() c_int { + // \\ var count: c_int = 0; + // \\ var opt_ptr: ?c_int = count; + // \\ while (opt_ptr) |_| : (count += 1) { + // \\ if (count == 4) opt_ptr = null; + // \\ } + // \\ return count - 5; + // \\} + // , ""); + //} { - var case = ctx.exeFromCompiledC("optionals", .{}); - - // Simple while loop + var case = ctx.exeFromCompiledC("errors", .{}); case.addCompareOutput( \\export fn main() c_int { - \\ var count: c_int = 0; - \\ var opt_ptr: ?*c_int = &count; - \\ while (opt_ptr) |_| : (count += 1) { - \\ if (count == 4) opt_ptr = null; - \\ } - \\ return count - 5; + \\ var e1 = error.Foo; + \\ var e2 = error.Bar; + \\ assert(e1 != e2); + \\ assert(e1 == error.Foo); + \\ assert(e2 == error.Bar); + \\ return 0; \\} - , ""); - - // Same with non pointer optionals - case.addCompareOutput( - \\export fn main() c_int { - \\ var count: c_int = 0; - \\ var opt_ptr: ?c_int = count; - \\ while (opt_ptr) |_| : (count += 1) { - \\ if (count == 4) opt_ptr = null; - \\ } - \\ return count - 5; + \\fn assert(b: bool) void { + \\ if (!b) unreachable; \\} , ""); } From 30ffa052f2b53d9a01aedbe672bec3c5e28d5118 Mon Sep 17 00:00:00 2001 From: jacob gw Date: Mon, 1 Mar 2021 15:16:18 -0500 Subject: [PATCH 28/49] stage2 cbe: add error union and error union operations --- src/codegen/c.zig | 85 +++++++++++++++++++++++++++++++++++++++++++-- src/link/C.zig | 3 +- src/type.zig | 11 ++++++ test/stage2/cbe.zig | 14 ++++++++ 4 files changed, 110 insertions(+), 3 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 084c653d1c..ceafe01d35 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -251,6 +251,31 @@ pub const DeclGen = struct { // error values will be #defined at the top of the file return writer.print("zig_error_{s}", .{payload.data.name}); }, + .ErrorUnion => { + const error_type = t.errorUnionSet(); + const payload_type = t.errorUnionChild(); + const data = val.castTag(.error_union).?.data; + try writer.writeByte('('); + try dg.renderType(writer, t); + try writer.writeAll("){"); + if (val.getError()) |_| { + try writer.writeAll(" .error = "); + try dg.renderValue( + writer, + error_type, + data, + ); + try writer.writeAll(" }"); + } else { + try writer.writeAll(" .payload = "); + try dg.renderValue( + writer, + payload_type, + data, + ); + try writer.writeAll(", .error = 0 }"); + } + }, else => |e| return dg.fail(dg.decl.src(), "TODO: C backend: implement value {s}", .{ @tagName(e), }), @@ -385,16 +410,17 @@ pub const DeclGen = struct { return w.writeAll(some.name); } const child_type = t.errorUnionChild(); + const set_type = t.errorUnionSet(); var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); defer buffer.deinit(); const bw = buffer.writer(); try bw.writeAll("typedef struct { "); - try dg.renderType(bw, t.errorUnionChild()); + try dg.renderType(bw, child_type); try bw.writeAll(" payload; uint16_t error; } "); const name_index = buffer.items.len; - try bw.print("zig_err_union_{s}_t;\n", .{typeToCIdentifier(child_type)}); + try bw.print("zig_err_union_{s}_{s}_t;\n", .{ typeToCIdentifier(set_type), typeToCIdentifier(child_type) }); const rendered = buffer.toOwnedSlice(); errdefer dg.typedefs.allocator.free(rendered); @@ -543,6 +569,12 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi .optional_payload_ptr => try genOptionalPayload(o, inst.castTag(.optional_payload).?), .is_err => try genIsErr(o, inst.castTag(.is_err).?), .is_err_ptr => try genIsErr(o, inst.castTag(.is_err_ptr).?), + .unwrap_errunion_payload => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload).?), + .unwrap_errunion_err => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err).?), + .unwrap_errunion_payload_ptr => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload_ptr).?), + .unwrap_errunion_err_ptr => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err_ptr).?), + .wrap_errunion_payload => try genWrapErrUnionPay(o, inst.castTag(.wrap_errunion_payload).?), + .wrap_errunion_err => try genWrapErrUnionErr(o, inst.castTag(.wrap_errunion_err).?), else => |e| return o.dg.fail(o.dg.decl.src(), "TODO: C backend: implement codegen for {}", .{e}), }; switch (result_value) { @@ -962,6 +994,35 @@ fn genOptionalPayload(o: *Object, inst: *Inst.UnOp) !CValue { return local; } +// *(E!T) -> E NOT *E +fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue { + const writer = o.writer(); + const operand = try o.resolveInst(inst.operand); + + const maybe_deref = if (inst.operand.ty.zigTypeTag() == .Pointer) "->" else "."; + + const local = try o.allocLocal(inst.base.ty, .Const); + try writer.writeAll(" = ("); + try o.writeCValue(writer, operand); + + try writer.print("){s}error;\n", .{maybe_deref}); + return local; +} +fn genUnwrapErrUnionPay(o: *Object, inst: *Inst.UnOp) !CValue { + const writer = o.writer(); + const operand = try o.resolveInst(inst.operand); + + const maybe_deref = if (inst.operand.ty.zigTypeTag() == .Pointer) "->" else "."; + const maybe_addrof = if (inst.base.ty.zigTypeTag() == .Pointer) "&" else ""; + + const local = try o.allocLocal(inst.base.ty, .Const); + try writer.print(" = {s}(", .{maybe_addrof}); + try o.writeCValue(writer, operand); + + try writer.print("){s}payload;\n", .{maybe_deref}); + return local; +} + fn genWrapOptional(o: *Object, inst: *Inst.UnOp) !CValue { const writer = o.writer(); const operand = try o.resolveInst(inst.operand); @@ -978,6 +1039,26 @@ fn genWrapOptional(o: *Object, inst: *Inst.UnOp) !CValue { try writer.writeAll("};\n"); return local; } +fn genWrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue { + const writer = o.writer(); + const operand = try o.resolveInst(inst.operand); + + const local = try o.allocLocal(inst.base.ty, .Const); + try writer.writeAll(" = { .error = "); + try o.writeCValue(writer, operand); + try writer.writeAll(" };\n"); + return local; +} +fn genWrapErrUnionPay(o: *Object, inst: *Inst.UnOp) !CValue { + const writer = o.writer(); + const operand = try o.resolveInst(inst.operand); + + const local = try o.allocLocal(inst.base.ty, .Const); + try writer.writeAll(" = { .error = 0, .payload = "); + try o.writeCValue(writer, operand); + try writer.writeAll(" };\n"); + return local; +} fn genIsErr(o: *Object, inst: *Inst.UnOp) !CValue { const writer = o.writer(); diff --git a/src/link/C.zig b/src/link/C.zig index 60844ce43b..655a044394 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -179,7 +179,8 @@ pub fn flushModule(self: *C, comp: *Compilation) !void { if (module.global_error_set.size == 0) break :render_errors; var it = module.global_error_set.iterator(); while (it.next()) |entry| { - try err_typedef_writer.print("#define zig_error_{s} {d}\n", .{ entry.key, entry.value }); + // + 1 because 0 represents no error + try err_typedef_writer.print("#define zig_error_{s} {d}\n", .{ entry.key, entry.value + 1 }); } try err_typedef_writer.writeByte('\n'); } diff --git a/src/type.zig b/src/type.zig index c3a99bb184..b9f9207d4a 100644 --- a/src/type.zig +++ b/src/type.zig @@ -1825,6 +1825,17 @@ pub const Type = extern union { }; } + pub fn errorUnionSet(self: Type) Type { + return switch (self.tag()) { + .anyerror_void_error_union => Type.initTag(.anyerror), + .error_union => { + const payload = self.castTag(.error_union).?; + return payload.data.error_set; + }, + else => unreachable, + }; + } + /// Asserts the type is an array or vector. pub fn arrayLen(self: Type) u64 { return switch (self.tag()) { diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig index 1487b15e12..e9082f57fa 100644 --- a/test/stage2/cbe.zig +++ b/test/stage2/cbe.zig @@ -286,6 +286,20 @@ pub fn addCases(ctx: *TestContext) !void { \\ if (!b) unreachable; \\} , ""); + case.addCompareOutput( + \\export fn main() c_int { + \\ var e: anyerror!c_int = 0; + \\ const i = e catch 69; + \\ return i; + \\} + , ""); + case.addCompareOutput( + \\export fn main() c_int { + \\ var e: anyerror!c_int = error.Foo; + \\ const i = e catch 69; + \\ return 69 - i; + \\} + , ""); } ctx.c("empty start function", linux_x64, \\export fn _start() noreturn { From d9e46a4b901fad0e6d684102ea08444dcec30909 Mon Sep 17 00:00:00 2001 From: jacob gw Date: Mon, 1 Mar 2021 15:40:48 -0500 Subject: [PATCH 29/49] stage2: fix memory leak in the cbe --- src/link/C.zig | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/link/C.zig b/src/link/C.zig index 655a044394..440f52c49f 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -92,6 +92,12 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void { const typedefs = &decl.fn_link.c.typedefs; const code = &decl.link.c.code; fwd_decl.shrinkRetainingCapacity(0); + { + var it = typedefs.iterator(); + while (it.next()) |entry| { + module.gpa.free(entry.value.rendered); + } + } typedefs.clearRetainingCapacity(); code.shrinkRetainingCapacity(0); From fc62ff77c3921758624a81970f3098300992ee47 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Mon, 1 Mar 2021 23:03:26 +0200 Subject: [PATCH 30/49] stage2: error union payload must also be a valid variable type --- src/codegen/c.zig | 2 +- src/type.zig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index ceafe01d35..af8d2d272d 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -566,7 +566,7 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi .is_non_null_ptr => try genIsNull(o, inst.castTag(.is_non_null_ptr).?), .wrap_optional => try genWrapOptional(o, inst.castTag(.wrap_optional).?), .optional_payload => try genOptionalPayload(o, inst.castTag(.optional_payload).?), - .optional_payload_ptr => try genOptionalPayload(o, inst.castTag(.optional_payload).?), + .optional_payload_ptr => try genOptionalPayload(o, inst.castTag(.optional_payload_ptr).?), .is_err => try genIsErr(o, inst.castTag(.is_err).?), .is_err_ptr => try genIsErr(o, inst.castTag(.is_err_ptr).?), .unwrap_errunion_payload => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload).?), diff --git a/src/type.zig b/src/type.zig index b9f9207d4a..9599a165fd 100644 --- a/src/type.zig +++ b/src/type.zig @@ -1663,7 +1663,6 @@ pub const Type = extern union { .Int, .Float, .ErrorSet, - .ErrorUnion, .Enum, .Frame, .AnyFrame, @@ -1687,6 +1686,7 @@ pub const Type = extern union { return ty.optionalChild(&buf).isValidVarType(is_extern); }, .Pointer, .Array => ty = ty.elemType(), + .ErrorUnion => ty = ty.errorUnionChild(), .Fn => @panic("TODO fn isValidVarType"), .Struct => @panic("TODO struct isValidVarType"), From d01bb211732f1d0aebf558c43825a52254d034ab Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Fri, 5 Mar 2021 12:09:57 -0800 Subject: [PATCH 31/49] translate-c: Explicitly cast decayed array to pointer with @ptrCast This enables translation of code that uses pointer arithmetic with arrays --- src/translate_c.zig | 7 ++++--- test/run_translated_c.zig | 4 ++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/translate_c.zig b/src/translate_c.zig index 896f89283d..00b549b961 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -1493,7 +1493,8 @@ fn transImplicitCastExpr( } const addr = try Tag.address_of.create(c.arena, try transExpr(c, scope, sub_expr, .used)); - return maybeSuppressResult(c, scope, result_used, addr); + const casted = try transCPtrCast(c, scope, expr.getBeginLoc(), dest_type, src_type, addr); + return maybeSuppressResult(c, scope, result_used, casted); }, .NullToPointer => { return Tag.null_literal.init(); @@ -3156,10 +3157,10 @@ fn transCPtrCast( const src_child_type = src_ty.getPointeeType(); const dst_type_node = try transType(c, scope, ty, loc); - if ((src_child_type.isConstQualified() and + if (!src_ty.isArrayType() and ((src_child_type.isConstQualified() and !child_type.isConstQualified()) or (src_child_type.isVolatileQualified() and - !child_type.isVolatileQualified())) + !child_type.isVolatileQualified()))) { // Casting away const or volatile requires us to use @intToPtr const ptr_to_int = try Tag.ptr_to_int.create(c.arena, expr); diff --git a/test/run_translated_c.zig b/test/run_translated_c.zig index abefe46459..8b8a1bca19 100644 --- a/test/run_translated_c.zig +++ b/test/run_translated_c.zig @@ -1164,6 +1164,10 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { \\ y = (x -= idx); \\ if (y != x || y != &array[5]) abort(); \\ + \\ if (array + idx != &array[1] || array + 1 != &array[1]) abort(); + \\ idx = -1; + \\ if (array - idx != &array[1]) abort(); + \\ \\ return 0; \\} , ""); From b988815bf0771dd86a345ce8ed8b0d3eb9d6f55e Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Sun, 7 Mar 2021 12:38:08 +0100 Subject: [PATCH 32/49] parser: fix parsing/rendering of a[b.. :c] slicing The modification to the grammar in the comment is in line with the grammar in the zig-spec repo. Note: checking if the previous token is a colon is insufficent to tell if a block has a label, the identifier must be checked for as well. This can be seen in sentinel terminated slicing: `foo[0..1:{}]` --- lib/std/zig/ast.zig | 9 ++++++--- lib/std/zig/parse.zig | 36 +++++++++++++++++------------------- lib/std/zig/parser_test.zig | 2 ++ lib/std/zig/render.zig | 29 +++++++++++++++-------------- src/astgen.zig | 4 +++- 5 files changed, 43 insertions(+), 37 deletions(-) diff --git a/lib/std/zig/ast.zig b/lib/std/zig/ast.zig index 1a0515b475..59de727bcc 100644 --- a/lib/std/zig/ast.zig +++ b/lib/std/zig/ast.zig @@ -525,7 +525,9 @@ pub const Tree = struct { => { // Look for a label. const lbrace = main_tokens[n]; - if (token_tags[lbrace - 1] == .colon) { + if (token_tags[lbrace - 1] == .colon and + token_tags[lbrace - 2] == .identifier) + { end_offset += 2; } return lbrace - end_offset; @@ -989,13 +991,13 @@ pub const Tree = struct { }, .slice => { const extra = tree.extraData(datas[n].rhs, Node.Slice); - assert(extra.end != 0); // should have used SliceOpen + assert(extra.end != 0); // should have used slice_open end_offset += 1; // rbracket n = extra.end; }, .slice_sentinel => { const extra = tree.extraData(datas[n].rhs, Node.SliceSentinel); - assert(extra.sentinel != 0); // should have used Slice + assert(extra.sentinel != 0); // should have used slice end_offset += 1; // rbracket n = extra.sentinel; }, @@ -2925,6 +2927,7 @@ pub const Node = struct { pub const SliceSentinel = struct { start: Index, + /// May be 0 if the slice is "open" end: Index, sentinel: Index, }; diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index ad32d3182d..c8a4cffdbd 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -3441,7 +3441,7 @@ const Parser = struct { } /// SuffixOp - /// <- LBRACKET Expr (DOT2 (Expr (COLON Expr)?)?)? RBRACKET + /// <- LBRACKET Expr (DOT2 (Expr? (COLON Expr)?)?)? RBRACKET /// / DOT IDENTIFIER /// / DOTASTERISK /// / DOTQUESTIONMARK @@ -3453,17 +3453,6 @@ const Parser = struct { if (p.eatToken(.ellipsis2)) |_| { const end_expr = try p.parseExpr(); - if (end_expr == 0) { - _ = try p.expectToken(.r_bracket); - return p.addNode(.{ - .tag = .slice_open, - .main_token = lbracket, - .data = .{ - .lhs = lhs, - .rhs = index_expr, - }, - }); - } if (p.eatToken(.colon)) |_| { const sentinel = try p.parseExpr(); _ = try p.expectToken(.r_bracket); @@ -3479,20 +3468,29 @@ const Parser = struct { }), }, }); - } else { - _ = try p.expectToken(.r_bracket); + } + _ = try p.expectToken(.r_bracket); + if (end_expr == 0) { return p.addNode(.{ - .tag = .slice, + .tag = .slice_open, .main_token = lbracket, .data = .{ .lhs = lhs, - .rhs = try p.addExtra(Node.Slice{ - .start = index_expr, - .end = end_expr, - }), + .rhs = index_expr, }, }); } + return p.addNode(.{ + .tag = .slice, + .main_token = lbracket, + .data = .{ + .lhs = lhs, + .rhs = try p.addExtra(Node.Slice{ + .start = index_expr, + .end = end_expr, + }), + }, + }); } _ = try p.expectToken(.r_bracket); return p.addNode(.{ diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index 5c0f4b3bda..b9f856fba3 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -852,6 +852,7 @@ test "zig fmt: slices" { try testCanonical( \\const a = b[0..]; \\const c = d[0..1]; + \\const d = f[0.. :0]; \\const e = f[0..1 :0]; \\ ); @@ -861,6 +862,7 @@ test "zig fmt: slices with spaces in bounds" { try testCanonical( \\const a = b[0 + 0 ..]; \\const c = d[0 + 0 .. 1]; + \\const c = d[0 + 0 .. :0]; \\const e = f[0 .. 1 + 1 :0]; \\ ); diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index 069b62af79..30e83e9a7c 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -470,9 +470,9 @@ fn renderExpression(gpa: *Allocator, ais: *Ais, tree: ast.Tree, node: ast.Node.I return renderToken(ais, tree, rbracket, space); // ] }, - .slice_open => return renderSlice(gpa, ais, tree, tree.sliceOpen(node), space), - .slice => return renderSlice(gpa, ais, tree, tree.slice(node), space), - .slice_sentinel => return renderSlice(gpa, ais, tree, tree.sliceSentinel(node), space), + .slice_open => return renderSlice(gpa, ais, tree, node, tree.sliceOpen(node), space), + .slice => return renderSlice(gpa, ais, tree, node, tree.slice(node), space), + .slice_sentinel => return renderSlice(gpa, ais, tree, node, tree.sliceSentinel(node), space), .deref => { try renderExpression(gpa, ais, tree, datas[node].lhs, .none); @@ -815,6 +815,7 @@ fn renderSlice( gpa: *Allocator, ais: *Ais, tree: ast.Tree, + slice_node: ast.Node.Index, slice: ast.full.Slice, space: Space, ) Error!void { @@ -822,7 +823,9 @@ fn renderSlice( const after_start_space_bool = nodeCausesSliceOpSpace(node_tags[slice.ast.start]) or if (slice.ast.end != 0) nodeCausesSliceOpSpace(node_tags[slice.ast.end]) else false; const after_start_space = if (after_start_space_bool) Space.space else Space.none; - const after_dots_space = if (slice.ast.end != 0) after_start_space else Space.none; + const after_dots_space = if (slice.ast.end != 0) + after_start_space + else if (slice.ast.sentinel != 0) Space.space else Space.none; try renderExpression(gpa, ais, tree, slice.ast.sliced, .none); try renderToken(ais, tree, slice.ast.lbracket, .none); // lbracket @@ -830,20 +833,18 @@ fn renderSlice( const start_last = tree.lastToken(slice.ast.start); try renderExpression(gpa, ais, tree, slice.ast.start, after_start_space); try renderToken(ais, tree, start_last + 1, after_dots_space); // ellipsis2 ("..") - if (slice.ast.end == 0) { - return renderToken(ais, tree, start_last + 2, space); // rbracket + + if (slice.ast.end != 0) { + const after_end_space = if (slice.ast.sentinel != 0) Space.space else Space.none; + try renderExpression(gpa, ais, tree, slice.ast.end, after_end_space); } - const end_last = tree.lastToken(slice.ast.end); - const after_end_space = if (slice.ast.sentinel != 0) Space.space else Space.none; - try renderExpression(gpa, ais, tree, slice.ast.end, after_end_space); - if (slice.ast.sentinel == 0) { - return renderToken(ais, tree, end_last + 1, space); // rbracket + if (slice.ast.sentinel != 0) { + try renderToken(ais, tree, tree.firstToken(slice.ast.sentinel) - 1, .none); // colon + try renderExpression(gpa, ais, tree, slice.ast.sentinel, .none); } - try renderToken(ais, tree, end_last + 1, .none); // colon - try renderExpression(gpa, ais, tree, slice.ast.sentinel, .none); - try renderToken(ais, tree, tree.lastToken(slice.ast.sentinel) + 1, space); // rbracket + try renderToken(ais, tree, tree.lastToken(slice_node), space); // rbracket } fn renderAsmOutput( diff --git a/src/astgen.zig b/src/astgen.zig index 939fba2ff3..903c385fef 100644 --- a/src/astgen.zig +++ b/src/astgen.zig @@ -848,7 +848,9 @@ pub fn blockExpr( const token_tags = tree.tokens.items(.tag); const lbrace = main_tokens[block_node]; - if (token_tags[lbrace - 1] == .colon) { + if (token_tags[lbrace - 1] == .colon and + token_tags[lbrace - 2] == .identifier) + { return labeledBlockExpr(mod, scope, rl, block_node, statements, .block); } From c760532be04667c8a8d49a7cf1b7582da3d1632d Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Sun, 7 Mar 2021 13:34:27 -0800 Subject: [PATCH 33/49] translate-c: Add compound literal support --- src/clang.zig | 5 +++++ src/translate_c.zig | 8 ++++++-- src/zig_clang.cpp | 5 +++++ src/zig_clang.h | 2 ++ test/run_translated_c.zig | 16 ++++++++++++++++ 5 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/clang.zig b/src/clang.zig index 854ee7dc7e..60afa28cf1 100644 --- a/src/clang.zig +++ b/src/clang.zig @@ -271,6 +271,11 @@ pub const CompoundAssignOperator = opaque { extern fn ZigClangCompoundAssignOperator_getRHS(*const CompoundAssignOperator) *const Expr; }; +pub const CompoundLiteralExpr = opaque { + pub const getInitializer = ZigClangCompoundLiteralExpr_getInitializer; + extern fn ZigClangCompoundLiteralExpr_getInitializer(*const CompoundLiteralExpr) *const Expr; +}; + pub const CompoundStmt = opaque { pub const body_begin = ZigClangCompoundStmt_body_begin; extern fn ZigClangCompoundStmt_body_begin(*const CompoundStmt) ConstBodyIterator; diff --git a/src/translate_c.zig b/src/translate_c.zig index 00b549b961..f0fd54eceb 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -1054,6 +1054,10 @@ fn transStmt( return maybeSuppressResult(c, scope, result_used, expr); }, .OffsetOfExprClass => return transOffsetOfExpr(c, scope, @ptrCast(*const clang.OffsetOfExpr, stmt), result_used), + .CompoundLiteralExprClass => { + const compound_literal = @ptrCast(*const clang.CompoundLiteralExpr, stmt); + return transExpr(c, scope, compound_literal.getInitializer(), result_used); + }, else => { return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "TODO implement translation of stmt class {s}", .{@tagName(sc)}); }, @@ -2560,8 +2564,8 @@ fn transConstantExpr(c: *Context, scope: *Scope, expr: *const clang.Expr, used: }); return maybeSuppressResult(c, scope, used, as_node); }, - else => { - return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "unsupported constant expression kind", .{}); + else => |kind| { + return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "unsupported constant expression kind '{s}'", .{kind}); }, } } diff --git a/src/zig_clang.cpp b/src/zig_clang.cpp index 0ddb597371..b789df0764 100644 --- a/src/zig_clang.cpp +++ b/src/zig_clang.cpp @@ -2808,6 +2808,11 @@ const struct ZigClangExpr *ZigClangCompoundAssignOperator_getRHS(const struct Zi return reinterpret_cast(casted->getRHS()); } +const struct ZigClangExpr *ZigClangCompoundLiteralExpr_getInitializer(const ZigClangCompoundLiteralExpr *self) { + auto casted = reinterpret_cast(self); + return reinterpret_cast(casted->getInitializer()); +} + enum ZigClangUO ZigClangUnaryOperator_getOpcode(const struct ZigClangUnaryOperator *self) { auto casted = reinterpret_cast(self); return (ZigClangUO)casted->getOpcode(); diff --git a/src/zig_clang.h b/src/zig_clang.h index b3717946ea..34e2d5afb7 100644 --- a/src/zig_clang.h +++ b/src/zig_clang.h @@ -1217,6 +1217,8 @@ ZIG_EXTERN_C enum ZigClangBO ZigClangCompoundAssignOperator_getOpcode(const stru ZIG_EXTERN_C const struct ZigClangExpr *ZigClangCompoundAssignOperator_getLHS(const struct ZigClangCompoundAssignOperator *); ZIG_EXTERN_C const struct ZigClangExpr *ZigClangCompoundAssignOperator_getRHS(const struct ZigClangCompoundAssignOperator *); +ZIG_EXTERN_C const struct ZigClangExpr *ZigClangCompoundLiteralExpr_getInitializer(const struct ZigClangCompoundLiteralExpr *); + ZIG_EXTERN_C enum ZigClangUO ZigClangUnaryOperator_getOpcode(const struct ZigClangUnaryOperator *); ZIG_EXTERN_C struct ZigClangQualType ZigClangUnaryOperator_getType(const struct ZigClangUnaryOperator *); ZIG_EXTERN_C const struct ZigClangExpr *ZigClangUnaryOperator_getSubExpr(const struct ZigClangUnaryOperator *); diff --git a/test/run_translated_c.zig b/test/run_translated_c.zig index 8b8a1bca19..01df88c852 100644 --- a/test/run_translated_c.zig +++ b/test/run_translated_c.zig @@ -1171,4 +1171,20 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { \\ return 0; \\} , ""); + + cases.add("Compound literals", + \\#include + \\struct Foo { + \\ int a; + \\ char b[2]; + \\ float c; + \\}; + \\int main() { + \\ struct Foo foo; + \\ int x = 1, y = 2; + \\ foo = (struct Foo) {x + y, {'a', 'b'}, 42.0f}; + \\ if (foo.a != x + y || foo.b[0] != 'a' || foo.b[1] != 'b' || foo.c != 42.0f) abort(); + \\ return 0; + \\} + , ""); } From 767eb2d4698203a11ee92618d9aff59d77984900 Mon Sep 17 00:00:00 2001 From: Loris Cro Date: Tue, 9 Mar 2021 22:15:01 +0100 Subject: [PATCH 34/49] update to latest fetch-them-macos-headers --- .../aarch64-macos-gnu/AvailabilityInternal.h | 10 +- .../aarch64-macos-gnu/AvailabilityMacros.h | 5 +- .../aarch64-macos-gnu/AvailabilityVersions.h | 4 + lib/libc/include/aarch64-macos-gnu/libproc.h | 187 ++++ .../aarch64-macos-gnu/mach/arm/vm_param.h | 4 + .../include/aarch64-macos-gnu/net/route.h | 257 ++++++ .../aarch64-macos-gnu/sys/_symbol_aliasing.h | 12 + .../aarch64-macos-gnu/sys/kern_control.h | 151 ++++ .../include/aarch64-macos-gnu/sys/proc_info.h | 799 ++++++++++++++++++ 9 files changed, 1422 insertions(+), 7 deletions(-) create mode 100644 lib/libc/include/aarch64-macos-gnu/libproc.h create mode 100644 lib/libc/include/aarch64-macos-gnu/net/route.h create mode 100644 lib/libc/include/aarch64-macos-gnu/sys/kern_control.h create mode 100644 lib/libc/include/aarch64-macos-gnu/sys/proc_info.h diff --git a/lib/libc/include/aarch64-macos-gnu/AvailabilityInternal.h b/lib/libc/include/aarch64-macos-gnu/AvailabilityInternal.h index 56de12636a..3b2b4c6b92 100644 --- a/lib/libc/include/aarch64-macos-gnu/AvailabilityInternal.h +++ b/lib/libc/include/aarch64-macos-gnu/AvailabilityInternal.h @@ -55,7 +55,7 @@ #ifdef __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ /* compiler sets __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ when -mtvos-version-min is used */ #define __TV_OS_VERSION_MIN_REQUIRED __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ - #define __TV_OS_VERSION_MAX_ALLOWED __TVOS_14_2 + #define __TV_OS_VERSION_MAX_ALLOWED __TVOS_14_3 /* for compatibility with existing code. New code should use platform specific checks */ #define __IPHONE_OS_VERSION_MIN_REQUIRED 90000 #endif @@ -65,7 +65,7 @@ #ifdef __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ /* compiler sets __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ when -mwatchos-version-min is used */ #define __WATCH_OS_VERSION_MIN_REQUIRED __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ - #define __WATCH_OS_VERSION_MAX_ALLOWED __WATCHOS_7_1 + #define __WATCH_OS_VERSION_MAX_ALLOWED __WATCHOS_7_2 /* for compatibility with existing code. New code should use platform specific checks */ #define __IPHONE_OS_VERSION_MIN_REQUIRED 90000 #endif @@ -75,7 +75,7 @@ #ifdef __ENVIRONMENT_BRIDGE_OS_VERSION_MIN_REQUIRED__ #define __BRIDGE_OS_VERSION_MIN_REQUIRED __ENVIRONMENT_BRIDGE_OS_VERSION_MIN_REQUIRED__ - #define __BRIDGE_OS_VERSION_MAX_ALLOWED 50000 + #define __BRIDGE_OS_VERSION_MAX_ALLOWED 50100 /* for compatibility with existing code. New code should use platform specific checks */ #define __IPHONE_OS_VERSION_MIN_REQUIRED 110000 #endif @@ -90,14 +90,14 @@ #ifdef __MAC_OS_X_VERSION_MIN_REQUIRED /* make sure a default max version is set */ #ifndef __MAC_OS_X_VERSION_MAX_ALLOWED - #define __MAC_OS_X_VERSION_MAX_ALLOWED __MAC_11_0 + #define __MAC_OS_X_VERSION_MAX_ALLOWED __MAC_11_1 #endif #endif /* __MAC_OS_X_VERSION_MIN_REQUIRED */ #ifdef __IPHONE_OS_VERSION_MIN_REQUIRED /* make sure a default max version is set */ #ifndef __IPHONE_OS_VERSION_MAX_ALLOWED - #define __IPHONE_OS_VERSION_MAX_ALLOWED __IPHONE_14_2 + #define __IPHONE_OS_VERSION_MAX_ALLOWED __IPHONE_14_3 #endif /* make sure a valid min is set */ #if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_2_0 diff --git a/lib/libc/include/aarch64-macos-gnu/AvailabilityMacros.h b/lib/libc/include/aarch64-macos-gnu/AvailabilityMacros.h index bd044a2a03..3903acfa5d 100644 --- a/lib/libc/include/aarch64-macos-gnu/AvailabilityMacros.h +++ b/lib/libc/include/aarch64-macos-gnu/AvailabilityMacros.h @@ -118,6 +118,7 @@ #define MAC_OS_X_VERSION_10_14_4 101404 #define MAC_OS_X_VERSION_10_15 101500 #define MAC_OS_VERSION_11_0 110000 +#define MAC_OS_VERSION_11_1 110100 /* * If min OS not specified, assume 10.4 for intel @@ -144,10 +145,10 @@ * if max OS not specified, assume larger of (10.15, min) */ #ifndef MAC_OS_X_VERSION_MAX_ALLOWED - #if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_VERSION_11_0 + #if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_VERSION_11_1 #define MAC_OS_X_VERSION_MAX_ALLOWED MAC_OS_X_VERSION_MIN_REQUIRED #else - #define MAC_OS_X_VERSION_MAX_ALLOWED MAC_OS_VERSION_11_0 + #define MAC_OS_X_VERSION_MAX_ALLOWED MAC_OS_VERSION_11_1 #endif #endif diff --git a/lib/libc/include/aarch64-macos-gnu/AvailabilityVersions.h b/lib/libc/include/aarch64-macos-gnu/AvailabilityVersions.h index 265379fb61..694fa9cb3e 100644 --- a/lib/libc/include/aarch64-macos-gnu/AvailabilityVersions.h +++ b/lib/libc/include/aarch64-macos-gnu/AvailabilityVersions.h @@ -58,6 +58,7 @@ #define __MAC_10_15_4 101504 #define __MAC_10_16 101600 #define __MAC_11_0 110000 +#define __MAC_11_1 110100 /* __MAC_NA is not defined to a value but is used as a token by macros to indicate that the API is unavailable */ #define __IPHONE_2_0 20000 @@ -110,6 +111,7 @@ #define __IPHONE_14_0 140000 #define __IPHONE_14_1 140100 #define __IPHONE_14_2 140200 +#define __IPHONE_14_3 140300 /* __IPHONE_NA is not defined to a value but is used as a token by macros to indicate that the API is unavailable */ #define __TVOS_9_0 90000 @@ -136,6 +138,7 @@ #define __TVOS_14_0 140000 #define __TVOS_14_1 140100 #define __TVOS_14_2 140200 +#define __TVOS_14_3 140300 #define __WATCHOS_1_0 10000 #define __WATCHOS_2_0 20000 @@ -158,6 +161,7 @@ #define __WATCHOS_6_2 60200 #define __WATCHOS_7_0 70000 #define __WATCHOS_7_1 70100 +#define __WATCHOS_7_2 70200 /* * Set up standard Mac OS X versions diff --git a/lib/libc/include/aarch64-macos-gnu/libproc.h b/lib/libc/include/aarch64-macos-gnu/libproc.h new file mode 100644 index 0000000000..de2c766c28 --- /dev/null +++ b/lib/libc/include/aarch64-macos-gnu/libproc.h @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2006, 2007, 2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#ifndef _LIBPROC_H_ +#define _LIBPROC_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for audit_token_t */ + +#include + +#include +#include + +/* + * This header file contains private interfaces to obtain process information. + * These interfaces are subject to change in future releases. + */ + +/*! + * @define PROC_LISTPIDSPATH_PATH_IS_VOLUME + * @discussion This flag indicates that all processes that hold open + * file references on the volume associated with the specified + * path should be returned. + */ +#define PROC_LISTPIDSPATH_PATH_IS_VOLUME 1 + + +/*! + * @define PROC_LISTPIDSPATH_EXCLUDE_EVTONLY + * @discussion This flag indicates that file references that were opened + * with the O_EVTONLY flag should be excluded from the matching + * criteria. + */ +#define PROC_LISTPIDSPATH_EXCLUDE_EVTONLY 2 + +__BEGIN_DECLS + + +/*! + * @function proc_listpidspath + * @discussion A function which will search through the current + * processes looking for open file references which match + * a specified path or volume. + * @param type types of processes to be searched (see proc_listpids) + * @param typeinfo adjunct information for type + * @param path file or volume path + * @param pathflags flags to control which files should be considered + * during the process search. + * @param buffer a C array of int-sized values to be filled with + * process identifiers that hold an open file reference + * matching the specified path or volume. Pass NULL to + * obtain the minimum buffer size needed to hold the + * currently active processes. + * @param buffersize the size (in bytes) of the provided buffer. + * @result the number of bytes of data returned in the provided buffer; + * -1 if an error was encountered; + */ +int proc_listpidspath(uint32_t type, + uint32_t typeinfo, + const char *path, + uint32_t pathflags, + void *buffer, + int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); + +int proc_listpids(uint32_t type, uint32_t typeinfo, void *buffer, int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +int proc_listallpids(void * buffer, int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_1); +int proc_listpgrppids(pid_t pgrpid, void * buffer, int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_1); +int proc_listchildpids(pid_t ppid, void * buffer, int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_1); +int proc_pidinfo(int pid, int flavor, uint64_t arg, void *buffer, int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +int proc_pidfdinfo(int pid, int fd, int flavor, void * buffer, int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +int proc_pidfileportinfo(int pid, uint32_t fileport, int flavor, void *buffer, int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3); +int proc_name(int pid, void * buffer, uint32_t buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +int proc_regionfilename(int pid, uint64_t address, void * buffer, uint32_t buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +int proc_kmsgbuf(void * buffer, uint32_t buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +int proc_pidpath(int pid, void * buffer, uint32_t buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +int proc_pidpath_audittoken(audit_token_t *audittoken, void * buffer, uint32_t buffersize) API_AVAILABLE(macos(11.0), ios(14.0), watchos(7.0), tvos(14.0)); +int proc_libversion(int *major, int * minor) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); + +/* + * Return resource usage information for the given pid, which can be a live process or a zombie. + * + * Returns 0 on success; or -1 on failure, with errno set to indicate the specific error. + */ +int proc_pid_rusage(int pid, int flavor, rusage_info_t *buffer) __OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0); + +/* + * A process can use the following api to set its own process control + * state on resoure starvation. The argument can have one of the PROC_SETPC_XX values + */ +#define PROC_SETPC_NONE 0 +#define PROC_SETPC_THROTTLEMEM 1 +#define PROC_SETPC_SUSPEND 2 +#define PROC_SETPC_TERMINATE 3 + +int proc_setpcontrol(const int control) __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2); +int proc_setpcontrol(const int control); + +int proc_track_dirty(pid_t pid, uint32_t flags); +int proc_set_dirty(pid_t pid, bool dirty); +int proc_get_dirty(pid_t pid, uint32_t *flags); +int proc_clear_dirty(pid_t pid, uint32_t flags); + +int proc_terminate(pid_t pid, int *sig); + +/* + * NO_SMT means that on an SMT CPU, this thread must be scheduled alone, + * with the paired CPU idle. + * + * Set NO_SMT on the current proc (all existing and future threads) + * This attribute is inherited on fork and exec + */ +int proc_set_no_smt(void) __API_AVAILABLE(macos(11.0)); + +/* Set NO_SMT on the current thread */ +int proc_setthread_no_smt(void) __API_AVAILABLE(macos(11.0)); + +/* + * CPU Security Mitigation APIs + * + * Set CPU security mitigation on the current proc (all existing and future threads) + * This attribute is inherited on fork and exec + */ +int proc_set_csm(uint32_t flags) __API_AVAILABLE(macos(11.0)); + +/* Set CPU security mitigation on the current thread */ +int proc_setthread_csm(uint32_t flags) __API_AVAILABLE(macos(11.0)); + +/* + * flags for CPU Security Mitigation APIs + * PROC_CSM_ALL should be used in most cases, + * the individual flags are provided only for performance evaluation etc + */ +#define PROC_CSM_ALL 0x0001 /* Set all available mitigations */ +#define PROC_CSM_NOSMT 0x0002 /* Set NO_SMT - see above */ +#define PROC_CSM_TECS 0x0004 /* Execute VERW on every return to user mode */ + +#ifdef PRIVATE +#include +/* + * Enumerate potential userspace pointers embedded in kernel data structures. + * Currently inspects kqueues only. + * + * NOTE: returned "pointers" are opaque user-supplied values and thus not + * guaranteed to address valid objects or be pointers at all. + * + * Returns the number of pointers found (which may exceed buffersize), or -1 on + * failure and errno set appropriately. + */ +int proc_list_uptrs(pid_t pid, uint64_t *buffer, uint32_t buffersize); + +int proc_list_dynkqueueids(int pid, kqueue_id_t *buf, uint32_t bufsz); +int proc_piddynkqueueinfo(int pid, int flavor, kqueue_id_t kq_id, void *buffer, + int buffersize); +#endif /* PRIVATE */ + +int proc_udata_info(int pid, int flavor, void *buffer, int buffersize); + +__END_DECLS + +#endif /*_LIBPROC_H_ */ \ No newline at end of file diff --git a/lib/libc/include/aarch64-macos-gnu/mach/arm/vm_param.h b/lib/libc/include/aarch64-macos-gnu/mach/arm/vm_param.h index 75db0423d4..1738a03517 100644 --- a/lib/libc/include/aarch64-macos-gnu/mach/arm/vm_param.h +++ b/lib/libc/include/aarch64-macos-gnu/mach/arm/vm_param.h @@ -87,6 +87,10 @@ #define MACH_VM_MIN_ADDRESS ((mach_vm_offset_t) MACH_VM_MIN_ADDRESS_RAW) #define MACH_VM_MAX_ADDRESS ((mach_vm_offset_t) MACH_VM_MAX_ADDRESS_RAW) +#define MACH_VM_MIN_GPU_CARVEOUT_ADDRESS_RAW 0x0000001000000000ULL +#define MACH_VM_MAX_GPU_CARVEOUT_ADDRESS_RAW 0x0000007000000000ULL +#define MACH_VM_MIN_GPU_CARVEOUT_ADDRESS ((mach_vm_offset_t) MACH_VM_MIN_GPU_CARVEOUT_ADDRESS_RAW) +#define MACH_VM_MAX_GPU_CARVEOUT_ADDRESS ((mach_vm_offset_t) MACH_VM_MAX_GPU_CARVEOUT_ADDRESS_RAW) #else /* defined(__arm64__) */ #error architecture not supported diff --git a/lib/libc/include/aarch64-macos-gnu/net/route.h b/lib/libc/include/aarch64-macos-gnu/net/route.h new file mode 100644 index 0000000000..ae2f5013e9 --- /dev/null +++ b/lib/libc/include/aarch64-macos-gnu/net/route.h @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2000-2017 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)route.h 8.3 (Berkeley) 4/19/94 + * $FreeBSD: src/sys/net/route.h,v 1.36.2.1 2000/08/16 06:14:23 jayanth Exp $ + */ + +#ifndef _NET_ROUTE_H_ +#define _NET_ROUTE_H_ +#include +#include +#include +#include + +/* + * These numbers are used by reliable protocols for determining + * retransmission behavior and are included in the routing structure. + */ +struct rt_metrics { + u_int32_t rmx_locks; /* Kernel leaves these values alone */ + u_int32_t rmx_mtu; /* MTU for this path */ + u_int32_t rmx_hopcount; /* max hops expected */ + int32_t rmx_expire; /* lifetime for route, e.g. redirect */ + u_int32_t rmx_recvpipe; /* inbound delay-bandwidth product */ + u_int32_t rmx_sendpipe; /* outbound delay-bandwidth product */ + u_int32_t rmx_ssthresh; /* outbound gateway buffer limit */ + u_int32_t rmx_rtt; /* estimated round trip time */ + u_int32_t rmx_rttvar; /* estimated rtt variance */ + u_int32_t rmx_pksent; /* packets sent using this route */ + u_int32_t rmx_state; /* route state */ + u_int32_t rmx_filler[3]; /* will be used for TCP's peer-MSS cache */ +}; + +/* + * rmx_rtt and rmx_rttvar are stored as microseconds; + */ +#define RTM_RTTUNIT 1000000 /* units for rtt, rttvar, as units per sec */ + + + +#define RTF_UP 0x1 /* route usable */ +#define RTF_GATEWAY 0x2 /* destination is a gateway */ +#define RTF_HOST 0x4 /* host entry (net otherwise) */ +#define RTF_REJECT 0x8 /* host or net unreachable */ +#define RTF_DYNAMIC 0x10 /* created dynamically (by redirect) */ +#define RTF_MODIFIED 0x20 /* modified dynamically (by redirect) */ +#define RTF_DONE 0x40 /* message confirmed */ +#define RTF_DELCLONE 0x80 /* delete cloned route */ +#define RTF_CLONING 0x100 /* generate new routes on use */ +#define RTF_XRESOLVE 0x200 /* external daemon resolves name */ +#define RTF_LLINFO 0x400 /* DEPRECATED - exists ONLY for backward + * compatibility */ +#define RTF_LLDATA 0x400 /* used by apps to add/del L2 entries */ +#define RTF_STATIC 0x800 /* manually added */ +#define RTF_BLACKHOLE 0x1000 /* just discard pkts (during updates) */ +#define RTF_NOIFREF 0x2000 /* not eligible for RTF_IFREF */ +#define RTF_PROTO2 0x4000 /* protocol specific routing flag */ +#define RTF_PROTO1 0x8000 /* protocol specific routing flag */ + +#define RTF_PRCLONING 0x10000 /* protocol requires cloning */ +#define RTF_WASCLONED 0x20000 /* route generated through cloning */ +#define RTF_PROTO3 0x40000 /* protocol specific routing flag */ + /* 0x80000 unused */ +#define RTF_PINNED 0x100000 /* future use */ +#define RTF_LOCAL 0x200000 /* route represents a local address */ +#define RTF_BROADCAST 0x400000 /* route represents a bcast address */ +#define RTF_MULTICAST 0x800000 /* route represents a mcast address */ +#define RTF_IFSCOPE 0x1000000 /* has valid interface scope */ +#define RTF_CONDEMNED 0x2000000 /* defunct; no longer modifiable */ +#define RTF_IFREF 0x4000000 /* route holds a ref to interface */ +#define RTF_PROXY 0x8000000 /* proxying, no interface scope */ +#define RTF_ROUTER 0x10000000 /* host is a router */ +#define RTF_DEAD 0x20000000 /* Route entry is being freed */ + /* 0x40000000 and up unassigned */ + +#define RTPRF_OURS RTF_PROTO3 /* set on routes we manage */ +#define RTF_BITS \ + "\020\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE" \ + "\10DELCLONE\11CLONING\12XRESOLVE\13LLINFO\14STATIC\15BLACKHOLE" \ + "\16NOIFREF\17PROTO2\20PROTO1\21PRCLONING\22WASCLONED\23PROTO3" \ + "\25PINNED\26LOCAL\27BROADCAST\30MULTICAST\31IFSCOPE\32CONDEMNED" \ + "\33IFREF\34PROXY\35ROUTER" + +#define IS_DIRECT_HOSTROUTE(rt) \ + (((rt)->rt_flags & (RTF_HOST | RTF_GATEWAY)) == RTF_HOST) +/* + * Routing statistics. + */ +struct rtstat { + short rts_badredirect; /* bogus redirect calls */ + short rts_dynamic; /* routes created by redirects */ + short rts_newgateway; /* routes modified by redirects */ + short rts_unreach; /* lookups which failed */ + short rts_wildcard; /* lookups satisfied by a wildcard */ + short rts_badrtgwroute; /* route to gateway is not direct */ +}; + +/* + * Structures for routing messages. + */ +struct rt_msghdr { + u_short rtm_msglen; /* to skip over non-understood messages */ + u_char rtm_version; /* future binary compatibility */ + u_char rtm_type; /* message type */ + u_short rtm_index; /* index for associated ifp */ + int rtm_flags; /* flags, incl. kern & message, e.g. DONE */ + int rtm_addrs; /* bitmask identifying sockaddrs in msg */ + pid_t rtm_pid; /* identify sender */ + int rtm_seq; /* for sender to identify action */ + int rtm_errno; /* why failed */ + int rtm_use; /* from rtentry */ + u_int32_t rtm_inits; /* which metrics we are initializing */ + struct rt_metrics rtm_rmx; /* metrics themselves */ +}; + +struct rt_msghdr2 { + u_short rtm_msglen; /* to skip over non-understood messages */ + u_char rtm_version; /* future binary compatibility */ + u_char rtm_type; /* message type */ + u_short rtm_index; /* index for associated ifp */ + int rtm_flags; /* flags, incl. kern & message, e.g. DONE */ + int rtm_addrs; /* bitmask identifying sockaddrs in msg */ + int32_t rtm_refcnt; /* reference count */ + int rtm_parentflags; /* flags of the parent route */ + int rtm_reserved; /* reserved field set to 0 */ + int rtm_use; /* from rtentry */ + u_int32_t rtm_inits; /* which metrics we are initializing */ + struct rt_metrics rtm_rmx; /* metrics themselves */ +}; + + +#define RTM_VERSION 5 /* Up the ante and ignore older versions */ + +/* + * Message types. + */ +#define RTM_ADD 0x1 /* Add Route */ +#define RTM_DELETE 0x2 /* Delete Route */ +#define RTM_CHANGE 0x3 /* Change Metrics or flags */ +#define RTM_GET 0x4 /* Report Metrics */ +#define RTM_LOSING 0x5 /* RTM_LOSING is no longer generated by xnu + * and is deprecated */ +#define RTM_REDIRECT 0x6 /* Told to use different route */ +#define RTM_MISS 0x7 /* Lookup failed on this address */ +#define RTM_LOCK 0x8 /* fix specified metrics */ +#define RTM_OLDADD 0x9 /* caused by SIOCADDRT */ +#define RTM_OLDDEL 0xa /* caused by SIOCDELRT */ +#define RTM_RESOLVE 0xb /* req to resolve dst to LL addr */ +#define RTM_NEWADDR 0xc /* address being added to iface */ +#define RTM_DELADDR 0xd /* address being removed from iface */ +#define RTM_IFINFO 0xe /* iface going up/down etc. */ +#define RTM_NEWMADDR 0xf /* mcast group membership being added to if */ +#define RTM_DELMADDR 0x10 /* mcast group membership being deleted */ +#define RTM_IFINFO2 0x12 /* */ +#define RTM_NEWMADDR2 0x13 /* */ +#define RTM_GET2 0x14 /* */ + +/* + * Bitmask values for rtm_inits and rmx_locks. + */ +#define RTV_MTU 0x1 /* init or lock _mtu */ +#define RTV_HOPCOUNT 0x2 /* init or lock _hopcount */ +#define RTV_EXPIRE 0x4 /* init or lock _expire */ +#define RTV_RPIPE 0x8 /* init or lock _recvpipe */ +#define RTV_SPIPE 0x10 /* init or lock _sendpipe */ +#define RTV_SSTHRESH 0x20 /* init or lock _ssthresh */ +#define RTV_RTT 0x40 /* init or lock _rtt */ +#define RTV_RTTVAR 0x80 /* init or lock _rttvar */ + +/* + * Bitmask values for rtm_addrs. + */ +#define RTA_DST 0x1 /* destination sockaddr present */ +#define RTA_GATEWAY 0x2 /* gateway sockaddr present */ +#define RTA_NETMASK 0x4 /* netmask sockaddr present */ +#define RTA_GENMASK 0x8 /* cloning mask sockaddr present */ +#define RTA_IFP 0x10 /* interface name sockaddr present */ +#define RTA_IFA 0x20 /* interface addr sockaddr present */ +#define RTA_AUTHOR 0x40 /* sockaddr for author of redirect */ +#define RTA_BRD 0x80 /* for NEWADDR, broadcast or p-p dest addr */ + +/* + * Index offsets for sockaddr array for alternate internal encoding. + */ +#define RTAX_DST 0 /* destination sockaddr present */ +#define RTAX_GATEWAY 1 /* gateway sockaddr present */ +#define RTAX_NETMASK 2 /* netmask sockaddr present */ +#define RTAX_GENMASK 3 /* cloning mask sockaddr present */ +#define RTAX_IFP 4 /* interface name sockaddr present */ +#define RTAX_IFA 5 /* interface addr sockaddr present */ +#define RTAX_AUTHOR 6 /* sockaddr for author of redirect */ +#define RTAX_BRD 7 /* for NEWADDR, broadcast or p-p dest addr */ +#define RTAX_MAX 8 /* size of array to allocate */ + +struct rt_addrinfo { + int rti_addrs; + struct sockaddr *rti_info[RTAX_MAX]; +}; + + +#endif /* _NET_ROUTE_H_ */ \ No newline at end of file diff --git a/lib/libc/include/aarch64-macos-gnu/sys/_symbol_aliasing.h b/lib/libc/include/aarch64-macos-gnu/sys/_symbol_aliasing.h index 151156d6e1..b78874bf26 100644 --- a/lib/libc/include/aarch64-macos-gnu/sys/_symbol_aliasing.h +++ b/lib/libc/include/aarch64-macos-gnu/sys/_symbol_aliasing.h @@ -329,6 +329,12 @@ #define __DARWIN_ALIAS_STARTING_IPHONE___IPHONE_14_2(x) #endif +#if defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) && __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 140300 +#define __DARWIN_ALIAS_STARTING_IPHONE___IPHONE_14_3(x) x +#else +#define __DARWIN_ALIAS_STARTING_IPHONE___IPHONE_14_3(x) +#endif + #if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1000 #define __DARWIN_ALIAS_STARTING_MAC___MAC_10_0(x) x #else @@ -531,4 +537,10 @@ #define __DARWIN_ALIAS_STARTING_MAC___MAC_11_0(x) x #else #define __DARWIN_ALIAS_STARTING_MAC___MAC_11_0(x) +#endif + +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 110100 +#define __DARWIN_ALIAS_STARTING_MAC___MAC_11_1(x) x +#else +#define __DARWIN_ALIAS_STARTING_MAC___MAC_11_1(x) #endif \ No newline at end of file diff --git a/lib/libc/include/aarch64-macos-gnu/sys/kern_control.h b/lib/libc/include/aarch64-macos-gnu/sys/kern_control.h new file mode 100644 index 0000000000..a9cc866d88 --- /dev/null +++ b/lib/libc/include/aarch64-macos-gnu/sys/kern_control.h @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2000-2004, 2012-2016 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/*! + * @header kern_control.h + * This header defines an API to communicate between a kernel + * extension and a process outside of the kernel. + */ + +#ifndef KPI_KERN_CONTROL_H +#define KPI_KERN_CONTROL_H + + +#include +#include +#include +#include + +/* + * Define Controller event subclass, and associated events. + * Subclass of KEV_SYSTEM_CLASS + */ + +/*! + * @defined KEV_CTL_SUBCLASS + * @discussion The kernel event subclass for kernel control events. + */ +#define KEV_CTL_SUBCLASS 2 + +/*! + * @defined KEV_CTL_REGISTERED + * @discussion The event code indicating a new controller was + * registered. The data portion will contain a ctl_event_data. + */ +#define KEV_CTL_REGISTERED 1 /* a new controller appears */ + +/*! + * @defined KEV_CTL_DEREGISTERED + * @discussion The event code indicating a controller was unregistered. + * The data portion will contain a ctl_event_data. + */ +#define KEV_CTL_DEREGISTERED 2 /* a controller disappears */ + +/*! + * @struct ctl_event_data + * @discussion This structure is used for KEV_CTL_SUBCLASS kernel + * events. + * @field ctl_id The kernel control id. + * @field ctl_unit The kernel control unit. + */ +struct ctl_event_data { + u_int32_t ctl_id; /* Kernel Controller ID */ + u_int32_t ctl_unit; +}; + +/* + * Controls destined to the Controller Manager. + */ + +/*! + * @defined CTLIOCGCOUNT + * @discussion The CTLIOCGCOUNT ioctl can be used to determine the + * number of kernel controllers registered. + */ +#define CTLIOCGCOUNT _IOR('N', 2, int) /* get number of control structures registered */ + +/*! + * @defined CTLIOCGINFO + * @discussion The CTLIOCGINFO ioctl can be used to convert a kernel + * control name to a kernel control id. + */ +#define CTLIOCGINFO _IOWR('N', 3, struct ctl_info) /* get id from name */ + + +/*! + * @defined MAX_KCTL_NAME + * @discussion Kernel control names must be no longer than + * MAX_KCTL_NAME. + */ +#define MAX_KCTL_NAME 96 + +/* + * Controls destined to the Controller Manager. + */ + +/*! + * @struct ctl_info + * @discussion This structure is used with the CTLIOCGINFO ioctl to + * translate from a kernel control name to a control id. + * @field ctl_id The kernel control id, filled out upon return. + * @field ctl_name The kernel control name to find. + */ +struct ctl_info { + u_int32_t ctl_id; /* Kernel Controller ID */ + char ctl_name[MAX_KCTL_NAME]; /* Kernel Controller Name (a C string) */ +}; + + +/*! + * @struct sockaddr_ctl + * @discussion The controller address structure is used to establish + * contact between a user client and a kernel controller. The + * sc_id/sc_unit uniquely identify each controller. sc_id is a + * unique identifier assigned to the controller. The identifier can + * be assigned by the system at registration time or be a 32-bit + * creator code obtained from Apple Computer. sc_unit is a unit + * number for this sc_id, and is privately used by the kernel + * controller to identify several instances of the controller. + * @field sc_len The length of the structure. + * @field sc_family AF_SYSTEM. + * @field ss_sysaddr AF_SYS_KERNCONTROL. + * @field sc_id Controller unique identifier. + * @field sc_unit Kernel controller private unit number. + * @field sc_reserved Reserved, must be set to zero. + */ +struct sockaddr_ctl { + u_char sc_len; /* depends on size of bundle ID string */ + u_char sc_family; /* AF_SYSTEM */ + u_int16_t ss_sysaddr; /* AF_SYS_KERNCONTROL */ + u_int32_t sc_id; /* Controller unique identifier */ + u_int32_t sc_unit; /* Developer private unit number */ + u_int32_t sc_reserved[5]; +}; + + + +#endif /* KPI_KERN_CONTROL_H */ \ No newline at end of file diff --git a/lib/libc/include/aarch64-macos-gnu/sys/proc_info.h b/lib/libc/include/aarch64-macos-gnu/sys/proc_info.h new file mode 100644 index 0000000000..8c6b668afb --- /dev/null +++ b/lib/libc/include/aarch64-macos-gnu/sys/proc_info.h @@ -0,0 +1,799 @@ +/* + * Copyright (c) 2005-2020 Apple Computer, Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#ifndef _SYS_PROC_INFO_H +#define _SYS_PROC_INFO_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +__BEGIN_DECLS + + +#define PROC_ALL_PIDS 1 +#define PROC_PGRP_ONLY 2 +#define PROC_TTY_ONLY 3 +#define PROC_UID_ONLY 4 +#define PROC_RUID_ONLY 5 +#define PROC_PPID_ONLY 6 +#define PROC_KDBG_ONLY 7 + +struct proc_bsdinfo { + uint32_t pbi_flags; /* 64bit; emulated etc */ + uint32_t pbi_status; + uint32_t pbi_xstatus; + uint32_t pbi_pid; + uint32_t pbi_ppid; + uid_t pbi_uid; + gid_t pbi_gid; + uid_t pbi_ruid; + gid_t pbi_rgid; + uid_t pbi_svuid; + gid_t pbi_svgid; + uint32_t rfu_1; /* reserved */ + char pbi_comm[MAXCOMLEN]; + char pbi_name[2 * MAXCOMLEN]; /* empty if no name is registered */ + uint32_t pbi_nfiles; + uint32_t pbi_pgid; + uint32_t pbi_pjobc; + uint32_t e_tdev; /* controlling tty dev */ + uint32_t e_tpgid; /* tty process group id */ + int32_t pbi_nice; + uint64_t pbi_start_tvsec; + uint64_t pbi_start_tvusec; +}; + + +struct proc_bsdshortinfo { + uint32_t pbsi_pid; /* process id */ + uint32_t pbsi_ppid; /* process parent id */ + uint32_t pbsi_pgid; /* process perp id */ + uint32_t pbsi_status; /* p_stat value, SZOMB, SRUN, etc */ + char pbsi_comm[MAXCOMLEN]; /* upto 16 characters of process name */ + uint32_t pbsi_flags; /* 64bit; emulated etc */ + uid_t pbsi_uid; /* current uid on process */ + gid_t pbsi_gid; /* current gid on process */ + uid_t pbsi_ruid; /* current ruid on process */ + gid_t pbsi_rgid; /* current tgid on process */ + uid_t pbsi_svuid; /* current svuid on process */ + gid_t pbsi_svgid; /* current svgid on process */ + uint32_t pbsi_rfu; /* reserved for future use*/ +}; + + + + +/* pbi_flags values */ +#define PROC_FLAG_SYSTEM 1 /* System process */ +#define PROC_FLAG_TRACED 2 /* process currently being traced, possibly by gdb */ +#define PROC_FLAG_INEXIT 4 /* process is working its way in exit() */ +#define PROC_FLAG_PPWAIT 8 +#define PROC_FLAG_LP64 0x10 /* 64bit process */ +#define PROC_FLAG_SLEADER 0x20 /* The process is the session leader */ +#define PROC_FLAG_CTTY 0x40 /* process has a control tty */ +#define PROC_FLAG_CONTROLT 0x80 /* Has a controlling terminal */ +#define PROC_FLAG_THCWD 0x100 /* process has a thread with cwd */ +/* process control bits for resource starvation */ +#define PROC_FLAG_PC_THROTTLE 0x200 /* In resource starvation situations, this process is to be throttled */ +#define PROC_FLAG_PC_SUSP 0x400 /* In resource starvation situations, this process is to be suspended */ +#define PROC_FLAG_PC_KILL 0x600 /* In resource starvation situations, this process is to be terminated */ +#define PROC_FLAG_PC_MASK 0x600 +/* process action bits for resource starvation */ +#define PROC_FLAG_PA_THROTTLE 0x800 /* The process is currently throttled due to resource starvation */ +#define PROC_FLAG_PA_SUSP 0x1000 /* The process is currently suspended due to resource starvation */ +#define PROC_FLAG_PSUGID 0x2000 /* process has set privileges since last exec */ +#define PROC_FLAG_EXEC 0x4000 /* process has called exec */ + + +struct proc_taskinfo { + uint64_t pti_virtual_size; /* virtual memory size (bytes) */ + uint64_t pti_resident_size; /* resident memory size (bytes) */ + uint64_t pti_total_user; /* total time */ + uint64_t pti_total_system; + uint64_t pti_threads_user; /* existing threads only */ + uint64_t pti_threads_system; + int32_t pti_policy; /* default policy for new threads */ + int32_t pti_faults; /* number of page faults */ + int32_t pti_pageins; /* number of actual pageins */ + int32_t pti_cow_faults; /* number of copy-on-write faults */ + int32_t pti_messages_sent; /* number of messages sent */ + int32_t pti_messages_received; /* number of messages received */ + int32_t pti_syscalls_mach; /* number of mach system calls */ + int32_t pti_syscalls_unix; /* number of unix system calls */ + int32_t pti_csw; /* number of context switches */ + int32_t pti_threadnum; /* number of threads in the task */ + int32_t pti_numrunning; /* number of running threads */ + int32_t pti_priority; /* task priority*/ +}; + +struct proc_taskallinfo { + struct proc_bsdinfo pbsd; + struct proc_taskinfo ptinfo; +}; + +#define MAXTHREADNAMESIZE 64 + +struct proc_threadinfo { + uint64_t pth_user_time; /* user run time */ + uint64_t pth_system_time; /* system run time */ + int32_t pth_cpu_usage; /* scaled cpu usage percentage */ + int32_t pth_policy; /* scheduling policy in effect */ + int32_t pth_run_state; /* run state (see below) */ + int32_t pth_flags; /* various flags (see below) */ + int32_t pth_sleep_time; /* number of seconds that thread */ + int32_t pth_curpri; /* cur priority*/ + int32_t pth_priority; /* priority*/ + int32_t pth_maxpriority; /* max priority*/ + char pth_name[MAXTHREADNAMESIZE]; /* thread name, if any */ +}; + +struct proc_regioninfo { + uint32_t pri_protection; + uint32_t pri_max_protection; + uint32_t pri_inheritance; + uint32_t pri_flags; /* shared, external pager, is submap */ + uint64_t pri_offset; + uint32_t pri_behavior; + uint32_t pri_user_wired_count; + uint32_t pri_user_tag; + uint32_t pri_pages_resident; + uint32_t pri_pages_shared_now_private; + uint32_t pri_pages_swapped_out; + uint32_t pri_pages_dirtied; + uint32_t pri_ref_count; + uint32_t pri_shadow_depth; + uint32_t pri_share_mode; + uint32_t pri_private_pages_resident; + uint32_t pri_shared_pages_resident; + uint32_t pri_obj_id; + uint32_t pri_depth; + uint64_t pri_address; + uint64_t pri_size; +}; + +#define PROC_REGION_SUBMAP 1 +#define PROC_REGION_SHARED 2 + +#define SM_COW 1 +#define SM_PRIVATE 2 +#define SM_EMPTY 3 +#define SM_SHARED 4 +#define SM_TRUESHARED 5 +#define SM_PRIVATE_ALIASED 6 +#define SM_SHARED_ALIASED 7 +#define SM_LARGE_PAGE 8 + + +/* + * Thread run states (state field). + */ + +#define TH_STATE_RUNNING 1 /* thread is running normally */ +#define TH_STATE_STOPPED 2 /* thread is stopped */ +#define TH_STATE_WAITING 3 /* thread is waiting normally */ +#define TH_STATE_UNINTERRUPTIBLE 4 /* thread is in an uninterruptible + * wait */ +#define TH_STATE_HALTED 5 /* thread is halted at a + * clean point */ + +/* + * Thread flags (flags field). + */ +#define TH_FLAGS_SWAPPED 0x1 /* thread is swapped out */ +#define TH_FLAGS_IDLE 0x2 /* thread is an idle thread */ + + +struct proc_workqueueinfo { + uint32_t pwq_nthreads; /* total number of workqueue threads */ + uint32_t pwq_runthreads; /* total number of running workqueue threads */ + uint32_t pwq_blockedthreads; /* total number of blocked workqueue threads */ + uint32_t pwq_state; +}; + +/* + * workqueue state (pwq_state field) + */ +#define WQ_EXCEEDED_CONSTRAINED_THREAD_LIMIT 0x1 +#define WQ_EXCEEDED_TOTAL_THREAD_LIMIT 0x2 +#define WQ_FLAGS_AVAILABLE 0x4 + +struct proc_fileinfo { + uint32_t fi_openflags; + uint32_t fi_status; + off_t fi_offset; + int32_t fi_type; + uint32_t fi_guardflags; +}; + +/* stats flags in proc_fileinfo */ +#define PROC_FP_SHARED 1 /* shared by more than one fd */ +#define PROC_FP_CLEXEC 2 /* close on exec */ +#define PROC_FP_GUARDED 4 /* guarded fd */ +#define PROC_FP_CLFORK 8 /* close on fork */ + +#define PROC_FI_GUARD_CLOSE (1u << 0) +#define PROC_FI_GUARD_DUP (1u << 1) +#define PROC_FI_GUARD_SOCKET_IPC (1u << 2) +#define PROC_FI_GUARD_FILEPORT (1u << 3) + +struct proc_exitreasonbasicinfo { + uint32_t beri_namespace; + uint64_t beri_code; + uint64_t beri_flags; + uint32_t beri_reason_buf_size; +} __attribute__((packed)); + +struct proc_exitreasoninfo { + uint32_t eri_namespace; + uint64_t eri_code; + uint64_t eri_flags; + uint32_t eri_reason_buf_size; + uint64_t eri_kcd_buf; +} __attribute__((packed)); + +/* + * A copy of stat64 with static sized fields. + */ +struct vinfo_stat { + uint32_t vst_dev; /* [XSI] ID of device containing file */ + uint16_t vst_mode; /* [XSI] Mode of file (see below) */ + uint16_t vst_nlink; /* [XSI] Number of hard links */ + uint64_t vst_ino; /* [XSI] File serial number */ + uid_t vst_uid; /* [XSI] User ID of the file */ + gid_t vst_gid; /* [XSI] Group ID of the file */ + int64_t vst_atime; /* [XSI] Time of last access */ + int64_t vst_atimensec; /* nsec of last access */ + int64_t vst_mtime; /* [XSI] Last data modification time */ + int64_t vst_mtimensec; /* last data modification nsec */ + int64_t vst_ctime; /* [XSI] Time of last status change */ + int64_t vst_ctimensec; /* nsec of last status change */ + int64_t vst_birthtime; /* File creation time(birth) */ + int64_t vst_birthtimensec; /* nsec of File creation time */ + off_t vst_size; /* [XSI] file size, in bytes */ + int64_t vst_blocks; /* [XSI] blocks allocated for file */ + int32_t vst_blksize; /* [XSI] optimal blocksize for I/O */ + uint32_t vst_flags; /* user defined flags for file */ + uint32_t vst_gen; /* file generation number */ + uint32_t vst_rdev; /* [XSI] Device ID */ + int64_t vst_qspare[2]; /* RESERVED: DO NOT USE! */ +}; + +struct vnode_info { + struct vinfo_stat vi_stat; + int vi_type; + int vi_pad; + fsid_t vi_fsid; +}; + +struct vnode_info_path { + struct vnode_info vip_vi; + char vip_path[MAXPATHLEN]; /* tail end of it */ +}; + +struct vnode_fdinfo { + struct proc_fileinfo pfi; + struct vnode_info pvi; +}; + +struct vnode_fdinfowithpath { + struct proc_fileinfo pfi; + struct vnode_info_path pvip; +}; + +struct proc_regionwithpathinfo { + struct proc_regioninfo prp_prinfo; + struct vnode_info_path prp_vip; +}; + +struct proc_regionpath { + uint64_t prpo_addr; + uint64_t prpo_regionlength; + char prpo_path[MAXPATHLEN]; +}; + +struct proc_vnodepathinfo { + struct vnode_info_path pvi_cdir; + struct vnode_info_path pvi_rdir; +}; + +struct proc_threadwithpathinfo { + struct proc_threadinfo pt; + struct vnode_info_path pvip; +}; + +/* + * Socket + */ + + +/* + * IPv4 and IPv6 Sockets + */ + +#define INI_IPV4 0x1 +#define INI_IPV6 0x2 + +struct in4in6_addr { + u_int32_t i46a_pad32[3]; + struct in_addr i46a_addr4; +}; + +struct in_sockinfo { + int insi_fport; /* foreign port */ + int insi_lport; /* local port */ + uint64_t insi_gencnt; /* generation count of this instance */ + uint32_t insi_flags; /* generic IP/datagram flags */ + uint32_t insi_flow; + + uint8_t insi_vflag; /* ini_IPV4 or ini_IPV6 */ + uint8_t insi_ip_ttl; /* time to live proto */ + uint32_t rfu_1; /* reserved */ + /* protocol dependent part */ + union { + struct in4in6_addr ina_46; + struct in6_addr ina_6; + } insi_faddr; /* foreign host table entry */ + union { + struct in4in6_addr ina_46; + struct in6_addr ina_6; + } insi_laddr; /* local host table entry */ + struct { + u_char in4_tos; /* type of service */ + } insi_v4; + struct { + uint8_t in6_hlim; + int in6_cksum; + u_short in6_ifindex; + short in6_hops; + } insi_v6; +}; + +/* + * TCP Sockets + */ + +#define TSI_T_REXMT 0 /* retransmit */ +#define TSI_T_PERSIST 1 /* retransmit persistence */ +#define TSI_T_KEEP 2 /* keep alive */ +#define TSI_T_2MSL 3 /* 2*msl quiet time timer */ +#define TSI_T_NTIMERS 4 + +#define TSI_S_CLOSED 0 /* closed */ +#define TSI_S_LISTEN 1 /* listening for connection */ +#define TSI_S_SYN_SENT 2 /* active, have sent syn */ +#define TSI_S_SYN_RECEIVED 3 /* have send and received syn */ +#define TSI_S_ESTABLISHED 4 /* established */ +#define TSI_S__CLOSE_WAIT 5 /* rcvd fin, waiting for close */ +#define TSI_S_FIN_WAIT_1 6 /* have closed, sent fin */ +#define TSI_S_CLOSING 7 /* closed xchd FIN; await FIN ACK */ +#define TSI_S_LAST_ACK 8 /* had fin and close; await FIN ACK */ +#define TSI_S_FIN_WAIT_2 9 /* have closed, fin is acked */ +#define TSI_S_TIME_WAIT 10 /* in 2*msl quiet wait after close */ +#define TSI_S_RESERVED 11 /* pseudo state: reserved */ + +struct tcp_sockinfo { + struct in_sockinfo tcpsi_ini; + int tcpsi_state; + int tcpsi_timer[TSI_T_NTIMERS]; + int tcpsi_mss; + uint32_t tcpsi_flags; + uint32_t rfu_1; /* reserved */ + uint64_t tcpsi_tp; /* opaque handle of TCP protocol control block */ +}; + +/* + * Unix Domain Sockets + */ + + +struct un_sockinfo { + uint64_t unsi_conn_so; /* opaque handle of connected socket */ + uint64_t unsi_conn_pcb; /* opaque handle of connected protocol control block */ + union { + struct sockaddr_un ua_sun; + char ua_dummy[SOCK_MAXADDRLEN]; + } unsi_addr; /* bound address */ + union { + struct sockaddr_un ua_sun; + char ua_dummy[SOCK_MAXADDRLEN]; + } unsi_caddr; /* address of socket connected to */ +}; + +/* + * PF_NDRV Sockets + */ + +struct ndrv_info { + uint32_t ndrvsi_if_family; + uint32_t ndrvsi_if_unit; + char ndrvsi_if_name[IF_NAMESIZE]; +}; + +/* + * Kernel Event Sockets + */ + +struct kern_event_info { + uint32_t kesi_vendor_code_filter; + uint32_t kesi_class_filter; + uint32_t kesi_subclass_filter; +}; + +/* + * Kernel Control Sockets + */ + +struct kern_ctl_info { + uint32_t kcsi_id; + uint32_t kcsi_reg_unit; + uint32_t kcsi_flags; /* support flags */ + uint32_t kcsi_recvbufsize; /* request more than the default buffer size */ + uint32_t kcsi_sendbufsize; /* request more than the default buffer size */ + uint32_t kcsi_unit; + char kcsi_name[MAX_KCTL_NAME]; /* unique nke identifier, provided by DTS */ +}; + +/* + * VSock Sockets + */ + +struct vsock_sockinfo { + uint32_t local_cid; + uint32_t local_port; + uint32_t remote_cid; + uint32_t remote_port; +}; + +/* soi_state */ + +#define SOI_S_NOFDREF 0x0001 /* no file table ref any more */ +#define SOI_S_ISCONNECTED 0x0002 /* socket connected to a peer */ +#define SOI_S_ISCONNECTING 0x0004 /* in process of connecting to peer */ +#define SOI_S_ISDISCONNECTING 0x0008 /* in process of disconnecting */ +#define SOI_S_CANTSENDMORE 0x0010 /* can't send more data to peer */ +#define SOI_S_CANTRCVMORE 0x0020 /* can't receive more data from peer */ +#define SOI_S_RCVATMARK 0x0040 /* at mark on input */ +#define SOI_S_PRIV 0x0080 /* privileged for broadcast, raw... */ +#define SOI_S_NBIO 0x0100 /* non-blocking ops */ +#define SOI_S_ASYNC 0x0200 /* async i/o notify */ +#define SOI_S_INCOMP 0x0800 /* Unaccepted, incomplete connection */ +#define SOI_S_COMP 0x1000 /* unaccepted, complete connection */ +#define SOI_S_ISDISCONNECTED 0x2000 /* socket disconnected from peer */ +#define SOI_S_DRAINING 0x4000 /* close waiting for blocked system calls to drain */ + +struct sockbuf_info { + uint32_t sbi_cc; + uint32_t sbi_hiwat; /* SO_RCVBUF, SO_SNDBUF */ + uint32_t sbi_mbcnt; + uint32_t sbi_mbmax; + uint32_t sbi_lowat; + short sbi_flags; + short sbi_timeo; +}; + +enum { + SOCKINFO_GENERIC = 0, + SOCKINFO_IN = 1, + SOCKINFO_TCP = 2, + SOCKINFO_UN = 3, + SOCKINFO_NDRV = 4, + SOCKINFO_KERN_EVENT = 5, + SOCKINFO_KERN_CTL = 6, + SOCKINFO_VSOCK = 7, +}; + +struct socket_info { + struct vinfo_stat soi_stat; + uint64_t soi_so; /* opaque handle of socket */ + uint64_t soi_pcb; /* opaque handle of protocol control block */ + int soi_type; + int soi_protocol; + int soi_family; + short soi_options; + short soi_linger; + short soi_state; + short soi_qlen; + short soi_incqlen; + short soi_qlimit; + short soi_timeo; + u_short soi_error; + uint32_t soi_oobmark; + struct sockbuf_info soi_rcv; + struct sockbuf_info soi_snd; + int soi_kind; + uint32_t rfu_1; /* reserved */ + union { + struct in_sockinfo pri_in; /* SOCKINFO_IN */ + struct tcp_sockinfo pri_tcp; /* SOCKINFO_TCP */ + struct un_sockinfo pri_un; /* SOCKINFO_UN */ + struct ndrv_info pri_ndrv; /* SOCKINFO_NDRV */ + struct kern_event_info pri_kern_event; /* SOCKINFO_KERN_EVENT */ + struct kern_ctl_info pri_kern_ctl; /* SOCKINFO_KERN_CTL */ + struct vsock_sockinfo pri_vsock; /* SOCKINFO_VSOCK */ + } soi_proto; +}; + +struct socket_fdinfo { + struct proc_fileinfo pfi; + struct socket_info psi; +}; + + + +struct psem_info { + struct vinfo_stat psem_stat; + char psem_name[MAXPATHLEN]; +}; + +struct psem_fdinfo { + struct proc_fileinfo pfi; + struct psem_info pseminfo; +}; + + + +struct pshm_info { + struct vinfo_stat pshm_stat; + uint64_t pshm_mappaddr; + char pshm_name[MAXPATHLEN]; +}; + +struct pshm_fdinfo { + struct proc_fileinfo pfi; + struct pshm_info pshminfo; +}; + + +struct pipe_info { + struct vinfo_stat pipe_stat; + uint64_t pipe_handle; + uint64_t pipe_peerhandle; + int pipe_status; + int rfu_1; /* reserved */ +}; + +struct pipe_fdinfo { + struct proc_fileinfo pfi; + struct pipe_info pipeinfo; +}; + + +struct kqueue_info { + struct vinfo_stat kq_stat; + uint32_t kq_state; + uint32_t rfu_1; /* reserved */ +}; + +struct kqueue_dyninfo { + struct kqueue_info kqdi_info; + uint64_t kqdi_servicer; + uint64_t kqdi_owner; + uint32_t kqdi_sync_waiters; + uint8_t kqdi_sync_waiter_qos; + uint8_t kqdi_async_qos; + uint16_t kqdi_request_state; + uint8_t kqdi_events_qos; + uint8_t kqdi_pri; + uint8_t kqdi_pol; + uint8_t kqdi_cpupercent; + uint8_t _kqdi_reserved0[4]; + uint64_t _kqdi_reserved1[4]; +}; + +/* keep in sync with KQ_* in sys/eventvar.h */ +#define PROC_KQUEUE_SELECT 0x01 +#define PROC_KQUEUE_SLEEP 0x02 +#define PROC_KQUEUE_32 0x08 +#define PROC_KQUEUE_64 0x10 +#define PROC_KQUEUE_QOS 0x20 + + +struct kqueue_fdinfo { + struct proc_fileinfo pfi; + struct kqueue_info kqueueinfo; +}; + +struct appletalk_info { + struct vinfo_stat atalk_stat; +}; + +struct appletalk_fdinfo { + struct proc_fileinfo pfi; + struct appletalk_info appletalkinfo; +}; + +typedef uint64_t proc_info_udata_t; + +/* defns of process file desc type */ +#define PROX_FDTYPE_ATALK 0 +#define PROX_FDTYPE_VNODE 1 +#define PROX_FDTYPE_SOCKET 2 +#define PROX_FDTYPE_PSHM 3 +#define PROX_FDTYPE_PSEM 4 +#define PROX_FDTYPE_KQUEUE 5 +#define PROX_FDTYPE_PIPE 6 +#define PROX_FDTYPE_FSEVENTS 7 +#define PROX_FDTYPE_NETPOLICY 9 + +struct proc_fdinfo { + int32_t proc_fd; + uint32_t proc_fdtype; +}; + +struct proc_fileportinfo { + uint32_t proc_fileport; + uint32_t proc_fdtype; +}; + + +/* Flavors for proc_pidinfo() */ +#define PROC_PIDLISTFDS 1 +#define PROC_PIDLISTFD_SIZE (sizeof(struct proc_fdinfo)) + +#define PROC_PIDTASKALLINFO 2 +#define PROC_PIDTASKALLINFO_SIZE (sizeof(struct proc_taskallinfo)) + +#define PROC_PIDTBSDINFO 3 +#define PROC_PIDTBSDINFO_SIZE (sizeof(struct proc_bsdinfo)) + +#define PROC_PIDTASKINFO 4 +#define PROC_PIDTASKINFO_SIZE (sizeof(struct proc_taskinfo)) + +#define PROC_PIDTHREADINFO 5 +#define PROC_PIDTHREADINFO_SIZE (sizeof(struct proc_threadinfo)) + +#define PROC_PIDLISTTHREADS 6 +#define PROC_PIDLISTTHREADS_SIZE (2* sizeof(uint32_t)) + +#define PROC_PIDREGIONINFO 7 +#define PROC_PIDREGIONINFO_SIZE (sizeof(struct proc_regioninfo)) + +#define PROC_PIDREGIONPATHINFO 8 +#define PROC_PIDREGIONPATHINFO_SIZE (sizeof(struct proc_regionwithpathinfo)) + +#define PROC_PIDVNODEPATHINFO 9 +#define PROC_PIDVNODEPATHINFO_SIZE (sizeof(struct proc_vnodepathinfo)) + +#define PROC_PIDTHREADPATHINFO 10 +#define PROC_PIDTHREADPATHINFO_SIZE (sizeof(struct proc_threadwithpathinfo)) + +#define PROC_PIDPATHINFO 11 +#define PROC_PIDPATHINFO_SIZE (MAXPATHLEN) +#define PROC_PIDPATHINFO_MAXSIZE (4*MAXPATHLEN) + +#define PROC_PIDWORKQUEUEINFO 12 +#define PROC_PIDWORKQUEUEINFO_SIZE (sizeof(struct proc_workqueueinfo)) + +#define PROC_PIDT_SHORTBSDINFO 13 +#define PROC_PIDT_SHORTBSDINFO_SIZE (sizeof(struct proc_bsdshortinfo)) + +#define PROC_PIDLISTFILEPORTS 14 +#define PROC_PIDLISTFILEPORTS_SIZE (sizeof(struct proc_fileportinfo)) + +#define PROC_PIDTHREADID64INFO 15 +#define PROC_PIDTHREADID64INFO_SIZE (sizeof(struct proc_threadinfo)) + +#define PROC_PID_RUSAGE 16 +#define PROC_PID_RUSAGE_SIZE 0 + +/* Flavors for proc_pidfdinfo */ + +#define PROC_PIDFDVNODEINFO 1 +#define PROC_PIDFDVNODEINFO_SIZE (sizeof(struct vnode_fdinfo)) + +#define PROC_PIDFDVNODEPATHINFO 2 +#define PROC_PIDFDVNODEPATHINFO_SIZE (sizeof(struct vnode_fdinfowithpath)) + +#define PROC_PIDFDSOCKETINFO 3 +#define PROC_PIDFDSOCKETINFO_SIZE (sizeof(struct socket_fdinfo)) + +#define PROC_PIDFDPSEMINFO 4 +#define PROC_PIDFDPSEMINFO_SIZE (sizeof(struct psem_fdinfo)) + +#define PROC_PIDFDPSHMINFO 5 +#define PROC_PIDFDPSHMINFO_SIZE (sizeof(struct pshm_fdinfo)) + +#define PROC_PIDFDPIPEINFO 6 +#define PROC_PIDFDPIPEINFO_SIZE (sizeof(struct pipe_fdinfo)) + +#define PROC_PIDFDKQUEUEINFO 7 +#define PROC_PIDFDKQUEUEINFO_SIZE (sizeof(struct kqueue_fdinfo)) + +#define PROC_PIDFDATALKINFO 8 +#define PROC_PIDFDATALKINFO_SIZE (sizeof(struct appletalk_fdinfo)) + + + +/* Flavors for proc_pidfileportinfo */ + +#define PROC_PIDFILEPORTVNODEPATHINFO 2 /* out: vnode_fdinfowithpath */ +#define PROC_PIDFILEPORTVNODEPATHINFO_SIZE \ + PROC_PIDFDVNODEPATHINFO_SIZE + +#define PROC_PIDFILEPORTSOCKETINFO 3 /* out: socket_fdinfo */ +#define PROC_PIDFILEPORTSOCKETINFO_SIZE PROC_PIDFDSOCKETINFO_SIZE + +#define PROC_PIDFILEPORTPSHMINFO 5 /* out: pshm_fdinfo */ +#define PROC_PIDFILEPORTPSHMINFO_SIZE PROC_PIDFDPSHMINFO_SIZE + +#define PROC_PIDFILEPORTPIPEINFO 6 /* out: pipe_fdinfo */ +#define PROC_PIDFILEPORTPIPEINFO_SIZE PROC_PIDFDPIPEINFO_SIZE + +/* used for proc_setcontrol */ +#define PROC_SELFSET_PCONTROL 1 + +#define PROC_SELFSET_THREADNAME 2 +#define PROC_SELFSET_THREADNAME_SIZE (MAXTHREADNAMESIZE -1) + +#define PROC_SELFSET_VMRSRCOWNER 3 + +#define PROC_SELFSET_DELAYIDLESLEEP 4 + +/* used for proc_dirtycontrol */ +#define PROC_DIRTYCONTROL_TRACK 1 +#define PROC_DIRTYCONTROL_SET 2 +#define PROC_DIRTYCONTROL_GET 3 +#define PROC_DIRTYCONTROL_CLEAR 4 + +/* proc_track_dirty() flags */ +#define PROC_DIRTY_TRACK 0x1 +#define PROC_DIRTY_ALLOW_IDLE_EXIT 0x2 +#define PROC_DIRTY_DEFER 0x4 +#define PROC_DIRTY_LAUNCH_IN_PROGRESS 0x8 +#define PROC_DIRTY_DEFER_ALWAYS 0x10 + +/* proc_get_dirty() flags */ +#define PROC_DIRTY_TRACKED 0x1 +#define PROC_DIRTY_ALLOWS_IDLE_EXIT 0x2 +#define PROC_DIRTY_IS_DIRTY 0x4 +#define PROC_DIRTY_LAUNCH_IS_IN_PROGRESS 0x8 + +/* Flavors for proc_udata_info */ +#define PROC_UDATA_INFO_GET 1 +#define PROC_UDATA_INFO_SET 2 + + + + +__END_DECLS + +#endif /*_SYS_PROC_INFO_H */ \ No newline at end of file From a033735c8d2faf4ecd42b9d736d8ea61d22d3fcb Mon Sep 17 00:00:00 2001 From: Loris Cro Date: Tue, 9 Mar 2021 22:43:16 +0100 Subject: [PATCH 35/49] update to latest fetch-them-macos-headers --- .../include/aarch64-macos-gnu/sys/ucontext.h | 41 ++++++++++++++ lib/libc/include/aarch64-macos-gnu/ucontext.h | 54 +++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 lib/libc/include/aarch64-macos-gnu/sys/ucontext.h create mode 100644 lib/libc/include/aarch64-macos-gnu/ucontext.h diff --git a/lib/libc/include/aarch64-macos-gnu/sys/ucontext.h b/lib/libc/include/aarch64-macos-gnu/sys/ucontext.h new file mode 100644 index 0000000000..7f03256166 --- /dev/null +++ b/lib/libc/include/aarch64-macos-gnu/sys/ucontext.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2002-2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#ifndef _SYS_UCONTEXT_H_ +#define _SYS_UCONTEXT_H_ + +#include +#include + +#include +#include + +#include + + +#endif /* _SYS_UCONTEXT_H_ */ \ No newline at end of file diff --git a/lib/libc/include/aarch64-macos-gnu/ucontext.h b/lib/libc/include/aarch64-macos-gnu/ucontext.h new file mode 100644 index 0000000000..897e1ddd9c --- /dev/null +++ b/lib/libc/include/aarch64-macos-gnu/ucontext.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2002, 2008, 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +/* + * These routines are DEPRECATED and should not be used. + */ +#ifndef _UCONTEXT_H_ +#define _UCONTEXT_H_ + +#include + +#ifdef _XOPEN_SOURCE +#include +#include + +__BEGIN_DECLS +__API_DEPRECATED("No longer supported", macos(10.5, 10.6)) +int getcontext(ucontext_t *); + +__API_DEPRECATED("No longer supported", macos(10.5, 10.6)) +void makecontext(ucontext_t *, void (*)(), int, ...); + +__API_DEPRECATED("No longer supported", macos(10.5, 10.6)) +int setcontext(const ucontext_t *); + +__API_DEPRECATED("No longer supported", macos(10.5, 10.6)) +int swapcontext(ucontext_t * __restrict, const ucontext_t * __restrict); + +__END_DECLS +#else /* !_XOPEN_SOURCE */ +#error The deprecated ucontext routines require _XOPEN_SOURCE to be defined +#endif /* _XOPEN_SOURCE */ + +#endif /* _UCONTEXT_H_ */ \ No newline at end of file From 1a5d0cea15963437af68bbb7a50354a378cf4efd Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Mon, 8 Mar 2021 09:48:19 +0100 Subject: [PATCH 36/49] stage2: Use correct ELF emulation for mips64 The n32 ABI requires a different emulation string than n64. --- src/link/Elf.zig | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index bbbc9e25ed..314e443f3a 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -3230,8 +3230,20 @@ fn getLDMOption(target: std.Target) ?[]const u8 { .sparcv9 => return "elf64_sparc", .mips => return "elf32btsmip", .mipsel => return "elf32ltsmip", - .mips64 => return "elf64btsmip", - .mips64el => return "elf64ltsmip", + .mips64 => { + if (target.abi == .gnuabin32) { + return "elf32btsmipn32"; + } else { + return "elf64btsmip"; + } + }, + .mips64el => { + if (target.abi == .gnuabin32) { + return "elf32ltsmipn32"; + } else { + return "elf64ltsmip"; + } + }, .s390x => return "elf64_s390", .x86_64 => { if (target.abi == .gnux32) { From f2b96782ecdc9e2f8740eb7d294203b2a585ea52 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Mon, 8 Mar 2021 09:49:16 +0100 Subject: [PATCH 37/49] stage2: Fix glibc lookup path for MIPS crt files Add some more rules to let the compiler find the correct startup files for the selected target and ABI. --- src/glibc.zig | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/glibc.zig b/src/glibc.zig index b154bd530d..1acf57e267 100644 --- a/src/glibc.zig +++ b/src/glibc.zig @@ -453,7 +453,20 @@ fn start_asm_path(comp: *Compilation, arena: *Allocator, basename: []const u8) ! } else if (arch.isARM()) { try result.appendSlice("arm"); } else if (arch.isMIPS()) { - try result.appendSlice("mips"); + if (!mem.eql(u8, basename, "crti.S") and !mem.eql(u8, basename, "crtn.S")) { + try result.appendSlice("mips"); + } else { + if (is_64) { + const abi_dir = if (comp.getTarget().abi == .gnuabin32) + "n32" + else + "n64"; + try result.appendSlice("mips" ++ s ++ "mips64" ++ s); + try result.appendSlice(abi_dir); + } else { + try result.appendSlice("mips" ++ s ++ "mips32"); + } + } } else if (arch == .x86_64) { try result.appendSlice("x86_64"); } else if (arch == .i386) { From c5eb15526dac23e231ae54c2461f0eb15b28303e Mon Sep 17 00:00:00 2001 From: Asherah Connor Date: Wed, 10 Mar 2021 16:26:41 +1100 Subject: [PATCH 38/49] expose machine field in ELF header --- lib/std/elf.zig | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/std/elf.zig b/lib/std/elf.zig index 26674288a4..4ad859d877 100644 --- a/lib/std/elf.zig +++ b/lib/std/elf.zig @@ -337,6 +337,7 @@ pub const ET = extern enum(u16) { /// All integers are native endian. pub const Header = struct { endian: builtin.Endian, + machine: EM, is_64: bool, entry: u64, phoff: u64, @@ -387,8 +388,14 @@ pub const Header = struct { else => return error.InvalidElfClass, }; + const machine = if (need_bswap) blk: { + const value = @enumToInt(hdr32.e_machine); + break :blk @intToEnum(EM, @byteSwap(@TypeOf(value), value)); + } else hdr32.e_machine; + return @as(Header, .{ .endian = endian, + .machine = machine, .is_64 = is_64, .entry = int(is_64, need_bswap, hdr32.e_entry, hdr64.e_entry), .phoff = int(is_64, need_bswap, hdr32.e_phoff, hdr64.e_phoff), From 24e5959840c790ec74665d22a21d82b68418bc57 Mon Sep 17 00:00:00 2001 From: Asherah Connor Date: Thu, 11 Mar 2021 10:09:51 +1100 Subject: [PATCH 39/49] elf: make EM non-exhaustive --- lib/std/elf.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/std/elf.zig b/lib/std/elf.zig index 4ad859d877..36382ecc42 100644 --- a/lib/std/elf.zig +++ b/lib/std/elf.zig @@ -1525,6 +1525,8 @@ pub const EM = extern enum(u16) { /// Linux kernel bpf virtual machine _BPF = 247, + + _, }; /// Section data should be writable during execution. From bdb917006c9920f2a0d2091cb0f3d52454e039f0 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Sun, 7 Mar 2021 00:25:21 +0100 Subject: [PATCH 40/49] stage2 tzir: Add wrapping integer arithmetic instructions --- src/codegen.zig | 30 ++++++++++++++++++++++++++++++ src/ir.zig | 6 ++++++ src/zir.zig | 6 ++++++ src/zir_sema.zig | 11 +++++++---- 4 files changed, 49 insertions(+), 4 deletions(-) diff --git a/src/codegen.zig b/src/codegen.zig index c3cd64cf73..e21626bdb6 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -865,6 +865,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { fn genFuncInst(self: *Self, inst: *ir.Inst) !MCValue { switch (inst.tag) { .add => return self.genAdd(inst.castTag(.add).?), + .addwrap => return self.genAddWrap(inst.castTag(.addwrap).?), .alloc => return self.genAlloc(inst.castTag(.alloc).?), .arg => return self.genArg(inst.castTag(.arg).?), .assembly => return self.genAsm(inst.castTag(.assembly).?), @@ -900,12 +901,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .loop => return self.genLoop(inst.castTag(.loop).?), .not => return self.genNot(inst.castTag(.not).?), .mul => return self.genMul(inst.castTag(.mul).?), + .mulwrap => return self.genMulWrap(inst.castTag(.mulwrap).?), .ptrtoint => return self.genPtrToInt(inst.castTag(.ptrtoint).?), .ref => return self.genRef(inst.castTag(.ref).?), .ret => return self.genRet(inst.castTag(.ret).?), .retvoid => return self.genRetVoid(inst.castTag(.retvoid).?), .store => return self.genStore(inst.castTag(.store).?), .sub => return self.genSub(inst.castTag(.sub).?), + .subwrap => return self.genSubWrap(inst.castTag(.subwrap).?), .switchbr => return self.genSwitch(inst.castTag(.switchbr).?), .unreach => return MCValue{ .unreach = {} }, .optional_payload => return self.genOptionalPayload(inst.castTag(.optional_payload).?), @@ -1129,6 +1132,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { } } + fn genAddWrap(self: *Self, inst: *ir.Inst.BinOp) !MCValue { + // No side effects, so if it's unreferenced, do nothing. + if (inst.base.isUnused()) + return MCValue.dead; + switch (arch) { + else => return self.fail(inst.base.src, "TODO implement addwrap for {}", .{self.target.cpu.arch}), + } + } + fn genMul(self: *Self, inst: *ir.Inst.BinOp) !MCValue { // No side effects, so if it's unreferenced, do nothing. if (inst.base.isUnused()) @@ -1139,6 +1151,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { } } + fn genMulWrap(self: *Self, inst: *ir.Inst.BinOp) !MCValue { + // No side effects, so if it's unreferenced, do nothing. + if (inst.base.isUnused()) + return MCValue.dead; + switch (arch) { + else => return self.fail(inst.base.src, "TODO implement mulwrap for {}", .{self.target.cpu.arch}), + } + } + fn genBitAnd(self: *Self, inst: *ir.Inst.BinOp) !MCValue { // No side effects, so if it's unreferenced, do nothing. if (inst.base.isUnused()) @@ -1392,6 +1413,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { } } + fn genSubWrap(self: *Self, inst: *ir.Inst.BinOp) !MCValue { + // No side effects, so if it's unreferenced, do nothing. + if (inst.base.isUnused()) + return MCValue.dead; + switch (arch) { + else => return self.fail(inst.base.src, "TODO implement subwrap for {}", .{self.target.cpu.arch}), + } + } + fn genArmBinOp(self: *Self, inst: *ir.Inst, op_lhs: *ir.Inst, op_rhs: *ir.Inst, op: ir.Inst.Tag) !MCValue { const lhs = try self.resolveInst(op_lhs); const rhs = try self.resolveInst(op_rhs); diff --git a/src/ir.zig b/src/ir.zig index 996f3b9782..95896d523b 100644 --- a/src/ir.zig +++ b/src/ir.zig @@ -53,6 +53,7 @@ pub const Inst = struct { pub const Tag = enum { add, + addwrap, alloc, arg, assembly, @@ -105,8 +106,10 @@ pub const Inst = struct { /// Write a value to a pointer. LHS is pointer, RHS is value. store, sub, + subwrap, unreach, mul, + mulwrap, not, floatcast, intcast, @@ -165,8 +168,11 @@ pub const Inst = struct { => UnOp, .add, + .addwrap, .sub, + .subwrap, .mul, + .mulwrap, .cmp_lt, .cmp_lte, .cmp_eq, diff --git a/src/zir.zig b/src/zir.zig index cb1f4561bf..0b9df55624 100644 --- a/src/zir.zig +++ b/src/zir.zig @@ -1680,8 +1680,11 @@ const DumpTzir = struct { }, .add, + .addwrap, .sub, + .subwrap, .mul, + .mulwrap, .cmp_lt, .cmp_lte, .cmp_eq, @@ -1803,8 +1806,11 @@ const DumpTzir = struct { }, .add, + .addwrap, .sub, + .subwrap, .mul, + .mulwrap, .cmp_lt, .cmp_lte, .cmp_eq, diff --git a/src/zir_sema.zig b/src/zir_sema.zig index 9cbdfd07dd..2df32709c5 100644 --- a/src/zir_sema.zig +++ b/src/zir_sema.zig @@ -2179,10 +2179,13 @@ fn zirArithmetic(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError! } const b = try mod.requireRuntimeBlock(scope, inst.base.src); - const ir_tag = switch (inst.base.tag) { - .add => Inst.Tag.add, - .sub => Inst.Tag.sub, - .mul => Inst.Tag.mul, + const ir_tag: Inst.Tag = switch (inst.base.tag) { + .add => .add, + .addwrap => .addwrap, + .sub => .sub, + .subwrap => .subwrap, + .mul => .mul, + .mulwrap => .mulwrap, else => return mod.fail(scope, inst.base.src, "TODO implement arithmetic for operand '{s}''", .{@tagName(inst.base.tag)}), }; From 482424e2b12cfcfe71280c826a2d31cb5df13a1a Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Thu, 11 Mar 2021 23:05:46 +0100 Subject: [PATCH 41/49] std: Handle empty MultiArrayList in items() Closes #8211 --- lib/std/multi_array_list.zig | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/std/multi_array_list.zig b/lib/std/multi_array_list.zig index 37c4456d22..f4d89d198c 100644 --- a/lib/std/multi_array_list.zig +++ b/lib/std/multi_array_list.zig @@ -28,8 +28,11 @@ pub fn MultiArrayList(comptime S: type) type { capacity: usize, pub fn items(self: Slice, comptime field: Field) []FieldType(field) { - const byte_ptr = self.ptrs[@enumToInt(field)]; const F = FieldType(field); + if (self.len == 0) { + return &[_]F{}; + } + const byte_ptr = self.ptrs[@enumToInt(field)]; const casted_ptr = @ptrCast([*]F, @alignCast(@alignOf(F), byte_ptr)); return casted_ptr[0..self.len]; } @@ -300,6 +303,8 @@ test "basic usage" { var list = MultiArrayList(Foo){}; defer list.deinit(ally); + testing.expectEqual(@as(usize, 0), list.items(.a).len); + try list.ensureCapacity(ally, 2); list.appendAssumeCapacity(.{ From a5cb4ab95e80c4f75356b80251c3628811956b19 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Sun, 7 Mar 2021 19:20:41 +0100 Subject: [PATCH 42/49] parser: disallow ptr modifiers on array types --- lib/std/zig/ast.zig | 8 +++++--- lib/std/zig/parse.zig | 35 ++++++++++++++++++++--------------- lib/std/zig/parser_test.zig | 12 +++++++++--- 3 files changed, 34 insertions(+), 21 deletions(-) diff --git a/lib/std/zig/ast.zig b/lib/std/zig/ast.zig index 59de727bcc..0b0459ec88 100644 --- a/lib/std/zig/ast.zig +++ b/lib/std/zig/ast.zig @@ -275,8 +275,10 @@ pub const Tree = struct { .extra_volatile_qualifier => { return stream.writeAll("extra volatile qualifier"); }, - .invalid_align => { - return stream.writeAll("alignment not allowed on arrays"); + .ptr_mod_on_array_child_type => { + return stream.print("pointer modifier '{s}' not allowed on array child type", .{ + token_tags[parse_error.token].symbol(), + }); }, .invalid_and => { return stream.writeAll("`&&` is invalid; note that `and` is boolean AND"); @@ -2388,7 +2390,7 @@ pub const Error = struct { extra_allowzero_qualifier, extra_const_qualifier, extra_volatile_qualifier, - invalid_align, + ptr_mod_on_array_child_type, invalid_and, invalid_bit_range, invalid_token, diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index c8a4cffdbd..805ee95571 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -1612,13 +1612,15 @@ const Parser = struct { /// PrefixTypeOp /// <- QUESTIONMARK /// / KEYWORD_anyframe MINUSRARROW - /// / ArrayTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* + /// / SliceTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* /// / PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* + /// / ArrayTypeStart + /// SliceTypeStart <- LBRACKET (COLON Expr)? RBRACKET /// PtrTypeStart /// <- ASTERISK /// / ASTERISK2 /// / LBRACKET ASTERISK (LETTERC / COLON Expr)? RBRACKET - /// ArrayTypeStart <- LBRACKET Expr? (COLON Expr)? RBRACKET + /// ArrayTypeStart <- LBRACKET Expr (COLON Expr)? RBRACKET fn parseTypeExpr(p: *Parser) Error!Node.Index { switch (p.token_tags[p.tok_i]) { .question_mark => return p.addNode(.{ @@ -1785,15 +1787,15 @@ const Parser = struct { else 0; _ = try p.expectToken(.r_bracket); - const mods = try p.parsePtrModifiers(); - const elem_type = try p.expectTypeExpr(); - if (mods.bit_range_start != 0) { - try p.warnMsg(.{ - .tag = .invalid_bit_range, - .token = p.nodes.items(.main_token)[mods.bit_range_start], - }); - } if (len_expr == 0) { + const mods = try p.parsePtrModifiers(); + const elem_type = try p.expectTypeExpr(); + if (mods.bit_range_start != 0) { + try p.warnMsg(.{ + .tag = .invalid_bit_range, + .token = p.nodes.items(.main_token)[mods.bit_range_start], + }); + } if (sentinel == 0) { return p.addNode(.{ .tag = .ptr_type_aligned, @@ -1826,12 +1828,15 @@ const Parser = struct { }); } } else { - if (mods.align_node != 0) { - try p.warnMsg(.{ - .tag = .invalid_align, - .token = p.nodes.items(.main_token)[mods.align_node], - }); + switch (p.token_tags[p.tok_i]) { + .keyword_align, + .keyword_const, + .keyword_volatile, + .keyword_allowzero, + => return p.fail(.ptr_mod_on_array_child_type), + else => {}, } + const elem_type = try p.expectTypeExpr(); if (sentinel == 0) { return p.addNode(.{ .tag = .array_type, diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index b9f856fba3..b6bd2844a4 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -4350,11 +4350,17 @@ test "zig fmt: error for invalid bit range" { }); } -test "zig fmt: error for invalid align" { +test "zig fmt: error for ptr mod on array child type" { try testError( - \\var x: [10]align(10)u8 = bar; + \\var a: [10]align(10) u8 = e; + \\var b: [10]const u8 = f; + \\var c: [10]volatile u8 = g; + \\var d: [10]allowzero u8 = h; , &[_]Error{ - .invalid_align, + .ptr_mod_on_array_child_type, + .ptr_mod_on_array_child_type, + .ptr_mod_on_array_child_type, + .ptr_mod_on_array_child_type, }); } From 3010bfb08af0b47d801d492e4f2e21a988e8399a Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Fri, 12 Mar 2021 10:15:38 +0100 Subject: [PATCH 43/49] Fix Progress printing on Windows systems The cursor must be restored after the line is printed, not before. Take into account the visible viewport to correctly compute the terminal size. --- lib/std/Progress.zig | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig index 93e816feec..ee41501bfc 100644 --- a/lib/std/Progress.zig +++ b/lib/std/Progress.zig @@ -209,7 +209,13 @@ fn refreshWithHeldLock(self: *Progress) void { unreachable; saved_cursor_pos = info.dwCursorPosition; - const fill_chars = @intCast(windows.DWORD, info.dwSize.X * (info.dwSize.Y - info.dwCursorPosition.Y) - info.dwCursorPosition.X); + + const window_height = @intCast(windows.DWORD, info.srWindow.Bottom - info.srWindow.Top) + 1; + const window_width = @intCast(windows.DWORD, info.srWindow.Right - info.srWindow.Left) + 1; + // Number of terminal cells to clear, starting from the cursor position + // and ending at the window bottom right corner. + const fill_chars = window_width * (window_width - @intCast(windows.DWORD, info.dwCursorPosition.Y - info.srWindow.Top)) - + @intCast(windows.DWORD, info.dwCursorPosition.Y - info.srWindow.Top); var written: windows.DWORD = undefined; if (windows.kernel32.FillConsoleOutputAttribute( @@ -271,11 +277,7 @@ fn refreshWithHeldLock(self: *Progress) void { const seq_after = DECRC; std.mem.copy(u8, self.output_buffer[end..], seq_after); end += seq_after.len; - } else if (std.builtin.os.tag == .windows) { - if (windows.kernel32.SetConsoleCursorPosition(file.handle, saved_cursor_pos) != windows.TRUE) { - unreachable; - } - } else { + } else if (std.builtin.os.tag != .windows) { self.output_buffer[end] = '\n'; end += 1; } @@ -284,6 +286,12 @@ fn refreshWithHeldLock(self: *Progress) void { // Stop trying to write to this file once it errors. self.terminal = null; }; + + if (std.builtin.os.tag == .windows) { + if (windows.kernel32.SetConsoleCursorPosition(file.handle, saved_cursor_pos) != windows.TRUE) + unreachable; + } + self.prev_refresh_timestamp = self.timer.read(); } From 89e522b935a8cca96b2e6d0cce0515a1eb8e6451 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Marie?= Date: Tue, 2 Mar 2021 08:09:51 +0000 Subject: [PATCH 44/49] make std.c.getErrno() return same type as _errno() aka c_int adjust std.os.unexpectedErrno() to be correct for all std.os.system.errno (c_int, u12, usize, ...) --- lib/std/Thread.zig | 2 +- lib/std/c.zig | 4 ++-- lib/std/io/c_writer.zig | 2 +- lib/std/os.zig | 34 ++++++++++++++++++++-------------- 4 files changed, 24 insertions(+), 18 deletions(-) diff --git a/lib/std/Thread.zig b/lib/std/Thread.zig index b9a69d151f..7e8a6226e6 100644 --- a/lib/std/Thread.zig +++ b/lib/std/Thread.zig @@ -362,7 +362,7 @@ pub fn spawn(comptime startFn: anytype, context: SpawnContextType(@TypeOf(startF os.EAGAIN => return error.SystemResources, os.EPERM => unreachable, os.EINVAL => unreachable, - else => return os.unexpectedErrno(@intCast(usize, err)), + else => return os.unexpectedErrno(err), } return thread_obj; diff --git a/lib/std/c.zig b/lib/std/c.zig index 1688824dd9..bd0ce04f75 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -37,9 +37,9 @@ pub usingnamespace switch (std.Target.current.os.tag) { else => struct {}, }; -pub fn getErrno(rc: anytype) u16 { +pub fn getErrno(rc: anytype) c_int { if (rc == -1) { - return @intCast(u16, _errno().*); + return _errno().*; } else { return 0; } diff --git a/lib/std/io/c_writer.zig b/lib/std/io/c_writer.zig index fa7d7eb13a..26ae1fde01 100644 --- a/lib/std/io/c_writer.zig +++ b/lib/std/io/c_writer.zig @@ -30,7 +30,7 @@ fn cWriterWrite(c_file: *std.c.FILE, bytes: []const u8) std.fs.File.WriteError!u os.ENOSPC => return error.NoSpaceLeft, os.EPERM => return error.AccessDenied, os.EPIPE => return error.BrokenPipe, - else => |err| return os.unexpectedErrno(@intCast(usize, err)), + else => |err| return os.unexpectedErrno(err), } } diff --git a/lib/std/os.zig b/lib/std/os.zig index a2e62ed0ae..362a58f7fb 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -144,25 +144,27 @@ pub fn getrandom(buffer: []u8) GetRandomError!void { std.c.versionCheck(builtin.Version{ .major = 2, .minor = 25, .patch = 0 }).ok; while (buf.len != 0) { - var err: u16 = undefined; - - const num_read = if (use_c) blk: { + const res = if (use_c) blk: { const rc = std.c.getrandom(buf.ptr, buf.len, 0); - err = std.c.getErrno(rc); - break :blk @bitCast(usize, rc); + break :blk .{ + .num_read = @bitCast(usize, rc), + .err = std.c.getErrno(rc), + }; } else blk: { const rc = linux.getrandom(buf.ptr, buf.len, 0); - err = linux.getErrno(rc); - break :blk rc; + break :blk .{ + .num_read = rc, + .err = linux.getErrno(rc), + }; }; - switch (err) { - 0 => buf = buf[num_read..], + switch (res.err) { + 0 => buf = buf[res.num_read..], EINVAL => unreachable, EFAULT => unreachable, EINTR => continue, ENOSYS => return getRandomBytesDevURandom(buf), - else => return unexpectedErrno(err), + else => return unexpectedErrno(res.err), } } return; @@ -1500,7 +1502,7 @@ pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 { EINVAL => unreachable, ENOENT => return error.CurrentWorkingDirectoryUnlinked, ERANGE => return error.NameTooLong, - else => return unexpectedErrno(@intCast(usize, err)), + else => return unexpectedErrno(err), } } @@ -3661,7 +3663,7 @@ pub fn mmap( const err = if (builtin.link_libc) blk: { const rc = std.c.mmap(ptr, length, prot, flags, fd, offset); if (rc != std.c.MAP_FAILED) return @ptrCast([*]align(mem.page_size) u8, @alignCast(mem.page_size, rc))[0..length]; - break :blk @intCast(usize, system._errno().*); + break :blk system._errno().*; } else blk: { const rc = system.mmap(ptr, length, prot, flags, fd, offset); const err = errno(rc); @@ -4321,7 +4323,7 @@ pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealP ENAMETOOLONG => return error.NameTooLong, ELOOP => return error.SymLinkLoop, EIO => return error.InputOutput, - else => |err| return unexpectedErrno(@intCast(usize, err)), + else => |err| return unexpectedErrno(err), }; return mem.spanZ(result_path); } @@ -4622,7 +4624,11 @@ pub const UnexpectedError = error{ /// Call this when you made a syscall or something that sets errno /// and you get an unexpected error. -pub fn unexpectedErrno(err: usize) UnexpectedError { +pub fn unexpectedErrno(err: anytype) UnexpectedError { + if (@typeInfo(@TypeOf(err)) != .Int) { + @compileError("err is expected to be an integer"); + } + if (unexpected_error_tracing) { std.debug.warn("unexpected errno: {d}\n", .{err}); std.debug.dumpCurrentStackTrace(null); From b5a50a26ebac6a08dacf79f5d1db9bdd94ba33a5 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Fri, 12 Mar 2021 15:08:48 +0100 Subject: [PATCH 45/49] Fix many thinkos Somehow I forgot to save after copy-pasting some code and changing it. --- lib/std/Progress.zig | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig index ee41501bfc..b1f8dff1c1 100644 --- a/lib/std/Progress.zig +++ b/lib/std/Progress.zig @@ -214,8 +214,9 @@ fn refreshWithHeldLock(self: *Progress) void { const window_width = @intCast(windows.DWORD, info.srWindow.Right - info.srWindow.Left) + 1; // Number of terminal cells to clear, starting from the cursor position // and ending at the window bottom right corner. - const fill_chars = window_width * (window_width - @intCast(windows.DWORD, info.dwCursorPosition.Y - info.srWindow.Top)) - - @intCast(windows.DWORD, info.dwCursorPosition.Y - info.srWindow.Top); + const fill_chars = window_width * (window_height - + @intCast(windows.DWORD, info.dwCursorPosition.Y - info.srWindow.Top)) - + @intCast(windows.DWORD, info.dwCursorPosition.X - info.srWindow.Left); var written: windows.DWORD = undefined; if (windows.kernel32.FillConsoleOutputAttribute( From b0724a350f07c5e2e8fab572951ffaaa92860b2c Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Fri, 12 Mar 2021 17:14:50 +0100 Subject: [PATCH 46/49] Handle some weird edge cases of Win32 API Sometimes the viewport srWindow may report an invalid rectangle where the top row is below the bottom one. --- lib/std/Progress.zig | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig index b1f8dff1c1..3f462f5c08 100644 --- a/lib/std/Progress.zig +++ b/lib/std/Progress.zig @@ -210,13 +210,15 @@ fn refreshWithHeldLock(self: *Progress) void { saved_cursor_pos = info.dwCursorPosition; - const window_height = @intCast(windows.DWORD, info.srWindow.Bottom - info.srWindow.Top) + 1; - const window_width = @intCast(windows.DWORD, info.srWindow.Right - info.srWindow.Left) + 1; + const window_height = @intCast(windows.DWORD, info.srWindow.Bottom - info.srWindow.Top + 1); + const window_width = @intCast(windows.DWORD, info.srWindow.Right - info.srWindow.Left + 1); // Number of terminal cells to clear, starting from the cursor position // and ending at the window bottom right corner. - const fill_chars = window_width * (window_height - - @intCast(windows.DWORD, info.dwCursorPosition.Y - info.srWindow.Top)) - - @intCast(windows.DWORD, info.dwCursorPosition.X - info.srWindow.Left); + const fill_chars = if (window_width == 0 or window_height == 0) 0 else chars: { + break :chars window_width * (window_height - + @intCast(windows.DWORD, info.dwCursorPosition.Y - info.srWindow.Top)) - + @intCast(windows.DWORD, info.dwCursorPosition.X - info.srWindow.Left); + }; var written: windows.DWORD = undefined; if (windows.kernel32.FillConsoleOutputAttribute( From a745e704f6ded6cf1ec02e9666cac554697f69ff Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 12 Mar 2021 17:45:20 +0100 Subject: [PATCH 47/49] macos: add missing x86_64 libc headers --- lib/libc/include/x86_64-macos-gnu/libproc.h | 187 ++++ lib/libc/include/x86_64-macos-gnu/net/route.h | 257 ++++++ lib/libc/include/x86_64-macos-gnu/os/clock.h | 18 + .../include/x86_64-macos-gnu/os/workgroup.h | 37 + .../x86_64-macos-gnu/os/workgroup_base.h | 78 ++ .../x86_64-macos-gnu/os/workgroup_interval.h | 155 ++++ .../x86_64-macos-gnu/os/workgroup_object.h | 357 ++++++++ .../x86_64-macos-gnu/os/workgroup_parallel.h | 74 ++ .../x86_64-macos-gnu/sys/kern_control.h | 151 ++++ .../include/x86_64-macos-gnu/sys/proc_info.h | 799 ++++++++++++++++++ .../include/x86_64-macos-gnu/sys/ucontext.h | 41 + lib/libc/include/x86_64-macos-gnu/ucontext.h | 54 ++ 12 files changed, 2208 insertions(+) create mode 100644 lib/libc/include/x86_64-macos-gnu/libproc.h create mode 100644 lib/libc/include/x86_64-macos-gnu/net/route.h create mode 100644 lib/libc/include/x86_64-macos-gnu/os/clock.h create mode 100644 lib/libc/include/x86_64-macos-gnu/os/workgroup.h create mode 100644 lib/libc/include/x86_64-macos-gnu/os/workgroup_base.h create mode 100644 lib/libc/include/x86_64-macos-gnu/os/workgroup_interval.h create mode 100644 lib/libc/include/x86_64-macos-gnu/os/workgroup_object.h create mode 100644 lib/libc/include/x86_64-macos-gnu/os/workgroup_parallel.h create mode 100644 lib/libc/include/x86_64-macos-gnu/sys/kern_control.h create mode 100644 lib/libc/include/x86_64-macos-gnu/sys/proc_info.h create mode 100644 lib/libc/include/x86_64-macos-gnu/sys/ucontext.h create mode 100644 lib/libc/include/x86_64-macos-gnu/ucontext.h diff --git a/lib/libc/include/x86_64-macos-gnu/libproc.h b/lib/libc/include/x86_64-macos-gnu/libproc.h new file mode 100644 index 0000000000..4094fe40ef --- /dev/null +++ b/lib/libc/include/x86_64-macos-gnu/libproc.h @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2006, 2007, 2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#ifndef _LIBPROC_H_ +#define _LIBPROC_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for audit_token_t */ + +#include + +#include +#include + +/* + * This header file contains private interfaces to obtain process information. + * These interfaces are subject to change in future releases. + */ + +/*! + * @define PROC_LISTPIDSPATH_PATH_IS_VOLUME + * @discussion This flag indicates that all processes that hold open + * file references on the volume associated with the specified + * path should be returned. + */ +#define PROC_LISTPIDSPATH_PATH_IS_VOLUME 1 + + +/*! + * @define PROC_LISTPIDSPATH_EXCLUDE_EVTONLY + * @discussion This flag indicates that file references that were opened + * with the O_EVTONLY flag should be excluded from the matching + * criteria. + */ +#define PROC_LISTPIDSPATH_EXCLUDE_EVTONLY 2 + +__BEGIN_DECLS + + +/*! + * @function proc_listpidspath + * @discussion A function which will search through the current + * processes looking for open file references which match + * a specified path or volume. + * @param type types of processes to be searched (see proc_listpids) + * @param typeinfo adjunct information for type + * @param path file or volume path + * @param pathflags flags to control which files should be considered + * during the process search. + * @param buffer a C array of int-sized values to be filled with + * process identifiers that hold an open file reference + * matching the specified path or volume. Pass NULL to + * obtain the minimum buffer size needed to hold the + * currently active processes. + * @param buffersize the size (in bytes) of the provided buffer. + * @result the number of bytes of data returned in the provided buffer; + * -1 if an error was encountered; + */ +int proc_listpidspath(uint32_t type, + uint32_t typeinfo, + const char *path, + uint32_t pathflags, + void *buffer, + int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); + +int proc_listpids(uint32_t type, uint32_t typeinfo, void *buffer, int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +int proc_listallpids(void * buffer, int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_1); +int proc_listpgrppids(pid_t pgrpid, void * buffer, int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_1); +int proc_listchildpids(pid_t ppid, void * buffer, int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_1); +int proc_pidinfo(int pid, int flavor, uint64_t arg, void *buffer, int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +int proc_pidfdinfo(int pid, int fd, int flavor, void * buffer, int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +int proc_pidfileportinfo(int pid, uint32_t fileport, int flavor, void *buffer, int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3); +int proc_name(int pid, void * buffer, uint32_t buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +int proc_regionfilename(int pid, uint64_t address, void * buffer, uint32_t buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +int proc_kmsgbuf(void * buffer, uint32_t buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +int proc_pidpath(int pid, void * buffer, uint32_t buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +int proc_pidpath_audittoken(audit_token_t *audittoken, void * buffer, uint32_t buffersize) API_AVAILABLE(macos(11.0), ios(14.0), watchos(7.0), tvos(14.0)); +int proc_libversion(int *major, int * minor) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); + +/* + * Return resource usage information for the given pid, which can be a live process or a zombie. + * + * Returns 0 on success; or -1 on failure, with errno set to indicate the specific error. + */ +int proc_pid_rusage(int pid, int flavor, rusage_info_t *buffer) __OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0); + +/* + * A process can use the following api to set its own process control + * state on resoure starvation. The argument can have one of the PROC_SETPC_XX values + */ +#define PROC_SETPC_NONE 0 +#define PROC_SETPC_THROTTLEMEM 1 +#define PROC_SETPC_SUSPEND 2 +#define PROC_SETPC_TERMINATE 3 + +int proc_setpcontrol(const int control) __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2); +int proc_setpcontrol(const int control); + +int proc_track_dirty(pid_t pid, uint32_t flags); +int proc_set_dirty(pid_t pid, bool dirty); +int proc_get_dirty(pid_t pid, uint32_t *flags); +int proc_clear_dirty(pid_t pid, uint32_t flags); + +int proc_terminate(pid_t pid, int *sig); + +/* + * NO_SMT means that on an SMT CPU, this thread must be scheduled alone, + * with the paired CPU idle. + * + * Set NO_SMT on the current proc (all existing and future threads) + * This attribute is inherited on fork and exec + */ +int proc_set_no_smt(void) __API_AVAILABLE(macos(11.0)); + +/* Set NO_SMT on the current thread */ +int proc_setthread_no_smt(void) __API_AVAILABLE(macos(11.0)); + +/* + * CPU Security Mitigation APIs + * + * Set CPU security mitigation on the current proc (all existing and future threads) + * This attribute is inherited on fork and exec + */ +int proc_set_csm(uint32_t flags) __API_AVAILABLE(macos(11.0)); + +/* Set CPU security mitigation on the current thread */ +int proc_setthread_csm(uint32_t flags) __API_AVAILABLE(macos(11.0)); + +/* + * flags for CPU Security Mitigation APIs + * PROC_CSM_ALL should be used in most cases, + * the individual flags are provided only for performance evaluation etc + */ +#define PROC_CSM_ALL 0x0001 /* Set all available mitigations */ +#define PROC_CSM_NOSMT 0x0002 /* Set NO_SMT - see above */ +#define PROC_CSM_TECS 0x0004 /* Execute VERW on every return to user mode */ + +#ifdef PRIVATE +#include +/* + * Enumerate potential userspace pointers embedded in kernel data structures. + * Currently inspects kqueues only. + * + * NOTE: returned "pointers" are opaque user-supplied values and thus not + * guaranteed to address valid objects or be pointers at all. + * + * Returns the number of pointers found (which may exceed buffersize), or -1 on + * failure and errno set appropriately. + */ +int proc_list_uptrs(pid_t pid, uint64_t *buffer, uint32_t buffersize); + +int proc_list_dynkqueueids(int pid, kqueue_id_t *buf, uint32_t bufsz); +int proc_piddynkqueueinfo(int pid, int flavor, kqueue_id_t kq_id, void *buffer, + int buffersize); +#endif /* PRIVATE */ + +int proc_udata_info(int pid, int flavor, void *buffer, int buffersize); + +__END_DECLS + +#endif /*_LIBPROC_H_ */ diff --git a/lib/libc/include/x86_64-macos-gnu/net/route.h b/lib/libc/include/x86_64-macos-gnu/net/route.h new file mode 100644 index 0000000000..ff5abdd268 --- /dev/null +++ b/lib/libc/include/x86_64-macos-gnu/net/route.h @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2000-2017 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/* + * Copyright (c) 1980, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)route.h 8.3 (Berkeley) 4/19/94 + * $FreeBSD: src/sys/net/route.h,v 1.36.2.1 2000/08/16 06:14:23 jayanth Exp $ + */ + +#ifndef _NET_ROUTE_H_ +#define _NET_ROUTE_H_ +#include +#include +#include +#include + +/* + * These numbers are used by reliable protocols for determining + * retransmission behavior and are included in the routing structure. + */ +struct rt_metrics { + u_int32_t rmx_locks; /* Kernel leaves these values alone */ + u_int32_t rmx_mtu; /* MTU for this path */ + u_int32_t rmx_hopcount; /* max hops expected */ + int32_t rmx_expire; /* lifetime for route, e.g. redirect */ + u_int32_t rmx_recvpipe; /* inbound delay-bandwidth product */ + u_int32_t rmx_sendpipe; /* outbound delay-bandwidth product */ + u_int32_t rmx_ssthresh; /* outbound gateway buffer limit */ + u_int32_t rmx_rtt; /* estimated round trip time */ + u_int32_t rmx_rttvar; /* estimated rtt variance */ + u_int32_t rmx_pksent; /* packets sent using this route */ + u_int32_t rmx_state; /* route state */ + u_int32_t rmx_filler[3]; /* will be used for TCP's peer-MSS cache */ +}; + +/* + * rmx_rtt and rmx_rttvar are stored as microseconds; + */ +#define RTM_RTTUNIT 1000000 /* units for rtt, rttvar, as units per sec */ + + + +#define RTF_UP 0x1 /* route usable */ +#define RTF_GATEWAY 0x2 /* destination is a gateway */ +#define RTF_HOST 0x4 /* host entry (net otherwise) */ +#define RTF_REJECT 0x8 /* host or net unreachable */ +#define RTF_DYNAMIC 0x10 /* created dynamically (by redirect) */ +#define RTF_MODIFIED 0x20 /* modified dynamically (by redirect) */ +#define RTF_DONE 0x40 /* message confirmed */ +#define RTF_DELCLONE 0x80 /* delete cloned route */ +#define RTF_CLONING 0x100 /* generate new routes on use */ +#define RTF_XRESOLVE 0x200 /* external daemon resolves name */ +#define RTF_LLINFO 0x400 /* DEPRECATED - exists ONLY for backward + * compatibility */ +#define RTF_LLDATA 0x400 /* used by apps to add/del L2 entries */ +#define RTF_STATIC 0x800 /* manually added */ +#define RTF_BLACKHOLE 0x1000 /* just discard pkts (during updates) */ +#define RTF_NOIFREF 0x2000 /* not eligible for RTF_IFREF */ +#define RTF_PROTO2 0x4000 /* protocol specific routing flag */ +#define RTF_PROTO1 0x8000 /* protocol specific routing flag */ + +#define RTF_PRCLONING 0x10000 /* protocol requires cloning */ +#define RTF_WASCLONED 0x20000 /* route generated through cloning */ +#define RTF_PROTO3 0x40000 /* protocol specific routing flag */ + /* 0x80000 unused */ +#define RTF_PINNED 0x100000 /* future use */ +#define RTF_LOCAL 0x200000 /* route represents a local address */ +#define RTF_BROADCAST 0x400000 /* route represents a bcast address */ +#define RTF_MULTICAST 0x800000 /* route represents a mcast address */ +#define RTF_IFSCOPE 0x1000000 /* has valid interface scope */ +#define RTF_CONDEMNED 0x2000000 /* defunct; no longer modifiable */ +#define RTF_IFREF 0x4000000 /* route holds a ref to interface */ +#define RTF_PROXY 0x8000000 /* proxying, no interface scope */ +#define RTF_ROUTER 0x10000000 /* host is a router */ +#define RTF_DEAD 0x20000000 /* Route entry is being freed */ + /* 0x40000000 and up unassigned */ + +#define RTPRF_OURS RTF_PROTO3 /* set on routes we manage */ +#define RTF_BITS \ + "\020\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE" \ + "\10DELCLONE\11CLONING\12XRESOLVE\13LLINFO\14STATIC\15BLACKHOLE" \ + "\16NOIFREF\17PROTO2\20PROTO1\21PRCLONING\22WASCLONED\23PROTO3" \ + "\25PINNED\26LOCAL\27BROADCAST\30MULTICAST\31IFSCOPE\32CONDEMNED" \ + "\33IFREF\34PROXY\35ROUTER" + +#define IS_DIRECT_HOSTROUTE(rt) \ + (((rt)->rt_flags & (RTF_HOST | RTF_GATEWAY)) == RTF_HOST) +/* + * Routing statistics. + */ +struct rtstat { + short rts_badredirect; /* bogus redirect calls */ + short rts_dynamic; /* routes created by redirects */ + short rts_newgateway; /* routes modified by redirects */ + short rts_unreach; /* lookups which failed */ + short rts_wildcard; /* lookups satisfied by a wildcard */ + short rts_badrtgwroute; /* route to gateway is not direct */ +}; + +/* + * Structures for routing messages. + */ +struct rt_msghdr { + u_short rtm_msglen; /* to skip over non-understood messages */ + u_char rtm_version; /* future binary compatibility */ + u_char rtm_type; /* message type */ + u_short rtm_index; /* index for associated ifp */ + int rtm_flags; /* flags, incl. kern & message, e.g. DONE */ + int rtm_addrs; /* bitmask identifying sockaddrs in msg */ + pid_t rtm_pid; /* identify sender */ + int rtm_seq; /* for sender to identify action */ + int rtm_errno; /* why failed */ + int rtm_use; /* from rtentry */ + u_int32_t rtm_inits; /* which metrics we are initializing */ + struct rt_metrics rtm_rmx; /* metrics themselves */ +}; + +struct rt_msghdr2 { + u_short rtm_msglen; /* to skip over non-understood messages */ + u_char rtm_version; /* future binary compatibility */ + u_char rtm_type; /* message type */ + u_short rtm_index; /* index for associated ifp */ + int rtm_flags; /* flags, incl. kern & message, e.g. DONE */ + int rtm_addrs; /* bitmask identifying sockaddrs in msg */ + int32_t rtm_refcnt; /* reference count */ + int rtm_parentflags; /* flags of the parent route */ + int rtm_reserved; /* reserved field set to 0 */ + int rtm_use; /* from rtentry */ + u_int32_t rtm_inits; /* which metrics we are initializing */ + struct rt_metrics rtm_rmx; /* metrics themselves */ +}; + + +#define RTM_VERSION 5 /* Up the ante and ignore older versions */ + +/* + * Message types. + */ +#define RTM_ADD 0x1 /* Add Route */ +#define RTM_DELETE 0x2 /* Delete Route */ +#define RTM_CHANGE 0x3 /* Change Metrics or flags */ +#define RTM_GET 0x4 /* Report Metrics */ +#define RTM_LOSING 0x5 /* RTM_LOSING is no longer generated by xnu + * and is deprecated */ +#define RTM_REDIRECT 0x6 /* Told to use different route */ +#define RTM_MISS 0x7 /* Lookup failed on this address */ +#define RTM_LOCK 0x8 /* fix specified metrics */ +#define RTM_OLDADD 0x9 /* caused by SIOCADDRT */ +#define RTM_OLDDEL 0xa /* caused by SIOCDELRT */ +#define RTM_RESOLVE 0xb /* req to resolve dst to LL addr */ +#define RTM_NEWADDR 0xc /* address being added to iface */ +#define RTM_DELADDR 0xd /* address being removed from iface */ +#define RTM_IFINFO 0xe /* iface going up/down etc. */ +#define RTM_NEWMADDR 0xf /* mcast group membership being added to if */ +#define RTM_DELMADDR 0x10 /* mcast group membership being deleted */ +#define RTM_IFINFO2 0x12 /* */ +#define RTM_NEWMADDR2 0x13 /* */ +#define RTM_GET2 0x14 /* */ + +/* + * Bitmask values for rtm_inits and rmx_locks. + */ +#define RTV_MTU 0x1 /* init or lock _mtu */ +#define RTV_HOPCOUNT 0x2 /* init or lock _hopcount */ +#define RTV_EXPIRE 0x4 /* init or lock _expire */ +#define RTV_RPIPE 0x8 /* init or lock _recvpipe */ +#define RTV_SPIPE 0x10 /* init or lock _sendpipe */ +#define RTV_SSTHRESH 0x20 /* init or lock _ssthresh */ +#define RTV_RTT 0x40 /* init or lock _rtt */ +#define RTV_RTTVAR 0x80 /* init or lock _rttvar */ + +/* + * Bitmask values for rtm_addrs. + */ +#define RTA_DST 0x1 /* destination sockaddr present */ +#define RTA_GATEWAY 0x2 /* gateway sockaddr present */ +#define RTA_NETMASK 0x4 /* netmask sockaddr present */ +#define RTA_GENMASK 0x8 /* cloning mask sockaddr present */ +#define RTA_IFP 0x10 /* interface name sockaddr present */ +#define RTA_IFA 0x20 /* interface addr sockaddr present */ +#define RTA_AUTHOR 0x40 /* sockaddr for author of redirect */ +#define RTA_BRD 0x80 /* for NEWADDR, broadcast or p-p dest addr */ + +/* + * Index offsets for sockaddr array for alternate internal encoding. + */ +#define RTAX_DST 0 /* destination sockaddr present */ +#define RTAX_GATEWAY 1 /* gateway sockaddr present */ +#define RTAX_NETMASK 2 /* netmask sockaddr present */ +#define RTAX_GENMASK 3 /* cloning mask sockaddr present */ +#define RTAX_IFP 4 /* interface name sockaddr present */ +#define RTAX_IFA 5 /* interface addr sockaddr present */ +#define RTAX_AUTHOR 6 /* sockaddr for author of redirect */ +#define RTAX_BRD 7 /* for NEWADDR, broadcast or p-p dest addr */ +#define RTAX_MAX 8 /* size of array to allocate */ + +struct rt_addrinfo { + int rti_addrs; + struct sockaddr *rti_info[RTAX_MAX]; +}; + + +#endif /* _NET_ROUTE_H_ */ diff --git a/lib/libc/include/x86_64-macos-gnu/os/clock.h b/lib/libc/include/x86_64-macos-gnu/os/clock.h new file mode 100644 index 0000000000..665e1d8716 --- /dev/null +++ b/lib/libc/include/x86_64-macos-gnu/os/clock.h @@ -0,0 +1,18 @@ +#ifndef __OS_CLOCK__ +#define __OS_CLOCK__ + +#include +#include + +/* + * @typedef os_clockid_t + * + * @abstract + * Describes the kind of clock that the workgroup timestamp parameters are + * specified in + */ +OS_ENUM(os_clockid, uint32_t, + OS_CLOCK_MACH_ABSOLUTE_TIME = 32, +); + +#endif /* __OS_CLOCK__ */ diff --git a/lib/libc/include/x86_64-macos-gnu/os/workgroup.h b/lib/libc/include/x86_64-macos-gnu/os/workgroup.h new file mode 100644 index 0000000000..96b870c10c --- /dev/null +++ b/lib/libc/include/x86_64-macos-gnu/os/workgroup.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 Apple Inc. All rights reserved. + * + * @APPLE_APACHE_LICENSE_HEADER_START@ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @APPLE_APACHE_LICENSE_HEADER_END@ + */ + +#ifndef __OS_WORKGROUP__ +#define __OS_WORKGROUP__ + +#ifndef __DISPATCH_BUILDING_DISPATCH__ +#ifndef __OS_WORKGROUP_INDIRECT__ +#define __OS_WORKGROUP_INDIRECT__ +#endif /* __OS_WORKGROUP_INDIRECT__ */ + +#include +#include +#include +#include + +#undef __OS_WORKGROUP_INDIRECT__ +#endif /* __DISPATCH_BUILDING_DISPATCH__ */ + +#endif /* __OS_WORKGROUP__ */ diff --git a/lib/libc/include/x86_64-macos-gnu/os/workgroup_base.h b/lib/libc/include/x86_64-macos-gnu/os/workgroup_base.h new file mode 100644 index 0000000000..3983f002ae --- /dev/null +++ b/lib/libc/include/x86_64-macos-gnu/os/workgroup_base.h @@ -0,0 +1,78 @@ +#ifndef __OS_WORKGROUP_BASE__ +#define __OS_WORKGROUP_BASE__ + +#ifndef __OS_WORKGROUP_INDIRECT__ +#error "Please #include instead of this file directly." +#endif + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#if __has_feature(assume_nonnull) +#define OS_WORKGROUP_ASSUME_NONNULL_BEGIN _Pragma("clang assume_nonnull begin") +#define OS_WORKGROUP_ASSUME_NONNULL_END _Pragma("clang assume_nonnull end") +#else +#define OS_WORKGROUP_ASSUME_NONNULL_BEGIN +#define OS_WORKGROUP_ASSUME_NONNULL_END +#endif +#define OS_WORKGROUP_WARN_RESULT __attribute__((__warn_unused_result__)) +#define OS_WORKGROUP_EXPORT OS_EXPORT +#define OS_WORKGROUP_RETURNS_RETAINED OS_OBJECT_RETURNS_RETAINED + +#define OS_WORKGROUP_DECL(name, swift_name) \ + OS_SWIFT_NAME(swift_name) \ + OS_OBJECT_SHOW_CLASS(name, OS_OBJECT_CLASS(object)) + +#if OS_OBJECT_USE_OBJC +#define OS_WORKGROUP_SUBCLASS_DECL_PROTO(name, swift_name, ...) \ + OS_SWIFT_NAME(swift_name) \ + OS_OBJECT_DECL_PROTOCOL(name ## __VA_ARGS__ ) +#else +#define OS_WORKGROUP_SUBCLASS_DECL_PROTO(name, swift_name, ...) +#endif + +#define OS_WORKGROUP_SUBCLASS_DECL(name, super, swift_name, ...) \ + OS_SWIFT_NAME(swift_name) \ + OS_OBJECT_SHOW_SUBCLASS(name, super, name, ## __VA_ARGS__) + +#if defined(__LP64__) +#define __OS_WORKGROUP_ATTR_SIZE__ 60 +#define __OS_WORKGROUP_INTERVAL_DATA_SIZE__ 56 +#define __OS_WORKGROUP_JOIN_TOKEN_SIZE__ 36 +#else +#define __OS_WORKGROUP_ATTR_SIZE__ 60 +#define __OS_WORKGROUP_INTERVAL_DATA_SIZE__ 56 +#define __OS_WORKGROUP_JOIN_TOKEN_SIZE__ 28 +#endif + +#define _OS_WORKGROUP_ATTR_SIG_DEFAULT_INIT 0x2FA863B4 +#define _OS_WORKGROUP_ATTR_SIG_EMPTY_INIT 0x2FA863C4 + +struct OS_REFINED_FOR_SWIFT os_workgroup_attr_opaque_s { + uint32_t sig; + char opaque[__OS_WORKGROUP_ATTR_SIZE__]; +}; + +#define _OS_WORKGROUP_INTERVAL_DATA_SIG_INIT 0x52A74C4D +struct OS_REFINED_FOR_SWIFT os_workgroup_interval_data_opaque_s { + uint32_t sig; + char opaque[__OS_WORKGROUP_INTERVAL_DATA_SIZE__]; +}; + +struct OS_REFINED_FOR_SWIFT os_workgroup_join_token_opaque_s { + uint32_t sig; + char opaque[__OS_WORKGROUP_JOIN_TOKEN_SIZE__]; +}; + +#endif /* __OS_WORKGROUP_BASE__ */ diff --git a/lib/libc/include/x86_64-macos-gnu/os/workgroup_interval.h b/lib/libc/include/x86_64-macos-gnu/os/workgroup_interval.h new file mode 100644 index 0000000000..8594aed529 --- /dev/null +++ b/lib/libc/include/x86_64-macos-gnu/os/workgroup_interval.h @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2020 Apple Inc. All rights reserved. + * + * @APPLE_APACHE_LICENSE_HEADER_START@ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @APPLE_APACHE_LICENSE_HEADER_END@ + */ + +#ifndef __OS_WORKGROUP_INTERVAL__ +#define __OS_WORKGROUP_INTERVAL__ + +#ifndef __OS_WORKGROUP_INDIRECT__ +#error "Please #include instead of this file directly." +#include // For header doc +#endif + +__BEGIN_DECLS + +OS_WORKGROUP_ASSUME_NONNULL_BEGIN + +/*! + * @typedef os_workgroup_interval_t + * + * @abstract + * A subclass of an os_workgroup_t for tracking work performed as part of + * a repeating interval-driven workload. + */ +OS_WORKGROUP_SUBCLASS_DECL_PROTO(os_workgroup_interval, Repeatable); +OS_WORKGROUP_SUBCLASS_DECL(os_workgroup_interval, os_workgroup, WorkGroupInterval); + +/* During the first instance of this API, the only supported interval + * workgroups are for audio workloads. Please refer to the AudioToolbox + * framework for more information. + */ + +/* + * @typedef os_workgroup_interval_data, os_workgroup_interval_data_t + * + * @abstract + * An opaque structure containing additional configuration for the workgroup + * interval. + */ +typedef struct os_workgroup_interval_data_opaque_s os_workgroup_interval_data_s; +typedef struct os_workgroup_interval_data_opaque_s *os_workgroup_interval_data_t; +#define OS_WORKGROUP_INTERVAL_DATA_INITIALIZER \ + { .sig = _OS_WORKGROUP_INTERVAL_DATA_SIG_INIT } + +/*! + * @function os_workgroup_interval_start + * + * @abstract + * Indicates to the system that the member threads of this + * os_workgroup_interval_t have begun working on an instance of the repeatable + * interval workload with the specified timestamps. This function is real time + * safe. + * + * This function will set and return an errno in the following cases: + * + * - The current thread is not a member of the os_workgroup_interval_t + * - The os_workgroup_interval_t has been cancelled + * - The timestamps passed in are malformed + * - os_workgroup_interval_start() was previously called on the + * os_workgroup_interval_t without an intervening os_workgroup_interval_finish() + * - A concurrent workgroup interval configuration operation is taking place. + * + * @param start + * Start timestamp specified in the os_clockid_t with which the + * os_workgroup_interval_t was created. This is generally a time in the past and + * indicates when the workgroup started working on an interval period + * + * @param deadline + * Deadline timestamp specified in the os_clockid_t with which the + * os_workgroup_interval_t was created. This specifies the deadline which the + * interval period would like to meet. + * + * @param data + * This field is currently unused and should be NULL + */ +API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0), watchos(7.0)) +OS_REFINED_FOR_SWIFT OS_WORKGROUP_EXPORT OS_WORKGROUP_WARN_RESULT +int +os_workgroup_interval_start(os_workgroup_interval_t wg, uint64_t start, uint64_t + deadline, os_workgroup_interval_data_t _Nullable data); + +/*! + * @function os_workgroup_interval_update + * + * @abstract + * Updates an already started interval workgroup to have the new + * deadline specified. This function is real time safe. + * + * This function will return an error in the following cases: + * - The current thread is not a member of the os_workgroup_interval_t + * - The os_workgroup_interval_t has been cancelled + * - The timestamp passed in is malformed + * - os_workgroup_interval_start() was not previously called on the + * os_workgroup_interval_t or was already matched with an + * os_workgroup_interval_finish() + * - A concurrent workgroup interval configuration operation is taking place + * + * @param deadline + * Timestamp specified in the os_clockid_t with + * which the os_workgroup_interval_t was created. + * + * @param data + * This field is currently unused and should be NULL + */ +API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0), watchos(7.0)) +OS_REFINED_FOR_SWIFT OS_WORKGROUP_EXPORT OS_WORKGROUP_WARN_RESULT +int +os_workgroup_interval_update(os_workgroup_interval_t wg, uint64_t deadline, + os_workgroup_interval_data_t _Nullable data); + +/*! + * @function os_workgroup_interval_finish + * + * @abstract + * Indicates to the system that the member threads of + * this os_workgroup_interval_t have finished working on the current instance + * of the interval workload. This function is real time safe. + * + * This function will return an error in the following cases: + * - The current thread is not a member of the os_workgroup_interval_t + * - os_workgroup_interval_start() was not previously called on the + * os_workgroup_interval_t or was already matched with an + * os_workgroup_interval_finish() + * - A concurrent workgroup interval configuration operation is taking place. + * + * @param data + * This field is currently unused and should be NULL + * + */ +API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0), watchos(7.0)) +OS_REFINED_FOR_SWIFT OS_WORKGROUP_EXPORT OS_WORKGROUP_WARN_RESULT +int +os_workgroup_interval_finish(os_workgroup_interval_t wg, + os_workgroup_interval_data_t _Nullable data); + +OS_WORKGROUP_ASSUME_NONNULL_END + +__END_DECLS + +#endif /* __OS_WORKGROUP_INTERVAL__ */ diff --git a/lib/libc/include/x86_64-macos-gnu/os/workgroup_object.h b/lib/libc/include/x86_64-macos-gnu/os/workgroup_object.h new file mode 100644 index 0000000000..984d3e2e80 --- /dev/null +++ b/lib/libc/include/x86_64-macos-gnu/os/workgroup_object.h @@ -0,0 +1,357 @@ +/* + * Copyright (c) 2020 Apple Inc. All rights reserved. + * + * @APPLE_APACHE_LICENSE_HEADER_START@ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @APPLE_APACHE_LICENSE_HEADER_END@ + */ + +#ifndef __OS_WORKGROUP_OBJECT__ +#define __OS_WORKGROUP_OBJECT__ + +#ifndef __OS_WORKGROUP_INDIRECT__ +#error "Please #include instead of this file directly." +#include // For header doc +#endif + +__BEGIN_DECLS + +OS_WORKGROUP_ASSUME_NONNULL_BEGIN + +/*! + * @typedef os_workgroup_t + * + * @abstract + * A reference counted os object representing a workload that needs to + * be distinctly recognized and tracked by the system. The workgroup + * tracks a collection of threads all working cooperatively. An os_workgroup + * object - when not an instance of a specific os_workgroup_t subclass - + * represents a generic workload and makes no assumptions about the kind of + * work done. + * + * @discussion + * Threads can explicitly join an os_workgroup_t to mark themselves as + * participants in the workload. + */ +OS_WORKGROUP_DECL(os_workgroup, WorkGroup); + + +/* Attribute creation and specification */ + +/*! + * @typedef os_workgroup_attr_t + * + * @abstract + * Pointer to an opaque structure for describing attributes that can be + * configured on a workgroup at creation. + */ +typedef struct os_workgroup_attr_opaque_s os_workgroup_attr_s; +typedef struct os_workgroup_attr_opaque_s *os_workgroup_attr_t; + +/* os_workgroup_t attributes need to be initialized before use. This initializer + * allows you to create a workgroup with the system default attributes. */ +#define OS_WORKGROUP_ATTR_INITIALIZER_DEFAULT \ + { .sig = _OS_WORKGROUP_ATTR_SIG_DEFAULT_INIT } + + + +/* The main use of the workgroup API is through instantiations of the concrete + * subclasses - please refer to os/workgroup_interval.h and + * os/workgroup_parallel.h for more information on creating workgroups. + * + * The functions below operate on all subclasses of os_workgroup_t. + */ + +/*! + * @function os_workgroup_copy_port + * + * @abstract + * Returns a reference to a send right representing this workgroup that is to be + * sent to other processes. This port is to be passed to + * os_workgroup_create_with_port() to create a workgroup object. + * + * It is the client's responsibility to release the send right reference. + * + * If an error is encountered, errno is set and returned. + */ +API_AVAILABLE(macos(11.0)) +API_UNAVAILABLE(ios, tvos, watchos) +OS_REFINED_FOR_SWIFT OS_WORKGROUP_EXPORT OS_WORKGROUP_WARN_RESULT +int +os_workgroup_copy_port(os_workgroup_t wg, mach_port_t *mach_port_out); + +/*! + * @function os_workgroup_create_with_port + * + * @abstract + * Create an os_workgroup_t object from a send right returned by a previous + * call to os_workgroup_copy_port, potentially in a different process. + * + * A newly created os_workgroup_t has no initial member threads - in particular + * the creating thread does not join the os_workgroup_t implicitly. + * + * @param name + * A client specified string for labelling the workgroup. This parameter is + * optional and can be NULL. + * + * @param mach_port + * The send right to create the workgroup from. No reference is consumed + * on the specified send right. + */ +API_AVAILABLE(macos(11.0)) +API_UNAVAILABLE(ios, tvos, watchos) +OS_SWIFT_NAME(WorkGroup.init(__name:port:)) OS_WORKGROUP_EXPORT OS_WORKGROUP_RETURNS_RETAINED +os_workgroup_t _Nullable +os_workgroup_create_with_port(const char *_Nullable name, mach_port_t mach_port); + +/*! + * @function os_workgroup_create_with_workgroup + * + * @abstract + * Create a new os_workgroup object from an existing os_workgroup. + * + * The newly created os_workgroup has no initial member threads - in particular + * the creating threaad does not join the os_workgroup_t implicitly. + * + * @param name + * A client specified string for labelling the workgroup. This parameter is + * optional and can be NULL. + * + * @param wg + * The existing workgroup to create a new workgroup object from. + */ +API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0), watchos(7.0)) +OS_REFINED_FOR_SWIFT OS_WORKGROUP_EXPORT OS_WORKGROUP_RETURNS_RETAINED +os_workgroup_t _Nullable +os_workgroup_create_with_workgroup(const char * _Nullable name, os_workgroup_t wg); + +/*! + * @typedef os_workgroup_join_token, os_workgroup_join_token_t + * + * @abstract + * An opaque join token which the client needs to pass to os_workgroup_join + * and os_workgroup_leave + */ +OS_REFINED_FOR_SWIFT +typedef struct os_workgroup_join_token_opaque_s os_workgroup_join_token_s; +OS_REFINED_FOR_SWIFT +typedef struct os_workgroup_join_token_opaque_s *os_workgroup_join_token_t; + + +/*! + * @function os_workgroup_join + * + * @abstract + * Joins the current thread to the specified workgroup and populates the join + * token that has been passed in. This API is real-time safe. + * + * @param wg + * The workgroup that the current thread would like to join + * + * @param token_out + * Pointer to a client allocated struct which the function will populate + * with the join token. This token must be passed in by the thread when it calls + * os_workgroup_leave(). + * + * Errors will be returned in the following cases: + * + * EALREADY The thread is already part of a workgroup that the specified + * workgroup does not nest with + * EINVAL The workgroup has been cancelled + */ +API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0), watchos(7.0)) +OS_REFINED_FOR_SWIFT OS_WORKGROUP_EXPORT OS_WORKGROUP_WARN_RESULT +int +os_workgroup_join(os_workgroup_t wg, os_workgroup_join_token_t token_out); + +/*! + * @function os_workgroup_leave + * + * @abstract + * This removes the current thread from a workgroup it has previously + * joined. Threads must leave all workgroups in the reverse order that they + * have joined them. Failing to do so before exiting will result in undefined + * behavior. + * + * If the join token is malformed, the process will be aborted. + * + * This API is real time safe. + * + * @param wg + * The workgroup that the current thread would like to leave. + * + * @param token + * This is the join token populated by the most recent call to + * os_workgroup_join(). + */ +API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0), watchos(7.0)) +OS_REFINED_FOR_SWIFT OS_WORKGROUP_EXPORT +void +os_workgroup_leave(os_workgroup_t wg, os_workgroup_join_token_t token); + +/* Working Arena index of a thread in a workgroup */ +typedef uint32_t os_workgroup_index; +/* Destructor for Working Arena */ +typedef void (*os_workgroup_working_arena_destructor_t)(void * _Nullable); + +/*! + * @function os_workgroup_set_working_arena + * + * @abstract + * Associates a client defined working arena with the workgroup. The arena + * is local to the workgroup object in the process. This is intended for + * distributing a manually managed memory allocation between member threads + * of the workgroup. + * + * This function can be called multiple times and the client specified + * destructor will be called on the previously assigned arena, if any. This + * function can only be called when no threads have currently joined the + * workgroup and all workloops associated with the workgroup are idle. + * + * @param wg + * The workgroup to associate the working arena with + * + * @param arena + * The client managed arena to associate with the workgroup. This value can + * be NULL. + * + * @param max_workers + * The maximum number of threads that will ever query the workgroup for the + * arena and request an index into it. If the arena is not used to partition + * work amongst member threads, then this field can be 0. + * + * @param destructor + * A destructor to call on the previously assigned working arena, if any + */ +API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0), watchos(7.0)) +OS_REFINED_FOR_SWIFT OS_WORKGROUP_EXPORT OS_WORKGROUP_WARN_RESULT +int +os_workgroup_set_working_arena(os_workgroup_t wg, void * _Nullable arena, + uint32_t max_workers, os_workgroup_working_arena_destructor_t destructor); + +/*! + * @function os_workgroup_get_working_arena + * + * @abstract + * Returns the working arena associated with the workgroup and the current + * thread's index in the workgroup. This function can only be called by a member + * of the workgroup. Multiple calls to this API by a member thread will return + * the same arena and index until the thread leaves the workgroup. + * + * For workloops with an associated workgroup, every work item on the workloop + * will receive the same index in the arena. + * + * This method returns NULL if no arena is set on the workgroup. The index + * returned by this function is zero-based and is namespaced per workgroup + * object in the process. The indices provided are strictly monotonic and never + * reused until a future call to os_workgroup_set_working_arena. + * + * @param wg + * The workgroup to get the working arena from. + * + * @param index_out + * A pointer to a os_workgroup_index which will be populated by the caller's + * index in the workgroup. + */ +API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0), watchos(7.0)) +OS_REFINED_FOR_SWIFT OS_WORKGROUP_EXPORT +void * _Nullable +os_workgroup_get_working_arena(os_workgroup_t wg, + os_workgroup_index * _Nullable index_out); + +/*! + * @function os_workgroup_cancel + * + * @abstract + * This API invalidates a workgroup and indicates to the system that the + * workload is no longer relevant to the caller. + * + * No new work should be initiated for a cancelled workgroup and + * work that is already underway should periodically check for + * cancellation with os_workgroup_testcancel and initiate cleanup if needed. + * + * Threads currently in the workgroup continue to be tracked together but no + * new threads may join this workgroup - the only possible operation allowed is + * to leave the workgroup. Other actions may have undefined behavior or + * otherwise fail. + * + * This API is idempotent. Cancellation is local to the workgroup object + * it is called on and does not affect other workgroups. + * + * @param wg + * The workgroup that that the thread would like to cancel + */ +API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0), watchos(7.0)) +OS_REFINED_FOR_SWIFT OS_WORKGROUP_EXPORT +void +os_workgroup_cancel(os_workgroup_t wg); + +/*! + * @function os_workgroup_testcancel + * + * @abstract + * Returns true if the workgroup object has been cancelled. See also + * os_workgroup_cancel + */ +API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0), watchos(7.0)) +OS_REFINED_FOR_SWIFT OS_WORKGROUP_EXPORT +bool +os_workgroup_testcancel(os_workgroup_t wg); + +/*! + * @typedef os_workgroup_max_parallel_threads_attr_t + * + * @abstract + * A pointer to a structure describing the set of properties of a workgroup to + * override with the explicitly specified values in the structure. + * + * See also os_workgroup_max_parallel_threads. + */ +OS_REFINED_FOR_SWIFT +typedef struct os_workgroup_max_parallel_threads_attr_s os_workgroup_mpt_attr_s; +OS_REFINED_FOR_SWIFT +typedef struct os_workgroup_max_parallel_threads_attr_s *os_workgroup_mpt_attr_t; + +/*! + * @function os_workgroup_max_parallel_threads + * + * @abstract + * Returns the system's recommendation for maximum number of threads the client + * should make for a multi-threaded workload in a given workgroup. + * + * This API takes into consideration the current hardware the code is running on + * and the attributes of the workgroup. It does not take into consideration the + * current load of the system and therefore always provides the most optimal + * recommendation for the workload. + * + * @param wg + * The workgroup in which the multi-threaded workload will be performed in. The + * threads performing the multi-threaded workload are expected to join this + * workgroup. + * + * @param attr + * This value is currently unused and should be NULL. + */ +API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0), watchos(7.0)) +OS_REFINED_FOR_SWIFT OS_WORKGROUP_EXPORT +int +os_workgroup_max_parallel_threads(os_workgroup_t wg, os_workgroup_mpt_attr_t + _Nullable attr); + +OS_WORKGROUP_ASSUME_NONNULL_END + +__END_DECLS + +#endif /* __OS_WORKGROUP_OBJECT__ */ diff --git a/lib/libc/include/x86_64-macos-gnu/os/workgroup_parallel.h b/lib/libc/include/x86_64-macos-gnu/os/workgroup_parallel.h new file mode 100644 index 0000000000..48d232e47a --- /dev/null +++ b/lib/libc/include/x86_64-macos-gnu/os/workgroup_parallel.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2020 Apple Inc. All rights reserved. + * + * @APPLE_APACHE_LICENSE_HEADER_START@ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @APPLE_APACHE_LICENSE_HEADER_END@ + */ + +#ifndef __OS_WORKGROUP_PARALLEL__ +#define __OS_WORKGROUP_PARALLEL__ + +#ifndef __OS_WORKGROUP_INDIRECT__ +#error "Please #include instead of this file directly." +#include // For header doc +#endif + +#include + +__BEGIN_DECLS + +OS_WORKGROUP_ASSUME_NONNULL_BEGIN + +/*! + * @typedef os_workgroup_parallel_t + * + * @abstract + * A subclass of an os_workgroup_t for tracking parallel work. + */ +OS_WORKGROUP_SUBCLASS_DECL_PROTO(os_workgroup_parallel, Parallelizable); +OS_WORKGROUP_SUBCLASS_DECL(os_workgroup_parallel, os_workgroup, WorkGroupParallel); + +/*! + * @function os_workgroup_parallel_create + * + * @abstract + * Creates an os_workgroup_t which tracks a parallel workload. + * A newly created os_workgroup_interval_t has no initial member threads - + * in particular the creating thread does not join the os_workgroup_parallel_t + * implicitly. + * + * See also os_workgroup_max_parallel_threads(). + * + * @param name + * A client specified string for labelling the workgroup. This parameter is + * optional and can be NULL. + * + * @param attr + * The requested set of workgroup attributes. NULL is to be specified for the + * default set of attributes. + */ +API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0), watchos(7.0)) +OS_WORKGROUP_EXPORT OS_WORKGROUP_RETURNS_RETAINED +OS_SWIFT_NAME(WorkGroupParallel.init(__name:attr:)) +os_workgroup_parallel_t _Nullable +os_workgroup_parallel_create(const char * _Nullable name, + os_workgroup_attr_t _Nullable attr); + +OS_WORKGROUP_ASSUME_NONNULL_END + +__END_DECLS + +#endif /* __OS_WORKGROUP_PARALLEL__ */ diff --git a/lib/libc/include/x86_64-macos-gnu/sys/kern_control.h b/lib/libc/include/x86_64-macos-gnu/sys/kern_control.h new file mode 100644 index 0000000000..be74e7d09e --- /dev/null +++ b/lib/libc/include/x86_64-macos-gnu/sys/kern_control.h @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2000-2004, 2012-2016 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/*! + * @header kern_control.h + * This header defines an API to communicate between a kernel + * extension and a process outside of the kernel. + */ + +#ifndef KPI_KERN_CONTROL_H +#define KPI_KERN_CONTROL_H + + +#include +#include +#include +#include + +/* + * Define Controller event subclass, and associated events. + * Subclass of KEV_SYSTEM_CLASS + */ + +/*! + * @defined KEV_CTL_SUBCLASS + * @discussion The kernel event subclass for kernel control events. + */ +#define KEV_CTL_SUBCLASS 2 + +/*! + * @defined KEV_CTL_REGISTERED + * @discussion The event code indicating a new controller was + * registered. The data portion will contain a ctl_event_data. + */ +#define KEV_CTL_REGISTERED 1 /* a new controller appears */ + +/*! + * @defined KEV_CTL_DEREGISTERED + * @discussion The event code indicating a controller was unregistered. + * The data portion will contain a ctl_event_data. + */ +#define KEV_CTL_DEREGISTERED 2 /* a controller disappears */ + +/*! + * @struct ctl_event_data + * @discussion This structure is used for KEV_CTL_SUBCLASS kernel + * events. + * @field ctl_id The kernel control id. + * @field ctl_unit The kernel control unit. + */ +struct ctl_event_data { + u_int32_t ctl_id; /* Kernel Controller ID */ + u_int32_t ctl_unit; +}; + +/* + * Controls destined to the Controller Manager. + */ + +/*! + * @defined CTLIOCGCOUNT + * @discussion The CTLIOCGCOUNT ioctl can be used to determine the + * number of kernel controllers registered. + */ +#define CTLIOCGCOUNT _IOR('N', 2, int) /* get number of control structures registered */ + +/*! + * @defined CTLIOCGINFO + * @discussion The CTLIOCGINFO ioctl can be used to convert a kernel + * control name to a kernel control id. + */ +#define CTLIOCGINFO _IOWR('N', 3, struct ctl_info) /* get id from name */ + + +/*! + * @defined MAX_KCTL_NAME + * @discussion Kernel control names must be no longer than + * MAX_KCTL_NAME. + */ +#define MAX_KCTL_NAME 96 + +/* + * Controls destined to the Controller Manager. + */ + +/*! + * @struct ctl_info + * @discussion This structure is used with the CTLIOCGINFO ioctl to + * translate from a kernel control name to a control id. + * @field ctl_id The kernel control id, filled out upon return. + * @field ctl_name The kernel control name to find. + */ +struct ctl_info { + u_int32_t ctl_id; /* Kernel Controller ID */ + char ctl_name[MAX_KCTL_NAME]; /* Kernel Controller Name (a C string) */ +}; + + +/*! + * @struct sockaddr_ctl + * @discussion The controller address structure is used to establish + * contact between a user client and a kernel controller. The + * sc_id/sc_unit uniquely identify each controller. sc_id is a + * unique identifier assigned to the controller. The identifier can + * be assigned by the system at registration time or be a 32-bit + * creator code obtained from Apple Computer. sc_unit is a unit + * number for this sc_id, and is privately used by the kernel + * controller to identify several instances of the controller. + * @field sc_len The length of the structure. + * @field sc_family AF_SYSTEM. + * @field ss_sysaddr AF_SYS_KERNCONTROL. + * @field sc_id Controller unique identifier. + * @field sc_unit Kernel controller private unit number. + * @field sc_reserved Reserved, must be set to zero. + */ +struct sockaddr_ctl { + u_char sc_len; /* depends on size of bundle ID string */ + u_char sc_family; /* AF_SYSTEM */ + u_int16_t ss_sysaddr; /* AF_SYS_KERNCONTROL */ + u_int32_t sc_id; /* Controller unique identifier */ + u_int32_t sc_unit; /* Developer private unit number */ + u_int32_t sc_reserved[5]; +}; + + + +#endif /* KPI_KERN_CONTROL_H */ diff --git a/lib/libc/include/x86_64-macos-gnu/sys/proc_info.h b/lib/libc/include/x86_64-macos-gnu/sys/proc_info.h new file mode 100644 index 0000000000..b6809a463b --- /dev/null +++ b/lib/libc/include/x86_64-macos-gnu/sys/proc_info.h @@ -0,0 +1,799 @@ +/* + * Copyright (c) 2005-2020 Apple Computer, Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#ifndef _SYS_PROC_INFO_H +#define _SYS_PROC_INFO_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +__BEGIN_DECLS + + +#define PROC_ALL_PIDS 1 +#define PROC_PGRP_ONLY 2 +#define PROC_TTY_ONLY 3 +#define PROC_UID_ONLY 4 +#define PROC_RUID_ONLY 5 +#define PROC_PPID_ONLY 6 +#define PROC_KDBG_ONLY 7 + +struct proc_bsdinfo { + uint32_t pbi_flags; /* 64bit; emulated etc */ + uint32_t pbi_status; + uint32_t pbi_xstatus; + uint32_t pbi_pid; + uint32_t pbi_ppid; + uid_t pbi_uid; + gid_t pbi_gid; + uid_t pbi_ruid; + gid_t pbi_rgid; + uid_t pbi_svuid; + gid_t pbi_svgid; + uint32_t rfu_1; /* reserved */ + char pbi_comm[MAXCOMLEN]; + char pbi_name[2 * MAXCOMLEN]; /* empty if no name is registered */ + uint32_t pbi_nfiles; + uint32_t pbi_pgid; + uint32_t pbi_pjobc; + uint32_t e_tdev; /* controlling tty dev */ + uint32_t e_tpgid; /* tty process group id */ + int32_t pbi_nice; + uint64_t pbi_start_tvsec; + uint64_t pbi_start_tvusec; +}; + + +struct proc_bsdshortinfo { + uint32_t pbsi_pid; /* process id */ + uint32_t pbsi_ppid; /* process parent id */ + uint32_t pbsi_pgid; /* process perp id */ + uint32_t pbsi_status; /* p_stat value, SZOMB, SRUN, etc */ + char pbsi_comm[MAXCOMLEN]; /* upto 16 characters of process name */ + uint32_t pbsi_flags; /* 64bit; emulated etc */ + uid_t pbsi_uid; /* current uid on process */ + gid_t pbsi_gid; /* current gid on process */ + uid_t pbsi_ruid; /* current ruid on process */ + gid_t pbsi_rgid; /* current tgid on process */ + uid_t pbsi_svuid; /* current svuid on process */ + gid_t pbsi_svgid; /* current svgid on process */ + uint32_t pbsi_rfu; /* reserved for future use*/ +}; + + + + +/* pbi_flags values */ +#define PROC_FLAG_SYSTEM 1 /* System process */ +#define PROC_FLAG_TRACED 2 /* process currently being traced, possibly by gdb */ +#define PROC_FLAG_INEXIT 4 /* process is working its way in exit() */ +#define PROC_FLAG_PPWAIT 8 +#define PROC_FLAG_LP64 0x10 /* 64bit process */ +#define PROC_FLAG_SLEADER 0x20 /* The process is the session leader */ +#define PROC_FLAG_CTTY 0x40 /* process has a control tty */ +#define PROC_FLAG_CONTROLT 0x80 /* Has a controlling terminal */ +#define PROC_FLAG_THCWD 0x100 /* process has a thread with cwd */ +/* process control bits for resource starvation */ +#define PROC_FLAG_PC_THROTTLE 0x200 /* In resource starvation situations, this process is to be throttled */ +#define PROC_FLAG_PC_SUSP 0x400 /* In resource starvation situations, this process is to be suspended */ +#define PROC_FLAG_PC_KILL 0x600 /* In resource starvation situations, this process is to be terminated */ +#define PROC_FLAG_PC_MASK 0x600 +/* process action bits for resource starvation */ +#define PROC_FLAG_PA_THROTTLE 0x800 /* The process is currently throttled due to resource starvation */ +#define PROC_FLAG_PA_SUSP 0x1000 /* The process is currently suspended due to resource starvation */ +#define PROC_FLAG_PSUGID 0x2000 /* process has set privileges since last exec */ +#define PROC_FLAG_EXEC 0x4000 /* process has called exec */ + + +struct proc_taskinfo { + uint64_t pti_virtual_size; /* virtual memory size (bytes) */ + uint64_t pti_resident_size; /* resident memory size (bytes) */ + uint64_t pti_total_user; /* total time */ + uint64_t pti_total_system; + uint64_t pti_threads_user; /* existing threads only */ + uint64_t pti_threads_system; + int32_t pti_policy; /* default policy for new threads */ + int32_t pti_faults; /* number of page faults */ + int32_t pti_pageins; /* number of actual pageins */ + int32_t pti_cow_faults; /* number of copy-on-write faults */ + int32_t pti_messages_sent; /* number of messages sent */ + int32_t pti_messages_received; /* number of messages received */ + int32_t pti_syscalls_mach; /* number of mach system calls */ + int32_t pti_syscalls_unix; /* number of unix system calls */ + int32_t pti_csw; /* number of context switches */ + int32_t pti_threadnum; /* number of threads in the task */ + int32_t pti_numrunning; /* number of running threads */ + int32_t pti_priority; /* task priority*/ +}; + +struct proc_taskallinfo { + struct proc_bsdinfo pbsd; + struct proc_taskinfo ptinfo; +}; + +#define MAXTHREADNAMESIZE 64 + +struct proc_threadinfo { + uint64_t pth_user_time; /* user run time */ + uint64_t pth_system_time; /* system run time */ + int32_t pth_cpu_usage; /* scaled cpu usage percentage */ + int32_t pth_policy; /* scheduling policy in effect */ + int32_t pth_run_state; /* run state (see below) */ + int32_t pth_flags; /* various flags (see below) */ + int32_t pth_sleep_time; /* number of seconds that thread */ + int32_t pth_curpri; /* cur priority*/ + int32_t pth_priority; /* priority*/ + int32_t pth_maxpriority; /* max priority*/ + char pth_name[MAXTHREADNAMESIZE]; /* thread name, if any */ +}; + +struct proc_regioninfo { + uint32_t pri_protection; + uint32_t pri_max_protection; + uint32_t pri_inheritance; + uint32_t pri_flags; /* shared, external pager, is submap */ + uint64_t pri_offset; + uint32_t pri_behavior; + uint32_t pri_user_wired_count; + uint32_t pri_user_tag; + uint32_t pri_pages_resident; + uint32_t pri_pages_shared_now_private; + uint32_t pri_pages_swapped_out; + uint32_t pri_pages_dirtied; + uint32_t pri_ref_count; + uint32_t pri_shadow_depth; + uint32_t pri_share_mode; + uint32_t pri_private_pages_resident; + uint32_t pri_shared_pages_resident; + uint32_t pri_obj_id; + uint32_t pri_depth; + uint64_t pri_address; + uint64_t pri_size; +}; + +#define PROC_REGION_SUBMAP 1 +#define PROC_REGION_SHARED 2 + +#define SM_COW 1 +#define SM_PRIVATE 2 +#define SM_EMPTY 3 +#define SM_SHARED 4 +#define SM_TRUESHARED 5 +#define SM_PRIVATE_ALIASED 6 +#define SM_SHARED_ALIASED 7 +#define SM_LARGE_PAGE 8 + + +/* + * Thread run states (state field). + */ + +#define TH_STATE_RUNNING 1 /* thread is running normally */ +#define TH_STATE_STOPPED 2 /* thread is stopped */ +#define TH_STATE_WAITING 3 /* thread is waiting normally */ +#define TH_STATE_UNINTERRUPTIBLE 4 /* thread is in an uninterruptible + * wait */ +#define TH_STATE_HALTED 5 /* thread is halted at a + * clean point */ + +/* + * Thread flags (flags field). + */ +#define TH_FLAGS_SWAPPED 0x1 /* thread is swapped out */ +#define TH_FLAGS_IDLE 0x2 /* thread is an idle thread */ + + +struct proc_workqueueinfo { + uint32_t pwq_nthreads; /* total number of workqueue threads */ + uint32_t pwq_runthreads; /* total number of running workqueue threads */ + uint32_t pwq_blockedthreads; /* total number of blocked workqueue threads */ + uint32_t pwq_state; +}; + +/* + * workqueue state (pwq_state field) + */ +#define WQ_EXCEEDED_CONSTRAINED_THREAD_LIMIT 0x1 +#define WQ_EXCEEDED_TOTAL_THREAD_LIMIT 0x2 +#define WQ_FLAGS_AVAILABLE 0x4 + +struct proc_fileinfo { + uint32_t fi_openflags; + uint32_t fi_status; + off_t fi_offset; + int32_t fi_type; + uint32_t fi_guardflags; +}; + +/* stats flags in proc_fileinfo */ +#define PROC_FP_SHARED 1 /* shared by more than one fd */ +#define PROC_FP_CLEXEC 2 /* close on exec */ +#define PROC_FP_GUARDED 4 /* guarded fd */ +#define PROC_FP_CLFORK 8 /* close on fork */ + +#define PROC_FI_GUARD_CLOSE (1u << 0) +#define PROC_FI_GUARD_DUP (1u << 1) +#define PROC_FI_GUARD_SOCKET_IPC (1u << 2) +#define PROC_FI_GUARD_FILEPORT (1u << 3) + +struct proc_exitreasonbasicinfo { + uint32_t beri_namespace; + uint64_t beri_code; + uint64_t beri_flags; + uint32_t beri_reason_buf_size; +} __attribute__((packed)); + +struct proc_exitreasoninfo { + uint32_t eri_namespace; + uint64_t eri_code; + uint64_t eri_flags; + uint32_t eri_reason_buf_size; + uint64_t eri_kcd_buf; +} __attribute__((packed)); + +/* + * A copy of stat64 with static sized fields. + */ +struct vinfo_stat { + uint32_t vst_dev; /* [XSI] ID of device containing file */ + uint16_t vst_mode; /* [XSI] Mode of file (see below) */ + uint16_t vst_nlink; /* [XSI] Number of hard links */ + uint64_t vst_ino; /* [XSI] File serial number */ + uid_t vst_uid; /* [XSI] User ID of the file */ + gid_t vst_gid; /* [XSI] Group ID of the file */ + int64_t vst_atime; /* [XSI] Time of last access */ + int64_t vst_atimensec; /* nsec of last access */ + int64_t vst_mtime; /* [XSI] Last data modification time */ + int64_t vst_mtimensec; /* last data modification nsec */ + int64_t vst_ctime; /* [XSI] Time of last status change */ + int64_t vst_ctimensec; /* nsec of last status change */ + int64_t vst_birthtime; /* File creation time(birth) */ + int64_t vst_birthtimensec; /* nsec of File creation time */ + off_t vst_size; /* [XSI] file size, in bytes */ + int64_t vst_blocks; /* [XSI] blocks allocated for file */ + int32_t vst_blksize; /* [XSI] optimal blocksize for I/O */ + uint32_t vst_flags; /* user defined flags for file */ + uint32_t vst_gen; /* file generation number */ + uint32_t vst_rdev; /* [XSI] Device ID */ + int64_t vst_qspare[2]; /* RESERVED: DO NOT USE! */ +}; + +struct vnode_info { + struct vinfo_stat vi_stat; + int vi_type; + int vi_pad; + fsid_t vi_fsid; +}; + +struct vnode_info_path { + struct vnode_info vip_vi; + char vip_path[MAXPATHLEN]; /* tail end of it */ +}; + +struct vnode_fdinfo { + struct proc_fileinfo pfi; + struct vnode_info pvi; +}; + +struct vnode_fdinfowithpath { + struct proc_fileinfo pfi; + struct vnode_info_path pvip; +}; + +struct proc_regionwithpathinfo { + struct proc_regioninfo prp_prinfo; + struct vnode_info_path prp_vip; +}; + +struct proc_regionpath { + uint64_t prpo_addr; + uint64_t prpo_regionlength; + char prpo_path[MAXPATHLEN]; +}; + +struct proc_vnodepathinfo { + struct vnode_info_path pvi_cdir; + struct vnode_info_path pvi_rdir; +}; + +struct proc_threadwithpathinfo { + struct proc_threadinfo pt; + struct vnode_info_path pvip; +}; + +/* + * Socket + */ + + +/* + * IPv4 and IPv6 Sockets + */ + +#define INI_IPV4 0x1 +#define INI_IPV6 0x2 + +struct in4in6_addr { + u_int32_t i46a_pad32[3]; + struct in_addr i46a_addr4; +}; + +struct in_sockinfo { + int insi_fport; /* foreign port */ + int insi_lport; /* local port */ + uint64_t insi_gencnt; /* generation count of this instance */ + uint32_t insi_flags; /* generic IP/datagram flags */ + uint32_t insi_flow; + + uint8_t insi_vflag; /* ini_IPV4 or ini_IPV6 */ + uint8_t insi_ip_ttl; /* time to live proto */ + uint32_t rfu_1; /* reserved */ + /* protocol dependent part */ + union { + struct in4in6_addr ina_46; + struct in6_addr ina_6; + } insi_faddr; /* foreign host table entry */ + union { + struct in4in6_addr ina_46; + struct in6_addr ina_6; + } insi_laddr; /* local host table entry */ + struct { + u_char in4_tos; /* type of service */ + } insi_v4; + struct { + uint8_t in6_hlim; + int in6_cksum; + u_short in6_ifindex; + short in6_hops; + } insi_v6; +}; + +/* + * TCP Sockets + */ + +#define TSI_T_REXMT 0 /* retransmit */ +#define TSI_T_PERSIST 1 /* retransmit persistence */ +#define TSI_T_KEEP 2 /* keep alive */ +#define TSI_T_2MSL 3 /* 2*msl quiet time timer */ +#define TSI_T_NTIMERS 4 + +#define TSI_S_CLOSED 0 /* closed */ +#define TSI_S_LISTEN 1 /* listening for connection */ +#define TSI_S_SYN_SENT 2 /* active, have sent syn */ +#define TSI_S_SYN_RECEIVED 3 /* have send and received syn */ +#define TSI_S_ESTABLISHED 4 /* established */ +#define TSI_S__CLOSE_WAIT 5 /* rcvd fin, waiting for close */ +#define TSI_S_FIN_WAIT_1 6 /* have closed, sent fin */ +#define TSI_S_CLOSING 7 /* closed xchd FIN; await FIN ACK */ +#define TSI_S_LAST_ACK 8 /* had fin and close; await FIN ACK */ +#define TSI_S_FIN_WAIT_2 9 /* have closed, fin is acked */ +#define TSI_S_TIME_WAIT 10 /* in 2*msl quiet wait after close */ +#define TSI_S_RESERVED 11 /* pseudo state: reserved */ + +struct tcp_sockinfo { + struct in_sockinfo tcpsi_ini; + int tcpsi_state; + int tcpsi_timer[TSI_T_NTIMERS]; + int tcpsi_mss; + uint32_t tcpsi_flags; + uint32_t rfu_1; /* reserved */ + uint64_t tcpsi_tp; /* opaque handle of TCP protocol control block */ +}; + +/* + * Unix Domain Sockets + */ + + +struct un_sockinfo { + uint64_t unsi_conn_so; /* opaque handle of connected socket */ + uint64_t unsi_conn_pcb; /* opaque handle of connected protocol control block */ + union { + struct sockaddr_un ua_sun; + char ua_dummy[SOCK_MAXADDRLEN]; + } unsi_addr; /* bound address */ + union { + struct sockaddr_un ua_sun; + char ua_dummy[SOCK_MAXADDRLEN]; + } unsi_caddr; /* address of socket connected to */ +}; + +/* + * PF_NDRV Sockets + */ + +struct ndrv_info { + uint32_t ndrvsi_if_family; + uint32_t ndrvsi_if_unit; + char ndrvsi_if_name[IF_NAMESIZE]; +}; + +/* + * Kernel Event Sockets + */ + +struct kern_event_info { + uint32_t kesi_vendor_code_filter; + uint32_t kesi_class_filter; + uint32_t kesi_subclass_filter; +}; + +/* + * Kernel Control Sockets + */ + +struct kern_ctl_info { + uint32_t kcsi_id; + uint32_t kcsi_reg_unit; + uint32_t kcsi_flags; /* support flags */ + uint32_t kcsi_recvbufsize; /* request more than the default buffer size */ + uint32_t kcsi_sendbufsize; /* request more than the default buffer size */ + uint32_t kcsi_unit; + char kcsi_name[MAX_KCTL_NAME]; /* unique nke identifier, provided by DTS */ +}; + +/* + * VSock Sockets + */ + +struct vsock_sockinfo { + uint32_t local_cid; + uint32_t local_port; + uint32_t remote_cid; + uint32_t remote_port; +}; + +/* soi_state */ + +#define SOI_S_NOFDREF 0x0001 /* no file table ref any more */ +#define SOI_S_ISCONNECTED 0x0002 /* socket connected to a peer */ +#define SOI_S_ISCONNECTING 0x0004 /* in process of connecting to peer */ +#define SOI_S_ISDISCONNECTING 0x0008 /* in process of disconnecting */ +#define SOI_S_CANTSENDMORE 0x0010 /* can't send more data to peer */ +#define SOI_S_CANTRCVMORE 0x0020 /* can't receive more data from peer */ +#define SOI_S_RCVATMARK 0x0040 /* at mark on input */ +#define SOI_S_PRIV 0x0080 /* privileged for broadcast, raw... */ +#define SOI_S_NBIO 0x0100 /* non-blocking ops */ +#define SOI_S_ASYNC 0x0200 /* async i/o notify */ +#define SOI_S_INCOMP 0x0800 /* Unaccepted, incomplete connection */ +#define SOI_S_COMP 0x1000 /* unaccepted, complete connection */ +#define SOI_S_ISDISCONNECTED 0x2000 /* socket disconnected from peer */ +#define SOI_S_DRAINING 0x4000 /* close waiting for blocked system calls to drain */ + +struct sockbuf_info { + uint32_t sbi_cc; + uint32_t sbi_hiwat; /* SO_RCVBUF, SO_SNDBUF */ + uint32_t sbi_mbcnt; + uint32_t sbi_mbmax; + uint32_t sbi_lowat; + short sbi_flags; + short sbi_timeo; +}; + +enum { + SOCKINFO_GENERIC = 0, + SOCKINFO_IN = 1, + SOCKINFO_TCP = 2, + SOCKINFO_UN = 3, + SOCKINFO_NDRV = 4, + SOCKINFO_KERN_EVENT = 5, + SOCKINFO_KERN_CTL = 6, + SOCKINFO_VSOCK = 7, +}; + +struct socket_info { + struct vinfo_stat soi_stat; + uint64_t soi_so; /* opaque handle of socket */ + uint64_t soi_pcb; /* opaque handle of protocol control block */ + int soi_type; + int soi_protocol; + int soi_family; + short soi_options; + short soi_linger; + short soi_state; + short soi_qlen; + short soi_incqlen; + short soi_qlimit; + short soi_timeo; + u_short soi_error; + uint32_t soi_oobmark; + struct sockbuf_info soi_rcv; + struct sockbuf_info soi_snd; + int soi_kind; + uint32_t rfu_1; /* reserved */ + union { + struct in_sockinfo pri_in; /* SOCKINFO_IN */ + struct tcp_sockinfo pri_tcp; /* SOCKINFO_TCP */ + struct un_sockinfo pri_un; /* SOCKINFO_UN */ + struct ndrv_info pri_ndrv; /* SOCKINFO_NDRV */ + struct kern_event_info pri_kern_event; /* SOCKINFO_KERN_EVENT */ + struct kern_ctl_info pri_kern_ctl; /* SOCKINFO_KERN_CTL */ + struct vsock_sockinfo pri_vsock; /* SOCKINFO_VSOCK */ + } soi_proto; +}; + +struct socket_fdinfo { + struct proc_fileinfo pfi; + struct socket_info psi; +}; + + + +struct psem_info { + struct vinfo_stat psem_stat; + char psem_name[MAXPATHLEN]; +}; + +struct psem_fdinfo { + struct proc_fileinfo pfi; + struct psem_info pseminfo; +}; + + + +struct pshm_info { + struct vinfo_stat pshm_stat; + uint64_t pshm_mappaddr; + char pshm_name[MAXPATHLEN]; +}; + +struct pshm_fdinfo { + struct proc_fileinfo pfi; + struct pshm_info pshminfo; +}; + + +struct pipe_info { + struct vinfo_stat pipe_stat; + uint64_t pipe_handle; + uint64_t pipe_peerhandle; + int pipe_status; + int rfu_1; /* reserved */ +}; + +struct pipe_fdinfo { + struct proc_fileinfo pfi; + struct pipe_info pipeinfo; +}; + + +struct kqueue_info { + struct vinfo_stat kq_stat; + uint32_t kq_state; + uint32_t rfu_1; /* reserved */ +}; + +struct kqueue_dyninfo { + struct kqueue_info kqdi_info; + uint64_t kqdi_servicer; + uint64_t kqdi_owner; + uint32_t kqdi_sync_waiters; + uint8_t kqdi_sync_waiter_qos; + uint8_t kqdi_async_qos; + uint16_t kqdi_request_state; + uint8_t kqdi_events_qos; + uint8_t kqdi_pri; + uint8_t kqdi_pol; + uint8_t kqdi_cpupercent; + uint8_t _kqdi_reserved0[4]; + uint64_t _kqdi_reserved1[4]; +}; + +/* keep in sync with KQ_* in sys/eventvar.h */ +#define PROC_KQUEUE_SELECT 0x01 +#define PROC_KQUEUE_SLEEP 0x02 +#define PROC_KQUEUE_32 0x08 +#define PROC_KQUEUE_64 0x10 +#define PROC_KQUEUE_QOS 0x20 + + +struct kqueue_fdinfo { + struct proc_fileinfo pfi; + struct kqueue_info kqueueinfo; +}; + +struct appletalk_info { + struct vinfo_stat atalk_stat; +}; + +struct appletalk_fdinfo { + struct proc_fileinfo pfi; + struct appletalk_info appletalkinfo; +}; + +typedef uint64_t proc_info_udata_t; + +/* defns of process file desc type */ +#define PROX_FDTYPE_ATALK 0 +#define PROX_FDTYPE_VNODE 1 +#define PROX_FDTYPE_SOCKET 2 +#define PROX_FDTYPE_PSHM 3 +#define PROX_FDTYPE_PSEM 4 +#define PROX_FDTYPE_KQUEUE 5 +#define PROX_FDTYPE_PIPE 6 +#define PROX_FDTYPE_FSEVENTS 7 +#define PROX_FDTYPE_NETPOLICY 9 + +struct proc_fdinfo { + int32_t proc_fd; + uint32_t proc_fdtype; +}; + +struct proc_fileportinfo { + uint32_t proc_fileport; + uint32_t proc_fdtype; +}; + + +/* Flavors for proc_pidinfo() */ +#define PROC_PIDLISTFDS 1 +#define PROC_PIDLISTFD_SIZE (sizeof(struct proc_fdinfo)) + +#define PROC_PIDTASKALLINFO 2 +#define PROC_PIDTASKALLINFO_SIZE (sizeof(struct proc_taskallinfo)) + +#define PROC_PIDTBSDINFO 3 +#define PROC_PIDTBSDINFO_SIZE (sizeof(struct proc_bsdinfo)) + +#define PROC_PIDTASKINFO 4 +#define PROC_PIDTASKINFO_SIZE (sizeof(struct proc_taskinfo)) + +#define PROC_PIDTHREADINFO 5 +#define PROC_PIDTHREADINFO_SIZE (sizeof(struct proc_threadinfo)) + +#define PROC_PIDLISTTHREADS 6 +#define PROC_PIDLISTTHREADS_SIZE (2* sizeof(uint32_t)) + +#define PROC_PIDREGIONINFO 7 +#define PROC_PIDREGIONINFO_SIZE (sizeof(struct proc_regioninfo)) + +#define PROC_PIDREGIONPATHINFO 8 +#define PROC_PIDREGIONPATHINFO_SIZE (sizeof(struct proc_regionwithpathinfo)) + +#define PROC_PIDVNODEPATHINFO 9 +#define PROC_PIDVNODEPATHINFO_SIZE (sizeof(struct proc_vnodepathinfo)) + +#define PROC_PIDTHREADPATHINFO 10 +#define PROC_PIDTHREADPATHINFO_SIZE (sizeof(struct proc_threadwithpathinfo)) + +#define PROC_PIDPATHINFO 11 +#define PROC_PIDPATHINFO_SIZE (MAXPATHLEN) +#define PROC_PIDPATHINFO_MAXSIZE (4*MAXPATHLEN) + +#define PROC_PIDWORKQUEUEINFO 12 +#define PROC_PIDWORKQUEUEINFO_SIZE (sizeof(struct proc_workqueueinfo)) + +#define PROC_PIDT_SHORTBSDINFO 13 +#define PROC_PIDT_SHORTBSDINFO_SIZE (sizeof(struct proc_bsdshortinfo)) + +#define PROC_PIDLISTFILEPORTS 14 +#define PROC_PIDLISTFILEPORTS_SIZE (sizeof(struct proc_fileportinfo)) + +#define PROC_PIDTHREADID64INFO 15 +#define PROC_PIDTHREADID64INFO_SIZE (sizeof(struct proc_threadinfo)) + +#define PROC_PID_RUSAGE 16 +#define PROC_PID_RUSAGE_SIZE 0 + +/* Flavors for proc_pidfdinfo */ + +#define PROC_PIDFDVNODEINFO 1 +#define PROC_PIDFDVNODEINFO_SIZE (sizeof(struct vnode_fdinfo)) + +#define PROC_PIDFDVNODEPATHINFO 2 +#define PROC_PIDFDVNODEPATHINFO_SIZE (sizeof(struct vnode_fdinfowithpath)) + +#define PROC_PIDFDSOCKETINFO 3 +#define PROC_PIDFDSOCKETINFO_SIZE (sizeof(struct socket_fdinfo)) + +#define PROC_PIDFDPSEMINFO 4 +#define PROC_PIDFDPSEMINFO_SIZE (sizeof(struct psem_fdinfo)) + +#define PROC_PIDFDPSHMINFO 5 +#define PROC_PIDFDPSHMINFO_SIZE (sizeof(struct pshm_fdinfo)) + +#define PROC_PIDFDPIPEINFO 6 +#define PROC_PIDFDPIPEINFO_SIZE (sizeof(struct pipe_fdinfo)) + +#define PROC_PIDFDKQUEUEINFO 7 +#define PROC_PIDFDKQUEUEINFO_SIZE (sizeof(struct kqueue_fdinfo)) + +#define PROC_PIDFDATALKINFO 8 +#define PROC_PIDFDATALKINFO_SIZE (sizeof(struct appletalk_fdinfo)) + + + +/* Flavors for proc_pidfileportinfo */ + +#define PROC_PIDFILEPORTVNODEPATHINFO 2 /* out: vnode_fdinfowithpath */ +#define PROC_PIDFILEPORTVNODEPATHINFO_SIZE \ + PROC_PIDFDVNODEPATHINFO_SIZE + +#define PROC_PIDFILEPORTSOCKETINFO 3 /* out: socket_fdinfo */ +#define PROC_PIDFILEPORTSOCKETINFO_SIZE PROC_PIDFDSOCKETINFO_SIZE + +#define PROC_PIDFILEPORTPSHMINFO 5 /* out: pshm_fdinfo */ +#define PROC_PIDFILEPORTPSHMINFO_SIZE PROC_PIDFDPSHMINFO_SIZE + +#define PROC_PIDFILEPORTPIPEINFO 6 /* out: pipe_fdinfo */ +#define PROC_PIDFILEPORTPIPEINFO_SIZE PROC_PIDFDPIPEINFO_SIZE + +/* used for proc_setcontrol */ +#define PROC_SELFSET_PCONTROL 1 + +#define PROC_SELFSET_THREADNAME 2 +#define PROC_SELFSET_THREADNAME_SIZE (MAXTHREADNAMESIZE -1) + +#define PROC_SELFSET_VMRSRCOWNER 3 + +#define PROC_SELFSET_DELAYIDLESLEEP 4 + +/* used for proc_dirtycontrol */ +#define PROC_DIRTYCONTROL_TRACK 1 +#define PROC_DIRTYCONTROL_SET 2 +#define PROC_DIRTYCONTROL_GET 3 +#define PROC_DIRTYCONTROL_CLEAR 4 + +/* proc_track_dirty() flags */ +#define PROC_DIRTY_TRACK 0x1 +#define PROC_DIRTY_ALLOW_IDLE_EXIT 0x2 +#define PROC_DIRTY_DEFER 0x4 +#define PROC_DIRTY_LAUNCH_IN_PROGRESS 0x8 +#define PROC_DIRTY_DEFER_ALWAYS 0x10 + +/* proc_get_dirty() flags */ +#define PROC_DIRTY_TRACKED 0x1 +#define PROC_DIRTY_ALLOWS_IDLE_EXIT 0x2 +#define PROC_DIRTY_IS_DIRTY 0x4 +#define PROC_DIRTY_LAUNCH_IS_IN_PROGRESS 0x8 + +/* Flavors for proc_udata_info */ +#define PROC_UDATA_INFO_GET 1 +#define PROC_UDATA_INFO_SET 2 + + + + +__END_DECLS + +#endif /*_SYS_PROC_INFO_H */ diff --git a/lib/libc/include/x86_64-macos-gnu/sys/ucontext.h b/lib/libc/include/x86_64-macos-gnu/sys/ucontext.h new file mode 100644 index 0000000000..5b55545a47 --- /dev/null +++ b/lib/libc/include/x86_64-macos-gnu/sys/ucontext.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2002-2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#ifndef _SYS_UCONTEXT_H_ +#define _SYS_UCONTEXT_H_ + +#include +#include + +#include +#include + +#include + + +#endif /* _SYS_UCONTEXT_H_ */ diff --git a/lib/libc/include/x86_64-macos-gnu/ucontext.h b/lib/libc/include/x86_64-macos-gnu/ucontext.h new file mode 100644 index 0000000000..27b4111035 --- /dev/null +++ b/lib/libc/include/x86_64-macos-gnu/ucontext.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2002, 2008, 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +/* + * These routines are DEPRECATED and should not be used. + */ +#ifndef _UCONTEXT_H_ +#define _UCONTEXT_H_ + +#include + +#ifdef _XOPEN_SOURCE +#include +#include + +__BEGIN_DECLS +__API_DEPRECATED("No longer supported", macos(10.5, 10.6)) +int getcontext(ucontext_t *); + +__API_DEPRECATED("No longer supported", macos(10.5, 10.6)) +void makecontext(ucontext_t *, void (*)(), int, ...); + +__API_DEPRECATED("No longer supported", macos(10.5, 10.6)) +int setcontext(const ucontext_t *); + +__API_DEPRECATED("No longer supported", macos(10.5, 10.6)) +int swapcontext(ucontext_t * __restrict, const ucontext_t * __restrict); + +__END_DECLS +#else /* !_XOPEN_SOURCE */ +#error The deprecated ucontext routines require _XOPEN_SOURCE to be defined +#endif /* _XOPEN_SOURCE */ + +#endif /* _UCONTEXT_H_ */ From b83ef595a53377189d42bdb697af866f30838040 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Fri, 12 Mar 2021 14:51:18 +0100 Subject: [PATCH 48/49] std/linux: sync io_uring library with liburing liburing commit: https://github.com/axboe/liburing/commit/1bafb3ce5f5eeb11cd982c7540f6aa74e3f381d4 As stated in the liburing commit message, this fixes a regression, reverting code that was added specutively to avoid a syscall in some cases. --- lib/std/os/linux/io_uring.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/std/os/linux/io_uring.zig b/lib/std/os/linux/io_uring.zig index 340020cf9b..e900bdcd6a 100644 --- a/lib/std/os/linux/io_uring.zig +++ b/lib/std/os/linux/io_uring.zig @@ -163,9 +163,9 @@ pub const IO_Uring = struct { /// Returns the number of SQEs submitted. /// Matches the implementation of io_uring_submit_and_wait() in liburing. pub fn submit_and_wait(self: *IO_Uring, wait_nr: u32) !u32 { - var submitted = self.flush_sq(); + const submitted = self.flush_sq(); var flags: u32 = 0; - if (self.sq_ring_needs_enter(submitted, &flags) or wait_nr > 0) { + if (self.sq_ring_needs_enter(&flags) or wait_nr > 0) { if (wait_nr > 0 or (self.flags & linux.IORING_SETUP_IOPOLL) != 0) { flags |= linux.IORING_ENTER_GETEVENTS; } @@ -236,9 +236,9 @@ pub const IO_Uring = struct { /// or if IORING_SQ_NEED_WAKEUP is set and the SQ thread must be explicitly awakened. /// For the latter case, we set the SQ thread wakeup flag. /// Matches the implementation of sq_ring_needs_enter() in liburing. - pub fn sq_ring_needs_enter(self: *IO_Uring, submitted: u32, flags: *u32) bool { + pub fn sq_ring_needs_enter(self: *IO_Uring, flags: *u32) bool { assert(flags.* == 0); - if ((self.flags & linux.IORING_SETUP_SQPOLL) == 0 and submitted > 0) return true; + if ((self.flags & linux.IORING_SETUP_SQPOLL) == 0) return true; if ((@atomicLoad(u32, self.sq.flags, .Unordered) & linux.IORING_SQ_NEED_WAKEUP) != 0) { flags.* |= linux.IORING_ENTER_SQ_WAKEUP; return true; From 8ebb18d9da0bfbe6a974636fd36e3391d1de253b Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sat, 13 Mar 2021 00:42:42 +0100 Subject: [PATCH 49/49] std: Use more common escape sequences in Progress This should fix the badly-rendered progress message when run in Terminal.app. --- lib/std/Progress.zig | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig index 3f462f5c08..c912b60f57 100644 --- a/lib/std/Progress.zig +++ b/lib/std/Progress.zig @@ -185,9 +185,11 @@ pub fn refresh(self: *Progress) void { // ED -- Clear screen const ED = "\x1b[J"; // DECSC -- Save cursor position -const DECSC = "\x1b[s"; +const DECSC = "\x1b7"; // DECRC -- Restore cursor position -const DECRC = "\x1b[u"; +const DECRC = "\x1b8"; +// Note that ESC7/ESC8 are used instead of CSI s/CSI u as the latter are not +// supported by some terminals (eg. Terminal.app). fn refreshWithHeldLock(self: *Progress) void { const is_dumb = !self.supports_ansi_escape_codes and !(std.builtin.os.tag == .windows);