From 3b19869853282546fe290a63fc447ae88e713031 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 27 Jun 2022 14:31:54 -0700 Subject: [PATCH 1/6] Sema: honor the --test-filter flag --- src/Module.zig | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index bcf6491ce6..bdf206490d 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -4607,6 +4607,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi DeclAdapter{ .mod = mod }, Namespace.DeclContext{ .module = mod }, ); + const comp = mod.comp; if (!gop.found_existing) { const new_decl_index = try mod.allocateNewDecl(namespace, decl_node, iter.parent_decl.src_scope); const new_decl = mod.declPtr(new_decl_index); @@ -4625,7 +4626,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi 1 => blk: { // test decl with no name. Skip the part where we check against // the test name filter. - if (!mod.comp.bin_file.options.is_test) break :blk false; + if (!comp.bin_file.options.is_test) break :blk false; if (decl_pkg != mod.main_pkg) { if (!mod.main_pkg_in_std) break :blk false; const std_pkg = mod.main_pkg.table.get("std").?; @@ -4636,19 +4637,23 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi }, else => blk: { if (!is_named_test) break :blk false; - if (!mod.comp.bin_file.options.is_test) break :blk false; + if (!comp.bin_file.options.is_test) break :blk false; if (decl_pkg != mod.main_pkg) { if (!mod.main_pkg_in_std) break :blk false; const std_pkg = mod.main_pkg.table.get("std").?; if (std_pkg != decl_pkg) break :blk false; } - // TODO check the name against --test-filter + if (comp.test_filter) |test_filter| { + if (mem.indexOf(u8, decl_name, test_filter) == null) { + break :blk false; + } + } try mod.test_functions.put(gpa, new_decl_index, {}); break :blk true; }, }; if (want_analysis) { - mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl_index }); + comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl_index }); } new_decl.is_pub = is_pub; new_decl.is_exported = is_exported; @@ -4675,24 +4680,24 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi decl.has_linksection_or_addrspace = has_linksection_or_addrspace; decl.zir_decl_index = @intCast(u32, decl_sub_index); if (decl.getFunction()) |_| { - switch (mod.comp.bin_file.tag) { + switch (comp.bin_file.tag) { .coff => { // TODO Implement for COFF }, .elf => if (decl.fn_link.elf.len != 0) { // TODO Look into detecting when this would be unnecessary by storing enough state // in `Decl` to notice that the line number did not change. - mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index }); + comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index }); }, .macho => if (decl.fn_link.macho.len != 0) { // TODO Look into detecting when this would be unnecessary by storing enough state // in `Decl` to notice that the line number did not change. - mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index }); + comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index }); }, .plan9 => { // TODO Look into detecting when this would be unnecessary by storing enough state // in `Decl` to notice that the line number did not change. - mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index }); + comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index }); }, .c, .wasm, .spirv, .nvptx => {}, } From 8d8a5f973314cb692d543773db8d59a9001b91ad Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 27 Jun 2022 16:02:41 -0700 Subject: [PATCH 2/6] LLVM: support calls to varargs functions closes #11944 --- src/codegen/llvm.zig | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index bf09ec0f35..29e25f1079 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -4197,7 +4197,7 @@ pub const FuncGen = struct { } var it = iterateParamTypes(self.dg, fn_info); - while (it.next()) |lowering| switch (lowering) { + while (it.nextCall(self, args)) |lowering| switch (lowering) { .no_bits => continue, .byval => { const arg = args[it.zig_index - 1]; @@ -9004,10 +9004,26 @@ const ParamTypeIterator = struct { slice, }; - fn next(it: *ParamTypeIterator) ?Lowering { + pub fn next(it: *ParamTypeIterator) ?Lowering { if (it.zig_index >= it.fn_info.param_types.len) return null; - const ty = it.fn_info.param_types[it.zig_index]; + return nextInner(it, ty); + } + + /// `airCall` uses this instead of `next` so that it can take into account variadic functions. + pub fn nextCall(it: *ParamTypeIterator, fg: *FuncGen, args: []const Air.Inst.Ref) ?Lowering { + if (it.zig_index >= it.fn_info.param_types.len) { + if (it.zig_index >= args.len) { + return null; + } else { + return nextInner(it, fg.air.typeOf(args[it.zig_index])); + } + } else { + return nextInner(it, it.fn_info.param_types[it.zig_index]); + } + } + + fn nextInner(it: *ParamTypeIterator, ty: Type) ?Lowering { if (!ty.hasRuntimeBitsIgnoreComptime()) { it.zig_index += 1; return .no_bits; From 3c1daf951cf72e454de18e70f84b9a896aa17dd0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 27 Jun 2022 17:12:45 -0700 Subject: [PATCH 3/6] LLVM: fix invalid IR on `@returnAddress` of wasm/bpf see #11946 --- lib/std/x/os/net.zig | 2 +- src/codegen/llvm.zig | 11 +++++++++-- src/stage1/codegen.cpp | 4 ++-- src/target.zig | 8 ++++++++ 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/lib/std/x/os/net.zig b/lib/std/x/os/net.zig index a7cc8fbc4a..98d63969b9 100644 --- a/lib/std/x/os/net.zig +++ b/lib/std/x/os/net.zig @@ -433,7 +433,7 @@ pub const IPv6 = extern struct { '0'...'9' => fmt.parseInt(u32, scope_id_slice, 10), else => resolveScopeId(scope_id_slice) catch |err| switch (err) { error.InterfaceNotFound => return error.InterfaceNotFound, - else => err, + else => |e| return e, }, } catch return error.UnknownScopeId; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 29e25f1079..2b320923ad 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -7212,11 +7212,17 @@ pub const FuncGen = struct { fn airRetAddr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { if (self.liveness.isUnused(inst)) return null; + const llvm_usize = try self.dg.lowerType(Type.usize); + const target = self.dg.module.getTarget(); + if (!target_util.supportsReturnAddress(target)) { + // https://github.com/ziglang/zig/issues/11946 + return llvm_usize.constNull(); + } + const llvm_i32 = self.context.intType(32); const llvm_fn = self.getIntrinsic("llvm.returnaddress", &.{}); const params = [_]*const llvm.Value{llvm_i32.constNull()}; const ptr_val = self.builder.buildCall(llvm_fn, ¶ms, params.len, .Fast, .Auto, ""); - const llvm_usize = try self.dg.lowerType(Type.usize); return self.builder.buildPtrToInt(ptr_val, llvm_usize, ""); } @@ -8021,7 +8027,6 @@ pub const FuncGen = struct { assert(union_obj.haveFieldTypes()); const field = union_obj.fields.values()[extra.field_index]; const field_llvm_ty = try self.dg.lowerType(field.ty); - const tag_llvm_ty = try self.dg.lowerType(union_obj.tag_ty); const field_size = field.ty.abiSize(target); const field_align = field.normalAlignment(target); @@ -8044,6 +8049,7 @@ pub const FuncGen = struct { const fields: [1]*const llvm.Type = .{payload}; break :t self.context.structType(&fields, fields.len, .False); } + const tag_llvm_ty = try self.dg.lowerType(union_obj.tag_ty); var fields: [3]*const llvm.Type = undefined; var fields_len: c_uint = 2; if (layout.tag_align >= layout.payload_align) { @@ -8100,6 +8106,7 @@ pub const FuncGen = struct { index_type.constInt(@boolToInt(layout.tag_align < layout.payload_align), .False), }; const field_ptr = self.builder.buildInBoundsGEP(casted_ptr, &indices, indices.len, ""); + const tag_llvm_ty = try self.dg.lowerType(union_obj.tag_ty); const llvm_tag = tag_llvm_ty.constInt(extra.field_index, .False); const store_inst = self.builder.buildStore(llvm_tag, field_ptr); store_inst.setAlignment(union_obj.tag_ty.abiAlignment(target)); diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index d6fb6d4753..318a90732c 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -6764,8 +6764,8 @@ static LLVMValueRef ir_render_return_address(CodeGen *g, Stage1Air *executable, Stage1AirInstReturnAddress *instruction) { if ((target_is_wasm(g->zig_target) && g->zig_target->os != OsEmscripten) || target_is_bpf(g->zig_target)) { - // I got this error from LLVM 10: - // "Non-Emscripten WebAssembly hasn't implemented __builtin_return_address" + // LLVM 13 reports "Non-Emscripten WebAssembly hasn't implemented __builtin_return_address" + // https://github.com/ziglang/zig/issues/11946 return LLVMConstNull(get_llvm_type(g, instruction->base.value->type)); } diff --git a/src/target.zig b/src/target.zig index 14af2675d2..93c179b7f0 100644 --- a/src/target.zig +++ b/src/target.zig @@ -283,6 +283,14 @@ pub fn supportsStackProbing(target: std.Target) bool { (target.cpu.arch == .i386 or target.cpu.arch == .x86_64); } +pub fn supportsReturnAddress(target: std.Target) bool { + return switch (target.cpu.arch) { + .wasm32, .wasm64 => target.os.tag == .emscripten, + .bpfel, .bpfeb => false, + else => true, + }; +} + pub fn osToLLVM(os_tag: std.Target.Os.Tag) llvm.OSType { return switch (os_tag) { .freestanding, .other, .opencl, .glsl450, .vulkan, .plan9 => .UnknownOS, From df1f401cf0e997ed289e14f69ab1114e839b56ee Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 27 Jun 2022 18:26:50 -0700 Subject: [PATCH 4/6] std.x.os.net: make error set consistent across targets --- lib/std/x/os/net.zig | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/lib/std/x/os/net.zig b/lib/std/x/os/net.zig index 98d63969b9..d9eb910b79 100644 --- a/lib/std/x/os/net.zig +++ b/lib/std/x/os/net.zig @@ -9,10 +9,25 @@ const testing = std.testing; const native_os = builtin.os; const have_ifnamesize = @hasDecl(os.system, "IFNAMESIZE"); +pub const ResolveScopeIdError = error{ + NameTooLong, + PermissionDenied, + AddressFamilyNotSupported, + ProtocolFamilyNotAvailable, + ProcessFdQuotaExceeded, + SystemFdQuotaExceeded, + SystemResources, + ProtocolNotSupported, + SocketTypeNotSupported, + InterfaceNotFound, + FileSystem, + Unexpected, +}; + /// Resolves a network interface name into a scope/zone ID. It returns /// an error if either resolution fails, or if the interface name is /// too long. -pub fn resolveScopeId(name: []const u8) !u32 { +pub fn resolveScopeId(name: []const u8) ResolveScopeIdError!u32 { if (have_ifnamesize) { if (name.len >= os.IFNAMESIZE) return error.NameTooLong; @@ -433,7 +448,7 @@ pub const IPv6 = extern struct { '0'...'9' => fmt.parseInt(u32, scope_id_slice, 10), else => resolveScopeId(scope_id_slice) catch |err| switch (err) { error.InterfaceNotFound => return error.InterfaceNotFound, - else => |e| return e, + else => err, }, } catch return error.UnknownScopeId; From 0b8bd9b2b4f609fb4ae7d31da7e7a0fd4f5ad987 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 27 Jun 2022 18:27:06 -0700 Subject: [PATCH 5/6] std.os.linux.clone: upgrade to stage2 fn ptr semantics --- lib/std/os/linux/arm64.zig | 7 ++++++- lib/std/os/linux/i386.zig | 7 ++++++- lib/std/os/linux/mips.zig | 7 ++++++- lib/std/os/linux/powerpc.zig | 7 ++++++- lib/std/os/linux/powerpc64.zig | 7 ++++++- lib/std/os/linux/riscv64.zig | 7 ++++++- lib/std/os/linux/sparc64.zig | 7 ++++++- 7 files changed, 42 insertions(+), 7 deletions(-) diff --git a/lib/std/os/linux/arm64.zig b/lib/std/os/linux/arm64.zig index dc6c7077ba..8f928cab36 100644 --- a/lib/std/os/linux/arm64.zig +++ b/lib/std/os/linux/arm64.zig @@ -98,8 +98,13 @@ pub fn syscall6( ); } +const CloneFn = switch (@import("builtin").zig_backend) { + .stage1 => fn (arg: usize) callconv(.C) u8, + else => *const fn (arg: usize) callconv(.C) u8, +}; + /// This matches the libc clone function. -pub extern fn clone(func: fn (arg: usize) callconv(.C) u8, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; +pub extern fn clone(func: CloneFn, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; pub const restore = restore_rt; diff --git a/lib/std/os/linux/i386.zig b/lib/std/os/linux/i386.zig index a85f6c713b..9ca127eb0c 100644 --- a/lib/std/os/linux/i386.zig +++ b/lib/std/os/linux/i386.zig @@ -118,8 +118,13 @@ pub fn socketcall(call: usize, args: [*]usize) usize { ); } +const CloneFn = switch (@import("builtin").zig_backend) { + .stage1 => fn (arg: usize) callconv(.C) u8, + else => *const fn (arg: usize) callconv(.C) u8, +}; + /// This matches the libc clone function. -pub extern fn clone(func: fn (arg: usize) callconv(.C) u8, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; +pub extern fn clone(func: CloneFn, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; pub fn restore() callconv(.Naked) void { return asm volatile ("int $0x80" diff --git a/lib/std/os/linux/mips.zig b/lib/std/os/linux/mips.zig index 4341949b2f..ee0aff281c 100644 --- a/lib/std/os/linux/mips.zig +++ b/lib/std/os/linux/mips.zig @@ -190,8 +190,13 @@ pub fn syscall7( ); } +const CloneFn = switch (@import("builtin").zig_backend) { + .stage1 => fn (arg: usize) callconv(.C) u8, + else => *const fn (arg: usize) callconv(.C) u8, +}; + /// This matches the libc clone function. -pub extern fn clone(func: fn (arg: usize) callconv(.C) u8, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; +pub extern fn clone(func: CloneFn, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; pub fn restore() callconv(.Naked) void { return asm volatile ("syscall" diff --git a/lib/std/os/linux/powerpc.zig b/lib/std/os/linux/powerpc.zig index c25f520ba2..d0601f858b 100644 --- a/lib/std/os/linux/powerpc.zig +++ b/lib/std/os/linux/powerpc.zig @@ -126,8 +126,13 @@ pub fn syscall6( ); } +const CloneFn = switch (@import("builtin").zig_backend) { + .stage1 => fn (arg: usize) callconv(.C) u8, + else => *const fn (arg: usize) callconv(.C) u8, +}; + /// This matches the libc clone function. -pub extern fn clone(func: fn (arg: usize) callconv(.C) u8, stack: usize, flags: usize, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; +pub extern fn clone(func: CloneFn, stack: usize, flags: usize, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; pub const restore = restore_rt; diff --git a/lib/std/os/linux/powerpc64.zig b/lib/std/os/linux/powerpc64.zig index 221fd935f2..329674cd72 100644 --- a/lib/std/os/linux/powerpc64.zig +++ b/lib/std/os/linux/powerpc64.zig @@ -126,8 +126,13 @@ pub fn syscall6( ); } +const CloneFn = switch (@import("builtin").zig_backend) { + .stage1 => fn (arg: usize) callconv(.C) u8, + else => *const fn (arg: usize) callconv(.C) u8, +}; + /// This matches the libc clone function. -pub extern fn clone(func: fn (arg: usize) callconv(.C) u8, stack: usize, flags: usize, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; +pub extern fn clone(func: CloneFn, stack: usize, flags: usize, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; pub const restore = restore_rt; diff --git a/lib/std/os/linux/riscv64.zig b/lib/std/os/linux/riscv64.zig index 079d501c32..dbf22e0aa4 100644 --- a/lib/std/os/linux/riscv64.zig +++ b/lib/std/os/linux/riscv64.zig @@ -95,7 +95,12 @@ pub fn syscall6( ); } -pub extern fn clone(func: fn (arg: usize) callconv(.C) u8, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; +const CloneFn = switch (@import("builtin").zig_backend) { + .stage1 => fn (arg: usize) callconv(.C) u8, + else => *const fn (arg: usize) callconv(.C) u8, +}; + +pub extern fn clone(func: CloneFn, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; pub const restore = restore_rt; diff --git a/lib/std/os/linux/sparc64.zig b/lib/std/os/linux/sparc64.zig index d7b74d95eb..6e1792ae0a 100644 --- a/lib/std/os/linux/sparc64.zig +++ b/lib/std/os/linux/sparc64.zig @@ -178,8 +178,13 @@ pub fn syscall6( ); } +const CloneFn = switch (@import("builtin").zig_backend) { + .stage1 => fn (arg: usize) callconv(.C) u8, + else => *const fn (arg: usize) callconv(.C) u8, +}; + /// This matches the libc clone function. -pub extern fn clone(func: fn (arg: usize) callconv(.C) u8, stack: usize, flags: usize, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; +pub extern fn clone(func: CloneFn, stack: usize, flags: usize, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; pub const restore = restore_rt; From a71d00a4d504edfdb09cd169d29ca1bbc0b909c4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 27 Jun 2022 19:05:51 -0700 Subject: [PATCH 6/6] std.crypto.25519.field: avoid excessive inlining This valid zig code produces reasonable LLVM IR, however, on the wasm32-wasi target, when using the wasmtime runtime, the number of locals of the `isSquare` function exceeds 50000, causing wasmtime to refuse to execute the binary. The `inline` keyword in Zig is intended to be used only where it is semantically necessary; not as an optimization hint. Otherwise, this may produce unwanted binary bloat for the -OReleaseSmall use case. In the future, it is possible that we may end up with both `inline` keyword, which operates as it does in status quo, and additionally `callconv(.inline_hint)` which has no semantic impact, but may be observed by optimization passes. In this commit, I also cleaned up `isSquare` by eliminating an unnecessary mutable variable, replacing it with several local constants. Closes #11947. --- lib/std/crypto/25519/field.zig | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/std/crypto/25519/field.zig b/lib/std/crypto/25519/field.zig index ce021ffb2a..1a786e0c32 100644 --- a/lib/std/crypto/25519/field.zig +++ b/lib/std/crypto/25519/field.zig @@ -341,7 +341,7 @@ pub const Fe = struct { } /// Square a field element `n` times - inline fn sqn(a: Fe, comptime n: comptime_int) Fe { + fn sqn(a: Fe, n: usize) Fe { var i: usize = 0; var fe = a; while (i < n) : (i += 1) { @@ -390,13 +390,12 @@ pub const Fe = struct { const _11 = a.mul(a.sq()); const _1111 = _11.mul(_11.sq().sq()); const _11111111 = _1111.mul(_1111.sq().sq().sq().sq()); - var t = _11111111.sqn(2).mul(_11); - const u = t; - t = t.sqn(10).mul(u).sqn(10).mul(u); - t = t.sqn(30).mul(t); - t = t.sqn(60).mul(t); - t = t.sqn(120).mul(t).sqn(10).mul(u).sqn(3).mul(_11).sq(); - return @bitCast(bool, @truncate(u1, ~(t.toBytes()[1] & 1))); + const u = _11111111.sqn(2).mul(_11); + const t = u.sqn(10).mul(u).sqn(10).mul(u); + const t2 = t.sqn(30).mul(t); + const t3 = t2.sqn(60).mul(t2); + const t4 = t3.sqn(120).mul(t3).sqn(10).mul(u).sqn(3).mul(_11).sq(); + return @bitCast(bool, @truncate(u1, ~(t4.toBytes()[1] & 1))); } fn uncheckedSqrt(x2: Fe) Fe {