From 0f4f6078143e67d50bfd66840e31cf3b38987261 Mon Sep 17 00:00:00 2001 From: Ryo Ota Date: Thu, 20 Apr 2023 22:03:01 +0900 Subject: [PATCH 01/44] fix http client build error --- lib/std/http/Client.zig | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/std/http/Client.zig b/lib/std/http/Client.zig index 4ff29a215a..3de15774d1 100644 --- a/lib/std/http/Client.zig +++ b/lib/std/http/Client.zig @@ -797,18 +797,18 @@ pub const Request = struct { /// Write `bytes` to the server. The `transfer_encoding` request header determines how data will be sent. pub fn write(req: *Request, bytes: []const u8) WriteError!usize { - switch (req.headers.transfer_encoding) { + switch (req.transfer_encoding) { .chunked => { - try req.connection.data.conn.writer().print("{x}\r\n", .{bytes.len}); - try req.connection.data.conn.writeAll(bytes); - try req.connection.data.conn.writeAll("\r\n"); + try req.connection.data.buffered.writer().print("{x}\r\n", .{bytes.len}); + try req.connection.data.buffered.writeAll(bytes); + try req.connection.data.buffered.writeAll("\r\n"); return bytes.len; }, .content_length => |*len| { if (len.* < bytes.len) return error.MessageTooLong; - const amt = try req.connection.data.conn.write(bytes); + const amt = try req.connection.data.buffered.write(bytes); len.* -= amt; return amt; }, @@ -828,7 +828,7 @@ pub const Request = struct { /// Finish the body of a request. This notifies the server that you have no more data to send. pub fn finish(req: *Request) FinishError!void { switch (req.transfer_encoding) { - .chunked => try req.connection.data.conn.writeAll("0\r\n\r\n"), + .chunked => try req.connection.data.buffered.writeAll("0\r\n\r\n"), .content_length => |len| if (len != 0) return error.MessageNotCompleted, .none => {}, } From 39c0c24b5659bbcaec0a6f5ba4acc8666e3b8086 Mon Sep 17 00:00:00 2001 From: Ryo Ota Date: Fri, 21 Apr 2023 01:08:15 +0900 Subject: [PATCH 02/44] fix memory leaks and add an HTTP test --- lib/std/http/Client.zig | 69 +++++++++++++++++++++++++++++++++++++++++ lib/std/http/Server.zig | 5 ++- 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/lib/std/http/Client.zig b/lib/std/http/Client.zig index 3de15774d1..661dcaab5b 100644 --- a/lib/std/http/Client.zig +++ b/lib/std/http/Client.zig @@ -1046,3 +1046,72 @@ test { std.testing.refAllDecls(@This()); } + +test "client requests server" { + const builtin = @import("builtin"); + + // This test requires spawning threads. + if (builtin.single_threaded) { + return error.SkipZigTest; + } + + const native_endian = comptime builtin.cpu.arch.endian(); + if (builtin.zig_backend == .stage2_llvm and native_endian == .Big) { + // https://github.com/ziglang/zig/issues/13782 + return error.SkipZigTest; + } + + if (builtin.os.tag == .wasi) return error.SkipZigTest; + + const allocator = std.testing.allocator; + + const max_header_size = 8192; + var server = std.http.Server.init(allocator, .{ .reuse_address = true }); + defer server.deinit(); + + const address = try std.net.Address.parseIp("127.0.0.1", 0); + try server.listen(address); + const server_port = server.socket.listen_address.in.getPort(); + + const thread = try std.Thread.spawn(.{}, (struct { + fn apply(s: *std.http.Server) !void { + const res = try s.accept(.{ .dynamic = max_header_size }); + defer res.reset(); + try res.wait(); + + const server_body: []const u8 = "message from server!\n"; + res.transfer_encoding = .{ .content_length = server_body.len }; + try res.headers.append("content-type", "text/plain"); + try res.headers.append("connection", "close"); + try res.do(); + + var buf: [128]u8 = undefined; + const n = try res.readAll(&buf); + try testing.expect(std.mem.eql(u8, buf[0..n], "Hello, World!\n")); + _ = try res.writer().writeAll(server_body); + try res.finish(); + } + }).apply, .{&server}); + + var uri_buf: [22]u8 = undefined; + const uri = std.Uri.parse(try std.fmt.bufPrint(&uri_buf, "http://127.0.0.1:{d}", .{server_port})) catch unreachable; + var client = std.http.Client{ .allocator = allocator }; + defer client.deinit(); + var client_headers = std.http.Headers{ .allocator = allocator }; + defer client_headers.deinit(); + var client_req = try client.request(.POST, uri, client_headers, .{}); + defer client_req.deinit(); + + client_req.transfer_encoding = .{ .content_length = 14 }; // this will be checked to ensure you sent exactly 14 bytes + try client_req.start(); // this sends the request + try client_req.writeAll("Hello, "); + try client_req.writeAll("World!\n"); + try client_req.finish(); + try client_req.do(); // this waits for a response + + const body = try client_req.reader().readAllAlloc(allocator, 8192 * 1024); + defer allocator.free(body); + try testing.expect(std.mem.eql(u8, body, "message from server!\n")); + + thread.join(); +} diff --git a/lib/std/http/Server.zig b/lib/std/http/Server.zig index 445a10bf48..b1205594ad 100644 --- a/lib/std/http/Server.zig +++ b/lib/std/http/Server.zig @@ -338,6 +338,9 @@ pub const Response = struct { /// Reset this response to its initial state. This must be called before handling a second request on the same connection. pub fn reset(res: *Response) void { + res.request.headers.deinit(); + res.headers.deinit(); + switch (res.request.compression) { .none => {}, .deflate => |*deflate| deflate.deinit(), @@ -357,7 +360,7 @@ pub const Response = struct { res.request.parser.header_bytes.deinit(res.server.allocator); } - res.* = undefined; + res.server.allocator.destroy(res); } else { res.request.parser.reset(); } From 06763c4c8c35d2e984ee2315e674a01d5506ded1 Mon Sep 17 00:00:00 2001 From: Ryo Ota Date: Fri, 21 Apr 2023 09:36:45 +0900 Subject: [PATCH 03/44] move the HTTP test to lib/std/http/test.zig --- lib/std/http.zig | 1 + lib/std/http/Client.zig | 69 --------------------------------------- lib/std/http/test.zig | 71 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 69 deletions(-) create mode 100644 lib/std/http/test.zig diff --git a/lib/std/http.zig b/lib/std/http.zig index 364cc4eeda..2f0a20de8d 100644 --- a/lib/std/http.zig +++ b/lib/std/http.zig @@ -275,4 +275,5 @@ test { _ = Client; _ = Method; _ = Status; + _ = @import("http/test.zig"); } diff --git a/lib/std/http/Client.zig b/lib/std/http/Client.zig index 661dcaab5b..3de15774d1 100644 --- a/lib/std/http/Client.zig +++ b/lib/std/http/Client.zig @@ -1046,72 +1046,3 @@ test { std.testing.refAllDecls(@This()); } - -test "client requests server" { - const builtin = @import("builtin"); - - // This test requires spawning threads. - if (builtin.single_threaded) { - return error.SkipZigTest; - } - - const native_endian = comptime builtin.cpu.arch.endian(); - if (builtin.zig_backend == .stage2_llvm and native_endian == .Big) { - // https://github.com/ziglang/zig/issues/13782 - return error.SkipZigTest; - } - - if (builtin.os.tag == .wasi) return error.SkipZigTest; - - const allocator = std.testing.allocator; - - const max_header_size = 8192; - var server = std.http.Server.init(allocator, .{ .reuse_address = true }); - defer server.deinit(); - - const address = try std.net.Address.parseIp("127.0.0.1", 0); - try server.listen(address); - const server_port = server.socket.listen_address.in.getPort(); - - const thread = try std.Thread.spawn(.{}, (struct { - fn apply(s: *std.http.Server) !void { - const res = try s.accept(.{ .dynamic = max_header_size }); - defer res.reset(); - try res.wait(); - - const server_body: []const u8 = "message from server!\n"; - res.transfer_encoding = .{ .content_length = server_body.len }; - try res.headers.append("content-type", "text/plain"); - try res.headers.append("connection", "close"); - try res.do(); - - var buf: [128]u8 = undefined; - const n = try res.readAll(&buf); - try testing.expect(std.mem.eql(u8, buf[0..n], "Hello, World!\n")); - _ = try res.writer().writeAll(server_body); - try res.finish(); - } - }).apply, .{&server}); - - var uri_buf: [22]u8 = undefined; - const uri = std.Uri.parse(try std.fmt.bufPrint(&uri_buf, "http://127.0.0.1:{d}", .{server_port})) catch unreachable; - var client = std.http.Client{ .allocator = allocator }; - defer client.deinit(); - var client_headers = std.http.Headers{ .allocator = allocator }; - defer client_headers.deinit(); - var client_req = try client.request(.POST, uri, client_headers, .{}); - defer client_req.deinit(); - - client_req.transfer_encoding = .{ .content_length = 14 }; // this will be checked to ensure you sent exactly 14 bytes - try client_req.start(); // this sends the request - try client_req.writeAll("Hello, "); - try client_req.writeAll("World!\n"); - try client_req.finish(); - try client_req.do(); // this waits for a response - - const body = try client_req.reader().readAllAlloc(allocator, 8192 * 1024); - defer allocator.free(body); - try testing.expect(std.mem.eql(u8, body, "message from server!\n")); - - thread.join(); -} diff --git a/lib/std/http/test.zig b/lib/std/http/test.zig new file mode 100644 index 0000000000..7c053dc6b3 --- /dev/null +++ b/lib/std/http/test.zig @@ -0,0 +1,71 @@ +const std = @import("std"); +const expect = std.testing.expect; + +test "client requests server" { + const builtin = @import("builtin"); + + // This test requires spawning threads. + if (builtin.single_threaded) { + return error.SkipZigTest; + } + + const native_endian = comptime builtin.cpu.arch.endian(); + if (builtin.zig_backend == .stage2_llvm and native_endian == .Big) { + // https://github.com/ziglang/zig/issues/13782 + return error.SkipZigTest; + } + + if (builtin.os.tag == .wasi) return error.SkipZigTest; + + const allocator = std.testing.allocator; + + const max_header_size = 8192; + var server = std.http.Server.init(allocator, .{ .reuse_address = true }); + defer server.deinit(); + + const address = try std.net.Address.parseIp("127.0.0.1", 0); + try server.listen(address); + const server_port = server.socket.listen_address.in.getPort(); + + const server_thread = try std.Thread.spawn(.{}, (struct { + fn apply(s: *std.http.Server) !void { + const res = try s.accept(.{ .dynamic = max_header_size }); + defer res.reset(); + try res.wait(); + + const server_body: []const u8 = "message from server!\n"; + res.transfer_encoding = .{ .content_length = server_body.len }; + try res.headers.append("content-type", "text/plain"); + try res.headers.append("connection", "close"); + try res.do(); + + var buf: [128]u8 = undefined; + const n = try res.readAll(&buf); + try expect(std.mem.eql(u8, buf[0..n], "Hello, World!\n")); + _ = try res.writer().writeAll(server_body); + try res.finish(); + } + }).apply, .{&server}); + + var uri_buf: [22]u8 = undefined; + const uri = try std.Uri.parse(try std.fmt.bufPrint(&uri_buf, "http://127.0.0.1:{d}", .{server_port})); + var client = std.http.Client{ .allocator = allocator }; + defer client.deinit(); + var client_headers = std.http.Headers{ .allocator = allocator }; + defer client_headers.deinit(); + var client_req = try client.request(.POST, uri, client_headers, .{}); + defer client_req.deinit(); + + client_req.transfer_encoding = .{ .content_length = 14 }; // this will be checked to ensure you sent exactly 14 bytes + try client_req.start(); // this sends the request + try client_req.writeAll("Hello, "); + try client_req.writeAll("World!\n"); + try client_req.finish(); + try client_req.do(); // this waits for a response + + const body = try client_req.reader().readAllAlloc(allocator, 8192 * 1024); + defer allocator.free(body); + try expect(std.mem.eql(u8, body, "message from server!\n")); + + server_thread.join(); +} From afebef2465a828132f87cd2aeff0c5873ca10de5 Mon Sep 17 00:00:00 2001 From: Ryo Ota Date: Fri, 21 Apr 2023 10:17:16 +0900 Subject: [PATCH 04/44] create std.http.Server.Response.deinit to handle keepalive connections --- lib/std/http/Server.zig | 6 ++++-- lib/std/http/test.zig | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/std/http/Server.zig b/lib/std/http/Server.zig index b1205594ad..59b76ea20d 100644 --- a/lib/std/http/Server.zig +++ b/lib/std/http/Server.zig @@ -336,6 +336,10 @@ pub const Response = struct { headers: http.Headers, request: Request, + pub fn deinit(res: *Response) void { + res.server.allocator.destroy(res); + } + /// Reset this response to its initial state. This must be called before handling a second request on the same connection. pub fn reset(res: *Response) void { res.request.headers.deinit(); @@ -359,8 +363,6 @@ pub const Response = struct { if (res.request.parser.header_bytes_owned) { res.request.parser.header_bytes.deinit(res.server.allocator); } - - res.server.allocator.destroy(res); } else { res.request.parser.reset(); } diff --git a/lib/std/http/test.zig b/lib/std/http/test.zig index 7c053dc6b3..ce55c21392 100644 --- a/lib/std/http/test.zig +++ b/lib/std/http/test.zig @@ -30,6 +30,7 @@ test "client requests server" { const server_thread = try std.Thread.spawn(.{}, (struct { fn apply(s: *std.http.Server) !void { const res = try s.accept(.{ .dynamic = max_header_size }); + defer res.deinit(); defer res.reset(); try res.wait(); From c4b295bb6e9fcb1d02122cb70c95019c81ae543e Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sat, 15 Apr 2023 16:17:25 +0200 Subject: [PATCH 05/44] wasm: implement `cmp_lt_errors_len` instruction Creates a global undefined symbol when this instruction is called. The linker will then resolve it as a lazy symbol, ensuring it is only generated when the symbol was created. In `flush` it will then generate the function as only then, all errors are known and we can generate the function body. This logic allows us to re-use the same functionality of linker-synthetic-functions. --- src/arch/wasm/CodeGen.zig | 8 +++++-- src/link/Wasm.zig | 45 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index b94d3993f9..0d73e35185 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -3338,9 +3338,13 @@ fn airCmpVector(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { fn airCmpLtErrorsLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { const un_op = func.air.instructions.items(.data)[inst].un_op; const operand = try func.resolveInst(un_op); + const sym_index = try func.bin_file.getGlobalSymbol("__zig_lt_errors_len", null); - _ = operand; - return func.fail("TODO implement airCmpLtErrorsLen for wasm", .{}); + try func.emitWValue(operand); + try func.addLabel(.call, sym_index); + const result = try func.allocLocal(Type.bool); + try func.addLabel(.local_set, result.local.value); + return func.finishAir(inst, result, &.{un_op}); } fn airBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 2125a8faaa..1b7c43a6a0 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -1209,6 +1209,10 @@ fn resolveLazySymbols(wasm: *Wasm) !void { try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc); } } + if (wasm.undefs.fetchSwapRemove("__zig_lt_errors_len")) |kv| { + const loc = try wasm.createSyntheticSymbol("__zig_lt_errors_len", .function); + try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc); + } } // Tries to find a global symbol by its name. Returns null when not found, @@ -2185,6 +2189,46 @@ fn setupInitFunctions(wasm: *Wasm) !void { std.sort.sort(InitFuncLoc, wasm.init_funcs.items, {}, InitFuncLoc.lessThan); } +/// Generates the function which verifies if an integer value is less than the +/// amount of error values. This will only be generated if the symbol exists. +fn setupLtErrorsLenFunction(wasm: *Wasm) !void { + if (wasm.findGlobalSymbol("__zig_lt_errors_len") == null) return; + const errors_len = wasm.base.options.module.?.global_error_set.count(); + + var body_list = std.ArrayList(u8).init(wasm.base.allocator); + defer body_list.deinit(); + const writer = body_list.writer(); + + { + // generates bytecode for the following function: + // fn (index: u16) bool { + // return index < errors_len; + // } + + // no locals + try leb.writeULEB128(writer, @as(u32, 0)); + + // get argument + try writer.writeByte(std.wasm.opcode(.local_get)); + try leb.writeULEB128(writer, @as(u32, 0)); + + // get error length + try writer.writeByte(std.wasm.opcode(.i32_const)); + try leb.writeULEB128(writer, @intCast(u32, errors_len)); + + try writer.writeByte(std.wasm.opcode(.i32_lt_u)); + + // stack values are implicit return values so keep the value + // on the stack and end the function. + + // end function + try writer.writeByte(std.wasm.opcode(.end)); + } + + const func_type: std.wasm.Type = .{ .params = &.{std.wasm.Valtype.i32}, .returns = &.{std.wasm.Valtype.i32} }; + try wasm.createSyntheticFunction("__zig_lt_errors_len", func_type, &body_list); +} + /// Creates a function body for the `__wasm_call_ctors` symbol. /// Loops over all constructors found in `init_funcs` and calls them /// respectively based on their priority which was sorted by `setupInitFunctions`. @@ -3379,6 +3423,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod try wasm.setupInitMemoryFunction(); try wasm.setupTLSRelocationsFunction(); try wasm.initializeTLSFunction(); + try wasm.setupLtErrorsLenFunction(); try wasm.setupExports(); try wasm.writeToFile(enabled_features, emit_features_count, arena); } From 27a41413f71b9b2d2fb15ea14a96983b4ba9bd2e Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sat, 15 Apr 2023 16:23:41 +0200 Subject: [PATCH 06/44] wasm: enable `@intToError` test --- test/behavior/cast.zig | 1 - 1 file changed, 1 deletion(-) diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index bdff7c4de4..fc364da0b8 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -401,7 +401,6 @@ test "expected [*c]const u8, found [*:0]const u8" { } test "explicit cast from integer to error type" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO From d4ceb12ae9d409dbd52c1f5c96312a1e6ad7d6bc Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sun, 16 Apr 2023 15:10:45 +0200 Subject: [PATCH 07/44] wasm: implement `error_set_has_value` This implements the safety check for error casts. The instruction generates a jump table with 2 possibilities. The operand is used as an index into the jump table. For cases where the value does not exist within the error set, it will generate a jump to the 'false' block. For cases where it does exist, it will generate a jump to the 'true' block. By calculating the highest and lowest value we can keep the jump table smaller, as it doesn't need to contain an index into the entire error set. --- src/Module.zig | 2 +- src/arch/wasm/CodeGen.zig | 85 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 85 insertions(+), 2 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index fa91e8c1ed..27f7b24e7a 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -6626,7 +6626,7 @@ pub fn backendSupportsFeature(mod: Module, feature: Feature) bool { .safety_check_formatted => mod.comp.bin_file.options.use_llvm, .error_return_trace => mod.comp.bin_file.options.use_llvm, .is_named_enum_value => mod.comp.bin_file.options.use_llvm, - .error_set_has_value => mod.comp.bin_file.options.use_llvm, + .error_set_has_value => mod.comp.bin_file.options.use_llvm or mod.comp.bin_file.options.target.isWasm(), .field_reordering => mod.comp.bin_file.options.use_llvm, }; } diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 0d73e35185..c0bab03428 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1946,6 +1946,8 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { .ret_addr => func.airRetAddr(inst), .tag_name => func.airTagName(inst), + .error_set_has_value => func.airErrorSetHasValue(inst), + .mul_sat, .mod, .assembly, @@ -1967,7 +1969,6 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { .set_err_return_trace, .save_err_return_trace_index, .is_named_enum_value, - .error_set_has_value, .addrspace_cast, .vector_store_elem, .c_va_arg, @@ -6514,3 +6515,85 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 { const func_type = try genFunctype(arena, .Unspecified, &.{int_tag_ty}, slice_ty, func.target); return func.bin_file.createFunction(func_name, func_type, &body_list, &relocs); } + +fn airErrorSetHasValue(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { + const ty_op = func.air.instructions.items(.data)[inst].ty_op; + if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand}); + + const operand = try func.resolveInst(ty_op.operand); + const error_set_ty = func.air.getRefType(ty_op.ty); + const result = try func.allocLocal(Type.bool); + + const names = error_set_ty.errorSetNames(); + var values = try std.ArrayList(u32).initCapacity(func.gpa, names.len); + defer values.deinit(); + + const module = func.bin_file.base.options.module.?; + var lowest: ?u32 = null; + var highest: ?u32 = null; + for (names) |name| { + const err_int = module.global_error_set.get(name).?; + if (lowest) |*l| { + if (err_int < l.*) { + l.* = err_int; + } + } else { + lowest = err_int; + } + if (highest) |*h| { + if (err_int > h.*) { + highest = err_int; + } + } else { + highest = err_int; + } + + values.appendAssumeCapacity(err_int); + } + + // start block for 'true' branch + try func.startBlock(.block, wasm.block_empty); + // start block for 'false' branch + try func.startBlock(.block, wasm.block_empty); + // block for the jump table itself + try func.startBlock(.block, wasm.block_empty); + + // lower operand to determine jump table target + try func.emitWValue(operand); + try func.addImm32(@intCast(i32, lowest.?)); + try func.addTag(.i32_sub); + + // Account for default branch so always add '1' + const depth = @intCast(u32, highest.? - lowest.? + 1); + const jump_table: Mir.JumpTable = .{ .length = depth }; + const table_extra_index = try func.addExtra(jump_table); + try func.addInst(.{ .tag = .br_table, .data = .{ .payload = table_extra_index } }); + try func.mir_extra.ensureUnusedCapacity(func.gpa, depth); + + var value: u32 = lowest.?; + while (value <= highest.?) : (value += 1) { + const idx: u32 = blk: { + for (values.items) |val| { + if (val == value) break :blk 1; + } + break :blk 0; + }; + func.mir_extra.appendAssumeCapacity(idx); + } + try func.endBlock(); + + // 'false' branch (i.e. error set does not have value + // ensure we set local to 0 in case the local was re-used. + try func.addImm32(0); + try func.addLabel(.local_set, result.local.value); + try func.addLabel(.br, 1); + try func.endBlock(); + + // 'true' branch + try func.addImm32(1); + try func.addLabel(.local_set, result.local.value); + try func.addLabel(.br, 0); + try func.endBlock(); + + return func.finishAir(inst, result, &.{ty_op.operand}); +} From 6c1ab376ddcdbb05610487e5b813d42ff37da40d Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Mon, 17 Apr 2023 20:05:24 +0200 Subject: [PATCH 08/44] wasm: store `__zig_lt_errors_len` in linear data Rather than using a function call to verify if an error fits within the global error set's length, we now store the error set' size in the .rodata segment of the linear memory and load that value onto the stack to check with the integer value. --- src/arch/wasm/CodeGen.zig | 12 +++---- src/link/Wasm.zig | 76 +++++++++++++++++++-------------------- 2 files changed, 43 insertions(+), 45 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index c0bab03428..9bf39b73f1 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -3339,13 +3339,14 @@ fn airCmpVector(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { fn airCmpLtErrorsLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { const un_op = func.air.instructions.items(.data)[inst].un_op; const operand = try func.resolveInst(un_op); - const sym_index = try func.bin_file.getGlobalSymbol("__zig_lt_errors_len", null); + const sym_index = try func.bin_file.getGlobalSymbol("__zig_errors_len", null); + const errors_len = WValue{ .memory = sym_index }; try func.emitWValue(operand); - try func.addLabel(.call, sym_index); - const result = try func.allocLocal(Type.bool); - try func.addLabel(.local_set, result.local.value); - return func.finishAir(inst, result, &.{un_op}); + const errors_len_val = try func.load(errors_len, Type.err_int, 0); + const result = try func.cmp(.stack, errors_len_val, Type.err_int, .lt); + + return func.finishAir(inst, try result.toLocal(func, Type.bool), &.{un_op}); } fn airBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { @@ -6518,7 +6519,6 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 { fn airErrorSetHasValue(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { const ty_op = func.air.instructions.items(.data)[inst].ty_op; - if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ty_op.operand}); const operand = try func.resolveInst(ty_op.operand); const error_set_ty = func.air.getRefType(ty_op.ty); diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 1b7c43a6a0..0fe9ec5e3b 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -1209,9 +1209,10 @@ fn resolveLazySymbols(wasm: *Wasm) !void { try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc); } } - if (wasm.undefs.fetchSwapRemove("__zig_lt_errors_len")) |kv| { - const loc = try wasm.createSyntheticSymbol("__zig_lt_errors_len", .function); + if (wasm.undefs.fetchSwapRemove("__zig_errors_len")) |kv| { + const loc = try wasm.createSyntheticSymbol("__zig_errors_len", .data); try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc); + _ = wasm.resolved_symbols.swapRemove(kv.value); } } @@ -2189,44 +2190,41 @@ fn setupInitFunctions(wasm: *Wasm) !void { std.sort.sort(InitFuncLoc, wasm.init_funcs.items, {}, InitFuncLoc.lessThan); } -/// Generates the function which verifies if an integer value is less than the -/// amount of error values. This will only be generated if the symbol exists. -fn setupLtErrorsLenFunction(wasm: *Wasm) !void { - if (wasm.findGlobalSymbol("__zig_lt_errors_len") == null) return; +/// Generates an atom containing the global error set' size. +/// This will only be generated if the symbol exists. +fn setupErrorsLen(wasm: *Wasm) !void { + const loc = wasm.findGlobalSymbol("__zig_errors_len") orelse return; + const errors_len = wasm.base.options.module.?.global_error_set.count(); + // overwrite existing atom if it already exists (maybe the error set has increased) + // if not, allcoate a new atom. + const atom_index = if (wasm.symbol_atom.get(loc)) |index| blk: { + const atom = wasm.getAtomPtr(index); + if (atom.next) |next_atom_index| { + const next_atom = wasm.getAtomPtr(next_atom_index); + next_atom.prev = atom.prev; + atom.next = null; + } + if (atom.prev) |prev_index| { + const prev_atom = wasm.getAtomPtr(prev_index); + prev_atom.next = atom.next; + atom.prev = null; + } + atom.deinit(wasm); + break :blk index; + } else new_atom: { + const atom_index = @intCast(Atom.Index, wasm.managed_atoms.items.len); + try wasm.symbol_atom.put(wasm.base.allocator, loc, atom_index); + try wasm.managed_atoms.append(wasm.base.allocator, undefined); + break :new_atom atom_index; + }; + const atom = wasm.getAtomPtr(atom_index); + atom.* = Atom.empty; + atom.sym_index = loc.index; + atom.size = 2; + try atom.code.writer(wasm.base.allocator).writeIntLittle(u16, @intCast(u16, errors_len)); - var body_list = std.ArrayList(u8).init(wasm.base.allocator); - defer body_list.deinit(); - const writer = body_list.writer(); - - { - // generates bytecode for the following function: - // fn (index: u16) bool { - // return index < errors_len; - // } - - // no locals - try leb.writeULEB128(writer, @as(u32, 0)); - - // get argument - try writer.writeByte(std.wasm.opcode(.local_get)); - try leb.writeULEB128(writer, @as(u32, 0)); - - // get error length - try writer.writeByte(std.wasm.opcode(.i32_const)); - try leb.writeULEB128(writer, @intCast(u32, errors_len)); - - try writer.writeByte(std.wasm.opcode(.i32_lt_u)); - - // stack values are implicit return values so keep the value - // on the stack and end the function. - - // end function - try writer.writeByte(std.wasm.opcode(.end)); - } - - const func_type: std.wasm.Type = .{ .params = &.{std.wasm.Valtype.i32}, .returns = &.{std.wasm.Valtype.i32} }; - try wasm.createSyntheticFunction("__zig_lt_errors_len", func_type, &body_list); + try wasm.parseAtom(atom_index, .{ .data = .read_only }); } /// Creates a function body for the `__wasm_call_ctors` symbol. @@ -3361,6 +3359,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod // So we can rebuild the binary file on each incremental update defer wasm.resetState(); try wasm.setupInitFunctions(); + try wasm.setupErrorsLen(); try wasm.setupStart(); try wasm.setupImports(); if (wasm.base.options.module) |mod| { @@ -3423,7 +3422,6 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod try wasm.setupInitMemoryFunction(); try wasm.setupTLSRelocationsFunction(); try wasm.initializeTLSFunction(); - try wasm.setupLtErrorsLenFunction(); try wasm.setupExports(); try wasm.writeToFile(enabled_features, emit_features_count, arena); } From f3d18d5413eb4c8fdc71bc12105577d4839ececa Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sat, 22 Apr 2023 13:48:34 +0300 Subject: [PATCH 09/44] AstGen: fix debug info for some builtins Closes #15163 --- src/AstGen.zig | 174 +++++++++++++++++++++---------------------------- 1 file changed, 74 insertions(+), 100 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index 4419f5e803..706d76a61c 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -839,12 +839,9 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE .slice_open => { const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs); - maybeAdvanceSourceCursorToMainToken(gz, node); - const line = gz.astgen.source_line - gz.decl_line; - const column = gz.astgen.source_column; - + const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[node].rhs); - try emitDbgStmt(gz, line, column); + try emitDbgStmt(gz, cursor); const result = try gz.addPlNode(.slice_start, node, Zir.Inst.SliceStart{ .lhs = lhs, .start = start, @@ -854,14 +851,11 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE .slice => { const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs); - maybeAdvanceSourceCursorToMainToken(gz, node); - const line = gz.astgen.source_line - gz.decl_line; - const column = gz.astgen.source_column; - + const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); const extra = tree.extraData(node_datas[node].rhs, Ast.Node.Slice); const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.start); const end = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end); - try emitDbgStmt(gz, line, column); + try emitDbgStmt(gz, cursor); const result = try gz.addPlNode(.slice_end, node, Zir.Inst.SliceEnd{ .lhs = lhs, .start = start, @@ -872,15 +866,12 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE .slice_sentinel => { const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs); - maybeAdvanceSourceCursorToMainToken(gz, node); - const line = gz.astgen.source_line - gz.decl_line; - const column = gz.astgen.source_column; - + const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); const extra = tree.extraData(node_datas[node].rhs, Ast.Node.SliceSentinel); const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.start); const end = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none; const sentinel = try expr(gz, scope, .{ .rl = .none }, extra.sentinel); - try emitDbgStmt(gz, line, column); + try emitDbgStmt(gz, cursor); const result = try gz.addPlNode(.slice_sentinel, node, Zir.Inst.SliceSentinel{ .lhs = lhs, .start = start, @@ -914,20 +905,16 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE .ref => { const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs); - maybeAdvanceSourceCursorToMainToken(gz, node); - const line = gz.astgen.source_line - gz.decl_line; - const column = gz.astgen.source_column; - try emitDbgStmt(gz, line, column); + const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); + try emitDbgStmt(gz, cursor); return gz.addUnNode(.optional_payload_safe_ptr, lhs, node); }, else => { const lhs = try expr(gz, scope, .{ .rl = .none }, node_datas[node].lhs); - maybeAdvanceSourceCursorToMainToken(gz, node); - const line = gz.astgen.source_line - gz.decl_line; - const column = gz.astgen.source_column; - try emitDbgStmt(gz, line, column); + const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); + try emitDbgStmt(gz, cursor); return rvalue(gz, ri, try gz.addUnNode(.optional_payload_safe, lhs, node), node); }, @@ -3330,23 +3317,17 @@ fn assignOp( const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs); - var line: u32 = undefined; - var column: u32 = undefined; - switch (op_inst_tag) { - .add, .sub, .mul, .div, .mod_rem => { - maybeAdvanceSourceCursorToMainToken(gz, infix_node); - line = gz.astgen.source_line - gz.decl_line; - column = gz.astgen.source_column; - }, - else => {}, - } + const cursor = switch (op_inst_tag) { + .add, .sub, .mul, .div, .mod_rem => maybeAdvanceSourceCursorToMainToken(gz, infix_node), + else => undefined, + }; const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node); const lhs_type = try gz.addUnNode(.typeof, lhs, infix_node); const rhs = try expr(gz, scope, .{ .rl = .{ .coerced_ty = lhs_type } }, node_datas[infix_node].rhs); switch (op_inst_tag) { .add, .sub, .mul, .div, .mod_rem => { - try emitDbgStmt(gz, line, column); + try emitDbgStmt(gz, cursor); }, else => {}, } @@ -5360,8 +5341,7 @@ fn tryExpr( if (!parent_gz.is_comptime) { try emitDbgNode(parent_gz, node); } - const try_line = astgen.source_line - parent_gz.decl_line; - const try_column = astgen.source_column; + const try_lc = LineColumn{ astgen.source_line - parent_gz.decl_line, astgen.source_column }; const operand_ri: ResultInfo = switch (ri.rl) { .ref => .{ .rl = .ref, .ctx = .error_handling_expr }, @@ -5382,7 +5362,7 @@ fn tryExpr( }; const err_code = try else_scope.addUnNode(err_tag, operand, node); try genDefers(&else_scope, &fn_block.base, scope, .{ .both = err_code }); - try emitDbgStmt(&else_scope, try_line, try_column); + try emitDbgStmt(&else_scope, try_lc); _ = try else_scope.addUnNode(.ret_node, err_code, node); try else_scope.setTryBody(try_inst, operand); @@ -5607,10 +5587,8 @@ fn addFieldAccess( const str_index = try astgen.identAsString(field_ident); const lhs = try expr(gz, scope, lhs_ri, object_node); - maybeAdvanceSourceCursorToMainToken(gz, node); - const line = gz.astgen.source_line - gz.decl_line; - const column = gz.astgen.source_column; - try emitDbgStmt(gz, line, column); + const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); + try emitDbgStmt(gz, cursor); return gz.addPlNode(tag, node, Zir.Inst.Field{ .lhs = lhs, @@ -5630,24 +5608,20 @@ fn arrayAccess( .ref => { const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs); - maybeAdvanceSourceCursorToMainToken(gz, node); - const line = gz.astgen.source_line - gz.decl_line; - const column = gz.astgen.source_column; + const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); const rhs = try expr(gz, scope, .{ .rl = .{ .ty = .usize_type } }, node_datas[node].rhs); - try emitDbgStmt(gz, line, column); + try emitDbgStmt(gz, cursor); return gz.addPlNode(.elem_ptr_node, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs }); }, else => { const lhs = try expr(gz, scope, .{ .rl = .none }, node_datas[node].lhs); - maybeAdvanceSourceCursorToMainToken(gz, node); - const line = gz.astgen.source_line - gz.decl_line; - const column = gz.astgen.source_column; + const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); const rhs = try expr(gz, scope, .{ .rl = .{ .ty = .usize_type } }, node_datas[node].rhs); - try emitDbgStmt(gz, line, column); + try emitDbgStmt(gz, cursor); return rvalue(gz, ri, try gz.addPlNode(.elem_val_node, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs }), node); }, @@ -5674,21 +5648,15 @@ fn simpleBinOp( } const lhs = try reachableExpr(gz, scope, .{ .rl = .none }, node_datas[node].lhs, node); - var line: u32 = undefined; - var column: u32 = undefined; - switch (op_inst_tag) { - .add, .sub, .mul, .div, .mod_rem => { - maybeAdvanceSourceCursorToMainToken(gz, node); - line = gz.astgen.source_line - gz.decl_line; - column = gz.astgen.source_column; - }, - else => {}, - } + const cursor = switch (op_inst_tag) { + .add, .sub, .mul, .div, .mod_rem => maybeAdvanceSourceCursorToMainToken(gz, node), + else => undefined, + }; const rhs = try reachableExpr(gz, scope, .{ .rl = .none }, node_datas[node].rhs, node); switch (op_inst_tag) { .add, .sub, .mul, .div, .mod_rem => { - try emitDbgStmt(gz, line, column); + try emitDbgStmt(gz, cursor); }, else => {}, } @@ -6787,14 +6755,15 @@ fn switchExpr( } const operand_ri: ResultInfo = .{ .rl = if (any_payload_is_ref) .ref else .none }; + astgen.advanceSourceCursorToNode(operand_node); - const operand_line = astgen.source_line - parent_gz.decl_line; - const operand_column = astgen.source_column; + const operand_lc = LineColumn{ astgen.source_line - parent_gz.decl_line, astgen.source_column }; + const raw_operand = try expr(parent_gz, scope, operand_ri, operand_node); const cond_tag: Zir.Inst.Tag = if (any_payload_is_ref) .switch_cond_ref else .switch_cond; const cond = try parent_gz.addUnNode(cond_tag, raw_operand, operand_node); // Sema expects a dbg_stmt immediately after switch_cond(_ref) - try emitDbgStmt(parent_gz, operand_line, operand_column); + try emitDbgStmt(parent_gz, operand_lc); // We need the type of the operand to use as the result location for all the prong items. const cond_ty_inst = try parent_gz.addUnNode(.typeof, cond, operand_node); const item_ri: ResultInfo = .{ .rl = .{ .ty = cond_ty_inst } }; @@ -7154,8 +7123,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref if (!gz.is_comptime) { try emitDbgNode(gz, node); } - const ret_line = astgen.source_line - gz.decl_line; - const ret_column = astgen.source_column; + const ret_lc = LineColumn{ astgen.source_line - gz.decl_line, astgen.source_column }; const defer_outer = &astgen.fn_block.?.base; @@ -7179,13 +7147,13 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref const defer_counts = countDefers(defer_outer, scope); if (!defer_counts.need_err_code) { try genDefers(gz, defer_outer, scope, .both_sans_err); - try emitDbgStmt(gz, ret_line, ret_column); + try emitDbgStmt(gz, ret_lc); _ = try gz.addStrTok(.ret_err_value, err_name_str_index, ident_token); return Zir.Inst.Ref.unreachable_value; } const err_code = try gz.addStrTok(.ret_err_value_code, err_name_str_index, ident_token); try genDefers(gz, defer_outer, scope, .{ .both = err_code }); - try emitDbgStmt(gz, ret_line, ret_column); + try emitDbgStmt(gz, ret_lc); _ = try gz.addUnNode(.ret_node, err_code, node); return Zir.Inst.Ref.unreachable_value; } @@ -7210,7 +7178,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref // As our last action before the return, "pop" the error trace if needed _ = try gz.addRestoreErrRetIndex(.ret, .always); - try emitDbgStmt(gz, ret_line, ret_column); + try emitDbgStmt(gz, ret_lc); try gz.addRet(ri, operand, node); return Zir.Inst.Ref.unreachable_value; }, @@ -7218,7 +7186,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref // Value is always an error. Emit both error defers and regular defers. const err_code = if (ri.rl == .ptr) try gz.addUnNode(.load, ri.rl.ptr.inst, node) else operand; try genDefers(gz, defer_outer, scope, .{ .both = err_code }); - try emitDbgStmt(gz, ret_line, ret_column); + try emitDbgStmt(gz, ret_lc); try gz.addRet(ri, operand, node); return Zir.Inst.Ref.unreachable_value; }, @@ -7227,7 +7195,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref if (!defer_counts.have_err) { // Only regular defers; no branch needed. try genDefers(gz, defer_outer, scope, .normal_only); - try emitDbgStmt(gz, ret_line, ret_column); + try emitDbgStmt(gz, ret_lc); // As our last action before the return, "pop" the error trace if needed const result = if (ri.rl == .ptr) try gz.addUnNode(.load, ri.rl.ptr.inst, node) else operand; @@ -7250,7 +7218,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref // As our last action before the return, "pop" the error trace if needed _ = try then_scope.addRestoreErrRetIndex(.ret, .always); - try emitDbgStmt(&then_scope, ret_line, ret_column); + try emitDbgStmt(&then_scope, ret_lc); try then_scope.addRet(ri, operand, node); var else_scope = gz.makeSubBlock(scope); @@ -7260,7 +7228,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref .both = try else_scope.addUnNode(.err_union_code, result, node), }; try genDefers(&else_scope, defer_outer, scope, which_ones); - try emitDbgStmt(&else_scope, ret_line, ret_column); + try emitDbgStmt(&else_scope, ret_lc); try else_scope.addRet(ri, operand, node); try setCondBrPayload(condbr, is_non_err, &then_scope, 0, &else_scope, 0); @@ -8649,11 +8617,14 @@ fn typeCast( rhs_node: Ast.Node.Index, tag: Zir.Inst.Tag, ) InnerError!Zir.Inst.Ref { - try emitDbgNode(gz, node); + const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); + const result_type = try typeExpr(gz, scope, lhs_node); + const operand = try expr(gz, scope, .{ .rl = .none }, rhs_node); + try emitDbgStmt(gz, cursor); const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{ - .lhs = try typeExpr(gz, scope, lhs_node), - .rhs = try expr(gz, scope, .{ .rl = .none }, rhs_node), + .lhs = result_type, + .rhs = operand, }); return rvalue(gz, ri, result, node); } @@ -8680,14 +8651,15 @@ fn simpleUnOp( operand_node: Ast.Node.Index, tag: Zir.Inst.Tag, ) InnerError!Zir.Inst.Ref { - switch (tag) { - .tag_name, .error_name, .ptr_to_int => try emitDbgNode(gz, node), - else => {}, - } + const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); const operand = if (tag == .compile_error) try comptimeExpr(gz, scope, operand_ri, operand_node) else try expr(gz, scope, operand_ri, operand_node); + switch (tag) { + .tag_name, .error_name, .ptr_to_int => try emitDbgStmt(gz, cursor), + else => {}, + } const result = try gz.addUnNode(tag, operand, node); return rvalue(gz, ri, result, node); } @@ -8759,12 +8731,12 @@ fn divBuiltin( rhs_node: Ast.Node.Index, tag: Zir.Inst.Tag, ) InnerError!Zir.Inst.Ref { - try emitDbgNode(gz, node); + const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); + const lhs = try expr(gz, scope, .{ .rl = .none }, lhs_node); + const rhs = try expr(gz, scope, .{ .rl = .none }, rhs_node); - const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{ - .lhs = try expr(gz, scope, .{ .rl = .none }, lhs_node), - .rhs = try expr(gz, scope, .{ .rl = .none }, rhs_node), - }); + try emitDbgStmt(gz, cursor); + const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs }); return rvalue(gz, ri, result, node); } @@ -8813,23 +8785,21 @@ fn shiftOp( rhs_node: Ast.Node.Index, tag: Zir.Inst.Tag, ) InnerError!Zir.Inst.Ref { - var line = gz.astgen.source_line - gz.decl_line; - var column = gz.astgen.source_column; const lhs = try expr(gz, scope, .{ .rl = .none }, lhs_node); - switch (gz.astgen.tree.nodes.items(.tag)[node]) { - .shl, .shr => { - maybeAdvanceSourceCursorToMainToken(gz, node); - line = gz.astgen.source_line - gz.decl_line; - column = gz.astgen.source_column; - }, - else => {}, - } + const cursor = switch (gz.astgen.tree.nodes.items(.tag)[node]) { + .shl, .shr => maybeAdvanceSourceCursorToMainToken(gz, node), + else => undefined, + }; const log2_int_type = try gz.addUnNode(.typeof_log2_int_type, lhs, lhs_node); const rhs = try expr(gz, scope, .{ .rl = .{ .ty = log2_int_type }, .ctx = .shift_op }, rhs_node); - try emitDbgStmt(gz, line, column); + switch (gz.astgen.tree.nodes.items(.tag)[node]) { + .shl, .shr => try emitDbgStmt(gz, cursor), + else => undefined, + } + const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs, @@ -12593,16 +12563,20 @@ fn detectLocalShadowing( }; } +const LineColumn = struct { u32, u32 }; + /// Advances the source cursor to the main token of `node` if not in comptime scope. /// Usually paired with `emitDbgStmt`. -fn maybeAdvanceSourceCursorToMainToken(gz: *GenZir, node: Ast.Node.Index) void { - if (gz.is_comptime) return; +fn maybeAdvanceSourceCursorToMainToken(gz: *GenZir, node: Ast.Node.Index) LineColumn { + if (gz.is_comptime) return .{ gz.astgen.source_line - gz.decl_line, gz.astgen.source_column }; const tree = gz.astgen.tree; const token_starts = tree.tokens.items(.start); const main_tokens = tree.nodes.items(.main_token); const node_start = token_starts[main_tokens[node]]; gz.astgen.advanceSourceCursor(node_start); + + return .{ gz.astgen.source_line - gz.decl_line, gz.astgen.source_column }; } /// Advances the source cursor to the beginning of `node`. @@ -12806,13 +12780,13 @@ fn countBodyLenAfterFixups(astgen: *AstGen, body: []const Zir.Inst.Index) u32 { return @intCast(u32, count); } -fn emitDbgStmt(gz: *GenZir, line: u32, column: u32) !void { +fn emitDbgStmt(gz: *GenZir, lc: LineColumn) !void { if (gz.is_comptime) return; _ = try gz.add(.{ .tag = .dbg_stmt, .data = .{ .dbg_stmt = .{ - .line = line, - .column = column, + .line = lc[0], + .column = lc[1], }, } }); } From c3b30a0fd0b0f4f0bd8966cc8c5fadb028d0517e Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 23 Apr 2023 12:29:58 +0300 Subject: [PATCH 10/44] enable passing test Closes #12360 --- test/behavior/type.zig | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test/behavior/type.zig b/test/behavior/type.zig index a12949fffd..0d309b9a6e 100644 --- a/test/behavior/type.zig +++ b/test/behavior/type.zig @@ -486,10 +486,9 @@ test "Type.Union from regular enum" { } test "Type.Fn" { - if (true) { - // https://github.com/ziglang/zig/issues/12360 - return error.SkipZigTest; - } + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO const some_opaque = opaque {}; const some_ptr = *some_opaque; From c3ce04b9e548b0dbf2c286e7c764320620f06bf9 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 22 Apr 2023 15:33:00 +0200 Subject: [PATCH 11/44] elf: add missing doc comments to some definitions --- lib/std/elf.zig | 71 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/lib/std/elf.zig b/lib/std/elf.zig index 2eb89e810a..6807e153fd 100644 --- a/lib/std/elf.zig +++ b/lib/std/elf.zig @@ -221,75 +221,138 @@ pub const DT_IA_64_NUM = 1; pub const DT_NIOS2_GP = 0x70000002; +/// Program header table entry unused pub const PT_NULL = 0; +/// Loadable program segment pub const PT_LOAD = 1; +/// Dynamic linking information pub const PT_DYNAMIC = 2; +/// Program interpreter pub const PT_INTERP = 3; +/// Auxiliary information pub const PT_NOTE = 4; +/// Reserved pub const PT_SHLIB = 5; +/// Entry for header table itself pub const PT_PHDR = 6; +/// Thread-local storage segment pub const PT_TLS = 7; +/// Number of defined types pub const PT_NUM = 8; +/// Start of OS-specific pub const PT_LOOS = 0x60000000; +/// GCC .eh_frame_hdr segment pub const PT_GNU_EH_FRAME = 0x6474e550; +/// Indicates stack executability pub const PT_GNU_STACK = 0x6474e551; +/// Read-only after relocation pub const PT_GNU_RELRO = 0x6474e552; pub const PT_LOSUNW = 0x6ffffffa; +/// Sun specific segment pub const PT_SUNWBSS = 0x6ffffffa; +/// Stack segment pub const PT_SUNWSTACK = 0x6ffffffb; pub const PT_HISUNW = 0x6fffffff; +/// End of OS-specific pub const PT_HIOS = 0x6fffffff; +/// Start of processor-specific pub const PT_LOPROC = 0x70000000; +/// End of processor-specific pub const PT_HIPROC = 0x7fffffff; +/// Section header table entry unused pub const SHT_NULL = 0; +/// Program data pub const SHT_PROGBITS = 1; +/// Symbol table pub const SHT_SYMTAB = 2; +/// String table pub const SHT_STRTAB = 3; +/// Relocation entries with addends pub const SHT_RELA = 4; +/// Symbol hash table pub const SHT_HASH = 5; +/// Dynamic linking information pub const SHT_DYNAMIC = 6; +/// Notes pub const SHT_NOTE = 7; +/// Program space with no data (bss) pub const SHT_NOBITS = 8; +/// Relocation entries, no addends pub const SHT_REL = 9; +/// Reserved pub const SHT_SHLIB = 10; +/// Dynamic linker symbol table pub const SHT_DYNSYM = 11; +/// Array of constructors pub const SHT_INIT_ARRAY = 14; +/// Array of destructors pub const SHT_FINI_ARRAY = 15; +/// Array of pre-constructors pub const SHT_PREINIT_ARRAY = 16; +/// Section group pub const SHT_GROUP = 17; +/// Extended section indices pub const SHT_SYMTAB_SHNDX = 18; +/// Start of OS-specific pub const SHT_LOOS = 0x60000000; +/// End of OS-specific pub const SHT_HIOS = 0x6fffffff; +/// Start of processor-specific pub const SHT_LOPROC = 0x70000000; +/// End of processor-specific pub const SHT_HIPROC = 0x7fffffff; +/// Start of application-specific pub const SHT_LOUSER = 0x80000000; +/// End of application-specific pub const SHT_HIUSER = 0xffffffff; +/// Local symbol pub const STB_LOCAL = 0; +/// Global symbol pub const STB_GLOBAL = 1; +/// Weak symbol pub const STB_WEAK = 2; +/// Number of defined types pub const STB_NUM = 3; +/// Start of OS-specific pub const STB_LOOS = 10; +/// Unique symbol pub const STB_GNU_UNIQUE = 10; +/// End of OS-specific pub const STB_HIOS = 12; +/// Start of processor-specific pub const STB_LOPROC = 13; +/// End of processor-specific pub const STB_HIPROC = 15; pub const STB_MIPS_SPLIT_COMMON = 13; +/// Symbol type is unspecified pub const STT_NOTYPE = 0; +/// Symbol is a data object pub const STT_OBJECT = 1; +/// Symbol is a code object pub const STT_FUNC = 2; +/// Symbol associated with a section pub const STT_SECTION = 3; +/// Symbol's name is file name pub const STT_FILE = 4; +/// Symbol is a common data object pub const STT_COMMON = 5; +/// Symbol is thread-local data object pub const STT_TLS = 6; +/// Number of defined types pub const STT_NUM = 7; +/// Start of OS-specific pub const STT_LOOS = 10; +/// Symbol is indirect code object pub const STT_GNU_IFUNC = 10; +/// End of OS-specific pub const STT_HIOS = 12; +/// Start of processor-specific pub const STT_LOPROC = 13; +/// End of processor-specific pub const STT_HIPROC = 15; pub const STT_SPARC_REGISTER = 13; @@ -1630,14 +1693,20 @@ pub const PF_MASKOS = 0x0ff00000; /// Bits for processor-specific semantics. pub const PF_MASKPROC = 0xf0000000; -// Special section indexes used in Elf{32,64}_Sym. +/// Undefined section pub const SHN_UNDEF = 0; +/// Start of reserved indices pub const SHN_LORESERVE = 0xff00; +/// Start of processor-specific pub const SHN_LOPROC = 0xff00; +/// End of processor-specific pub const SHN_HIPROC = 0xff1f; pub const SHN_LIVEPATCH = 0xff20; +/// Associated symbol is absolute pub const SHN_ABS = 0xfff1; +/// Associated symbol is common pub const SHN_COMMON = 0xfff2; +/// End of reserved indices pub const SHN_HIRESERVE = 0xffff; /// AMD x86-64 relocations. From b95cdf0aeb4d4d31c0b6a54302ef61baec8f6773 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 22 Apr 2023 16:41:25 +0200 Subject: [PATCH 12/44] elf: add helpers for extracting type and bind from symbol def --- lib/std/elf.zig | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/lib/std/elf.zig b/lib/std/elf.zig index 6807e153fd..e2cad5640e 100644 --- a/lib/std/elf.zig +++ b/lib/std/elf.zig @@ -719,6 +719,13 @@ pub const Elf32_Sym = extern struct { st_info: u8, st_other: u8, st_shndx: Elf32_Section, + + pub inline fn st_type(self: @This()) u4 { + return @truncate(u4, self.st_info); + } + pub inline fn st_bind(self: @This()) u4 { + return @truncate(u4, self.st_info >> 4); + } }; pub const Elf64_Sym = extern struct { st_name: Elf64_Word, @@ -727,6 +734,13 @@ pub const Elf64_Sym = extern struct { st_shndx: Elf64_Section, st_value: Elf64_Addr, st_size: Elf64_Xword, + + pub inline fn st_type(self: @This()) u4 { + return @truncate(u4, self.st_info); + } + pub inline fn st_bind(self: @This()) u4 { + return @truncate(u4, self.st_info >> 4); + } }; pub const Elf32_Syminfo = extern struct { si_boundto: Elf32_Half, @@ -744,7 +758,7 @@ pub const Elf32_Rel = extern struct { return @truncate(u24, self.r_info >> 8); } pub inline fn r_type(self: @This()) u8 { - return @truncate(u8, self.r_info & 0xff); + return @truncate(u8, self.r_info); } }; pub const Elf64_Rel = extern struct { @@ -755,7 +769,7 @@ pub const Elf64_Rel = extern struct { return @truncate(u32, self.r_info >> 32); } pub inline fn r_type(self: @This()) u32 { - return @truncate(u32, self.r_info & 0xffffffff); + return @truncate(u32, self.r_info); } }; pub const Elf32_Rela = extern struct { @@ -767,7 +781,7 @@ pub const Elf32_Rela = extern struct { return @truncate(u24, self.r_info >> 8); } pub inline fn r_type(self: @This()) u8 { - return @truncate(u8, self.r_info & 0xff); + return @truncate(u8, self.r_info); } }; pub const Elf64_Rela = extern struct { @@ -779,7 +793,7 @@ pub const Elf64_Rela = extern struct { return @truncate(u32, self.r_info >> 32); } pub inline fn r_type(self: @This()) u32 { - return @truncate(u32, self.r_info & 0xffffffff); + return @truncate(u32, self.r_info); } }; pub const Elf32_Dyn = extern struct { From f1e43d1f4fde4f7b6909c660ec210f9b14cacb4d Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 23 Apr 2023 13:00:33 +0300 Subject: [PATCH 13/44] Sema: emit cast to null panics for function pointers Closes #14676 --- src/Sema.zig | 6 ++--- ...inter casting to null function pointer.zig | 23 +++++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 test/cases/safety/pointer casting to null function pointer.zig diff --git a/src/Sema.zig b/src/Sema.zig index 5c19d37431..d5c3dbef6b 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -19626,7 +19626,7 @@ fn zirIntToPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai } try sema.requireRuntimeBlock(block, src, operand_src); - if (block.wantSafety() and try sema.typeHasRuntimeBits(elem_ty)) { + if (block.wantSafety() and (try sema.typeHasRuntimeBits(elem_ty) or elem_ty.zigTypeTag() == .Fn)) { if (!ptr_ty.isAllowzeroPtr()) { const is_non_zero = try block.addBinOp(.cmp_neq, operand_coerced, .zero_usize); try sema.addSafetyCheck(block, is_non_zero, .cast_to_null); @@ -19852,7 +19852,7 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air try sema.requireRuntimeBlock(block, src, null); if (block.wantSafety() and operand_ty.ptrAllowsZero() and !dest_ty.ptrAllowsZero() and - try sema.typeHasRuntimeBits(dest_ty.elemType2())) + (try sema.typeHasRuntimeBits(dest_ty.elemType2()) or dest_ty.elemType2().zigTypeTag() == .Fn)) { const ptr_int = try block.addUnOp(.ptrtoint, ptr); const is_non_zero = try block.addBinOp(.cmp_neq, ptr_int, .zero_usize); @@ -27718,7 +27718,7 @@ fn coerceCompatiblePtrs( try sema.requireRuntimeBlock(block, inst_src, null); const inst_allows_zero = inst_ty.zigTypeTag() != .Pointer or inst_ty.ptrAllowsZero(); if (block.wantSafety() and inst_allows_zero and !dest_ty.ptrAllowsZero() and - try sema.typeHasRuntimeBits(dest_ty.elemType2())) + (try sema.typeHasRuntimeBits(dest_ty.elemType2()) or dest_ty.elemType2().zigTypeTag() == .Fn)) { const actual_ptr = if (inst_ty.isSlice()) try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty) diff --git a/test/cases/safety/pointer casting to null function pointer.zig b/test/cases/safety/pointer casting to null function pointer.zig new file mode 100644 index 0000000000..bedbcdda85 --- /dev/null +++ b/test/cases/safety/pointer casting to null function pointer.zig @@ -0,0 +1,23 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { + _ = stack_trace; + if (std.mem.eql(u8, message, "cast causes pointer to be null")) { + std.process.exit(0); + } + std.process.exit(1); +} + +fn getNullPtr() ?*const anyopaque { + return null; +} +pub fn main() !void { + const null_ptr: ?*const anyopaque = getNullPtr(); + const required_ptr: *align(1) const fn() void = @ptrCast(*align(1) const fn() void, null_ptr); + _ = required_ptr; + return error.TestFailed; +} + +// run +// backend=llvm +// target=native From 9140249d2993d9d9f4c92b2892db421e1e0fb7ae Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Tue, 4 Apr 2023 21:00:09 +0100 Subject: [PATCH 14/44] std: enriching malloc api on freebsd. --- lib/std/c/freebsd.zig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/std/c/freebsd.zig b/lib/std/c/freebsd.zig index 534b6bc592..9f02872abe 100644 --- a/lib/std/c/freebsd.zig +++ b/lib/std/c/freebsd.zig @@ -29,6 +29,8 @@ pub const CPU_WHICH_TIDPID: cpuwhich_t = 8; extern "c" fn __error() *c_int; pub const _errno = __error; +pub extern "c" var malloc_options: [*:0]const u8; + pub extern "c" fn getdents(fd: c_int, buf_ptr: [*]u8, nbytes: usize) usize; pub extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int; pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) isize; @@ -42,6 +44,7 @@ pub extern "c" fn arc4random_buf(buf: [*]u8, len: usize) void; pub extern "c" fn posix_memalign(memptr: *?*anyopaque, alignment: usize, size: usize) c_int; pub extern "c" fn malloc_usable_size(?*const anyopaque) usize; +pub extern "c" fn reallocf(?*anyopaque, usize) ?*anyopaque; pub extern "c" fn getpid() pid_t; From 7b0e015eb42272d84396f44fc904ab79f38dd696 Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Thu, 6 Apr 2023 20:02:42 +0100 Subject: [PATCH 15/44] std: add a subset of the macOs's libproc api. --- lib/std/c/darwin.zig | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/std/c/darwin.zig b/lib/std/c/darwin.zig index a46337b721..19baee92d3 100644 --- a/lib/std/c/darwin.zig +++ b/lib/std/c/darwin.zig @@ -3846,3 +3846,11 @@ pub extern "c" fn os_signpost_interval_begin(log: os_log_t, signpos: os_signpost pub extern "c" fn os_signpost_interval_end(log: os_log_t, signpos: os_signpost_id_t, func: [*]const u8, ...) void; pub extern "c" fn os_signpost_id_make_with_pointer(log: os_log_t, ptr: ?*anyopaque) os_signpost_id_t; pub extern "c" fn os_signpost_enabled(log: os_log_t) bool; + +pub extern "c" fn proc_listpids(tpe: u32, tinfo: u32, buffer: ?*anyopaque, buffersize: c_int) c_int; +pub extern "c" fn proc_listallpids(buffer: ?*anyopaque, buffersize: c_int) c_int; +pub extern "c" fn proc_listpgrppids(pgrpid: pid_t, buffer: ?*anyopaque, buffersize: c_int) c_int; +pub extern "c" fn proc_listchildpids(ppid: pid_t, buffer: ?*anyopaque, buffersize: c_int) c_int; +pub extern "c" fn proc_pidinfo(pid: c_int, flavor: c_int, arg: u64, buffer: ?*anyopaque, buffersize: c_int) c_int; +pub extern "c" fn proc_name(pid: c_int, buffer: ?*anyopaque, buffersize: u32) c_int; +pub extern "c" fn proc_pidpath(pid: c_int, buffer: ?*anyopaque, buffersize: u32) c_int; From 1d322fe5102368f80fd4d00dcbbe3dca9e6306f8 Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Sat, 8 Apr 2023 15:21:43 +0100 Subject: [PATCH 16/44] std: add accept_filter struct to make use of SO_ACCEPTFILTER socket option --- lib/std/c/freebsd.zig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/std/c/freebsd.zig b/lib/std/c/freebsd.zig index 9f02872abe..16653ae47b 100644 --- a/lib/std/c/freebsd.zig +++ b/lib/std/c/freebsd.zig @@ -1105,6 +1105,11 @@ pub const DT = struct { pub const WHT = 14; }; +pub const accept_filter = extern struct { + af_name: [16]u8, + af_args: [240]u8, +}; + /// add event to kq (implies enable) pub const EV_ADD = 0x0001; From 9691cded95afa53f17bfc50edc371d4fe673b56a Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 15 Apr 2023 06:36:28 +0100 Subject: [PATCH 17/44] std: mcontext layout for x86 and fixing few x86_64 fields types for FreeBSD --- lib/std/c/freebsd.zig | 42 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/lib/std/c/freebsd.zig b/lib/std/c/freebsd.zig index 16653ae47b..037ba989ca 100644 --- a/lib/std/c/freebsd.zig +++ b/lib/std/c/freebsd.zig @@ -1391,15 +1391,47 @@ pub const mcontext_t = switch (builtin.cpu.arch) { rflags: u64, rsp: u64, ss: u64, - len: u64, - fpformat: u64, - ownedfp: u64, - fpstate: [64]u64 align(16), + len: c_long, + fpformat: c_long, + ownedfp: c_long, + fpstate: [64]c_long align(16), fsbase: u64, gsbase: u64, xfpustate: u64, xfpustate_len: u64, - spare: [4]u64, + spare: [4]c_long, + }, + .x86 => extern struct { + onstack: u32, + gs: u32, + fs: u32, + es: u32, + ds: u32, + edi: u32, + esi: u32, + ebp: u32, + isp: u32, + ebx: u32, + edx: u32, + ecx: u32, + eax: u32, + trapno: u32, + err: u32, + eip: u32, + cs: u32, + eflags: u32, + esp: u32, + ss: u32, + len: c_int, + fpformat: c_int, + ownedfp: c_int, + flags: u32, + fpstate: [128]c_int align(16), + fsbase: u32, + gsbase: u32, + xpustate: u32, + xpustate_len: u32, + spare2: [4]c_int, }, .aarch64 => extern struct { gpregs: extern struct { From 9ef615104acb0f7cfed8f871404679a7df5571fe Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Tue, 18 Apr 2023 19:43:09 +0100 Subject: [PATCH 18/44] std: adding FreeBSD's sched wrappers --- lib/std/c/freebsd.zig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/std/c/freebsd.zig b/lib/std/c/freebsd.zig index 037ba989ca..af9f6b023b 100644 --- a/lib/std/c/freebsd.zig +++ b/lib/std/c/freebsd.zig @@ -53,6 +53,9 @@ pub extern "c" fn kinfo_getvmmap(pid: pid_t, cntp: *c_int) ?[*]kinfo_vmentry; pub extern "c" fn cpuset_getaffinity(level: cpulevel_t, which: cpuwhich_t, id: id_t, setsize: usize, mask: *cpuset_t) c_int; pub extern "c" fn cpuset_setaffinity(level: cpulevel_t, which: cpuwhich_t, id: id_t, setsize: usize, mask: *const cpuset_t) c_int; +pub extern "c" fn sched_getaffinity(pid: pid_t, cpusetsz: usize, cpuset: *cpuset_t) c_int; +pub extern "c" fn sched_setaffinity(pid: pid_t, cpusetsz: usize, cpuset: *const cpuset_t) c_int; +pub extern "c" fn sched_getcpu() c_int; pub const sf_hdtr = extern struct { headers: [*]const iovec_const, From 23c4f55a612842d8544a9dfe604a9caf1ca39697 Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Sat, 22 Apr 2023 09:06:06 +0100 Subject: [PATCH 19/44] std: adding sigevent to supported platforms. --- lib/std/c/dragonfly.zig | 17 +++++++++++++++++ lib/std/c/freebsd.zig | 25 +++++++++++++++++++++++++ lib/std/c/haiku.zig | 19 +++++++++++++++++++ lib/std/c/netbsd.zig | 19 +++++++++++++++++++ lib/std/c/solaris.zig | 19 +++++++++++++++++++ 5 files changed, 99 insertions(+) diff --git a/lib/std/c/dragonfly.zig b/lib/std/c/dragonfly.zig index 900e1165f0..c7561d558d 100644 --- a/lib/std/c/dragonfly.zig +++ b/lib/std/c/dragonfly.zig @@ -1143,3 +1143,20 @@ pub const POLL = struct { pub const HUP = 0x0010; pub const NVAL = 0x0020; }; + +pub const SIGEV = struct { + pub const NONE = 0; + pub const SIGNAL = 1; + pub const THREAD = 2; +}; + +pub const sigevent = extern struct { + sigev_notify: c_int, + __sigev_u: extern union { + __sigev_signo: c_int, + __sigev_notify_kqueue: c_int, + __sigev_notify_attributes: ?*pthread_attr_t, + }, + sigev_value: sigval, + sigev_notify_function: ?*const fn (sigval) callconv(.C) void, +}; diff --git a/lib/std/c/freebsd.zig b/lib/std/c/freebsd.zig index af9f6b023b..ab84060522 100644 --- a/lib/std/c/freebsd.zig +++ b/lib/std/c/freebsd.zig @@ -2248,3 +2248,28 @@ pub const shm_largeconf = extern struct { pub extern "c" fn shm_create_largepage(path: [*:0]const u8, flags: c_int, psind: c_int, alloc_policy: c_int, mode: mode_t) c_int; pub extern "c" fn elf_aux_info(aux: c_int, buf: ?*anyopaque, buflen: c_int) c_int; + +pub const lwpid = i32; + +pub const SIGEV = struct { + pub const NONE = 0; + pub const SIGNAL = 1; + pub const THREAD = 2; + pub const KEVENT = 3; + pub const THREAD_ID = 4; +}; + +pub const sigevent = extern struct { + sigev_notify: c_int, + sigev_signo: c_int, + sigev_value: sigval, + _sigev_un: extern union { + _threadid: lwpid, + _sigev_thread: extern struct { + _function: ?*const fn (sigval) callconv(.C) void, + _attribute: ?**pthread_attr_t, + }, + _kevent_flags: c_ushort, + __spare__: [8]c_long, + }, +}; diff --git a/lib/std/c/haiku.zig b/lib/std/c/haiku.zig index 9c4f8460de..c6d661279c 100644 --- a/lib/std/c/haiku.zig +++ b/lib/std/c/haiku.zig @@ -1038,3 +1038,22 @@ pub const termios = extern struct { }; pub const MSG_NOSIGNAL = 0x0800; + +pub const SIGEV = struct { + pub const NONE = 0; + pub const SIGNAL = 1; + pub const THREAD = 2; +}; + +pub const sigval = extern union { + int: c_int, + ptr: ?*anyopaque, +}; + +pub const sigevent = extern struct { + sigev_notify: c_int, + sigev_signo: c_int, + sigev_value: sigval, + sigev_notify_function: ?*const fn (sigval) callconv(.C) void, + sigev_notify_attributes: ?*pthread_attr_t, +}; diff --git a/lib/std/c/netbsd.zig b/lib/std/c/netbsd.zig index 41524885f7..f70549775c 100644 --- a/lib/std/c/netbsd.zig +++ b/lib/std/c/netbsd.zig @@ -1633,3 +1633,22 @@ pub const POLL = struct { pub const HUP = 0x0010; pub const NVAL = 0x0020; }; + +pub const SIGEV = struct { + pub const NONE = 0; + pub const SIGNAL = 1; + pub const THREAD = 2; +}; + +pub const sigval = extern union { + int: c_int, + ptr: ?*anyopaque, +}; + +pub const sigevent = extern struct { + sigev_notify: c_int, + sigev_signo: c_int, + sigev_value: sigval, + sigev_notify_function: ?*const fn (sigval) callconv(.C) void, + sigev_notify_attributes: ?*pthread_attr_t, +}; diff --git a/lib/std/c/solaris.zig b/lib/std/c/solaris.zig index b1b789c77b..c2c38f46ce 100644 --- a/lib/std/c/solaris.zig +++ b/lib/std/c/solaris.zig @@ -1927,3 +1927,22 @@ pub fn IOW(io_type: u8, nr: u8, comptime IOT: type) i32 { pub fn IOWR(io_type: u8, nr: u8, comptime IOT: type) i32 { return ioImpl(.read_write, io_type, nr, IOT); } + +pub const SIGEV = struct { + pub const NONE = 0; + pub const SIGNAL = 1; + pub const THREAD = 2; +}; + +pub const sigval = extern union { + int: c_int, + ptr: ?*anyopaque, +}; + +pub const sigevent = extern struct { + sigev_notify: c_int, + sigev_signo: c_int, + sigev_value: sigval, + sigev_notify_function: ?*const fn (sigval) callconv(.C) void, + sigev_notify_attributes: ?*pthread_attr_t, +}; From ce3fe72d9a5e380ad3e25018e562abebc5ee24a6 Mon Sep 17 00:00:00 2001 From: xEgoist Date: Sat, 22 Apr 2023 06:26:42 -0500 Subject: [PATCH 20/44] fs.Dir.deleteTree: Fix DirNotEmpty condition `deleteTree` needs to retry once the directory is reported to be not empty. Otherwise, the retry condition is never reached. --- lib/std/fs.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/fs.zig b/lib/std/fs.zig index b5e2d7ad63..61d7b9faca 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -2210,7 +2210,7 @@ pub const Dir = struct { var need_to_retry: bool = false; parent_dir.deleteDir(name) catch |err| switch (err) { error.FileNotFound => {}, - error.DirNotEmpty => need_to_retry = false, + error.DirNotEmpty => need_to_retry = true, else => |e| return e, }; From 284c7b22a85a6a915740be826e66f91a4cbc743e Mon Sep 17 00:00:00 2001 From: Nicolas Sterchele Date: Thu, 6 Apr 2023 22:57:25 +0200 Subject: [PATCH 21/44] process: add args definition comment To improve understandability of its purpose. --- lib/std/process.zig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/std/process.zig b/lib/std/process.zig index 578efa62c4..87dbed443f 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -818,7 +818,8 @@ pub const ArgIterator = struct { } }; -/// Use argsWithAllocator() for cross-platform code +/// Holds the command-line arguments, with the program name as the first entry. +/// Use argsWithAllocator() for cross-platform code. pub fn args() ArgIterator { return ArgIterator.init(); } From 40029d6875ce297406b573d78d11b0d00107cab6 Mon Sep 17 00:00:00 2001 From: Travis Staloch Date: Sun, 9 Apr 2023 16:05:17 -0700 Subject: [PATCH 22/44] std.tar: make sub dirs + trim spaces closes #15222. these changes allow the .tgz from this issue to decompress and the test code to succeed. --- lib/std/tar.zig | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/std/tar.zig b/lib/std/tar.zig index 91772d7319..ec668d5f93 100644 --- a/lib/std/tar.zig +++ b/lib/std/tar.zig @@ -35,7 +35,7 @@ pub const Header = struct { pub fn fileSize(header: Header) !u64 { const raw = header.bytes[124..][0..12]; const ltrimmed = std.mem.trimLeft(u8, raw, "0"); - const rtrimmed = std.mem.trimRight(u8, ltrimmed, "\x00"); + const rtrimmed = std.mem.trimRight(u8, ltrimmed, " \x00"); if (rtrimmed.len == 0) return 0; return std.fmt.parseInt(u64, rtrimmed, 8); } @@ -122,13 +122,16 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi .directory => { const file_name = try stripComponents(unstripped_file_name, options.strip_components); if (file_name.len != 0) { - try dir.makeDir(file_name); + try dir.makePath(file_name); } }, .normal => { if (file_size == 0 and unstripped_file_name.len == 0) return; const file_name = try stripComponents(unstripped_file_name, options.strip_components); + if (std.fs.path.dirname(file_name)) |dir_name| { + try dir.makePath(dir_name); + } var file = try dir.createFile(file_name, .{}); defer file.close(); From 316812786c376b33bd27c8c33bce04f6e2c223df Mon Sep 17 00:00:00 2001 From: Bogdan Romanyuk <65823030+wrongnull@users.noreply.github.com> Date: Sat, 22 Apr 2023 14:43:37 +0300 Subject: [PATCH 23/44] langref: add documentation for noinline keyword --- doc/langref.html.in | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/doc/langref.html.in b/doc/langref.html.in index 50807377cd..81b0450b4d 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -11966,6 +11966,17 @@ fn readU32Be() u32 {} + + +
{#syntax#}noinline{#endsyntax#}
+ + + {#syntax#}noinline{#endsyntax#} disallows function to be inlined in all call sites. +
    +
  • See also {#link|Functions#}
  • +
+ +
{#syntax#}nosuspend{#endsyntax#}
From 253eb72c1489604549ff35e1f9f32844f1bc31b5 Mon Sep 17 00:00:00 2001 From: Hubert Jasudowicz Date: Tue, 7 Mar 2023 22:51:25 +0100 Subject: [PATCH 24/44] std.os.linux: Add new CAP constants --- lib/std/os/linux.zig | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index b6ec05997f..6646cb1d32 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -3464,7 +3464,10 @@ pub const CAP = struct { pub const WAKE_ALARM = 35; pub const BLOCK_SUSPEND = 36; pub const AUDIT_READ = 37; - pub const LAST_CAP = AUDIT_READ; + pub const PERFMON = 38; + pub const BPF = 39; + pub const CHECKPOINT_RESTORE = 40; + pub const LAST_CAP = CHECKPOINT_RESTORE; pub fn valid(x: u8) bool { return x >= 0 and x <= LAST_CAP; From 658b4db223c24616a95cdf3bb3226caa23a3b50e Mon Sep 17 00:00:00 2001 From: Manlio Perillo Date: Sun, 23 Apr 2023 20:00:10 +0200 Subject: [PATCH 25/44] langref: improve for loop documentation - Add an example of iterating over consecutive integers using the range syntax - Add an example of iterating over multiple objects - Update the "nested break" and "nested continue" tests to use the range syntax, instead of a temporary array --- doc/langref.html.in | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 81b0450b4d..a8af959615 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -4667,6 +4667,29 @@ test "for basics" { sum2 += @intCast(i32, i); } try expect(sum2 == 10); + + // To iterate over consecutive integers, use the range syntax. + // Unbounded range is always a compile error. + var sum3 : usize = 0; + for (0..5) |i| { + sum3 += i; + } + try expect(sum3 == 10); +} + +test "multi object for" { + const items = [_]usize{ 1, 2, 3 }; + const items2 = [_]usize{ 4, 5, 6 }; + var count: usize = 0; + + // Iterate over multiple objects. + // All lengths must be equal at the start of the loop, otherwise detectable + // illegal behavior occurs. + for (items, items2) |i, j| { + count += i + j; + } + + try expect(count == 21); } test "for reference" { @@ -4710,8 +4733,8 @@ const expect = std.testing.expect; test "nested break" { var count: usize = 0; - outer: for ([_]i32{ 1, 2, 3, 4, 5 }) |_| { - for ([_]i32{ 1, 2, 3, 4, 5 }) |_| { + outer: for (1..6) |_| { + for (1..6) |_| { count += 1; break :outer; } @@ -4721,8 +4744,8 @@ test "nested break" { test "nested continue" { var count: usize = 0; - outer: for ([_]i32{ 1, 2, 3, 4, 5, 6, 7, 8 }) |_| { - for ([_]i32{ 1, 2, 3, 4, 5 }) |_| { + outer: for (1..9) |_| { + for (1..6) |_| { count += 1; continue :outer; } From bc8e1e1de4a2f22c56528240d320f682f1ec4b69 Mon Sep 17 00:00:00 2001 From: zooster Date: Sun, 23 Apr 2023 20:06:21 +0200 Subject: [PATCH 26/44] Improvements to docs and text * docs(std.math): elaborate on difference between absCast and absInt * docs(std.rand.Random.weightedIndex): elaborate on likelihood I think this makes it easier to understand. * langref: add small reminder * docs(std.fs.path.extension): brevity * docs(std.bit_set.StaticBitSet): mention the specific types * std.debug.TTY: explain what purpose this struct serves This should also make it clearer that this struct is not supposed to provide unrelated terminal manipulation functionality such as setting the cursor position or something because terminals are complicated and we should keep this struct simple and focused on debugging. * langref(package listing): brevity * langref: explain what exactly `threadlocal` causes to happen * std.array_list: link between swapRemove and orderedRemove Maybe this can serve as a TLDR and make it easier to decide. * PrefetchOptions.locality: clarify docs that this is a range This confused me previously and I thought I can only use either 0 or 3. * fix typos and more * std.builtin.CallingConvention: document some CCs * langref: explain possibly cryptic names I think it helps knowing what exactly these acronyms (@clz and @ctz) and abbreviations (@popCount) mean. * variadic function error: add missing preposition * std.fmt.format docs: nicely hyphenate * help menu: say what to optimize for I think this is slightly more specific than just calling it "optimizations". These are speed optimizations. I used the word "performance" here. --- doc/langref.html.in | 27 +++++++++++-------- lib/std/Build/Cache.zig | 2 +- lib/std/RingBuffer.zig | 2 +- lib/std/Uri.zig | 2 +- lib/std/array_list.zig | 2 ++ lib/std/bit_set.zig | 5 ++-- lib/std/builtin.zig | 27 +++++++++++++++++++ lib/std/compress/zstandard.zig | 2 +- lib/std/compress/zstandard/decode/fse.zig | 2 +- lib/std/debug.zig | 2 ++ lib/std/fmt.zig | 2 +- lib/std/fs/path.zig | 7 +++-- lib/std/macho.zig | 4 +-- lib/std/math.zig | 7 ++--- lib/std/mem.zig | 2 +- lib/std/os/linux/seccomp.zig | 2 +- lib/std/rand.zig | 2 ++ src/Air.zig | 2 +- src/Autodoc.zig | 4 +-- src/Sema.zig | 10 +++---- src/link/NvPtx.zig | 2 +- src/main.zig | 4 +-- ...nion_init_with_none_or_multiple_fields.zig | 4 +-- .../variadic_arg_validation.zig | 2 +- 24 files changed, 83 insertions(+), 44 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index a8af959615..254f119d3d 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -1422,7 +1422,8 @@ fn foo() i32 { {#header_open|Thread Local Variables#}

A variable may be specified to be a thread-local variable using the - {#syntax#}threadlocal{#endsyntax#} keyword:

+ {#syntax#}threadlocal{#endsyntax#} keyword, + which makes each thread work with a separate instance of the variable:

{#code_begin|test|test_thread_local_variables#} const std = @import("std"); const assert = std.debug.assert; @@ -4278,7 +4279,7 @@ const expectError = std.testing.expectError; fn isFieldOptional(comptime T: type, field_index: usize) !bool { const fields = @typeInfo(T).Struct.fields; return switch (field_index) { - // This prong is analyzed `fields.len - 1` times with `idx` being an + // This prong is analyzed `fields.len - 1` times with `idx` being a // unique comptime-known value each time. inline 0...fields.len - 1 => |idx| @typeInfo(fields[idx].type) == .Optional, else => return error.IndexOutOfBounds, @@ -8040,7 +8041,7 @@ pub const CallModifier = enum {

{#syntax#}@TypeOf(operand){#endsyntax#} must be an integer type or an integer vector type.

{#syntax#}operand{#endsyntax#} may be an {#link|integer|Integers#} or {#link|vector|Vectors#}.

- This function counts the number of most-significant (leading in a big-Endian sense) zeroes in an integer. + Counts the number of most-significant (leading in a big-endian sense) zeroes in an integer - "count leading zeroes".

If {#syntax#}operand{#endsyntax#} is a {#link|comptime#}-known integer, @@ -8190,7 +8191,7 @@ test "main" {

{#syntax#}@TypeOf(operand){#endsyntax#} must be an integer type or an integer vector type.

{#syntax#}operand{#endsyntax#} may be an {#link|integer|Integers#} or {#link|vector|Vectors#}.

- This function counts the number of least-significant (trailing in a big-Endian sense) zeroes in an integer. + Counts the number of least-significant (trailing in a big-endian sense) zeroes in an integer - "count trailing zeroes".

If {#syntax#}operand{#endsyntax#} is a {#link|comptime#}-known integer, @@ -8576,11 +8577,11 @@ test "@hasDecl" {

  • {#syntax#}@import("std"){#endsyntax#} - Zig Standard Library
  • -
  • {#syntax#}@import("builtin"){#endsyntax#} - Target-specific information. +
  • {#syntax#}@import("builtin"){#endsyntax#} - Target-specific information The command zig build-exe --show-builtin outputs the source to stdout for reference.
  • -
  • {#syntax#}@import("root"){#endsyntax#} - Points to the root source file. - This is usually src/main.zig but it depends on what file is chosen to be built. +
  • {#syntax#}@import("root"){#endsyntax#} - Root source file + This is usually src/main.zig but depends on what file is built.
{#see_also|Compile Variables|@embedFile#} @@ -8803,7 +8804,9 @@ test "@wasmMemoryGrow" {
{#syntax#}@popCount(operand: anytype) anytype{#endsyntax#}

{#syntax#}@TypeOf(operand){#endsyntax#} must be an integer type.

{#syntax#}operand{#endsyntax#} may be an {#link|integer|Integers#} or {#link|vector|Vectors#}.

-

Counts the number of bits set in an integer.

+

+ Counts the number of bits set in an integer - "population count". +

If {#syntax#}operand{#endsyntax#} is a {#link|comptime#}-known integer, the return type is {#syntax#}comptime_int{#endsyntax#}. @@ -8835,6 +8838,8 @@ test "@wasmMemoryGrow" { pub const PrefetchOptions = struct { /// Whether the prefetch should prepare for a read or a write. rw: Rw = .read, + /// The data's locality in an inclusive range from 0 to 3. + /// /// 0 means no temporal locality. That is, the data can be immediately /// dropped from the cache after it is accessed. /// @@ -8844,12 +8849,12 @@ pub const PrefetchOptions = struct { /// The cache that the prefetch should be preformed on. cache: Cache = .data, - pub const Rw = enum { + pub const Rw = enum(u1) { read, write, }; - pub const Cache = enum { + pub const Cache = enum(u1) { instruction, data, }; @@ -10971,7 +10976,7 @@ pub const MAKELOCAL = @compileError("unable to translate C expr: unexpected toke

{#syntax#}[*c]T{#endsyntax#} - C pointer.